Java:好奇的ImmutableList添加

时间:2017-12-07 13:38:11

标签: java collections guava

我不了解这种方法的实现。它的

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)

4 个答案:

答案 0 :(得分:2)

代码正在实施添加到给定的列表。如果输入列表是ImmutableList,则它首先创建一个可变列表(否则它不能添加到它)并将元素复制到它。如果不是,它只使用现有列表。

如果传入的列表为空,则返回ImmutableList有点奇怪,但如果给出非空ArrayList,则返回ImmutableList from django.conf.urls import url from . import views app_name = 'saferdb' urlpatterns = [ url(r'^/$', views.index, name='index'), url(r'^(?P<pk>[0-9]+)/$', views.detail, name='detail'), ] 1}}要添加,但也许这在更广泛的背景下是有意义的,在何处以及如何使用它。但这种不一致肯定是我在代码审查中查询的内容。

答案 1 :(得分:1)

此方法是反模式,不应使用。它摧毁了可变和不可变的数据结构,提供了两种实现中最差的。

  • 如果你正在使用不可变数据结构,你应该在你的类型中清楚地表明 - 强制转换为List失去那个重要的上下文。请参阅&#34;接口&#34;不是ImmutableCollection的实现部分。
  • 如果您正在使用可变数据,则应避免使用线性时间副本,而应利用数据结构的可变性(仔细)。

通常没有意义可互换地使用这两种类型 - 如果要将内容添加到现有集合中,请使用您拥有的可变集合。如果您打算将集合变为不可变,请不要尝试向其添加内容。此方法会丢弃该意图,并将导致运行时错误和/或性能降低。

答案 2 :(得分:0)

ImmutableList

的加法
  

为什么不是一个直截了当的list.add(element)

如果给定列表不可变,则无法调用该方法。实际上你可以,但通常这样的方法将抛出UnsupportedOperationExceptiondocumentation的Guavas ImmutableList#add

  

<强>已过时即可。不支持的操作。
  保证抛出异常并保留列表未修改

然而,该方法的目标似乎是通过创建可变克隆来为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方法可以使这里的事情更有效(它是一个惰性方法),否则我们需要在它们之间创建临时克隆。

但是我们不知道您的方法有哪些目标,因此可能在您的特定方法的上下文中它的作用更有意义

答案 3 :(得分:0)

这种方法不是&#34;直截了当的list.add(element)&#34;是因为此方法旨在能够向ImmutableList添加元素。很明显,这些是不可改变的(如果你看一下,他们的原生add方法throws an UnsupportedOperationException),所以唯一的方法就是&#34;添加&#34;对他们来说就是创建一个新列表。

新返回的列表现在是可变的这一事实是一个奇怪的设计决定,只有更广泛的上下文或来自代码作者的输入才能帮助解决这个问题。

空输入列表将返回不可变列表的特殊情况是另一个奇怪的设计决策。没有条件分支,该函数可以正常工作。

因为此方法返回列表的副本,所以应该小心地将结果分配给某个东西,可能是原始变量:

myList = TheClass.add(myList, newElement);

并注意以下用法实际上无效:

TheClass.add(myList, newElement);