Java 9用于创建不可变集合的 静态工厂方法

x33g5p2x  于2021-10-17 转载在 Java  
字(4.3k)|赞(0)|评价(0)|浏览(312)

Java 9 在集合 API 中引入了新的工厂方法,使开发人员可以更轻松地创建不可变集合。

在本文中,我将首先解释包含新工厂方法背后的动机,然后带您了解所有方法、它们的用法和实现细节。

为什么 Java 需要一种创建不可变集合的新方法?

好的!告诉我,您如何在 Java 8 或更低版本中使用一些初始键值对创建不可变 Map?

好吧,您必须执行以下操作-

// Instantiate a HashMap
Map<String, Integer> map = new HashMap<>();

// Put Key-Value Pairs in the Map
map.put("one", 1);
map.put("two", 2);
map.put("three", 3);

// Obtain an unmodifiable view of the map
map = Collections.unmodifiableMap(map);

但这太冗长了,不是吗?我们可以做点别的吗?

好吧,您实际上还有一个选择。您可以使用双括号初始化语法来初始化这样的不可变 Map -

Map<String, Integer> map = Collections.unmodifiableMap(
  new HashMap<String, Integer>() {{
    put("one", 1);
    put("two", 2);
    put("three", 3);
}};

这有点不那么冗长,但非常昂贵。双括号技术通过创建一个匿名内部类并提供一个实例初始化块来调用上面的所有 put() 语句。

因此,每次以这种方式创建映射时,都会创建一个不可重用的匿名类,其对象引用由 ClassLoader 保存。这可能会导致内存泄漏和序列化问题。

您可以阅读有关双括号技术及其问题 herehere 的更多信息。

因此,最好避免使用双支撑技术。所以最后,我们剩下的唯一选择是创建一个空映射,然后一个一个地添加键值对。

现在,将创建和初始化 Map 的 Java 方式与 Scala 版本进行比较 -

val map = Map("one" -> 1, "two" -> 2, "three" -> 3);

而且,这是 Kotlin 版本 -

val map = mapOf("one" to 1, "two" to 2, "three" to 3);

您会看到在 Scala 和 Kotlin 等语言中创建和初始化不可变集合是多么容易。

Java 确实需要一种不那么冗长的初始化不可变集合的方法,因此 Java 9 在 List、Set 和 Map 接口中引入了静态工厂方法来创建不可变集合。

用于创建不可变集合的 Java 9 工厂方法

让我们看看新的工厂方法在 Java 9 中是如何工作的。

#### 列表

以下是使用新的 List.of() 工厂方法创建不可变列表的方法 -

List<String> animals = List.of("Cat", "Dog", "Lion", "Tiger");

#### 放

Set 接口也包含一个类似的工厂方法 of() -

Set<String> socialMedia = Set.of("Facebook", "Twitter", "Linkedin", 
        "Pinterest", "Google+");

#### 地图

Map 接口的工厂方法接受 Map.of(k1, v1, k2, v2) 形式的键值对 -

Map<String, Integer> numberMap = Map.of("one", 1, "two", 2, "three", 3);

甜美而简单,不是吗?

请注意,上述 Map.of() 方法只能用于创建最多 10 个键值对的 Map。要创建大小超过 10 的地图,我们必须使用不同的方法。我们很快就会明白这背后的原因。

关于新工厂方法实现的说明

关于新工厂方法的实现有一件有趣的事情需要注意。

尽管提供了工厂方法来创建包含任意数量元素的集合,但 API 由 of() 方法的固定参数重载版本组成,用于创建大小为 10 或更小的集合,以及用于创建大小集合的可变参数重载超过 10 个。

例如,以下是 List.of() 方法的重载版本 -

List<E> List<E>.<E>of(E e1)
List<E> List<E>.<E>of(E e1, E e2)
List<E> List<E>.<E>of(E e1, E e2, E e3)
List<E> List<E>.<E>of(E e1, E e2, E e3, E e4)
List<E> List<E>.<E>of(E e1, E e2, E e3, E e4, E e5)
List<E> List<E>.<E>of(E e1, E e2, E e3, E e4, E e5, E e6)
List<E> List<E>.<E>of(E e1, E e2, E e3, E e4, E e5, E e6, E e7)
List<E> List<E>.<E>of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8)
List<E> List<E>.<E>of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8, E e9)
List<E> List<E>.<E>of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8, E e9, E e10)

List<E> List<E>.<E>of(E... elements)

有 10 个固定参数重载方法和一个接受可变参数的方法。

提供了固定参数重载方法,以在 vararg 调用的情况下节省数组分配、初始化和垃圾收集的开销

因此,对于大小为 10 或更小的列表,使用固定参数方法,对于大小超过 10 的列表,使用 of() 方法的可变参数版本。

Set 接口包含完全相同的方法集。

同样,Map 接口也包含了 of() 工厂方法的 10 个重载版本,用于创建最多 10 个键值对的 Map。但是,对于大小超过 10 个元素的 Map,它有一个不同的工厂方法,称为 ofEntries() -

Map<K,V> Map<K, V>.<K, V>ofEntries(Map.Entry<? extends K,? extends V>... entries)

此方法接受 Map.entry 的可变参数。以下是如何使用此方法创建任何大小的地图 -

import static java.util.Map.entry;

Map<String, Integer> numerMap = Map.ofEntries(
        entry("one", 1), 
        entry("two", 2), 
        entry("three", 3)
);

新工厂方法的使用指南

1. 不允许空值

当你使用新的工厂方法时,你不能用空值初始化 List、Set 或 Map -

// Throws java.lang.NullPointerException
List<String> list = List.of("A", "B", null, "C");
// Throws java.lang.NullPointerException
Set<String> set = Set.of("Computer", "Mobile", null, "TV");
// Throws java.lang.NullPointerException
Map<String, String> asciiMap = Map.of("A", "a", "B", null)

// Throws java.lang.NullPointerException
Map<String, String> map = Map.ofEntries(
    entry("A", "a"),
    entry("B", null)
)

2. 不允许使用重复值初始化Set

您不能使用 of() 工厂方法初始化具有重复值的 Set -

Set<String> set = Set.of("A", "B", "A");
// java.lang.IllegalArgumentException thrown: duplicate element: A

但是请注意,当您像这样创建一个不可变的 Set 时,这会起作用 -

Set<String> set = Collections.unmodifiableSet(
        new HashSet<>(Arrays.asList("A","B","A"))
);
// Works and Produces - set ==> [A, B]

3. 不允许使用重复键初始化 Map

使用新工厂方法初始化 Map 时不允许添加重复键 -

Map.of("A", 1, "A", 2);
// java.lang.IllegalArgumentException thrown: duplicate key: A

但是,如果您使用初始化 Map 的旧方法,则重复的键将被简单地覆盖-

Map<String, Integer> map = new HashMap<>();
map.put("A", 1);
map.put("A", 2);
map = Collections.unmodifiableMap(map)
// Works and Produces - map ==> {A=2}

相关文章