在集合中使用For循环而不是while循环

时间:2019-01-07 09:21:51

标签: java collections

我对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());
            }
        }
    }
}

为什么要使用

3 个答案:

答案 0 :(得分:5)

1)10是一个常数,表示小列表和大列表之间的分界。如果List不支持随机访问(这意味着它不支持O(1)的{​​{1}}时间),list.get(i)可能会很昂贵,因此您只想使用如果列表很小的话。 get(i)是不支持随机访问的LinkedList的示例。

2)Listfor循环都是可能的,但是当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}}当然需要方法调用。