Java 8 API添加了一个新的抽象称为流Stream,可以让你以一种声明的方式处理数据。
stream()
操作将集合转换成一个流,filter()
执行我们自定义的筛选处理,这里是通过lambda表达式筛选出所有偶数,最后我们通过collect()
对结果进行封装处理,并通过Collectors.toList()
指定其封装成为一个List集合返回。
下面所有的实例都源自于开发实战总结,集合元素为对象实例。主要通过对象的某些属性进行操作,举一反三即可。下面所有的举例对象均为熟悉的学生对象。
1)、通过对学生对象中的某个属性进行排序(升序,降序)操作。
2)、注意:使用stream流排序时不改变原集合!但是下面代码提供了一种改变排序后对象的集合操作方式。
public class DTest {
@Data
@AllArgsConstructor
class User {
String name;
String sex;
int age;
}
@Test
public void dTest() {
// 这是一个自定义集合
ArrayList users = Lists.newArrayList();
users.add(new User("小明", "男", 12));
users.add(new User("小明", "女", 10));
users.add(new User("小红", "女", 14));
users.add(new User("小红", "男", 10));
// 根据年龄升序
List collect1 = users.stream().sorted(Comparator.comparing(User::getAge)).collect(Collectors.toList());
System.out.println(collect1);
//[User(name=小明, sex=女, age=10), User(name=小红, sex=男, age=10), User(name=小明, sex=男, age=12), User(name=小红, sex=女, age=14)]
// 根据年龄降序
List collect2 = users.stream().sorted(Comparator.comparing(User::getAge).reversed()).collect(Collectors.toList());
System.out.println(collect2);
//[User(name=小红, sex=女, age=14), User(name=小明, sex=男, age=12), User(name=小明, sex=女, age=10), User(name=小红, sex=男, age=10)]
// 按年龄,姓名升序排序
List collect3 = users.stream().sorted(Comparator.comparing(User::getAge).thenComparing(User::getName)).collect(Collectors.toList());
System.out.println(collect3);
//[User(name=小明, sex=女, age=10), User(name=小红, sex=男, age=10), User(name=小明, sex=男, age=12), User(name=小红, sex=女, age=14)]
// 在List集合内部排序,改变原来的集合元素顺序
// 按年龄倒序
collect3.sort(Comparator.comparing(User::getAge).reversed());
System.out.println(collect3);
//[User(name=小红, sex=女, age=14), User(name=小明, sex=男, age=12), User(name=小明, sex=女, age=10), User(name=小红, sex=男, age=10)]
}
}
1)、提供对学生对象的某个属性(例如姓名)进行分组的操作,分组后为Map对象。
2)、对于分组后的结果,可以对结果集合进行表达式计算,例如计算平均分等。
public class DTest {
@Data
@AllArgsConstructor
class User{
String name;
String sex;
int age;
}
@Test
public void dTest(){
// 这是一个自定义集合
ArrayList users = Lists.newArrayList();
users.add(new User("小明", "男", 12));
users.add(new User("小明", "女", 10));
users.add(new User("小红", "女", 14));
users.add(new User("小红", "女", 12));
users.add(new User("小红", "男", 10));
// 分组结果
Map> groupBy = users.stream().collect(Collectors.groupingBy(User::getName));
System.out.println(groupBy);
//{小明=[DTest.User(name=小明, sex=男, age=12), DTest.User(name=小明, sex=女, age=10)],小红=[DTest.User(name=小红, sex=女, age=14), DTest.User(name=小红, sex=女, age=12), DTest.User(name=小红, sex=男, age=10)]}
// 分组平均值结果
Map avgGroup = users.stream().collect(Collectors.groupingBy(User::getName, Collectors.averagingInt(User::getAge)));
System.out.println(avgGroup);
// {小明=11.0, 小红=12.0}
// 分组总数值结果
Map sumGroup = users.stream().collect(Collectors.groupingBy(User::getName, Collectors.summingInt(User::getAge)));
System.out.println(sumGroup);
// {小明=22, 小红=36}
}
}
1)、提供对学生对象某个属性进行提取,转换为Map对象,Map对象可能存在Key值重复导致报错,所以下方个例举例了,冲突时保留策略。
2)、提供对集合的转Map时,同时可以抽取部分属性自定义Map的 value 值。
public class DTest {
@Data
@AllArgsConstructor
class User{
String name;
String sex;
int age;
}
@Test
public void dTest(){
// 这是一个自定义集合
ArrayList users = Lists.newArrayList();
users.add(new User("小明", "男", 12));
users.add(new User("小明", "女", 10));
users.add(new User("小红", "女", 14));
users.add(new User("小红", "女", 12));
users.add(new User("小红", "男", 10));
// List转Map, 遇到相同的key, k值则保留第一次遇到的值
Map toMapK1 = users.stream().collect(Collectors.toMap(User::getName, a -> a, (k1,k2) -> k1));
System.out.println(toMapK1);
// {小明=DTest.User(name=小明, sex=男, age=12), 小红=DTest.User(name=小红, sex=女, age=14)}
// k值保留第二次遇见的数据
Map toMapK2 = users.stream().collect(Collectors.toMap(User::getName, a -> a, (k1,k2) -> k2));
System.out.println(toMapK2);
//{小明=DTest.User(name=小明, sex=女, age=10), 小红=DTest.User(name=小红, sex=男, age=10)}
// 取对象中的两个属性组成Map
Map collect = users.stream().collect(Collectors.toMap(User::getName, a -> a.getSex(), (k1, k2) -> k2));
System.out.println(collect);
//{小明=女, 小红=男}
}
}
开发过程中经常需要需要去重的情况,其中主要比较棘手的是List集合中的元素,根据元素的某个属性或多个属性去重!以下代码演示!
举例了根据学生对象中的某个属性值去重,默认保留第一次遇到的。所以如果有保留的某些需求。可以先对集合进行排序。
public class DTest {
@Data
@AllArgsConstructor
class User{
String name;
String sex;
int age;
}
@Test
public void dTest(){
// 这是一个自定义集合
ArrayList users = Lists.newArrayList();
users.add(new User("小明", "男", 12));
users.add(new User("小明", "女", 10));
users.add(new User("小红", "女", 14));
users.add(new User("小红", "女", 12));
users.add(new User("小红", "男", 10));
// 根据User.name属性值去重
ArrayList userList = users.stream().collect(Collectors.collectingAndThen(
Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(User::getName))), ArrayList::new));
// 按顺序保留了第一次遇到name
System.out.println(userList);
//[DTest.User(name=小明, sex=男, age=12), DTest.User(name=小红, sex=女, age=14)]
}
}
举例了根据学生对象中的多个属性值组合来去重,默认保留第一次遇到的。所以如果有保留的某些需求。可以先对集合进行排序。
public class DTest {
@Data
@AllArgsConstructor
class User{
String name;
String sex;
int age;
}
@Test
public void dTest(){
// 这是一个自定义集合
ArrayList users = Lists.newArrayList();
users.add(new User("小明", "男", 12));
users.add(new User("小明", "女", 10));
users.add(new User("小红", "女", 14));
users.add(new User("小红", "女", 12));
users.add(new User("小红", "男", 10));
// 根据User.name和User.sex 两个属性值组合去重
List userList= users.stream().collect(
Collectors.collectingAndThen(Collectors.toCollection(() ->
new TreeSet<>(Comparator.comparing(o -> o.getName() + ";" + o.getSex()))
), ArrayList::new));
// 按顺序保留了第一次遇到name+sex,结果去掉了12岁的小红
System.out.println(userList);
//[DTest.User(name=小明, sex=女, age=10), DTest.User(name=小明, sex=男, age=12), DTest.User(name=小红, sex=女, age=14), DTest.User(name=小红, sex=男, age=10)]
}
}
1)、通过map()方法遍历返回集合中对象某个属性的集合。
2)、通过map()方法遍历,对遍历对象进行操作,然后返回自定义的集合。
public class DTest {
@Data
@AllArgsConstructor
class User{
String name;
String sex;
int age;
}
@Test
public void dTest(){
// 这是一个自定义集合
ArrayList users = Lists.newArrayList();
users.add(new User("小明", "男", 12));
users.add(new User("小明", "女", 10));
// 获取年龄数组
List collect = users.stream().map(User::getAge).collect(Collectors.toList());
// 按顺序获取年龄
System.out.println(collect);
// [12, 10]
// 对元素的属性进行操作
List userList = users.stream().map(e -> {
e.setName("小红");
return e;
}).collect(Collectors.toList());
// 全部改名
System.out.println(userList);
// [DTest.User(name=小红, sex=男, age=12), DTest.User(name=小红, sex=女, age=10)]
}
}
1)、学生对象单个属性自定义规则筛选,返回集合。
2)、学生对象属性自定义规则筛选,对筛选结果进行自定义截取,返回最终的集合。
3)、学生对象多个属性自定义规则筛选,返回集合。
public class DTest {
@Data
@AllArgsConstructor
class User{
String name;
String sex;
int age;
}
@Test
public void dTest() {
// 这是一个自定义集合
ArrayList users = Lists.newArrayList();
users.add(new User("小明", "男", 12));
users.add(new User("小明", "男", 10));
users.add(new User("小红", "女", 14));
// 单属性筛选: 姓名为小明的人
List user1 = users.stream().filter(e -> e.getName().equals("小明")).collect(Collectors.toList());
System.out.println(user1);
// [DTest.User(name=小明, sex=男, age=12), DTest.User(name=小明, sex=男, age=10)]
/**
* 筛选结果扩展:
* limit(n): 返回另一个不超过给定大小的流,取前几个;超过集合大小则返回所有;
* skip(n) : 返回丢弃前n个元素的流,如果流中的元素少于n个,则返回空流;
*/
List user2 = users.stream().filter(e -> e.getName().equals("小明")).limit(1).collect(Collectors.toList());
System.out.println(user2);
// [DTest.User(name=小明, sex=男, age=12)]
List user3 = users.stream().filter(e -> e.getName().equals("小明")).skip(1).collect(Collectors.toList());
System.out.println(user3);
// [DTest.User(name=小明, sex=男, age=10)]
/**
* 多属性筛选: 姓名为小明,年龄=10的人
*/
List user4 = users.stream().filter(e -> {
if (e.getName().equals("小明") && e.getAge() == 10) {
return true;
}
return false;
}).collect(Collectors.toList());
System.out.println(user4);
// [DTest.User(name=小明, sex=男, age=10)]
}
}
Stream.reduce() 是 Stream 的一个聚合方法,它可以把一个Stream 的所有元素按照聚合函数聚合成一个结果。常用可以进行累加累减少!
案例主要是举例进行了累加,累减,字符串拼接操作,具体的通用规则也可以自定义。
public class DTest {
@Data
@AllArgsConstructor
class User{
String name;
String sex;
int age;
}
@Test
public void dTest(){
// 这是一个自定义集合
ArrayList users = Lists.newArrayList();
users.add(new User("小明", "男", 12));
users.add(new User("小明", "女", 10));
users.add(new User("小红", "女", 14));
// 从0开始累加:0为设置的初始值
Integer collect1 = users.stream().map(User::getAge).reduce(0, (a, b) -> a + b);
System.out.println(collect1); // 36
// 从设置的初始值12进行累减
Integer collect2 = users.stream().map(User::getAge).reduce(12, (a, b) -> a - b);
System.out.println(collect2); // -24
// 建名字进行字符串拼接
String collect3 = users.stream().map(User::getName).reduce("姓名集:", (a, b) -> a + b);
System.out.println(collect3); // 姓名集:小明小明小红
}
}
集合之间的关系基本如下。一图秒解,下面的实例基本以元素对象的某个或多个属性进行讲解,基本来源于实战总结。
交集:A & B,即A与B ( x x ( ) x x )
并集:A | B, 即A或B ( ( ) )
差集:A - B, 即A减B ( ( x x ) x x )
补集:A ^ B,即A异B ( ( x x ) )
并集的核心解题思路是,首先是将两个集合合并,然后对合并结果去重,上面教程元素单属性去重,元素自定义多属性去重,可往上查看。
1. 使用Stream.concat()方法将 list1 和 list2 合并成一个Stream对象。
2. 使用Stream.distinct() 方法去重,保留不同的学生对象。
3. 使用Collectors.toList()方法将Stream对象转换成List<>对象,存储到resultList中。
注意:Stream.distinct()方法去重自定义对象,例如Student等,Student类需要实现equals()和hashCode()方法,否则不能正确去重。
@Test
public void dTest(){
// 这是一个自定义集合list1
ArrayList list1 = Lists.newArrayList();
list1.add(22);
list1.add(23);
// 这是一个自定义集合list2
ArrayList list2 = Lists.newArrayList();
list1.add(23);
list1.add(24);
List resultList = Stream.concat(list1.stream(), list2.stream())
.distinct()
.collect(Collectors.toList());
System.out.println(ArrayUtil.toString(resultList));
// 运行结果:[22, 23, 24]
}
如下是举例了根据元素指定的单个属性去重,如果根据多个属性去重,则看回上面的教程查看。
class DTest {
@Data
@AllArgsConstructor
class User{
String name;
String sex;
int age;
}
@Test
public void dTest(){
// 这是一个自定义集合
ArrayList users1 = Lists.newArrayList();
users1.add(new User("小明", "男", 12));
users1.add(new User("小华", "男", 10));
users1.add(new User("小红", "女", 14));
// 这是一个自定义集合
ArrayList users2 = Lists.newArrayList();
users2.add(new User("小明", "男", 12));
users2.add(new User("小华", "男", 16));
users2.add(new User("小青", "女", 16));
/**
* 使用TreeSet去重,最后将结果转换为ArrayList集合并返回。
* 其中,Comparator.comparing()方法用于指定按照Age进行排序。根据指定的排序字段去重
*/
List resultList = Stream.concat(users1.stream(), users2.stream())
.collect(Collectors.collectingAndThen(
Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(User::getAge))),
ArrayList::new));
resultList.forEach(System.out::println);
/*
打印结果:
DTest.User(name=小华, sex=男, age=10)
DTest.User(name=小明, sex=男, age=12)
DTest.User(name=小红, sex=女, age=14)
DTest.User(name=小华, sex=男, age=16)
*/
}
}
class DTest {
@Data
@AllArgsConstructor
class User{
String name;
String sex;
int age;
}
@Test
public void dTest(){
// 这是一个自定义集合
ArrayList users1 = Lists.newArrayList();
users1.add(new User("小明", "男", 12));
users1.add(new User("小华", "男", 10));
users1.add(new User("小红", "女", 14));
// 这是一个自定义集合
ArrayList users2 = Lists.newArrayList();
users2.add(new User("小明", "男", 12));
users2.add(new User("小华", "男", 16));
users2.add(new User("小青", "女", 16));
/**
* 使用filter条件筛选(选取两者集合互相包含的元素),最后将结果转换为ArrayList集合并返回。
*/
List resultList = users1.stream()
.filter(users2::contains)
.collect(Collectors.toList());
resultList.forEach(System.out::println);
/*
打印结果:
DTest.User(name=小明, sex=男, age=12)
*/
}
}
在filter过滤器中,使用自定义匹配规则,两个集合中被规则匹配中的元素则为交集。
class DTest {
@Data
@AllArgsConstructor
class User{
String name;
String sex;
int age;
}
@Test
public void dTest(){
// 这是一个自定义集合
ArrayList users1 = Lists.newArrayList();
users1.add(new User("小明", "男", 12));
users1.add(new User("小华", "男", 10));
users1.add(new User("小红", "女", 14));
// 这是一个自定义集合
ArrayList users2 = Lists.newArrayList();
users2.add(new User("小明", "男", 12));
users2.add(new User("小华", "男", 16));
users2.add(new User("小青", "女", 16));
/**
* 使用filter条件筛选(选取两者集合指定的匹配规则),最后将结果转换为ArrayList集合并返回。
*/
List resultList = users1.stream()
.filter(s1 -> users2.stream()
.anyMatch(s2 -> s1.getName().equals(s2.getName()) && s1.getSex().equals(s2.getSex())))
.collect(Collectors.toList());
resultList.forEach(System.out::println);
/*
打印结果:
DTest.User(name=小明, sex=男, age=12)
DTest.User(name=小华, sex=男, age=10)
*/
}
}
在filter过滤器中,使用自定义匹配规则,排除两个集合中被规则匹配中的元素则为差集。
class DTest {
@Data
@AllArgsConstructor
class User{
String name;
String sex;
int age;
}
@Test
public void dTest(){
// 这是一个自定义集合
ArrayList users1 = Lists.newArrayList();
users1.add(new User("小明", "男", 12));
users1.add(new User("小华", "男", 10));
users1.add(new User("小红", "女", 14));
// 这是一个自定义集合
ArrayList users2 = Lists.newArrayList();
users2.add(new User("小明", "男", 12));
users2.add(new User("小华", "男", 16));
users2.add(new User("小青", "女", 16));
/**
* 使用filter条件筛选(排除两者集合指定的匹配规则选中的元素),最后将结果转换为ArrayList集合并返回。
*/
List resultList = users1.stream()
.filter(s1 -> users2.stream()
.noneMatch(s2 -> s1.getName().equals(s2.getName()) && s1.getSex().equals(s2.getSex())))
.collect(Collectors.toList());
resultList.forEach(System.out::println);
/*
打印结果:
DTest.User(name=小红, sex=女, age=14)
*/
}
}