Питання полягає в (зараз) про збереження безлічі даних, які можна представити, використовуючи примітивні типи, наприклад int
, на карті. Деякі з відповідей тут, на мій погляд, дуже вводять в оману. Давайте розберемося, чому.
Я змінив орієнтир від trove, щоб виміряти як час виконання, так і споживання пам'яті. Я також додав PCJ до цього еталону, який є іншою бібліотекою колекцій для примітивних типів (я широко використовую цю). Офіційний бенчмарк не порівнює IntIntMaps з Java Collection Map<Integer, Integer>
, ймовірно, зберігання Integers
та зберігання ints
з технічної точки зору не є однаковим. Але користувач може не перейматися цією технічною деталлю, він хоче ефективно зберігати дані, що представляються ints
.
Спочатку відповідна частина коду:
new Operation() {
private long usedMem() {
System.gc();
return Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
}
// trove
public void ours() {
long mem = usedMem();
TIntIntHashMap ours = new TIntIntHashMap(SET_SIZE);
for ( int i = dataset.size(); i-- > 0; ) {
ours.put(i, i);
}
mem = usedMem() - mem;
System.err.println("trove " + mem + " bytes");
ours.clear();
}
public void pcj() {
long mem = usedMem();
IntKeyIntMap map = new IntKeyIntOpenHashMap(SET_SIZE);
for ( int i = dataset.size(); i-- > 0; ) {
map.put(i, i);
}
mem = usedMem() - mem;
System.err.println("pcj " + mem + " bytes");
map.clear();
}
// java collections
public void theirs() {
long mem = usedMem();
Map<Integer, Integer> map = new HashMap<Integer, Integer>(SET_SIZE);
for ( int i = dataset.size(); i-- > 0; ) {
map.put(i, i);
}
mem = usedMem() - mem;
System.err.println("java " + mem + " bytes");
map.clear();
}
Я припускаю, що дані надходять як примітивні ints
, що здається здоровим. Але це передбачає штраф за час утиліти Java, через автоматичний бокс, який не є необхідним для примітивних систем колекцій.
Результати виконання (без gc()
викликів, звичайно) на WinXP, jdk1.6.0_10:
100000 операцій пут 100000 містить операції
колекції java 1938 мс 203 мс
trove 234 мс 125 мс
pcj 516 ms 94 мс
Хоча це вже може здатися драстичним, це не є причиною використання такої рамки.
Причина - продуктивність пам’яті. Результати для карти, що містить 100000 int
записів:
Колекції java коливаються між 6644536 та 7168840 байтами
trove 1853296 байт
pcj 1866112 байт
Колекціям Java потрібно більше ніж утричі більше пам’яті порівняно з примітивними рамками колекції. Тобто ви можете зберігати в пам'яті втричі більше даних, не вдаючись до дискового вводу-виводу, що знижує продуктивність роботи на величини. І це має значення. Прочитайте високу масштабність, щоб з’ясувати, чому.
На мій досвід, велике споживання пам’яті є найбільшою проблемою продуктивності Java, що, звичайно, призводить до гіршої продуктивності роботи. Тут можуть справді допомогти примітивні рамки колекції.
Отже: Ні, java.util - це не відповідь. І "додавання функціональності" до колекцій Java - не сенс запитувати про ефективність. Також сучасні колекції JDK не «випереджають навіть спеціалізовані колекції Trove».
Відмова від відповідальності: Орієнтир тут ще далеко не повний, і він не є ідеальним. Це покликане загнати додому точку, яку я пережив у багатьох проектах. Примітивні колекції досить корисні, щоб переносити рибний API - якщо ви працюєте з великою кількістю даних.