Java Stream API 编程实战
1. 前言
本文通过一些Stream API的编程题目和示例,来加强对Java函数式编程的理解
2. 题目合集
2.1 求两个列表的并集(降序排列)
// 给定两个整数列表
List<Integer> list1 = Arrays.asList(1, 2, 3, 4, 5, 6);
List<Integer> list2 = Arrays.asList(4, 5, 6, 7, 8, 9);// 预期输出 — [9, 8, 7, 6, 5, 4, 3, 2, 1]
要求:使用Java Stream API编写代码,找出两个列表的并集——即包含所有不重复元素的列表,并按降序排列。
解决方案
public static void main(String args[]) {// 输出 - [9, 8, 7, 6, 5, 4, 3, 2, 1]List<Integer> list1 = Arrays.asList(1, 2, 3, 4, 5, 6);List<Integer> list2 = Arrays.asList(4, 5, 6, 7, 8, 9);// 方案1:使用Stream.of和flatMapList<Integer> unionDescList = Stream.of(list1, list2).flatMap(Collection::stream) // 将两个列表扁平化为一个流.distinct() // 去重.sorted(Comparator.reverseOrder()) // 降序排序.toList(); // 收集为列表System.out.println("并集(降序): 方案1 - " + unionDescList);// 方案2:使用Stream.concatList<Integer> unionDescList1 = Stream.concat(list1.stream(), list2.stream()).distinct().sorted(Comparator.reverseOrder()).toList();System.out.println("并集(降序): 方案2 - " + unionDescList1);
}
技术要点:
• flatMap用于将多个流合并为一个流
• distinct确保元素唯一性
• sorted(Comparator.reverseOrder())实现降序排列
• Java 16+的toList()方法比collect(Collectors.toList())更简洁
2.2 找出满足目标和的元素对
// 给定两个整数列表
List<Integer> list1 = Arrays.asList(1, 2, 3, 6, 17, 1, 10, 8);
List<Integer> list2 = Arrays.asList(1, 17, 12, 12, 2, 4, 6);// 预期输出 — (a, b)形式的元素对
要求:使用Java Stream API编写程序,找出所有满足以下条件的元素对(a, b):
a来自list1,b来自list2,a和b的和为18。
解决方案
public static void main(String args[]) {List<Integer> list1 = Arrays.asList(1, 2, 3, 6, 17, 1, 10, 8);List<Integer> list2 = Arrays.asList(1, 17, 12, 12, 2, 4, 6);// 方案1:使用flatMapSystem.out.println("----方案1 ----");List<String> result = list1.stream().flatMap(a -> list2.stream().filter(b -> a + b == 18).map(b -> a + "," + b)).toList();result.forEach(System.out::println);// 方案2:使用Stream.of和filterSystem.out.println("----方案2 ----");list1.stream().map(a -> list2.stream().filter(b -> a + b == 18).map(b -> a + "," + b)).flatMap(s -> s).forEach(System.out::println);
}
技术要点:
• flatMap用于处理嵌套流结构
• filter条件判断实现业务逻辑
2.3 找出两个列表的共同元素(交集)
// 给定两个整数列表
List<Integer> list1 = Arrays.asList(10, 20, 30, 40, 50);
List<Integer> list2 = Arrays.asList(30, 40, 50, 60, 70);// 预期输出: [30, 40, 50]
要求:使用Java Stream API,找出两个列表中共同的元素(交集),并将它们作为列表返回。
解决方案
public static void main(String args[]) {List<Integer> list1 = Arrays.asList(10, 20, 30, 40, 50);List<Integer> list2 = Arrays.asList(30, 40, 50, 60, 70);// 方案1:使用filter和flatMapSystem.out.println("----方案1 ----");list1.stream().map(a -> list2.stream().filter(b -> Objects.equals(a, b))).flatMap(s -> s).forEach(System.out::println);// 方案2:使用Filter和containsSystem.out.println("----方案2 ----");list1.stream().filter(list2::contains).toList().forEach(System.out::println);
}
技术要点:
• 方案2更简洁高效,推荐在实际开发中使用
• list2::contains是方法引用,相当于b -> list2.contains(b)
• 注意使用Objects.equals进行null安全的比较
2.4 找出列表中所有重复出现的元素
// 给定一个整数列表
List<Integer> list = Arrays.asList(1, 2, 3, 2, 4, 5, 3, 6);// 预期输出: [2, 3]
要求:使用Stream API,找出并返回列表中所有重复出现的元素。
解决方案
public static void main(String args[]) {List<Integer> list = Arrays.asList(1, 2, 3, 2, 4, 5, 3, 6);list.stream()// groupingBy(Function.identity(), counting()) → 统计每个元素的出现次数.collect(Collectors.groupingBy(Function.identity(), Collectors.counting())) .entrySet().stream().filter(a -> a.getValue() > 1) // 只保留出现次数大于1的元素.map(Map.Entry::getKey) // 提取元素本身.toList() // collect(Collectors.toList()) → 收集重复元素到列表.forEach(System.out::println);
}
技术要点:
• groupingBy+counting是统计元素频率的经典组合
• 使用entrySet().stream()将Map转换为流进行处理
2.5 使用Java Stream查找字符串中第一个不重复的字符
// 示例如下
String input = "swiss";/*
• 's'重复出现
• 'w'是唯一的
• 因此,答案应该是:'w'
*/
解决方案
public static void main(String args[]) {String input = "swiss";Optional<Character> firstNonRepeating = input.chars() // 将String转换为IntStream.mapToObj(c -> (char) c) // 将int值转换为Character对象.collect(Collectors.groupingBy( // 使用LinkedHashMap保持插入顺序,这对找到第一个唯一字符至关重要Function.identity(), LinkedHashMap::new, Collectors.counting() // Collectors.counting() → 统计每个字符的出现次数)).entrySet().stream().filter(entry -> entry.getValue() == 1) // 只保留不重复的字符.map(Map.Entry::getKey).findFirst(); // .map(...).findFirst() → 按插入顺序找到第一个符合条件的字符firstNonRepeating.ifPresent(System.out::println); // 输出: w
}
技术要点:
• 使用LinkedHashMap保持字符顺序是关键
• findFirst返回Optional,安全处理可能为空的情况
3. 总结
掌握Stream API能极大地提升编写代码的简洁性和表达力,为了更好地掌握其使用,一定要在理解每个Stream方法的工作原理的基础上动手实践。