Java-Stream-API-的使用
目录
Java Stream API 的使用
java8引入的java.util.stream.Stream流操作,使得访问和操作数组(Array)、集合(Collection)变得非常方便和优雅。
1、过滤元素和转化元素类型
private static void filterMapToInt() {
List<String> list = new ArrayList<>();
list.add("12");
list.add("ab");
list.add("34");
int[] array = list.stream().filter(str -> {
Pattern pattern = Pattern.compile("\\d+");
return pattern.matcher(str).find();
}).mapToInt(Integer::parseInt).toArray();
System.out.println(Arrays.toString(array)); // [12, 34]
}
2、平铺流元素和转化
private static void flatMapStreamOfMap() {
List<String> list = new ArrayList<>();
list.add("12,ab,34");
list.add("11,22,bb");
Stream<String> stream = list.stream();
Stream<String> stringStream = stream.flatMap(str -> Stream.of(str.split(",")).map(String::toUpperCase));
System.out.println(stringStream.toList()); // [12, AB, 34, 11, 22, BB]
}
3、嵌套类型转为列表
private static void mapMulti() {
List<Object> objects = List.of(1, List.of(2, List.of(3, 4)), 5);
Stream<Object> objectStream = objects.stream().mapMulti(StreamTest::expandIterable);
List<Integer> list = objectStream.map(o -> Integer.parseInt(o.toString())).toList();
System.out.println(list); // [1, 2, 3, 4, 5]
}
static void expandIterable(Object e, Consumer<Object> c) {
if (e instanceof Iterable<?> elements) {
for (Object ie : elements) {
expandIterable(ie, c);
}
} else if (e != null) {
c.accept(e);
}
}
4、多个数据类型转为单一类型
private static void mapMulti2() {
Map<String, Object> map = new HashMap<>();
map.put("aa", new Object());
map.put("23.3", new Object());
List<Object> objects = List.of(1, map, 1.345f, List.of("bb", 2.45d));
// 整数不变,浮点数转化为整数,字符串丢弃,map只需要key
Stream<Object> objectStream = objects.stream().mapMulti(StreamTest::handleMapMulti);
System.out.println(objectStream.map(object -> Integer.parseInt(object.toString())).toList()); // [1, 23, 1, 2]
}
static void handleMapMulti(Object e, Consumer<Object> c) {
if (e instanceof Iterable<?> elements) {
for (Object ie : elements) {
handleMapMulti(ie, c);
}
} else if (e != null) {
Object intTarget = e;
if (e instanceof Map<?, ?> map) {
handleMapMulti(map.keySet(), c);
}
if (intTarget instanceof Float f) {
intTarget = Math.round(f);
}
if (intTarget instanceof Double d) {
intTarget = (int) Math.round(d);
}
if (intTarget instanceof String str) {
try {
intTarget = (int) Float.parseFloat(str);
} catch (NumberFormatException _) {
}
}
if (intTarget instanceof Integer integer) {
c.accept(integer);
}
}
}
5、元素去重
private static void distinct() {
List<String> list = new ArrayList<>();
list.add("aa");
list.add("aa");
list.add("bb");
System.out.println(list.stream().distinct().toList()); // [aa, bb]
}
6、无参数排序(元素自己实现了Comparable)
private static void sortedByItself() {
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(3);
list.add(2);
System.out.println(list.stream().sorted().toList()); // [1, 2, 3]
System.out.println(list.stream().sorted(Comparator.reverseOrder()).toList()); // [3, 2, 1] 倒序
}
7、有参数排序(传入Comparator)
private static void sortedByComparator() {
List<String> list = new ArrayList<>();
list.add("xiaoming,96");
list.add("xiaohong,80");
list.add("xiaozhang,100");
List<String> list1 = list.stream().sorted((s1, s2) -> {
String[] info1 = s1.split(",");
String[] info2 = s2.split(",");
int score1 = Integer.parseInt(info1[1]);
int score2 = Integer.parseInt(info2[1]);
return score2 - score1;
}).toList();
System.out.println(list1); // [xiaozhang,100, xiaoming,96, xiaohong,80]
}
8、用于debug程序的peek
private static void debugByPeek() {
List<String> list = Stream.of("one", "two", "three", "four")
.filter(e -> e.length() > 3)
.peek(e -> System.out.println("Filtered value: " + e))
.map(String::toUpperCase)
.peek(e -> System.out.println("Mapped value: " + e)).toList();
System.out.println(list);
}
打印:
Filtered value: three
Mapped value: THREE
Filtered value: four
Mapped value: FOUR
[THREE, FOUR]
9、限制返回个数
private static void limit() {
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(3);
list.add(2);
System.out.println(list.stream().limit(2).toList()); // [1, 3]
}
10、跳过若干个元素
private static void skip() {
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(3);
list.add(2);
System.out.println(list.stream().skip(2).toList()); // [2]
}
11、有条件的获取元素(java9)
private static void taskWhile() {
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// 使用 takeWhile 提取元素,直到遇到第一个大于 5 的元素
Stream<Integer> takenStream = stream.takeWhile(n -> n <= 5);
// 输出提取的元素
takenStream.forEach(System.out::print); // 12345
}
12、有条件的丢弃元素(java9)
private static void dropWhile() {
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// 使用 dropWhile 提取元素,丢弃小于等于 5 的元素
Stream<Integer> takenStream = stream.dropWhile(n -> n <= 5);
// 输出提取的元素
takenStream.forEach(System.out::print); // 678910
}
13、按顺序执行(并行流(多线程)也能按顺序)
private static void foreachOrdered() {
// 在并行流中,forEachOrdered 需要额外的同步机制来保证顺序,因此性能可能不如 forEach。如果顺序不重要,优先使用 forEach
// 如果流本身是无序的(例如 HashSet 的流),forEachOrdered 的行为可能与 forEach 相同
// 在串行流中,forEach 和 forEachOrdered 的行为几乎一致
Stream<Integer> parallelStream = Stream.of(1, 2, 3, 4, 5).parallel();
parallelStream.forEach(System.out::print); // 34152 顺序不保证
Stream<Integer> parallelStream2 = Stream.of(1, 2, 3, 4, 5).parallel();
parallelStream2.forEachOrdered(System.out::print); // 12345 顺序保证
}
14、转为list或者转为数组
private static void toListToArray() {
String s = "1,2,3";
List<String> list = Stream.of(s.split(",")).toList();
Object[] objectArr = Stream.of(s.split(",")).toArray();
String[] strArr = Stream.of(s.split(",")).toArray(String[]::new);
Integer[] intArr = Stream.of(s.split(",")).map(Integer::parseInt).toArray(Integer[]::new);
System.out.println("stream.toList:" + list);
System.out.println("stream.toArray:" + Arrays.toString(objectArr));
System.out.println("stream.toArray(String[]::new):" + Arrays.toString(strArr));
System.out.println("stream.toArray(Integer[]::new):" + Arrays.toString(intArr));
}
打印:
stream.toList:[1, 2, 3]
stream.toArray:[1, 2, 3]
stream.toArray(String[]::new):[1, 2, 3]
stream.toArray(Integer[]::new):[1, 2, 3]
15、将列表转个单个元素(累积函数,根据某个规则将多个元素合并)
private static void reduce() {
List<Integer> numbers = Arrays.asList(1, 2, 3);
Optional<Integer> sum = numbers.stream().reduce(Integer::sum); // 求和
System.out.println(sum.orElse(0)); // 输出 6
Optional<Integer> max = numbers.stream().reduce(Integer::max);// 最大值
System.out.println(max.orElse(0)); // 输出 3
List<String> list = Arrays.asList("Hello ", "world!");
String concat = list.stream().reduce("", String::concat); // 字符调拼接
System.out.println(concat); // Hello world!
// 初始值(identity)
// 必须是累积函数的单位元,即满足:
// accumulator.apply(identity, t) == t
// 例如:加法初始值为0(0 + t = t),乘法初始值为1(1 * t = t)
List<Integer> intList = Arrays.asList(1, 2, 3);
int product = intList.stream().reduce(1, (a, b) -> a * b);
System.out.println(product); // 输出 6
}
16、把流转化为集合、Set或者对流分组和分区
private static void collect() {
// 转为 List (等价于:Stream.of("1", "2", "3").toList())
List<String> list = Stream.of("1", "2", "3").collect(Collectors.toList());
// 转为 Set(自动去重)
Set<String> set = Stream.of("1", "2", "3").collect(Collectors.toSet());
// 转为特定集合类型
TreeSet<String> treeSet = Stream.of("1", "2", "3").collect(Collectors.toCollection(TreeSet::new));
// 直接拼接 (等价于: String.join("", "1", "2", "3");)
String joined = Stream.of("1", "2", "3").collect(Collectors.joining());
// 带分隔符(等价于:String.join(", ", "1", "2", "3");)
String joinedWithComma = Stream.of("1", "2", "3").collect(Collectors.joining(", "));
// 带前缀和后缀
String wrapped = Stream.of("1", "2", "3").collect(Collectors.joining(", ", "[", "]"));
// 平均值
Double avg = Stream.of("1", "2", "3").collect(Collectors.averagingInt(Integer::parseInt));
// 总和 (等价于:Stream.of("1", "2", "3").mapToInt(Integer::parseInt).sum();)
Integer sum = Stream.of("1", "2", "3").collect(Collectors.summingInt(Integer::parseInt));
// 基础用法(键重复会抛出异常)
Map<String, Integer> map = Stream.of(new Student(93, "xiaoming")).collect(
Collectors.toMap(
Student::getName, // 键提取函数
Student::getScore // 值提取函数
)
);
// 处理键冲突(第三个参数定义合并策略)
Map<String, Integer> safeMap = Stream.of(new Student(93, "xiaoming"), new Student(94, "xiaohong"), new Student(6, "xiaohong")).collect(
Collectors.toMap(
Student::getName,
Student::getScore,
Integer::sum // 合并重复键的值
)
);
// 自定义 Map 类型(如 LinkedHashMap)
Map<String, Integer> linkedMap = Stream.of(new Student(93, "xiaoming"), new Student(94, "xiaoming")).collect(
Collectors.toMap(
Student::getName,
Student::getScore,
(oldVal, _) -> oldVal,
LinkedHashMap::new
)
);
// 示例:自定义收集到逗号分隔的字符串
Collector<Student, StringJoiner, String> customCollector =
Collector.of(
() -> new StringJoiner(","), // Supplier(初始化容器)
(j, e) -> j.add(e.getName()), // Accumulator(累加器:添加元素)
StringJoiner::merge, // Combiner(多线程:合并并行结果)
StringJoiner::toString // Finisher(最终转换)
);
String names = Stream.of(new Student(93, "xiaoming"), new Student(94, "xiaohong"))
.collect(customCollector);
System.out.println(names); // xiaoming, xiaohong
// 分区(按布尔条件分为两组)
Map<Boolean, List<Student>> partition = Stream.of(new Student(93, "xiaoming"), new Student(94, "xiaohong"))
.collect(Collectors.partitioningBy(e -> e.getScore() > 93));
System.out.println(partition); // {false=[score=93, name='xiaoming], true=[score=94, name='xiaohong]}
// 按学科分组(默认返回 Map<K, List<T>>)
Map<String, List<Student>> groupByDept = Stream.of(new Student(93, "xiaoming", "English"))
.collect(Collectors.groupingBy(Student::getSubject));
// 分组后统计(下游收集器,统计每个学科有多少人)
Map<String, Long> countByDept = Stream.of(new Student(93, "xiaoming", "English"))
.collect(Collectors.groupingBy(
Student::getSubject,
Collectors.counting() // 统计每组的元素数量
));
}
static class Student {
int score;
String name;
String subject;
}
代码中Student省略getter、setter和构造函数
17、求最大值最小值
private static void minMax() {
Optional<Integer> min = Stream.of(1, 2, 3).min(Integer::compareTo);
System.out.println(min.orElse(0)); // 1
Optional<Integer> max = Stream.of(1, 2, 3).max(Integer::compareTo);
System.out.println(max.orElse(4)); // 3
}
18、计算流包含的元素个数
private static void count() {
System.out.println(Stream.of(1,2,3).count()); // 3
}
19、执行条件匹配的判断
private static void anyMatchAllMatchNoneMatch() {
System.out.println(Stream.of(1, 2, 3).anyMatch(num -> num > 1)); // true
System.out.println(Stream.of(1, 2, 3).allMatch(num -> num > 2)); // false,不是全都大于2
System.out.println(Stream.of(1, 2, 3).noneMatch(num -> num > 1)); // false,存在匹配的数据
}
20、查找流的第一个元素或者任意一个元素
private static void findFirstFindAny() {
Optional<Integer> first = Stream.of(null, 2, 3).findFirst(); // java.lang.NullPointerException
Optional<Integer> one = Stream.of(1, 2, 3).findFirst(); // 1
System.out.println(Stream.of(1, 2, 3).findAny().get()); // 其中一个,也会报空指针异常的可能
}
21、通过构造器生成流
private static void builder() {
// add(T t) 添加元素到流,返回 Builder 自身(支持链式调用)
// accept(T t) 与 add 功能相同,但返回 void(实现 Consumer 接口)
// build() 构建最终的 Stream,之后不能再修改
Stream.Builder<String> builder = Stream.builder();
builder.add("Apple");
builder.add("Banana").add("Cherry"); // 链式调用
Stream<String> stream = builder.build();
stream.forEach(System.out::println); // 输出:Apple Banana Cherry
}
22、无限流的构造以及无限流的终止
private static void iterate() {
// static <T> Stream<T> iterate(T seed, UnaryOperator<T> f)
// iterate是一个用于生成无限流或有限流的静态方法, 生成一个无限流,从初始值seed开始,依次应用UnaryOperator生成后续元素
// 在java8中结合limit来限制个数,java9中允许通过条件(predicate)来终止迭代
// 生成偶数序列:0, 2, 4, 6, ...
Stream<Integer> evenNumbers = Stream.iterate(0, n -> n + 2);
// 通常需要结合 limit() 截断无限流(java8)
evenNumbers.limit(5).forEach(System.out::println); // 输出 0 2 4 6 8
// 生成数字直到小于 10:0, 1, 2, ..., 9 (java9)
Stream<Integer> numbers = Stream.iterate(
0,
n -> n < 10, // 条件:当 n < 10 时继续
n -> n + 1 // 迭代函数
);
numbers.forEach(System.out::println); // 输出 0 到 9
}