Массив или список в Java. Что быстрее?

Я должен хранить тысячи строк в памяти для последовательного доступа к Java. Должен ли я хранить их в массиве или использовать какой-то список?

Поскольку массивы хранят все данные в непрерывной части памяти (в отличие от списков), вызовет ли проблема использование массива для хранения тысяч строк?

Ответ 1

Я предлагаю вам использовать профайлер для проверки, который быстрее.

Мое личное мнение состоит в том, что вы должны использовать списки.

Я работаю над большой базой данных, а предыдущая группа разработчиков использует массивы везде. Это сделало код очень негибким. После изменения больших кусков его в Списки мы не заметили разницы в скорости.

Ответ 2

Java-путь заключается в том, что вы должны учитывать, какая абстракция данных больше всего подходит вашим потребностям. Помните, что в Java List является абстрактным, а не конкретным типом данных. Вы должны объявить строки как список, а затем инициализировать его с помощью реализации ArrayList.

List<String> strings = new ArrayList<String>();

Это разделение абстрактного типа данных и конкретной реализации является одним из ключевых аспектов объектно-ориентированного программирования.

ArrayList реализует список абстрактных типов данных, используя массив в качестве основной реализации. Скорость доступа практически идентична массиву с дополнительными преимуществами возможности добавления и вычитания элементов в список (хотя это операция O (n) с ArrayList) и что если вы решите изменить базовую реализацию позже, ты можешь. Например, если вы понимаете, что вам нужен синхронизированный доступ, вы можете изменить реализацию на вектор, не переписывая весь свой код.

Фактически, ArrayList был специально разработан для замены конструкции низкоуровневого массива в большинстве контекстов. Если Java разрабатывался сегодня, вполне возможно, что массивы были бы полностью исключены в пользу конструкции ArrayList.

Поскольку массивы сохраняют все данные в непрерывном блоке памяти (в отличие от списков), будет ли использование массива для хранения тысяч строк причиной проблем?

В Java все коллекции хранят только ссылки на объекты, а не сами объекты. Оба массива и ArrayList будут хранить несколько тысяч ссылок в смежном массиве, поэтому они практически идентичны. Вы можете считать, что непрерывный блок из нескольких тысяч 32-битных ссылок всегда будет легко доступен на современном оборудовании. Это не гарантирует, что вы, конечно, не исчерпаете всю память, просто для того, чтобы непреодолимый блок памяти требовал неумения.

Ответ 3

Вы должны использовать общие типы для массивов. Как упоминалось другими, массивы негибкие и не обладают выразительной способностью генерических типов. (Тем не менее, они поддерживают проверку типов времени выполнения, но это плохо смешивается с родовыми типами.)

Но, как всегда, при оптимизации вы всегда должны следовать следующим шагам:

  • Не оптимизируйте, пока у вас не будет хорошей, чистой и рабочей версии вашего кода. Переход на общие типы вполне мог бы быть мотивирован на этом этапе уже.
  • Когда у вас есть хорошая и чистая версия, решите, достаточно ли она.
  • Если он не достаточно быстрый, измерить его производительность. Этот шаг важен по двум причинам. Если вы не измеряете, вы не будете (1) знать влияние любых сделанных вами оптимизаций и (2) знать, где оптимизировать.
  • Оптимизируйте самую жаркую часть вашего кода.
  • Измерьте снова. Это так же важно, как и измерение до. Если оптимизация не улучшила ситуацию, вернет ее. Помните, что код без оптимизации был чистым, приятным и работающим.

Ответ 4

Хотя ответы, предлагающие использовать ArrayList, имеют смысл в большинстве сценариев, на настоящий вопрос об относительной производительности ответа пока нет.

Есть несколько вещей, которые вы можете сделать с массивом:

  • создать это
  • установить предмет
  • получить предмет
  • клонировать/скопировать

Общий вывод

Хотя операции получения и установки в ArrayList выполняются несколько медленнее (соответственно, 1 и 3 наносекунды на вызов на моей машине), использование ArrayList по сравнению с массивом очень мало для любого не интенсивного использования. Однако следует помнить несколько вещей:

  • операции по изменению размера списка (при вызове list.add(...)) являются дорогостоящими, и по возможности следует попытаться установить начальную емкость на адекватном уровне (обратите внимание, что та же проблема возникает при использовании массива)
  • при работе с примитивами массивы могут быть значительно быстрее, поскольку они позволят избежать многих преобразований в бокс/распаковку
  • приложение, которое получает/устанавливает значения только в ArrayList (не очень часто!), может получить прирост производительности более чем на 25%, переключившись на массив

Подробные результаты

Вот результаты, которые я измерил для этих трех операций, используя библиотеку сравнительного анализа jmh (время в наносекундах) с JDK 7 на стандартном настольном компьютере с архитектурой x86. Обратите внимание, что ArrayList никогда не изменяется в тестах, чтобы убедиться, что результаты сопоставимы. Код теста доступен здесь.

Array/ArrayList Создание

Я выполнил 4 теста, выполнив следующие утверждения:

  • createArray1: Integer[] array = new Integer[1];
  • createList1: List<Integer> list = new ArrayList<> (1);
  • createArray10000: Integer[] array = new Integer[10000];
  • createList10000: List<Integer> list = new ArrayList<> (10000);

Результаты (в наносекундах за звонок, 95% достоверность):

a.p.g.a.ArrayVsList.CreateArray1         [10.933, 11.097]
a.p.g.a.ArrayVsList.CreateList1          [10.799, 11.046]
a.p.g.a.ArrayVsList.CreateArray10000    [394.899, 404.034]
a.p.g.a.ArrayVsList.CreateList10000     [396.706, 401.266]

Вывод: заметной разницы нет.

получить операции

Я выполнил 2 теста, выполнив следующие утверждения:

  • getList: return list.get(0);
  • getArray: return array[0];

Результаты (в наносекундах за звонок, 95% достоверность):

a.p.g.a.ArrayVsList.getArray   [2.958, 2.984]
a.p.g.a.ArrayVsList.getList    [3.841, 3.874]

Вывод: получение из массива примерно на 25% быстрее, чем из ArrayList, хотя разница составляет всего лишь одну наносекунду.

операции над множествами

Я выполнил 2 теста, выполнив следующие утверждения:

  • setList: list.set(0, value);
  • setArray: array[0] = value;

Результаты (в наносекундах за звонок):

a.p.g.a.ArrayVsList.setArray   [4.201, 4.236]
a.p.g.a.ArrayVsList.setList    [6.783, 6.877]

Вывод: операции над множествами над массивами выполняются примерно на 40% быстрее, чем над списками, но, как и в случае с get, каждая операция над множеством занимает несколько наносекунд - поэтому для того, чтобы разница достигала 1 секунды, необходимо установить элементы в списке/массиве сотни миллионов раз!

Клон/копия

Конструктор копирования ArrayList делегирует Arrays.copyOf поэтому производительность идентична копированию массива (копирование массива с помощью clone, Arrays.copyOf или System.arrayCopy оказывает существенного System.arrayCopy производительность).

Ответ 5

Я предполагаю, что исходный плакат исходит из фона С++/STL, который вызывает некоторую путаницу. В С++ std::list есть дважды связанный список.

В Java [java.util.]List - интерфейс без реализации (чистый абстрактный класс в терминах С++). List может быть двусвязным списком - java.util.LinkedList. Однако в 99 раз из 100, когда вы хотите создать новый List, вместо этого вы хотите использовать java.util.ArrayList, что является грубым эквивалентом С++ std::vector. Существуют и другие стандартные реализации, такие как те, которые возвращаются java.util.Collections.emptyList() и java.util.Arrays.asList().

С точки зрения производительности есть очень маленький успех, связанный с необходимостью пройти через интерфейс и дополнительный объект, однако вложение времени выполнения означает, что это редко имеет какое-либо значение. Также помните, что String обычно представляют собой объект плюс массив. Поэтому для каждой записи у вас, вероятно, есть еще два объекта. В С++ std::vector<std::string>, хотя копирование по значению без указателя как такового, массивы символов будут формировать объект для строки (и они обычно не будут использоваться совместно).

Если этот конкретный код действительно чувствителен к производительности, вы можете создать один массив char[] (или даже byte[]) для всех символов всех строк, а затем массив смещений. IIRC, так реализуется javac.

Ответ 6

Я согласен с тем, что в большинстве случаев вам следует выбирать гибкость и элегантность ArrayLists для массивов - и в большинстве случаев влияние на производительность программы будет незначительным.

Однако, если вы выполняете постоянную, тяжелую итерацию с небольшими структурными изменениями (нет добавлений и удалений), например, для рендеринга программной графики или пользовательской виртуальной машины, мои тесты сравнительного тестирования по последовательному доступу показывают, что ArrayLists - это 1,5 x медленнее, чем массивы в моей системе (Java 1.6 на моем однолетнем iMac).

Некоторые коды:

import java.util.*;

public class ArrayVsArrayList {
    static public void main( String[] args ) {

        String[] array = new String[300];
        ArrayList<String> list = new ArrayList<String>(300);

        for (int i=0; i<300; ++i) {
            if (Math.random() > 0.5) {
                array[i] = "abc";
            } else {
                array[i] = "xyz";
            }

            list.add( array[i] );
        }

        int iterations = 100000000;
        long start_ms;
        int sum;

        start_ms = System.currentTimeMillis();
        sum = 0;

        for (int i=0; i<iterations; ++i) {
          for (int j=0; j<300; ++j) sum += array[j].length();
        }

        System.out.println( (System.currentTimeMillis() - start_ms) + " ms (array)" );
        // Prints ~13,500 ms on my system

        start_ms = System.currentTimeMillis();
        sum = 0;

        for (int i=0; i<iterations; ++i) {
          for (int j=0; j<300; ++j) sum += list.get(j).length();
        }

        System.out.println( (System.currentTimeMillis() - start_ms) + " ms (ArrayList)" );
        // Prints ~20,800 ms on my system - about 1.5x slower than direct array access
    }
}

Ответ 7

Хорошо, во-первых, стоит ли уточнить, имеете ли вы в виду "список" в классическом понимании структур данных compi (т.е. связанный список) или вы имеете в виду java.util.List? Если вы имеете в виду java.util.List, это интерфейс. Если вы хотите использовать массив, просто используйте реализацию ArrayList, и вы получите поведение и семантику, подобную массиву. Проблема решена.

Если вы имеете в виду массив против связанного списка, это немного другой аргумент, для которого мы возвращаемся к Big O (здесь простое английское объяснение, если это незнакомый термин.

Массив

  • Случайный доступ: O (1);
  • Вставка: O (n);
  • Удалить: O (n).

Связанный список:

  • Случайный доступ: O (n);
  • Вставка: O (1);
  • Удалить: O (1).

Таким образом, вы выбираете тот, который лучше всего подходит для изменения размера массива. Если вы изменяете размер, вставляете и удаляете много, возможно, связанный список - лучший выбор. То же самое происходит, если случайный доступ встречается редко. Вы говорите о серийном доступе. Если вы в основном выполняете последовательный доступ с очень небольшим изменением, то, вероятно, не имеет значения, что вы выберете.

Связанные списки имеют несколько более высокие накладные расходы, поскольку, как вы говорите, вы имеете дело с потенциально несмежными блоками памяти и (эффективно) указателями на следующий элемент. Это, вероятно, не важный фактор, если вы не имеете дело с миллионами записей.

Ответ 8

Я написал небольшой ориентир, чтобы сравнить ArrayLists с массивами. На моем старом ноутбуке время, пройденное через 5000-элементный arraylist, в 1000 раз, было примерно на 10 миллисекунд медленнее, чем эквивалентный массив массивов.

Итак, если вы ничего не делаете, кроме повторения списка, и вы делаете это много, возможно, стоит оптимизировать. В противном случае я бы использовал List, потому что это упростит, когда вам нужно оптимизировать код.

n.b. Я заметил, что использование for String s: stringsList было примерно на 50% медленнее, чем использование старого цикла for-loop для доступа к списку. Идите фигуру... Здесь две функции, которые я приурочил; массив и список были заполнены 5000 случайными (разными) строками.

private static void readArray(String[] strings) {
    long totalchars = 0;
    for (int j = 0; j < ITERATIONS; j++) {
        totalchars = 0;
        for (int i = 0; i < strings.length; i++) {
            totalchars += strings[i].length();

        }
    }
}

private static void readArrayList(List<String> stringsList) {
    long totalchars = 0;
    for (int j = 0; j < ITERATIONS; j++) {
        totalchars = 0;
        for (int i = 0; i < stringsList.size(); i++) {
            totalchars += stringsList.get(i).length();
        }
    }
}

Ответ 9

Нет, потому что технически массив сохраняет только ссылку на строки. Строки сами выделяются в другом месте. Для тысячи элементов я бы сказал, что список будет лучше, он медленнее, но он предлагает большую гибкость и его проще в использовании, особенно если вы собираетесь изменить его размер.

Ответ 10

Если у вас тысячи, подумайте об использовании trie. Три - древовидная структура, объединяющая общие префиксы сохраненной строки.

Например, если строки были

intern
international
internationalize
internet
internets

Команда trie сохранит:

intern
 -> \0
 international
 -> \0
 -> ize\0
 net
 ->\0
 ->s\0

Строки требуют 57 символов (включая нулевой ограничитель, '\ 0') для хранения, плюс любой размер объекта String, который их удерживает. (По правде говоря, мы должны, вероятно, округлять все размеры до кратных 16, но...) Назовите это 57 + 5 = 62 байта, примерно.

Для хранения trie требуется 29 (включая нулевой ограничитель, '\ 0') для хранения, а также размер trie-узлов, которые являются ссылкой на массив и список дочерних три-узлов.

В этом примере это, вероятно, говорит о том же; для тысяч, он, вероятно, выходит меньше, если у вас есть общие префиксы.

Теперь, когда вы используете trie в другом коде, вам придется преобразовать в String, возможно, используя StringBuffer в качестве посредника. Если многие из строк используются одновременно как строки, вне trie, это потеря.

Но если вы используете только несколько в то время - скажем, чтобы искать вещи в словаре - trie может сэкономить вам много места. Определенно меньше места, чем хранение их в HashSet.

Вы говорите, что вы обращаетесь к ним "последовательно" - если это означает последовательно в алфавитном порядке, trie также, очевидно, дает вам алфавитный порядок бесплатно, если вы повторите его по глубине.

Ответ 11

UPDATE:

Как отмечал Марк, после разогрева JVM (несколько тестовых проходов) существенной разницы нет. Проверяется с помощью повторно созданного массива или даже нового прохода, начинающегося с новой строки матрицы. С большой вероятностью эти знаки простого массива с доступом к индексу не должны использоваться в пользу коллекций.

Еще один 1-2 прохода простой массив в 2-3 раза быстрее.

ОРИГИНАЛЬНАЯ ПОЧТА:

Слишком много слов для предмета слишком просто проверить. Без любого массива вопросов в несколько раз быстрее, чем любой контейнер класса. Я задаю этот вопрос, ища альтернативы для моего критически важного раздела. Вот код прототипа, который я создал для проверки реальной ситуации:

import java.util.List;
import java.util.Arrays;

public class IterationTest {

    private static final long MAX_ITERATIONS = 1000000000;

    public static void main(String [] args) {

        Integer [] array = {1, 5, 3, 5};
        List<Integer> list = Arrays.asList(array);

        long start = System.currentTimeMillis();
        int test_sum = 0;
        for (int i = 0; i < MAX_ITERATIONS; ++i) {
//            for (int e : array) {
            for (int e : list) {
                test_sum += e;
            }
        }
        long stop = System.currentTimeMillis();

        long ms = (stop - start);
        System.out.println("Time: " + ms);
    }
}

И вот ответ:

На основе массива (строка 16 активна):

Time: 7064

На основе списка (строка 17 активна):

Time: 20950

Чем больше комментариев "быстрее"? Это вполне понятно. Вопрос в том, что примерно на 3 часа быстрее для вас лучше, чем гибкость List. Но это еще один вопрос. Кстати, я тоже проверил это на основе построенного вручную ArrayList. Почти тот же результат.

Ответ 12

Поскольку здесь уже есть много хороших ответов, я хотел бы предоставить вам другую информацию о практическом представлении, которая сравнивает сравнение вставки и итерации: примитивный массив vs Linked-list в Java.

Это реальная простая проверка производительности.
Таким образом, результат будет зависеть от производительности машины.

Исходный код, используемый для этого, приведен ниже:

import java.util.Iterator;
import java.util.LinkedList;

public class Array_vs_LinkedList {

    private final static int MAX_SIZE = 40000000;

    public static void main(String[] args) {

        LinkedList lList = new LinkedList(); 

        /* insertion performance check */

        long startTime = System.currentTimeMillis();

        for (int i=0; i<MAX_SIZE; i++) {
            lList.add(i);
        }

        long stopTime = System.currentTimeMillis();
        long elapsedTime = stopTime - startTime;
        System.out.println("[Insert]LinkedList insert operation with " + MAX_SIZE + " number of integer elapsed time is " + elapsedTime + " millisecond.");

        int[] arr = new int[MAX_SIZE];

        startTime = System.currentTimeMillis();
        for(int i=0; i<MAX_SIZE; i++){
            arr[i] = i; 
        }

        stopTime = System.currentTimeMillis();
        elapsedTime = stopTime - startTime;
        System.out.println("[Insert]Array Insert operation with " + MAX_SIZE + " number of integer elapsed time is " + elapsedTime + " millisecond.");


        /* iteration performance check */

        startTime = System.currentTimeMillis();

        Iterator itr = lList.iterator();

        while(itr.hasNext()) {
            itr.next();
            // System.out.println("Linked list running : " + itr.next());
        }

        stopTime = System.currentTimeMillis();
        elapsedTime = stopTime - startTime;
        System.out.println("[Loop]LinkedList iteration with " + MAX_SIZE + " number of integer elapsed time is " + elapsedTime + " millisecond.");


        startTime = System.currentTimeMillis();

        int t = 0;
        for (int i=0; i < MAX_SIZE; i++) {
            t = arr[i];
            // System.out.println("array running : " + i);
        }

        stopTime = System.currentTimeMillis();
        elapsedTime = stopTime - startTime;
        System.out.println("[Loop]Array iteration with " + MAX_SIZE + " number of integer elapsed time is " + elapsedTime + " millisecond.");
    }
}

Результат производительности ниже:

введите описание изображения здесь

Ответ 13

Помните, что ArrayList инкапсулирует массив, поэтому разница в сравнении с использованием примитивного массива (за исключением того факта, что в Java намного проще работать с java).

В значительной степени единственный раз, когда имеет смысл предпочесть массив массиву ArrayList, это когда вы храните примитивы, то есть байты, int и т.д., и вам нужна конкретная эффективность пространства, которую вы получаете с помощью примитивных массивов.

Ответ 14

Выбор массива и списка не так важен (учитывая производительность) в случае хранения строковых объектов. Поскольку и массив, и список будут хранить ссылки на объектные объекты, а не фактические объекты.

  • Если количество строк почти постоянное, используйте массив (или ArrayList). Но если число слишком сильно меняется, вам лучше использовать LinkedList.
  • Если есть (или будет) необходимость добавления или удаления элементов в середине, то вам, безусловно, придется использовать LinkedList.

Ответ 15

Если вы заранее знаете, насколько велики данные, тогда массив будет быстрее.

Список более гибкий. Вы можете использовать ArrayList, который поддерживается массивом.

Ответ 16

список медленнее, чем массивы. Если вам нужны массивы эффективности использования. Если вам нужен список использования гибкости.

Ответ 17

Это вы можете жить с фиксированным размером, массивы будут быстрее и нуждаются в меньшем объеме памяти.

Если вам нужна гибкость интерфейса List с добавлением и удалением элементов, остается вопрос, какую реализацию вы должны выбрать. Часто ArrayList рекомендуется и используется для любого случая, но также ArrayList имеет проблемы с производительностью, если элементы в начале или в середине списка должны быть удалены или вставлены.

Поэтому вы можете посмотреть http://java.dzone.com/articles/gaplist-%E2%80%93-lightning-fast-list, который представляет GapList. Эта новая реализация списка сочетает в себе сильные стороны как ArrayList, так и LinkedList, что дает очень хорошую производительность почти для всех операций.

Ответ 18

"Тысячи" - это не большое количество. Несколько тысяч строк длины абзаца имеют размер в несколько мегабайт. Если все, что вы хотите сделать, это доступ к ним последовательно, используйте неизменяемый односвязный список.

Ответ 19

В зависимости от реализации. возможно, что массив примитивных типов будет меньше и эффективнее, чем ArrayList. Это связано с тем, что массив будет хранить значения непосредственно в непрерывном блоке памяти, тогда как простейшая реализация ArrayList будет хранить указатели для каждого значения. В частности, на 64-битной платформе это может иметь огромное значение.

Конечно, для реализации jvm может быть особый случай для этой ситуации, и в этом случае производительность будет одинаковой.

Ответ 20

Список является предпочтительным способом в java 1.5 и выше, поскольку он может использовать generics. Массивы не могут иметь дженерики. Кроме того, массивы имеют заданную длину, которая не может динамически расти. Инициализация массива с большим размером - это не очень хорошая идея. ArrayList - это способ объявить массив с дженериками, и он может динамически расти. Но если использовать более частое удаление и вставку, связанный список - это самая быстрая структура данных, которая будет использоваться.

Ответ 21

Массивы рекомендуются везде, где вы можете использовать их вместо списка, особенно если вы знаете, что количество и размер элементов не изменяются.

См. рекомендацию Oracle Java: http://docs.oracle.com/cd/A97688_16/generic.903/bp/java.htm#1007056

Конечно, если вам нужно добавлять и удалять объекты из коллекции, много раз легко использовать списки.

Ответ 22

ArrayList хранит его элементы в массиве Object[] и использует метод untyped toArray, который намного быстрее (синяя полоса), чем напечатанная. Это типично, поскольку нетипизированный массив обернут в общий тип ArrayList<T>, который проверяется компилятором.

enter image description here

В этой диаграмме показан эталон с n = 5 на Java 7. Однако изображение больше не меняется с большим количеством элементов или другой виртуальной машины. Накладные расходы процессора могут показаться не столь резкими, но они складываются. Скорее всего, потребители массива должны преобразовать его в коллекцию, чтобы что-то сделать с ним, а затем преобразовать результат обратно в массив, чтобы передать его в другой метод интерфейса и т.д. Использование простого ArrayList вместо массива повышает производительность, не добавляя большого объема. ArrayList добавляет постоянные накладные расходы в 32 байта к обернутому массиву. Например, array с десятью объектами требует 104 байта, ArrayList 136 байт.

Эта операция выполняется в постоянное время, поэтому она намного быстрее, чем любая из вышеперечисленных (желтая полоса). Это не то же самое, что защитная копия. Неизменяемая коллекция изменится при изменении внутренних данных. Если это произойдет, клиенты могут работать в ConcurrentModificationException, итерации по элементам. Можно считать плохим дизайном, что интерфейс предоставляет методы, которые бросают UnsupportedOperationException во время выполнения. Однако, по крайней мере для внутреннего использования, этот метод может быть высокоэффективной альтернативой защитной копии - что невозможно с массивами.

Ответ 23

Ни один из ответов не имел информации, которая меня интересовала, - повторного сканирования одного и того же массива много раз. Пришлось создать для этого тест JMH.

Результаты (Java 1.8.0_66 x32, итерация простого массива как минимум в 5 раз быстрее, чем ArrayList):

Benchmark                    Mode  Cnt   Score   Error  Units
MyBenchmark.testArrayForGet  avgt   10   8.121 ? 0.233  ms/op
MyBenchmark.testListForGet   avgt   10  37.416 ? 0.094  ms/op
MyBenchmark.testListForEach  avgt   10  75.674 ? 1.897  ms/op

Test

package my.jmh.test;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Warmup;

@State(Scope.Benchmark)
@Fork(1)
@Warmup(iterations = 5, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 10)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
public class MyBenchmark {

    public final static int ARR_SIZE = 100;
    public final static int ITER_COUNT = 100000;

    String arr[] = new String[ARR_SIZE];
    List<String> list = new ArrayList<>(ARR_SIZE);

    public MyBenchmark() {
        for( int i = 0; i < ARR_SIZE; i++ ) {
            list.add(null);
        }
    }

    @Benchmark
    public void testListForEach() {
        int count = 0;
        for( int i = 0; i < ITER_COUNT; i++ ) {
            for( String str : list ) {
                if( str != null )
                    count++;
            }
        }
        if( count > 0 )
            System.out.print(count);
    }

    @Benchmark
    public void testListForGet() {
        int count = 0;
        for( int i = 0; i < ITER_COUNT; i++ ) {
            for( int j = 0; j < ARR_SIZE; j++ ) {
                if( list.get(j) != null )
                    count++;
            }
        }
        if( count > 0 )
            System.out.print(count);
    }

    @Benchmark
    public void testArrayForGet() {
        int count = 0;
        for( int i = 0; i < ITER_COUNT; i++ ) {
            for( int j = 0; j < ARR_SIZE; j++ ) {
                if( arr[j] != null )
                    count++;
            }
        }
        if( count > 0 )
            System.out.print(count);
    }

}

Ответ 24

Не попадайте в ловушку оптимизации без надлежащего бенчмаркинга. Как другие предложили использовать профилировщик, прежде чем делать какие-либо предположения.

Различные структуры данных, которые вы перечислили, имеют разные цели. Список очень эффективен при вставке элементов в начале и в конце, но очень много страдает при доступе к случайным элементам. Массив имеет фиксированное хранилище, но обеспечивает быстрый произвольный доступ. Наконец, ArrayList улучшает интерфейс к массиву, позволяя ему расти. Обычно используемая структура данных должна быть продиктована тем, как данные будут сохранены или будут добавлены.

О потреблении памяти. Кажется, вы смешиваете некоторые вещи. Массив будет предоставлять вам непрерывный кусок памяти для типа данных, которые у вас есть. Не забывайте, что java имеет фиксированные типы данных: boolean, char, int, long, float и Object (сюда относятся все объекты, даже массив - это Object). Это означает, что если вы объявите массив строк String [1000] или MyObject myObjects [1000], вы получите только 1000 ящиков памяти, достаточных для хранения местоположения (ссылок или указателей) объектов. Вы не получите 1000 ящиков памяти, достаточно больших, чтобы соответствовать размеру объектов. Не забывайте, что ваши объекты сначала создаются с помощью "нового". Это когда выделение памяти выполняется, а затем в массиве хранится ссылка (адрес памяти). Объект не копируется в массив только в качестве ссылки.

Ответ 25

Я не думаю, что это имеет значение для Strings. То, что смежно в массиве строк, это ссылки на строки, сами строки хранятся в случайных местах в памяти.

Массивы против списков могут иметь значение для примитивных типов, а не для объектов. Если вы заранее знаете количество элементов и не нуждаетесь в гибкости, массив из миллионов целых чисел или удваивается будет более эффективным в памяти и незначительно быстрее, чем список, потому что действительно они будут храниться смежно и мгновенно получать доступ. Поэтому Java по-прежнему использует массивы символов для строк, массивов int для данных изображения и т.д.

Ответ 26

Массив выполняется быстрее - вся память предварительно назначается заранее.

Ответ 27

Многочисленные микрофункции, найденные здесь, нашли числа в несколько наносекунд для таких вещей, как чтение массива /ArrayList. Это вполне разумно, если все в вашем кэше L1.

Кэш более высокого уровня или доступ к основной памяти могут иметь порядок величин времени, например, 10nS-100nS, более похожий на 1nS для кеша L1. Доступ к ArrayList имеет дополнительную косвенную память, и в реальном приложении вы можете заплатить эту стоимость от почти никогда до каждого раза, в зависимости от того, что делает ваш код между обращениями. И, конечно, если у вас много небольших ArrayLists, это может добавить к вашему использованию памяти и сделать его более вероятным, вы будете пропускать кеш.

Оригинальный плакат, кажется, использует только один, и через некоторое время получает доступ к большому количеству контента, поэтому не должно быть больших трудностей. Но это может быть другим для других людей, и вы должны следить за интерпретацией микрообъектов.

Java Strings, однако, ужасно расточительны, особенно если вы храните большое количество маленьких (просто посмотрите на них с помощью анализатора памяти, это похоже нa > 60 байт для строки из нескольких символов). Массив строк имеет косвенную привязку к объекту String, а другой - от объекта String до char [], который содержит строку. Если что-то убьет ваш кеш L1, это будет в сочетании с тысячами или десятками тысяч строк. Итак, если вы серьезно - серьезно, - соскабливая как можно больше производительности, вы можете смотреть на это по-другому. Вы могли бы, например, провести два массива, char [] со всеми строками в нем, один за другим, и int [] с смещениями к старту. Это будет PITA, чтобы что-либо сделать, и вы почти наверняка не нуждаетесь в этом. И если вы это сделаете, вы выбрали неправильный язык.

Ответ 28

Я пришел сюда, чтобы лучше понять влияние использования списков на массивы на производительность. Здесь мне пришлось адаптировать код для моего сценария: массив/список ~ 1000 дюймов, используя в основном геттеры, что означает массив [j] против list.get(j)

Принимая лучшее из 7, чтобы быть ненаучным об этом (первые несколько со списком, где в 2,5 раза медленнее) я получаю это:

array Integer[] best 643ms iterator
ArrayList<Integer> best 1014ms iterator

array Integer[] best 635ms getter
ArrayList<Integer> best 891ms getter (strange though)

- так, примерно на 30% быстрее с массивом

Вторая причина публикации сейчас заключается в том, что никто не упоминает о влиянии, если вы выполняете математический/матричный/симуляционный/оптимизационный код с вложенными циклами.

Допустим, у вас есть три вложенных уровня, а внутренний цикл в два раза медленнее, чем при 8-кратном снижении производительности. То, что будет работать через день, теперь занимает неделю.

* РЕДАКТИРОВАТЬ Довольно шокирован здесь, для ударов я попытался объявить int [1000], а не Integer [1000]

array int[] best 299ms iterator
array int[] best 296ms getter

Использование Integer [] и int [] представляет двойной удар по производительности, ListArray с итератором в 3 раза медленнее, чем int []. Реально продуманные реализации списков Java были похожи на собственные массивы...

Код для справки (звоните несколько раз):

    public static void testArray()
    {
        final long MAX_ITERATIONS = 1000000;
        final int MAX_LENGTH = 1000;

        Random r = new Random();

        //Integer[] array = new Integer[MAX_LENGTH];
        int[] array = new int[MAX_LENGTH];

        List<Integer> list = new ArrayList<Integer>()
        {{
            for (int i = 0; i < MAX_LENGTH; ++i)
            {
                int val = r.nextInt();
                add(val);
                array[i] = val;
            }
        }};

        long start = System.currentTimeMillis();
        int test_sum = 0;
        for (int i = 0; i < MAX_ITERATIONS; ++i)
        {
//          for (int e : array)
//          for (int e : list)          
            for (int j = 0; j < MAX_LENGTH; ++j)
            {
                int e = array[j];
//              int e = list.get(j);
                test_sum += e;
            }
        }

        long stop = System.currentTimeMillis();

        long ms = (stop - start);
        System.out.println("Time: " + ms);
    }

Ответ 29

Это зависит от того, как вы должны получить к нему доступ.

После сохранения, если вы в основном хотите выполнить операцию поиска, с небольшим или отсутствующим вставкой/удалением, перейдите в Array (поскольку поиск выполняется в O (1) в массивах, тогда как для добавления/удаления может потребоваться переупорядочение элементы).

После сохранения, если ваша основная цель состоит в том, чтобы добавлять/удалять строки, при небольшой или никакой операции поиска, перейдите к списку.

Ответ 30

ArrayList внутренне использует объект массива для добавления (или сохранения) элементы. Другими словами, ArrayList поддерживается данными Array -структура. Массив ArrayList может изменяться (или динамически).

Массив быстрее, чем Array, потому что ArrayList внутренне использует массив. если мы можем напрямую добавить элементы в Array и косвенно добавить элемент в Массив через ArrayList всегда напрямую механизм быстрее, чем косвенно механизм.

В классе ArrayList есть два перегруженных метода add():
1. add(Object): добавляет объект в конец списка.
2. add(int index , Object ): вставляет указанный объект в указанную позицию в списке.

Как динамически растет размер массива ArrayList?

public boolean add(E e)        
{       
     ensureCapacity(size+1);
     elementData[size++] = e;         
     return true;
}

Важным моментом для заметок из вышеприведенного кода является то, что мы проверяем емкость массива ArrayList перед добавлением элемента. securityCapacity() определяет, каков текущий размер занятых элементов и каков максимальный размер массива. Если размер заполненных элементов (включая новый элемент, добавляемый в класс ArrayList) больше максимального размера массива, тогда увеличьте размер массива. Но размер массива нельзя увеличить динамически. Итак, что происходит внутри, создается новый массив с емкостью

До Java 6

int newCapacity = (oldCapacity * 3)/2 + 1;

(Обновление) Из Java 7

 int newCapacity = oldCapacity + (oldCapacity >> 1);

также данные из старого массива копируются в новый массив.

Имея служебные методы в ArrayList, почему массив быстрее, чем ArrayList.