Подтвердить что ты не робот

Самый эффективный способ хранения массива в Java?

Я не слишком беспокоюсь о эффективности времени (операция будет редка), а скорее об эффективности памяти: Могу ли я увеличить массив без временного использования всех значений в два раза?

Есть ли более эффективный способ выращивания большого массива, чем создание нового и копирование по всем значениям? Как, соединяя его с новым?

Как насчет того, что массивы фиксированного размера хранятся в другом массиве и перераспределяют/копируют этот верхний уровень? Оставит ли это фактические значения на месте?

Мне известно о ArrayList, но мне нужно много контролировать доступ к массиву, и доступ должен быть очень быстрым. Например, я предпочитаю a[i] al.get(i).

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

4b9b3361

Ответ 1

Существует ли более эффективный способ роста большой массив, чем создание нового и копирование всех значений? Подобно, конкатенируя его с новым?

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

Как насчет массивов фиксированного размера хранится в другом массиве и перераспределяется /копировать этот файл верхнего уровня? Будет ли это оставить фактические значения на месте?

Вы имеете в виду массив массивов и рассматриваете его как один большой массив, состоящий из конкатенации базовых массивов? Да, это сработало бы (подход "подделка путем использования косвенности" ), как и в Java, Object[][] - это просто массив указателей на экземпляры Object[].

Ответ 2

Лучший способ иметь динамический размер массива или список элементов - использовать ArrayList.

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

Но если вы должны изменить размер собственного массива, лучше использовать System.arraycopy() или Arrays.copyOf().

Arrays.copyOf() можно просто использовать следующим образом:

int[] oldArr;
int newArr = Arrays.copyOf(oldArr, oldArr.length * 2);

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

В классе Arrays есть много отличных методов для работы с массивами.

И

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

Ответ 3

Массивы являются постоянными размерами, поэтому нет возможности их выращивать. Вы можете копировать их только с помощью System.arrayCopy.


ArrayList делает именно то, что вам нужно. Он оптимизировался намного лучше, чем мог бы сделать любой из нас, если вы не посвятите ему значительное время. Он использует внутренне System.arrayCopy.


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

Ответ 4

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

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

alt text

Ответ 5

"Могу ли я вырастить массив без временно имея все значения дважды?"

Даже если вы скопируете массивы, вы будете иметь все значения один раз. Если вы не вызываете clone() для своих значений, они передаются по ссылке в новый массив.

Если у вас уже есть ваши значения в памяти, то только дополнительный расход памяти при копировании в новый массив выделяет новый массив Object [], который не занимает много памяти, поскольку это всего лишь список указателей на объектов значения.

Ответ 7

AFAIK единственным способом увеличения или уменьшения массива является использование System.arraycopy

   /**
    * Removes the element at the specified position in this list.
    * Shifts any subsequent elements to the left (subtracts one from their
    * indices).
    *
    * @param index the index of the element to removed.
    * @return the element that was removed from the list.
    * @throws    IndexOutOfBoundsException if index out of range <tt>(index
    *     &lt; 0 || index &gt;= length)</tt>.
    */
    public static <T> T[] removeArrayIndex(T[] src, int index) {
        Object[] tmp = src.clone();

        int size = tmp.length;
        if ((index < 0) && (index >= size)) {
            throw new ArrayIndexOutOfBoundsException(index);
        }

        int numMoved = size - index - 1;
        if (numMoved > 0) {
            System.arraycopy(tmp, index + 1, tmp, index, numMoved);
        }
        tmp[--size] = null; // Let gc do its work

        return (T[]) Arrays.copyOf(tmp, size - 1);
    }

   /**
    * Inserts the element at the specified position in this list.
    * Shifts any subsequent elements to the rigth (adds one to their indices).
    *
    * @param index the index of the element to inserted.
    * @return the element that is inserted in the list.
    * @throws    IndexOutOfBoundsException if index out of range <tt>(index
    *     &lt; 0 || index &gt;= length)</tt>.
    */
    public static <T> T[] insertArrayIndex(T[] src, Object newData, int index) {
        Object[] tmp = null;
        if (src == null) {
            tmp = new Object[index+1];
        } else {
            tmp = new Object[src.length+1];

            int size = tmp.length;
            if ((index < 0) && (index >= size)) {
                throw new ArrayIndexOutOfBoundsException(index);
            }

            System.arraycopy(src, 0, tmp, 0, index);
            System.arraycopy(src, index, tmp, index+1, src.length-index);
        }

        tmp[index] = newData;

        return (T[]) Arrays.copyOf(tmp, tmp.length);
    }    

Ответ 8

Очевидно, что важный бит здесь не в том случае, если вы объедините массивы или скопируете их; что важнее ваша стратегия роста массива. Нетрудно заметить, что очень хороший способ выращивания массива всегда удваивает свой размер, когда он становится полным. Таким образом, вы увеличите стоимость добавления элемента к O (1), поскольку фактический этап роста будет происходить только относительно редко.

Ответ 9

Является ли массив большим, или вы ссылаетесь на большие ссылочные типы?

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

int[] largeArrayWithSmallElements = new int[1000000000000];
myClass[] smallArrayWithLargeElements = new myClass[10000];

Edit:

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

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

Для оптимальной эффективности памяти вы можете создать контейнерный класс с массивом ArrayList.

Что-то вроде:

class DynamicList
{
    public long BufferSize;
    public long CurrentIndex;

    ArrayList al = new ArrayList();

    public DynamicList(long bufferSize)
    {
        BufferSize = bufferSize;

        al.add(new long[BufferSize]);
    }

    public void add(long val)
    {
        long[] array;

        int arrayIndex = (int)(CurrentIndex / BufferSize);

        if (arrayIndex > al.size() - 1)
        {
            array = new long[BufferSize];
            al.add(array);
        }
        else
        {
            array = (long[])al.get(arrayIndex);
        }

        array[CurrentIndex % BufferSize] = val;
    }

    public void removeLast()
    {
        CurrentIndex--;
    }

    public long get(long index)
    {
        long[] array;

        int arrayIndex = (int)(index / BufferSize);

        if (arrayIndex < al.size())
        {
            array = (long[])al.get(arrayIndex);
        }
        else
        {
            // throw Exception
        }

        return array[index % BufferSize];
    }
}

(моя джава ржавая, поэтому, пожалуйста, медведь со мной...)

Ответ 10

Один из способов сделать это - связанный список узлов массива. Это несколько сложно, но предпосылка такова:

У вас есть связанный список, и каждый node в списке ссылается на массив. Таким образом, ваш массив может расти без копирования. Чтобы расти, вам нужно только добавить дополнительные узлы в конце. Поэтому операция "дорогого" роста происходит только при каждом M-операциях, где M - размер каждого node. Конечно, это предполагает, что вы всегда добавляете в конец, и вы не удаляете.

Вставка и удаление в этой структуре довольно сложны, но если вы можете избежать их, то это идеально.

Единственная потеря с этой структурой (игнорирование вставки и удаления) - с помощью get. Получатели будут немного длиннее; для доступа к правильному node требуется доступ к правильному node в связанном списке, а затем выборка. Если есть много посещений по середине, это может быть медленным, но есть трюки для ускорения связанных списков вверх.

Ответ 11

Вы просмотрели GNU Trove для высокоэффективных коллекций java? Их коллекции хранят примитивы непосредственно для гораздо более эффективного использования памяти.

Ответ 12

Heres отметка времени, затраченного на добавление и удаление элементов из коллекции/arraylist/vector