繁体   English   中英

在Java中使用List

[英]Cons'ing a List in Java

说我有一个java.util.List list ,我想创建一个新的List中加入元素e到年初list (即我想利弊 elist )。 例如,如果list

[1,2,3,4]

并且e5 ,那么cons(e,list)将是

[5,1,2,3,4]

listcons(e,list)的元素可以共享,但不应修改list

实施cons的最简单和/或最有效的方法是什么? 结果不可修改是可以的。 允许使用Google Collections Library。

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

编辑回应评论:由于问题是“最简单和/或最有效的方式来实施利弊”,我选择了“最简单”。 我不会惊讶地发现有更有效的方法。 将元素放在列表之前是另一种有效的方法,并且最初分配正确的大小可能会提高性能。 过早优化是万恶之源。

Clojure提供了那种Lisp-y的东西。 虽然大多数人都认为使用Clojure语言(就像我一样),但Clojure库都是真正的Java代码,如果你愿意,你可以使用Java中的数据结构作为一个特殊的库。 通过这种方式,您可以获得利弊等等,并获得Clojure使用的不变性。 Clojure数据strcutres也实现了等效的Java类型。

只是从不同的方向思考。

我相信你真正想要的答案是这样的:

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

该方法甚至被称为cons

我没有这个库的经验。 前几天听说过它。 我希望这很好!

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

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

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

我确信其他一些方法可以提高效率,而通过扩展AbstractSequentialList可以更好地满足顺序情况。

这不是众所周知的,但Sun javac编译器API包含一个不可变的List实现,它使用append()prepend()等方法返回新的不可变列表。 要使用它,您需要在类路径上使用tools.jar ,并且需要以下import语句:

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

输出:

6,7
8,7,6,5,4,3,2,1

class com.sun.tools.javac.util.List
class com.sun.tools.javac.util.List

我知道这是一个内部API,不应该在生产中使用,但它非常有趣(最后一个感觉正确的java集合)。

注意:显然, CollectionList接口中的所有标准可变方法(如add() addAll() )都会抛出UnsupportedOperationException

参考:

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

Java支持链表作为java.util.LinkedList 这些对于线性遍历是有益的,并且可以非常有效地插入元素。 这些与我上面提到的链表最相似。

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

(谷歌的ImmutableList被称为“随机访问”,所以它可能更接近后者。)

如果您打算经常使用cons方法,我建议将其与链表一起使用。 cons操作实质上是在列表的开头添加元素。 这对于诸如阵列的线性结构没有多大意义。 出于这个原因,我建议不要使用数组列表:它们在概念上对于这项工作是错误的。

非常挑剔:因为每次调用cons都会返回一个新列表,无论列表是LinkedList还是ArrayList ,都必须进行复制。 但是, cons操作的整个原理是它在链表上运行。

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

请注意,上面的代码是在阅读上述答案后编写的,所以我为任何偶然的抄袭道歉。 让我知道你是否看到它,我会正确地承认。

假设您对返回LinkedList感到满意,可以在此示例中使用ImmutableList作为cdr

你能使用CompositeCollection吗?

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

这不受其中一个参数是否是不可变的影响,并且仍然由原始集合c1和c2支持。

如果你需要一个List我可能会做以下事情:

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

我打算花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;
}

使用ImmutableList (不知道这有多高效):

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

当然,LinkedList是在列表头部插入项目的最有效方式吗?

只需使用Java附带的LinkedList

另一种变化,如果你只有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