Java8 Stream API学习笔记,只看一文成为Lambda表达式、Stream API编写熟手,直接拷贝过去运行即可


package cn.cuit.testa0;


import static java.util.stream.Collectors.averagingDouble;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Random;
import java.util.Set;
import java.util.Spliterator;
import java.util.Stack;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ConcurrentSkipListMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.function.IntPredicate;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;

import org.junit.Before;
import org.junit.Test;

import com.google.common.collect.ContiguousSet;
import com.google.common.collect.DiscreteDomain;
import com.google.common.collect.Range;


/**
 * 参考文章: 搜索java Stream API查看更多文章
 * https://www.cnblogs.com/CarpenterLee/p/6545321.html
 * https://www.cnblogs.com/CarpenterLee/p/6550212.html
 * https://www.ibm.com/developerworks/cn/java/j-lo-java8streamapi/index.html
 * 
 * 
 * 当把一个数据结构包装成 Stream 后,就要开始对里面的元素进行各类操作了。常见的操作可以归类如下。
 * 
 * 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
 * 
 * java8 四大核心函数式接口Function、Consumer、Supplier、Predicate
 * https://blog.csdn.net/smithallenyu/article/details/80653897
 * 
 * Function
 * 
 * T:入参类型,R:出参类型
 * 
 * 调用方法:R apply(T t);
 * 
 * 定义函数示例:Function func = p -> p * 10; // 输出入参的10倍
 * 
 * 调用函数示例:func.apply(10); // 结果100
 * 
 * Consumer
 * 
 * T:入参类型;没有出参
 * 
 * 调用方法:void accept(T t);
 * 
 * 定义函数示例:Consumer consumer= p -> System.out.println(p); //
 * 因为没有出参,常用于打印、发送短信等消费动作
 * 
 * 调用函数示例:consumer.accept("18800008888");
 * 
 * Supplier
 * 
 * T:出参类型;没有入参
 * 
 * 调用方法:T get();
 * 
 * 定义函数示例:Supplier supplier= () -> 100; //
 * 常用于业务“有条件运行”时,符合条件再调用获取结果的应用场景;运行结果须提前定义,但不运行。
 * 
 * 调用函数示例:supplier.get();
 * 
 * Predicate
 * 
 * T:入参类型;出参类型是Boolean, 所以Boolean表达式就用这个函数式接口
 * 
 * 调用方法:boolean test(T t);
 * 
 * 定义函数示例:Predicate predicate = p -> p % 2 == 0; // 判断是否、是不是偶数
 * 
 * 调用函数示例:predicate.test(100); // 运行结果true
 * 
 * Collector接受三个泛型参数,对可变减少操作的数据类型作相应限制:
 * 
 * T:输入元素类型 A:缩减操作的可变累积类型(通常隐藏为实现细节) R:可变减少操作的结果类型
 * 
 * UnaryOperator UnaryOperator 接收T对象,返回T对象,是Function的特例化,本质上也是Function型的函数式接口
 * 
 * BinaryOperator BinaryOperator 接收两个T对象,返回T对象
 * 
 * 原则:遇到操作或者生产集合类型及数组容器,都可以想到可以使用Stream操作,并非要刻意去使用,简 单易懂的代码才是好代码
 */
public class Java8StreanAPI
{

    // @Test
    // public void testTakeWhileAndDropWhile() {
    /**
     * 从Stream中依次获取满足条件的元素,直到不满足条件为止结束获取
     * 
     * 举例:Stream中的元素 12, 4, 3, 6, 8, 9
     * 
     * 条件是 x -> x % 2 == 0 ,即判断是否为偶数,即当遇到元素不为偶数时终止获取
     * 
     * 那么得到的结果输出就是,12, 4 因为下一个元素为3不为偶数,即结束获取,丢弃后面的其他元素
     */
    // IntStream.of(12,4,3,6,8,9).takeWhile(x->x%2==0).forEach(System.out::print);

    /**
     * 从Stream中依次删除满足条件的元素,直到不满足条件为止结束删除
     * 
     * 举例:Stream中的元素 12, 4, 3, 6, 8, 9
     * 
     * 条件是 x -> x % 2 == 0 ,即判断是否为偶数,即当遇到元素不为偶数时终止删除
     * 
     * 那么得到的结果输出就是,3, 6, 8, 9 因为下一个元素为3不为偶数,即结束删除,返回3及以后剩下的所有元素
     */
    // IntStream.of(12,4,3,6,8,9).dropWhile(x->x%2==0).forEach(System.out::print);
    // }

    // public void testIO(String source, String target) {
    // Java7后自动关闭流,把需要关闭的资源声明再try的小括号里,Java10后的类型推断var
    // try (var in = new FileInputStream(source); var out = new
    // FileOutputStream(target)) {
    // Java9后,文件复制
    // in.transferTo(out);
    // } catch (Exception e) {
    // e.printStackTrace();
    // }
    // }

    /**
     * 改进的 Stream API
     * 
     * 在 Java 9 中它会变得更好。Stream 接口中添加了 4 个新的方法:dropWhile, takeWhile, ofNullable。还有个
     * iterate 方法的新重载方法,可以让你提供一个 Predicate (判断条件)来指定什么时候结束迭代
     * 
     * Optional 和 Stream 之间的结合也得到了改进。现在可以通过 Optional 的新方法 stram将一个 Optional
     * 对象转换为一个(可能是空的) Stream 对象,在组合复杂的 Stream 管道时,将 Optional 转换为 Stream 非常有用。
     */
    @Test
    public void test03()
    {
        // 向控制台打印 1 到 99
        // IntStream.iterate(1, i -> i < 100, i -> i + 1).forEach(System.out::println);
        // Optional转换为Stream
        // Stream s = Optional.of(1).stream();
        // s.forEach(System.out::println);
    }

    public static List transactions = new ArrayList<>();

    public static Transaction transaction = null;
    static
    {
        for (int i = 0; i < 10; i++ )
        {
            if (new Random().nextBoolean())
            {
                transaction = new Transaction(i, "JAVA8", "JDK8-Stream");
                transactions.add(transaction);
            }
            else
            {
                transaction = new Transaction(i, "JAVA7", "JDK7-Non-Stream");
                transactions.add(transaction);
            }
        }
    }

    /**
     * 在 Java 7 中,如果要发现 type 为 grocery 的所有Transaction,然后返回以Transaction降序排序好的交易 ID
     * 集合,我们需要这样写:
     */
    @SuppressWarnings({"unchecked", "rawtypes"})
    @Test
    public void testInJ7()
    {
        List groceryTransactions = new ArrayList<>();
        for (Transaction t : transactions)
        {
            if (t.getType() == Transaction.GROCERY)
            {
                groceryTransactions.add(t);
            }
        }

        Collections.sort(groceryTransactions, new Comparator()
        {
            public int compare(Object t1, Object t2)
            {
                return ((Transaction)t2).getValue().compareTo(((Transaction)t1).getValue());
            }
        });

        List transactionsIds = new ArrayList<>();
        for (Transaction t : groceryTransactions)
        {
            transactionsIds.add(t.getId());
        }

        transactionsIds.forEach(System.out::print);

    }

    /**
     * 对比上面的Java 7代码,在 Java 8 中,使用Stream API首先不说其他的,代码量就少了一般,而且看起来也更加优雅,默认升序
     */
    @Test
    public void testInJ8()
    {
        List transactionsIds = transactions.parallelStream().filter(t -> t.getType() == Transaction.GROCERY).sorted(
            Comparator.comparing(Transaction::getValue).reversed()).map(Transaction::getId).collect(Collectors.toList());

        transactionsIds.forEach(System.out::print);

        transactions.stream().filter(e -> e != null).filter(e -> e.getId() == 1).findAny().get();
        transactions.stream().filter(Objects::nonNull).filter(Java8StreanAPI.idIs1()).findAny().get();
        transactions.stream().filter(Objects::nonNull).filter(Java8StreanAPI::idIs101).findAny().get();
    }

    public static Predicate idIs1()
    {
        return e -> e.getId() == 1;
    }

    public static boolean idIs101(Transaction e)
    {
        return e.getId() == 1;
    }

    /* 构造流的方式很多,下面是的几种常见方法 */
    @SuppressWarnings({"unused", "rawtypes"})
    @Test
    public void testCreateStream()
    {
        // 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 list = Arrays.asList(strArray);
        stream = list.stream();

        // 需要注意的是,对于基本数值型,目前有三种对应的包装类型 Stream:
        // IntStream、LongStream、DoubleStream
        // 数值流的构造
        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);

        // 流转换为其它数据结构
        // 一个 Stream 只可以使用一次,下面的代码为了简洁而重复使用了数次
        // 1. Array
        String[] strArray1 = (String[])stream.toArray(String[]::new);

        // 2. Collection
        List list1 = (List)stream.collect(Collectors.toList());
        List list2 = (List)stream.collect(Collectors.toCollection(ArrayList::new));
        Set set1 = (Set)stream.collect(Collectors.toSet());
        Stack stack1 = (Stack)stream.collect(Collectors.toCollection(Stack::new));

        // 3. String
        String str = stream.collect(Collectors.joining()).toString();
    }

    @SuppressWarnings("unused")
    @Test
    public void testMapWithFlatMap()
    {
        List nums = Arrays.asList(1, 2, 3, 4);
        List squareNums = nums.stream().map(n -> n * n).collect(Collectors.toList());

        squareNums.forEach(System.out::print);

        // 一对多,flatMap 把 input Stream 中的层级结构扁平化【就是把多个集合整合成一个集合】,就是将最底层元素抽出来放到一起,最终
        // output 的新 Stream
        // 里面已经没有 List 了,都是直接的数字。
        /**
         * Stream -> flatMap -> Stream 
* Stream> -> flatMap -> Stream
* Stream> -> flatMap -> Stream
* Stream> -> flatMap -> Stream
* * 也就是Stream里面是集合而不是单体对象,当时集合的时候,Stream的操作(filter,sum,distinct * ...)和collectors不支持它,所以使用FlatMap可以整合成一个几个单体对象的Stream,就可以了 */ Stream> inputStream = Stream.of(Arrays.asList(1), Arrays.asList(2, 3), Arrays.asList(4, 5, 6)); Stream outputStream = inputStream.flatMap(childList -> childList.stream()); Stream outputStream1 = inputStream.flatMap(List::stream); // 等价上面,数组也可以Arrays::stream outputStream.forEach(System.out::print); // Optional optional = Optional.ofNullable(Integer.valueOf(0)); } // map/flatMap它的作用就是把 input Stream 的每一个元素,映射成 output Stream 的另外一个元素 @SuppressWarnings({"unused", "resource"}) @Test public void testMapAndfFlatMap() throws IOException { List wordList = new ArrayList<>(); wordList.add("abcd"); // 转换大写 List output = wordList.stream().map(String::toUpperCase).collect(Collectors.toList()); List output01 = wordList.stream().map(String::toUpperCase).collect(Collectors.toCollection(ArrayList::new)); output.forEach(System.out::println); output01.forEach(System.out::println); // 这段代码生成一个整数 list 的平方数 {1, 4, 9, 16} /** * 从上面例子可以看出,map 生成的是个 1:1 映射,每个输入元素, 都按照规则转换成为另外一个元素。还有一些场景,是一对多映射关系的,这时需要 * flatMap */ List nums = Arrays.asList(1, 2, 3, 4); List squareNums = nums.stream().map(n -> n * n).collect(Collectors.toList()); squareNums.forEach(System.out::println); // 一对多,flatMap 把 input Stream 中的层级结构扁平化,就是将最底层元素抽出来放到一起【多个集合变一个集合】,最终 output 的新 // Stream // 里面已经没有 List 了,都是直接的数字。 Stream> inputStream = Stream.of(Arrays.asList(1), Arrays.asList(2, 3), Arrays.asList(4, 5, 6)); Stream outputStream = inputStream.flatMap((childList) -> childList.stream()); outputStream.forEach(System.out::println); // filter 对原始 Stream 进行某项测试,通过测试的元素被留下来生成一个新 Stream // 留下偶数 Integer[] sixNums = {1, 2, 3, 4, 5, 6}; Integer[] evens = Stream.of(sixNums).filter(n -> n % 2 == 0).toArray(Integer[]::new); // 把单词挑出来 // 这段代码首先把每行的单词用 flatMap 整理到新的 Stream,然后保留长度不为 0 的,就是整篇文章中的全部单词了 String REGEXP = ","; BufferedReader reader = new BufferedReader(new FileReader(new File("test.txt"))); List outputWords = reader.lines().flatMap(line -> Stream.of(line.split(REGEXP))).filter(word -> word.length() > 0).collect(Collectors.toList()); // 当需要为多核系统优化时,可以 // parallelStream().forEach(),只是此时原有元素的次序没法保证,并行的情况下将改变串行时操作的行为,此时 forEach // 本身的实现不需要调整,而 Java8 以前的 for 循环 code 可能需要加入额外的多线程逻辑 // 但一般认为,forEach 和常规 for 循环的差异不涉及到性能,它们仅仅是函数式风格与传统 Java 风格的差别 // forEach 不能修改自己包含的本地变量值,也不能用 break/return 之类的关键字提前结束循环。 // 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。这也是一个模仿 Scala,在Java9后Optional可以和Stream互相转换 // 语言中的概念,作为一个容器,它可能含有某值,或者不包含。使用它的目的是尽可能避免 NullPointerException /** * 这个方法的主要作用是把 Stream 元素组合起来。它提供一个起始值(种子),然后依照运算规则(BinaryOperator),和前面 Stream * 的第一个、第二个、第 n 个元素组合。从这个意义上说,字符串拼接、数值的 sum、min、max、average 都是特殊的 reduce。例如 * Stream 的 sum 就相当于 * * Integer sum = integers.reduce(0, (a, b) -> a+b); 或 * * Integer sum = integers.reduce(0, Integer::sum); * * 也有没有起始值的情况,这时会把 Stream 的前面两个元素组合起来,返回的是 Optional。 * BinaryOperator接口,可以看到reduce方法接受一个函数,这个函数有两个参数,第一个参数是上次函数执行的返回值(也称为中间结果),第二个参数是stream中的元素,这个函数把这两个值相加,得到的和会被赋值给下次执行这个函数的第一个参数。要注意的是:第一次执行的时候第一个参数的值是Stream的第一个元素,第二个参数是Stream的第二个元素。这个方法返回值类型是Optional * * 第二个变形,与第一种变形相同的是都会接受一个BinaryOperator函数接口,不同的是其会接受一个identity参数,用来指定Stream循环的初始值。如果Stream为空,就直接返回该值。另一方面,该方法不会返回Optional,因为该方法不会出现null * * reduce前两种变形,因为接受参数不同,其执行的操作也有相应变化: * * 变形1,未定义初始值,从而第一次执行的时候第一个参数的值是Stream的第一个元素,第二个参数是Stream的第二个元素 * 变形2,定义了初始值,从而第一次执行的时候第一个参数的值是初始值,第二个参数是Stream的第一个元素 */ // 代码例如第一个示例的 reduce(),第一个参数(空白字符)即为起始值,第二个参数(String::concat)为 // BinaryOperator,即操作运行规则。这类有起始值的 reduce() 都返回具体的对象。而对于第四个示例没有起始值的 // reduce(),由于可能没有足够的元素,返回的是 Optional,请留意这个区别 // 字符串连接,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, 无起始值 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); // 对 Stream 的排序通过 sorted 进行,它比数组的排序更强之处在于你可以首先对 Stream 进行各类 // map、filter、limit、skip 甚至 distinct 来减少元素数量后,再排序,这能帮助程序明显缩短执行时间。我们对清单 14 进行优化: List persons = new ArrayList(); for (int i = 1; i <= 5; i++ ) { Person person = new Person(i, "name" + i, 0); persons.add(person); } List personList2 = persons.stream().limit(2).sorted((p1, p2) -> p1.getName().compareTo(p2.getName())).collect(Collectors.toList()); System.out.println(personList2); /** * min/max/distinct * * min 和 max 的功能也可以通过对 Stream 元素先排序,再 findFirst 来实现,但前者的性能会更好,为 O(n),而 sorted * 的成本是 O(n log n)。同时它们作为特殊的 reduce 方法被独立出来也是因为求最大最小值是很常见的操作 */ // /找出最长一行的长度 BufferedReader br = new BufferedReader(new FileReader("c:\\SUService.log")); int longest = br.lines().mapToInt(String::length).max().getAsInt(); br.close(); System.out.println(longest); // 下面的例子则使用 distinct 来找出不重复的单词。 // 找出全文的单词,转小写,并排序 List words = br.lines().flatMap(line -> Stream.of(line.split(" "))).filter(word -> word.length() > 0).map(String::toLowerCase).distinct().sorted().collect( Collectors.toList()); br.close(); System.out.println(words); /** * Stream 有三个 match 方法,从语义上说: * * allMatch:Stream 中全部元素符合传入的 predicate,返回 true anyMatch:Stream 中只要有一个元素符合传入的 * predicate,返回 true noneMatch:Stream 中没有一个元素符合传入的 predicate,返回 true * * 它们都不是要遍历全部元素才能返回结果。例如 allMatch 只要一个元素不满足条件,就 skip 剩下的所有元素,返回 false。对清单 13 中的 * Person 类稍做修改,加入一个 age 属性和 getAge 方法。 */ List persons01 = new ArrayList(); persons01.add(new Person(1, "name" + 1, 10)); persons01.add(new Person(2, "name" + 2, 21)); persons01.add(new Person(3, "name" + 3, 34)); persons01.add(new Person(4, "name" + 4, 6)); persons01.add(new Person(5, "name" + 5, 55)); boolean isAllAdult = persons.stream().allMatch(p -> p.getAge() > 18); System.out.println("All are adult? " + isAllAdult); boolean isThereAnyChild = persons.stream().anyMatch(p -> p.getAge() < 12); System.out.println("Any child? " + isThereAnyChild); } // limit 返回 Stream 的前面 n 个元素;skip 则是扔掉前 n 个元素(它是由一个叫 subStream 的方法改名而来) public void testLimitAndSkip() { List persons = new ArrayList(); for (int i = 1; i <= 10000; i++ ) { Person person = new Person(i, "name" + i, 0); persons.add(person); } List personList2 = persons.stream().map(Person::getName).limit(10).skip(3).collect(Collectors.toList()); System.out.println(personList2); } // 这是一个有 10,000 个元素的 Stream,但在 short-circuiting 操作 limit 和 skip 的作用下,管道中 map // 操作指定的 getName() 方法的执行次数为 limit 所限定的 10 次,而最终返回结果在跳过前 3 个元素后只有后面 7 个返回。 // 有一种情况是 limit/skip 无法达到 short-circuiting 目的的,就是把它们放在 Stream 的排序操作后, // 原因跟 sorted // 这个 intermediate 操作有关:此时系统并不知道 Stream 排序后的次序如何, // 所以 sorted // 中的操作看上去就像完全没有被 limit // 或者 skip 一样 // limit 和 skip 对 sorted 后的运行次数无影响 /** * 即虽然最后的返回元素数量是 2,但整个管道中的 sorted 表达式执行次数没有像前面例子相应减少。 * * 最后有一点需要注意的是,对一个 parallel 的 Steam 管道来说,如果其元素是有序的,那么 limit * 操作的成本会比较大,因为它的返回对象必须是前 n 个也有一样次序的元素。取而代之的策略是取消元素间的次序,或者不要用 parallel Stream */ @Test public void testLimitAndSkipNo() { List persons = new ArrayList(); for (int i = 1; i <= 5; i++ ) { Person person = new Person(i, "name" + i, 0); persons.add(person); } List personList2 = persons.stream().sorted((p1, p2) -> p1.getName().compareTo(p2.getName())).limit(2).collect(Collectors.toList()); // 当集合中存在null元素时,可以使用针对null友好的比较器,null元素排在集合的最前面 List persons2 = new ArrayList(); IntStream.range(0, 5).forEach(i -> { Person person = new Person(i, "name" + i, 0); persons2.add(person); }); persons2.add(null); List personList3 = persons2.stream().sorted(Comparator.nullsFirst(Comparator.comparing(Person::getAge).thenComparing(Person::getName)) // 参数 keyComparator ,keyComparator 是创建一个自定义的比较器 .thenComparing((x, y) -> { return new StringBuilder(x.getName()).reverse().toString().compareToIgnoreCase(y.getName()); })).limit(2).collect(Collectors.toCollection(ArrayList::new)); System.out.println(personList2); personList3.forEach(System.out::println); } class Person { public int no; private String name; private int age; public Person(int no, String name, int age) { this.no = no; this.name = name; this.age = age; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getName() { System.out.println(name); return name; } @Override public String toString() { return "Person [no=" + no + ", name=" + name + ", age=" + age + "]"; } } // Optional 的两个用例,Java8的时候Stream可以转换为Optional,而Optional不能转为Stream,Java8以后,就可以互转了 // optional.stream()//optional 可以转为stream @Test public void testOptional() { String strA = " abcd ", strB = null; print(strA); print(""); print(strB); getLength(strA); getLength(""); getLength(strB); } public static void print(String text) { // Java 8 Optional.ofNullable(text).ifPresent(System.out::println); BigDecimal positiveSum = null; positiveSum = Optional. ofNullable(positiveSum).orElse(BigDecimal.ZERO); // Pre-Java 8 if (text != null) { System.out.println(text); } if (positiveSum == null) { positiveSum = BigDecimal.ZERO; } } public static int getLength(String text) { // Java 8 return Optional.ofNullable(text).map(String::length).orElse(-1); // Pre-Java 8 // return if (text != null) ? text.length() : -1; }; // 进阶:自己生成流 /** * Stream.generate * * 通过实现 Supplier 接口,你可以自己来控制流的生成。这种情形通常用于随机数、常量的 Stream,或者需要前后元素间维持着某种状态信息的 * Stream。把 Supplier 实例传递给 Stream.generate() 生成的 Stream,默认是串行(相对 parallel * 而言)但无序的(相对 ordered 而言)。由于它是无限的,在管道中,必须利用 limit 之类的操作限制 Stream 大小。 */ @SuppressWarnings({"rawtypes", "unchecked"}) @Test public void testCreateSelfStream() { // 生成 10 个随机整数 Random seed = new Random(); Supplier random = seed::nextInt; Stream.generate(random).limit(10).forEach(System.out::println); Stream.generate(seed::nextInt).limit(10).forEach(System.out::println); Stream.generate(() -> Math.random()); Stream.generate(Math::random); seed.ints(10).forEach(System.out::println); // Another way IntStream.generate(() -> (int)(System.nanoTime() % 100)).limit(10).forEach(System.out::println); // Stream.generate() 还接受自己实现的 Supplier。例如在构造海量测试数据的时候,用某种自动的规则给每一个变量赋值;或者依据公式计算 // Stream 的每个元素值。这些都是维持状态信息的情形 // 自实现 Supplier Stream.generate(new PersonSupplier()).limit(10).forEach(p -> System.out.println(p.getName() + ", " + p.getAge())); /** * Stream.iterate * * iterate 跟 reduce 操作很像,接受一个种子值,和一个 UnaryOperator(例如 f)。然后种子值成为 Stream * 的第一个元素,f(seed) 为第二个,f(f(seed)) 第三个,以此类推 */ // 生成一个等差数列,与 Stream.generate 相仿,在 iterate 时候管道必须有 limit 这样的操作来限制 Stream 大小 Stream.iterate(0, n -> n + 3).limit(10).forEach(x -> System.out.print(x + " ")); /** * 进阶:用 Collectors 来进行 reduction 操作 java.util.stream.Collectors * 类的主要作用就是辅助进行各类有用的 reduction 操作,例如转变输出为 Collection,把 Stream 元素进行归组。 * * groupingBy/partitioningBy */ // 按照年龄归组 Map> personGroups = Stream.generate(new PersonSupplier()).limit(100).collect(Collectors.groupingBy(Person::getAge)); Iterator it = personGroups.entrySet().iterator(); while (it.hasNext()) { Map.Entry> persons = (Map.Entry>)it.next(); System.out.println("Age " + persons.getKey() + " = " + persons.getValue().size()); } // 上面的 code,首先生成 100 人的信息,然后按照年龄归组,相同年龄的人放到同一个 list 中 // 按照未成年人和成年人归组 Map> 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()); /** * 在使用条件“年龄小于 18”进行分组后可以看到,不到 18 岁的未成年人是一组,成年人是另外一组。partitioningBy 其实是一种特殊的 * groupingBy,它依照条件测试的是否两种结果来构造返回的数据结构,get(true) 和 get(false) 能即为全部的元素对象 */ } /** * 前面的章节我们提及到过双冒号运算符,双冒号运算就是Java中的[方法引用],[方法引用]的格式是 * * 类名::方法名 , 这种式子一般是用作Lambda表达式,Lambda有所谓懒加载嘛 表达式: * * person -> person.getAge(); * * 可以替换成 * * Person::getAge * * 表达式 * * () -> new HashMap<>(); * * 可以替换成 * * HashMap::new * * 这种[方法引用]或者说[双冒号运算]对应的参数类型是Function T表示传入类型,R表示返回类型。比如表达式person -> * person.getAge(); * 传入参数是person,返回值是person.getAge(),那么方法引用Person::getAge就对应着Function类型 * */ @Test public void convertTest() { List collected01 = new ArrayList<>(); collected01.add("alpha"); collected01.add("beta"); collected01 = collected01.stream().map(string -> string.toUpperCase()).collect(Collectors.toList()); System.out.println(collected01); List collected02 = new ArrayList<>(); collected02.add("alpha"); collected02.add("beta"); collected02 = collected02.stream().map(String::toUpperCase).collect(Collectors.toCollection(ArrayList::new));// 注意发生的变化 System.out.println(collected02); } private class PersonSupplier implements Supplier { private int index = 0; private Random random = new Random(); @Override public Person get() { return new Person(index++ , "StormTestUser" + index, random.nextInt(100)); } } // ============================追加,根据4大函数式接口,设计一些简单的自己的函数接口和函数式编程============================== @Test public void testIsPrime() { System.out.println(isPrime(1)); System.out.println(isPrime(2)); System.out.println(isPrime(3)); System.out.println(isPrime(4)); System.out.println(isPrime01(1)); System.out.println(isPrime01(2)); System.out.println(isPrime01(3)); System.out.println(isPrime01(4)); System.out.println(isPrimeByBoolean(4)); } // 判断质数 public boolean isPrime(final int number) { return number > 1 && IntStream.range(2, number).noneMatch(element -> number % element == 0); // noneMatch(IntPredicate) } // 判断质数 public boolean isPrimeByBoolean(final int number) { return Boolean.logicalAnd(number > 1, IntStream.range(2, number).noneMatch(element -> number % element == 0)); } // 判断质数改写 public static boolean isPrime01(final int number) { IntPredicate predicate = element -> number % element == 0; return number > 1 && IntStream.range(2, number).noneMatch(predicate); // noneMatch(IntPredicate) } // 从一个数字集合中找到第一个大于3的数,并将这个数的二倍返回 @Test public void testFindNum() { List values = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); System.out.println(values.stream().filter(e -> e > 3).filter(e -> e % 2 == 0).map(e -> e * 2).findFirst().get()); } // 从一个数字集合中找到第一个大于3的数,并将这个数的二倍返回,改写01 @Test public void testFindNum01() { List values = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); System.out.println(values.stream().filter(Java8StreanAPI::isGreaterThan3).filter(Java8StreanAPI::isEven).map(Java8StreanAPI::doubleIt).findFirst().get()); } public static boolean isGreaterThan3(int number) { return number > 3; } public static boolean isEven(int number) { return number % 2 == 0; } public static int doubleIt(int number) { return number * 2; } // 从一个数字集合中找到第一个大于3的数,并将这个数的二倍返回,改写02 @Test public void testFindNum02() { List values = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); System.out.println(values.stream().filter(isGreaterThan301()).filter(isEven01()).map(doubleIt01()).findFirst().get()); } @Test public void testFindNum03() { List values = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); System.out.println(values.stream().filter(isGreaterThan301()).filter(isEven01()).map(doubleIt01()).findFirst().get()); } @Test public void testFindNum04() { List values = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); System.out.println(values.stream().filter(isGreaterThan().apply(3)).filter(isEven01()).map(doubleIt01()).findFirst().get()); } @Test public void testFindNum05() { System.out.println(IntStream.range(1, 10).filter(number -> number > 3).filter(number -> number % 2 == 0).map(number -> number * 2).findFirst().getAsInt()); } public static Predicate isGreaterThan301() { return number -> number > 3; } public static Predicate isEven01() { return number -> number % 2 == 0; } // 函数式接口之间可以转换的[虽然这是个]Function函数式接口,但是返回值也可以是其他的函数式接口,在调用的时候就不同了参考testFindNum04方法 public static Function> isGreaterThan() { // 解释: pivot -> (number -> number > pivot) // 括号里面的看出是一坨,整个Lamdab有入有出是Function接口,括号里面也一个Lamdab,是一个Predicate接口,有入有出,只是出规定必须是Boolean类型,这个出同样作为Function接口的出,Function的出可以是任意类型 return pivot -> number -> number > pivot; // pivot表示传进来的元素,number则是Stream里面的元素,这种的话可读性就差了,不好理解了 } public static Function doubleIt01() { return number -> number * 2; } @Test public void testSum() { List values = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); System.out.println(values.parallelStream().mapToInt(element -> element * 2).sum()); System.out.println(values.parallelStream().mapToInt(element -> element * 2).reduce(0, (a, b) -> a + b)); } // 原始版[线程控制没管,这里只看Lamdab和StreamAPI] @Test public void testMutilThread() { ExecutorService executorService = Executors.newFixedThreadPool(10); for (int i = 0; i < 10; i++ ) { int index = i; executorService.submit(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName() + ">>>>exe Task--index is:" + index); } }); } System.out.println("All Task was started...."); executorService.shutdown(); } @Test public void testMutilThread01() { ExecutorService executorService = Executors.newFixedThreadPool(10); for (int i = 0; i < 10; i++ ) { int index = i; executorService.submit(() -> System.out.println(Thread.currentThread().getName() + ">>>>exe Task--index is:" + index)); } System.out.println("All Task was started...."); executorService.shutdown(); } @Test public void testMutilThread02() { ExecutorService executorService = Executors.newFixedThreadPool(10); // 利用Stream可以产生集合 IntStream.range(0, 10).forEach(i -> { int index = i; executorService.submit(() -> System.out.println(Thread.currentThread().getName() + ">>>>exe Task--index is:" + index)); }); System.out.println("All Task was started...."); executorService.shutdown(); } // 使用Stream构建For(int i=0; i values = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); IntStream.range(0, 10).forEach(i -> { int index = i; System.out.println("index is >>>> " + index); // 结果:0~9, IntStream.range(0, 10)左闭右开区间即[0, 10) }); IntStream.range(0, values.size()).forEach(i -> { int index = i; System.out.println("index is >>>> " + index); }); int[] array = IntStream.range(0, 10).toArray(); for (int j : array) { System.out.println("index is >>>> " + j); } } @Test public void testMutilThread03() { ExecutorService executorService = Executors.newFixedThreadPool(10); // 利用Stream可以产生集合 IntStream.range(0, 10).forEach(i -> executorService.submit(() -> System.out.println(Thread.currentThread().getName() + ">>>>exe Task--index is:" + i))); // 利用Google Guava产生Range也可以产生集合 ContiguousSet.create(Range. closed(0, 10), DiscreteDomain.integers()).stream().forEach( i -> executorService.submit(() -> System.out.println(Thread.currentThread().getName() + ">>>>exe Task--index2 is:" + i)));; System.out.println("All Task was started...."); executorService.shutdown(); } // 原始 @Test public void testGet100PrimeSqrt() { List sqrtOfFrist100Primes = new ArrayList<>(); int index = 1; while (sqrtOfFrist100Primes.size() < 100) { if (this.isPrime(index)) { sqrtOfFrist100Primes.add(Math.sqrt(index)); } index++ ; } System.out.println(String.format("Computer %d values, first is %g, last is %g", sqrtOfFrist100Primes.size(), sqrtOfFrist100Primes.get(0), sqrtOfFrist100Primes.get(sqrtOfFrist100Primes.size() - 1))); } @Test public void testGet100PrimeSqrt01() { // 注意这里不能使用e + 1不能写成e++,否则JVM停不下来 List sqrtOfFrist100Primes = Stream.iterate(1, e -> e + 1).filter(Java8StreanAPI::isPrime01).map(Math::sqrt).limit(100).collect( Collectors.toCollection(ArrayList::new));// ArrayList::new // 相当与调用new这个构造方法,等价Collectors.toList()); System.out.println(String.format("Computer %d values, first is %g, last is %g", sqrtOfFrist100Primes.size(), sqrtOfFrist100Primes.get(0), sqrtOfFrist100Primes.get(sqrtOfFrist100Primes.size() - 1))); } @Test public void testListDirFileName() { File dir = new File("D:\\SoftTools"); File[] children = dir.listFiles(); if (Objects.nonNull(children)) { // 数组不能直接调用stream方法,要转化一下,Collectors作为工具类,可以静态导入 System.out.println(Stream.of(children).map(File::getName).map(String::toUpperCase).collect(Collectors.joining(", "))); } } // =====================================追加========================================== // Spliterator:Stream流的迭代器的使用,并行流处理优势强大 @Test public void testSpliterator01() { List persons01 = new ArrayList<>(); persons01.add(new Person(1, "name" + 1, 10)); persons01.add(new Person(2, "name" + 2, 21)); persons01.add(new Person(3, "name" + 3, 34)); persons01.add(new Person(4, "name" + 4, 6)); persons01.add(new Person(5, "name" + 5, 55)); // 获取一个元素 Spliterator spliterator = persons01.spliterator(); spliterator.tryAdvance(System.out::println); System.out.println("----------------"); // while(persons01.spliterator().tryAdvance(System.out::println));错误写法,死循环 while (spliterator.tryAdvance(System.out::println)); System.out.println("----------------"); // 从输出可以看出同一个spliterator只能使用一次 spliterator.forEachRemaining(System.out::println); } @Test public void testSpliterator02() { List persons01 = new ArrayList<>(); persons01.add(new Person(1, "name" + 1, 10)); persons01.add(new Person(2, "name" + 2, 21)); persons01.add(new Person(3, "name" + 3, 34)); persons01.add(new Person(4, "name" + 4, 6)); persons01.add(new Person(5, "name" + 5, 55)); // 获取一个元素 Spliterator spliterator = persons01.spliterator(); spliterator.tryAdvance(System.out::println); System.out.println("----------------"); while (spliterator.tryAdvance(System.out::println)); System.out.println("----------------"); Spliterator spliterator02 = persons01.spliterator(); spliterator02.forEachRemaining(System.out::println); } @Test public void testSpliterator03() { List persons01 = new ArrayList<>(); persons01.add(new Person(1, "name" + 1, 10)); persons01.add(new Person(2, "name" + 2, 21)); persons01.add(new Person(3, "name" + 3, 34)); persons01.add(new Person(4, "name" + 4, 6)); persons01.add(new Person(5, "name" + 5, 55)); persons01.add(new Person(6, "name" + 6, 431)); Spliterator spliterator = persons01.spliterator(); // 将Spliterator拆分成两个Spliterator,这两个Spliterator包含所有的元素,如果无法拆分返回null Spliterator trySplit = spliterator.trySplit(); Optional.ofNullable(trySplit).ifPresent(x -> { x.forEachRemaining(System.out::println); }); System.out.println("----------------"); spliterator.forEachRemaining(System.out::println); } @Test public void testBi() { System.getProperties().entrySet().forEach(System.out::println); // BiConsumer,两个入参,无返回,带有Bi开头的函数式接口都是两个参数的,如BiFunction、BiPredicate System.getProperties().forEach((x, y) -> { System.out.println(x + " ----> " + y); }); System.out.println(System.getProperty("os.name")); } // Map的Stream示例,主要处理过滤条件Map对象,过滤掉了null和空字符串 等操作 public Map parseMapForFilter(Map map) { if (map == null) { return null; } else { map = map.entrySet().stream().filter((e) -> checkValue(e.getValue())).collect(Collectors.toMap((e) -> (String)e.getKey(), (e) -> e.getValue())); } return map; } private static boolean checkValue(Object object) { if (object instanceof String && "".equals(object)) { return false; } if (null == object) { return false; } return true; } // 结合Optional优化,吊 public Map parseMapForFilterByOptional(Map map) { return Optional.ofNullable(map).map((v) -> { Map params = v.entrySet().stream().filter((e) -> checkValue(e.getValue())).collect(Collectors.toMap((e) -> e.getKey(), (e) -> e.getValue())); return params; }).orElse(null); } @Test public void testMap() { Map map = new HashMap<>(); map.put("a", ""); map.put("b", null); map.put("c", "c"); this.parseMapForFilter(map).entrySet().stream().forEach(x -> { System.out.println(x.getKey() + "---" + x.getValue()); }); this.parseMapForFilterByOptional(map).entrySet().stream().forEach(x -> { System.out.println(x.getKey() + "---" + x.getValue()); }); } // ===============================Collectors收集器工具API================================== // toMap/groupingBy/partitioningBy这几个会收集为Map @Test public void getMapValues() { Map> map = new HashMap<>(); Stack stack = new Stack<>(); for (int i = 0; i < 10; i++ ) { stack.push("str" + i); } map.put("a", stack); List collect = map.entrySet().stream().map(Map.Entry::getValue).flatMap(Stack::stream).collect(Collectors.toList()); collect.forEach(System.out::println); } @Test public void testList2Map() { Map> map = new HashMap<>(); Stack stack = new Stack<>(); for (int i = 0; i < 10; i++ ) { stack.push("str"); } map.put("a", stack); map.put("b", stack); Map map2 = map.entrySet().stream().collect(Collectors.groupingBy(Map.Entry::getKey, Collectors.counting())); List persons01 = new ArrayList<>(); persons01.add(new Person(1, "name" + 1, 10)); persons01.add(new Person(2, "name" + 2, 21)); persons01.add(new Person(3, "name" + 3, 34)); persons01.add(new Person(4, "name" + 4, 6)); persons01.add(new Person(5, "name" + 5, 55)); persons01.add(new Person(6, "name" + 6, 431)); // 这行代码会打印name,List收集为Map Map collect = persons01.stream().collect(Collectors.toMap(Person::getName, Person::getAge)); map2.entrySet().forEach(System.out::println); collect.entrySet().forEach(System.out::println); } List students = new ArrayList<>(); List persons = new ArrayList<>(); @Before public void CreateData() { persons.add(new Person(1, "name" + 1, 10)); persons.add(new Person(2, "name" + 2, 21)); persons.add(new Person(3, "name" + 3, 34)); persons.add(new Person(4, "name" + 4, 6)); persons.add(new Person(5, "name" + 5, 55)); persons.add(new Person(6, "name" + 6, 431)); students.add(new Student("ZS1", 20.2, "SC", 45.3, 34, new Date(), "M")); students.add(new Student("ZS2", 89.2, "SC", 34.3, 34, new Date(), "M")); students.add(new Student("LS", 23.1, "SC", 69.5, 56, new Date(), "W")); students.add(new Student("WW1", 14.4, "BJ", 75.5, 632, new Date(), "W")); students.add(new Student("WW2", 78.4, "BJ", 75.0, 632, new Date(), "W")); students.add(new Student("WW", 34.4, "BJ", 89.0, 632, new Date(), "M")); students.add(new Student("ZL1", 56.8, "SH", 99.8, 86, new Date(), "M")); students.add(new Student("ZL", 99.8, "SH", 45.8, 86, new Date(), "W")); students.add(new Student("ZL3", 99.8, "SH", 99.8, 86, new Date(), "M")); } /* * averagingDouble(ToDoubleFunction mapper) averagingDouble/Int/Long 汇总求出平均值 */ @Test public void averagingDoubleTest() { Objects.requireNonNull(students, "The parameter cannot be empty"); Double collect = students.stream() // 静态导入Collectors, 适合于静态方法多次使用的情况下,如果只使用一两次没必要,如果使用里面的静态方法很多,可以使用.*导入全部的静态方法 .collect(averagingDouble(student -> student.getHeight())); System.out.println(collect); } /* * collectingAndThen(Collector downstream, Function finisher) collectingAndThen 将收集来的结果进行另外的转换 * 本例中将收集的结果放入不可变List中。 */ @Test public void collectingAndThenTest() { Objects.requireNonNull(students, "The parameter cannot be empty"); List collect = students.stream().map(student -> student.getName()).collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)); Stream collect2 = students.stream().map(student -> student.getName()) // 收集为一个List后,然后对这个List执行一个Function的操作,x就是收集后的List .collect(Collectors.collectingAndThen(Collectors.toList(), x -> { return x.stream().map(String::toLowerCase); })); collect.forEach(System.out::println); collect2.forEach(System.out::println); } /* * 简单的计数器,简单的计算流元素。 */ @Test public void countingTest() { Objects.requireNonNull(students, "The parameter cannot be empty"); // 元素的个数 Long collect = students.stream().collect(Collectors.counting()); System.out.println(collect); } /* * groupingBy(Function classifier) 通过省份将学生分组到List中。 */ @Test public void groupingByToList() { Objects.requireNonNull(students, "The parameter cannot be empty"); // by的属性就是key,返回一个这个key的map Map> collect = students.stream().collect(Collectors.groupingBy(Student::getFrom)); collect.entrySet().forEach(System.out::println); } /* * groupingBy(Function classifier, Collector downstream) 通过省份将学生分组到Set中。 */ @Test public void groupingByToSet() { Objects.requireNonNull(students, "The parameter cannot be empty"); Map> collect = students.stream().collect(Collectors.groupingBy(Student::getFrom, Collectors.toSet())); collect.entrySet().forEach(System.out::println); } /* * groupingBy(Function classifier, Supplier mapFactory, Collector downstream) * 通过省份将学生分组到Set中。并返回一个TreeMap[按key会排序],TreeMap和TreeSet都会排序. */ @Test public void groupingByToTreeMap() { Objects.requireNonNull(students, "The parameter cannot be empty"); TreeMap> collect = students.stream().collect(Collectors.groupingBy(Student::getFrom, TreeMap::new, Collectors.toSet())); collect.entrySet().forEach(System.out::println); } /* * groupingByConcurrent(Function classifier) 返回并发的Map,即ConcurrentMap */ @Test public void groupingByConcurrentTest() { Objects.requireNonNull(students, "The parameter cannot be empty"); ConcurrentMap> collect = students.stream().collect(Collectors.groupingByConcurrent(Student::getFrom)); collect.entrySet().forEach(System.out::println); } /* * groupingByConcurrent(Function classifier, Collector downstream) * 返回ConcurrentMap,类似上面的Map */ @Test public void groupingByConcurrentToSet() { Objects.requireNonNull(students, "The parameter cannot be empty"); ConcurrentMap> collect = students.stream().collect(Collectors.groupingByConcurrent(Student::getFrom, Collectors.toSet())); collect.entrySet().forEach(System.out::println); } /* * 返回并发的Map,排序的,他实现了SortedMap */ @Test public void groupingByConcurrentToSkipMap() { Objects.requireNonNull(students, "The parameter cannot be empty"); ConcurrentSkipListMap> collect = students.stream().collect( Collectors.groupingByConcurrent(Student::getFrom, ConcurrentSkipListMap::new, Collectors.toSet())); collect.entrySet().forEach(System.out::println); } /* * 返回所有的学生的姓名 joining() joining(CharSequence delimiter) joining(CharSequence delimiter, CharSequence prefix, * CharSequence suffix) */ @Test public void joiningTest() { Objects.requireNonNull(students, "The parameter cannot be empty"); String collect = students.stream().map(Student::getName).collect(Collectors.joining()); String collect2 = students.stream().map(Student::getName).collect(Collectors.joining(",")); String collect3 = students.stream().map(Student::getName).collect(Collectors.joining(",", "{", "}")); // 先执行collect操作后再执行第二个参数的表达式。这里是先拼接字符串,再在最后+ "d"。 Collectors.collectingAndThen(Collectors.joining(","), x -> x + "d"); System.out.println(collect); System.out.println(collect2); System.out.println(collect3); } /* * mapping(Function mapper, Collector downstream) 以省份分组,并返回学生姓名的集合。 */ @Test public void mappingTest() { Objects.requireNonNull(students, "The parameter cannot be empty"); Map> collect = students.stream().collect(Collectors.groupingBy(Student::getFrom, // mapping对执行一个Function动作后的结果进行收集 Collectors.mapping(Student::getName, Collectors.toList()))); // 跟map操作类似,只是参数有点区别。map后,执行一个Function操作,在收集 System.out.println(Stream.of("a", "b", "c").collect(Collectors.mapping(x -> x.toUpperCase(), Collectors.joining(",")))); collect.entrySet().forEach(System.out::println); } /** * maxBy(Comparator comparator) * * 返回身高最高的学生 */ @Test public void maxByTest() { Objects.requireNonNull(students, "The parameter cannot be empty"); Student student = students.stream().collect(Collectors.maxBy(Comparator.comparingDouble(Student::getHeight))).get(); System.out.println(student); } /** * @return 返回体重最轻的学生。 */ @Test public void minByTest() { Objects.requireNonNull(students, "The parameter cannot be empty"); Student student = students.stream().collect(Collectors.minBy(Comparator.comparingDouble(Student::getWeight))).get(); System.out.println(student); } /** * partitioningBy(Predicate predicate) * * 根据Predicate将流分割,并将它们组织成一个 Map>返回。 */ @Test public void partitioningByTest() { Objects.requireNonNull(students, "The parameter cannot be empty"); Map> collect = students.stream().collect( // 给定一个Predicate来分区 Collectors.partitioningBy(student -> "SC".equals(student.getFrom()))); collect.entrySet().forEach(System.out::println); // 输出结果如下 // false=[Student [name=WW, height=14.4, from=BJ, weight=75.0, scores=632.0, // birthday=Tue May 28 10:24:58 SGT 2019], Student [name=ZL, height=56.8, // from=SH, weight=99.8, scores=86.0, birthday=Tue May 28 10:24:58 SGT 2019]] // true=[Student [name=ZS, height=20.2, from=SC, weight=45.3, scores=34.0, // birthday=Tue May 28 10:24:58 SGT 2019], Student [name=LS, height=23.1, // from=SC, weight=69.5, scores=56.0, birthday=Tue May 28 10:24:58 SGT 2019]] } /** * partitioningBy(Predicate predicate, Collector * downstream) * * 根据Predicate将流分割,并将它们组织成一个 Map>返回。 */ @Test public void partitioningByToSet() { Objects.requireNonNull(students, "The parameter cannot be empty"); Map> collect = students.stream().collect(Collectors.partitioningBy(student -> "SH".equals(student.getFrom()), Collectors.toSet())); collect.entrySet().forEach(System.out::println); } /** * reducing(BinaryOperator op) * * BinaryOperator中有两个静态方法,是用于比较两个数字或字符串的大小 * * 返回一个班级中每个省份的身高最高的学生。 */ @SuppressWarnings("unused") @Test public void reducingTest1() { Objects.requireNonNull(students, "The parameter cannot be empty"); Comparator byHeight2 = (s1, s2) -> Double.compare(s1.getHeight(), s2.getHeight()); Comparator byHeight3 = (s1, s2) -> Double.valueOf(s1.getHeight()).compareTo(Double.valueOf(s2.getHeight())); Comparator byHeight = Comparator.comparing(Student::getHeight); Map> collect = students.stream().collect(Collectors.groupingBy(Student::getFrom, // 收集身高最高的,BinaryOperator是一个BiFunction,有一个问题就是最高的有两个的话,结果中只有一个 Collectors.reducing(BinaryOperator.maxBy(byHeight)))); collect.entrySet().forEach(System.out::println); } /** * reducing(BinaryOperator op) * * 返回一个班级中每个省份的身高最高的学生。 */ @Test public void reducingTest2() { Objects.requireNonNull(students, "The parameter cannot be empty"); Map> collect = students.stream().collect(Collectors.groupingBy(Student::getFrom, // 收集身高最高的,BinaryOperator是一个BiFunction, -> 如果使用Lamdab的自动返回后面不能有{}, // Function式是有入有出的, BinaryOperator是BiFunction,则是两个入一个出, 这里s1.getHeight() // 返回的是基本类型double,不是对象类型,没有方法了,所以不能在.继续操作 // 需要包装成Double才能继续.操作Double提供的方法,或者使用compareTo方法,这里强转有精度损失,不管,这里只是示例 Collectors.reducing(BinaryOperator.maxBy((s1, s2) -> (int)(s1.getHeight() - s2.getHeight()))))); collect.entrySet().forEach(System.out::println); // reducing的另一个重载 // 在求累计值的时候,还可以对参数值进行改变,这里是都+1后再求和。跟reduce方法有点类似,但reduce方法没有第二个参数。 System.out.println(Stream.of(1, 3, 4).collect(Collectors.reducing(0, x -> x + 1, (x, y) -> x + y))); } /** * reducing(BinaryOperator op) * * 返回一个班级中每个省份的身高最高的学生。 */ @Test public void reducingTest3() { Objects.requireNonNull(students, "The parameter cannot be empty"); Map> collect = students.stream().collect(Collectors.groupingBy(Student::getFrom, // 收集身高最高的,BinaryOperator是一个BiFunction, -> 如果使用Lamdab的自动返回后面不能有{}, // Function式是有入有出的, BinaryOperator是BiFunction,则是两个入一个出 Collectors.reducing(BinaryOperator.maxBy((s1, s2) -> Double.compare(s1.getHeight(), s2.getHeight()))))); collect.entrySet().forEach(System.out::println); } /** * summarizingDouble(ToDoubleFunction mapper) * * summarizingDouble/Int/Long 用另一种方式来求学生的平均体重。 */ @Test public void summarizingDoubleTest() { Objects.requireNonNull(students, "The parameter cannot be empty"); double average = students.stream().collect(Collectors.summarizingDouble(Student::getWeight)).getAverage(); System.out.println(average); } /** * 求和,如果元素不存在,则结果为0. * * 求学生的平均身高 */ @Test public void summingDoubleTest() { Objects.requireNonNull(students, "The parameter cannot be empty"); // 这样如果没添加返回值接受,就会报错,即没有Double collect = 后面的 Double collect = students.stream().collect(Collectors.summingDouble(Student::getHeight)).doubleValue() / students.size(); Double collect1 = students.stream().collect(Collectors.summingDouble(Student::getHeight)); Double double1 = collect1 / students.size(); System.out.println(collect + "---" + double1); } /** * 将元素放入制定的集合中 */ @Test public void toCollectionToList() { students.stream().map(Student::getName).collect(Collectors.toCollection(ArrayList::new)).forEach(System.out::println); } /** * 获取学生的成绩 */ @Test public void toConcurrentMapTest() { students.stream().collect(Collectors.toConcurrentMap(Student::getName, Student::getScores)).entrySet().forEach(System.out::println); } @Test public void toMapTest() { Map collect = students.stream().collect(Collectors.toMap(Student::getName, Student::getBirthday)); collect.entrySet().forEach(System.out::println); } // 收集成实体本身map @Test public void getIdAccountMap() { Map collect = students.stream() // student -> student是一个返回本身的lambda表达式,其实还可以使用Function接口中的一个默认方法代替,使整个方法更简洁优雅 .collect(Collectors.toMap(Student::getName, student -> student)); collect.entrySet().forEach(System.out::println); } // 收集成实体本身map,与上面等价 @Test public void getIdAccountMap1() { Map collect = students.stream() // student -> student是一个返回本身的lambda表达式,其实还可以使用Function接口中的一个默认方法代替,使整个方法更简洁优雅 .collect(Collectors.toMap(Student::getName, // Returns a function that always returns its input argument Function.identity())); collect.entrySet().forEach(System.out::println); } // 上面这个方法可能报错(java.lang.IllegalStateException: Duplicate // key),因为name是有可能重复的。toMap有个重载方法,可以传入一个合并的函数来解决key冲突问题: @Test public void getIdAccountMap2() { Map collect = students.stream() // student -> student是一个返回本身的lambda表达式,其实还可以使用Function接口中的一个默认方法代替,使整个方法更简洁优雅 .collect(Collectors.toMap(Student::getName, Function.identity(), (k1, k2) -> k2)); collect.entrySet().forEach(System.out::println); } // 指定具体收集的map // toMap还有另一个重载方法,可以指定一个Map的具体实现,来收集数据: @Test public void getNameAccountMap() { LinkedHashMap collect = students.stream().collect(Collectors.toMap(Student::getName, Function.identity(), (key1, key2) -> key2, LinkedHashMap::new)); collect.entrySet().forEach(System.out::println); } /** * * @param address * @return * @throws FileNotFoundException * 统计一篇文章的词频直方图。 */ public Map toMapForWord(String address) throws FileNotFoundException { Objects.requireNonNull(address, "The parameter cannot be empty"); Pattern whitespace = Pattern.compile("\\s+"); try (BufferedReader reader = new BufferedReader(new FileReader(new File(address)))) { return reader.lines().flatMap(string -> whitespace.splitAsStream(string)).collect(Collectors.groupingBy(String::toLowerCase, Collectors.counting())); } catch (Exception e) { // Error log return new HashMap(); } } /** * 返回每个省份的最高学生的所有信息。 */ @Test public void accountMaxHeightByProvince() { Objects.requireNonNull(students, "The parameter cannot be empty"); Map>> collect = students.stream().collect( Collectors.groupingBy(Student::getFrom, Collectors.groupingBy(Student::getName, Collectors.maxBy(Comparator.comparingDouble(Student::getHeight))))); collect.entrySet().forEach(System.out::println); } /** * 先以性别分组然后再以省份分组,返回学生的平均身高。 */ @Test public void averageHeightByProvince() { Objects.requireNonNull(students, "The parameter cannot be empty"); Map> collect = students.stream().collect( Collectors.partitioningBy(student -> "M".equals(student.getGender()), Collectors.groupingBy(Student::getFrom, Collectors.averagingDouble(Student::getHeight)))); collect.entrySet().forEach(System.out::println); } } // 辅助类 class Transaction { public static final String GROCERY = "JAVA8"; private Integer id; private String type; private String value; public Transaction() { super(); } public Transaction(Integer id, String type, String value) { super(); this.id = id; this.type = type; this.value = value; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getType() { return type; } public void setType(String type) { this.type = type; } public String getValue() { return value; } public void setValue(String value) { this.value = value; } @Override public String toString() { return "Transaction [id=" + id + ", type=" + type + ", value=" + value + "]"; } } class Student { private String name; private Double height; private String from; private Double weight; private Double scores; private Date birthday; private String gender; public Student(String name, double height, String from, double weight, double scores, Date birthday, String gender) { super(); this.name = name; this.height = height; this.from = from; this.weight = weight; this.scores = scores; this.birthday = birthday; this.gender = gender; } public String getName() { return name; } public void setName(String name) { this.name = name; } public double getHeight() { return height; } public void setHeight(double height) { this.height = height; } public String getFrom() { return from; } public void setFrom(String from) { this.from = from; } public double getWeight() { return weight; } public void setWeight(double weight) { this.weight = weight; } public double getScores() { return scores; } public void setScores(double scores) { this.scores = scores; } public Date getBirthday() { return birthday; } public void setBirthday(Date birthday) { this.birthday = birthday; } public String getGender() { return gender; } public void setGender(String gender) { this.gender = gender; } @Override public String toString() { return "Student [name=" + name + ", height=" + height + ", from=" + from + ", weight=" + weight + ", scores=" + scores + ", birthday=" + birthday + ", gender=" + gender + "]"; } }

 

你可能感兴趣的:(Java)