Для цикла вместо цикла while в коллекциях - PullRequest
0 голосов
/ 07 января 2019

У меня есть вопросы о Collections.class и методе "copy".

1) Почему мы проверяем размер списка источников в секунду, если условно для кода ниже, и почему он должен быть меньше 10? Почему это так важно?

2) Более того, почему мы используем цикл for в этом условном выражении вместо - 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 голосов
/ 07 января 2019

Комментарий выше некоторых констант дает хорошее понимание здесь

Многие из алгоритмов списка имеют две реализации, одна из которых подходит для RandomAccess списки, другой для "последовательного". Часто вариант произвольного доступа дает лучшую производительность в небольших списках последовательного доступа. Параметры настройки ниже определяют точку отсечки для чего составляет «маленький» список последовательного доступа для каждого алгоритма. The нижеприведенные значения были определены эмпирически, чтобы они хорошо работали для LinkedList. Надеюсь, они должны быть разумными для другого последовательного доступа List реализации. Те, кто выполняет работу над этим кодом, будут делать чтобы время от времени проверять значения этих параметров.

Думаю, бит, который я выделил, особенно актуален. 10 поскольку порог не является произвольным, они пришли к нему с помощью серии измерений и наблюдений, как и все хорошие оптимизации производительности. То, что считается «маленьким» списком, также отличается в зависимости от контекста.

0 голосов
/ 07 января 2019

2) Более того, почему мы используем цикл for в этом условном выражении вместо - 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(). Поскольку число копируемых элементов известно, можно использовать подсчитанный цикл, и он более эффективен, поскольку он избегает любых вызовов методов как части логики управления циклом. То есть i < srcSize и ++i компилируются в линию по сравнению с чем-то вроде while (si.hasNext()), что, конечно, требует вызова метода.

0 голосов
/ 07 января 2019

1) 10 - это константа, которая представляет собой разрыв между маленькими списками и большими списками. Если List не поддерживает произвольный доступ (что означает, что он не поддерживает O(1) время для list.get(i)), get(i) может быть дорогим, поэтому вы можете использовать его, только если список небольшой. LinkedList является примером List, который не поддерживает произвольный доступ.

2) Возможны оба цикла: for и while, но когда List s поддерживает произвольный доступ (или достаточно мал), может быть более эффективно использовать get и set вместо создание итераторов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...