https://www.ibm.com/developerworks/cn/java/j-lo-java8streamapi/
构造Stream
// 1. Individual values Stream stream = Stream.of("a", "b", "c"); // 2. Arrays String [] strArray = new String[] {"a", "b", "c"}; stream = Stream.of(strArray); stream = Arrays.stream(strArray); // 3. Collections List<String> list = Arrays.asList(strArray); stream = list.stream();
数值流的构造
//数值流有IntStream、DoubleStream、LongStream三种,目的是为了减少封箱拆箱的花费
IntStream.of(new int[]{1, 2, 3}).forEach(System.out::println); IntStream.range(1, 3).forEach(System.out::println); IntStream.rangeClosed(1, 3).forEach(System.out::println);
流转化为其他数据结构
// 1. Array String[] strArray1 = stream.toArray(String[]::new); // 2. Collection List<String> list1 = stream.collect(Collectors.toList()); List<String> list2 = stream.collect(Collectors.toCollection(ArrayList::new)); Set set1 = stream.collect(Collectors.toSet()); Stack stack1 = stream.collect(Collectors.toCollection(Stack::new)); // 3. String String str = stream.collect(Collectors.joining()).toString(); //joining就是把流的每一个元素连接起来,可以传入参数表示连接的时候中间的连接符号
流的操作分类
- Intermediate:
map (mapToInt, flatMap 等)、 filter、 distinct、 sorted、 peek、 limit、 skip、 parallel、 sequential、 unordered
- Terminal:
forEach、 forEachOrdered、 toArray、 reduce、 collect、 min、 max、 count、 anyMatch、 allMatch、 noneMatch、 findFirst、 findAny、 iterator
- Short-circuiting:
anyMatch、 allMatch、 noneMatch、 findFirst、 findAny、 limit
map/flatMap(映射)
//一对一的映射
List<String> output = wordList.stream(). map(String::toUpperCase).collect(Collectors.toList()); List<Integer> nums = Arrays.asList(1, 2, 3, 4); List<Integer> squareNums = nums.stream().map(n -> n * n).collect(Collectors.toList());
//一对多
Stream<List<Integer>> inputStream = Stream.of(
Arrays.asList(1), Arrays.asList(2, 3),Arrays.asList(4, 5, 6));
Stream<Integer> outputStream = inputStream.flatMap(childList -> childList.stream());
所谓flat,就是降低了这个流的维度,把每一个元素拿出来组成一个新的流,所谓的使得流扁平了
filter
List<String> output = reader.lines(). flatMap(line -> Stream.of(line.split(REGEXP))). filter(word -> word.length() > 0). collect(Collectors.toList());
//这里我reader是BufferedReader,lines方法返回每一行的数据作为一个元素,注意Stream一次性的特点,消耗之后就不能在使用看来,在处理文件相关内容的时候要格外的注意
forEach
stream.filter(p -> p.getGender() == Person.Sex.MALE)
.forEach(p -> System.out.println(p.getName()));
forEach 方法接收一个 Lambda 表达式,然后在 Stream 的每一个元素上执行该表达式。
forEach 是 terminal 操作,因此它执行后,Stream 的元素就被“消费”掉了,你无法对一个 Stream 进行两次 terminal 运算
peek
对每个元素执行操作并返回一个新的 Stream Stream.of("one", "two", "three", "four") .filter(e -> e.length() > 3) .peek(e -> System.out.println("Filtered value: " + e)) .map(String::toUpperCase) .peek(e -> System.out.println("Mapped value: " + e)) .collect(Collectors.toList());
findFirst
一个 termimal 兼 short-circuiting 操作,它总是返回 Stream 的第一个元素,或者空。 返回值类型:Optional
reduce
提供一个起始值(种子),然后依照运算规则(BinaryOperator),和前面 Stream 的第一个、第二个、第 n 个元素组合。从这个意义上说,字符串拼接、数值的 sum、min、max、average 都是特殊的 reduce Stream.reduce(0, (a, b) -> a+b)等价于sum() // 字符串连接,concat = "ABCD" String concat = Stream.of("A", "B", "C", "D").reduce("", String::concat); // 求最小值,minValue = -3.0 double minValue = Stream.of(-1.5, 1.0, -3.0, -2.0).reduce(Double.MAX_VALUE, Double::min); // 求和,sumValue = 10, 有起始值 int sumValue = Stream.of(1, 2, 3, 4).reduce(0, Integer::sum); // 求和,sumValue = 10, 无起始值,这里返回的是Optional类型 sumValue = Stream.of(1, 2, 3, 4).reduce(Integer::sum).get(); // 过滤,字符串连接,concat = "ace" concat = Stream.of("a", "B", "c", "D", "e", "F"). filter(x -> x.compareTo("Z") > 0). reduce("", String::concat);
limit/skip
limit 返回 Stream 的前面 n 个元素;skip 则是扔掉前 n 个元素。
limit 和 skip 对 sorted 后的运行次数无影响,就是说你不知道sorted的结果你limit没有用,不知道从哪里limit还是一样得排序
sorted
List<Person> personList2 = persons.stream().limit(2).sorted((p1, p2) -> p1.getName().compareTo(p2.getName())).collect(Collectors.toList());
没有实现Comparable的要指定Comparable(Lumbda表达式)
min/max/distinct
BufferedReader br = new BufferedReader(new FileReader("c:\SUService.log")); int longest = br.lines().mapToInt(String::length).max().getAsInt(); br.close(); System.out.println(longest); List<String> words = br.lines().flatMap(line -> Stream.of(line.split(" "))). filter(word -> word.length() > 0). map(String::toLowerCase).distinct(). sorted(). collect(Collectors.toList());
Match
- allMatch:Stream 中全部元素符合传入的 predicate,返回 true
- anyMatch:Stream 中只要有一个元素符合传入的 predicate,返回 true
- noneMatch:Stream 中没有一个元素符合传入的 predicate,返回 true
boolean isAllAdult = persons.stream().allMatch(p -> p.getAge() > 18);
Stream.generate
根据传入的Supplier 生成流,流的长度是无限的
Stream.iterate
iterate 跟 reduce 操作很像,接受一个种子值,和一个 UnaryOperator(例如 f)。然后种子值成为 Stream 的第一个元素,f(seed) 为第二个,f(f(seed)) 第三个,以此类推。
Stream.iterate(0, n -> n + 3).limit(10). forEach(x -> System.out.print(x + " "));.
用 Collectors 来进行 reduction 操作
java.util.stream.Collectors 类的主要作用就是辅助进行各类有用的 reduction 操作,例如转变输出为 Collection,把 Stream 元素进行归组。
Collectors .groupingBy/partitioningBy
按照条件分类,返回一个Map
把学生按照年龄进行分组,注意返回值的Map:key是分组的条件,val是符合条件的数据的集合
Map<Integer, List<Person>> personGroups = Stream.generate(new PersonSupplier()). limit(100). collect(Collectors.groupingBy(Person::getAge)); Iterator it = personGroups.entrySet().iterator(); while (it.hasNext()) { Map.Entry<Integer, List<Person>> persons = (Map.Entry) it.next(); System.out.println("Age " + persons.getKey() + " = " + persons.getValue().size()); }
partitioningBy 注意返回值的key是Boolean类型,就是把集合分成了两部分
Map<Boolean, List<Person>> children = Stream.generate(new PersonSupplier()). limit(100). collect(Collectors.partitioningBy(p -> p.getAge() < 18)); System.out.println("Children number: " + children.get(true).size()); System.out.println("Adult number: " + children.get(false).size());
总之,Stream 的特性可以归纳为:
- 不是数据结构
- 它没有内部存储,它只是用操作管道从 source(数据结构、数组、generator function、IO channel)抓取数据。
- 它也绝不修改自己所封装的底层数据结构的数据。例如 Stream 的 filter 操作会产生一个不包含被过滤元素的新 Stream,而不是从 source 删除那些元素。
- 所有 Stream 的操作必须以 lambda 表达式为参数
- 不支持索引访问
- 你可以请求第一个元素,但无法请求第二个,第三个,或最后一个。不过请参阅下一项。
- 很容易生成数组或者 List
- 惰性化
- 很多 Stream 操作是向后延迟的,一直到它弄清楚了最后需要多少数据才会开始。
- Intermediate 操作永远是惰性化的。
- 并行能力
- 当一个 Stream 是并行化的,就不需要再写多线程代码,所有对它的操作会自动并行进行的。
- 可以是无限的
- 集合有固定大小,Stream 则不必。limit(n) 和 findFirst() 这类的 short-circuiting 操作可以对无限的 Stream 进行运算并很快完成。