Java9为集合接口List、Set、Map提供了创建不可变实例的工厂方法。这些工厂方法为便利而生,以简单的方式创建这些集合的不可变实例。
Java9之前创建不可变集合
在Java9之前,创建不可变集合通常需要通过其他方式,比如使用Collections.unmodifiableList()
、Collections.unmodifiableSet()
和Collections.unmodifiableMap()
等方法来包装一个可变集合,使其变为不可变。
package com.morris.java9;
import java.util.*;
/**
* Java8创建不可变集合
*/
public class Collection8Demo {
public static void main(String[] args) {
// 创建不可变List
List<String> mutableList = new ArrayList<>(Arrays.asList("one", "two", "three"));
List<String> immutableList = Collections.unmodifiableList(mutableList);
System.out.println(immutableList);
mutableList.add("four");
System.out.println(immutableList);
// 创建不可变Set
Set<String> mutableSet = new HashSet<>(Arrays.asList("one", "two", "three"));
Set<String> immutableSet = Collections.unmodifiableSet(mutableSet);
System.out.println(immutableSet);
// 创建不可变Map
Map<String, Integer> mutableMap = new HashMap<>();
mutableMap.put("one", 1);
mutableMap.put("two", 2);
Map<String, Integer> immutableMap = Collections.unmodifiableMap(mutableMap);
System.out.println(immutableMap);
}
}
运行结果如下:
[one, two, three]
[one, two, three, four]
[one, two, three]
{one=1, two=2}
创建可变集合很简单,但是创建不可变集合则先需要创建一个可变集合,然后再使用Collections.unmodifiableXxx
来创建不可变集合。为什么会这么复杂?因为不可变集合一旦创建元素是固定的,也就无法使用add/put/remove等方法来修改集合中的元素。
Collections.unmodifiableList()的底层会使用静态代理模式将原来的ArrayList封装为UnmodifiableList类,对UnmodifiableList类的修改操作都会抛出异常,UnmodifiableList类的add/put/remove方法源码如下:
public E set(int index, E element) {
throw new UnsupportedOperationException();
}
public void add(int index, E element) {
throw new UnsupportedOperationException();
}
public E remove(int index) {
throw new UnsupportedOperationException();
}
然而,这种方式仍然依赖于原始的可变集合,如果原始集合被修改,那么这些不可变集合的行为就会变得不可预测。在上面的例子中可以看到对原始集合进行添加元素,不可变集合中的元素也被修改了。
Java9创建不可变集合
在Java9中,为集合接口(List、Set、Map)提供了创建不可变实例的工厂方法,其中最常用的方法是of()。这个方法用于定义三种集合的不可变实例,参数即为不可变实例的所有元素。
package com.morris.java9;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Java9创建不可变集合
*/
public class Collection9Demo {
public static void main(String[] args) {
// 创建不可变List
List<String> immutableList = List.of("one", "two", "three");
System.out.println(immutableList);
// 创建不可变Set
Set<String> immutableSet = Set.of("one", "two", "three");
System.out.println(immutableSet);
// 创建不可变Map
Map<String, Integer> immutableMap = Map.of("one", 1, "two", 2);
System.out.println(immutableMap);
}
}
运行结果如下:
[one, two, three]
[three, two, one]
{two=2, one=1}
对于List、Set和Map三个接口的of()方法,重载方法的参数有0~10个不等。如果要创建的不可变集合的元素数量超过了10个,就不能再用of()方法了。
对于List和Set的of()方法,重载方法还包含了一个参数args用于接受数量不定的值,这样就可以创建包含任意数量的List、Set。
List的12个重载of()方法:
static <E> List<E> of() {
return (List<E>) ImmutableCollections.EMPTY_LIST;
}
static <E> List<E> of(E e1) {
return new ImmutableCollections.List12<>(e1);
}
static <E> List<E> of(E e1, E e2) {
return new ImmutableCollections.List12<>(e1, e2);
}
static <E> List<E> of(E e1, E e2, E e3) {
return ImmutableCollections.listFromTrustedArray(e1, e2, e3);
}
static <E> List<E> of(E e1, E e2, E e3, E e4) {
return ImmutableCollections.listFromTrustedArray(e1, e2, e3, e4);
}
static <E> List<E> of(E e1, E e2, E e3, E e4, E e5) {
return ImmutableCollections.listFromTrustedArray(e1, e2, e3, e4, e5);
}
static <E> List<E> of(E e1, E e2, E e3, E e4, E e5, E e6) {
return ImmutableCollections.listFromTrustedArray(e1, e2, e3, e4, e5,
e6);
}
static <E> List<E> of(E e1, E e2, E e3, E e4, E e5, E e6, E e7) {
return ImmutableCollections.listFromTrustedArray(e1, e2, e3, e4, e5,
e6, e7);
}
static <E> List<E> of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8) {
return ImmutableCollections.listFromTrustedArray(e1, e2, e3, e4, e5,
e6, e7, e8);
}
static <E> List<E> of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8, E e9) {
return ImmutableCollections.listFromTrustedArray(e1, e2, e3, e4, e5,
e6, e7, e8, e9);
}
static <E> List<E> of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8, E e9, E e10) {
return ImmutableCollections.listFromTrustedArray(e1, e2, e3, e4, e5,
e6, e7, e8, e9, e10);
}
static <E> List<E> of(E... elements) {
switch (elements.length) { // implicit null check of elements
case 0:
@SuppressWarnings("unchecked")
var list = (List<E>) ImmutableCollections.EMPTY_LIST;
return list;
case 1:
return new ImmutableCollections.List12<>(elements[0]);
case 2:
return new ImmutableCollections.List12<>(elements[0], elements[1]);
default:
return ImmutableCollections.listFromArray(elements);
}
}
Set和List一样,同样有12个重载of()方法。
而如果要创建的不可变哈希(HashMap)的数量超过了10个,就不能再用of()方法了,而需要使用ofEntries方法。
Map.ofEntries()的使用如下:
package com.morris.java9;
import java.util.Map;
/**
* Map.ofEntries(),创建的不可变哈希(HashMap)的数量超过了10个使用
*/
public class MapOfEntriesDemo {
public static void main(String[] args) {
Map<Integer, String> map = Map.ofEntries(
Map.entry(1, "a"),
Map.entry(2, "b"),
Map.entry(3, "c"),
Map.entry(26, "z"));
System.out.println(map);
}
}
除了of()方法,Java还提供了copyOf()方法,这个方法可以用来创建一个包含另一个集合所有元素的不可变版本。
List.copyOf()的使用如下:
package com.morris.java9;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
/**
* List.copyOf,创建一个包含另一个集合所有元素的不可变版本
*/
public class ListCopyOfDemo {
public static void main(String[] args) {
List<String> mutableList = new ArrayList<>(Arrays.asList("one", "two", "three"));
// 创建不可变List
List<String> immutableList = List.copyOf(mutableList);
mutableList.add("four");
System.out.println(immutableList);
}
}
运行结果如下:
[one, two, three]
从运行结果可以看出,原集合的修改不影响新集合。
List.copyOf()方法的源码如下:
static <E> List<E> copyOf(Collection<? extends E> coll) {
return ImmutableCollections.listCopy(coll);
}
static <E> List<E> listCopy(Collection<? extends E> coll) {
if (coll instanceof List12 || (coll instanceof ListN<?> c && !c.allowNulls)) {
return (List<E>)coll;
} else if (coll.isEmpty()) { // implicit nullcheck of coll
return List.of();
} else {
return (List<E>)List.of(coll.toArray());
}
}
List.copyOf()方法底层使用List.of()来构建不可变集合。
对比两者的区别
安全性
在Java 9之前,虽然使用了Collections.unmodifiableXXX()方法,但是如果原始的可变集合被修改,那么不可变集合的语义就会被破坏,可能会导致UnsupportedOperationException异常。
在Java 9及以后,使用工厂方法创建的集合是真正不可变的,不依赖于任何外部可变集合,因此更加安全。
简洁性
Java 9之前的做法需要首先创建一个可变集合,然后再调用Collections.unmodifiableXXX()来创建不可变版本,代码相对繁琐。
Java 9及以后的做法直接通过工厂方法一步到位,代码更加简洁。
性能
工厂方法创建的不可变集合在内部实现上可能进行了优化,因此可能在某些情况下具有更好的性能。
灵活性
Java 9之前的做法在处理大量元素时可能需要额外的步骤,例如使用Arrays.asList()或循环添加元素到集合中。
Java 9的工厂方法提供了多个重载版本,可以方便地处理不同数量的元素,同时还可以通过copyOf()方法从现有集合创建不可变版本,提供了更多的灵活性。
总的来说,Java 9引入的集合不可变实例工厂方法提高了创建不可变集合的便捷性、安全性和性能,是Java集合API的一个显著改进。