1. 为什么需要 Stream
Stream 作为 Java 8 的一大亮点,它与 java.io 包里的 InputStream 和 OutputStream 是完全不同的概念。它也不同于 StAX 对 XML 解析的 Stream,也不是 Amazon Kinesis 对大数据实时处理的 Stream。Java 8 中的 Stream 是对集合(Collection)对象功能的增强,它专注于对集合对象进行各种非常便利、高效的聚合操作(aggregate operation),或者大批量数据操作 (bulk data operation)。Stream API 借助于同样新出现的 Lambda 表达式,极大的提高编程效率和程序可读性。同时它提供串行和并行两种模式进行汇聚操作,并发模式能够充分利用多核处理器的优势,使用 fork/join 并行方式来拆分任务和加速处理过程。通常编写并行代码很难而且容易出错, 但使用 Stream API 无需编写一行多线程的代码,就可以很方便地写出高性能的并发程序。
2. 常见的stream接口继承关系图
图中4种stream接口继承自BaseStream,其中IntStream, LongStream, DoubleStream对应三种基本类型(int, long, double,注意不是包装类型),Stream对应所有剩余类型的stream视图。为不同数据类型设置不同stream接口,可以1.提高性能,2.增加特定接口函数。
需要注意的是,IntStream, LongStream, DoubleStream 并不是Stream的子接口
疑问:为什么不把IntStream, LongStream, DoubleStream 设计成Stream的子接口呢?
原因:这些方法的名字虽然相同,但是返回类型不同,如果设计成父子接口关系,这些方法将不能共存,因为Java不允许只有返回类型不同的方法重载。
3. 如何得到一个stream?
stream并不是某种数据结构,它只是数据源的一种视图。这里的数据源可以是一个数组,Java容器或I/O channel等。正因为如此要得到一个stream通常不会手动创建,而是调用对应的工具方法。
3.1 获取stream方法
- 调用Collection.stream()方法
- 调用Collection.parallelStream()方法
- 调用Arrays.stream(T[] array)方法
- 直接使用Stream.of()方法就能从一组对象创建一个stream对象
4. stream和collections区别
虽然大部分情况下stream是容器调用Collection.stream()方法得到的,但stream和collections有以下不同:
- 无存储。 stream不是一种数据结构,它只是某种数据源的一个视图,数据源可以是一个数组,Java容器或I/O channel等。
- 为函数式编程而生。 对stream的任何修改都不会修改背后的数据源,比如对stream执行过滤操作并不会删除被过滤的元素,而是会产生一个不包含被过滤元素的新stream。
- 惰式执行。 stream上的操作并不会立即执行,只有等到用户真正需要结果的时候才会执行。
- 可消费性。 stream只能被“消费”一次,一旦遍历过就会失效,就像容器的迭代器那样,想要再次遍历必须重新生成。
5.操作stream类型
对stream的操作分为为两类,中间操作(intermediate operations)和结束操作(terminal operations), 二者特点是:
- 中间操作总是会惰式执行,调用中间操作只会生成一个标记了该操作的新stream,仅此而已。
- 结束操作会触发实际计算,计算发生时会把所有中间操作积攒的操作以pipeline的方式执行,这样可以减少迭代次数。计算完成之后stream就会失效。
区分中间操作和结束操作最简单的方法,就是看方法的返回值,返回值为stream的大都是中间操作,否则是结束操作。
6.操作stream接口方法
操作类型 |
接口方法 |
中间操作 |
concat()、distinct()、filter()、flatMap()、limit()、map()、peek()、skip()、sorted()、parallel()、sequential()、unordered() |
结束操作 |
allMatch()、anyMatch()、collect()、count()、findAny()、findFirst()、forEach()、forEachOrdered()、max()、min()、noneMatch()、reduce()、toArray() |
7.stream方法使用
stream跟函数接口关系非常紧密,没有函数接口stream就无法工作。【函数接口是指内部只有一个抽象方法的接口。通常函数接口出现的地方都可以使用Lambda表达式,所以不必记忆函数接口的名字。】
7.1 中间操作方法
1. concat() : 合并两个stream
签名:
static <T> Stream<T> concat(Stream<? extends T> a,tream<? extends T> b)
|
作用:
- 创建一个延迟连接的流,其元素是a的所有元素和b的所有元素。
- 如果两个输入流都是有序的,则对所得到的流进行排序。
- 如果任一输入流是并行的,则得到的流是平行的。
- 关闭结果流时,将调用两个输入流的关闭处理程序。
代码:
public static void main(String[] args) { Stream<String> language1 = Stream.of("PHP", "JAVA"); Stream<String> language2 = Stream.of("GO", "PYTHON"); Stream<String> concat = Stream.concat(language1, language2); List<String> collect = concat.collect(Collectors.toList()); System.out.println(collect); }
|
更多concat用例
2. distinct() : 去重
说明:
返回:返回由该流的不同元素(根据 Object.equals(Object) )组成的Stream。
代码:
List<String> strings = Arrays.asList("AA", "BB", "CC", "AA", "BB"); List<String> collect = strings.stream().distinct().collect(Collectors.toList()); System.out.println(collect);
|
3. filter() : 过滤
签名:
Stream<T> filter(Predicate<? super T> predicate)
|
返回:作用是返回一个只包含满足predicate条件元素的Stream
代码:
List<Integer> integers = Arrays.asList(10, 12, 71, 63, 921, 68, 121); List<Integer> collect = integers.stream().filter(integer -> integer > 100).collect(Collectors.toList()); System.out.println(collect);
|
4. flatMap() : 合并
签名:
<R> Stream<R> flatMap(Function<? super T,? extends Stream<? extends R>> mapper)
|
作用:把几个小的list转换到一个大的list
效果示意图:
代码:
ArrayList<List<Integer>> arrayLists = new ArrayList(){{ add(Arrays.asList(1, 2)); add(Arrays.asList(3, 4, 5)); }};
List<Integer> collect = arrayLists.stream().flatMap(pList -> pList.stream()).collect(Collectors.toList()); System.out.println(collect);
|
5. limit() : 截取(返回前N个元素)
签名:
Stream<T> limit(long maxSize)
|
作用:对一个Stream进行截断操作,获取其前N个元素,如果原Stream中包含的元素个数小于N,那就获取其所有的元素;
代码:
List<Integer> integers = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> collect1 = integers.stream().limit(3).collect(Collectors.toList()); System.out.println(collect1);
List<Integer> collect2 = integers.stream().limit(6).collect(Collectors.toList()); System.out.println(collect2);
|
6. skip() : 截取(返回不包含前N个元素)
签名:
作用:返回一个丢弃原Stream的前N个元素后剩下元素组成的新Stream,如果原Stream中包含的元素个数小于N,那么返回空Stream;
代码:
List<Integer> integers = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> collect1 = integers.stream().skip(3).collect(Collectors.toList()); System.out.println(collect1);
List<Integer> collect2 = integers.stream().skip(6).collect(Collectors.toList()); System.out.println(collect2);
|
7. sorted(): 排序
签名:
Stream<T> sorted()和Stream<T> sorted(Comparator<? super T> comparator)。
|
作用:返回排序后的Stream;
代码:对整型集合排序
List<Integer> integers = Arrays.asList(11, 42, 23, 74, 5);
List<Integer> collect = integers.stream().sorted().collect(Collectors.toList()); System.out.println("自然序排序后结果: " + collect + ""); List<Integer> collect2 = integers.stream().sorted(Comparator.reverseOrder()).collect(Collectors.toList()); System.out.println("逆序排序后结果: " + collect2 + "");
|
代码:对字符串集合排序
List<String> strings = Arrays.asList("PHP", "JAVA", "GO", "C", "PYTHON","LUA","ABC"); List<String> collect = strings.stream().sorted().collect(Collectors.toList()); System.out.println("自然序排序后结果 => " + collect + "");
List<String> collect1 = strings.stream().sorted(Comparator.reverseOrder()).collect(Collectors.toList()); System.out.println("自然序逆排序后结果 => " + collect1 + "");
List<String> collect2 = strings.stream().sorted((str1,str2)-> str1.length()- str2.length()).collect(Collectors.toList()); System.out.println("根据长度正序排序 => " + collect2 + "");
List<String> collect3 = strings.stream().sorted((str1,str2)-> str2.length()- str1.length()).collect(Collectors.toList()); System.out.println("根据长度逆序排序 => " + collect3 + "");
|
由上面例子,可以看出对字符串集合排序时,会发现简单使用sorted会有问题!!
8. map(): 递归处理
签名:
<R> Stream<R> map(Function<? super T,? extends R> mapper)。
|
作用:返回一个对当前所有元素执行mapper之后的结果组成的Stream。直观的说,就是对每个元素按照某种操作进行转换,转换前后Stream中元素的个数不会改变,但元素的类型取决于转换之后的类型;
代码
List<String> strings = Arrays.asList("PHP", "JAVA", "GO", "C", "PYTHON","LUA"); List<String> collect = strings.stream().map(s -> s.toLowerCase()).collect(Collectors.toList()); System.out.println("转小写 ==> " + collect + "");
List<Integer> integers = Arrays.asList(1, 2, 3, 4, 5); List<Integer> collect1 = integers.stream().map(integer -> integer * 2).collect(Collectors.toList()); System.out.println("元素都乘以2 ==> " + collect1 + "");
|
7.2 结束操作方法
1. allMatch(): 判断(都是)
签名:
boolean allMatch(Predicate<? super T> predicate)
|
作用:判断条件里的元素都满足,则返回true
代码
List<Integer> integers = Arrays.asList(1, 2, 3, 4, 5); boolean a = integers.stream().allMatch(integer -> integer > 10); System.out.println("所有元素都大于10 ==> " + a + "");
boolean b = integers.stream().allMatch(integer -> integer < 10); System.out.println("所有元素都小于10 ==> " + b + "");
|
2. anyMatch(): 判断(有一个是)
签名:
boolean anyMatch(Predicate<? super T> predicate)
|
作用:判断条件里的元素有一个满足,则返回true
代码
List<Integer> integers = Arrays.asList(1, 2, 3, 4, 5, 11); boolean a = integers.stream().anyMatch(integer -> integer > 10); System.out.println("所有元素都大于10 ==> " + a + "");
boolean b = integers.stream().anyMatch(integer -> integer < 10); System.out.println("所有元素都小于10 ==> " + b + "");
|
3. noneMatch(): 判断(都不是)
签名:
boolean noneMatch(Predicate<? super T> predicate)
|
作用:跟allMatch相反,判断条件里的元素都不满足,则返回true
代码
List<Integer> integers = Arrays.asList(1, 2, 3, 4, 5, 11); boolean a = integers.stream().noneMatch(integer -> integer > 10); System.out.println("所有元素都大于10 ==> " + a + "");
boolean b = integers.stream().noneMatch(integer -> integer < 10); System.out.println("所有元素都小于10 ==> " + b + "");
|
4. count(): 统计
签名:
作用:统计个数
代码
List<Integer> integers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 9, 10); long count = integers.stream().count(); System.out.println("总个数 ==> " + count + "");
long count1 = integers.stream().filter(integer -> integer > 5).count(); System.out.println("大于5的总个数 ==> " + count1 + "");
long count2 = integers.stream().filter(integer -> integer > 5).distinct().count(); System.out.println("大于5的去重后的总个数 ==> " + count2 + "");
|
5. max()、mix(): 最大和最小
签名:
Optional<T> max(Comparator<? super T> comparator)
Optional<T> min(Comparator<? super T> comparator)
|
作用:返回最大和最小
代码
List<Integer> integers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); Integer max = integers.stream().max(Comparator.comparing(Integer::valueOf)).get(); System.out.println(" 最大整数值 ==> " + max + ""); Integer min = integers.stream().min(Comparator.comparing(Integer::valueOf)).get(); System.out.println(" 最小整数值 ==> " + min + "");
List<String> strings = Arrays.asList("B", "E", "F", "G", "H","Z","A"); String maxs = strings.stream().max(Comparator.comparing(String::valueOf)).get(); System.out.println(" 最大字符串 ==> " + maxs + ""); String mins = strings.stream().min(Comparator.comparing(String::valueOf)).get(); System.out.println(" 最小字符串 ==> " + mins + "");
|
获取最大和最小对象
public class Main { public static void main(String[] args) { List<Employee> emps = new ArrayList<Employee>(); emps.add(new Employee("小明", 16)); emps.add(new Employee("张三", 28)); emps.add(new Employee("李四", 32)); Comparator<Employee> comparator = Comparator.comparing(Employee::getAge); Employee minObject = emps.stream().min(comparator).get(); Employee maxObject = emps.stream().max(comparator).get(); System.out.println("最小对象 = " + minObject); System.out.println("最大对象 = " + maxObject);
}
static class Employee { private String name; private int age;
public Employee(String name, int age) { setName(name); setAge(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() { StringBuilder str = null; str = new StringBuilder(); str.append(" Name:- " + getName() + " Age:- " + getAge()); return str.toString(); } } }
|
6. findAny() findFirst() 查找
签名:
Optional<T> findFirst()
Optional<T> findAny()
|
作用:
- findFirst: 返回集合的第一个对象
- findAny: 返回这个集合中,取到的任何一个对象
@注意:
1. 在串行的流中,findAny和findFirst返回的,都是第一个对象
2. 在并行的流中,findAny返回的是最快处理完的那个线程的数据
3. 在并行操作中,对数据没有顺序上的要求,那么findAny的效率会比findFirst要快的
代码
List<Integer> integers = Arrays.asList(10, 32, 23, 14, 51, 64, 72, 81, 59, 110); Object[] objects = integers.stream().sorted().toArray(); Integer findFirst = integers.stream().filter(integer -> integer > 15).sorted().findFirst().get(); Integer findAny = integers.stream().filter(integer -> integer > 15).sorted().findAny().get(); System.out.println("排序后 => " + Arrays.toString(objects) + ""); System.out.println("findFirst => " + findFirst + ""); System.out.println("findAny => " + findAny + "");
|
7. reduce() 生成一个符合的值
签名:
三种重写形式:
Optional<T> reduce(BinaryOperator<T> accumulator)
T reduce(T identity, BinaryOperator<T> accumulator)
<U> U reduce(U identity, BiFunction<U,? super T,U> accumulator, BinaryOperator<U> combiner)
|
说明:
reduce操作可以实现从一组元素中生成一个值,sum()、max()、min()、count()等都是reduce操作,将他们单独设为函数只是因为常用。
代码
List<Integer> integers = Arrays.asList(1,2,3,4,5);
Integer sum = integers.stream().reduce(Integer::sum).get(); System.out.println("求和 => " + sum + "");
Integer sum1 = integers.stream().reduce(100, (total, item) -> total + item); System.out.println("求和后再加100-A => " + sum1 + ""); Integer sum2 = integers.stream().reduce(100,Integer::sum); System.out.println("求和后再加100-B => " + sum2 + "");
Integer min = integers.stream().reduce(Integer::min).get(); System.out.println("最小值 => " + min + "");
Integer max = integers.stream().reduce(Integer::max).get(); System.out.println("最大值 => " + max + "");
Integer product = integers.stream().reduce((a, b) -> a * b).get(); System.out.println("相乘结果: => " + product + "");
Stream<String> stream = Stream.of("go", "php", "Java"); String lengthStr = stream.reduce((s1, s2) -> s1.length() >= s2.length() ? s1 : s2).get(); System.out.println("最长的单词: => " + lengthStr + "");
|
8. forEach() 遍历
签名:
void forEach(Consumer<? super E> action)
|
作用: 是对容器中的每个元素执行action指定的动作,也就是对元素进行遍历
代码
HashMap<String,Integer> gradeMap = new HashMap(4) {{ put("张三", 82); put("李四", 70); put("小明", 91); put("老王", 59); }};
gradeMap.entrySet().stream().forEach(item-> { System.out.println("name = " + item.getKey() + ", fraction = [ " + item.getValue() + " ]"); });
List<String> strings = Arrays.asList("张三", "李四", "小明", "老王"); strings.stream().forEach(s -> System.out.println("name = [ " + s + " ]"));
|
9. forEachOrdered() 遍历
签名:
void forEachOrdered(Consumer<? super T> action)
|
作用: 是对容器中的每个元素执行action指定的动作,也就是对元素进行遍历
@注意:
1. 在串行的流中,forEach和forEachOrdered遍历输出的结果一样。
2. 在并行的流中,forEach每次输出的结果不一致。
3. 在并行的流中,forEachOrdered每次输出的结果一致。
代码
List<Integer> strings = Arrays.asList(1, 2, 3, 4, 5); strings.stream().forEach(s -> System.out.println("串行流-forEach => " + s + " ")); strings.stream().forEachOrdered(s -> System.out.println("串行流-forEachOrdered => " + s + " "));
strings.parallelStream().forEach(s -> System.out.println("并行流-forEach => " + s + " ")); strings.parallelStream().forEachOrdered(s -> System.out.println("并行流-forEachOrdered => " + s + " "));
|
Java8 parallelStream浅析-知乎
10. toArray() 转成数组
签名:
<A> A[] toArray(IntFunction<A[]> generator)
|
作用: 将流转成数组
代码
List<Integer> integerList = Arrays.asList(1, 2, 3,4,5,6,7,8,9); Integer[] integers = integerList.stream().filter(integer -> integer % 3 == 0).toArray(Integer[]::new); System.out.println("整型集合过滤后的结果,toArray -> " + Arrays.toString(integers) + "");
List<String> stringsList = Arrays.asList("PHP", "GO", "JAVA", "PYTHON", "LUA"); String[] strings = stringsList.stream().filter(s -> s.length() > 3).toArray(String[]::new); System.out.println("字符串集合过滤后的结果,toArray -> " + Arrays.toString(strings) + "");
|
11. collect()
由于collect过于特殊和强大,拉出去单讲【collect() 和 Collectors(收集器) 】。
Java-基础篇-Collect方法详解