Java流中的方法
Java 8 引入了 Stream API,它是一个强大的工具,用于处理集合和数组等数据源。
-
过滤(Filter):
filter(Predicate p)
方法根据给定的条件筛选元素,返回一个满足条件的新流。List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); List<Integer> evenNumbers = numbers.stream() .filter(num -> num % 2 == 0) .collect(Collectors.toList());
-
累加(Reduce):
reduce(T identity, BinaryOperator op)
方法可以对流中的元素进行累加或合并操作,返回一个单一的值。List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5); int sum = numbers.stream() .reduce(0, Integer::sum);
-
查找第一个(Find First):
findFirst()
方法返回流中的第一个元素。findAny() : 返回流中的任意一个元素;如果流是空的,则返回空;
【大多数情况下,数据量不大的情况下,findAny()也会返回第一个元素,此时效果与findFirst()一致】
查找第一个元素 List<Integer> numbers = Arrays.asList(1, 3, 5, 2, 4, 6); Optional<Integer> firstEven = numbers.stream() .filter(num -> num % 2 == 0) .findFirst(); if (firstEven.isPresent()) { // 如果Optional中存在值,输出该值 System.out.println("第一个偶数是: " + firstEven.get()); } else { // 如果Optional为空,即没有找到偶数 System.out.println("列表中没有偶数"); } isPresent()方法用来检查Optional对象是否包含一个非空值
-
查找最后一个(Find Last):虽然流 API 没有直接的
findLast()
方法,但是可以通过排序或使用reduce()
方法来实现。查找最后一个元素 List<Integer> numbers = Arrays.asList(2, 4, 6, 1, 3, 5); Optional<Integer> lastOdd = numbers.stream() .filter(num -> num % 2 != 0) .reduce((first, second) -> second);
-
统计(Count):
count()
方法返回流中元素的总数。统计被二整除的元素个数 List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); long evenCount = numbers.stream() .filter(num -> num % 2 == 0) .count();
为什么使用流?
-
函数式编程:流 API 支持函数式编程,可以写出更简洁、声明式的代码。
-
聚合操作:流 API 提供了如
filter
、map
、reduce
等聚合操作,简化了数据处理。 -
并行处理:流 API 可以很容易地进行并行处理,提高性能。
-
无副作用:流操作不修改原始数据源,保证了数据的不变性。
-
惰性求值:流操作是惰性求值的,只有当必要时才计算结果,这可以提高效率。
Map 和 Map Filter 的区别
在 Java 流中,map
是一个中间操作,用于将流中的每个元素转换成另一种形式。
-
Map:
map(Function f)
方法接收一个函数作为参数,将流中的每个元素应用该函数,返回一个新流。示例:
将list里的名字转成他的字符长度 List<String> names = Arrays.asList("Alice", "Bob", "Charlie"); List<Integer> lengths = names.stream() .map(String::length) .collect(Collectors.toList());
1. 创建字符串列表
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
这行代码创建了一个包含三个字符串("Alice"、"Bob"、"Charlie")的
List
集合。2. 创建流并应用
map
操作names.stream() .map(String::length)
-
names.stream()
: 将List
集合转换成了一个流(Stream)。流允许以声明式的方式处理数据集合。 -
.map(String::length)
:map
是一个中间操作,它将一个函数应用于流中的每个元素,并产生一个新的流。这里的String::length
是一个方法引用,它引用了String
类的length()
实例方法。length()
方法返回字符串的字符数。因此,.map(String::length)
将流中的每个字符串转换成了它们的长度。
3. 收集结果到列表
.collect(Collectors.toList());
-
.collect()
: 是一个终止操作,它将流中的元素汇总或归纳成一个结果。在这个例子中,collect
操作将流中的元素(字符串的长度)收集到一个新的列表中。 -
Collectors.toList()
: 是一个收集器,它定义了如何将流中的元素收集到一个List
集合中。
4. 存储结果
List<Integer> lengths = ...
-
lengths
: 是一个List<Integer>
类型的变量,它存储了collect
操作的结果。在这个例子中,lengths
将包含原始字符串列表中每个字符串的长度。
5. 输出结果
执行上述代码后,
lengths
列表将包含以下元素:-
"Alice"的长度是5
-
"Bob"的长度是3
-
"Charlie"的长度是7
因此,
lengths
列表的结果是:[5, 3, 7]
-
-
Map Filter:实际上流 API 中并没有
mapFilter
这样的方法,但filter
方法结合map
方法可以达到类似mapFilter
的效果,即先过滤后映射。示例:
List<Integer> evenLengths = names.stream() .filter(name -> name.length() % 2 == 0) .map(String::length) .collect(Collectors.toList());
创建一个新的列表
evenLengths
,其中只包含这些字符串长度为偶数的字符串的长度。
原始列表:
List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David");假设
names
列表包含了上述几个字符串。流的创建:
names.stream()通过调用
names
列表的stream()
方法,我们创建了一个字符串流,这是一个元素序列,可以对其执行一系列操作。过滤操作:
.filter(name -> name.length() % 2 == 0)
filter()
是一个中间操作,它接受一个Predicate
函数作为参数。在这个例子中,我们传递了一个Lambda表达式name -> name.length() % 2 == 0
,它检查每个字符串的长度是否为偶数(即长度除以2的余数是否为0)。这个操作会返回一个新流,其中只包含满足条件的字符串。映射操作:
.map(String::length)
map()
也是一个中间操作,它接受一个函数作为参数。这里我们使用了方法引用String::length
,它将流中的每个字符串转换成它们的长度。这个操作同样返回一个新流,这次流中包含的是数字(字符串长度),等同于 Lambda 表达式s -> s.length()
。收集操作:
.collect(Collectors.toList())
collect()
是一个终止操作,它将流中的元素汇总或归纳成一个结果。这里我们使用了Collectors.toList()
收集器,它将流中的元素收集到一个新的List
中。最终结果:
List<Integer> evenLengths = ...最终,我们得到了一个名为
evenLengths
的新列表,其中包含了原始列表中长度为偶数的字符串的长度。示例执行过程
假设
names
列表包含以下字符串:
Alice(长度为5,奇数)
Bob(长度为3,奇数)
Charlie(长度为7,奇数)
David(长度为5,奇数)
执行过滤操作后,由于我们只保留长度为偶数的字符串,所以没有任何字符串被选中。
执行映射操作时,由于没有字符串被过滤出来,所以映射操作没有元素可以处理,最终得到的流是空的。
最后,通过收集操作,我们得到的
evenLengths
列表将是空的。注意事项
流操作是惰性求值的,这意味着实际的计算会在调用终止操作时才执行。
流操作可以并行执行,以提高性能,但在这个例子中,我们使用的是顺序流。
collect()
方法需要一个Collector接口的实现,Collectors.toList()
是Collector接口的一个实现,用于将流元素收集到列表中。通过这种方式,Stream API 提供了一种声明式的处理集合数据的方法,使代码更加简洁、易读,并能够利用现代多核处理器的计算能力。
示例
-
Map 示例:
List<String> words = Arrays.asList("one", "two", "three"); List<Integer> wordLengths = words.stream() .map(String::length) .collect(Collectors.toList());
-
MapFilter 示例:
List<String> words = Arrays.asList("one", "two", "three", "four"); List<String> evenLengthWords = words.stream() .filter(word -> word.length() % 2 == 0) .map(word -> word.toUpperCase()) .collect(Collectors.toList());
在这个示例中,我们首先过滤出长度为偶数的单词,然后将它们转换为大写形式。
flatMap
通俗易懂的讲解
flatMap
是 Java 8 Stream API 中的一个方法,它用于将流中的每个元素转换成另一个流,然后将这些流“扁平化”(或“展平”)成一个新的流。简单来说,flatMap
可以看作是 map
和 flatten
(扁平化)两个操作的结合。
flatMap
与 map
的区别
-
map: 这个操作将一个函数应用于流中的每个元素,返回一个新的流。每个元素被转换成另一个对象,但流的结构不变,即一个元素映射后仍然是一个元素。
-
flatMap: 这个操作也是将一个函数应用于流中的每个元素,但它允许你将每个元素转换成一个流,然后
flatMap
会将这些流连接起来,形成一个统一的流。
map
的应用场景
-
当你需要对集合中的每个元素应用一个函数,并且想要保持元素的一一对应关系时,使用
map
。 -
例如,你可能有一个字符串列表,想要将每个字符串转换成它的大写形式。
-
当你需要将集合中的每个元素转换成另一种形式时,比如将字符串列表转换为它们的长度列表。
flatMap
的应用场景
-
当你需要将一个元素转换成多个元素,并且想要在结果流中将它们“展平”时,使用
flatMap
。 -
例如,如果你有一个嵌套列表(列表的列表),你可能想要将它们展平成一个单一的列表。
Map Filter
的应用场景
当你需要先根据条件筛选出一部分元素,然后对这些元素进行转换时。比如,筛选出长度为偶数的字符串,然后获取它们的长度。
map
的优点
-
代码简洁:
map
提供了一种简洁的方式来转换流中的元素。 -
易于理解:由于其直观的操作,
map
很容易理解和使用。
flatMap
的优点
-
强大的数据转换能力:
flatMap
允许你将复杂数据结构(如嵌套列表)展平成更简单的结构。 -
灵活性:
flatMap
提供了更多的灵活性来处理更复杂的数据转换逻辑。
flatMap
示例
假设我们有一个嵌套的字符串列表,我们想要将其展平成一个单一的列表:
List<List<String>> nestedList = Arrays.asList( Arrays.asList("Alice", "Bob"), Arrays.asList("Charlie", "David", "Eve") ); List<String> flatList = nestedList.stream() .flatMap(innerList -> innerList.stream()) .collect(Collectors.toList());
在这个例子中:
-
nestedList.stream()
创建了一个包含嵌套列表的流。 -
flatMap(innerList -> innerList.stream())
将每个嵌套列表转换成一个流,然后展平成一个单一的流。这里使用了 Lambda 表达式innerList -> innerList.stream()
,它对每个嵌套列表调用stream()
方法。 -
collect(Collectors.toList())
将展平后的流收集到一个新的列表中。
执行上述代码后,flatList
列表将包含所有嵌套列表中的元素:
["Alice", "Bob", "Charlie", "David", "Eve"]
这个例子展示了 flatMap
如何将一个嵌套列表结构展平成一个简单的列表,这是 map
无法做到的。使用 flatMap
可以处理更复杂的数据转换任务,特别是涉及到嵌套结构或需要将一个元素转换成多个元素的情况。
总结
流 API 是 Java 8 中处理集合的强大工具,它提供了一种声明式、函数式的方法来处理数据。map
用于转换元素,而 filter
用于筛选元素。结合使用这两个方法可以达到特定条件下的元素转换,适用于各种数据处理场景。