我对Collections.class和“复制”方法有疑问。
1)如果以下面的代码为条件,为什么我们要在第二秒检查源列表的大小,为什么它必须小于10?为什么这么重要?
2)而且,为什么我们在这种条件下而不是-while (hasNext())
public static <T> void copy(List<? super T> dest, List<? extends T> src) {
int srcSize = src.size();
if (srcSize > dest.size()) {
throw new IndexOutOfBoundsException("Source does not fit in dest");
} else {
if (srcSize < 10 || src instanceof RandomAccess && dest instanceof RandomAccess) {
for (int i = 0; i < srcSize; ++i) {
dest.set(i, src.get(i));
}
} else {
ListIterator<? super T> di = dest.listIterator();
ListIterator<? extends T> si = src.listIterator();
for (int i = 0; i < srcSize; ++i) {
di.next();
di.set(si.next());
}
}
}
}
为什么要使用
答案 0 :(得分:5)
1)10是一个常数,表示小列表和大列表之间的分界。如果List
不支持随机访问(这意味着它不支持O(1)
的{{1}}时间),list.get(i)
可能会很昂贵,因此您只想使用如果列表很小的话。 get(i)
是不支持随机访问的LinkedList
的示例。
2)List
和for
循环都是可能的,但是当while
支持随机访问(或足够小)时,使用{{1 }}和List
而不是创建迭代器。
答案 1 :(得分:3)
comment above some of the constants在这里提供了很好的见识
许多List算法具有 两种实现,其中一种适用于
RandomAccess
列表中,另一个为“顺序”。通常,随机访问变体 在较小的顺序访问列表上产生更好的性能。的 下面的调整参数确定了什么的临界点 构成每种算法的“较小”顺序访问列表。 根据经验确定,以下值对于LinkedList
来说效果很好。 希望它们对于其他顺序访问List
应该是合理的 实现。对此代码进行性能工作的人会做 很好地验证这些参数的值。
我认为,我用粗体显示的内容特别重要。 10,因为阈值不是任意的,所以他们通过一系列测量和观察得出了这个阈值,就像所有良好的性能优化一样。根据上下文,所谓的“小”列表也有所不同。
答案 2 :(得分:0)
2)而且,为什么我们在这种条件下而不是-
while (hasNext())
我假设这个问题是引用代码中的最后一个for循环,该循环遍历迭代器:
ListIterator<? super T> di = dest.listIterator();
ListIterator<? extends T> si = src.listIterator();
for (int i = 0; i < srcSize; ++i) {
di.next();
di.set(si.next());
}
大多数传统的迭代器循环看起来像这样:
Iterator<T> it = src.iterator();
while (it.hasNext()) {
T t = it.next();
// process t
}
此代码不执行此操作。确实,它在任何一个迭代器上都根本不调用hasNext()
,这很不寻常。 (迭代器是ListIterator
而不是Iterator
的实例,因为需要调用set()
方法。ListIterator
的使用与循环控制的问题无关。也适用于Iterator
。
在循环中未调用hasNext()
的原因是代码已经知道集合的大小。它知道要复制的元素数,即srcSize
。它已经检查了目标大小,以确保它足够大以容纳所有元素。因此,循环最多可以调用si.next()
次di.next()
和srcSize
,而不必调用任何hasNext()
方法。 next()
调用失败的唯一方法是,如果集合的迭代器之一实现不正确。
(如果其中一个列表同时更改大小,则循环也可能失败。嗯。此代码明确地假定在迭代过程中两个列表的大小均未更改。)
在假设列表大小不变的情况下,没有理由调用任何hasNext()
方法。由于要复制的元素数量已知,因此可以使用计数的for循环,并且效率更高,因为它避免了将任何方法调用作为循环控制逻辑的一部分。也就是说,i < srcSize
和++i
是与while (si.hasNext())
之类的东西相比进行内联编译的,而后者{{1}}当然需要方法调用。