[英]Cons'ing a List in Java
说我有一个java.util.List list
,我想创建一个新的List
中加入元素e
到年初list
(即我想利弊 e
和list
)。 例如,如果list
是
[1,2,3,4]
并且e
是5
,那么cons(e,list)
将是
[5,1,2,3,4]
list
和cons(e,list)
的元素可以共享,但不应修改list
。
实施cons
的最简单和/或最有效的方法是什么? 结果不可修改是可以的。 允许使用Google Collections Library。
如果list
是com.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集合)。
注意:显然, Collection
和List
接口中的所有标准可变方法(如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.