JDK8 Stream
文章目录
- JDK8 Stream
- 概念
- 特点
- 示例
- 流程
- 常用操作符
- 具体用法
- 一、流的创建
- 1.1使用Collection下的 stream() 和 parallelStream() 方法。
- 1.2 使用Arrays 中的 stream() 方法,将数组转成流。
- 1.3 使用Stream中的静态方法:of()、iterate()、generate()。
- 1.4 使用 BufferedReader.lines() 方法,将每行内容转成流。
- 1.5 使用 Pattern.splitAsStream() 方法,将字符串分隔成流。
- 1.6 ……
- 二、Stream中间操作
- 1.filter:该方法用于通过设置的条件过滤出元素。
- 2.distinct:通过流中元素的 hashCode() 和 equals() 去除重复元素。
- 3.limit:获取流中的前n个元素。
- 4.skip:获取流中的除去前n个元素的其他元素。
- 5.peek:对每个元素执行操作并返回一个新的 Stream。
- 6.sorted:将流中的元素按照自然排序方式进行排序。
- 7.map:对流中的所有元素进行统一操作。
- 8.flatmap:对流扁平化处理。
- 三、Stream最终操作
- 1.forEach():遍历流中的每一个元素,执行顺序不一定按照流的顺序。
- 2.forEachOrddered():遍历流中的每一个元素,执行顺序按照流的顺序。
- 3.toArray():将流中的元素放入到一个数组中。
- 4.reduce():这个方法的主要作用是把 Stream 元素组合起来。它提供一个起始值,然后依照运算规则拼接或者运算。
- 5.collect():是Stream的一个函数,负责收集流,将流中的结果汇总。结果是由传入collect()中的Collector定义的。
- 6.min():返回流中的最小值。
- 7.max():返回流中的最大值。
- 8.count():返回流中元素的数量。
- 9.anyMatch():流中只要有一个元素符合传入的断言,就返回 true,否则返回false,如果流为空,永远返回false。
- 10.allMatch():流中所有元素都符合传入的断言时返回 true,否则返回false,流为空时总是返回true。
- 11.noneMatch():流中所有元素都不满足传入的断言时返回 true,否则返回false,流为空时总是返回true。
- 12.findFirst():返回Optional[流中第一个元素],使用`Optional对象.get()`可以获取流中第一个元素。若流为空则返回Optional.empty,使用`Optional对象.get()`会报NoSuchElementException异常。
- 13.findAny():返回Optional[流中的任意一个元素],若流为空则返回Optional.empty。
概念
Stream是JDK8 API的新成员,他允许以声明的方式处理数据集合。
Stream API可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。
特点
代码简洁
函数式编程写出的代码简介且意图明确,使用Stream接口让你从此告别for循环。
多核友好
Java函数式编程使得编写并行程序从未如此简单,你需要的全部就是调用一下方法。
示例
用给出的妹子数据源,按所在地分组,算出妹子的平均薪资
foreach方式
@Test
public void test1() {
List<Girl> girlList = new ArrayList<>();
girlList.add(new Girl("小红", 19, 176, "北京", 7500.0));
girlList.add(new Girl("小玲", 18, 165, "北京", 7000.0));
girlList.add(new Girl("小冬", 22, 173, "湖南", 6000.0));
girlList.add(new Girl("小星", 18, 172, "广西", 4500.0));
girlList.add(new Girl("小莹", 19, 166, "广东", 5300.0));
girlList.add(new Girl("小北", 20, 169, "广东", 6800.0));
/**
* 按所在地分组,算出妹子的平均薪资
*/
/**
* 按所在地分组后的 map:
* {
* 广东 = [Girl {
* name = '小莹', age = 19, height = 166, origin = '广东', salary = 5300.0
* }, Girl {
* name = '小北', age = 20, height = 169, origin = '广东', salary = 6800.0
* }],
* 广西 = [Girl {
* name = '小星', age = 18, height = 172, origin = '广西', salary = 4500.0
* }],
* 湖南 = [Girl {
* name = '小冬', age = 22, height = 173, origin = '湖南', salary = 6000.0
* }],
* 北京 = [Girl {
* name = '小红', age = 19, height = 176, origin = '北京', salary = 7500.0
* }, Girl {
* name = '小玲', age = 18, height = 165, origin = '北京', salary = 7000.0
* }]
* }
*/
//1.基于所在地分组
Map<String, List<Girl>> map = new HashMap<>();
for (Girl girl : girlList) {
List<Girl> list = map.computeIfAbsent(girl.getOrigin(), key -> new ArrayList<>());
list.add(girl);
}
//2.求出不同地方妹子的平均薪资
for (Map.Entry<String, List<Girl>> entry : map.entrySet()) {
Double sum = 0.0;
for (Girl girl : entry.getValue()) {
sum+=girl.getSalary();
}
System.out.println(entry.getKey()+"女孩的平均薪资是"+(sum/entry.getValue().size()));
}
}
运行结果:
广东女孩的平均薪资是6050.0
广西女孩的平均薪资是4500.0
湖南女孩的平均薪资是6000.0
北京女孩的平均薪资是7250.0
Process finished with exit code 0
Stream方式
@Test
public void test2() {
List<Girl> girlList = new ArrayList<>();
girlList.add(new Girl("小红", 19, 176, "北京", 7500.0));
girlList.add(new Girl("小玲", 18, 165, "北京", 7000.0));
girlList.add(new Girl("小冬", 22, 173, "湖南", 6000.0));
girlList.add(new Girl("小星", 18, 172, "广西", 4500.0));
girlList.add(new Girl("小莹", 19, 166, "广东", 5300.0));
girlList.add(new Girl("小北", 20, 169, "广东", 6800.0));
/**
* 按生源地分组,算出妹子的平均薪资
*/
girlList.stream().collect(Collectors.groupingBy(girl -> girl.getOrigin(), //1.基于所在地分组
Collectors.averagingDouble(girl -> girl.getSalary()))) //2.求出不同地方妹子的平均薪资
.forEach((k,v)-> System.out.println(k+"女孩的平均薪资是"+v));
}
运行结果:
广东女孩的平均薪资是6050.0
广西女孩的平均薪资是4500.0
湖南女孩的平均薪资是6000.0
北京女孩的平均薪资是7250.0
Process finished with exit code 0
流程
第一步:把数据源转换为Stream对象
第二步:操作Stream流对象
Stream流对象在管道中经过中间操作的处理,最终操作得到前面处理的结果。
操作特性
- Stream不存储数据:Stream不是一种数据结构,它只是某种数据源的一个视图,数据源可以是一个数组,Java容器或I/O channel等。
- Stream不改变数据源:Stream的任何修改都不会修改背后的数据源,比如对Stream执行过滤操作并不会删除被过滤的元素,而是会产生一个不包含被过滤元素的新Stream。
- Stream不可重复使用:Stream只能被“消费”一次,一旦遍历过就会失效,就像容器的迭代器那样,想要再次遍历必须重新生成。
常用操作符
方法 | 描述 | 类型 |
---|---|---|
filter() | 该方法用于通过设置的条件过滤出元素。 | 中间操作 |
distinct() | 通过流中元素的 hashCode() 和 equals() 去除重复元素。 | 中间操作 |
limit() | 获取流中的前n个元素。 | 中间操作 |
skip() | 获取流中的除去前n个元素的其他元素。 | 中间操作 |
sorted() | 它使用自然顺序对流的元素进行排序。 | 中间操作 |
peek() | 对每个元素执行操作并返回一个新的Stream。 | 中间操作 |
map() | 对流中的所有元素进行统一操作。 | 中间操作 |
flatmap() | 对流进行扁平化处理。 | 中间操作 |
forEach() | 遍历流中的每一个元素,执行顺序不一定按照流的顺序。 | 最终操作 |
forEachOredered() | 遍历流中的每一个元素,执行顺序按照流的顺序。 | 最终操作 |
toArray() | 将流中的元素放入到一个数组中。 | 最终操作 |
reduce() | 是把流元素组合起来。它提供一个起始值,然后依照运算规则拼接或者运算。 | 最终操作 |
collect() | 负责收集流,将流中的结果汇总。结果是由传入collect()中的Collector定义的。 | 最终操作 |
min() | 返回流中的最小值。 | 最终操作 |
max() | 返回流中的最大值。 | 最终操作 |
count() | 返回流中元素的数量。 | 最终操作 |
anyMatch() | 流中只要有一个元素符合传入的断言,就返回 true,否则返回false,如果流为空,永远返回false。 | 最终操作 |
allMatch() | 流中所有元素都符合传入的断言时返回 true,否则返回false,流为空时总是返回true。 | 最终操作 |
noneMatch() | 流中所有元素都不满足传入的断言时返回 true,否则返回false,流为空时总是返回true。 | 最终操作 |
findFirst() | 返回Optional[流中第一个元素],使用Optional对象.get() 可以获取流中第一个元素。若流为空则返回Optional.empty,使用Optional对象.get() 会报NoSuchElementException异常。 |
最终操作 |
findAny() | 返回Optional[流中的任意一个元素],若流为空则返回Optional.empty。 | 最终操作 |
具体用法
一、流的创建
1.1使用Collection下的 stream() 和 parallelStream() 方法。
@Test
public void create(){
List<String> stringList = Arrays.asList("ad", "", "cbx", "cbx", "", "abf", "ged");
Stream<String> stream = stringList.stream(); //获取一个串行流
Stream<String> parallelStream = stringList.parallelStream(); //获取一个并行流
}
串行流:用于主线程,单线程
并行流:用于多个线程同时运行
1.2 使用Arrays 中的 stream() 方法,将数组转成流。
@Test
public void create(){
String[] strings = new String[10];
Stream<String> stream = Arrays.stream(strings);
}
1.3 使用Stream中的静态方法:of()、iterate()、generate()。
@Test
public void create(){
Stream<String> stream1 = Stream.of("ad", "", "cbx", "cbx", "", "abf", "ged");
Stream stream2 = Stream.iterate(......);
Stream stream3 = Stream.generate(......);
}
1.4 使用 BufferedReader.lines() 方法,将每行内容转成流。
1.5 使用 Pattern.splitAsStream() 方法,将字符串分隔成流。
1.6 ……
二、Stream中间操作
1.filter:该方法用于通过设置的条件过滤出元素。
@Test
public void filter(){
List<String> stringList = Arrays.asList("ad","","cbx","cbx","","abf","ged");
List<String> filters = stringList.stream().filter(s -> s.contains("d")).collect(Collectors.toList());
System.out.println(filters);
}
运行结果:
[ad, ged]
Process finished with exit code 0
2.distinct:通过流中元素的 hashCode() 和 equals() 去除重复元素。
@Test
public void distinct(){
List<String> stringList = Arrays.asList("ad","","cbx","cbx","","abf","ged");
List<String> distinct = stringList.stream().distinct().collect(Collectors.toList());
System.out.println(distinct);
}
运行结果:
[ad, , cbx, abf, ged]
Process finished with exit code 0
3.limit:获取流中的前n个元素。
@Test
public void limit(){
List<String> stringList = Arrays.asList("ad","","cbx","cbx","","abf","ged");
List<String> limited = stringList.stream().limit(3).collect(Collectors.toList());
System.out.println(limited);
}
运行结果:
[ad, , cbx]
Process finished with exit code 0
4.skip:获取流中的除去前n个元素的其他元素。
@Test
public void skip(){
List<String> stringList = Arrays.asList("ad","","cbx","cbx","","abf","ged");
List<String> skipped = stringList.stream().skip(3).collect(Collectors.toList());
System.out.println(skipped);
}
运行结果:
[cbx, , abf, ged]
Process finished with exit code 0
5.peek:对每个元素执行操作并返回一个新的 Stream。
@Test
public void peek(){
List<String> stringList = Arrays.asList("ad", "", "cbx", "cbx", "", "abf", "ged");
stringList.stream().filter(e -> e.contains("a"))
.peek(e -> System.out.println("Filtered value: " + e))
.map(e -> e.toUpperCase())
.peek(e -> System.out.println("Mapped value: " + e))
.collect(Collectors.toList());
}
运行结果:
Filtered value: ad
Mapped value: AD
Filtered value: abf
Mapped value: ABF
Process finished with exit code 0
6.sorted:将流中的元素按照自然排序方式进行排序。
@Test
public void sorted(){
List<String> stringList = Arrays.asList("ad", "", "cbx", "cbx", "", "abf", "ged");
List<String> sorted = stringList.stream().sorted().collect(Collectors.toList());
System.out.println(sorted);
}
运行结果:
[, , abf, ad, cbx, cbx, ged]
Process finished with exit code 0
7.map:对流中的所有元素进行统一操作。
@Test
public void map() {
List<String> stringList = Arrays.asList("ad", "", "cbx", "cbx", "", "abf", "ged");
List<String> map = stringList.stream().map(s -> s.concat("@")).collect(Collectors.toList());
System.out.println(map);
}
运行结果:
[ad@, @, cbx@, cbx@, @, abf@, ged@]
Process finished with exit code 0
8.flatmap:对流扁平化处理。
@Test
public void flatmap(){
String[] words = new String[]{"abc","ABC"};
Arrays.stream(words)
.map(word -> word.split(""))
.collect(Collectors.toList())
.forEach(s -> System.out.println(s));
Arrays.stream(words)
.map(word -> word.split(""))
.flatMap(m -> Arrays.stream(m))
.collect(Collectors.toList())
.forEach(s -> System.out.println(s));
}
运行结果:
[Ljava.lang.String;@c2e1f26
[Ljava.lang.String;@dcf3e99
a
b
c
A
B
C
Process finished with exit code 0
map和fiatmap的区别
https://www.cnblogs.com/wangjing666/p/9999666.html
三、Stream最终操作
1.forEach():遍历流中的每一个元素,执行顺序不一定按照流的顺序。
@Test
public void forEach(){
List<String> stringList = Arrays.asList("ad", "", "cbx", "cbx", "", "abf", "ged");
System.out.println("串行流forEach():");
stringList.stream().forEach(s -> System.out.print(s+" "));
System.out.println();
System.out.println("并行流forEach():");
stringList.parallelStream().forEach(s -> System.out.print(s+" "));
}
运行结果:
串行流forEach():
ad cbx cbx abf ged
并行流forEach():
cbx abf ged ad cbx
Process finished with exit code 0
2.forEachOrddered():遍历流中的每一个元素,执行顺序按照流的顺序。
@Test
public void forEachOrdered(){
List<String> stringList = Arrays.asList("ad", "", "cbx", "cbx", "", "abf", "ged");
System.out.println("并行流forEach():");
stringList.stream().forEachOrdered(s -> System.out.print(s+" "));
System.out.println();
System.out.println("并行流forEach():");
stringList.parallelStream().forEachOrdered(s -> System.out.print(s+" "));
}
运行结果:
并行流forEach():
ad cbx cbx abf ged
并行流forEach():
ad cbx cbx abf ged
Process finished with exit code 0
3.toArray():将流中的元素放入到一个数组中。
@Test
public void toArray() {
List<String> stringList = Arrays.asList("ad", "", "cbx", "cbx", "", "abf", "ged");
String[] toArray = stringList.stream().toArray(String[]::new);
System.out.println(Arrays.toString(toArray));
}
运行结果:
[ad, , cbx, cbx, , abf, ged]
Process finished with exit code 0
4.reduce():这个方法的主要作用是把 Stream 元素组合起来。它提供一个起始值,然后依照运算规则拼接或者运算。
@Test
public void reduce(){
// reduce():字符串拼接
String reduceStr = Stream.of("Java", "Html", "Python").reduce("", (x, y) -> x + y);
System.out.println(reduceStr);
// reduce():求和,identity(起始值)为0
Integer total = Stream.of(1, 2, 3, 4).reduce(0, (x, y) -> x + y);
System.out.println(total);
// 求和,sumValue = 10, 无起始值
Integer total2 = Stream.of(1, 2, 3, 4).reduce((a, b) -> Integer.sum(a, b)).get();
System.out.println(total2);
// reduce():求最小值
double minValue = Stream.of(-1.1, 8.8, -2.2, -6.6).reduce(Double.MAX_VALUE, (a, b) -> Double.min(a, b));
System.out.println(minValue);
// reduce():求最大值
double maxValue = Stream.of(-1.1, 8.8, -2.2, -6.6).reduce(Double.MIN_VALUE, (a, b) -> Double.max(a, b));
System.out.println(maxValue);
}
运行结果:
JavaHtmlPython
10
10
-6.6
8.8
Process finished with exit code 0
5.collect():是Stream的一个函数,负责收集流,将流中的结果汇总。结果是由传入collect()中的Collector定义的。
@Test
public void collect(){
List<User> userList = Arrays.asList(new User(1,"张三"), new User(2,"李四"));
Map<Integer, String> userMap = userList.stream().collect(Collectors.toMap(User::getId, User::getName));
System.out.println(userMap);
}
运行结果:
{1=张三, 2=李四}
Process finished with exit code 0
6.min():返回流中的最小值。
@Test
public void min(){
List<Integer> nums = Arrays.asList(12, 24, 18, 14, 27, 6);
Integer min = nums.stream().min((x,y) -> x.compareTo(y)).get();
System.out.println(min);
}
运行结果:
6
Process finished with exit code 0
7.max():返回流中的最大值。
@Test
public void max(){
List<Integer> nums = Arrays.asList(12, 24, 18, 14, 27, 6);
Integer min = nums.stream().max((x,y) -> x.compareTo(y)).get();
System.out.println(min);
}
运行结果:
27
Process finished with exit code 0
8.count():返回流中元素的数量。
@Test
public void count() {
List<String> stringList = Arrays.asList("ad", "", "cbx", "cbx", "", "abf", "ged");
long count = stringList.stream().count();
System.out.println(count);
}
运行结果:
7
Process finished with exit code 0
9.anyMatch():流中只要有一个元素符合传入的断言,就返回 true,否则返回false,如果流为空,永远返回false。
@Test
public void anyMatch() {
List<String> stringList = Arrays.asList("ad", "", "cbx", "cbx", "", "abf", "ged");
boolean b = stringList.stream().anyMatch(s -> s.length() == 2);
System.out.println("只要有一个符合=》"+b);
boolean b1 = stringList.stream().anyMatch(s -> s.length() == 6);
System.out.println("一个都不符合=》"+b1);
List<String> nullStringList = Arrays.asList();
boolean b2 = nullStringList.stream().anyMatch(s -> s.length() == 2);
System.out.println("流为空=》"+b2);
}
运行结果:
只要有一个符合=》true
一个都不符合=》false
流为空=》false
Process finished with exit code 0
10.allMatch():流中所有元素都符合传入的断言时返回 true,否则返回false,流为空时总是返回true。
@Test
public void allMatch() {
List<String> stringList = Arrays.asList("ad", "", "cbx", "cbx", "", "abf", "ged");
boolean b = stringList.stream().allMatch(s -> s.length() < 6);
System.out.println("所有元素都符合=》" + b);
boolean b1 = stringList.stream().allMatch(s -> s.length() > 1);
System.out.println("有一个或多个不符合=》" + b1);
List<String> nullStringList = Arrays.asList();
boolean b2 = nullStringList.stream().allMatch(s -> s.length() > 1);
System.out.println("流为空=》" + b2);
}
运行结果:
所有元素都符合=》true
有一个或多个不符合=》false
流为空=》true
Process finished with exit code 0
11.noneMatch():流中所有元素都不满足传入的断言时返回 true,否则返回false,流为空时总是返回true。
@Test
public void noneMatch() {
List<String> stringList = Arrays.asList("ad", "", "cbx", "cbx", "", "abf", "ged");
boolean b = stringList.stream().noneMatch(s -> s.length() > 6);
System.out.println("所有元素都不符合=》" + b);
boolean b1 = stringList.stream().noneMatch(s -> s.length() < 3);
System.out.println("有一个或多个符合=》" + b1);
List<String> nullStringList = Arrays.asList();
boolean b2 = nullStringList.stream().noneMatch(s -> s.length() > 1);
System.out.println("流为空=》" + b2);
}
运行结果:
所有元素都不符合=》true
有一个或多个符合=》false
流为空=》true
Process finished with exit code 0
12.findFirst():返回Optional[流中第一个元素],使用Optional对象.get()
可以获取流中第一个元素。若流为空则返回Optional.empty,使用Optional对象.get()
会报NoSuchElementException异常。
@Test
public void findFirst() {
List<String> stringList = Arrays.asList("ad", "", "cbx", "cbx", "", "abf", "ged");
Optional<String> first = stringList.stream().findFirst();
System.out.println(first);
System.out.println("findFirst方法返回值=》"+first.get());
List<String> strings = Arrays.asList();
Optional<String> nullFirst = strings.stream().findFirst();
System.out.println(nullFirst);
System.out.println("findFirst方法返回值=》"+nullFirst.get());
}
运行结果:
Optional[ad]
findFirst方法返回值=》ad
Optional.empty
java.util.NoSuchElementException: No value present
at java.util.Optional.get(Optional.java:135)
at org.example.AppTest.findFirst(AppTest.java:224)
……
Process finished with exit code -1
13.findAny():返回Optional[流中的任意一个元素],若流为空则返回Optional.empty。
串行流(stream):
@Test
public void findAny(){
List<String> stringList = Arrays.asList("a", "b","c","d","e","f","g","h","i","j","k","l");
for (int i = 0; i < 300; i++) {
Optional<String> streamAny = stringList.stream().findAny();
System.out.print(streamAny.get()+" ");
if ((i+1)%50==0){
System.out.println();
}
}
}
运行结果:
a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a
a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a
a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a
a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a
a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a
a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a
Process finished with exit code 0
并行流(parallelStream):
@Test
public void findAny(){
List<String> stringList = Arrays.asList("a", "b","c","d","e","f","g","h","i","j","k","l");
for (int i = 0; i < 300; i++) {
Optional<String> streamAny = stringList.parallelStream().findAny();
System.out.print(streamAny.get()+" ");
if ((i+1)%50==0){
System.out.println();
}
}
}
运行结果:
h h h d h h g d d h d d h d h d h h h h h d h h d h d h k h h d h h h h h h h h h h g h h h h h h h
h d d g g h h h d h g h h h g h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h
h h h h h h h h h h h h h h h h g d h h h h h d h d h h d h h d h h h k h h d h h h h h h h h h h h
h h h h h h h h h h h h d h k h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h
h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h
h h h h h h h h h h h h h h h h h d h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h
Process finished with exit code 0