将流收集到Java不可变集合中
在Java 8中引入了流(Streams)和收集器(Collectors)的概念。流是一系列对象的序列,我们从输入源(如数组、列表等)生成流,并支持聚合操作,例如筛选、映射、限制、缩减等。我们使用流将数据进行管道传输,并最终将其收集到某种形式的收集器中。收集器是存储流处理结果的容器,收集器可以是列表、映射或集合。
不可变集合是一种创建后无法更改其值的集合。我们需要不可变集合,因为许多时候我们可以拥有不会改变的数据集合,比如一年中的月份列表或一周中的天数等。使这些数据不可变(即数据不可改变)使得它们更加内存和空间高效。它们也是固有的线程安全的。总的来说,不可变对象需要比它们的可变对应对象占用更少的内存。
当我们构造一个对象后,如果它的状态不会改变,则将该对象视为不可变的。在创建一个不可变的集合实例之后,只要存在对它的引用,它就会始终保留相同的数据。
方法: 根据Java版本的不同,可以使用各种方法创建不可变对象的方法。
- Java 10之前
- 使用”collectingAndThen”将流收集到不可修改的结构中
- 谷歌的Guava库
- Java 10之后
- toUnmodifiableList()
- toUnmodifiableMap()
- toUnmodifiableSet()
场景1: Java 10之前
方法1: 将流收集到不可修改的结构。在Java 10之前,Java中没有直接的方法来创建不可变集合。一种方法是创建一个流,并使用”collectingAndThen”方法将其收集到一个不可修改的结构中。
示例:
// Java程序演示将流收集到不可变集合中
// Pre java 10
// 使用collectingAndThen方法
// 从java.util package导入Collections、Collectors和Stream类
import java.util.Collections;
import java.util.stream.Collectors;
import java.util.stream.Stream;
// 主类
// PreJava10ImmutableCollections
public class GFG {
// 主函数
public static void main(String[] args) {
// 创建带有整数元素的自定义输入列表
var unmodifiableList
= Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
.collect(Collectors.collectingAndThen(
Collectors.toList(),
Collections::unmodifiableList));
System.out.println(unmodifiableList);
// 对此类操作将导致异常
unmodifiableList.add(12);
}
}
输出:

方法2: 使用谷歌的Guava库。为此,需要先将JAR文件包括在内。Guava库的JAR文件是下载JAR文件并在eclipse中将其添加到构建路径中。Guava库提供了ImmutableList类。下面是一个例子。
示例:
// Java程序来说明把流集合为不可变集合
// Java 10之前使用Google的Guava库
// 导入Guava库
import com.google.common.collect.ImmutableList;
// 从java.util包中导入类
import java.util.List;
import java.util.stream.IntStream;
// 主类
public class PreJava10ImmutableCollections {
// 主方法
public static void main(String[] args) {
// 使用Guava库
List<Integer> someList
= IntStream.range(0, 15).boxed().collect(
ImmutableList.toImmutableList());
// 打印和显示元素
System.out.println(someList);
}
}
输出:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
注意: 我们也可以实现我们自己的不变列表、映射或集合版本。
场景2:Java 10及以上版本
Java 10引入了通过Collectors类创建不可变集合的函数。我们有三种方法,一种用于List,一种用于Set,一种用于Map。
方法1: 使用 toUnmodifiableList() 方法。假设我们有一个包含50个偶数的流,并从中创建一个不变的列表。
示例:
// Java程序来说明把流集合为不可变集合
// Java 10之后
// 使用toUnmodifiableList()方法
// 从java.util包中导入类
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
// 主类
public class GFG {
// 主方法
public static void main(String[] args)
{
// 创建一个整数类型的Stream类对象
Stream<Integer> evenNumberStream
= Stream.iterate(0, i -> i + 2).limit(50);
// 创建一个整数类型的List类对象
List<Integer> evenNumbers
= (List<Integer>)evenNumberStream.collect(
Collectors.toUnmodifiableList());
// 打印列表中的所有元素
System.out.println(evenNumbers);
// 这些将会导致java.lang.UnsupportedOperationException
evenNumbers.add(90);
// evenNumbers.remove(1);
}
}
输出:

方法2: 使用toUnmodifiableMap()方法
考虑一个Books类的示例。Books对象有两个参数,即‘Id’和‘Name’。我们将生成一个book对象的流。假设我们需要从这个对象的collection中生成一个不可变的Map。为此,我们使用toUnmodifiableMap()函数。
示例:
// Java程序演示如何将流收集到
// 不可变集合中
// Java 10以上版本
// 使用toUnmodifiableMap()方法
// 导入所需库
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
// 主类
// ImmutableCollectionMap
public class GFG {
// 主方法
public static void main(String[] args)
{
// 通过books类型的List类对象创建一本书流,并将其转换为Map类对象
List<Books> libInventory = new ArrayList<Books>();
// 上面创建对象后,添加元素
// 自定义输入
libInventory.add(
new Books(1, "傲慢与偏见"));
libInventory.add(new Books(2, "四签名"));
libInventory.add(
new Books(3, "理智与感性"));
libInventory.add(new Books(4, "曼斯菲尔德公园"));
libInventory.add(
new Books(5, "马特雷斯圈"));
libInventory.add(
new Books(6, "巴斯克维尔的猎犬"));
libInventory.add(new Books(7, "晚安月亮"));
libInventory.add(new Books(
8, "离我的生日还有多少天"));
libInventory.add(
new Books(9, "神秘身份"));
libInventory.add(new Books(10, "谋杀谁谁"));
libInventory.add(new Books(
11, "赫克特·波洛的冒险"));
libInventory.add(
new Books(12, "冰与火之歌"));
// 创建Map类对象
// 声明类型为Integer和String的对象
Map<Integer, String> unmutableInventory
= libInventory.stream().collect(
Collectors.toUnmodifiableMap(
Books::getBookNumber,
Books::getBookName));
// 输出Map对象中的所有元素
System.out.println(unmutableInventory);
// 这将引发异常
unmutableInventory.put(13, "某本书");
}
}
输出:

方法 3: 使用toUnmodifiableSet()方法
实现: 通过流创建不可变集合,我们可以使用toUnmodifiableSet()函数。
// Java程序演示如何将流收集到
// 不可变集合中
// Java 10以上版本
// 使用toUnmodifiableSet()方法
// 导入所需库
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
// 主类
// ImmutableCollectionSet
public class GFG {
// 主方法
public static void main(String[] args)
{
// 创建类型为Double的流对象
Stream<Double> randomDecimals
= Stream.generate(Math::random).limit(30);
// 现在创建类型为Double的Set类对象
Set<Double> randomSet = randomDecimals.collect(
Collectors.toUnmodifiableSet());
// 输出Set对象中的元素
System.out.println(randomSet);
// 这将引发异常
randomSet.add(100.0);
}
}

结论: 这里我们看到了如何在Java中创建不可变集合。我们还看到了基于使用的Java版本创建集合的不同方法。
极客教程