- Stream关注的是对数据的运算,与CPU打交道;集合关注的是数据的存储,与内存打交道
- Stream主要是用来对容器(集合、Map、数组、Optional)进行操作。Stream通过流水线的方式对容器类数据进行内部迭代处理。
- 使用Stream API可以对内存中的数据进行过滤、排序、映射、 归约等操作、类似于SQL对数据库表的相关操作
流的特点:
- Stream不会存储元素
- Stream不会改变源对象。它会返回一个持有结果的新的Stream
- Stream操作是延迟执行的,即它会等到真正需要结果的时候才执行
- 只能遍历一次 :可以把流想象成一条流水线,流水线的源头是数据源,数据源中的元素依次被输送到流水线上,我们可以在流水线上对元素进行各种操作。一旦元素走到了流水线的另一头,那么这些元素就被“消费掉了”,我们无法再对这个流进行操作。当然,我们可以从数据源那里再获得一个新的流重新遍历一遍。
- 采用内部迭代方式 :若要对集合进行处理,则需我们手写处理代码,这就叫做外部迭代。而要对流进行处理,我们只需告诉流我们需要什么结果,处理过程由流自行完成,这就称为内部迭代。 集合操作非常麻烦,若要对集合进行筛选、投影,需要写大量的代码,而流是以声明的形式操作集合,它就像SQL语句,我们只需告诉流需要对集合进行什么操作,它就会自动进行操作,并将执行结果交给你,无需我们自己手写代码。流的集合操作对我们来说是透明的,我们只需向流下达命令,它就会自动把我们想要的结果给我们。由于操作过程完全由Java处理,因此它可以根据当前硬件环境选择最优的方法处理,我们也无需编写复杂又容易出错的多线程代码了。
流的操作分为中间操作和终端操作两种:
- 中间操作:在执行的时候会返回一个流,多个中间操作可以串连起来形成一条流水线,在遇到终端操作的时候,一次性全部处理。 常见的中间操作有: map (mapToInt, flatMap 等)、 filter、 distinct、 sorted、 peek、 skip、 parallel、 sequential、 unordered……
- 终端操作:会从流水线中返回一个执行结果。 常见的终端操作有:forEach、 forEachOrdered、 toArray、 reduce、 collect、 min、 max、 count、iterator、anyMatch、 allMatch、 noneMatch、 findFirst、 findAny、 limit……
Stream的使用步骤:
- 准备一个数据源:数据源可以是集合、数组、Options
- 创建Stream对象
- 执行中间操作:中间操作可以有多个,它们可以串连起来形成流水线。
- 执行终端操作:执行终端操作后本次流结束,将获得一个执行结果。
public static void main(String[] args) {
//通过集合创建
List depts = DB.getDepts();
Stream stream1 = depts.stream();//返回一个顺序流
Stream stream2 = depts.parallelStream();//返回一个并行流
//通过数组创建
String[] array = {"zhangsan","lisi","wanger","maizi"};
Stream stream3 = Arrays.stream(array);
//通过Stream的of方法创建
Stream stream4 = Stream.of("zhangsan", "lisi", "wanger", "mazi");
//创建无限流
Stream stream5 = Stream.iterate(0, item -> item + 2).limit(10);
}
示例2:串行流转并行流
public static void main(String[] args) {
List list =Arrays.asList(1,8,3,2,10,4,9,7,6);
//获取并行流
Stream parallelStream = list.parallelStream();
//获取串行流
Stream stream = list.stream();
//串行流转并行流
Stream parallelStream2 = stream.parallel();
}
示例3:Stream转集合或数组
public static void main(String[] args) {
Stream stream = Stream.of("aa", "bb", "cc", "dd");
//转换为List
List list = stream.collect(Collectors.toList());
//转换为Set
Set set = stream.collect(Collectors.toSet());
//转换为数组
String[] array = stream.toArray(String[]::new);
}
示例3:Stream转字符串
public static void main(String[] args) {
final String[] array = {"zhangsan", "lisi", "wanger", "mazi"};
final String res1 = Stream.of(array).collect(Collectors.joining(""));
System.out.println(res1);
final Optional res2 = Stream.of(array).reduce(String::concat);
System.out.println(res2.get());
}
结果:
- map:接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
- flatMap:接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。 flatMap通过一个 Function 把一个元素类型为 T 的流转换成元素类型为 R 的流,再把这些转换之后的流合并。 flatMap会把Stream中的层级结构扁平化,就是将最底层元素抽出来放到一起,最终输出的新 Stream 里面已经没有 List 了,都是直接的元素。 示例1:
结果:public static void main(String[] args) { List list = Arrays.asList("11,22,33", "aa,bb,cc"); //将每个元素转成一个新的且不带逗号的元素 list.stream() .map(s -> s.replaceAll(",", ".")) .forEach(System.out::println); //将每个元素转换成一个stream list.stream() .flatMap(item->Arrays.stream(item.split(","))) .forEach(System.out::println); }
示例2:
结果:public static void main(String[] args) { Stream.of("aa","bb","cc") .map(String::toUpperCase) .forEach(item-> System.out.print(item+"\t")); System.out.println(); Stream.of(4,9,16) .map(Math::sqrt) .forEach(item-> System.out.print(item+"\t")); System.out.println(); Stream.of(Arrays.asList("aa","bb"),Arrays.asList("11","22")) .map(item->item.stream()) .forEach(System.out::println); Stream.of(Arrays.asList("aa", "bb"), Arrays.asList("11", "22")) .map(item -> item.stream()) .forEach(item->item.forEach(System.out::print)); System.out.println(); Stream.of(Arrays.asList("aa","bb"),Arrays.asList("11","22")) .flatMap(item->item.stream()) .forEach(System.out::println); }
示例:一段非常有用的代码
public static void main(String[] args) {
List res = List.of("ACCOUNTING", "RESEARCH")
.stream()
.map(temp -> new Dept(10, temp, temp))
.collect(Collectors.toList());
System.out.println(res);
List list = List.of(new Dept(10, "ACCOUNTING", "NEWYORK"), new Dept(20, "RESEARCH", "DALLAS"))
.stream()
.map(temp -> temp.getDname())
.collect(Collectors.toList());
System.out.println(list);
}
4.2、排序:sorted
-
sorted():自然排序,流中元素需实现Comparable接口
-
sorted(Comparator com):定制排序,可以自定义Comparator排序器
进行排序的类必须实现Comparable类,否则会报错cannot be cast to class java.lang.Comparable:
示例:
public static void main(String[] args) { List depts = DB.getDepts(); depts.stream().map(Dept::getDname).sorted().forEach(System.out::println); depts.stream().sorted((d1,d2)->{ //不要求Dept类实现omparable接口 if(d1.getDname().equals(d2.getDname())) { return d1.getLoc().compareTo(d2.getLoc()); }else { return d1.getDname().compareTo(d2.getDname()); } }).forEach(System.out::println); depts.stream().sorted().forEach(System.out::println); //要求Dept类必须得实现Comparable接口 //----------- ① }
结果:
编号①处必须得实现Comparable接口的Dept类的源代码:
public class Dept implements Comparable{ private Integer deptno; private String dname; private String loc; //……gettter/setter、默认构造方法、全参构造方法、toString()方法 @Override public int compareTo(Dept o) { return this.getDname().compareTo(o.getDname()); } }
- filter:过滤流中的某些元素,只保留满足由 Predicate 所指定的条件的元素。
- limit(n):截断流使其最多只包含指定数量的元素。
- skip(n):返回一个新的流,并跳过原始流中的前 N 个元素。配合limit(n)可实现分页
- distinct:通过方法hashCode() 和 equals() 来删除流中重复的元素 示例:调用顺序不一样,结果就不一样。
结果:public static void main(String[] args) { Stream.of("zhangsan","lisi","wanger","mazi","wanger","qianwu","wanger","zhaoliu","wanger") .filter(item->item.length()!=4) .skip(2) .distinct() .limit(3) .forEach(System.out::println); Stream.of("zhangsan","lisi","wanger","mazi","wanger","qianwu","wanger","zhaoliu","wanger") .filter(item->item.length()!=4) .skip(2) .limit(3) .distinct() .forEach(System.out::println); }
返回的流与原始流相同。当原始流中的元素被消费时,会首先调用 peek 方法中指定的 Consumer 实现对元素进行处理。 peek和map类似,但map接收的是一个Function表达式,有返回值;而peek接收的是Consumer表达式,没有返回值。
示例:
public static void main(String[] args) {
Dept dept = new Dept(30, "SALES", "CHICAGO");
Stream.of(dept)
.map(item->item.getDname())
.forEach(System.out::println);
Stream.of(dept)
.peek(item->item.setDname("AAA"))
.forEach(System.out::println);
Stream.of(dept)
.peek(item->item.setDname("AAA"))
.map(item->item.getDname())
.forEach(System.out::println);
}
结果:
从原始流起始位置开始删除满足指定 Predicate 的元素,直到遇到第一个不满足 Predicate 的元素。
4.10、takeWhile从原始流起始位置开始保留满足指定 Predicate 的元素,直到遇到第一个不满足 Predicate 的元素。
5、结束操作 5.1、匹配、聚合操作- allMatch:接收一个 Predicate 函数,当流中每个元素都符合该断言时才返回true,否则返回false
- noneMatch:接收一个 Predicate 函数,当流中每个元素都不符合该断言时才返回true,否则返回false
- anyMatch:接收一个 Predicate 函数,只要流中有一个元素满足该断言则返回true,否则返回false
- findFirst:返回流中第一个元素
- findAny:返回流中的任意元素
- count:返回流中元素的总个数
- max:返回流中元素最大值
- min:返回流中元素最小值 示例:
public static void main(String[] args) {
List list = Arrays.asList(18, 22, 3, 2, 15,7);
boolean allMatch = list.stream().allMatch(e -> e > 13);
System.out.println(allMatch);
boolean noneMatch = list.stream().noneMatch(e -> e > 13);
System.out.println(noneMatch);
boolean anyMatch = list.stream().anyMatch(e -> e > 13);
System.out.println(anyMatch);
Optional firstOpt = list.stream().findFirst();
System.out.println(firstOpt.get());
Integer findAny = list.stream().findAny().get();
System.out.println(findAny);
long count = list.stream().count();
System.out.println(count);
Optional maxtOpt = list.stream().max(Integer::compareTo);
System.out.println(maxtOpt.get());
Integer min = list.stream().min(Integer::compareTo).get();
System.out.println(min);
}
结果:
-
Optional reduce(BinaryOperator accumulator): 可以将流中元素反复结合起来,返回Optional< T >。 第一次执行时,accumulator函数的第一个参数为流中的第一个元素,第二个参数为流中元素的第二个元素;第二次执行时,第一个参数为第一次函数执行的结果,第二个参数为流中的第三个元素;依次类推。 示例:
public static void main(String[] args) { List list = Arrays.asList(1,3,2,6,8,3,9); Optional reduce = list.stream().reduce((x, y) -> x + y); System.out.println(reduce.get()); // 32 }
-
T reduce(T identity, BinaryOperator accumulator) 可以将流中元素反复结合起来得到一个值,返回T。 第一次执行时,accumulator函数的第一个参数为identity,而第二个参数为流中的第一个元素。 示例1:
public static void main(String[] args) { List list = Arrays.asList(1, 3, 2, 6, 8, 3, 9); Integer reduce = list.stream().reduce(10, (x, y) -> x + y); System.out.println(reduce); //42 }
示例2:
public static void main(String[] args) { List list = Arrays.asList(1, 2, 3, 4); Optional reduce = list.stream() .map(item -> item * 10) .reduce((t1, t2) -> t1 + t2); System.out.println(reduce.get()); //100 Integer res = list.stream().reduce(100, (i1, i2) -> i1 * i2); System.out.println(res); //2400 }
-
< U> U reduce(U identity,BiFunction accumulator,BinaryOperator< U> combiner)
- 在串行流(stream)中,该方法跟第二个方法一样,即第三个参数combiner不会起作用。
- 在并行流(parallelStream)中,流被fork join出多个线程进行执行,此时每个线程的执行流程就跟第二个方法reduce(identity,accumulator)一样,而第三个参数combiner函数,则是将每个线程的执行结果当成一个新的流,然后使用第一个方法reduce(accumulator)流程进行规约。 示例:
public static void main(String[] args) { List list = Arrays.asList(1, 2, 3, 4); Integer res1 = list.stream().reduce(0, (x1, x2) -> { System.out.println("accumulator: x1:" + x1 + " x2:" + x2); return x1 - x2; }, (x1, x2) -> { System.out.println("combiner: x1:" + x1 + " x2:" + x2); return x1 * x2; }); System.out.println(res1); // -10 Integer res2 = list.parallelStream().reduce(0, (x1, x2) -> { System.out.println("accumulator: x1:" + x1 + " x2:" + x2); return x1 - x2; }, (x1, x2) -> { System.out.println("combiner: x1:" + x1 + " x2:" + x2); return x1 * x2; }); System.out.println(res2); // -24 }
结果:
将流转换为其他形式,接收一个Collector接口的实现,用于给Stream中元素做汇总的方法
collect方法的定义如下:
- R collect(Collector
关注打赏
最近更新
- 深拷贝和浅拷贝的区别(重点)
- 【Vue】走进Vue框架世界
- 【云服务器】项目部署—搭建网站—vue电商后台管理系统
- 【React介绍】 一文带你深入React
- 【React】React组件实例的三大属性之state,props,refs(你学废了吗)
- 【脚手架VueCLI】从零开始,创建一个VUE项目
- 【React】深入理解React组件生命周期----图文详解(含代码)
- 【React】DOM的Diffing算法是什么?以及DOM中key的作用----经典面试题
- 【React】1_使用React脚手架创建项目步骤--------详解(含项目结构说明)
- 【React】2_如何使用react脚手架写一个简单的页面?