 
www.jianshu.com/p/11c925cdba50
相信Java8的Stream 大家都已听说过了,但是可能大家不会用或者用的不熟,文章将带大家从零开始使用,循序渐进,带你走向Stream的巅峰。
操作符
什么是操作符呢?操作符就是对数据进行的一种处理工作,一道加工程序;就好像工厂的工人对流水线上的产品进行一道加工程序一样。
 
Stream的操作符大体上分为两种:中间操作符和终止操作符
中间操作符
对于数据流来说,中间操作符在执行制定处理程序后,数据流依然可以传递给下一级的操作符。
中间操作符包含8种(排除了parallel,sequential,这两个操作并不涉及到对数据流的加工操作):
flatmap(flatmapToInt,flatmapToLong,flatmapToDouble) 拍平操作比如把 int[]{2,3,4} 拍平 变成 2,3,4 也就是从原来的一个数据变成了3个数据,这里默认提供了拍平成int,long,double的操作符。
distint 去重操作,对重复元素去重,底层使用了equals方法。
peek 挑出操作,如果想对数据进行某些操作,如:读取、编辑修改等。
sorted(unordered) 排序操作,对元素排序,前提是实现Comparable接口,当然也可以自定义比较器。
终止操作符
数据经过中间加工操作,就轮到终止操作符上场了;终止操作符就是用来对数据进行收集或者消费的,数据到了终止操作这里就不会向下流动了,终止操作符只能使用一次。
count 统计操作,统计最终的数据个数。
noneMatch、allMatch、anyMatch 匹配操作,数据流中是否存在符合条件的元素 返回值为bool 值。
reduce 规约操作,将整个数据流的值规约为一个值,count、min、max底层就是使用reduce。
toArray 数组操作,将数据流的元素转换成数组。
这里只介绍了Stream,并没有涉及到IntStream、LongStream、DoubleStream,这三个流实现了一些特有的操作符,我将在后续文章中介绍到。Java知音公众号内回复“面试题聚合”,送你一份各大公司面试汇总宝典。
说了这么多,只介绍这些操作符还远远不够;俗话说,实践出真知。那么,Let‘s go。
代码演练
Stream 的一系列操作必须要使用终止操作,否者整个数据流是不会流动起来的,即处理操作不会执行。
 
map操作将原来的单词 转换成了每个单的长度,利用了String自身的length()方法,该方法返回类型为int。这里我直接使用了lambda表达式,关于lambda表达式 还请读者们自行了解吧。
public class Main {
    public static void main(String[] args) {
        Stream.of("apple","banana","orange","waltermaleon","grape")
                .map(e-e.length()) //转成单词的长度 int
                .forEach(e-System.out.println(e)); //输出
    }
}
当然也可以这样,这里使用了成员函数引用,为了便于读者们理解,后续的例子中将使用lambda表达式而非函数引用。
public class Main {
    public static void main(String[] args) {
         Stream.of("apple","banana","orange","waltermaleon","grape")
                .map(String::length) //转成单词的长度 int
                .forEach(System.out::println);
    }
}
结果如图:
 
 
public class Main {
    public static void main(String[] args) {
         Stream.of("apple", "banana", "orange", "waltermaleon", "grape")
                .mapToInt(e - e.length()) //转成int
                .forEach(e - System.out.println(e));
    }
}
mapToInt如图:
 
public class Main {
    public static void main(String[] args) {
         Stream.of("apple", "banana", "orange", "waltermaleon", "grape")
                .mapToLong(e - e.length()) //转成long ,本质上是int 但是存在类型自动转换
                .forEach(e - System.out.println(e));
    }
}
mapToLong 如图:
 
public class Main {
    public static void main(String[] args) {
         Stream.of("apple", "banana", "orange", "waltermaleon", "grape")
                .mapToDouble(e - e.length()) //转成Double ,自动类型转换成Double
                .forEach(e - System.out.println(e));
    }
}
mapToDouble如图:
 
 
public class Main {
    public static void main(String[] args) {
        Stream.of("a-b-c-d","e-f-i-g-h")
                .flatMap(e-Stream.of(e.split("-")))
                .forEach(e-System.out.println(e));
    }
}
flatmap 如图:
 
limit 限制元素的个数,只需传入 long 类型 表示限制的最大数
public class Main {
    public static void main(String[] args) {
        Stream.of(1,2,3,4,5,6)
                .limit(3) //限制三个
                .forEach(e-System.out.println(e)); //将输出 前三个 1,2,3
    }
}
limit如图:
 
 
public class Main {
    public static void main(String[] args) {
        Stream.of(1,2,3,1,2,5,6,7,8,0,0,1,2,3,1)
                .distinct() //去重
                .forEach(e-System.out.println(e));
    }
}
distinct 如图:
 
public class Main {
    public static void main(String[] args) {
        Stream.of(1,2,3,1,2,5,6,7,8,0,0,1,2,3,1)
                .filter(e-e=5) //过滤小于5的
                .forEach(e-System.out.println(e));
    }
}
filter 如图:
 
public class Main {
    public static void main(String[] args) {
        User w = new User("w",10);
        User x = new User("x",11);
        User y = new User("y",12);
        Stream.of(w,x,y)
                .peek(e-{e.setName(e.getAge()+e.getName());}) //重新设置名字 变成 年龄+名字
                .forEach(e-System.out.println(e.toString()));
    }
    static class User {
        private String name;
        private int age;
        public User(String name, int age) {
            this.name = name;
            this.age = age;
        }
        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;
        }
        @Override
        public String toString() {
            return "User{" +
                    "name='" + name + ''' +
                    ", age=" + age +
                    '}';
        }
    }
}
peek 如图:
 
public class Main {
    public static void main(String[] args) {
        Stream.of(1,2,3,4,5,6,7,8,9)
                .skip(4) //跳过前四个
                .forEach(e-System.out.println(e)); //输出的结果应该只有5,6,7,8,9
    }
}
skip 如图:
 
这里Integer 实现了比较器
public class Main {
    public static void main(String[] args) {
        Stream.of(2,1,3,6,4,9,6,8,0)
                .sorted()
                .forEach(e-System.out.println(e));
    }
}
sorted 默认比较器如图:
 
这里使用自定义比较,当然User 可以实现Comparable 接口
public class Main {
    public static void main(String[] args) {
        User x = new User("x",11);
        User y = new User("y",12);
        User w = new User("w",10);
        Stream.of(w,x,y)
                .sorted((e1,e2)-e1.agee2.age?1:e1.age==e2.age?0:-1)
                .forEach(e-System.out.println(e.toString()));
    }
    static class User {
        private String name;
        private int age;
        public User(String name, int age) {
            this.name = name;
            this.age = age;
        }
        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;
        }
        @Override
        public String toString() {
            return "User{" +
                    "name='" + name + ''' +
                    ", age=" + age +
                    '}';
        }
    }
}
如图:
 
这里我使用collect 将元素收集到一个set中
public class Main {
    public static void main(String[] args) {
        Stream.of("apple", "banana", "orange", "waltermaleon", "grape")
                .collect(Collectors.toSet()) //set 容器
                .forEach(e - System.out.println(e));
    }
}
咦?,不是说终止操作符只能使用一次吗,为什么这里调用了forEach 呢?forEach不仅仅是是Stream 中得操作符还是各种集合中得一个语法糖,不信咋们试试。Java知音公众号内回复“面试题聚合”,送你一份各大公司面试汇总宝典。
public class Main {
    public static void main(String[] args) {
        SetString stringSet = Stream.of("apple", "banana", "orange", "waltermaleon", "grape")
                .collect(Collectors.toSet()); //收集的结果就是set
        stringSet.forEach(e-System.out.println(e)); set的语法糖forEach
}
结果如图:
 
public class Main {
    public static void main(String[] args) {
        long count = Stream.of("apple", "banana", "orange", "waltermaleon", "grape")
                .count();
        System.out.println(count);
    }
}
count 如图:
 
这里找到第一个元素 apple
public class FindFirst {
    public static void main(String[] args) {
        OptionalString stringOptional = Stream.of("apple", "banana", "orange", "waltermaleon", "grape")
                .findFirst();
        stringOptional.ifPresent(e-System.out.println(e));
    }
}
findFirst 结果如图:
 
public class FindAny {
    public static void main(String[] args) {
        OptionalString stringOptional = Stream.of("apple", "banana", "orange", "waltermaleon", "grape")
                .parallel()
                .findAny(); //在并行流下每次返回的结果可能一样也可能不一样
        stringOptional.ifPresent(e-System.out.println(e));
    }
}
findAny 在并行流下 使用结果:
输出了orange
 
输出了banana
 
这里 的作用是是判断数据流中 一个都没有与aa 相等元素 ,但是流中存在 aa ,所以最终结果应该是false
public class NoneMatch {
    public static void main(String[] args) {
        boolean result = Stream.of("aa","bb","cc","aa")
                .noneMatch(e-e.equals("aa"));
        System.out.println(result);
    }
}
noneMatch 如图:
 
min 最小的一个,传入比较器,也可能没有(如果数据流为空)
public class Main {
    public static void main(String[] args) {
        OptionalInteger integerOptional = Stream.of(0,9,8,4,5,6,-1)
                .min((e1,e2)-e1.compareTo(e2));
        integerOptional.ifPresent(e-System.out.println(e));
    }
min如图:
 
public class Main {
    public static void main(String[] args) {
        OptionalInteger integerOptional = Stream.of(0,9,8,4,5,6,-1)
                .max((e1,e2)-e1.compareTo(e2));
        integerOptional.ifPresent(e-System.out.println(e));
    }
}
max 如图:
 
这里实现了一个加法,指定了初始化的值
public class Main {
    public static void main(String[] args) {
        int sum = Stream.of(0,9,8,4,5,6,-1)
              .reduce(0,(e1,e2)-e1+e2);
        System.out.println(sum);
    }
}
reduce 如图:
 
forEach 其实前就已经见过了,对每个数据遍历迭代
这里通过并行的方式输出数字
public class ForEachOrdered {
    public static void main(String[] args) {
        Stream.of(0,2,6,5,4,9,8,-1)
                .parallel()
                .forEachOrdered(e-{
                    System.out.println(Thread.currentThread().getName()+": "+e);});
    }
}
forEachOrdered 如图:
 
public class ToArray {
    public static void main(String[] args) {
        Object[] objects=Stream.of(0,2,6,5,4,9,8,-1)
                .toArray();
        for (int i = 0; i  objects.length; i++) {
            System.out.println(objects[i]);
        }
    }
}
toArray 如图:
 
总结
Java8 Stream就带大家认识到这里,如果你能跟着我的文章把每一个例子都敲一遍,相信都能掌握这些操作符的初步用法。
END
Java面试题专栏
【83期】面试被问到了Redis和MongoDB的区别?看这里就对了
【84期】面试中设计模式能问些什么?比如说一下三种单例模式实现
【85期】谈谈Java面向对象设计的六大原则,中高级面试常问!
【87期】面试官问:Java序列化和反序列化为什么要实现Serializable接口
【88期】面试官问:你能说说 Spring 中,接口的bean是如何注入的吗?
【89期】面试官 5 连问一个 TCP 连接可以发多少个 HTTP 请求?
【90期】面试官:说一下使用 Redis 实现大规模的帖子浏览计数的思路
 
我知道你 “在看”
原文始发于微信公众号(Java知音):玩转Java8中的 Stream 之从零认识 Stream
 本人花费半年的时间总结的《Java面试指南》已拿腾讯等大厂offer,已开源在github ,欢迎star!
        本人花费半年的时间总结的《Java面试指南》已拿腾讯等大厂offer,已开源在github ,欢迎star!
     
                        
                        