开始以前先摘抄一些概念,java
流的操做类型分为两种:数组
Intermediate:一个流能够后面跟随零个或多个 intermediate 操做。其目的主要是打开流,作出某种程度的数据映射/过滤,而后返回一个新的流,交给下一个操做使用。这类操做都是惰性化的(lazy),就是说,仅仅调用到这类方法,并无真正开始流的遍历。安全
Terminal:一个流只能有一个 terminal 操做,当这个操做执行后,流就会被,没法再被操做。因此这一定是流的最后一个操做。Terminal 操做的执行,才会真正开始流的遍历,而且会生成一个结果,或者一个 side effect。app
short-circuitingide
对于一个无限大(infinite/unbounded)的 Stream,返回有限的新的Stream或者能在有限的时间内返回结果性能
经过以上的概念,能够归类以下的方法ui
Intermediate:this
map (mapToInt, flatMap 等)、 filter、 distinct、 sorted、 peek、 limit、 skip、 parallel、 sequential、 unordered线程
Terminal:code
forEach、 forEachOrdered、 toArray、 reduce、 collect、 min、 max、 count、 anyMatch、 allMatch、 noneMatch、 findFirst、 findAny、 iterator
Short-circuiting:
anyMatch、 allMatch、 noneMatch、 findFirst、 findAny、 limit
// 1. 从Collection中生成 List list = new ArrayList<String>(){{add("A");add("B");}}; Stream<String> stream = list.stream(); // 2. 下面是从数组生成 int[] arr = new int[]{1,2,3}; IntStream stream = Arrays.stream(arr); // or Stream.of(1,2,3)
// 从BufferedReader中生成 java.io.BufferedReader.lines() // 经过一些静态的方法 java.util.stream.IntStream.range() java.nio.file.Files.walk() java.nio.file.Files.lines()
自定义
经过java.util.Spliterator 能够本身构建,这里就不展开了,有兴趣的能够参考下面两篇文章
http://ifeve.com/java8-stream-%E4%B8%ADspliterator%E7%9A%84%E4%BD%BF%E7%94%A8%E4%BA%8C/
先构建一个对象,下面全部的方法会用到这个基础对象,以及构造一个简单的persons,供下文使用
class Person{ private String name; private Integer age; // 省略getter,setter方法 public Person(String name, Integer age){ this.name = name; this.age = age; } }
List<Person> persons = new ArrayList<>(); persons.add(new Person("jack",17)); persons.add(new Person("rose",18)); persons.add(new Person("小明",17));
map会对流中的每一个元素应用map中的lambda表达式
List<String> result = persons.stream().map( a -> a.getName().toUpperCase()).collect(Collectors.toList()); // result中的元素为 [JACK, ROSE, 小明]
上面的意思是取流中的每一个元素(这里就是Person),把name属性转为大写,最后的collect(Collectors.toList())就是上面说的Terminal操做,所以咱们能够获取到一个List
上面是一对一的操做,若是是一对多的,则可使用flatMap
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());
flatMap抽取出底层的元素放在一块儿,就是上面的例子中把三个list中的元素所有抽取出来,最后返回一个数字流
List<Person> collect = persons.stream() .filter(a -> a.getAge() > 17).collect(Collectors.toList()); // 过滤出流中全部age > 17的元素,返回一个新的流
filter接受一个Predicate(谓词)参数,返回符合为true的元素
顾名思义就是去重的操
List<Integer> integers = Arrays.asList(1, 1, 1); List<Integer> result = integers.stream().distinct().collect(Collectors.toList()); // 最终result里面的元素只有一个[1]
须要注意的是,每一个元素去重基于的 Object#equals(Object) 方法,在一些场合使用Collection中的removeIf()默认方法更加合适
Stream提供了 foreach 方法来遍历流中的每一个数据
persons.stream().forEach(a -> System.out.println(a.getName())); // 更简单的写法以下,由于List接口中有了一个forEach默认方法,关于接口默认方法这里就不阐述了 persons.forEach(a -> System.out.println(a.getName()));
sorted会对流中的元素作排序,默认是从小大到的顺序
List<Person> result = persons.stream().sorted(Comparator.comparing(Person::getAge)).collect(Collectors.toList());
若是能够Stream 进行各种 map、filter、limit、skip 甚至 distinct 来减小元素数量后,再排序,这序明显缩短程序执行时间
max/min操做相似,不过他们各自返回最大值和最小值,若是是对象都是int,long,double类型,可使用IntStream、LongStream、DoubleStream,避免拆箱装箱的性能消耗。这个时候可使用以下操做
OptionalLong max = persons.stream().mapToLong(Person::getAge).max(); System.out.println(max.getAsLong()); //输出 18
以前咱们说过流只能被terminal 操做一次,若是有相似的场景须要屡次terminal 操做,peek能够达到目的
// peek方法方法会使用一个Consumer消费流中的元素,可是返回的流仍是包含原来的流中的元素。 OptionalLong max = persons.stream().peek(a -> System.out.println(a.getName())).mapToLong(Person::getAge).max(); System.out.println(max.getAsLong());
// 流中全部的元素都知足谓词中的条件才返回true public boolean allMatch(Predicate<? super T> predicate) // 流中全部的元素有一个知足谓词中的条件就返回true public boolean anyMatch(Predicate<? super T> predicate) // 流中全部的元素都不知足谓词中的条件才返回true public boolean noneMatch(Predicate<? super T> predicate)
reduce方法返回单个的结果值,而且reduce操做每处理一个元素老是建立一个新值。reduce主要有以下三个方法
// 最经常使用的,identity参数是初始值,accumulator是累加器,其方法签名是 apply(T t,U u),累加的值会被赋值给下次执行方法的第一个参数,也就是t T reduce(T identity, BinaryOperator<T> accumulator); // 没有初始值,返回的是一个Optional,由于null是不安全的 Optional<T> reduce(BinaryOperator<T> accumulator); // 第三个参数是使用并行流(parallelStream)时,合并每一个线程的操做 <U> U reduce(U identity, BiFunction<U, ? super T, U> accumulator, BinaryOperator<U> combiner);
因此经常使用的方法有average, sum, min, max, count,使用reduce方法均可实现。
// 实现加法 Integer result = Stream.of(1, 2, 3).stream().reduce(0, (a, b) -> a + b); // result:6 // 实现减法 Integer result = Stream.of(1, 2, 3).reduce(10, (a, b) -> a - b); // result:10 - 6 = 4
collect能够把流收集起来,能够是一个List,Map以及分组等
List<Integer> result = Stream.of(1, 2, 3).collect(ArrayList::new, ArrayList::add, List::addAll); // result: [1,2,3]
代码看起来并不清真,也不容易理解。所以,还有另外的简便写法,下面会提到
Collectors实现了Collector接口,提供了不少有用的方法
List<Integer> result = Stream.of(1, 2, 3).collect(ArrayList::new, ArrayList::add, List::addAll); // 能够简写为 List<Integer> result = Stream.of(1, 2, 3).collect(Collectors.toList()); // 若是须要去重,可使用toSet Set<Integer> result = Stream.of(1, 1, 3).collect(Collectors.toSet());
// Function.identity() 的做用等同于 a -> a,即输入等于输出 Map<Integer, Person> collect = persons.stream().collect(Collectors.toMap(Person::getAge, Function.identity())); // 这里就要有坑了,若是key相同的话,会抛出异常,能够经过指定第三个参数指定合并方式 Map<Integer, Person> collect = persons.stream().collect(Collectors.toMap(Person::getAge, Function.identity(),(a,b) -> a)); // 这里咱们指定了若是有冲突,取以前的那个
觉得这样就没有坑了么?不对,若是在value为null的时候,还会抛出 NPE的异常,HashMap中的merge代码中有以下这一行。
因此,最好不要使用这个方法,使用以下的写法代替
Map<Integer,Person> map = new HashMap<>(); persons.forEach(a -> map.put(a.getAge(),a));