Lambda表达式和流
函数接口
实现函数功能的接口
Lambda 表达式
语法
1
| (parameterList)->{statements}
|
例如:
1 2 3 4 5 6
| (x, y)->{return x+y;}
(x, y)-> x + y
value->System.out.println(value) ()->System.out.println("hello world!")
|
Streams类
在Java中,Streams类实现了Stream接口。流可以用来加工和传递数据。
使用流的好处:
Java 8 中的 Stream 是对集合(Collection)对象功能的增强。
它专注于对集合对象进行各种非常便利、高效的聚合操作(aggregate operation),或者大批量数据操作 (bulk data operation)。
Stream API 借助于同样新出现的 Lambda 表达式,极大的提高编程效率和程序可读性。
它提供串行和并行两种模式进行汇聚操作,并发模式能够充分利用多核处理器的优势,使用 fork/join 并行方式来拆分任务和加速处理过程。
通常编写并行代码很难而且容易出错, 但使用 Stream API 无需编写一行多线程的代码,就可以很方便地写出高性能的并发程序。所以说,Java 8 中首次出现的 java.util.stream 是一个函数式语言+多核时代综合影响的产物。
Stream Pipelines
流在移动数据的时候加工里面的元素,称之为管线。它不像Collections可以保留数据,原始数据被加工后就无法重新使用了。
Intermediate operation是懒惰的,它直到Terminal Operation被调用后才会执行,即不会改变流的状态,可以继续被其他的Operation调用。
而Terminal Operation是饥渴的,它会加工中间操作然后产生一个结果,这时流已经被破坏而不能使用了。
例如:
1 2 3
| IntStream intStream = IntStream.of(values); intStream.reduce(0, (x, y), x + y); intStream.reduce(0, (x, y), x + y);
|
实例1:使用IntStream Operations来简化Arrays和ArrayList
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| int[] values = {1,2,3};
IntStream.of(values).forEach(value->System.out.printf("%d ", value));
Arrays.toString(values); fori;
IntStream.of(values).count();
IntStream.of(values).min().getAsInt(); IntStream.of(values).max().getAsInt();
IntStream.of(values).reduce(0, (x, y)-> x+y);
IntStream.of(values).sum(); fori;
IntStream.of(values).filter(value->value % 2 == 0).map(vakye->value * 10).sorted().forEach(value->sysout(value));
IntStream.range(1,10).sum();
|
实例2:使用Stream< Integer > Manipulations来处理Integer[]
1 2 3 4 5 6
| Integer[] values = {1,2,3}; Arrays.stream(values).collect(Collectors.toList());
Collections.addAll(arrayList, values);
Arrays.stream(values).filter(value->value > 4).sorted().collect(Collectors.toList());
|
实例3:使用Stream< String > Manipulations简化Strings, Characters 和 Regular Expressions
1 2 3 4 5 6 7 8
| String[] strings = {"red", "blue", "yellow"};
Arrays.stream(strings).map(String::toUpperCase).collect(Collectors.toList());
System.out.println(Arrays.stream(strings).map(s->s.toUpperCase()).collect(Collectors.toList()));
Arrays.stream(strings).sorted(String.CASE_INSENSITIVE_ORDER).collect(Collectors.toList());
|
String::toUppercase 是一个方法引用,相当于把参数的表达式省略了,但实际上依然是使用具体的实例来引用方法。
其他的方式如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| Employee[] list; String toString();
list.stream().forEach(System.out::println);
list.stream().forEach(obj->sysout(obj));
Predicate<Employee> fourToSixThousand = e -> (e.getSalary() >= 4000 && e.getSalary() <= 6000); list.stream().filter(forToSixThousand).sorted(Comparator.comparing(Employee::getSalary)) .forEach(System.out::println);
list.stream().filter(fourToSixThousand).findFirst().get());
|
函数变量和多领域比较
1 2 3 4 5 6 7 8 9 10 11
| Function<Employee, String> byFirstName = Employee::getFirstName; Function<Employee, String> byLastName = Employee::getLastName;
Comparator<Employee> lastThenFirst = Comparator.comparing(byLastName).thenComparing(byFirstName);
list.stream().sorted(lastThenFirst).forEach(System.out::println);
list.stream().sorted(lastThenFirst.reversed()).forEach(System.out::println);
|
使用map和distinct来产生唯一元素:
1
| list.stream().map(Employee::getLastName).distinct().sorted().forEach(System.out::println);
|
groupingBy方法对对象进行分类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
| public class Solution { public static class Person{ private String name; private int age;
public Person(String name, int age) { this.name = name; this.age = age; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
@Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + '}'; }
public String byAgeRange(){ if(getAge() <= 59){ return "<=59"; } else if(getAge() == 100){ return "100"; } else { int ten = getAge() / 10; return String.format(ten + "0~" + ten + "9"); } } }
public static void main(String[] args) { Person[] perons = { new Person("chen", 10), new Person("wen", 78), new Person("shu", 100), new Person("guo", 31) }; List<Person> list = Arrays.stream(perons).collect(Collectors.toList()); list.add(new Person("fan", 65));
Map<String, List<Person>> groupByName = list.stream().collect(Collectors.groupingBy(Person::byAgeRange)); System.out.println(groupByName); } }
|
输出结果:
1
| {100=[Person{name='shu', age=100}], 60~69=[Person{name='fan', age=65}], 70~79=[Person{name='wen', age=78}], <=59=[Person{name='chen', age=10}, Person{name='guo', age=31}]}
|
将结果映射成double类型
1 2
| list.stream().mapToDouble(Employee::getSalary).reduce(0, (value1, value2)->value1 + value2); list.stream().mapToDouble(Employee::getSalary).average().getAsDouble();
|
实例4:从文件中创建字符流
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| import java.io.IOException; import java.nio.file.Files; import java.nio.file.Paths; import java.util.Map; import java.util.TreeMap; import java.util.regex.Pattern; import java.util.stream.Collectors;
public class StreamOfLines { public static void main(String[] args) throws IOException { Pattern pattern = Pattern.compile("\\s+");
Map<String, Long> wordCounts = Files.lines(Paths.get("src/main/resources/test.txt")) .map(line -> line.replaceAll("(?!')\\p{P}", "")) .filter(line -> line.compareTo("") != 0) .flatMap(line -> pattern.splitAsStream(line)) .collect(Collectors.groupingBy(String::toLowerCase, TreeMap::new, Collectors.counting()));
wordCounts.entrySet() .stream() .collect( Collectors.groupingBy(entry -> entry.getKey().charAt(0), TreeMap::new, Collectors.toList())) .forEach((letter, wordList) -> { System.out.printf("%n%C%n", letter); wordList.stream().forEach(word -> System.out.printf( "%13s: %d%n", word.getKey(), word.getValue() )); }); }
|
实例5:使用流来统计随机结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| import java.security.SecureRandom; import java.util.function.Function; import java.util.stream.Collectors;
public class RandomIntStream { public static void main(String[] args) { SecureRandom random = new SecureRandom();
System.out.printf("%-6s%s%n", "Face", "Frequency"); random.ints(600_000, 1, 7) .boxed() .collect(Collectors.groupingBy(Function.identity(), Collectors.counting())) .forEach((face, frequency) -> System.out.printf("%-6d%d%n", face, frequency)); } }
|
注:Java不允许原始数据放入集合中,而infinite stream(比如IntStream)的元素个数是未知的,因此需要使用boxed将其转换为Stream< Integer >。