一,简介
1,来源
在程序编写过程中,集合的处理应该是很普遍的。Java 8 对于 Collection
的处理花了很大的功夫,Java 8 中,引入了流
Stream
所有继承自 Collection
的接口都可以转换为 Stream
2,案例
假设我们有一个 List
包含一系列的 Person
,Person
有姓名 name
和年龄 age
连个字段。现要求这个列表中年龄大于 20 的人数。
通常按照以前我们可能会这么写:
long count = 0;
for (Person p : persons) {
if (p.getAge() > 20) {
count ++;
}
}
stream写法
long count = persons.stream()
.filter(person -> person.getAge() > 20)
.count();
大大简化了
二,常用操作
1,collect
collect(toList())
方法由 Stream
里的值生成一个列表,是一个及早求值操作。可以理解为 Stream
向 Collection
的转换。
注意这边的 toList()
其实是 Collectors.toList()
,因为采用了静态倒入,看起来显得简洁。
List<String> collected = Stream.of("a", "b", "c")
.collect(Collectors.toList());
assertEquals(Arrays.asList("a", "b", "c"), collected);
2,map
如果有一个函数可以将一种类型的值转换成另外一种类型,map
操作就可以使用该函数,将一个流中的值转换成一个新的流
List<String> collected = Stream.of("a", "b", "hello")
.map(string -> string.toUpperCase())
.collect(toList());
assertEquals(Arrays.asList("A", "B", "HELLO"), collected);
3,filter
List<String> beginningWithNumbers =
Stream.of("a", "1abc", "abc1")
.filter(value -> isDigit(value.charAt(0)))
.collect(toList());
assertEquals(Arrays.asList("1abc"), beginningWithNumbers);
4, flatMap
多个流的合集
List<Integer> together = Stream.of(asList(1, 2), asList(3, 4))
.flatMap(numbers -> numbers.stream())
.collect(toList());
assertEquals(asList(1, 2, 3, 4), together);
5,max和min
List<Integer> list = Lists.newArrayList(3, 5, 2, 9, 1);
int maxInt = list.stream()
.max(Integer::compareTo)
.get();
int minInt = list.stream()
.min(Integer::compareTo)
.get();
assertEquals(maxInt, 9);
assertEquals(minInt, 1);
6,reduce
累加
int result = Stream.of(1, 2, 3, 4)
.reduce(0, (acc, element) -> acc + element);
assertEquals(10, result);
累乘积
int result = Stream.of(1, 2, 3, 4)
.reduce(1, (acc, element) -> acc * element);
assertEquals(24, result);
7,并行
并行化操作流只需改变一个方法调用。如果已经有一个 Stream
对象,调用它的 parallel()
方法就能让其拥有并行操作的能力。如果想从一个集合类创建一个流,调用 parallelStream()
就能立即获得一个拥有并行能力的流
int sumSize = Stream.of("Apple", "Banana", "Orange", "Pear")
.parallel()
.map(s -> s.length())
.reduce(Integer::sum)
.get();
assertEquals(sumSize, 21);
三,更复杂的情况
1,@FunctionalInterface接口注解
我们讨论过函数接口定义的标准,但未提及 @FunctionalInterface 注释。事实上,每个用作函数接口的接口都应该添加这个注释。
但 Java 中有一些接口,虽然只含一个方法,但并不是为了使用 Lambda 表达式来实现的。比如,有些对象内部可能保存着某种状态,使用带有一个方法的接口可能纯属巧合。
该注释会强制 javac 检查一个接口是否符合函数接口的标准。如果该注释添加给一个枚举类型、类或另一个注释,或者接口包含不止一个抽象方法,javac 就会报错。重构代码时,使用它能很容易发现问题。
1.1,栗子
//如定义了一个函数式接口如下:
@FunctionalInterface
interface GreetingService {
void sayMessage(String message);
}
//那么就可以使用Lambda表达式来表示该接口的一个实现(注:JAVA 8 之前一般是用匿名类实现的):
GreetingService greetService1 = message -> System.out.println("Hello " + message);
2,排序
另外一个尚未提及的关于集合类的内容是流中的元素以何种顺序排列。一些集合类型中的元素是按顺序排列的,比如 List;而另一些则是无序的,比如 HashSet。增加了流操作后,顺序问题变得更加复杂。
总之记住。如果集合本身就是无序的,由此生成的流也是无序的。一些中间操作会产生顺序,比如对值做映射时,映射后的值是有序的,这种顺序就会保留 下来。如果进来的流是无序的,出去的流也是无序的。
如果我们需要对流中的数据进行排序,可以调用 sorted
方法:
List<Integer> list = Lists.newArrayList(3, 5, 1, 10, 8);
List<Integer> sortedList = list.stream()
.sorted(Integer::compareTo)
.collect(Collectors.toList());
assertEquals(sortedList, Lists.newArrayList(1, 3, 5, 8, 10));
四,总结
stream是不是很厉害?