流操作在本人接触到的部分,都是处理集合容器Collection,加入流操作主要是为了函数式编程,在很大程度上可以简化代码
简洁的代码处理复杂逻辑这是每个程序猿的追求,废话不多说,开始介绍
使用Stream基本步骤
1. 创建Stream;
2. 转换Stream,每次转换原有Stream对象不改变,返回一个新的Stream对象(**可以有多次转换**);
3. 对Stream进行聚合(Reduce)操作,获取想要的结果;
Stream 创建
//Lists是Guava中的一个工具类
List nums = Lists.newArrayList(1,null,3,4,null,6);
nums.stream().filter(num -> num != null).count();
/**
of方法:有两个overload方法,一个接受变长参数,一个接口单一值
*/
Stream integerStream = Stream.of(1, 2, 3, 5);
Stream stringStream = Stream.of("taobao");
/**
generator方法:生成一个无限长度的Stream,其元素的生成是通过给定的Supplier
*/
Stream.generate(new Supplier() {
@Override
public Double get() {
return Math.random();
}
});
Stream.generate(() -> Math.random());
Stream.generate(Math::random);
/**
iterate方法:也是生成无限长度的Stream,和generator不同的是,其元素的生成是重复对给定的种子值(seed)
调用用户指定函数来生成的。其中包含的元素可以认为是:seed
*/
Stream.iterate(1, item -> item + 1).limit(10).forEach(System.out::println);
所有集合类都可以直接调用stream()方法返回一个Stream对象
这个在本文的第一个例子中就展示了从List对象获取其对应的Stream对象,如果查看Java doc就可以发现Collection接口有一个stream方法,所以其所有子类都都可以获取对应的Stream对象。
public interface Collection extends Iterable {
//其他方法省略
default Stream stream() {
return StreamSupport.stream(spliterator(), false);
}
}
转换Stream1. distinct: 对于Stream中包含的元素进行去重操作(去重逻辑依赖元素的equals方法),新生成的Stream中没有重复的元素;
List list = Arrays.asList("aa", "bb", "cc", "a", "b", "c", "aa", "ab", "cc", "bb", "bc");
list = list.stream().distinct().collect(Collectors.toList()); // [aa, bb, cc, a, b, c, ab, bc]
List list = Arrays.asList("aa", "bb", "cc", "a", "b", "c","a", "aa", "ab", "cc", "bb", "bc");
list.stream().filter(e -> e.length()>=2).forEach(e -> System.out.print(e + ","));
// aa,bb,cc,aa,ab,cc,bb,bc,
List integerList = Arrays.asList(1, 2, 3, 8, 9, 6, 4, 2, 3, 7, 6);
integerList.stream().map(var -> { var ++; var += 2; return var; }).forEach(System.out::print);
DoubleStream doubleStream = integerList.stream().mapToDouble((value) -> value * 1.0);
List together = Stream.of(asList(1, 2),asList(3, 4)).flatMap(numbers -> numbers.stream()).collect(toList());
将两个队列一起处理流操作的时候,可以利用此方法将其转化为一个流
上部分的代码可以简化为
List together = Stream.of(Arrays.asList(1, 2),Arrays.asList(3, 4))
.flatMap(Collection::stream).collect(Collectors.toList());
5. peek: 生成一个包含原Stream的所有元素的新Stream,同时会提供一个消费函数(Consumer实例),新Stream每个元素被消费的时候都会执行给定的消费函数;
list.stream().distinct().peek(e -> System.out.println(e.length())).count();
在peek()方法中使用lambda 表达式的时候,返回值只能为void类型
方法暂时只介绍这么几个其余还有 limit() skip() forEach()等
聚合(Reduce)操作
下面会分两部分来介绍汇聚操作:
1. 可变汇聚:把输入的元素们累积到一个可变的容器中,比如Collection或者StringBuilder;
List nums = Lists.newArrayList(1,1,null,2,3,4,null,5,6,7,8,9,10);
List numsWithoutNull = nums.stream().filter(num -> num != null).
collect(() -> new ArrayList(),
(list, item) -> list.add(item),
(list1, list2) -> list1.addAll(list2));
上面这段代码就是对一个元素是Integer类型的List,先过滤掉全部的null,然后把剩下的元素收集到一个新的List中。进一步看一下collect方法的三个参数,都是lambda形式的函数(*上面的代码可以使用方法引用来简化,留给读者自己去思考*)。
但是上面的collect方法调用也有点太复杂了,没关系!我们来看一下collect方法另外一个override的版本,其依赖
super
T, A, R> collector);
Java8还给我们提供了Collector的工具类–[Collectors]其中已经定义了一些静态工厂方法,比如:Collectors.toCollection()收集到Collection中, Collectors.toList()收集到List中和Collectors.toSet()收集到Set中。这样的静态方法还有很多,这里就不一一介绍了,大家可以直接去看JavaDoc。下面看看使用Collectors对于代码的简化:
List numsWithoutNull = nums.stream().filter(num -> num != null).
collect(Collectors.toList());
2. 其他汇聚:除去可变汇聚剩下的,一般都不是通过反复修改某个可变对象,而是通过把前一次的汇聚结果当成下一次的入参,反复如此。比如reduce,count,allMatch;
Optional reduce(BinaryOperator accumulator);
接受一个BinaryOperator类型的参数,在使用的时候我们可以用lambda表达式来。
List ints = Lists.newArrayList(1,2,3,4,5,6,7,8,9,10);
System.out.println("ints sum is:" + ints.stream().reduce((sum, item) -> sum + item).get());
可以看到reduce方法接受一个函数,这个函数有两个参数,第一个参数是上次函数执行的返回值(也称为中间结果),第二个参数是stream中的元素,这个函数把这两个值相加,得到的和会被赋值给下次执行这个函数的第一个参数。要注意的是:**第一次执行的时候第一个参数的值是Stream的第一个元素,第二个参数是Stream的第二个元素**。这个方法返回值类型是Optional,这是Java8防止出现NPE的一种可行方法,后面的文章会详细介绍,这里就简单的认为是一个容器,其中可能会包含0个或者1个对象。
T reduce(T identity, BinaryOperator accumulator);
这个定义上上面已经介绍过的基本一致,不同的是:它允许用户提供一个循环计算的初始值,如果Stream为空,就直接返回该值。而且这个方法不会返回Optional,因为其不会出现null值。下面直接给出例子,就不再做说明了。
List ints = Lists.newArrayList(1,2,3,4,5,6,7,8,9,10);
System.out.println("ints sum is:" + ints.stream().reduce(0, (sum, item) -> sum + item));
List ints = Lists.newArrayList(1,2,3,4,5,6,7,8,9,10);
System.out.println("ints sum is:" + ints.stream().count());
List ints = Lists.newArrayList(1,2,3,4,5,6,7,8,9,10);
System.out.println(ints.stream().allMatch(item -> item < 100));
ints.stream().max((o1, o2) -> o1.compareTo(o2)).ifPresent(System.out::println);