Java: Curious ImmutableList 添加
Java: Curious ImmutableList add
我不明白这个方法的实现。这是
public static <T> List<T> add(List<T> list, T element) {
final int size = list.size();
if (size == 0) {
return ImmutableList.of(element);
} else if (list instanceof ImmutableList) {
if (size == 1) {
final T val = list.get(0);
list = Lists.newArrayList();
list.add(val);
} else {
list = Lists.newArrayList(list);
}
}
list.add(element);
return list;
}
为什么不直截了当list.add(element)
?
代码正在执行添加到给定列表的操作。如果输入列表是 ImmutableList
,它首先创建一个可变列表(否则无法添加)并将元素复制到其中。如果不是,它只使用现有列表。
有点奇怪,如果传入的列表是空的,它 returns 一个 ImmutableList
,但是如果给它一个非空的 [=10],它是一个(可变的)ArrayList
=] 添加到,但也许在更广泛的上下文中使用它以及如何使用它是有意义的。但这种不一致肯定是我在代码审查中要查询的问题。
ImmutableList
的补充
Why not a strightforward list.add(element)
?
如果给定列表不可变,则无法调用该方法。实际上你可以,但通常这样的方法会抛出一个 UnsupportedOperationException
。番石榴 ImmutableList#add
的 documentation 说
Deprecated. Unsupported operation.
Guaranteed to throw an exception and leave the list unmodified.
然而,该方法的目标似乎也是通过创建 可变克隆 支持对 ImmutableList
添加 。因此 直接 实施将是:
public static <T> List<T> add(List<T> list, T element) {
if (list instanceof ImmutableList) {
// Create mutable clone, ArrayList is mutable
list = Lists.newArrayList(list);
}
list.add(element);
return list;
}
其他内容
请注意,类型可能会发生变化。虽然输入可能是 ImmutableList
,但输出肯定 不是 。
您可以通过创建一个临时克隆来保留类型,添加到它(如图所示)然后再次环绕一些 ImmutableList
。然而,这似乎不是这种方法的目标。
另请注意,在相同情况下,该方法可能会向给定列表添加一些内容,而在某些情况下会创建一个新实例。所以方法的调用者必须知道方法有时会改变他的参数,有时不会。对我来说,这是一个非常奇怪的行为,它绝对必须在文档中突出显示,但我不建议这样做。
似乎该方法的另一个目标是保持列表不可变,如果它在方法调用时为空。这有点奇怪,但可能在其 文档 中突出显示。因此他们添加了这个调用:
if (size == 0) {
return ImmutableList.of(element);
}
除此之外,他们通过调用
做一些小事
Lists.newArrayList();
而不是
Lists.newArrayList(list);
如果 list
当前大小为 1
。但是我不确定他们为什么要执行此步骤。在我看来,他们可以保持原样。
所以总而言之,我可能会实现这样的方法
/**
* Creates a new list with the contents of the given list
* and the given element added to the end.
*
* <T> The type of the lists elements
*
* @params list The list to use elements of, the list will not be changed
* @params element The element to add to the end of the resulting list
*
* @return A new list with the contents of the given list and
* the given element added to the end. If the given list was
* of type {@link ImmutableList} the resulting list will
* also be of type {@link ImmutableList}.
**/
public static <T> List<T> add(List<T> list, T element) {
List<T> result;
// Create a Stream of all elements for the result
Stream<T> elements = Stream.concat(list.stream(), Stream.of(element));
// If the list was immutable, make the result also immutable
if (list instanceof ImmutableList) {
result = ImmutableList.of(elements.toArray(T[]::new));
} else {
result = elements.collect(Collectors.toList());
}
return result;
}
这样一来,您将永远不会更改参数 list
,并且如果是的话,您还将保留列表 ImmutableList
。使用 Stream#concat
方法可以使这里的事情更高效(这是一种惰性方法),否则我们需要在两者之间创建临时克隆。
但是我们不知道你的方法有哪些目标,所以可能在你的特定方法的上下文中它做了什么更有意义.
此方法不是"a straightforward list.add(element)
"的原因是因为此方法旨在能够向ImmutableList
添加元素。很明显,它们是不可变的(如果你看的话,它们的原生 add
方法 throws an UnsupportedOperationException
)因此对它们 "add" 的唯一方法是创建一个新列表。
新 returned 列表现在是可变的这一事实是一个奇怪的设计决定,只有更广泛的上下文或代码作者的输入才能帮助解决这个问题。
空输入列表 return 不可变列表的特殊情况是另一个奇怪的设计决定。如果没有该条件分支,该函数将正常工作。
因为这个方法return是列表的一个副本,你应该小心地将结果分配给一些东西,可能是原始变量:
myList = TheClass.add(myList, newElement);
并注意以下用法实际上什么都不做:
TheClass.add(myList, newElement);
此方法是一种反模式,不应使用。它改变了可变和不可变的数据结构,提供了两种实现中最糟糕的一种。
- 如果您使用的是不可变数据结构,您应该在类型中明确说明 - 强制转换为
List
会失去重要的上下文。请参阅 "Interfaces" 而不是 ImmutableCollection
的实现 部分。
- 如果您使用的是可变数据,则应避免进行线性时间复制,而应(谨慎)利用数据结构的可变性。
互换使用这两种类型通常没有意义 - 如果您想向现有集合中添加内容,请使用您拥有的可变集合。如果您希望集合不可变,请不要尝试向其中添加内容。此方法放弃了该意图,将导致运行时错误 and/or 降低性能。
我不明白这个方法的实现。这是
public static <T> List<T> add(List<T> list, T element) {
final int size = list.size();
if (size == 0) {
return ImmutableList.of(element);
} else if (list instanceof ImmutableList) {
if (size == 1) {
final T val = list.get(0);
list = Lists.newArrayList();
list.add(val);
} else {
list = Lists.newArrayList(list);
}
}
list.add(element);
return list;
}
为什么不直截了当list.add(element)
?
代码正在执行添加到给定列表的操作。如果输入列表是 ImmutableList
,它首先创建一个可变列表(否则无法添加)并将元素复制到其中。如果不是,它只使用现有列表。
有点奇怪,如果传入的列表是空的,它 returns 一个 ImmutableList
,但是如果给它一个非空的 [=10],它是一个(可变的)ArrayList
=] 添加到,但也许在更广泛的上下文中使用它以及如何使用它是有意义的。但这种不一致肯定是我在代码审查中要查询的问题。
ImmutableList
的补充
Why not a strightforward
list.add(element)
?
如果给定列表不可变,则无法调用该方法。实际上你可以,但通常这样的方法会抛出一个 UnsupportedOperationException
。番石榴 ImmutableList#add
的 documentation 说
Deprecated. Unsupported operation.
Guaranteed to throw an exception and leave the list unmodified.
然而,该方法的目标似乎也是通过创建 可变克隆 支持对 ImmutableList
添加 。因此 直接 实施将是:
public static <T> List<T> add(List<T> list, T element) {
if (list instanceof ImmutableList) {
// Create mutable clone, ArrayList is mutable
list = Lists.newArrayList(list);
}
list.add(element);
return list;
}
其他内容
请注意,类型可能会发生变化。虽然输入可能是 ImmutableList
,但输出肯定 不是 。
您可以通过创建一个临时克隆来保留类型,添加到它(如图所示)然后再次环绕一些 ImmutableList
。然而,这似乎不是这种方法的目标。
另请注意,在相同情况下,该方法可能会向给定列表添加一些内容,而在某些情况下会创建一个新实例。所以方法的调用者必须知道方法有时会改变他的参数,有时不会。对我来说,这是一个非常奇怪的行为,它绝对必须在文档中突出显示,但我不建议这样做。
似乎该方法的另一个目标是保持列表不可变,如果它在方法调用时为空。这有点奇怪,但可能在其 文档 中突出显示。因此他们添加了这个调用:
if (size == 0) {
return ImmutableList.of(element);
}
除此之外,他们通过调用
做一些小事Lists.newArrayList();
而不是
Lists.newArrayList(list);
如果 list
当前大小为 1
。但是我不确定他们为什么要执行此步骤。在我看来,他们可以保持原样。
所以总而言之,我可能会实现这样的方法
/**
* Creates a new list with the contents of the given list
* and the given element added to the end.
*
* <T> The type of the lists elements
*
* @params list The list to use elements of, the list will not be changed
* @params element The element to add to the end of the resulting list
*
* @return A new list with the contents of the given list and
* the given element added to the end. If the given list was
* of type {@link ImmutableList} the resulting list will
* also be of type {@link ImmutableList}.
**/
public static <T> List<T> add(List<T> list, T element) {
List<T> result;
// Create a Stream of all elements for the result
Stream<T> elements = Stream.concat(list.stream(), Stream.of(element));
// If the list was immutable, make the result also immutable
if (list instanceof ImmutableList) {
result = ImmutableList.of(elements.toArray(T[]::new));
} else {
result = elements.collect(Collectors.toList());
}
return result;
}
这样一来,您将永远不会更改参数 list
,并且如果是的话,您还将保留列表 ImmutableList
。使用 Stream#concat
方法可以使这里的事情更高效(这是一种惰性方法),否则我们需要在两者之间创建临时克隆。
但是我们不知道你的方法有哪些目标,所以可能在你的特定方法的上下文中它做了什么更有意义.
此方法不是"a straightforward list.add(element)
"的原因是因为此方法旨在能够向ImmutableList
添加元素。很明显,它们是不可变的(如果你看的话,它们的原生 add
方法 throws an UnsupportedOperationException
)因此对它们 "add" 的唯一方法是创建一个新列表。
新 returned 列表现在是可变的这一事实是一个奇怪的设计决定,只有更广泛的上下文或代码作者的输入才能帮助解决这个问题。
空输入列表 return 不可变列表的特殊情况是另一个奇怪的设计决定。如果没有该条件分支,该函数将正常工作。
因为这个方法return是列表的一个副本,你应该小心地将结果分配给一些东西,可能是原始变量:
myList = TheClass.add(myList, newElement);
并注意以下用法实际上什么都不做:
TheClass.add(myList, newElement);
此方法是一种反模式,不应使用。它改变了可变和不可变的数据结构,提供了两种实现中最糟糕的一种。
- 如果您使用的是不可变数据结构,您应该在类型中明确说明 - 强制转换为
List
会失去重要的上下文。请参阅 "Interfaces" 而不是ImmutableCollection
的实现 部分。 - 如果您使用的是可变数据,则应避免进行线性时间复制,而应(谨慎)利用数据结构的可变性。
互换使用这两种类型通常没有意义 - 如果您想向现有集合中添加内容,请使用您拥有的可变集合。如果您希望集合不可变,请不要尝试向其中添加内容。此方法放弃了该意图,将导致运行时错误 and/or 降低性能。