-
什么是 Stream
Stream(流)是一个来自数据源的元素队列并支持聚合操作
1)元素是特定类型的对象,形成一个队列。 Java中的Stream并不会存储元素,而是按需计算。
2)数据源 流的来源。 可以是集合,数组,I/O channel, 产生器generator 等。
3)聚合操作 类似SQL语句一样的操作, 比如filter, map, reduce, find, match, sorted等。
和以前的Collection操作不同, Stream操作还有两个基础的特征:
1)Pipelining: 中间操作都会返回流对象本身。 这样多个操作可以串联成一个管道, 如同流式风格(fluent style)。 这样做可以对操作进行优化, 比如延迟执行(laziness)和短路( short-circuiting)。
2)内部迭代: 以前对集合遍历都是通过Iterator或者For-Each的方式, 显式的在集合外部进行迭代, 这叫做外部迭代。 Stream提供了内部迭代的方式, 通过访问者模式(Visitor)实现。 -
生成流
在 Java 8 中, 集合接口有两个方法来生成流:
1)stream() − 为集合创建串行流。
2)parallelStream() − 为集合创建并行流。
3)Stream.of()方法,有针对int,long的专用流IntStream,LongStream。List<Integer> list = Arrays.asList(1, 2, 3); list.stream().forEach(n -> System.out.println(n));//结果按顺序输出 123 list.parallelStream().forEach(a -> System.out.println(a));//结果并不是按顺序输出
注:由上可知,串行流输出的结果是按顺序输出,而并行流并不是如此,并行流是使用的ForkJoinPool 分而治之的算法实现的,当在数据量比较大(计算量比较大)的时候使用并行流比较合适。
public static void main(String[] args) { List<User> users = initList(); //列出岁数大于30人 users.stream().filter(t -> t.getAge() > 30).collect(Collectors.toList()).forEach(a -> System.out.println(a.getName())); System.out.println("---------------------------------------------------------------------------------------------------"); //找到年纪最大的人 String name = users.stream().max((a1, a2) -> a1.getAge() - a2.getAge()).get().getName(); System.out.println(name); System.out.println("---------------------------------------------------------------------------------------------------"); //求所有人的年纪总和 Integer integer = users.stream().map(User::getAge).reduce(Integer::sum).get(); System.out.println(integer); System.out.println("---------------------------------------------------------------------------------------------------"); //按年纪分组 users = initList(); users.stream().collect(Collectors.groupingBy(User::getAge)).forEach((key, value) -> { System.out.println(key+ ": " +value.toString()); }); System.out.println("---------------------------------------------------------------------------------------------------"); users.stream().distinct().collect(Collectors.toList()).forEach(t -> System.out.println(t.toString())); } private static List<User> initList(){ List<User> list = new ArrayList(); list.add(new User("张三",15)); list.add(new User("李四",45)); list.add(new User("王五",76)); list.add(new User("范六",5)); list.add(new User("花七",67)); list.add(new User("算法",25)); list.add(new User("傲視",67)); return list; }
-
缩减操作
因为每个缩减操作都把一个流缩减为一个值,好比最大值,最小值。当然流API,把min()和max(),count(),reduce() 这些操作称为特例缩减。List<Integer> list = new ArrayList<>(); list.add(1); list.add(2); list.add(3); list.add(4); list.add(5); list.add(6); // list.stream().sorted().forEachOrdered(a -> System.out.println(a)); Optional<Integer> reduce = list.stream().reduce((a, b) -> a + b); //System.out.println(reduce.get()); if (reduce.isPresent()) System.out.println(reduce.get()); Integer reduce1 = list.stream().reduce(0, (a, b) -> a + b); System.out.println(reduce1); Optional<Integer> reduce2 = list.stream().reduce((a, b) -> a * b); if (reduce2.isPresent()) System.out.println(reduce2.get()); Integer reduce3 = list.stream().reduce(1, (a, b) -> a * b); System.out.println(reduce3);
-
映射
因为在很多时候,将一个流的元素映射到另一个流对我们是非常有帮助的。比如有一个包含有名字,手机号码和钱的数据库构成的流,可能你只想要映射钱这个字段到另一个流,这时候可能之前学到的知识就还不能解决,于是映射就站了出来了。
另外,如果你希望对流中的元素应用一些转换,然后把转换的元素映射到一个新流里面,这时候也可以用映射。List<HeroPlayerGold> lists = new ArrayList<>(); lists.add(new HeroPlayerGold("盖伦","RNG-Letme",100)); lists.add(new HeroPlayerGold("诸葛亮","RNG-Xiaohu",300)); lists.add(new HeroPlayerGold("露娜","RNG-MLXG",300)); lists.add(new HeroPlayerGold("狄仁杰","RNG-UZI",500)); lists.add(new HeroPlayerGold("牛头","RNG-Ming",600)); //计算团队经济 int reduce = lists.stream().map(player -> new Gold(player.getGold())).mapToInt(Gold::getGold).reduce(0, (a, b) -> a + b); System.out.println(reduce); double reduce1 = lists.stream().mapToInt(HeroPlayerGold::getGold).reduce(0, (a, b) -> a + b); System.out.println(reduce1);
List<String> citys =Arrays.asList("GuangZhou ShangHai" ,"GuangZhou ShenZhen" ,"ShangHai ShenZhen", "BeiJing ShangHai","GuangZhou BeiJing" ,"ShenZhen BeiJing"); citys.stream().map(mCitys -> Arrays.stream(mCitys.split(" "))).forEach(System.out :: println);//note1 citys.stream() .map(mCitys -> Arrays.stream(mCitys.split(" "))) .forEach(cities -> cities.forEach(city -> System.out.println(city+" ")));//note2 citys.stream().flatMap(mCitys -> Arrays.stream(mCitys.split(" "))).forEach(System.out :: println);//note3
其中 //note1 处是无法打印元素的,使用map()打印元素的方式在 //note2 ,原因也在注释中交待了,但是使用了flatMap()方法后,直接就可以打印了 //note3
-
收集
一个集合转成一个流,然后对流进行操作,其实这种操作是最多的,但有时候我们也是需要从流中收集起一些元素,并以集合的方式返回,我们把这种反向操作称为收集。
流API中给我们提供了两种:<R, A> R collect(Collector<? super T, A, R> collector); <R> R collect(Supplier<R> supplier,BiConsumer<R, ? super T> accumulator,BiConsumer<R, R> combiner);
Collector接口位于 java.util.stream包中的声明。
Collectors类是一个最终类,里面提供了大量的静态的收集器方法,借助他,我们基本可以实现各种复杂的功能了。
我们来看一下toList和toSet方法:public static<T> Collector <T, ?, List<T>> toList() public static <T> Collector <T, ?, Set <T>> toSet()
其中 Collectors#toList()返回的收集器可以把流中元素收集到一个List中, Collectors#toSet()返回的收集器可以把流中的元素收集到一个Set中。比如:如果你想把元素收集到List中,你可以这样用, steam.collect(Collectors.toList)。
-
Spliterator 迭代器
Spliterator是Java8新增的一种迭代器,这种迭代器由Spliterator接口定义,Spliterator也有普通的遍历元素功能,这一点与刚才说的迭代器类似的,但是,但是Spliterator方法和使用迭代器的方法是不同的。List<HeroPlayerGold> lists = new ArrayList(); lists. add(new HeroPlayerGold("盖伦","RNG-Letme",100)); lists.add(new HeroPlayerGold("诸葛亮","RNG-Xiaohu", 300)); lists.add(new HeroPlayerGold("露娜","RNG-MLXG", 300)); lists.add(new HeroPlayerGold("狄仁杰","RNG-UZl", 500)); lists.add(new HeroPlayerGold("牛头","RNG-Ming", 500)); lists.stream().spliterator().forEachRemaining(hp -> { if (hp.getGold() >= 300) System.out.println(hp.getHero()); }); System.out.println("---------------------------"); Spliterator<HeroPlayerGold> spliterator = lists.stream().spliterator(); Spliterator<HeroPlayerGold> s = spliterator.trySplit(); if (s != null ){ s.forEachRemaining(System.out :: println); } System.out.println("---------------------------"); spliterator.forEachRemaining(System.out :: println);
显示结果:
诸葛亮 露娜 狄仁杰 牛头 --------------------------- HeroPlayerGold{hero='盖伦', player='RNG-Letme', gold=100} HeroPlayerGold{hero='诸葛亮', player='RNG-Xiaohu', gold=300} --------------------------- HeroPlayerGold{hero='露娜', player='RNG-MLXG', gold=300} HeroPlayerGold{hero='狄仁杰', player='RNG-UZl', gold=500} HeroPlayerGold{hero='牛头', player='RNG-Ming', gold=500}
Spliterator的另一个值得注意的方法是trySplit(),它将被迭代的元素划分成了两部分,返回其中一部分的新Spliterator,另一部分则通过原来的Spliterator访问。
-
创建 Stream 的10方式
1、Stream.of 可变参数Stream<String> stream1 = Stream.of("A", "B", "C"); System.out.println("stream1:" + stream1.collect(joining()));
程序输出:
stream1:ABC
2、Stream.of 数组
String[] values = new String[]{"A", "B", "C"}; Stream<String> stream2 = Stream.of(values); System.out.println("stream2:" + stream2.collect(joining()));
程序输出:
stream2:ABC
看 Stream.of 源码,上面这两种方式其实就是第三种方式的包装版。
public static<T> Stream<T> of(T... values) { return Arrays.stream(values); }
我们直接使用源码中的方式也是一样的。
3、Arrays.stream
String[] values = new String[]{"A", "B", "C"}; Stream<String> stream3 = Arrays.stream(values); System.out.println("stream3:" + stream3.collect(joining()));
程序输出:
stream3:ABC
4、List
List<String> list = Arrays.asList("A", "B", "C"); Stream<String> stream4 = list.stream(); System.out.println("stream4:" + stream4.collect(joining()));
程序输出:
stream4:ABC
5、Set
Set<String> set = new HashSet<>(Arrays.asList("A", "B", "C")); Stream<String> stream5 = set.stream(); System.out.println("stream5:" + stream5.collect(joining()));
程序输出:
stream5:ABC
6、Map
Map<String, String> map = new HashMap<>(); map.put("1", "A"); map.put("2", "B"); map.put("3", "C"); Stream<String> stream6 = map.values().stream(); System.out.println("stream6:" + stream6.collect(joining()));
程序输出:
stream6:ABC
7、Stream.iterate
Stream<String> stream7 = Stream.iterate("A", e -> String.valueOf((char) (e.charAt(0) + 1))).limit(3); System.out.println("stream7:" + stream7.collect(joining()));
程序输出:
stream7:ABC
8、Pattern
String value = "A B C"; Stream<String> stream8 = Pattern.compile("\\W").splitAsStream(value); System.out.println("stream8:" + stream8.collect(joining()));
程序输出:
stream8:ABC
9、Files.lines
try { Stream<String> stream9 = Files.lines(Paths.get("d:/data.txt")); System.out.println("stream9:" + stream9.collect(joining())); } catch (IOException e) { e.printStackTrace(); } data.txt文件内容如下: A B C
程序输出:
stream9:ABC
10、Stream.generate
Stream<String> stream10 = Stream.generate(() -> "A").limit(3); System.out.println("stream10:" + stream10.collect(joining()));
程序输出:
stream10:AAA