У меня есть миллион целых чисел, которые я хочу сохранить в структуре данных. Я хотел знать, является ли массив int (int[]) более эффективным, чем ArrayList. Есть ли какой-либо прирост производительности при использовании int[] вместо ArrayList? Я считаю, что ArrayList использует больше памяти, чем int[], но я не знаю, незначительна ли эта дополнительная память или нет.
Если бы я увеличил размер массива (с одного миллиона до одного миллиарда), изменился бы ответ? Является ли дополнительная память, используемая ArrayList, незначительной или нет?
@WJS Правда... ArrayList будет хранить 1 миллион целочисленных объектов. Но объект Integer должен быть эффективным и не занимать так много памяти. Насколько велики будут накладные расходы? Будет ли дополнительная память незначительной или немалой?
Я бы рекомендовал вам сначала написать код, а затем оценить производительность, когда он будет готов. Начните со списка.
Вот API ArrayList. В начале есть краткое примечание о производительности. Но remove
требует, чтобы объекты, следующие за удаленным, были сдвинуты. Обратите внимание, что у него есть метод ensureCapacity
.
int[]
использует минимальное пространство, тогда как список целочисленных объектов хранит ссылки на целочисленные объекты, каждый из которых содержит поле int
.
Также стоит рассмотреть возможность использования сторонних библиотек; например Guava Ints.asList(int[])
, который оборачивает int[]
как List<Integer>
фиксированной длины
Вы также можете рассмотреть IntBuffer, который имеет преимущества в производительности массива, но дает вам некоторые, но не все возможности списка.
посмотрите здесь, чтобы получить информацию о производительности.
Проект Валгалла / Объекты ценностей должен устранить большинство/все различия, если/когда они возникнут.
По умолчанию емкость ArrayList
может в два раза превышать количество содержащихся в нем элементов. Итак, если вы знаете предполагаемый размер, укажите начальную вместимость . Если размер неизвестен, обрежьте, когда закончите добавлять элементы.
Однако основное различие между ArrayList<Integer>
и int[]
заключается в разнице между Integer
и int
: первый является объектом, и каждый объект хранит некоторые метаданные для JVM (тип среды выполнения, хеш-код идентификации и т. д.) в дополнение к своим полям. что делает его несколько больше обычного int
. Размер этих метаданных не указан и может варьироваться. Вы можете измерить его следующим образом:
public class Test {
static final int elements = 10_000_000;
private static long usedMemory() {
var r = Runtime.getRuntime();
return r.totalMemory() - r.freeMemory();
}
static void measure(String name, Runnable code) {
System.gc(); // clean up previous tests
long start = usedMemory();
code.run();
long end = usedMemory();
System.out.println(name + " ".repeat(35 - name.length()) + " used " + (double)(end - start) / elements + " bytes/element");
}
public static void main(String[] args) {
measure("int[]", () -> {
var a = new int[elements];
});
measure("Integer[] with small numbers", () -> {
var a = new Integer[elements];
for (int i = 0; i < elements; i++) {
a[i] = i % 64;
}
});
measure("Integer[] with large numbers", () -> {
var a = new Integer[elements];
for (int i = 0; i < elements; i++) {
a[i] = i;
}
});
measure("ideal capacity ArrayList<Integer>", () -> {
var a = new ArrayList<Integer>(elements);
for (int i = 0; i < elements; i++) {
a.add(i);
}
});
measure("auto sized ArrayList<Integer>", () -> {
var a = new ArrayList<Integer>();
for (int i = 0; i < elements; i++) {
a.add(i);
}
});
}
}
На моей JVM это печатает:
int[] used 4.1809328 bytes/element
Integer[] with small numbers used 4.1949816 bytes/element
Integer[] with large numbers used 20.7624576 bytes/element
ideal capacity ArrayList<Integer> used 20.8476584 bytes/element
auto sized ArrayList<Integer> used 30.3283208 bytes/element
То есть на этой JVM объект Integer
занимает 16 байт, а ссылка на объект 4 байта (если бы моя куча была достаточно большой, вместо этого ей понадобилось бы 8 байт). Если Integer
содержит небольшие числа, объекты Integer
будут использоваться повторно, и вы платите только за ссылку. Напротив, если объекты Integer
содержат большие числа, для каждого элемента создается новый Integer
.
ArrayList идеальной емкости имеет размер массива ссылок. ArrayList с автоматическим ростом может быть в два раза больше, чем необходимо.
Сравнение производительности также будет зависеть от 1) того, создаются ли значения Integer
с использованием Integer.valueOf(...)
или автобокса или new Integer(...)
2) являются ли числа обычно небольшими (и, следовательно, кэшируются) или нет, и 3) использует ли ваша JVM 32-битную или 64-битную версию. Рекомендации. Ваша оценка пессимальна по отношению к 2.
Однако... во всех случаях int[]
будет занимать меньше места, чем ArrayList
с тем же количеством элементов, даже если последний был обрезан с помощью trimToSize
.
@StephenC: Хорошая мысль, теперь я добавил для этого отдельный тестовый пример.
Внимание: System.gc();
— это просьба, а не приказ. Сбор мусора (GR) может происходить, а может и не происходить. Сборщик мусора, если он действительно начинается, может не завершиться до продолжения работы вашего кода, в зависимости от реализации сборщика мусора. У нас нет контроля.
ArrayLists
Берите только объекты, поэтому вместо 1 млн 32-битных целых чисел, хранящихся в массиве, вы будете хранить 1 млн целочисленных объектов. Конечно, списки проще в использовании и позволяют легче манипулировать значениями, например добавлять, удалять, удалять и т. д. И они динамически растут по мере необходимости.