有没有人知道是否可以在Java中以恒定时间合并两个列表(或任何集合)?
http://www.cppreference.com/wiki/stl/list/splice
使用C ...中的链接列表很容易做到这一点
谢谢,
答案 0 :(得分:11)
据我所知,JDK库中的类不支持这一点。
如果你构建自己的List
实现 - 你可以自由地做,这是完全合法的。您可以使用LinkedList
并识别要添加的集合也是LinkedList
的特殊情况。
在记录你的课程时,你需要指出添加的对象成为新对象的一部分,换句话说,很多普遍性都会丢失。还有很多可能出现的错误:在加入之后更改原始列表中的任何一个(如果它们是可变的)将允许您创建一个带有间隙或两个尾部的列表。此外,大多数其他操作不会受益于您的黑客攻击类。换句话说,乍一看似乎是一个疯狂的想法。
请注意,“合并”列表通常具有不同的含义;例如,当合并排序列表时,可以预期结果列表具有相同的排序。你加入两个链接列表时所说的最好被称为“拼接”。或者只是“加入”。
答案 1 :(得分:3)
您可以围绕多个List
实现复合“包装器”。为简单起见,我已将示例设为不可变,但您始终可以实现add
附加到复合对象中存储的“final”List
。
public class CompositeImmutableList<T> implements List<T> {
private final List<T> l1;
private final List<T> l2;
public CompositeImmutableList(List<T> l1, List<T> l2) {
this.l1 = l1;
this.l2 = l2;
}
public boolean add(T t) {
throw new UnsupportedOperationException();
}
public int size() {
return l1.size() + l2.size();
}
public T get(int i) {
int sz1 = l1.size();
return i < s1 : l1.get(i) : l2.get(sz1 - i);
}
// TODO: Implement remaining List API methods.
}
答案 2 :(得分:1)
您可以执行以下步骤:在此处获取Java源的LinkedList: LinkedList.java
然后在此实现中添加下一个函数:
public void concatenate(LinkedList<E> list)
{
header.previous.next = list.header.next;
list.header.next.previous = header.previous;
list.header.previous.next = header.next;
header.next.previous = list.header.previous;
list.header.next = header.next;
header.previous = list.header.previous;
size = size + list.size;
modCount = modCount + list.modCount + 1;
list.size = size;
list.modCount = modCount;
}
使用此代码,2 LinkedList将是相同的LinkedList,因此您将合并为一个。容器LinkedList将在末尾添加param LinkedList,最后LinkedList的头部将指向第一个和最后一个元素。 在这个方法中,我不关心两个列表中的一个是否为空,因此在使用它之前确保你有两个包含元素的列表,否则你必须检查并注意这个。
测试1:
public static void main(String[] args)
{
LinkedList<String> test1 = new LinkedList<String>();
LinkedList<String> test2 = new LinkedList<String>();
test1.add("s1");
test1.add("s2");
test2.add("s4");
test2.add("s5");
test1.concatenate(test2);
System.out.println(test1);
System.out.println(test2);
}
出:
[s1, s2, s4, s5]
[s1, s2, s4, s5]
Test2表现:
public static void main(String[] args)
{
int count = 100000;
myutil.LinkedList<String> test1 = new myutil.LinkedListExt<>();
myutil.LinkedList<String> test2 = new myutil.LinkedListExt<>();
test1.add("s1");
test1.add("s2");
test2.add("s3");
test2.add("s4");
for (int i=0; i<count; ++i)
test2.add("s");
long start = System.nanoTime();
test1.concatenate(test2);
long elapsedTime = System.nanoTime() - start;
System.out.println(elapsedTime/1000000.0);
java.util.LinkedList<String> test3 = new java.util.LinkedList<>();
java.util.LinkedList<String> test4 = new java.util.LinkedList<>();
test3.add("s1");
test3.add("s2");
test4.add("s3");
test4.add("s4");
for (int i=0; i<count; ++i)
test4.add("s");
start = System.nanoTime();
test3.addAll(test4);
elapsedTime = System.nanoTime() - start;
System.out.println(elapsedTime/1000000.0);
}
出:
0.004016
10.508312
答案 3 :(得分:0)
如果使用C中的链表很容易,那么我猜测LinkedList类提供相同的性能
所有操作的执行都与双向链表一样。索引到列表中的操作将从开头或结尾遍历列表,以较接近指定索引为准。
List list1 = new LinkedList();
list1.add(...);
List list2 = new LinkedList();
list2.add(...);
list1.addAll(list2);
编辑:没关系。看起来像LinkedList.addAll(Collection)调用LinkedList.addAll(int,Collection),它遍历新的集合。