1. 概述
在Java中处理List
时,有时我们可能希望为列表元素设置默认值。例如,在初始化列表时,这可能会很有用。
本教程将探讨在Java中为List
元素设置默认值的不同方法。
2. 使用 Arrays.fill()
方法
假设我们想要创建一个包含五个String
元素的列表,并且希望字符串"new"
作为默认值:
List<String> EXPECTED_LIST = Lists.newArrayList("new", "new", "new", "new", "new");
标准API提供了Arrays.fill()
方法,用于将整个数组填充为同一值。因此,我们可以先使用默认值初始化一个数组,然后使用Arrays.asList()
方法将数组转换为List
。
接下来,让我们实现这个想法。为了简化,我们将使用单元测试断言来验证从每个方法是否可以获取预期的列表:
String[] strings = new String[5];
Arrays.fill(strings, "new");
List<String> result = Arrays.asList(strings);
assertEquals(EXPECTED_LIST, result);
上述实现相当直接。但是,我们应该注意的是,Arrays.asList()
创建了一个固定大小的列表。如果我们尝试向列表添加或删除元素,它会抛出UnsupportedOperationException
:
assertThrows(UnsupportedOperationException.class, () -> result.add("a new string"));
assertThrows(UnsupportedOperationException.class, () -> result.remove(0));
然而,我们可以通过set()
方法更改元素的值,例如:
result.set(2, "a new value");
assertEquals("a new value", result.get(2));
3. 创建 newListWithDefault()
方法
如果我们需要初始化一个可变的列表并设置默认值,我们可以创建一个列表对象,然后添加默认值。当然,我们可以创建一个静态方法来完成这项工作:
static <T> List<T> newListWithDefault(T value, int size) {
List<T> list = new ArrayList<>(size);
for (int i = 0; i < size; i++) {
list.add(value);
}
return list;
}
由于newListWithDefault()
是一个泛型方法,我们可以创建不同类型的列表:
List<String> result = newListWithDefault("new", 5);
assertEquals(EXPECTED_LIST, result);
List<Integer> intList = newListWithDefault(42, 3);
assertEquals(Lists.newArrayList(42, 42, 42), intList);
由于newListWithDefault()
返回一个常规的ArrayList
,我们可以对它应用任何更改,如添加、删除和设置。
4. 如果元素是可变的呢?
在整个探索过程中,我们已经研究了两种初始化带有默认值的列表的方法:使用Arrays.fill()
和我们自定义的newListWithDefault()
方法。
为了简单起见,我们在示例中使用了不可变类型String
作为列表元素。但需要注意的是,当使用可变值时,这些方法的行为会有所不同。在这种情况下,如果涉及可变对象,列表中的所有元素都将引用同一个对象。因此,修改一个元素的值会影响其他所有元素的值。
接下来,让我们以Collections.nCopies()
方法为例,看看问题所在:
static final Date DATE_EPOCH = Date.from(Instant.EPOCH);
static final Date DATE_NOW = new Date();
List<Date> dateList = Collections.nCopies(2, Date.from(Instant.EPOCH));
assertEquals(Lists.newArrayList(DATE_EPOCH, DATE_EPOCH), dateList);
dateList.get(0).setTime(DATE_NOW.getTime());
assertEquals(Lists.newArrayList(DATE_NOW, DATE_NOW), dateList);
如图所示,首先,我们将Epoch
设为默认值,并创建一个包含两个Date
的列表。由于列表中的所有元素都引用同一个对象,修改一个元素的值会导致所有元素值的更改。
如果我们需要列表中的元素是不同的对象,我们可以更改newListWithDefault()
方法,将默认的"T value"
替换为Supplier<T>
:
static <T> List<T> newListWithDefault2(Supplier<T> supplier, int size) {
List<T> list = new ArrayList<>(size);
for (int i = 0; i < size; i++) {
list.add(supplier.get());
}
return list;
}
现在,让我们使用newListWithDefault2()
方法初始化一些列表,并验证它们是否包含不同的对象:
List<String> result = newListWithDefault2(() -> "new", 5);
assertEquals(EXPECTED_LIST, result);
List<Date> dateList = newListWithDefault2(() -> Date.from(Instant.EPOCH), 2);
assertEquals(Lists.newArrayList(DATE_EPOCH, DATE_EPOCH), dateList);
dateList.get(0).setTime(DATE_NOW.getTime());
assertEquals(Lists.newArrayList(DATE_NOW, DATE_EPOCH), dateList);
从上面的测试中可以看出,newListWithDefault2()
接受一个Supplier
函数作为参数,并生成带有默认值的列表。此外,当元素是可变类型时,更改一个元素不会影响其他元素。
5. 总结
在这篇文章中,我们探讨了初始化带有默认值的列表的各种方法。此外,我们还学习了如何使用Supplier
函数提供默认值,以便列表中的每个元素引用不同的对象。
如往常一样,这里展示的所有代码片段都可以在GitHub上找到。