实际中的Stream流的用法

本文详细介绍了Java Stream流的使用,包括如何生成stream流对象、forEach与map的区别、去null、创建新List、去除重复元素、转换为Map、获取特定元素、排序、聚合操作如求和、平均值等,以及分组和异常处理。强调了在处理过程中可能遇到的空指针异常和其他需要注意的问题,并提供了相应的解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

实际中的Stream流的用法

不同对象怎么生成stream流对象

package stream;

/**
 * @author 刘诗良
 * @version 1.0
 * @Description
 */
import java.util.*;
import java.util.stream.Stream;

public class StreamDemo {
   
    public static void main(String[] args) {
   
        //Collection体系的集合可以使用默认方法stream()生成流
        List<String> list = new ArrayList<>();
        Stream<String> listStream = list.stream();

        Set<String> set = new HashSet<>();
        Stream<String> setStream = set.stream();

        //Map体系的集合间接生成流
        Map<String,Integer> map = new HashMap<>();
        Stream<String> keyStream = map.keySet().stream();
        Stream<Integer> valueStream = map.values().stream();
        Stream<Map.Entry<String, Integer>> entryStream = map.entrySet().stream();

        //数组可以通过Stream接口的静态方法of(T...values)生成流
        String[] strArray = {
   "hello","world","java"};
        Stream<String> strArrayStream = Stream.of(strArray);
        Stream<String> strArrayStream2 = Stream.of("hello", "world", "java");
        Stream<Integer> intStream = Stream.of(10, 20, 30);
    }
}

image-20240221205424836

除了上面明显的方法调用对象是null外,不会出现异常。

forEach和map的不同

forEach会改变原集合(不管是否是引用)

package yimeng;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.*;

public class StreamTest {
   
    public static void main(String[] args) {
   
        List<Person> userList = new ArrayList<>();
        userList.add(new Person("段誉",25));
        userList.add(new Person("萧峰",40));
        userList.add(new Person("虚竹",30));
        userList.add(new Person("无涯子",100));
        userList.add(new Person("慕容复",35));
        userList.add(new Person("云中鹤",45));

        System.out.println("-------原集合--------");
        System.out.println(userList);
        System.out.println("-------修改引用类型--------");
        userList.stream().forEach(u -> {
   
            u.setName("天龙八部-" + u.getName());
        });
        System.out.println(userList);
        System.out.println("-------修改非引用类型--------");
        userList.stream().forEach(u -> {
   
            u.setAge(12);
        });
        System.out.println(userList);
    }
}

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
class Person {
   
    private String name;
    private int age;
}

image-20240223231616072

不管修改引用类型还是非引用类型的数据,都会影响原来集合。

除了上面明显的方法调用对象是null外,不会出现异常。forEach方法中u.setName(“天龙八部-” + u.getName());、u.setAge(12);的u为null会出现异常。

map不会改变原集合(不管是否是引用)

package yimeng;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.*;

public class StreamTest {
   
    public static void main(String[] args) {
   
        List<Person> userList = new ArrayList<>();
        userList.add(new Person("段誉",25));
        userList.add(new Person("萧峰",40));
        userList.add(new Person("虚竹",30));
        userList.add(new Person("无涯子",100));
        userList.add(new Person("慕容复",35));
        userList.add(new Person("云中鹤",45));

        System.out.println("-------原集合--------");
        System.out.println(userList);
        System.out.println("-------修改引用类型--------");
        userList.stream().map(u -> {
   
            u.setName("天龙八部-" + u.getName());
            return 1234;
        });
        System.out.println(userList);
        System.out.println("-------修改非引用类型--------");
        userList.stream().map(u -> {
   
            u.setAge(12);
            return "测试";
        });
        System.out.println(userList);
    }
}

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
class Person {
   
    private String name;
    private int age;
}

image-20240223233133701

除了上面明显的方法调用对象是null外,不会出现异常。forEach方法中u.setName(“天龙八部-” + u.getName());、u.setAge(12);的u为null会出现异常。

map必须要有返回值,但是forEach不用

看到下面这个map这样写是错的。

image-20240223232739988

map方法返回的还是一个流,map参数的方法要有返回值,forEach没有返回值,且是终结流,且它的参数的方法没有返回值

image-20240223233351856

注意:

下面这种,你把变量指向一个新的引用对象,是不会改变原来集合的。因为,下面的forEach相当于是把流中指向userList正在遍历的元素的引用赋值给了forEach中定义的变量,然后去执行操作的。所以上面的forEach案例你操作u.setAge()能改变原来集合,其实就是因为你u指向了真正的对象,所以u.setAge()就是让真正的对象去改变值,所以能改变原来集合。至于下面这种为什么不行,就是因为原来u是指向正在遍历的对象的,但是你u=new Person(“1”,1);就是把u指向另一个对象,所以执行就不会改变原来集合中的元素。

package yimeng;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.*;
import java.util.stream.Collectors;

public class StreamTest {
   
    public static void main(String[] args) {
   
        List<Person> userList = new ArrayList<>();
        userList.add(new Person("段誉",25));
        userList.add(new Person("萧峰",40));
        userList.add(new Person("虚竹",30));
        userList.add(new Person("无涯子",100));
        userList.add(new Person("慕容复",35));
        userList.add(new Person("云中鹤",45));

        userList.stream().forEach(u -> {
   
            u=new Person("1",1);
            u.setAge(111);
        });
        System.out.println(userList);
    }
}

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
class Person {
   
    private String name;
    private int age;
}

image-20240223234902755

去null

package yimeng;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

public class StreamTest {
   
    public static void main(String[] args) {
   
        List<UserInfo> list=new ArrayList<>();
        list.add(UserInfo.builder().userId(1L).userName("张三").build());
        list.add(UserInfo.builder().userId(2L).amount(10).userName("李四").build());
        list.add(UserInfo.builder().userId(3L).amount(15).build());

        List<String> userNameList = list.stream()
                .map(UserInfo::getUserName)
                .filter(Objects::nonNull)
                .collect(Collectors.toList());

        System.out.println(userNameList);
    }
}
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
class UserInfo {
   
    private Long userId;
    private String userName;
    private String status;
    private Integer amount;
    private Date createDate;
}

image-20240219215141924

但是这种方式不能去空字符串。

比如:

image-20240219215646681

解决:

image-20240219215823723

注意:上面的map方法调用前流中的元素如果是null,那么执行UserInfo::getUserName会出现空指针异常。

image-20240228213606914

解决:

image-20240228213737100

处理后返回一个新的List

package yimeng;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.commons.lang3.ObjectUtils;

import java.util.*;
import java.util.stream.Collectors;

public class StreamTest {
   
    public static void main(String[] args) {
   
        List<UserInfo> list=new ArrayList<>();
        list.add(UserInfo.builder().userId(1L).userName("张三").build());
        list.add(UserInfo.builder().userId(2L).amount(10).userName("李四").build());
        list.add(UserInfo.builder().userId(3L).amount(15).build());

        // List<String>转List<Long>
        List<Long> toLong = Arrays.asList("1","2").stream().map(Long::parseLong).collect(Collectors.toList());
        System.out.println(toLong);

        // List<Bean>转类型List<String>,其中新list的元素为老list中每一个元素的用户名。
        List<String> userNameList = list.stream().map(UserInfo::getUserName).collect(Collectors.toList());
        System.out.println(userNameList);
    }
}
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
class UserInfo {
   
    private Long userId;
    private String userName;
    private String status;
    private Integer amount;
    private Date createDate;
}

image-20240219220345064

返回一个新的List,要求没有null

package yimeng;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.commons.lang3.ObjectUtils;

import java.util.*;
import java.util.stream.Collectors;

public class StreamTest {
   
    public static void main(String[] args) {
   
        List<UserInfo> list=new ArrayList<>();
        list.add(UserInfo.builder().userId(1L).userName("张三").build());
        list.add(UserInfo.builder().userId(2L).amount(10).userName("李四").build());
        list.add(UserInfo.builder().userId(3L).userName("王五").build());
        list.add(UserInfo.builder().userId(4L).amount(15).build());
        list.add(UserInfo.builder().userId(5L).amount(25).userName("").build());
        list.add(UserInfo.builder().userId(6L).amount(45).userName("  ").build());

        // List<Bean>转类型List<String>,其中新list的元素为老list中每一个元素的用户名。排除空。
        List<Integer> amountList1 = list.stream().map(i -> i.getAmount() == null ? 0 : i.getAmount()).collect(Collectors.toList());
        System.out.println(amountList1);
    }
}
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
class UserInfo {
   
    private Long userId;
    private String userName;
    private String status;
    private Integer amount;
    private Date createDate;
}

image-20240219220815060

去除重复

distinct()方法的判断重复和HashMap判断重复的做法是一样的。是通过hashCode()方法和equals()方法来判断是否重复的(java中有约定,重写equals()方法就得重写hashCode(),并且约定重写的规则是hashCode()判定相同,equals()方法可以判定为不同。equals判定为相同,hashCode()得要判定为相同,当然你不遵循约定也是OK的)。distinct()判定如果重复了,那么只会留下先遍历到的那个元素,后面遍历到的被判定为重复的元素,而被舍弃。判断重复的规则是:通过“正在遍历的元素”.equals(“加入到新集合中的每一个元素”)来判断是否重复,只要存在返回true,那么就判定为重复,判断为重复那么正在遍历的元素就不会被加入到新集合。如果都不返回true,就判断为不重复,那么就会把正在遍历的元素加入到新集合中去

测试只有equals()判断重复,是不是被认定为重复:

package yimeng;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.*;
import java.util.stream.Collectors;

public class StreamTest {
   
    public static void main(String[] args) {
   
        List<UserInfo> list=new ArrayList<>();
        list.add(UserInfo.builder().userId(1L).userName("张三").build());
        list.add(UserInfo.builder().userId(2L).amount(10).userName("李四").build());
        list.add(UserInfo.builder().userId(3L).userName("王五").build());
        list.add(UserInfo.builder().userId(4L).amount(15).build());
        list.add(UserInfo.builder().userId(5L).amount(15).userName("").build());
        list.add(UserInfo.builder().userId(6L).amount(45).userName("  ").build());

        // 没有去重复
        List<UserInfo> amountList = list.stream().collect(Collectors.toList());
        System.out.println("没有去重:"+amountList);

        // 去重复
        List<UserInfo> amountList1 = list.stream().distinct().collect(Collectors.toList());
        System.out.println("去重:"+amountList1);
    }
}
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
class UserInfo {
   
    private Long userId;
    private String userName;
    private String status;
    private Integer amount;
    private Date createDate;

    @Override
    public boolean equals(Object o) {
   
        // 一定返回true
        return true;
    }

    @Override
    public int hashCode() {
   
        Random random = new Random();
        int randomNumber = random.nextInt();
        System.out.println(randomNumber);
        // 不会重复
        return randomNumber;
    }
}

结果(没有被判断为重复):

image-20240219222940786

两个方法都判断为重复,才会被认定为重复。并且只会留下第一个元素,后面被判断为重复的元素不会被留下:

package yimeng;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.*;
import java.util.stream.Collectors;

public class StreamTest {
   
    public static void main(String[] args) {
   
        List<UserInfo> list=new ArrayList<>();
        list.add(UserInfo.builder().userId(1L).userName("张三").build());
        list.add(UserInfo.builder().userId(2L).amount(10).userName("李四").build());
        list.add(UserInfo.builder().userId(3L).userName("王五").build());
        list.add(UserInfo.builder().userId(4L).amount(15).build());
        list.add(UserInfo.builder().userId(5L).amount(15).userName("").build());
        list.add(UserInfo.builder().userId(6L).amount(45).userName("  ").build());

        // 没有去重复
        List<UserInfo> amountList = list.stream().collect(Collectors.toList());
        System.out.println("没有去重:"+amountList);

        // 去重复
        List<UserInfo> amountList1 = list.stream().distinct().collect(Collectors.toList());
        System.out.println("去重:"+amountList1);
    }
}
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
class UserInfo {
   
    private Long userId;
    private String userName;
    private String status;
    private Integer amount;
    private Date createDate;

    @Override
    public boolean equals(Object o) {
   
        // 一定返回true
        return true;
    }

    @Override
    public int hashCode() {
   
        // 重复
        return 10;
    }
}

image-20240219223111178

判断重复的规则(通过“正在遍历的元素”.equals(“加入到新集合中的每一个元素”)来判断是否重复,只要存在返回true,那么就判定为重复,判断为重复那么正在遍历的元素就不会被加入到新集合。如果都不返回true,就判断为不重复,那么就会把正在遍历的元素加入到新集合中去):

package yimeng;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.*;
import java.util.stream.Collectors;

public class StreamTest {
   
    public static void main(String[] args) {
   
        List<UserInfo> list=new ArrayList<>();
        list.add(UserInfo.builder().userId(1L).userName("张三").build());
        list.add(UserInfo.builder().userId(2L).amount(10).userName("李四").build());
        list.add(UserInfo.builder().userId(3L).userName("王五").build());
        list.add(UserInfo.builder().userId(2L).amount(15).build());
        list.add(UserInfo.builder().userId(3L).amount(15).userName("").build());
        list.add(UserInfo.builder().userId(6L).amount(45).userName("  ").build());

        // 没有去重复
        List<UserInfo> amountList = list.stream().collect(Collectors.toList());
        System.out.println("没有去重:"+amountList);

        // 去重复
        List<UserInfo> amountList1 = list.stream().distinct().collect(Collectors.toList());
        System.out.println("去重:"+amountList1);
    }
}
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
class UserInfo {
   
    private Long userId;
    private String userName;
    private String status;
    private Integer amount;
    private Date createDate;

    @Override
    public boolean equals(Object o) {
   
        // “正在遍历的元素”.equals(“每一个元素”)来判断是否重复,只要有一个返回true就表示存在重复。所以,运行结果是除了userId为1的那个张三外,都会被判定为存在重复,又因为重复的话,后面的元素会被舍弃,那么结果就只有userId为1的元素。因为上面所有元素的userId中,没有userId比1小的,所以只有“张三”那个元素对比每一个元素时,“this.userId>((UserInfo)o).getUserId()”都为false。
        return this.userId>((UserInfo)o).getUserId();
    }

    @Override
    public int hashCode() {
   
        // 重复
        return 10;
    }
}

image-20240220225715557

package yimeng;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.*;
import java.util.stream.Collectors;

public class StreamTest {
   
    public static void main(String[] args) {
   
        List<UserInfo> list=new ArrayList<>();

        list.add(UserInfo.builder().userId(2L).amount(10).userName("李四").build());
        list.add(UserInfo.builder().userId(3L).userName("王五").build());
        list.add(UserInfo.builder().userId(2L).amount(15).build());
        list.add(UserInfo.builder().userId(1L).userName("张三").build());
        list.add(UserInfo.builder().userId(3L).amount(15).userName("").build());
        list.add(UserInfo.builder().userId(6L).amount(45).userName("  ").build());

        // 没有去重复
        List<UserInfo> amountList = list.stream().collect(Collectors.toList());
        System.out.println("没有去重:"+amountList);

        // 去重复
        List<UserInfo> amountList1 = list.stream().distinct().collect(Collectors.toList());
        System.out.println("去重:"+amountList1);
    }
}
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
class UserInfo {
   
    private Long userId;
    private String userName;
    private String status;
    private Integer amount;
    private Date createDate;

    @Override
    public boolean equals(Object o) {
   
        // “正在遍历的元素”.equals(“每一个元素”)来判断是否重复,只要有一个返回true就表示存在重复。所以,运行结果是除了userId为1的那个张三外,都会被判定为存在重复,又因为重复的话,后面的元素会被舍弃,那么结果就只有userId为1的元素。因为上面所有元素的userId中,没有userId比1小的,所以只有“张三”那个元素对比每一个元素时,“this.userId>((UserInfo)o).getUserId()”都为false。
        return this.userId>((UserInfo)o).getUserId();
    }

    @Override
    public int hashCode() {
   
        // 重复
        return 10;
    }
}

image-20240220232742778

转为Map

package yimeng;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.*;
import java.util.stream.Collectors;

public class StreamTest {
   
    public static void main(String[] args) {
   
        List<UserInfo> list=new ArrayList<>();
        list.add(UserInfo.builder().userId(1L).userName("张三").build());
        list.add(UserInfo.builder().userId(2L).amount(10).userName("李四").build());
        list.add(UserInfo.builder().userId(3L).userName("王五").build());
        list.add(UserInfo.builder().userId(4L).amount(15).build());
        list.add(UserInfo.builder().userId(5L).amount(15).userName("").build());
        list.add(UserInfo.builder().userId(6L).amount(45).userName("  ").build());

        Map<Long, UserInfo> map1 = list.stream().collect(Collectors.toMap(UserInfo::getUserId, i -> i));
        System.out.println(map1);
    }
}
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
class UserInfo {
   
    private Long userId;
    private String userName;
    private String status;
    private Integer amount;
    private Date createDate;
}

image-20240219223350277

但是,如果键有重复,那么会出现异常。

image-20240219223450627

解决:

package yimeng;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.*;
import java.util.stream.Collectors;

public class StreamTest {
   
    public static void main(String[] args) {
   
        List<UserInfo> list=new ArrayList<>();
        list.add(UserInfo.builder().userId(1L).userName("张三").build());
        list.add(UserInfo.builder().userId(2L).amount(10).userName("李四").build());
        list.add(UserInfo.builder().userId(3L).userName("王五").build());
        list.add(UserInfo.builder().userId(3L).amount(15).build());
        list.add(UserInfo.builder().userId(5L).amount(15).userName("1").build());
        list.add(UserInfo.builder().userId(6L).amount(45).userName("jdlf").build());

        Map<Long, String> map = list.stream().collect(Collectors.toMap(UserInfo::getUserId, i -> i.getUserName() == null ? "null" : i.getUserName(), (k1, k2) -> k2));
        System.out.println(map);
    }
}
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
class UserInfo {
   
    private Long userId;
    private String userName;
    private String status;
    private Integer amount;
    private Date createDate;
}

image-20240219223915598

取符合条件的一个元素

使用limit

package yimeng;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.*;
import java.util.stream.Collectors;

public class StreamTest {
   
    public static void main(String[] args) {
   
        List<UserInfo> list=new ArrayList<>();
        list.add(UserInfo.builder().userId(1L).userName("张三").build());
        list.add(UserInfo.builder().userId(2L).amount(10).userName("李四").build());
        list.add(UserInfo.builder().userId(3L).userName("王五").build());
        list.add(UserInfo.builder().userId(3L).amount(15).build());
        list.add(UserInfo.builder().userId(5L).amount(15).userName("1").build());
        list.add(UserInfo.builder().userId(6L).amount(45).userName("张三").build());

        List<UserInfo> collect = list.stream().filter(u -> "张三".equals(u.getUserName())).limit(1).collect(Collectors.toList());
        System.out.println(collect.get(0));

        List<UserInfo> collect1 = list.stream().filter(u -> "张三111".equals(u.getUserName())).limit(1).collect(Collectors.toList());
        System.out.println(collect1.get(0));

    }
}
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
class UserInfo {
   
    private Long userId;
    private String userName;
    private String status;
    private Integer amount;
    private Date createDate;
}

image-20240219232002715

使用findAny或者findFirst(找符合条件的元素可以用这个两个方法)

并行流时findAny的效率会比findFirst高一些,串行流的话两者效率一样,并且在并行流的情景下,我们一般是不会有需求知道哪一个元素是第一个被放到新集合去的,因为都用并行流了,说明不在意结果的集合中的元素的顺序了,所以并行流里一般也不会使用findFirst方法,所以,建议使用findAny

package yimeng;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.*;
import java.util.stream.Collectors;

public class StreamTest {
   
    public static void main(String[] args) {
   
        List<UserInfo> list=new ArrayList<>();
        list.add(UserInfo.builder().userId(1L).userName("张三").build());
        list.add(UserInfo.builder().userId(2L).amount(10).userName("李四").build());
        list.add(UserInfo.builder().userId(3L).userName("王五").build());
        list.add(UserInfo.builder().userId(3L).amount(15).build());
        list.add(UserInfo.builder().userId(5L).amount(15).userName("1").build());
        list.add(UserInfo.builder().userId(6L).amount(45).
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值