反对意见';用Java创建列表

反对意见';用Java创建列表,java,collections,list,guava,Java,Collections,List,Guava,假设我有一个java.util.List列表,我想通过在List的开头添加一个元素e来创建一个新的List(也就是说,我想对比e和List)。例如,如果列表为 [1,2,3,4] 而e是5,则cons(e,list)将是 [5,1,2,3,4] 可以共享list和cons(e,list)的元素,但不应修改list 实施cons的最简单和/或最有效的方法是什么?结果是不可修改的。允许使用谷歌收藏库 如果list是一个com.google.common.collect.ImmutableList

假设我有一个
java.util.List列表
,我想通过在
List
的开头添加一个元素
e
来创建一个新的
List
(也就是说,我想对比
e
List
)。例如,如果
列表

[1,2,3,4]
e
5
,则
cons(e,list)
将是

[5,1,2,3,4]
可以共享
list
cons(e,list)
的元素,但不应修改
list

实施
cons
的最简单和/或最有效的方法是什么?结果是不可修改的。允许使用谷歌收藏库

如果
list
是一个
com.google.common.collect.ImmutableList

你能用一个吗

这不会受到其中一个参数是否不可变以及是否仍由原始集合c1和c2支持的影响

如果您需要
列表
,我可能会执行以下操作:

public List cons(Collection c1, Collection c2)
{
    ArrayList cons = new ArrayList(c1.size() + c2.size());
    cons.addAll(c1);
    cons.addAll(c2);
    return cons;
}
公共静态列表cons(列表列表,T){
ArrayList结果=新建ArrayList(列表);
结果:添加(0,t);
返回结果;
}
根据评论编辑: 因为问题是“实现cons的最简单和/或最有效的方法”,所以我选择了“最简单”。如果知道有更有效的方法,我不会感到惊讶。将元素放在列表之前是另一种有效的方法,最初分配正确的大小可能会提高性能。过早优化是万恶之源。

提供了那种Lisp-y的东西。虽然大多数人都想使用Clojure作为语言(和我一样),但Clojure库都是真正的Java代码,如果您愿意,您可以将Java中的数据结构用作一个特殊的库。这样你就有能力做反义词之类的事情,你就有了Clojure使用的不变性。Clojure数据结构也实现了等效的Java类型


只是从另一个角度考虑。

我要拿出我的2美分,然后看看有没有人能想出更优雅的东西。在一般情况下:

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

当然,LinkedList将是在列表开头插入项目的最有效方式


只需使用Java附带的类

,因为您提到了
cons
函数,我假设您正在使用由
cons
单元格组成的链表的概念模型来解决这个问题。具体来说,我假设您认为每个列表都有一个
car
(第一个元素)和一个
cdr
(包含以下所有元素的子列表)

Java支持链接列表,如
Java.util.LinkedList
。这些工具适用于线性遍历,可以非常有效地插入元素。这些与我上面提到的链表最为相似

Java还提供了
Java.util.ArrayList
。这类列表适用于随机访问,但在插入元素时速度可能较慢。事实上,在列表开头插入元素时,它们的速度最慢。由于
ArrayList
s在后台实现为数组,因此必须将每个元素在列表中向前复制一个位置,以便为新的第一个元素腾出空间。现在,如果您碰巧还需要一个更大的数组,
ArrayList
将分配一个新数组,复制所有元素,依此类推

(谷歌的
ImmutableList
被称为“随机访问”,因此它可能更类似于后者。)

如果您计划经常使用
cons
方法,我建议将其用于链接列表。
cons
操作实质上是在列表的开头添加一个元素。这对于线性结构(如阵列)没有什么意义。出于这个原因,我建议不要使用数组列表:它们在概念上不适合这个任务

对于非常挑剔的人:因为每次调用
cons
时都会返回一个新列表,所以无论该列表是
LinkedList
还是
ArrayList
,都必须进行复制。然而,
cons
操作的全部原理是它在链表上操作

public LinkedList cons(电子汽车,列表cdr){
LinkedList目的地=新的LinkedList(cdr);
目的地:第一站(汽车);
返回目的地;
}
请注意,上述代码是在阅读了上述答案后编写的,因此我为任何意外抄袭行为道歉。如果你看到了,请告诉我,我会向你表示感谢


假设您对返回一个
LinkedList
感到满意,那么在本例中,您可以使用
ImmutableList
作为
cdr

另一个变体,如果您只有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;
}
公共静态列表cons(E、iter){
列表=新的ArrayList();
列表.添加(e);
对于(e2:iter)列表,添加(e2);
退货清单;
}
公共静态列表cons(Iterable…iters){
列表=新的ArrayList();
用于(iter:iters)用于(e1:iter1)列表。添加(e1);
退货清单;
}

我相信您真正想要的答案是:

该方法甚至被称为
cons


我对这个图书馆没有经验。前几天刚听说的。我希望它是好的

一个有效的解决方案是创建自己的列表实现,惰性地委托。如果不可修改,也不会太麻烦。如果是通过工厂方法创建的,那么您甚至可以使用两种不同的底层实现之一,具体取决于参数列表是否实现了随机访问

对于最简单的情况(未检查是否编译,未进行健全性检查等):

class ConsList扩展了AbstractList
{
私人决赛E优先;
私人最终名单;
ConsList(E优先,列表剩余)
{
this.first=first;
这个。休息=休息;
}
公共整数获取(整数索引)
{
返回(索引)
<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;
}
<E> ImmutableList<E> cons(E e, ImmutableList<E> list) {
    return ImmutableList.<E>builder()
                        .add(e)
                        .addAll(list)
                        .build();
}
public <E> LinkedList<E> cons(E car, List<E> cdr) {
    LinkedList<E> destination = new LinkedList<E>(cdr);
    destination.addFirst(car);
    return destination;
}
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;
}
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;
    }
}
import com.sun.tools.javac.util.List;
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());