集合是 Java 中使用最多的 API,几乎每个 Java 程序都需要创建和处理集合。为了更好的操作集合,JDK提供了很多工具类和很多第三方类库,比如Guava等,但是还不够。
例如:
在所有员工中,找到年龄小于 40 岁,按薪水倒序排序,列出薪水最高的前十名员工的姓名
在JAVA8之前,需要特殊处理。没有注释辅助,很难一眼看懂这段代码的含义。
public static void opBefore(List emps) {
List filterEmps = new ArrayList();
for (Emp emp : emps) {
if (emp.getAge() < 40) {
filterEmps.add(emp);
}
}
Collections.sort(filterEmps, new Comparator() {
@Override
public int compare(Emp e1, Emp e2) {
return Double.compare(e2.getSalary(), e1.getSalary());
}
});
List names = new ArrayList();
for (Emp emp : filterEmps) {
names.add(emp.getName());
if (names.size() >= 10) {
break;
}
}
System.out.println(names);
}
JAVA8之后呢?JAVA8的设计者设计了流程,使用流程,只需要下面的代码来表达同样的效果。代码简洁明了,关键是看懂意思。看完之后,是不是要再写一遍之前的代码呢?
public static void opAfter(List emps) {
emps.stream().filter(v -> v.getAge() < 40)
.sorted(Comparator.comparing(Emp::getSalary).reversed())
.limit(10)
.map(Emp::getName)
.forEach(System.out::println);
}
如果列表非常大并且需要多线程,只需将 () 更改为 () 。
流动
是 JDK8 的新成员,具有以下特性:
只遍历一次
像迭代器一样,流只能被遍历一次。如果再次遍历,就会抛出异常。如果要再次遍历,只能通过数据源获取新的流。
List langs = Arrays.asList("Java", "PHP", "Python");
Stream stream = langs.stream();
stream.forEach(System.out::println); // 打印
stream.forEach(System.out::println); // 抛出 java.lang.IllegalStateException 异常
外部迭代与内部迭代
使用接口(例如 for-each)进行迭代称为外迭代。该库使用内部迭代。
List names = new ArrayList();
for (Emp emp : emps) {
filterEmps.add(emp.getName());
}
List names = new ArrayList();
Iterator iterator = emps.iterator();
while(iterator.hasNext()) {
Emp emp = iterator.next();
filterEmps.add(emp.getName());
}
List names = emps.stream()
.map(Emp::getName)
.collect(toList());
外部迭代和内部迭代的区别。外部迭代需要用户显式获取每个项目并对其进行处理,这完全取决于用户。内部迭代由JDK进行迭代,JDK可以透明地进行优化处理,如根据硬件进行优化、使用并行处理等。随着JDK的发展,可以继承内部迭代的优化。 JDK。
流操作
emps.stream()
.filter(v -> v.getAge() < 40)
.sorted(Comparator.comparing(Emp::getSalary).reversed())
.map(Emp::getName)
.limit(10)
.forEach(System.out::println);
中间操作
比如 , , map 等,此类操作的目标是一个流,操作完成会返回另一个流,这样就可以将多个操作连接起来形成一个管道。中间操作不会立即处理,而是等待终端操作出现在管道上,并在终端操作期间一次性处理。
方法
操作说明
函数描述符
筛选
T->
地图
对象转换
T -> R
窥视
对象处理
T -> {}
种类
(T, T) ->
限制
截短
重复数据删除
跳过
跳过 n 个元素
终端操作
终端操作将从流管道生成结果。结果可以是统计、归约或处理函数。例如,上例中的连接是一个处理函数。
方法
操作说明
处理流中每个元素的处理函数,例如打印到控制台
数数
返回流中元素的数量
缩减功能,将流缩减为集合、List、Set、Map等。
使用 构建
集合构建流程
实现类(如:List、Set)直接调用()方法获取流。
List langs = Arrays.asList("JAVA", "PHP", "PYTHON")
Stream stream = langs.stream();
数组构建流
数组通过调用 .of() 方法获取流。
String[] langs = {"JAVA", "PHP", "Python"};
Stream stream = Stream.of(langs)
构建无限流
无限流由 .() 方法构造。该方法的参数是一个表达式,通常结合limit方法进行截断。否则,真正的无限流只能等待OOM。
建立一个均匀的流
Stream.iterate(0, n -> n + 2)
.limit(10)
.forEach(System.out::println);
构建随机无限流
使用 .() 方法构造一个无限流,流中的每个元素都有一个延续,因为向流中添加一个新元素是基于前一个元素的。相反,它是非连续的,适合生成随机流。
Stream.generate(Math::random)
.limit(5)
.forEach(System.out::println);
查找和匹配
示例:在流中查找偶数并打印到控制台
List numbers = Arrays.asList(1,2,3,4,5,6,7,8);
numbers.stream()
.filter(v -> v % 2 == 0)
.forEach(System.out::println);
输出如下
2
4
6
8
&
通过方法查找流中任意元素,该方法返回一个对象
List numbers = Arrays.asList(1,2,3,4,5,6,7,8);
numbers.stream()
.filter(v -> v % 2 == 0)
.findAny()
.ifPresent(System.out::println);
输出如下
2
通过方法找到流中的第一个元素。两种方法的区别在于找到第一种。当流使用并行处理时,需要等待所有的结果返回,归约,排序后判断是否是第一个。采取任何一个都更有效。所以如果对订单没有要求c语言字符数组逆序输出,请使用。
&&
用于判断流中是否存在满足条件的元素
示例:判断流中是否有偶数
List numbers = Arrays.asList(1,2,3,4,5,6,7,8);
System.out.println(numbers.stream().anyMatch(v -> v % 2 == 0));
输出如下
true
同样,判断流中任意一个元素匹配成功就足够了,但流中的所有元素都需要匹配。
示例:确定流中的所有数字是否都是偶数
List numbers = Arrays.asList(1,2,3,4,5,6,7,8);
System.out.println(numbers.stream().allMatch(v -> v % 2 == 0));
输出如下
false
反之,则判断流中的所有元素都不匹配。
示例:确定流中的所有数字是否不是偶数
List numbers = Arrays.asList(1,2,3,4,5,6,7,8);
System.out.println(numbers.stream().noneMatch(v -> v % 2 == 0));
输出如下
false
类似 SQL 的操作
重复数据删除
这个名字是不是很眼熟?col … from table,没错,就是SQL语句中的去重关键字,已经成为流计算中的一种方法,其作用也是去重。
例子
List numbers = Arrays.asList(1,2,2,3,3,4);
numbers.stream()
.distinct()
.forEach(System.out::println);
输出如下
1
2
3
4
截断和跳过
skip表示跳过n个元素,limit表示截取多少个元素,这两种方法可以组合成一个内存分页方法
List numbers = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
numbers.stream().skip(6).limit(3).forEach(System.out::println);
输出如下
7
8
9
内存分页工具方法
public static List pagination(List rows, int page, int pageSize) {
if (CollectionUtils.isEmpty(rows)) {
return Collections.emptyList();
}
int totalRows = rows.size();
int skipRows = (page - 1) * pageSize;
if (totalRows <= skipRows) {
return Collections.emptyList();
}
return rows.stream().skip(skipRows).limit(pageSize).collect(Collectors.toList());
}
地图
创建一个员工类以在后续示例中使用
public class Emp {
private Long id;
private String name;
private Integer age;
private Double salary;
private Integer sex;
}
地图
map 方法接受一个函数作为参数,将该函数应用于流中的每个元素,并返回一个新元素,替换流中的对象。
示例:打印员工姓名
emps.stream() // 流中的元素为 Emp 对象
.map(Emp::getName) // 流中的元素转换为 String 对象
.forEach(System.out::println);
示例:为所有员工提高 1000
emps.stream() // 流中的元素为 Emp 对象
.map(v -> {
v.setSalary(v.getSalary() + 1000D);
return v;
}) // 流中的元素还是 Emp 对象
.collect(Collectors.toList());
窥视
如上例所示,在map方法中,只处理流中的元素,不需要替换对象。peek 方法就是用来处理这种情况的
示例:为所有员工提高 1000
emps.stream()
.peek(v -> v.setSalary(v.getSalary() + 1000D)) // 对流中的元素进行处理,但不更换
.collect(Collectors.toList());
流的扁平化。在前面的例子中,流中的元素都是对象,每个元素都可以通过map方法进行处理,但是如果流中的元素是一个集合呢?
例如:指定单词列表[“Hello”, “World”],返回包含的所有字母并去重
首先需要将单词分解成字母,然后对所有字母的集合进行去重操作。通过上一篇的例子可以知道该方法可以去重,所以合并成如下代码
List words = Arrays.asList("Hello", "World");
List letters = words.stream()
.map(v -> v.split(""))
.distinct()
.collect(Collectors.toList());
可以看出,返回的结果是一个List类型,而不是想象中的一组字母。这是因为 map 方法返回一个字符串数组。处理完map之后,流中的元素就变成了一个字母数组,而我们其实想要基本上就是通过扁平化流。
List words = Arrays.asList("Hello", "World");
List letters = words.stream()
.map(v -> v.split("")) // 流中元素转换为 String[]
.flatMap(v -> Stream.of(v)) // 流中元素转换为 String
.distinct()
.collect(Collectors.toList());
自己做
1、给定一个数字列表,[1,2,3,4],返回每个数字的平方列表,结果是[1,4,9,16]
2、给定两个数字列表[1,2,3]和[3,4],对这两个列表进行笛卡尔集,结果为[(1,3),(1,< @4),(2,3),(2,<@4),(3,3),(3,<@4)]
答案 1
List numbers = Arrays.asList(1, 2, 3, 4);
List squares = numbers.stream().map(n -> n * n).collect(toList());
答案 2
List numbers1 = Arrays.asList(1, 2, 3);
List numbers2 = Arrays.asList(3, 4);
List pairs = numbers1.stream()
.flatMap(i -> numbers2.stream().map(j -> new int[]{i, j}))
.collect(toList());
减少
在上一篇文章中,我们使用 map 来处理流中的每个元素。处理后,我们需要收集处理结果。在前面的例子中,我们通过类似的方法收集为值,或者通过类似的方法收集为对象,但这些是定向收集,流也提供了更复杂的结果归约的功能。
和
设置初始值
int sum = numbers.stream().reduce(0, (a, b) -> a + b);
没有初始值(可能流中没有元素)
Optional sum = numbers.stream().reduce((a, b) -> (a + b));
乘
int result = numbers.stream().reduce(1, (a, b) -> a * b);
最大值 最小值
找到最大值
Optional max = numbers.stream().reduce(Integer::max);
找到最小值
Optional min = numbers.stream().reduce(Integer::min);
元素个数
通过map将集合中的元素转换为1,然后通过累加计算流中的元素个数。
int count = numbers.stream().map(v -> 1).reduce(0, (a, b) -> a + b);
集电极
提供了收集结果的方法,提供了类,提供了很多收集器,基本可以满足日常的需求。如果实现不能满足要求,也可以实现自定义采集器进行采集。
柜台
示例:计算流中的员工数量
long count = emps.Stream().collect(Collectors.counting());
也可以简写为
long count = emps.Stream().count()
比较器
示例:查找最年长的员工
Optional collect = emps.stream().collect(Collectors.maxBy(Comparator.comparing(Emp::getAge)));
也可以简写为
Optional collect = emps.stream().max(Comparator.comparing(Emp::getAge));
示例:查找薪水最低的员工
Optional collect = emps.stream().collect(Collectors.minBy(Comparator.comparing(Emp::getSalary)));
也可以简写为
Optional collect = emps.stream().min(Comparator.comparing(Emp::getSalary));
聚合集合
提供了三个收集器 , , 用于对相应的数据类型求和。
示例:计算所有员工的工资总和
Double sumSalary = emps.stream().collect(Comparator.summingDouble(Emp::getSalary));
, , 三个收集器,用于平均对应的数据类型。
示例:计算员工的平均工资
Double avgSalary = emps.stream().collect(Comparator.averagingDouble(Emp::getSalary));
, , 和三个收集器用于对相应的数据类型进行统计。统计结果包括记录数、汇总值、平均值、最大值和最小值。
DoubleSummaryStatistics collect = emps.stream().collect(Collectors.summarizingDouble(Emp::getSalary));
System.out.println("员工总数:" + collect.getCount());
System.out.println("员工总薪资:" + collect.getSum());
System.out.println("员工平均薪资:" + collect.getAverage());
System.out.println("员工最高薪资:" + collect.getMax());
System.out.println("员工最低薪资:" + collect.getMin());
字符串连接收集器
相当于.join()方法,将字符串集合连接成字符串,可以指定元素之间的间隔字符。
示例:获取所有员工的姓名并输出为字符串
String names = emps.stream().map(Emp::getName).collect(Collectors.joining(","));
列表收集器
示例:获取员工姓名列表
List names = emps.Stream()
.map(Emp::getName)
.collect(Collectors.toList());
集收集器
通过Set本身的特性可以实现去重
示例:获取员工 ID 的集合
Set names = emps.Stream()
.map(Emp::getId)
.collect(Collectors.toSet());
地图收集器
示例:获取员工ID->员工信息的键值对
Map names = emps.Stream()
.map(Emp::getName)
.collect(Collectors.toMap(Emp::getId, Function.identity()))
如果有相同id的记录,上面的代码会出现异常,因为有key冲突,没有解决冲突的办法。其实toMap方法的第三个参数就是解决冲突的函数,如下例,如果某个key出现了冲突,则丢弃后面的元素,保留第一个。
Map names = emps.Stream()
.map(Emp::getName)
.collect(Collectors.toMap(Emp::getId, Function.identity(), (v1, v2) -> v1))
数据包收集器
示例:按性别分组
Map<Integer, List> sexGroup = emps.stream()
.collect(Collectors.groupingBy(Emp::getSex));
示例:按年龄分组
Map<String, List> ageGroup = emps.stream()
.collect(Collectors.groupingBy(v -> {
return v.getAge() <= 35 ? "safety" : "dangerous";
}));
多级数据包收集器
收集器还可以接收第二个参数,类型为收集器,可以理解为组内的二级收集,然后传入一个分组收集器,实现多级分组收集。
示例:第二次按性别分组,然后按年龄分组
Map<Integer, Map<String, List>> collect = emps.stream()
.collect(Collectors.groupingBy(Emp::getSex, Collectors.groupingBy(v -> {
return v.getAge() <= 35 ? "safety" : "dangerous";
})));
组集合
第二个参数是收集器,可以在组内收集。
结合收集器统计组内记录数
示例:按性别查找人数的总和
Map sexCount = emps.stream()
.collect(Collectors.groupingBy(Emp::getSex, Collectors.counting()));
结合 maxBy 收集器查找组内的最大值
示例:按性别查找年龄最大的员工
Map<Integer, Optional> maxAge = emps.stream()
.collect(Collectors.groupingBy(Emp::getSex, Collectors.maxBy(Comparator.comparing(Emp::getAge))));
群内采集改造
提供了一个收集器。的第一个参数是一个,也就是实际执行的。第二个参数是一个转换器,用于对采集器的采集结果进行转换。
示例:按性别查找年龄最大的员工
Map maxAge = emps.stream()
.collect(Collectors.groupingBy(Emp::getSex,
Collectors.collectingAndThen(Collectors.maxBy(Comparator.comparing(Emp::getAge)), Optional::get)));
组内转化
提供了一个收集器,可以对组内的元素进行变换和收集
示例:获取不同性别员工姓名的集合
Map<Integer, List> collect = emps.stream()
.collect(Collectors.groupingBy(Emp::getSex,
Collectors.mapping(Emp::getName, Collectors.toList())));
示例:按性别分组,将每个分组转换为 id -> name 的键值对
Map<Integer, Map> collect = emps.stream()
.collect(Collectors.groupingBy(Emp::getSex,
Collectors.mapping(Function.identity(), Collectors.toMap(Emp::getId, Emp::getName))));
划分
提供一个分区收集器,与收集器类似,只是收集的Key被指定为一个类型
示例:按年龄划分,35岁以下为真,否则为假c语言字符数组逆序输出,分为两组
Map<Boolean, List> collect = emps.stream()
.collect(Collectors.partitioningBy(v -> v.getAge() <= 35));
文章来源:http://www.toutiao.com/a7018801121716224516/
感谢您的来访,获取更多精彩文章请收藏本站。

暂无评论内容