【问题标题】:Java: "cons" an item to a listJava:将项目“cons”到列表中
【发布时间】:2011-08-15 19:54:39
【问题描述】:

我有一个Item,它有一个方法List<Item> getChildren()(它返回一个不可变列表),对于我拥有的每个项目,我需要创建一个项目列表,然后是其子项。

cons”(在 Lisp/Scheme 意义上)我的项目以创建新的不可变列表的最快方法是什么?我当然可以做到以下几点,但这似乎是错误的/浪费的:

public List<Item> getItemAndItsChildren(Item item)
{
    if (item.getChildren.isEmpty())
        return Collections.singletonList(item);
    else
    {
        // would rather just "return cons(item, item.getChildren())"
        // than do the following -- which, although straightforward,
        // seems wrong/wasteful.
        List<Item> items = new ArrayList<Item>();
        items.add(item);
        items.addAll(item.getChildren());
        return Collections.unmodifiableList(items);
    }
}

【问题讨论】:

  • 您的代码对我来说似乎很不错。问候,Stéphane
  • Java Collections 框架中没有cons 方法(我知道,我也刚刚检查过),但是您可以编写一个名为 cons 的方法来执行此操作,因此您不需要不必到处写丑陋的实现。您还可以“跳过”一行代码:items.add(item);,因为您可以调用采用CollectionArrayList 构造函数。
  • 如果您尝试使用cons,是否真的需要遵守List 接口,或者可以放宽该要求吗? List 有一些包袱,允许随机访问、反向迭代等。
  • re: guava -- 我认为 guava 可能有一些东西 -- 我已经在使用它,而我不再倾向于使用 Apache Commons。

标签: java list guava


【解决方案1】:

我会改变我的要求。在大多数情况下,您的界面中不需要ListIterable 就可以了。方法如下:

public Iterable<Item> getItemWithChildren(Item item)    {
    return Iterables.unmodifiableIterable(
        Iterables.concat(
            Collections.singleton(item),
            item.getChildren()
        )
    );
}

这是缩短的版本(带有静态导入):

return unmodifiableIterable(concat(singleton(item), item.getChildren()));

【讨论】:

  • (我在一个不受我控制的接口中调用一个方法,它需要一个List&lt;&gt;,而不是一个可迭代的)
  • @Jason 在这种情况下我会用ImmutableList.copyOf(iterable) 包装它(当然要删除unmodifiableIterable()
【解决方案2】:

通过将头元素连接到可能在其他列表之间共享的尾元素来创建新的不可变列表的能力需要单链表实现。 Java 没有提供这样的开箱即用的东西,所以你的ArrayList 解决方案和任何东西一样好。

假设这些列表是短暂的并且列表中没有数万个元素,它也会相对有效。如果你这样做了,并且如果这个操作占用了你执行时间的很大一部分,那么实现你自己的单链表可能是值得的。

我为提高现有效率所做的一项更改:使用容量(1 + 旧列表的大小)构建新列表。

【讨论】:

  • 很好地解释了使用 cons 的意义,但我认为使用 guava ImmutableList.Builder 的代码可能会更好。
【解决方案3】:

您不需要对没有子项的项进行特殊处理。

public List<Item> getItemAndItsChildren(Item item)
{
    List<Item> items = new ArrayList<Item>();
    items.add(item);
    items.addAll(item.getChildren());
    return Collections.unmodifiableList(items);
}

此外,如果您希望使用一种不冗长的语言,那么 Java 是一个糟糕的选择。我相信您可以在 JVM 上运行的 Groovy 和 Scala 中使用更少的代码来做您喜欢的事情。 (更不用说 JRuby 或 Jython。)

【讨论】:

  • 我不介意代码的数量,我确实介意创建一个新的数组列表,而实际上它并不是必须的。
【解决方案4】:

听起来您正在寻找类似于CompositeList 的东西,类似于Apache Commons' CompositeCollection。一个实现可能像这样幼稚:

public class CompositeList<T> extends AbstractList<T>{
    private final List<T> first, second;

    public CompositeList(List<T> first, List<T> second) {
        this.second = second;
        this.first = first;
    }

    @Override
    public T get(int index) {
        if ( index < first.size() ) {
            return first.get(index);
        } else {
            return second.get(index - first.size());
        }
    }

    @Override
    public int size() {
        return first.size() + second.size();
    }
}

你可以这样使用它:

public List<Item> getItemAndItsChildren(Item item)
{
    return Collections.unmodifiableList( 
        new CompositeList<Item>(Collections.singletonList(item), item.getChildren()) );
}

但是有很多警告使得这样的类难以使用......主要问题是List 接口本身不能强制它是不可修改的。如果您要使用这样的东西,您必须确保此代码的客户端永远不会修改子代!

【讨论】:

  • 所以如果已知子项是不可变的,这可以吗?
  • @Jason:重要的不是项目的不变性,而是您传入的子列表的不变性。
  • 对,我说得不对——我想的是子项的集合,而不是项本​​身。
【解决方案5】:

我使用这些。 (使用 guava 的 ImmutableList 和 Iterables)

/** Returns a new ImmutableList with the given element added */
public static <T> ImmutableList<T> add(final Iterable<? extends T> list, final T elem) {
    return ImmutableList.copyOf(Iterables.concat(list, Collections.singleton(elem)));
}

/** Returns a new ImmutableList with the given elements added */
public static <T> ImmutableList<T> add(final Iterable<? extends T> list, final Iterable<? extends T> elems) {
    return ImmutableList.copyOf(Iterables.concat(list, elems));
}

/** Returns a new ImmutableList with the given element inserted at the given index */
public static <T> ImmutableList<T> add(final List<? extends T> list, final int index, final T elem) {
    return ImmutableList.copyOf(Iterables.concat(list.subList(0, index), Collections.singleton(elem), list.subList(index, list.size())));
}

/** Returns a new ImmutableList with the given element inserted at the given index */
public static <T> ImmutableList<T> add(final List<? extends T> list, final int index, final Iterable<?extends T> elems) {
    return ImmutableList.copyOf(Iterables.concat(list.subList(0, index), elems, list.subList(index, list.size())));
}

但没有一个是有效的。

将项目添加到列表中的示例:

ImmutableList<String> letters = ImmutableList.of("a", "b", "c");
add(letters, 0, "d");

正如@eneveu 指出的那样,对于更有效的不可变/持久集合,您应该查看 pcollections,尽管我不知道该库的质量如何。

【讨论】:

  • Iterables.concat 似乎使用了 Iterators.concat,它返回一个迭代器,如果传递的迭代器也无法删除,则该迭代器无法删除元素,而原始帖子似乎就是这种情况。如果是这样,您应该能够跳过缓慢的 ImmutableList.copyOf() 操作,如果您可以使用 Iterable 而不是 List 接口。
【解决方案6】:

pcollections 是一个你可能感兴趣的持久化 Java 集合库。我前段时间收藏了它,还没有使用它,但该项目似乎比较活跃。

如果你想使用 Guava,你可以使用 Lists.asList(E first, E[] rest) 返回的不可修改视图。它适用于数组,其主要目标是简化 var-args 方法的使用。但我认为你没有理由不能在你的情况下使用它:

public List<Item> getItemAndItsChildren(Item item) {
    return Lists.asList(item, item.getChildren().toArray());
}

返回的List 是一个不可修改的视图,但如果源数组被修改,它可能会改变。在您的情况下,这不是问题,因为 getChildren() 方法返回一个不可变列表。即使它是可变的,toArray() 方法 supposedly returns a "safe" array...

如果你想更加安全,你可以这样做:

public ImmutableList<Item> getItemAndItsChildren(Item item) {
    return ImmutableList.copyOf(Lists.asList(item, item.getChildren().toArray()));
}

注意Lists.asList() 避免了不必要的ArrayList 实例化,因为它是一个视图。此外,当子列表为空时,ImmutableList.copyOf() 将委托给ImmutableList.of(E element)(这与Collections.singletonList() 类似,节省空间)。

【讨论】:

    【解决方案7】:

    您应该使用将要放入其中的确切数字来实例化您的列表,以便在添加更多时消除扩展副本。

    List<Item> items = new ArrayList<Item>();
    

    应该是

    List<Item> items = new ArrayList<Item>(item.getChildren() + 1);
    

    否则,您所做的就是尽可能地使用惯用的 Java。

    另外,您可能会考虑使用Guava 及其ImmutableList 实现而不是Collections.unmodifiableList()

    不同于Collections.unmodifiableList(java.util.List),后者是一个视图 仍然可以更改的单独集合的一个实例 ImmutableList 包含自己的私有数据并且永远不会改变。 ImmutableList 方便公共静态最终列表(“常量 列表”),还可以让您轻松制作列表的“防御性副本” 由来电者提供给您的班级。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-12-27
      • 2021-06-24
      • 1970-01-01
      • 1970-01-01
      • 2018-12-09
      • 1970-01-01
      • 2011-10-12
      • 1970-01-01
      相关资源
      最近更新 更多