java8 中的 Stream's API


Stream’APi简介

  Stream’API是Java8中的新特性,基于Lambda表达式,对Collection(集合)的各种操作有了很大的改变,极大的提升了编码效率和代码的可读性。Stream有串行和并行两种模式,并行模式会自动创建多个线程,使用fork(join)Java7特性,来拆分任务和加速处理过程。Stream是一种类似IO流的东西,但是并不相同,实质是对集合操作的一种高度抽象,而且更重要的是,Stream不是数据结构,是不存储数据的,数据存储在底层的集合中,或者根据需要产生出来(例如Stream进行终端操作的时候,生成新的集合)。

Stream抽象概念

  Stream将要处理的数据看作一个流,流在管道中流动,可以在管道的节点上对流进行筛选,排序,聚合等操作,也就是数据流在管道中经过中间操作(intermediate operation),最后由终端操作(terminal operation)来得到前面处理过的数据,可以抽象的看作一种类似通过sql查询数据的方式。

Stream的特征

  • Stream不是数据结构,是不存储数据的;
  • Stream的构成数据源是集合、数组,I/O channel,generate等等;
  • Stream的中间操作会返还流对象本身,这样就形成了一个管道;
  • Stream的中间操作类似sql语句,比如filter(筛选), map(元素映射), reduce(聚合), find(查询), match(匹配), sorted(排序)等;
  • Stream的迭代跟for和foreach在集合外显式迭代不同,Stream是内部迭代,基于访问者模式(Visitor)实现;
  • Stream有串行和并行两种模式;

Stream用法

如何创建Stream流
  • stream() 为集合创建串行流。
  • parallelStream() 为集合创建并行流。
// 代码示例
// 集合创建流 Collection接口增加了Steam 方法()
List<String> listStr = Arrays.asList("java", "python", "shell", "javaScript");
Stream<String> steamStr = listStr.stream();
steamStr.forEach(str -> System.out.println(str));

Set<String> setStr = ZoneId.getAvailableZoneIds();
Stream<String> steamSetStr = setStr.stream();
steamSetStr.forEach(str -> System.out.println(str));

// 从数组创建流 Arrays类中增加stream(T[] array)方法
String[] strs = {"java", "python", "shell", "javaScript"};
Stream<String> stringStream = Arrays.stream(strs);

// 从静态方法中创建
Stream<String> staticStream1 = Stream.of("java", "python", "shell", "javaScript");
staticStream1.forEach(str -> System.out.println(str));

// 使用iterate静态方法(迭代器的方式)创建无限大小的流,需配合limit使用,防止内存溢出
// 会一直增加数据,没有上限
Stream<Integer> staticStream2 = Stream.iterate(0, x -> x + 1).limit(10);
staticStream2.forEach(integer -> System.out.println(integer));

// 使用generate静态方法创建无限大小的流,需要配合limit使用,防止内存溢出
// generate方式创建的无限流最大值是Long.MAX_VALUE
Random random = new Random();
Stream<Integer> staticStream3 = Stream.generate(() -> random.nextInt()).limit(10);
staticStream3.forEach(integer -> System.out.println(integer));

// 其他方式
java.io.BufferedReader.lines()
java.util.stream.IntStream.range()
java.nio.file.Files.walk()
java.util.Spliterator
Random.ints()
BitSet.stream()
Pattern.splitAsStream(java.lang.CharSequence)
JarFile.stream()
Stream流的中间操作

该表的作用是Stream流中间操作方法的参数类型

函数式接口名 作用
Function<T, R> 接受一个参数T,返回结果R
Predicate 接受一个参数T,返回boolean
Supplier 不接受任何参数T,返回结果T
Consumer 接受一个参数T,不返回结果
UnaryOperator 继承自Function<T,T>,返回相同类型T的结果
BiFunction<T, U, R> 接收两个参数T,U,返回结果R
BinaryOperator 继承自BiFunction<T,T,T>,返回相同类型T的结果
Runnable 实际上不接受任何参数,也不返回结果
Comparable 实际上是接受两个相同类型的T,返回int
Callable 不接受任何参数,返回结果V
过滤操作
  • filter() 将符合条件的所有元素(数据)转移到新流中。filter的参数类型是Predicate
// 代码示例
List<String> listStr = Arrays.asList("java", "python", "shell", "javaScript");
Stream<String> steamStr = listStr.stream();
steamStr.filter(s -> s.startsWith("j")).forEach(s -> System.out.println(s));
转换操作
  • map() 将所有数据经过处理之后(可以改变对象类型)转移到新流中。map的参数类型是Function<T,R>
  • flatMap(Function<? super T, ? extends Stream<? extends R>> mapper) 接收一个泛型参数,另一个为Stream流参数,返回的是泛型R,作用是将两个流合并成一个流输出
// 代码示例
List<String> listStr = Arrays.asList("java", "python", "shell", "javaScript");
Stream<String> steamStr = listStr.stream();
steamStr.map(s -> s.substring(2)).forEach(s -> System.out.println(s));

String[] strs = { "aaa", "bbb", "ccc" };
Arrays.stream(strs).map(str -> str.split("")).forEach(System.out::println);
// [Ljava.lang.String;@5cc7c2a6
// [Ljava.lang.String;@b97c004
// [Ljava.lang.String;@4590c9c3
Arrays.stream(strs).map(str -> str.split("")).flatMap(str -> Arrays.stream(str)).forEach(System.out::println);
// a/a/a/b/b/b/c/c/c

// map操作将strs拆分为三个数组,stream流中的元素由stream<String>变成了stream<String[]>
// flatMap操作将三个stream<String[]>流合并成一个stream<String>流
提取操作
  • skip(long n) 忽略流中的前n个元素(数据)
  • limit(long maxSize) 获取流中的maxSize个元素(数据)
// 代码示例
List<String> listStr = Arrays.asList("java", "python", "shell", "javaScript");
Stream<String> steamStr = listStr.stream();
steamStr.skip(2).forEach(s -> System.out.println(s));
steamStr.limit(2).forEach(s -> System.out.println(s));
去重操作
  • distinct() 去除流中重复的元素
// 代码示例
// 运行时记得注释其中stringStream执行语句否则会有异常
// java.lang.IllegalStateException: stream has already been operated upon or closed
Stream<String> stringStream = Arrays.asList("java", "python", "shell", "javaScript", "java").stream();
stringStream.distinct().forEach(s -> System.out.println(s));
stringStream.forEach(s -> System.out.println(s));
排序操作
  • sort() 将流中的元素(数据)排序
  • sort(Comparator<? super T> comparator) 将流中元素根据属性排序
// 代码示例
public class test {

    public static List<Emp> emps = new ArrayList<>(10);

    static {
        emps.add(new Emp("1", "xiaoHong1", 20));
        emps.add(new Emp("1", "xiaoHong2", 202));
        emps.add(new Emp("1", "xiaoHong3", 32));
        emps.add(new Emp("1", "xiaoHong4", 45));
        emps.add(new Emp("1", "xiaoHong5", 17));
        emps.add(new Emp("1", "xiaoHong6", 14));
        emps.add(new Emp("1", "xiaoHong7", 65));
        emps.add(new Emp("1", "xiaoHong8", 38));
    }

    public static class Emp {
        private String code;
        private String name;
        private int age;

        public Emp(String code, String name, int age) {
            this.code = code;
            this.name = name;
            this.age = age;
        }

        public String getCode() {
            return code;
        }

        public void setCode(String code) {
            this.code = code;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public int getAge() {
            return age;
        }

        public void setAge(int age) {
            this.age = age;
        }
    }

    /**
     * @param empList 输出集合中的元素
     */
    public static void println(List<Emp> empList) {
        empList.stream().forEach(emp -> System.out.println(String.format("编号 %S , 姓名 %s , 年龄 %s", emp.getCode(), emp.getName(), emp.getAge())));
    }

    /**
     * 根据对象中年龄进行排序
     */
    public static List<Emp> sortByAge(List<Emp> empList) {
        return empList.stream().sorted(Comparator.comparing(emp -> emp.getAge())).collect(Collectors.toList());
    }

    /**
     * 取出对象中姓名排序,并只返回包含姓名的集合
     */
    public static List<String> sortByName(List<Emp> empList) {
        return empList.stream().map(emp -> emp.getName()).sorted().collect(Collectors.toList());
    }

    public static void main(String[] args) {
        println(emps);
        println(sortByAge(emps));
        sortByName(emps).stream().forEach(s -> System.out.println(s));
    }
}
操作对象
  • peek(Consumer<? super T> action)
// 代码示例
    /**
     * @param empList 输出集合中的元素
     */
    public static void println(List<Emp> empList) {
        empList.stream().forEach(emp -> System.out.println(String.format("编号 %S , 姓名 %s , 年龄 %s", emp.getCode(), emp.getName(), emp.getAge())));
    }
    /**
     * 给年龄大于30岁增加十岁,并返回筛选后的集合
     */
    public static List<Emp> addAge(List<Emp> empList) {
        return empList.stream().filter(emp -> emp.getAge() > 30).peek(emp -> emp.setAge(emp.getAge() + 10)).collect(Collectors.toList());
    }

    public static void main(String[] args) {
        println(addAge(emps));
    }
聚合操作
  • reduce方法
- reduce(BinaryOperator<T> accumulator)
接收一个参数,且参数类型为函数式接口的计算规则,初始值是List集合的第一个值

- reduce(T identity, BinaryOperator<T> accumulator)
参数类型为函数式接口的计算规则,第一参数值是初始值,第二个参数是函数式接口的计算规则

- reduce(U identity, BiFunction<U, ? super T, U> accumulator, BinaryOperator<U> combiner)
在多线程中使用的,具体不知道(https://segmentfault.com/q/1010000004944450)

// 代码示例
// 将流中元素归约为一个值
Random random = new Random();
List<Integer> randomList = Stream.generate(() -> random.nextInt(10)).limit(10).collect(Collectors.toList());
randomList.stream().forEach(integer -> System.out.println(integer));
System.out.println(randomList.stream().reduce((a, b) -> a + b).get());
System.out.println(randomList.stream().reduce(1,(a, b) -> a + b).longValue());

List<Integer> iterateList = Stream.iterate(0, x -> x + 1).limit(10).collect(Collectors.toList());
iterateList.stream().forEach(integer -> System.out.println(integer));
System.out.println(iterateList.stream().reduce((a, b) -> a - b).get());
System.out.println(iterateList.stream().reduce(1,(a, b) -> a - b).longValue());
  • collect方法
    ```java

  • collect(Supplier supplier,BiConsumer<R, ? super T> accumulator,BiConsumer<R, R> combiner);
    传入三个参数的抽象方法

  • collect(Collector<? super T, A, R> collector);
    该方法只有一个参数,这个参数先看下stream中的collect操作Collectors静态工厂类,在这个静态工厂类里面,大多都使用三个参数的collect方法实现的。
    ```
    具体的实现类方法

    Collectors静态工厂类
    Collectors静态工厂类

// 代码示例
public class test {

    public static List<Emp> emps = new ArrayList<>(10);

    static {
        emps.add(new Emp("1", "xiaoHong1", 20));
        emps.add(new Emp("1", "xiaoHong2", 202));
        emps.add(new Emp("2", "xiaoHong3", 32));
        emps.add(new Emp("1", "xiaoHong4", 45));
        emps.add(new Emp("2", "xiaoHong5", 17));
        emps.add(new Emp("1", "xiaoHong6", 14));
        emps.add(new Emp("3", "xiaoHong7", 65));
        emps.add(new Emp("3", "xiaoHong8", 38));
    }

    public static class Emp {
        private String code;
        private String name;
        private int age;

        public Emp(String code, String name, int age) {
            this.code = code;
            this.name = name;
            this.age = age;
        }

        public String getCode() {
            return code;
        }

        public void setCode(String code) {
            this.code = code;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public int getAge() {
            return age;
        }

        public void setAge(int age) {
            this.age = age;
        }
    }

    public static void main(String[] args) {
        //转List
        List<String> nameList = emps.stream().map(emp -> emp.getName()).collect(Collectors.toList());
        //转Set
        Set<String> nameSet = emps.stream().map(emp -> emp.getName()).collect(Collectors.toSet());
        //转map,需指定key值,Function.identity()指的是当前对象本身
        Map<String, Emp> nameMap1 = emps.stream().collect(Collectors.toMap(emp -> emp.getName(), emp -> emp));
        Map<String, Emp> nameMap2 = emps.stream().collect(Collectors.toMap(emp -> emp.getName(), Function.identity()));

        //计算集合中元素个数
        long count = emps.stream().collect(Collectors.counting());

        //计算数据 summarizingInt summarizingDouble summarizingLong
        IntSummaryStatistics sum = emps.stream().collect(Collectors.summarizingInt(emp -> emp.getAge()));
        //平均数
        System.out.println(sum.getAverage());
        //总数
        System.out.println(sum.getCount());
        //最大值
        System.out.println(sum.getMax());
        //最小值
        System.out.println(sum.getMin());
        //求和
        System.out.println(sum.getSum());

        //连接字符串 前缀和后缀只会出现在整个字符串的首尾
        String nameStr1 = emps.stream().map(emp -> emp.getName()).collect(Collectors.joining());
        String nameStr2 = emps.stream().map(emp -> emp.getName()).collect(Collectors.joining("中间-"));
        String nameStr3 = emps.stream().map(emp -> emp.getName()).collect(Collectors.joining("中间-", "前缀*", "后缀&"));
        System.out.println(nameStr1);
        System.out.println(nameStr2);
        System.out.println(nameStr3);

        //最大值 maxBy 最小值 minBy
        Optional<Integer> maxAge = emps.stream().map(emp -> emp.getAge()).collect(Collectors.maxBy(Comparator.comparing(emp -> emp)));
        Optional<Integer> minAge = emps.stream().map(emp -> emp.getAge()).collect(Collectors.minBy(Comparator.comparing(emp -> emp)));
        System.out.println(maxAge.get());
        System.out.println(minAge.get());

        //聚合操作
        Optional<Integer> ageSum1 = emps.stream().map(emp -> emp.getAge()).collect(Collectors.reducing((x, y) -> x + y));
        Integer ageSum2 = emps.stream().map(emp -> emp.getAge()).collect(Collectors.reducing(1, (x, y) -> x + y));
        System.out.println(ageSum1.get());
        System.out.println(ageSum2);

        //分组操作 根据地址把原List进行分组
        Map<String, List<Emp>> mapGroup = emps.stream().collect(Collectors.groupingBy(emp -> emp.getCode()));
        mapGroup.forEach((s, empList) -> mapGroup.get(s).forEach(emp -> System.out.println(s + ":" + emp.getName())));

        //分区操作 需要根据类型进行 需要根据类型指定判断分区
        Map<Boolean, List<Emp>> mapPartitioning = emps.stream().collect(Collectors.partitioningBy(emp -> emp.getAge() > 20));
        mapPartitioning.forEach((b, empList) -> mapPartitioning.get(b).forEach(emp -> System.out.println(b + ":" + emp.getName())));

        List<String> listStr1 = Arrays.asList("java", "python", "shell", "javaScript");
        List<String> listStr2 = Arrays.asList("javaNew", "pythonNew", "shellNew", "javaScriptNew");
        Stream<String> steamSt1 = listStr1.stream();
        Stream<String> steamSt2 = listStr2.stream();
        Stream.concat(steamSt1, steamSt2).forEach(s -> System.out.println(s));
    }

}
stream的终端操作
对集合的流进行遍历
  • forEach(Consumer<? super T> action)
  • forEachOrdered(Consumer<? super T> action)
    // 代码示例
    // forEach遍历是无序的遍历
    // forEach遍历是按照元素的在流中的顺序进行遍历
    List<String> strList = Arrays.asList("aaa","bbb","ccc");
    strList.parallelStream().forEach(str-> System.out.println(str));
    System.out.println();
    strList.parallelStream().forEachOrdered(str-> System.out.println(str));
    
将流转换为数组
  • toArray(IntFunction<A[]> generator)
  • toArray()
// toArray()方法的底层调用的还是toArray(IntFunction<A[]> generator)方法
// 代码示例
List<String> strList = Arrays.asList("aaa", "bbb", "ccc");
Object[] o = strList.stream().toArray();
String[] s = strList.stream().toArray(str -> new String[strList.size()]);
//将集合转换为数组进行输出
Arrays.stream(o).forEach(o1 -> System.out.println(o1));
Arrays.stream(s).forEach(s1 -> System.out.println(s1));
流的长度及流中元素(数据)比较
  • long count() 计算流的长度
  • boolean anyMatch(Predicate<? super T> predicate) 判断条件中,流中任意一个元素符合,返回true
  • boolean allMatch(Predicate<? super T> predicate) 判断条件中,流中所有元素都符合,返回true
  • boolean noneMatch(Predicate<? super T> predicate) 判断条件中,流中任意一个元素都不符合,返回true
// 代码示例
public class test {

    public static List<Emp> emps = new ArrayList<>(10);

    static {
        emps.add(new Emp("1", "xiaoHong1", 20));
        emps.add(new Emp("1", "xiaoHong2", 202));
        emps.add(new Emp("1", "xiaoHong3", 32));
        emps.add(new Emp("1", "xiaoHong4", 45));
        emps.add(new Emp("1", "xiaoHong5", 17));
        emps.add(new Emp("1", "xiaoHong6", 14));
        emps.add(new Emp("1", "xiaoHong7", 65));
        emps.add(new Emp("1", "xiaoHong8", 38));
    }

    public static class Emp {
        private String code;
        private String name;
        private int age;

        public Emp(String code, String name, int age) {
            this.code = code;
            this.name = name;
            this.age = age;
        }

        public String getCode() {
            return code;
        }

        public void setCode(String code) {
            this.code = code;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public int getAge() {
            return age;
        }

        public void setAge(int age) {
            this.age = age;
        }
    }

    public static void main(String[] args) {
        System.out.println(emps.stream().filter(emp -> emp.getAge() > 30).count());
        // 5
        System.out.println(emps.stream().allMatch(emp -> emp.getAge() > 30));
        // false
        System.out.println(emps.stream().anyMatch(emp -> emp.getName().startsWith("xiao")));
        // true
        System.out.println(emps.stream().noneMatch(emp -> emp.getAge() < 10));
        // false
    }
}

文章借鉴处


文章作者: Huowy
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Huowy !
评论
  目录