Java:“缺点”列表中的项目

时间:2011-08-15 19:54:40

标签: java list guava

我有一个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);
    }
}

7 个答案:

答案 0 :(得分:7)

我会改变我的要求。在大多数情况下,您的界面中不需要ListIterable可以很好地完成。这是方法:

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

这是缩短的版本(使用静态导入):

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

答案 1 :(得分:3)

通过将head元素连接到可能在其他列表之间共享的尾部来创建新的不可变列表的能力需要单链接列表实现。 Java没有提供开箱即用的任何内容,因此您的ArrayList解决方案与任何内容一样好。

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

我改变现有效率的一个改变:构建一个容量大的新列表(1 +旧列表的大小)。

答案 2 :(得分:2)

你不需要特殊情况下没有孩子的物品。

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

此外,如果您希望使用非冗长的语言,那么Java是一个糟糕的选择。我相信你可以在Groovy和Scala中的代码少得多的地方运行,它们都运行在JVM上。 (更不用说JRuby或Jython了。)

答案 3 :(得分:2)

听起来你正在寻找像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界面本身不能强制它是不可修改的。如果你打算使用这样的东西,你必须确保这段代码的客户永远不会修改孩子们!

答案 4 :(得分:2)

我用这些。 (使用番石榴的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,虽然我不知道该库的质量是什么。

答案 5 :(得分:1)

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()

答案 6 :(得分:0)

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

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方便公共静态最终列表(“常量   列出“),还可以轻松制作列表的”防御性副本“   由来电者提供给您的班级。