初探Stream流


  在本文中将记录自主学习使用Stream的过程

实体类
@Data
public class BlackWhiteUserManage implements Serializable {
	private Integer id;
	...
	/**
     * 人脸图片
     */
    private String faceImg;
    /**
     * 人脸图片文件名
     */
    private String faceImgName;
    ...
}
查询SQL
	select
        id, ... , gender, age, face_img, face_img_name, ...
    from 
    	black_white_user_manage
     ...

操作符

(一)中间操作符
filter() 过滤

  用于通过设置的条件过滤出元素(filter中判空是!=null)

  一、String类型的字段进行非空过滤

// 查询这个结果集(后面代码段不再重复)
List<BlackWhiteUserManage> result = blackWhiteUserManageDao.queryAll(null);
// 针对 BlackWhiteUserManage 中的faceImg字段进行非空过滤
List<BlackWhiteUserManage> list = result.stream()
                .filter(item -> item.getFaceImg() != null)
                .collect(Collectors.toList());

  二、非String类型的字段进行非空过滤

// 针对 BlackWhiteUserManage 中的gender字段进行非空过滤
List<BlackWhiteUserManage> list2 = result.stream()
                .filter(item -> item.getGender() != null)
                .collect(Collectors.toList());

  三、同一字段进行多条件过滤
    将需要过滤的条件进行拼接(用&&)

	// 针对 age 字段进行了‘非空过滤’ 和 '年龄不少于20'的过滤
	List<BlackWhiteUserManage> list4 = result.stream()
	                .filter(item -> item.getAge() !=null && item.getAge() > 20)
	                .collect(Collectors.toList());

  四、不同字段组合过滤
    将需要过滤的条件进行拼接(用&&)

List<BlackWhiteUserManage> list5 = result.stream()
        .filter(item -> item.getAge() != null
                        && item.getUserId() != null
                        && item.getGender() != null
                        && item.getFaceImg() != null
                        && item.getAge() > 20)
        .collect(Collectors.toList());
map() 映射

  对流中的每个元素应用一个函数,将其映射为新的元素。这个函数会被应用到流中的每个元素,生成一个新的流,其中包含了应用映射函数后的结果。
  一、输入年龄结果集
    得到非空姓名set集

Set<Integer> listAgent = result.stream()
        .filter(item -> item.getAge() != null)
        .map(BlackWhiteUserManage::getAge) // 等价于  .map(item -> item.getAge())
        .collect(Collectors.toSet());

注意
  1、map()方法强调原始流中的元素和新流中元素的个数要相同,或者理解为一一对应。map()方法是将流中的元素转换为另外一个流中的元素,转换前后两个流的元素个数不发生改变。
  2、map()中不能像filter()一样写条件(如:item -> item.getAge() > 20),写了这样的条件后就会有问题,原始流为5条,这样的条件如果有效的话,得到的新流肯定与原始流不能一一对应。切记不要与去重弄混了,比如上面的代码得到结果集数是4,为什么又能执行呢?result中有5个元素,经过filter()后原始流中只有4个元素,map()后的新流依旧是4个元素(原始流与新流对应了),最后得到的结果实际上是经过set去重的,与map()无关。

distinct() 去重

  返回的是由该流中不同元素组成的流。
  一、String集合的去重
    直接调用distinct()

 // str 中的元素为 A A B B C
  strList.forEach(str -> System.out.print(str));//去重前:AABBC

  strList = strList.stream().distinct().collect(Collectors.toList());

  System.out.println("");
  System.out.print("去重后:");

  strList.forEach(str -> System.out.print(str));//去重后:ABC

  二、对象集合运用distinct()去重
    就对象中某个属性或多个属性去重,就需要重写对象类的equals()和hashcode()方法。
实体类(重写了equals()和hashcode()方法)
    如果UserInfo的实例对象obj1和obj2,obj1.userName.equals(obj2.userName)==true,就认为obj1 == obj2

@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserInfo {

    private Integer userId;
    private String userName;
    private Integer age;
    private Integer sex;
    private String phone;

    @Override
    public boolean equals(Object obj) {

        if (this == obj)//如果是同一个对象 直接返回true
            return true;
        if (obj == null)//如果比较的为null 直接返回false
            return false;
        if (getClass() != obj.getClass())//两者类类型不一样直接返回false
            return false;

        // 类型转换
        UserInfo other = (UserInfo) obj;
        if (userName == null) {

            if (other.userName != null) return false;

        } else if (!userName.equals(other.userName)) {
            return false;
        }
        return true;
    }
    @Override
    public int hashCode() {
        // 31是经验验证的一个能够很好地减小哈希碰撞的质数
        final int prime = 31;
        
        int result = 1;
        result = prime * result + ((userName == null) ? 0: userName.hashCode());
        return result;
    }
}

测试代码

	UserInfo info1 = new UserInfo(1,"张三",18,1,"0731-2211");
	UserInfo info2 = new UserInfo(1,"张三",18,1,"0731-2211");
	UserInfo info3 = new UserInfo(3,"张三",18,1,"0731-2211");
	UserInfo info4 = new UserInfo(4,"李四",18,1,"0731-2233");
	UserInfo info5 = new UserInfo(5,"王五",18,1,"0731-2244");
	
	List<UserInfo> result = new ArrayList<>();
	result.add(info1);
	result.add(info2);
	result.add(info3);
	result.add(info4);
	result.add(info5);
	
	List<UserInfo> userInfoList = result.stream().distinct().collect(Collectors.toList());
	Iterator<UserInfo> iterator = userInfoList.iterator();
    while (iterator.hasNext()) {
         UserInfo userInfo = iterator.next();
         System.out.println(userInfo);
     }

结果
UserInfo(userId=1, userName=张三, age=18, sex=1, phone=0731-2211),
UserInfo(userId=4, userName=李四, age=18, sex=1, phone=0731-2233),
UserInfo(userId=5, userName=王五, age=18, sex=1, phone=0731-2244)]

注意:
  1、对象列表result(List<UserInfo>)中有5条记录,未重写equals()和hashcode()时,只能对完全一样的记录(info1、info2)去重
  2、针对userName属性重写了equals()和hashcode()方法后,就对记录中userName出现重复的(info1、info2、info3)去重,结果显示,保留了info1。
  3、对于对象列表,想要实现属性级的去重,必须要重写equals()和hashcode(),因为distinct()就是根据equals()处理的。上述例子是针对UserInfo中userName属性去重,实际上还可以多属性组合去重。

  三、对象集合运用其它方法去重
    (1)、使用Collectors两个静态方法collectingAndThen()和toCollection(),以及TreeSet<>来去重

// 根据userName属性去重(不能多属性组合去重)
List<BlackWhiteUserManage> list8 = result.stream()
     .collect(Collectors.collectingAndThen(
         Collectors.toCollection(
            () -> new TreeSet<>(
            	Comparator.comparing(BlackWhiteUserManage::getUsername))), ArrayList::new));

简述Collectors 的操作:
  聚合元素toList、toSet、toCollection,这几个函数比较简单,是将聚合之后的元素,重新封装到队列中,然后返回。toList方法返回的是List子类toSet返回的是Set子类toCollection返回的是Collection子类。我们都知道,Collection的子类包括List、Set等众多子类,所以toCollection更加灵活
       toMap、toConcurrentMap,这两个方法的作用是将聚合元素,重新组装为Map结构,也就是 k-v 结构。toMap返回的是MaptoConcurrentMap返回ConcurrentMap。toMap()可玩性很高
  操作链collectingAndThen,先对集合进行一次聚合操作,然后通过Function定义的函数,对聚合后的结果再次处理。

    (2)、巧用filter()+自定义函数

// 根据userName去重
List<BlackWhiteUserManage> list9 = result.stream()
                .filter(distinctByKey(BlackWhiteUserManage::getUsername))
                .collect(Collectors.toList());
//自定义函数
 private static <T> Predicate<T> distinctByKey(Function<? super T, Object> keyExtractor) {
        Map<Object, Boolean> map = new ConcurrentHashMap<>();
        return t -> map.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
    }

注意:对于重复元素,将保留在遇到顺序中排在最前面的元素(可能不是想要的)。

sorted() 排序

按自然升序对集合进行排序,默认是升序排列;使用 Comparator 提供 reverseOrder() 方法实现降序排列。

方法说明
sorted()自然排序(升序),流中元素需实现Comparable接口。 例:list.stream().sorted()
sorted(Comparator com)定制排序。常用以下几种: list.stream().sorted(Comparator.reverseOrder()) //倒序排序(从大到小) list.stream().sorted(Comparator.comparing(Student::getAge)) //顺序排序(从小到大)list.stream().sorted(Comparator.comparing(Student::getAge).reversed()) // 倒序排序(从大到小)

  一、对象单字段(年龄从大到小)降序排序

List<BlackWhiteUserManage> list10 = result.stream()
                .filter(item -> item.getAge() != null)
                .sorted(Comparator.comparing(BlackWhiteUserManage::getAge).reversed())
                .collect(Collectors.toList());

  二、对象多字段全部升序排序

List<BlackWhiteUserManage> list11 = result.stream()
        .filter(item -> item.getAge() != null && item.getUsername() != null)
        .sorted(Comparator.comparing(BlackWhiteUserManage::getAge)
                        .thenComparing(BlackWhiteUserManage::getUsername))
        .collect(Collectors.toList());

注意
  1、将age和username字段用于排序,这两个字段不能为null,否则代码直接报错
  2、有一个comparing(),可以有多个thenComparing()。
  3、在此例中实际上是按照comparing()中指定的age进行排序的,将comparing()指定为username时,就按照username进行排序。

limit

  会返回一个不超过给定长度的流

// limit(3):取前3条数据
List<BlackWhiteUserManage> list13 = result.stream()
                        .limit(3).collect(Collectors.toList());
skip

  返回一个扔掉了前n个元素的流

// skip(3):跳过第3条取后几条
List<BlackWhiteUserManage> list14 = result.stream()
                .skip(3)
                .collect(Collectors.toList());
flatMap

  flatmap方法让你把一个流中的每个值都换成另一个流,然后把所有的流连接起来成为一个流。

List<String> list15 = result.stream()
        .flatMap(obj -> Arrays.stream(obj.getUsername().split(",")))
        .collect(Collectors.toList());

结果:[李四, 1, 老七, 老六, 老六]
map:对流中每一个元素进行处理
flatMap:流扁平化,让你把一个流中的“每个值”都换成另一个流,然后把所有的流连接起来成为一个流
本质区别:map是对一级元素进行操作,flatmap是对二级元素操作map返回一个值;flatmap返回一个流,多个值
应用场景:map对集合中每个元素加工,返回加工后结果;flatmap对集合中每个元素加工后,做扁平化处理后(拆分层级,放到同一层)然后返回

peek

  对元素进行遍历并处理

List<BlackWhiteUserManage> list16 = result.stream()
        .filter(item -> item.getUserId() != null)
        .peek(item -> item.setUserId(item.getUserId() + 1))
        .collect(Collectors.toList());

注意:只是对result结果集中的数据进行了二次处理,处理的字段必须不能为空(否则会报空指针异常)

(二)终端操作符
collect

  收集器,将流转换为其他形式。具体有:toList、toSet和toCollection。

forEach

  遍历流

// forEach:遍历当前流
result.stream().forEach(System.out::println);

  结果:将流中的记录逐行答应出来。

findFirst

  返回第一个元素

// findFirst:返回当前流中第一个元素
BlackWhiteUserManage blackWhiteUserManage1 = result.stream().findFirst().get();

  结果:输出流中第一个元素

findAny

  将返回当前流中的任意元素

// 返回当前流中任意元素
BlackWhiteUserManage blackWhiteUserManage2 = result.stream().findAny().get();
count

  返回当前流中元素总数

// 返回当前流中元素的总数
long count = result.stream().count();
sum

  求和,可以指定某个字段进行求和

// 求和
int sum = result.stream().mapToInt(item -> item.getDelFlag()).sum();

  注意:指定的字段不能有空值(null),否则直接报空指针错误。可以用filter过滤掉

max

  最大值,当前流的所有记录中某个字段的最大值

long max = result.stream()
	.filter(item -> item.getUserId() != null)
	.max(Comparator.comparingLong(item -> item.getUserId())).get().getUserId();

  注意:指定的字段不能有控制(可以用filter先过滤掉),否则直接报空指针错误。

min

  最小值,当前流的所有记录中某个字段的最小值

long min = result.stream()
	.filter(item -> item.getUserId() != null)
	.min(Comparator.comparingLong(item -> item.getUserId())).get().getUserId();

  注意:指定的字段不能有控制(可以用filter先过滤掉),否则直接报空指针错误。

anyMatch

  检查是否至少匹配一个元素,返回boolean

// anyMatch:检查是否至少匹配一个元素
boolean matchAny = result.stream().anyMatch(item ->"李四".equals(item.getUsername()));

  有一个匹配就会返回true

allMatch

  检查是否匹配所有元素,返回boolean

// allMatch:检查是否匹配所有元素
boolean matchAll = result.stream().allMatch(item ->"李四".equals(item.getUsername()));

  流中所有数据项中字段全部匹配成功才会返回true

noneMatch

  检查是否没有匹配所有元素,返回boolean

// noneMatch:检查是否没有匹配所有元素,返回boolean
boolean noneMatch = result.stream().noneMatch(item ->"王五".equals(item.getUsername()));
reduce

  可以将流中元素反复结合起来,得到一个值

Optional reduce = result.stream().reduce((blackWhiteUserManage, blackWhiteUserManage2) -> {
            return blackWhiteUserManage;
        });
if (reduce.isPresent())  log.info("新的结果集"+reduce.get());

Collect收集

  Collector:结果收集策略的核心接口,具备将指定元素累加存放到结果容器中的能力;并在Collectors工具中提供了Collector接口的实现类

toList

  例:将用户名字放入一个List集合中

List<Long> userIdList = result.stream()
			.map(item -> item.getUserId()).collect(Collectors.toList());

  结果:[64, null, null, 62, 62]

toMap

  将主键ID和Name以Key-Value形式存放到Map集合中

Map<Integer, String> userMap = result.stream()
               .collect(Collectors.toMap(BlackWhiteUserManage::getId,BlackWhiteUserManage::getUsername));

  结果:{2=李四, 3=1, 4=老七, 5=老六, 6=老六}

toSet

  将用户的create_id放入到set集合中

Set<Integer> createdIds = result.stream()
                .map(BlackWhiteUserManage::getCreateId).collect(Collectors.toSet());

  结果:[65, 62]

counting

  符合条件(年龄大于20)的用户总数

long count = result.stream()
               .filter(item -> item.getAge() !=null && item.getAge() > 20)
               .collect(Collectors.counting());

  注意:测试数据的问题,这里加了一个age的非空过滤

summingInt

  对结果元素(用户ID)求和

Integer sum = result.stream()
       .filter(item -> item.getAge() != null && item.getAge() > 20)
       .collect(Collectors.summingInt(BlackWhiteUserManage::getId));

  结果:12

minBy

  筛选元素中ID最小的用户

BlackWhiteUserManage minBy = result.stream()
               .collect(Collectors.minBy(Comparator.comparingInt(BlackWhiteUserManage::getId))).get();
joining

  将用户的名字,以指定分隔符链接成字符串;

String userNameStr = result.stream()
               .map(BlackWhiteUserManage::getUsername).collect(Collectors.joining("||"));

  结果:李四||1||老七||老六||老六

groupingBy

  根据isLongEffective字段进行分组

Map<Integer, List<BlackWhiteUserManage>> map1 = result.stream()
                        .collect(Collectors.groupingBy(BlackWhiteUserManage::getIsLongEffective));

结果:
  0=[BlackWhiteUserManage(…, isLongEffective=0,…),BlackWhiteUserManage(…, isLongEffective=0,…)],
  1=[BlackWhiteUserManage(…, isLongEffective=1,…),…,BlackWhiteUserManage(…, isLongEffective=1,…)]

orElse(null)

  表示如果一个都没找到返回null(orElse()中可以塞默认值。如果找不到就会返回orElse中设置的默认值)

orElseGet(null)

  表示如果一个都没找到返回null(orElseGet()中可以塞默认值。如果找不到就会返回orElseGet中设置的默认值)

  • 25
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值