Java Stream 必须掌握的几种用法

发布时间 2023-09-18 23:56:45作者: 程序员小波与Bug

Stream 是 Java8 推出的一套简化集合、数组操作的 API,掌握 Stream 的用法将极大的提升我们的编程能力。
image

流的获取

通过 Stream 自带的 API 获取:

// 通过传入可变参数构造
static<T> Stream<T> of(T... values); 
// 指定一个常量 seed,生成从 seed 到常量 f(由 UnaryOperator 返回的值得到)的流
static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f); 
// 生成一个空流
static<T> Stream<T> empty(); 
// 根据一个 Supplier 接口得到一个流
static<T> Stream<T> generate(Supplier<T> s); 

举个例子:

List<String> list = Stream.of("lily", "lucy", "hock", "spider").collect(Collectors.toList());
// list: [lily, lucy, hock, spider]
log.info("list: {}", list); 
...
List<Integer> list = Stream.iterate(1, i -> i * 2).limit(5).collect(Collectors.toList());
// list: [1, 2, 4, 8, 16]
log.info("list: {}", list);
...
List<Object> list = Stream.empty().collect(Collectors.toList());
// list: []
log.info("list: {}", list);
...
List<Object> list = Stream.generate(() -> new Random().nextInt(10)).limit(5).collect(Collectors.toList());
// list: [4, 1, 5, 5, 6]
log.info("list: {}", list);

将一个数组,转为 Stream 的话,除了 Stream.of 之外,还可以通过 Arrays 工具类获取:

static <T> Stream<T> stream(T[] array);

举个例子:

List<String> list = Arrays.stream(new String[]{"1", "2", "3"}).collect(Collectors.toList());
// list: [1, 2, 3]
log.info("list: {}", list);

将一个集合 Collection 接口(List、Set)转为 Stream 时,只需要通过 Collection 接口的默认方法:

// eg:new ArrayList<String>().stream();
default Stream<E> stream();

而将一个流转换为 Array 需要调用 .toArray,来看下:

String[] list = Stream.of("1", "2", "3").toArray(String[]::new);
// list: ["1","2","3"]
log.info("list: {}", JSON.toJSON(list));

流的常用操作

首先我们准备一个 User 对象的集合 userList:

List<User> userList = new ArrayList<>();
userList.add(User.builder().username("lily").age(20).id(1).build());
userList.add(User.builder().username("lucy").age(15).id(2).build());
userList.add(User.builder().username("hulu").age(30).id(3).build());
userList.add(User.builder().username("hulu").age(30).id(3).build());
userList.add(User.builder().username("rust").age(18).id(5).build());

过滤:

// 过滤出 age > 22 的
List<User> users = userList.stream().filter(u -> u.getAge() > 22).collect(Collectors.toList());
// users: [User(id=3, username=hulu, age=30), User(id=3, username=hulu, age=30)]
log.info("users: {}", users);

去重:

users = userList.stream().distinct().collect(Collectors.toList());
// users: [User(id=1, username=lily, age=20), User(id=2, username=lucy, age=15), User(id=3, username=hulu, age=30), User(id=5, username=rust, age=18)]
log.info("users: {}", users);

归约:

Integer ageTotal = userList.stream().map(User::getAge).reduce(0, Integer::sum);
// ageTotal: 113
log.info("ageTotal: {}", ageTotal);

匹配:

boolean hasHulu = userList.stream().anyMatch(u -> u.getUsername().equals("hulu"));
// hasHulu: true
log.info("hasHulu: {}", hasHulu);
boolean allPass18 = userList.stream().allMatch(u -> u.getAge() > 18);
// allPass18: false
log.info("allPass18: {}", allPass18);
boolean noneLessThen10 = userList.stream().noneMatch(u -> u.getAge() < 10);
// noneLessThen10: true
log.info("noneLessThen10: {}", noneLessThen10);

转换:

List<Integer> ageList = userList.stream().map(User::getAge).collect(Collectors.toList());
// ageList: [20, 15, 30, 30, 18]
log.info("ageList: {}", ageList);

List<Integer> list1 = CollUtil.newArrayList(1, 2, 3);
List<Integer> list2 = CollUtil.newArrayList(4, 5, 6);
List<Integer> list3 = CollUtil.newArrayList(7, 8, 9);
List<List<Integer>> cList1 = CollUtil.newArrayList();
cList1.add(list1);
cList1.add(list2);
cList1.add(list3);
List<Integer> lists = cList1.stream().flatMap(Collection::stream).collect(Collectors.toList());
// lists: [1, 2, 3, 4, 5, 6, 7, 8, 9]
log.info("lists: {}", lists);

排序:

users = userList.stream().sorted(Comparator.comparing(User::getAge)).collect(Collectors.toList());
// users: [User(id=2, username=lucy, age=15), User(id=5, username=rust, age=18), User(id=1, username=lily, age=20), User(id=3, username=hulu, age=30), User(id=3, username=hulu, age=30)]
log.info("users: {}", users);

遍历:

userList.stream().forEach(u -> u.setAge(null));
// userList: [User(id=1, username=lily, age=null), User(id=2, username=lucy, age=null), User(id=3, username=hulu, age=null), User(id=3, username=hulu, age=null), User(id=5, username=rust, age=null)]
log.info("userList: {}", userList);
StringBuilder sb = new StringBuilder();
userList.stream().peek(u -> sb.append(u.getUsername() + " ")).collect(Collectors.toList());
// sb: lily lucy hulu hulu rust 
log.info("sb: {}", sb);

跳过:

users = userList.stream().skip(4).collect(Collectors.toList());
// users: [User(id=5, username=rust, age=null)]
log.info("users: {}", users);

计数:

long count = userList.stream().filter(u -> u.getAge() > 25).count();
// count: 2
log.info("count: {}", count);
List<Integer> list = Stream.iterate(0, s -> s + 2).limit(5).collect(Collectors.toList());
// list: [0, 2, 4, 6, 8]
log.info("list: {}", list);

大小值:

Integer maxAge = userList.stream().map(User::getAge).max(Integer::compareTo).get();
// maxAge: 30
log.info("maxAge: {}", maxAge);
Integer minAge = userList.stream().map(User::getAge).min(Integer::compareTo).get();
// minAge: 15
log.info("minAge: {}", minAge);

转 List:

List<Integer> ageList = userList.stream().map(User::getAge).collect(Collectors.toList());
// ageList: [20, 15, 30, 30, 18]
log.info("ageList: {}", ageList);

转 Set:

Set<Integer> ageSet = userList.stream().map(User::getAge).collect(Collectors.toSet());
// ageSet [18, 20, 30, 15]
log.info("ageSet {}", ageSet);

转 Map:

Map<Integer, User> userIdToUserMap = userList.stream().collect(Collectors.toMap(User::getId, u -> u));
// Exception in thread "main" java.lang.IllegalStateException: Duplicate key User(id=3, username=hulu, age=30)
// 	at java.util.stream.Collectors.lambda$throwingMerger$0(Collectors.java:133)
// 	at java.util.HashMap.merge(HashMap.java:1255)
// 	at java.util.stream.Collectors.lambda$toMap$58(Collectors.java:1320)
// 	at java.util.stream.ReduceOps$3ReducingSink.accept(ReduceOps.java:169)
// 	at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1384)
// 	at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:482)
// 	at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:472)
// 	at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
// 	at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
// 	at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499)
// 	at com.example.demo.StreamDemo.main(StreamDemo.java:52)
log.info("userIdToUserMap {}", userIdToUserMap);

以上代码执行会报错,特别注意当 key 重复时,需要手动指定 key 的覆盖策略:

Map<Integer, User> userIdToUserMap = userList.stream().collect(Collectors.toMap(User::getId, u -> u, (k1, k2) -> k1));
// userIdToUserMap {1=User(id=1, username=lily, age=20), 2=User(id=2, username=lucy, age=15), 3=User(id=3, username=hulu, age=30), 5=User(id=5, username=rust, age=18)}
log.info("userIdToUserMap {}", userIdToUserMap);