繁体   English   中英

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

[英]Java: “cons” an item to a list

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

什么是最快的方式“ 利弊 ”(在Lisp中/计划的意义上)我的项目创建一个新的不可变列表? 我当然可以做到以下几点,但这似乎是错误的/浪费的:

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);
    }
}

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

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

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

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

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

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

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

你不应该特殊情况下没有孩子的物品。

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

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

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

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()方法也应该返回一个“安全”的数组 ......

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

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

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

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

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方便公共静态最终列表(“常量列表”),还可以让您轻松地为调用者提供给您的类的列表的“防御性副本”。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM