Java Stream collect 方法生成不同类型集合
Java Stream collect 方法生成不同类型集合
在Java 8引入Stream API后,极大地简化了集合操作。其中,collect
方法是Stream API中非常强大的终端操作之一,它可以将流中的元素收集到各种类型的集合中。本文将深入探讨collect
方法如何生成不同类型的集合,并通过丰富的代码示例帮助理解。
collect
方法的基础理解
collect
方法有多种重载形式,最常用的是接收一个Collector
接口实现作为参数。Collector
接口提供了一种灵活的方式来定义如何将流中的元素累积到一个可变的结果容器中,并且最终将这个结果容器转换为最终的结果类型。
以下是collect
方法的基本调用形式:
Stream<T> stream = Stream.of(elements);
R result = stream.collect(Collector<T, A, R> collector);
这里T
是流中元素的类型,A
是累积过程中使用的可变容器类型,R
是最终结果的类型。
生成List
集合
- 生成
ArrayList
最常见的需求之一是将流中的元素收集到ArrayList
中。Java提供了Collectors.toList()
方法来实现这一点。
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class StreamToListExample {
public static void main(String[] args) {
Stream<String> stream = Stream.of("apple", "banana", "cherry");
List<String> list = stream.collect(Collectors.toList());
System.out.println(list);
}
}
在上述代码中,Stream.of("apple", "banana", "cherry")
创建了一个包含三个字符串的流。通过调用collect(Collectors.toList())
,流中的元素被收集到一个ArrayList
中,并最终打印出来。
- 生成
LinkedList
虽然Collectors.toList()
默认返回ArrayList
,但我们可以通过自定义Collector
来生成LinkedList
。
import java.util.LinkedList;
import java.util.List;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class StreamToLinkedListExample {
public static void main(String[] args) {
Stream<String> stream = Stream.of("apple", "banana", "cherry");
Collector<String, ?, List<String>> toLinkedList = Collector.of(
LinkedList::new,
LinkedList::add,
(left, right) -> { left.addAll(right); return left; }
);
List<String> linkedList = stream.collect(toLinkedList);
System.out.println(linkedList);
}
}
在这段代码中,我们通过Collector.of
方法自定义了一个Collector
。LinkedList::new
用于创建一个新的LinkedList
实例,LinkedList::add
用于将流中的元素添加到LinkedList
中,而合并函数(left, right) -> { left.addAll(right); return left; }
用于处理并行流时的合并操作。
生成Set
集合
- 生成
HashSet
Collectors.toSet()
方法可以将流中的元素收集到HashSet
中。HashSet
不保证元素的顺序,并且会自动去除重复元素。
import java.util.HashSet;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class StreamToHashSetExample {
public static void main(String[] args) {
Stream<String> stream = Stream.of("apple", "banana", "apple", "cherry");
Set<String> set = stream.collect(Collectors.toSet());
System.out.println(set);
}
}
在上述代码中,流中包含重复的字符串“apple”,但通过collect(Collectors.toSet())
收集到HashSet
后,重复元素被去除。
- 生成
TreeSet
如果需要一个有序的Set
,可以使用Collectors.toCollection(TreeSet::new)
来生成TreeSet
。TreeSet
会根据元素的自然顺序或自定义比较器对元素进行排序。
import java.util.Set;
import java.util.TreeSet;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class StreamToTreeSetExample {
public static void main(String[] args) {
Stream<String> stream = Stream.of("banana", "apple", "cherry");
Set<String> treeSet = stream.collect(Collectors.toCollection(TreeSet::new));
System.out.println(treeSet);
}
}
在这段代码中,Stream.of("banana", "apple", "cherry")
创建的流中的元素被收集到TreeSet
中,并按照自然顺序(字典序)进行排序。
生成Map
集合
- 简单的键值对映射
Collectors.toMap
方法可以将流中的元素收集到Map
中。它接收两个函数,一个用于提取键,另一个用于提取值。
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class StreamToMapExample {
public static void main(String[] args) {
Stream<String> stream = Stream.of("apple", "banana", "cherry");
Map<String, Integer> map = stream.collect(
Collectors.toMap(
s -> s,
s -> s.length()
)
);
System.out.println(map);
}
}
在上述代码中,s -> s
用于提取键,即字符串本身,s -> s.length()
用于提取值,即字符串的长度。最终生成的Map
中,键是水果名称,值是名称的长度。
- 处理重复键
当流中的元素可能导致生成的Map
中出现重复键时,toMap
方法有一个重载形式可以处理这种情况。
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class StreamToMapDuplicateKeyExample {
public static void main(String[] args) {
Stream<String> stream = Stream.of("apple", "banana", "apple");
Map<String, Integer> map = stream.collect(
Collectors.toMap(
s -> s,
s -> s.length(),
(oldValue, newValue) -> newValue
)
);
System.out.println(map);
}
}
在这段代码中,(oldValue, newValue) -> newValue
是合并函数。当遇到重复键时,会使用新值替换旧值。如果希望保留旧值,可以返回oldValue
。
- 生成
TreeMap
要生成有序的Map
,可以使用Collectors.toMap
结合TreeMap::new
。
import java.util.Map;
import java.util.TreeMap;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class StreamToTreeMapExample {
public static void main(String[] args) {
Stream<String> stream = Stream.of("banana", "apple", "cherry");
Map<String, Integer> treeMap = stream.collect(
Collectors.toMap(
s -> s,
s -> s.length(),
(oldValue, newValue) -> newValue,
TreeMap::new
)
);
System.out.println(treeMap);
}
}
这里TreeMap::new
用于指定最终生成的Map
类型为TreeMap
,TreeMap
会根据键的自然顺序进行排序。
生成其他类型集合
- 生成
Queue
与生成LinkedList
类似,我们可以自定义Collector
来生成Queue
。
import java.util.LinkedList;
import java.util.Queue;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class StreamToQueueExample {
public static void main(String[] args) {
Stream<String> stream = Stream.of("apple", "banana", "cherry");
Collector<String, ?, Queue<String>> toQueue = Collector.of(
LinkedList::new,
Queue::add,
(left, right) -> { left.addAll(right); return left; }
);
Queue<String> queue = stream.collect(toQueue);
System.out.println(queue);
}
}
在这段代码中,通过自定义Collector
,将流中的元素收集到Queue
(这里是LinkedList
实现的Queue
)中。
- 生成自定义集合类型
假设我们有一个自定义的集合类型MyCustomCollection
,并且它有相应的构造函数和添加元素的方法。
import java.util.stream.Collector;
import java.util.stream.Collectors;
import java.util.stream.Stream;
class MyCustomCollection<T> {
private StringBuilder stringBuilder = new StringBuilder();
public void add(T element) {
stringBuilder.append(element).append(", ");
}
@Override
public String toString() {
if (stringBuilder.length() > 0) {
stringBuilder.setLength(stringBuilder.length() - 2);
}
return stringBuilder.toString();
}
}
public class StreamToCustomCollectionExample {
public static void main(String[] args) {
Stream<String> stream = Stream.of("apple", "banana", "cherry");
Collector<String, ?, MyCustomCollection<String>> toCustomCollection = Collector.of(
MyCustomCollection::new,
MyCustomCollection::add,
(left, right) -> { left.add(right.toString()); return left; }
);
MyCustomCollection<String> customCollection = stream.collect(toCustomCollection);
System.out.println(customCollection);
}
}
在上述代码中,我们自定义了一个MyCustomCollection
类型,它将元素以逗号分隔的形式存储在StringBuilder
中。通过自定义Collector
,将流中的元素收集到MyCustomCollection
实例中。
并行流与collect
方法
当使用并行流时,collect
方法中的Collector
的合并操作变得尤为重要。以生成ArrayList
为例,并行流会将元素分块处理,然后合并这些部分结果。
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class ParallelStreamToListExample {
public static void main(String[] args) {
Stream<String> parallelStream = Stream.of("apple", "banana", "cherry").parallel();
List<String> list = parallelStream.collect(Collectors.toList());
System.out.println(list);
}
}
在这个例子中,Stream.of("apple", "banana", "cherry").parallel()
创建了一个并行流。Collectors.toList()
内部的Collector
实现了正确的合并逻辑,确保并行处理的结果与顺序处理的结果一致。
总结不同集合类型的选择
-
List
ArrayList
:适用于需要快速随机访问的场景,因为它基于数组实现。但在频繁插入和删除元素时性能较差,特别是在列表中间位置操作时。LinkedList
:适合频繁插入和删除元素的场景,因为它基于链表结构。但随机访问性能不如ArrayList
。
-
Set
HashSet
:用于需要快速查找和去除重复元素的场景,不保证元素顺序。TreeSet
:适用于需要元素有序的场景,它会根据自然顺序或自定义比较器对元素进行排序。
-
Map
HashMap
:提供了快速的键值对查找和插入操作,不保证键的顺序。TreeMap
:适用于需要按键排序的场景,无论是自然顺序还是自定义顺序。
-
其他集合
Queue
:用于需要按照特定顺序处理元素的场景,如先进先出(FIFO)。- 自定义集合:当标准集合类型不能满足特定需求时,可以创建自定义集合类型并通过
collect
方法收集元素。
通过深入理解collect
方法如何生成不同类型的集合,并根据实际需求选择合适的集合类型,开发者可以充分利用Java Stream API的强大功能,编写高效、简洁的代码。无论是处理大规模数据集还是简单的集合操作,collect
方法都为我们提供了丰富的可能性。在实际应用中,要根据数据的特点、操作的频率以及性能要求等因素综合考虑选择合适的集合类型和collect
方法的使用方式。同时,并行流的使用可以进一步提升处理大数据集时的效率,但需要注意Collector
的合并操作是否正确实现,以确保结果的准确性。