Как известно, в Java существуют примитивные типы для чисел (byte, short, int, long, float, double) и объектные обёртки над ними (Byte, Short, Integer, Long, Float, Double). В различных статьях можно встретить диаметрально противоположные рекомендации о том, чем пользоваться. С одной стороны объектные обёртки универсальны: их можно использовать со стандартными коллекциями, которые удобны, инкапсулированы и вообще прекрасны. Но боксинг убивает производительность и ест кучу памяти. Примитивные типы быстры и компактны, но их можно поместить только в массивы, которые и от записи не защитишь, и абстракция на нуле. Если же вам нужно что-то типа Map, для отображения чего-нибудь на числа, то придётся либо мириться с потерей производительности и памяти, либо использовать сторонние библиотеки, реализующие нестандартный интерфейс. Однако в некоторых случаях вам помогут изменяемые (mutable) числа.
Представьте себе, что вам нужно подсчитывать количества разных строк, которые откуда-то поступают. Часто пишут примерно такой код:
Если типов строк не так много, а повторов хватает, то боксинг будет создавать миллионы временных Integer-объектов, которые потом будет чистить сборщик мусора. Представить страшно. Нет, это не катастрофически медленно, но всё же несложно ускорить процедуру 2-3 раза. Для этого мы и используем MutableInteger.
Такой класс есть в некоторых библиотеках (например, в org.apache.commons.lang), но его несложно и написать самому. Простая реализация может выглядеть примерно так:
Далее можно добавить простые методы для арифметических действий. Ещё удобно унаследовать Number, реализовать интерфейс Comparable, а также не забыть про equals и hashCode (в hashCode можно просто вернуть value). Но для текущей задачи нам хватит того, что написано. Теперь countStrings() можно переписать следующим образом:
Не очень хорошо, конечно, раскрывать детали реализации, возвращая Map<String, MutableInteger>, но если мы унаследуем java.lang.Number, то можно вернуть Map<String, Number>. Ну или в крайнем случае после подсчёта скопировать всё в новую Map. Аналогично можно собирать не только количество, но и другую статистику по набору объектов.
Заметим также, что java.util.concurrent.atomic.AtomicInteger по сути тоже MutableInteger, однако накладные расходы на атомарность могут даже превысить расходы на создание объектов и сборку мусора из первого примера, поэтому отдельный класс MutableInteger всё же нужен.
Представьте себе, что вам нужно подсчитывать количества разных строк, которые откуда-то поступают. Часто пишут примерно такой код:
public Map<String, Integer> countStrings() { Map<String, Integer> counts = new HashMap<String, Integer>(); while(true) { String next = getNextString(); if(next == null) break; Integer val = counts.get(next); if(val == null) counts.put(next, 1); else counts.put(next, val+1); } return counts; }
Если типов строк не так много, а повторов хватает, то боксинг будет создавать миллионы временных Integer-объектов, которые потом будет чистить сборщик мусора. Представить страшно. Нет, это не катастрофически медленно, но всё же несложно ускорить процедуру 2-3 раза. Для этого мы и используем MutableInteger.
Такой класс есть в некоторых библиотеках (например, в org.apache.commons.lang), но его несложно и написать самому. Простая реализация может выглядеть примерно так:
public class MutableInteger { private int value; public MutableInteger(int value) { this.value = value; } public int intValue() { return value; } public void set(int value) { this.value = value; } public void increment() { value++; } public String toString() { return String.valueOf(value); } }
Далее можно добавить простые методы для арифметических действий. Ещё удобно унаследовать Number, реализовать интерфейс Comparable, а также не забыть про equals и hashCode (в hashCode можно просто вернуть value). Но для текущей задачи нам хватит того, что написано. Теперь countStrings() можно переписать следующим образом:
public Map<String, MutableInteger> countStrings() { Map<String, MutableInteger> counts = new HashMap<String, MutableInteger>(); while(true) { String next = getNextString(); if(next == null) break; MutableInteger val = counts.get(next); if(val == null) counts.put(next, new MutableInteger(1)); else val.increment(); } return counts; }
Не очень хорошо, конечно, раскрывать детали реализации, возвращая Map<String, MutableInteger>, но если мы унаследуем java.lang.Number, то можно вернуть Map<String, Number>. Ну или в крайнем случае после подсчёта скопировать всё в новую Map. Аналогично можно собирать не только количество, но и другую статистику по набору объектов.
Заметим также, что java.util.concurrent.atomic.AtomicInteger по сути тоже MutableInteger, однако накладные расходы на атомарность могут даже превысить расходы на создание объектов и сборку мусора из первого примера, поэтому отдельный класс MutableInteger всё же нужен.
