简体   繁体   English

在Java中使用List

[英]Cons'ing a List in Java

Say I have a java.util.List list and I want to create a new List by adding an element e to the beginning of list (ie, I want to cons e and list ). 说我有一个java.util.List list ,我想创建一个新的List中加入元素e到年初list (即我想利弊 elist )。 For example, if list is 例如,如果list

[1,2,3,4]

and e is 5 , then cons(e,list) will be 并且e5 ,那么cons(e,list)将是

[5,1,2,3,4]

It's OK for the elements of list and cons(e,list) to be shared, but list should not be modified. listcons(e,list)的元素可以共享,但不应修改list

What is the simplest and/or most efficient way to implement cons ? 实施cons的最简单和/或最有效的方法是什么? It's OK for the result to be unmodifiable. 结果不可修改是可以的。 Use of the Google Collections Library is allowed. 允许使用Google Collections Library。

What if list is a com.google.common.collect.ImmutableList ? 如果listcom.google.common.collect.ImmutableList怎么办?

public static<T> List<T> cons(List<T> list, T t) {
    ArrayList<T> result = new ArrayList<T>(list);
    result.add(0, t);
    return result;
}

Edited in response to comments: Since the question asked for "the simplest and/or most efficient way to implement cons," I went with "simplest". 编辑回应评论:由于问题是“最简单和/或最有效的方式来实施利弊”,我选择了“最简单”。 I wouldn't be surprised to learn there are more efficient ways. 我不会惊讶地发现有更有效的方法。 Putting the element in before the list is another valid approach, and allocating the correct size initially could probably improve performance. 将元素放在列表之前是另一种有效的方法,并且最初分配正确的大小可能会提高性能。 Premature optimization is the root of all evil. 过早优化是万恶之源。

Clojure provides that kind of Lisp-y stuff. Clojure提供了那种Lisp-y的东西。 While most people think of using Clojure for the language (like I do), the Clojure libraries are all real Java code, and you can use the data structures from Java as just a special library if you'd like. 虽然大多数人都认为使用Clojure语言(就像我一样),但Clojure库都是真正的Java代码,如果你愿意,你可以使用Java中的数据结构作为一个特殊的库。 That way you'd get the ability to do cons and such, and you get the immutability that Clojure uses. 通过这种方式,您可以获得利弊等等,并获得Clojure使用的不变性。 The Clojure data strcutres implement the equivalent Java types too. Clojure数据strcutres也实现了等效的Java类型。

Just a thought from a different direction. 只是从不同的方向思考。

I believe the answer you're really looking for is this: 我相信你真正想要的答案是这样的:

http://functionaljava.googlecode.com/svn/artifacts/2.20/javadoc/fj/data/List.html http://functionaljava.googlecode.com/svn/artifacts/2.20/javadoc/fj/data/List.html

The method is even called cons . 该方法甚至被称为cons

I have no experience with this library. 我没有这个库的经验。 Just heard of it the other day. 前几天听说过它。 I hope it's good! 我希望这很好!

An efficient solution would be to create your own List implementation, lazily delegating. 一个有效的解决方案是创建自己的List实现,懒惰委托。 If unmodifiable, it wouldn't be too much trouble. 如果不可修改,那就不会太麻烦了。 If created through a factory method, then you could even use one of two different underlying implementations depending on whether the argument List implements RandomAccess or not. 如果通过工厂方法创建,那么您甚至可以使用两个不同的底层实现之一,具体取决于参数List是否实现RandomAccess。

For the simplest case (haven't checked to see whether this compiles, no sanity checks, etc.): 对于最简单的情况(没有检查是否编译,没有健全性检查等):

class ConsList<E> extends AbstractList<E>
{
    private final E first;
    private final List<E> rest;

    ConsList( E first, List<E> rest )
    {
        this.first = first;
        this.rest = rest;
    }

    public int get( int index )
    {
        return (index == 0) ? first : rest.get( index - 1 );
    }

    public int size()
    {
        return rest.size() + 1;
    }
}

I'm sure a few of the other methods could be made more efficient, and the sequential case would be better served by extending AbstractSequentialList instead. 我确信其他一些方法可以提高效率,而通过扩展AbstractSequentialList可以更好地满足顺序情况。

This is not well known, but the Sun javac compiler API contains an immutable List implementation that returns new immutable Lists using methods like append() and prepend() . 这不是众所周知的,但Sun javac编译器API包含一个不可变的List实现,它使用append()prepend()等方法返回新的不可变列表。 To use it, you need tools.jar on the classpath, and this import statement: 要使用它,您需要在类路径上使用tools.jar ,并且需要以下import语句:

import com.sun.tools.javac.util.List;

Sample Code: 示例代码:

final List<Integer> list = List.of(6, 7);
System.out.println(list);

final List<Integer> newList =
    list.prepend(5)                       // this is a new List
        .prependList(List.of(1, 2, 3, 4)) // and another new List
        .append(8)                        // and another
        .reverse();                       // and yet another
System.out.println(newList);

System.out.println(list == newList);

System.out.println(list.getClass());
System.out.println(newList.getClass());

Output: 输出:

6,7 6,7
8,7,6,5,4,3,2,1 8,7,6,5,4,3,2,1
false
class com.sun.tools.javac.util.List class com.sun.tools.javac.util.List
class com.sun.tools.javac.util.List class com.sun.tools.javac.util.List

I know this is an internal API and shouldn't be used in production, but it's great fun (finally a java collection that feels right). 我知道这是一个内部API,不应该在生产中使用,但它非常有趣(最后一个感觉正确的java集合)。

Note: All standard mutable methods from the Collection and List interfaces (like add() addAll() ) throw UnsupportedOperationException , obviously. 注意:显然, CollectionList接口中的所有标准可变方法(如add() addAll() )都会抛出UnsupportedOperationException

Reference: 参考:

Because you mention the cons function, I will assume that you're approaching this problem with the conceptual model of linked lists composed of cons cells. 因为你提到了cons函数,我将假设你正在使用由cons单元组成的链表的概念模型来解决这个问题。 Specifically, I assume that you're thinking of each list having a car (the first element) and a cdr (the sublist containing all following elements). 具体来说,我假设您正在考虑每个列表都有一个car (第一个元素)和一个cdr (包含所有后续元素的子列表)。

Java supports linked lists as java.util.LinkedList . Java支持链表作为java.util.LinkedList These are good for linear traversal and can have elements inserted very efficiently. 这些对于线性遍历是有益的,并且可以非常有效地插入元素。 These are most similar to the linked lists I mentioned above. 这些与我上面提到的链表最相似。

Java also offers java.util.ArrayList . Java还提供java.util.ArrayList Lists of this kind are good for random access, but can be slow when inserting elements. 这种列表适用于随机访问,但插入元素时可能会很慢。 In fact, they are slowest when inserting an element at the beginning of the list. 实际上,当在列表的开头插入元素时,它们是最慢的。 Because ArrayList s are implemented as arrays behind the scenes, every element must be copied one position forward in the list to make room for the new first element. 因为ArrayList在幕后实现为数组,所以每个元素必须在列表中向前复制一个位置,以便为新的第一个元素腾出空间。 Now, if you also happen to need a bigger array, the ArrayList will allocate a new array, copy all the elements over, and so on. 现在,如果您还需要更大的数组, ArrayList将分配一个新数组,复制所有元素,依此类推。

(Google's ImmutableList is cited as "random-access", so it is likely more similar to the latter.) (谷歌的ImmutableList被称为“随机访问”,所以它可能更接近后者。)

If you plan to use your cons method often, I recommend using it with linked lists. 如果您打算经常使用cons方法,我建议将其与链表一起使用。 A cons operation is essentially adding an element at the beginning of a list. cons操作实质上是在列表的开头添加元素。 This makes little sense with linear structures such as arrays. 这对于诸如阵列的线性结构没有多大意义。 I recommend against using array lists for this reason: that they're conceptually wrong for the job. 出于这个原因,我建议不要使用数组列表:它们在概念上对于这项工作是错误的。

For the very picky: because a new list is returned each time cons is called, copying must occur whether the list is a LinkedList or an ArrayList . 非常挑剔:因为每次调用cons都会返回一个新列表,无论列表是LinkedList还是ArrayList ,都必须进行复制。 However, the entire principal of a cons operation is that it is operating on a linked list. 但是, cons操作的整个原理是它在链表上运行。

public <E> LinkedList<E> cons(E car, List<E> cdr) {
    LinkedList<E> destination = new LinkedList<E>(cdr);
    destination.addFirst(car);
    return destination;
}

Note that the above code was written after reading the above answers, so I apologize for any accidental plagiarism. 请注意,上面的代码是在阅读上述答案后编写的,所以我为任何偶然的抄袭道歉。 Let me know if you see it and I'll properly acknowledge. 让我知道你是否看到它,我会正确地承认。

Assuming you're happy with returning a LinkedList , you can use ImmutableList as the cdr in this example. 假设您对返回LinkedList感到满意,可以在此示例中使用ImmutableList作为cdr

Could you use a CompositeCollection ? 你能使用CompositeCollection吗?

public Collection cons(Collection c1, Collection c2)
{
    CompositeCollection cons = new CompositeCollection();
    cons.addComposited(c1);
    cons.addComposited(c2);
    return cons;
}

This would not be affected by whether or not one of the parameters is immutable and is still backed by the original collections c1 and c2. 这不受其中一个参数是否是不可变的影响,并且仍然由原始集合c1和c2支持。

If you need a List I would probably do the following: 如果你需要一个List我可能会做以下事情:

public List cons(Collection c1, Collection c2)
{
    ArrayList cons = new ArrayList(c1.size() + c2.size());
    cons.addAll(c1);
    cons.addAll(c2);
    return cons;
}

I'm going to throw my 2 cents in, then see if anybody comes up with anything more elegant. 我打算花2美分,然后看看是否有人想出更优雅的东西。 In the general case: 在一般情况下:

<E> List<E> cons(E e, List<E> list) {
    List<E> res = Lists.newArrayListWithCapacity(list.size() + 1);
    res.add(e);
    res.addAll(list);
    return res;
}

With an ImmutableList (don't know how efficient this is): 使用ImmutableList (不知道这有多高效):

<E> ImmutableList<E> cons(E e, ImmutableList<E> list) {
    return ImmutableList.<E>builder()
                        .add(e)
                        .addAll(list)
                        .build();
}

Surely a LinkedList would be the most efficient way of inserting an item at the head of a list? 当然,LinkedList是在列表头部插入项目的最有效方式吗?

Just use the LinkedList class that comes with Java 只需使用Java附带的LinkedList

Another variation if you have only Iterable. 另一种变化,如果你只有Iterable。

public static <E> List<E> cons(E e, Iterable<E> iter) {
   List<E> list = new ArrayList<E>();
   list.add(e);
   for(E e2: iter) list.add(e2);
   return list;
}

public static <E> List<E> cons(Iterable<E>... iters) {
   List<E> list = new ArrayList<E>();
   for(Iterable<E> iter: iters) for(E e1: iter1) list.add(e1);
   return list;
}

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

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