stream是什么?
可以把Stream当成一个高级的Iterator(迭代器)。普通的Iterator,用户只能一个一个的遍历元素并对其执行某些操作;高级的Stream,用户只要给出需要对其包含的元素执行什么操作,比如“过滤掉长度大于10的字符串”、“获取每个字符串的首字母”等,stream将会开几个线程并行执行这些操作.
举个栗子
List<String> list = Arrays.asList("one","two","three", "four","five");
list.stream().filter(str -> str.length() > 3).forEach(System.out::println);
Java stream的使用过程可以分为三个步骤
- 创建stream
- 转换stream(每次转换原有Stream对象不改变,返回一个新的Stream对象(可以有多次转换))
- 汇聚操作(对Stream进行Reduce操作,获取想要的结果)
Java stream的使用过程
创建stream
- 通过stream类的静态方法
- Stream.of(创建一个定长的strea)
Stream<String> stream = Stream.of("1","2","3");
- Stream.generate(创建一个无限长的stream)
Stream<Double> stream = Stream.generate(Math::random);
- Stream.iterate(也是生成一个无限长的stream)
Stream<Integer> stream = Stream.iterate(1, item -> item + 1);
- 通过 collection的 stream()方法
Collection实现了stream()方法,它的子类(比如list,map)都拥有该方法.
default Stream<E> stream() {
return StreamSupport.stream(spliterator(), false);
}
使用:
List list = new ArrayList(Arrays.asList("1", "2", "3"));
Stream<String> stream = list.stream();
转换stream
- distinct (对重复的流元素去重)
List<Integer> list = new ArrayList(Arrays.asList(1,1,3,3,5,7,9));
list.stream().distinct().forEach(System.out::println);
出来的结果是: 1,3,5,7,9 ; 1,3被去重了
- filter (过滤器:只返回符合要求的元素)
List<Integer> list = new ArrayList(Arrays.asList(1,1,3,3,5,7,9));
list.stream().filter(i -> i > 4).forEach(System.out::println);
在这个例子中, i <= 4 的元素都被剔除掉了,只打出 5,7,9三个元素
- map(原stream的元素经过计算转变成为另一个stream)
List<Integer> list = new ArrayList(Arrays.asList(1,1,3,3,5,7,9));
list.stream().map(i -> i + 4).forEach(System.out::println);
本来应该输出1,1,3,3,5,7,9 ; 但是经过i + 4之后,变成输出 4,4,7,7,9,11,13
- peek(peek方法方法会使用一个Consumer消费流中的元素,但是返回的流还是包含原来的流中的元素)
List<Integer> list = new ArrayList(Arrays.asList(1,1,3,3,5,7,9));
list.stream().peek(System.out::println).map(i -> i + 4).count();
peek方法返回void
- limit(限制返回多少个元素)
List<Integer> list = new ArrayList(Arrays.asList(1,1,3,3,5,7,9));
list.stream().limit(2).forEach(System.out::println);
本来应该输出全部元素的,但是实际上只输出2个元素
- skip(和limit是相对的操作,limit是保存前n个元素,skip是跳出前n个元素)
List<Integer> list = new ArrayList(Arrays.asList(1,1,3,3,5,7,9));
list.stream().skip(2).forEach(System.out::println);
实际上输出3,3,5,7,9
- sorted(对元素进行排序)
List<Integer> list = new ArrayList(Arrays.asList(1, 1, 11, 3, 3, 5, 7, 9));
list.stream().sorted((x, y) -> y - x).forEach(System.out::println);
倒序
PS: 我们对stream进行N次转换, 以为一共执行了 N个循环,实际上只进行了一次循环
汇聚操作
汇聚操作(也称为折叠)接受一个元素序列为输入,反复使用某个合并操作,把序列中的元素合并成一个汇总的结果.。比如查找一个数字列表的总和或者最大值,或者把这些数字累积成一个List对象
汇聚操作的分类
- 可变汇聚(把输入的元素们累积到一个可变的容器中,比如Collection或者StringBuilder)
- 原生collect
<R> R collect(Supplier<R> supplier, BiConsumer<R, ? super T> accumulator, BiConsumer<R, R> combiner);
看一个栗子: 下面代码的作用是把猫的名字提取出来,放到一个新list里面
List<Cat> cats = new ArrayList<>(Arrays.asList(new Cat("one", 21), new Cat("two", 22)));
cats.stream().collect(ArrayList::new, (list, item) -> list.add(item.getName()), (list1, list2) -> list1.addAll(list2)).forEach(System.out::println);
第一个函数生成一个新的ArrayList实例
第二个函数接受两个参数,第一个是前面生成的ArrayList对象,二个是stream中包含的元素,函数体就是把stream中的元素加入ArrayList对象中。第二个函数被反复调用直到原stream的元素被消费完毕
第三个函数也是接受两个参数,这两个都是ArrayList类型的,函数体就是把第二个ArrayList全部加入到第一个中(为什么会有第三步呢?因为第二步他可能是并行操作的,即第二步会产生多个list,第三步把第二步中产生的所有list都添加到新的一个list中)
- Collectors(工具类)
如果你感觉原生的collect很难用,你可以用collectors来实现.
List<Cat> cats = new ArrayList<>(Arrays.asList(new Cat("one", 21), new Cat("two", 22)));
cats.stream().map(cat -> cat.getName()).collect(Collectors.toList());
- 其它汇聚
- count 计算stream中的元素的个数
- allMatch 是不是stream中所有元素都满足给定的匹配条件
- findFirst 查找stream的第一个元素
List<Cat> cats = new ArrayList<>(Arrays.asList(new Cat("one", 21), new Cat("two", 22)));
Optional<String> first = cats.stream().map(cat -> cat.getName()).findFirst();
System.out.println(first.get());
findFirst会返回一个optional对象,optional的用法后面再说
- max 和 min