Массив или Список в Java. Что быстрее? - PullRequest
329 голосов
/ 04 апреля 2009

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

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

Ответы [ 31 ]

5 голосов
/ 24 мая 2013

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 раза быстрее, чем гибкость списка. Но это другой вопрос. Кстати, я тоже это проверил на основе созданного вручную ArrayList. Почти тот же результат.

5 голосов
/ 03 ноября 2016

Поскольку здесь уже есть много хороших ответов, я хотел бы дать вам некоторую другую практическую информацию, а именно: сравнение производительности вставки и итерации: примитивный массив и 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.");
    }
}

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

enter image description here

4 голосов
/ 04 апреля 2009

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

  1. Если количество строк почти постоянно, используйте массив (или ArrayList). Но если число меняется слишком сильно, лучше использовать LinkedList.
  2. Если есть (или будет) необходимость в добавлении или удалении элементов посередине, то вам обязательно нужно использовать LinkedList.
4 голосов
/ 04 апреля 2009

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

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

3 голосов
/ 04 апреля 2009

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

3 голосов
/ 04 апреля 2009

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

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

3 голосов
/ 19 октября 2012

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

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

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

2 голосов
/ 03 июля 2018

Я пришел сюда, чтобы лучше понять влияние на производительность использования списков над массивами. Мне пришлось адаптировать код здесь для моего сценария: массив / список ~ 1000 int, используя в основном геттеры, что означает массив [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-кратном снижении производительности. То, что будет работать через день, теперь занимает неделю.

* EDIT Весьма шокирован, я пытался объявить 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);
    }
2 голосов
/ 01 июня 2016

Ни один из ответов не содержал информацию, которая меня интересовала - многократное сканирование одного и того же массива много раз. Для этого пришлось создать тест 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);
    }

}
2 голосов
/ 19 февраля 2014

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

См. Лучшие практики Oracle Java: http://docs.oracle.com/cd/A97688_16/generic.903/bp/java.htm#1007056

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

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