简体   繁体   English

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

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

I have an Item which has a method List<Item> getChildren() (which returns an immutable list) and for each of the items I have, I need to create a list of the item followed by its children. 我有一个Item ,它有一个方法List<Item> getChildren() (返回一个不可变列表),对于我拥有的每个项目,我需要创建一个项目列表,后跟其子项。

What's the quickest way to " cons " (in the Lisp/Scheme sense) my item to create a new immutable list? 什么是最快的方式“ 利弊 ”(在Lisp中/计划的意义上)我的项目创建一个新的不可变列表? I can certainly do the following, but it seems wrong/wasteful: 我当然可以做到以下几点,但这似乎是错误的/浪费的:

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

I'd change my requirements. 我会改变我的要求。 In most cases, you don't need a List in your interface, an Iterable will do nicely. 在大多数情况下,您的界面中不需要ListIterable可以很好地完成。 Here's the method: 这是方法:

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

and here's the shortened version (with static imports): 这是缩短版本(使用静态导入):

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

The ability to create a new immutable list by concatenating a head element to a tail that may be shared between other lists requires a singly-linked list implementation. 通过将head元素连接到可能在其他列表之间共享的尾部来创建新的不可变列表的能力需要单链接列表实现。 Java doesn't provide anything like this out of the box, so your ArrayList solution is as good as anything. Java没有提供开箱即用的任何东西,所以你的ArrayList解决方案就像任何东西一样好。

It's also going to be relatively efficient, assuming that these lists are short-lived and you don't have tens of thousands of element in the list. 假设这些列表是短暂的并且列表中没有数万个元素,它也会相对有效。 If you do, and if this operation is taking a significant portion of your execution time , then implmenting your own single-linked list might be worthwhile. 如果这样做,并且如果此操作占用了大部分执行时间 ,那么实施您自己的单链表可能是值得的。

My one change to improve your existing efficiency: construct the new list with a capacity (1 + size of old list). 我的一个改进是提高现有效率:构建一个容量大的新列表(1 +旧列表的大小)。

You shouldn't need to special case an Item with no children. 你不应该特殊情况下没有孩子的物品。

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

Also, if you are looking to use a language that isn't verbose, then Java is a poor choice. 此外,如果您希望使用非冗长的语言,那么Java是一个糟糕的选择。 I'm sure you can do what you like in far less code in Groovy and Scala which both run on the JVM. 我相信你可以在Groovy和Scala中的代码少得多的地方运行,它们都运行在JVM上。 (Not to mention JRuby or Jython.) (更不用说JRuby或Jython。)

It sounds like you're looking for something like a CompositeList , similar to the Apache Commons' CompositeCollection . 听起来你正在寻找像CompositeList这样的东西,类似于Apache Commons的CompositeCollection An implementation could be as naive as this: 一个实现可能像这样天真:

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

And you could use it like this: 你可以像这样使用它:

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

But there are huge caveats that make such a class difficult to use...the main problem being that the List interface cannot itself mandate that it is unmodifiable. 但是有很多警告使得这样的类难以使用......主要的问题是List接口本身不能强制它是不可修改的。 If you are going to use something like this you must ensure that clients of this code never modify the children! 如果您打算使用这样的东西,您必须确保此代码的客户端永远不会修改这些代码!

I use these. 我用这些。 (using guava's ImmutableList and Iterables) (使用番石榴的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())));
}

But none of them are efficient. 但它们都不是有效的。

Example of prepending/consing an item to a list: 在项目列表中添加/提交项目的示例:

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

For more efficient immutable/persistent collections you should, as @eneveu points out, look at pcollections, although I have no idea what the quality of that library is. 对于更高效的不可变/持久集合,你应该像@eneveu指出的那样,查看pcollections,尽管我不知道该库的质量是什么。

pcollections is a persistent Java collection library you might be interested in. I bookmarked it a while ago, and haven't yet used it, but the project seems relatively active. pcollections是一个你可能感兴趣的持久Java集合库。我前一段时间给它添加了书签,还没有使用它,但项目似乎相对活跃。

If you want to use Guava, you could use the unmodifiable view returned by Lists.asList(E first, E[] rest) . 如果你想使用Guava,你可以使用Lists.asList返回的不可修改的视图(E first,E [] rest) It works with arrays, and its primary goal is to simplify the use of var-args methods. 它适用于数组,其主要目标是简化var-args方法的使用。 But I see no reason you couldn't use it in your case: 但我认为没有理由你不能在你的情况下使用它:

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

The List returned is an unmodifiable view, but it may change if the source array is modified. 返回的List是一个不可修改的视图,但如果修改了源数组,它可能会更改。 In your case, it's not a problem, since the getChildren() method returns an immutable list. 在您的情况下,这不是问题,因为getChildren()方法返回不可变列表。 Even if it were mutable, the toArray() method supposedly returns a "safe" array ... 即使它是可变的, toArray()方法也应该返回一个“安全”的数组 ......

If you want to be extra safe, you could do: 如果你想要更安全,你可以这样做:

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

Note that Lists.asList() avoids un-necessary ArrayList instantiation, since it's a view. 请注意, Lists.asList()避免了不必要的ArrayList实例化,因为它是一个视图。 Also, ImmutableList.copyOf() would delegate to ImmutableList.of(E element) when the children list is empty (which, similarly to Collections.singletonList() , is space-efficient). 此外,当子列表为空(与Collections.singletonList()类似,节省空间ImmutableList.of(E element)时, ImmutableList.copyOf()将委托给ImmutableList.of(E element) )。

You should instantiate your list with the exact number you will be putting into it to eliminate expansion copies when you add more. 您应该使用您将添加的确切数字来实例化您的列表,以便在添加更多内容时消除扩展副本。

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

should be 应该

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

otherwise what you are doing is about as idiomatic Java as you can get. 否则你所做的就像你可以得到的惯用Java一样。

Another thing, is you might consider using Guava and its ImmutableList implementation rather than an Collections.unmodifiableList() . 另一件事是,您可能会考虑使用Guava及其ImmutableList实现而不是Collections.unmodifiableList()

Unlike Collections.unmodifiableList(java.util.List) , which is a view of a separate collection that can still change, an instance of ImmutableList contains its own private data and will never change. Collections.unmodifiableList(java.util.List)不同,后者是一个仍然可以更改的单独集合的视图, ImmutableList的实例包含其自己的私有数据,并且永远不会更改。 ImmutableList is convenient for public static final lists ("constant lists") and also lets you easily make a "defensive copy" of a list provided to your class by a caller. ImmutableList方便公共静态最终列表(“常量列表”),还可以让您轻松地为调用者提供给您的类的列表的“防御性副本”。

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

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