предисловие
Не так давно я помог коллегеreview
Одинjob
Когда реализация идет медленно, я обнаружил, что многим друзьям все еще нужно обращать внимание на детали при кодировании, чтобы реализовать функцию, поэтому у меня есть эта статья.
Шаги ArrayList на яме
List<String> temp = new ArrayList() ;
//获取一批数据
List<String> all = getData();
for(String str : all) {
temp.add(str);
}
Прежде всего, что не так с этим кодом?
На самом деле в большинстве случаев это не проблема, это не что иное, как циклArrayList
Только записывайте данные.
Но в особых случаях, как здесьgetData()
Последующие действия, когда возвращаемые данные очень великиtemp.add(str)
Будут проблемы.
Например, мыreview
Когда я кодирую, я обнаруживаю, что возвращаемые здесь данные иногда достигают 2000 Вт.ArrayList
Обостряется проблема письма.
Руководство по заполнению ямы
Всем известно, что ArrayList реализован массивом, а длина данных ограничена, массив нужно расширять в нужный момент.
Вот пример вставки в хвост add(E e).
ArrayList<String> temp = new ArrayList<>(2) ;
temp.add("1");
temp.add("2");
temp.add("3");
Когда мы инициализируем длину 2ArrayList
, и запишите в него три части данныхArrayList
Его нужно расширить, то есть скопировать предыдущие данные в новый массив длины 3.
Причина, по которой это 3, заключается в том, что новая длина = исходная длина * 1,5.
Из исходного кода мы можем знатьArrayList
Длина по умолчанию — 10.
Но на самом деле он не создается при инициализации.DEFAULT_CAPACITY = 10
массив .
но иду внутрьadd
Первые данные будут расширены до 10.
Теперь, когда мы знаем, что длина по умолчанию равна 10, это означает, что после записи девятого элемента он расширится до10*1.5 =15
.
Этот шаг заключается в копировании массива, то есть повторном открытии нового пространства памяти для хранения 15 массивов.
Если мы будем писать часто и в больших количествах, это вызовет много копий массива, что крайне неэффективно.
Но если мы заранее предскажем, сколько фрагментов данных может быть записано, мы сможем заранее избежать этой проблемы.
Например, когда мы записываем в него 1000W кусков данных, возникает огромный разрыв в производительности между заданной длиной массива и длиной по умолчанию 10 при инициализации.
Я проверил тест JMH следующим образом:
@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
public class CollectionsTest {
private static final int TEN_MILLION = 10000000;
@Benchmark
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
public void arrayList() {
List<String> array = new ArrayList<>();
for (int i = 0; i < TEN_MILLION; i++) {
array.add("123");
}
}
@Benchmark
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
public void arrayListSize() {
List<String> array = new ArrayList<>(TEN_MILLION);
for (int i = 0; i < TEN_MILLION; i++) {
array.add("123");
}
}
public static void main(String[] args) throws RunnerException {
Options opt = new OptionsBuilder()
.include(CollectionsTest.class.getSimpleName())
.forks(1)
.build();
new Runner(opt).run();
}
}
По результатам видно, что эффективность предустановленной длины будет намного выше, чем при использовании по умолчанию (здесьScore
относится ко времени, которое требуется для выполнения функции).
Так что тут всем настоятельно рекомендуется: когда пишется большой объем данныхArrayList
, обязательно инициализируйте указанную длину.
Опять же, будьте осторожныadd(int index, E element)
Запишите данные в указанное место.
Из исходного кода видно, что каждая запись будет перемещать данные после индекса обратно, собственно, суть в том, чтобы скопировать массив;
Но это отличается от записи данных в конец массива обычным способом, он будет каждый раз копировать массив, что крайне неэффективно.
LinkedList
упомянулArrayList
я должен поговоритьLinkedList
Этот брат-близнец; хотя обаList
контейнер, но основная реализация совершенно другая.
LinkedList
Он состоит из связанного списка, и каждый узел имеет два узла, головной и хвостовой, которые относятся к переднему и заднему узлам соответственно; следовательно, это также двусвязный список.
Так что в теории его запись очень эффективна, не будет копирования массива, что крайне неэффективно в ArrayList, и каждый раз нужно перемещать только указатель.
Я не буду рисовать тут картинки, если мне лень, каждый сам наверстает.
Сравнительный тест
Ходили слухи, что:
Эффективность записи LinkedList выше, чем у ArrayList, поэтому он очень подходит для LinkedList, когда запись больше, чем чтение.
@Benchmark
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
public void linkedList() {
List<String> array = new LinkedList<>();
for (int i = 0; i < TEN_MILLION; i++) {
array.add("123");
}
}
Вот тест, чтобы увидеть, если вывод последователен; то же самое верноLinkedList
написать1000W
Вторичные данные, через результаты, чтобы увидеть длину инициализированного массиваArrayList
Эффективность явно вышеLinkedList
.
Но предпосылка здесь заключается в том, чтобы заранее установитьArrayList
Длина массива, чтобы избежать расширения массива, чтобыArrayList
Эффективность записи очень высока, в то время какLinkedList
Хотя ему не нужно копировать память, ему необходимо создавать объекты, преобразовывать указатели и выполнять другие операции.
И вопрос, разумеется,ArrayList
Может поддерживаться произвольный доступ по подписке, и эффективность очень высока.
LinkedList
Поскольку нижний слой не является массивом, доступ по индексу не поддерживается, но необходимо решить, следует ли проходить с начала или с конца в соответствии с позицией индекса запроса.
Но независимо от того, какой, вам нужно перемещать указатель, чтобы пройти один за другим, особенноindex
Это будет очень медленно ближе к середине.
Суммировать
Как упоминалось здесь, высокопроизводительные приложения строятся из мелких деталей.ArrayList
Как и в случае с ямами данных, в повседневном использовании нет больших проблем.Как только объем данных станет большим, все маленькие проблемы станут большими проблемами.
Итак, подведем итог:
- При повторном использовании ArrayList, если вы можете заранее предсказать размер данных, вы должны указать его длину, когда она станет больше.
- Избегайте, насколько это возможно
add(index,e)
api, что приведет к копированию массива и снижению эффективности. - Еще один момент, другой обычно используется
Map
контейнерHashMap
Также рекомендуется инициализировать длину, чтобы избежать расширения.
Весь тестовый код в этой статье:
Ваши лайки и репост - лучшая поддержка для меня