Ответ 1
Другие уже предложили несколько сторонних реализаций примитивнозначных карт. Для полноты, я хотел бы упомянуть о некоторых способах избавиться от карт, которые вы, возможно, захотите рассмотреть. Эти решения не всегда будут возможны, но когда они будут, они, как правило, будут быстрее и эффективнее с точки зрения памяти, чем любая карта.
Альтернатива 1: используйте простые старые массивы.
Простой массив double[]
может быть не таким сексуальным, как причудливая карта, но очень мало может превзойти его в компактности и скорости доступа.
Конечно, массивы поставляются с рядом ограничений: их размер фиксирован (хотя вы всегда можете создать новый массив и скопировать в него старый контент), а их ключи могут быть только небольшими положительными целыми числами, которые для эффективности, должен быть достаточно плотным (т.е. общее количество используемых ключей должно быть достаточно большой доли наивысшего значения ключа). Но если это случается для ваших ключей, или если вы можете организовать это так, массивы примитивных значений могут быть очень эффективными.
В частности, если вы можете назначить уникальный малый целочисленный идентификатор для каждого ключевого объекта, вы можете использовать этот идентификатор в качестве индекса в массиве. Аналогично, если вы уже сохраняете свои объекты в массиве (например, как часть более сложной структуры данных) и просматриваете их по индексу, вы можете просто использовать один и тот же индекс для поиска любых дополнительных значений метаданных в другом массиве.
Вы даже можете отказаться от требования уникальности идентификатора, если вы внедрили какой-то механизм обработки столкновений, но в этот момент вы хорошо на пути к реализации собственной хеш-таблицы. В некоторых случаях это может иметь смысл, но обычно в этот момент, вероятно, проще использовать существующую стороннюю реализацию.
Альтернатива 2: Настройте свои объекты.
Вместо того, чтобы сохранять карту из ключевых объектов в примитивные значения, почему бы просто не сделать эти значения в свойствах самих объектов? Это, в конце концов, то, что объектно-ориентированное программирование - все о — группируя связанные данные в значимые объекты.
Например, вместо сохранения HashMap<Point2D, Boolean> onSea
, почему бы просто не дать своим точкам логическое свойство onSea
? Конечно, вам нужно будет определить свой собственный класс точек для этого, но нет причин, по которым вы не можете заставить его расширить стандартный класс Point2D
, если хотите, чтобы вы могли передавать свои пользовательские точки в любой метод который ожидает a Point2D
.
Опять же, этот подход может не всегда работать напрямую, например. если вам нужно работать с классами, которые невозможно изменить (но см. ниже), или если значения, которые вы хотите сохранить, связаны с несколькими объектами (как в вашем ConcurrentHashMap<Point2D, HashMap<Point2D, Double>>
).
Однако для последнего случая вы все равно сможете решить проблему, соответствующим образом переработав представление данных. Например, вместо представления взвешенного графика как Map<Node, Map<Node, Double>>
вы можете определить класс Edge
, например:
class Edge {
Node a, b;
double weight;
}
а затем добавьте свойство Edge[]
(или Vector<Edge>
) к каждому node, содержащему любые ребра, связанные с этим node.
Альтернатива 3: Объединение нескольких карт в один.
Если у вас есть несколько карт с одинаковыми ключами и не может просто превратить значения в новые свойства объектов ключей, как было предложено выше, рассмотрите их группировку в один класс метаданных и создайте одну карту из ключей в объекты этого класс. Например, вместо Map<Item, Double> accessFrequency
и a Map<Item, Long> creationTime
рассмотрим определение одного класса метаданных, например:
class ItemMetadata {
double accessFrequency;
long creationTime;
}
и имеет единственный Map<Item, ItemMetadata>
для хранения всех значений метаданных. Это более эффективно с точки зрения памяти, чем наличие нескольких карт, а также позволяет экономить время, избегая избыточных поисков карт.
В некоторых случаях для удобства вы также можете включить в каждый объект метаданных ссылку на соответствующий основной объект, чтобы вы могли получить доступ как через одну ссылку на объект метаданных. Который естественно переходит в...
Альтернатива 4: Используйте декоратор.
Как сочетание двух предыдущих альтернатив, если вы не можете напрямую добавлять дополнительные свойства метаданных в ключевые объекты, рассмотрите их вместо декораторов который может содержать дополнительные значения. Таким образом, например, вместо прямого создания собственного класса точек с дополнительными свойствами вы могли бы просто сделать что-то вроде:
class PointWrapper {
Point2D point;
boolean onSea;
// ...
}
Если вам нравится, вы можете даже превратить эту оболочку в полномасштабный декоратор, реализуя переадресацию метода, но даже для простой "тупой" оболочки может быть достаточно для многих целей.
Этот подход наиболее полезен, если вы можете организовать хранение и работу только с обертками, так что вам никогда не нужно искать оболочку, соответствующую разворачиваемому объекту. Конечно, если вам это нужно делать иногда (например, потому что вы только получаете разворачиваемые объекты из другого кода), вы можете сделать это с помощью одного Map<Point2D, PointWrapper>
, но затем вы снова вернетесь к предыдущей альтернативе.