我习惯在我的程序中使用HashMap
,因为我知道它通常效率最高(如果使用得当)并且可以轻松应对大型地图。我知道EnumMap
对于枚举键非常有用,但是我经常会生成一个永远不会很大的小地图,很快就会被丢弃,并且没有并发问题。
这些小型,本地和临时用途HashMap<K,V>
是否过于复杂?在这些情况下,我可以使用另一个简单的实现吗?
我想我正在寻找Map
实施,类似于ArrayList
List
。它存在吗?
在回复后添加:
这是一个缓慢但非常简单的实现可能更好的情况 - 当我有很多这些Map
时。例如,假设我有一百万左右的这些微小的小地图,每个地图都有少量(通常少于三个)条目。我的参考率很低 - 也许我实际上并没有在大部分时间丢弃之前引用它们。仍然是HashMap
是他们最好的选择吗?
资源利用率不仅仅是速度 - 我想要的东西不会破坏堆很多,并且使GC需要很长时间,例如。
可能HashMap
是正确答案,但这不是过早优化的情况(或者至少可能不是)。
经过一番思考后添加了很多:
我决定手工编写自己的SmallMap
代码。用AbstractMap
制作一个很容易。我还添加了几个构造函数,以便可以从现有的SmallMap
构建Map
。
在此过程中,我必须决定如何代表Entry
并为SmallSet
方法实施entrySet
。
我通过编码(并对其进行单元测试)学到了很多东西,并想分享这个,以防其他人想要一个。它在github上here。
答案 0 :(得分:18)
Java中没有Map
的标准小实现。 HashMap
是最好,最灵活的Map
实施方案之一,很难被击败。但是,在非常小的需求区域 - 堆使用和构造速度至关重要 - 可以做得更好。
我在GitHub上实现了SmallCollections来演示如何做到这一点。我会喜欢关于我是否成功的一些评论。我无法确定。
虽然这里提供的答案有时是有帮助的,但总的来说,他们倾向于误解这一点。无论如何,回答我自己的问题,最终对我来说比给予它更有用。
这里的问题已经达到了目的,这就是为什么我“自己回答”。
答案 1 :(得分:12)
我认为这是不成熟的优化。你有记忆问题吗?创建太多地图会导致性能问题?如果不是,我认为HashMap没问题。
此外,看看API,我没有看到比HashMap
更简单的东西。
如果您遇到问题,可以推出自己的Map实现,它具有非常简单的内部功能。但是我怀疑你会比默认的Map实现做得更好,而且你要确保你的新类有效。在这种情况下,您的设计可能存在问题。
答案 2 :(得分:4)
HashMap可能是最轻量级的简单集合。
有时,更有效的解决方案是使用POJO。例如如果您的键是字段名称和/或您的值是基元。
答案 3 :(得分:2)
HashMap是一个不错的选择,因为它提供了普通案例O(1)
置列和获取。它不保证排序虽然像SortedMap实现(即TreeMap O(log n)
puts和gets)但如果你没有要求排序那么HashMap更好。
答案 4 :(得分:1)
我同意@hvgotcodes认为这是过早的优化,但知道工具箱中的所有工具仍然很好。
如果你对地图中的内容做了很多迭代,那么LinkedHashMap通常比HashMap快得多,如果你有很多线程同时使用Map,那么ConcurrentHashMap通常是更好的选择。我不担心任何Map实现对于小型数据集都是低效的。通常情况下,如果您的哈希值不正确,或者某些事情导致其负载过少,则错误构造的地图很容易因大量数据而变得低效。
然后当然有些情况下HashMap完全没有意义,比如你有三个值,你总是用键0,1和2索引,但我假设你理解: - )
答案 5 :(得分:1)
HashMap使用更多或更少的内存(创建时),具体取决于您的初始化方式:更多存储桶意味着更多内存使用,但更快访问适用于大量项目;如果您只需要少量项目,则可以使用较小的值对其进行初始化,这样可以减少仍然很快的桶数(因为它们每个都会收到一些项目)。如果正确设置,则不会浪费内存(权衡主要取决于内存使用情况与速度的关系)。
对于堆碎片和GC循环浪费等等,Map实现对它们的作用并不多;这一切都归结为你如何设置它。理解这不是关于Java的实现,但是泛型(例如,不能假设像EnumMap
这样的关键值的任何事情)哈希表(不是HashTable
s)是最好的实现地图结构。
答案 6 :(得分:1)
Android有一个ArrayMap,旨在最大限度地减少内存。除了处于核心之外,它还在v4支持库中,理论上,它也应该能够为Oracle或OpenJDK JRE编译。这是指向the source of ArrayMap in a fork of the v4 support library on github的链接。
答案 7 :(得分:0)
有一个名为AirConcurrentMap的替代方案,在1K条目之上的内存效率高于我找到的任何其他Map,并且比基于键的操作的ConcurrentSkipListMap更快,并且比任何迭代Map更快,并且具有内部线程池并行扫描。它是一个有序的,即NavigableMap和ConcurrentMap。它可以免费用于非商业性的无源使用,并且可以使用或不使用来源进行商业许可。有关图表,请参阅boilerbay.com。完全披露:我是作者。
AirConcurrentMap符合标准,因此它在任何地方都是插件兼容的,即使对于常规地图也是如此。
迭代器已经非常快,尤其是1K以上的条目。高速扫描使用具有单次访问(k,v)回调的“访问者”模型,该回调达到Java 8并行流的速度。 AirConcurrentMap并行扫描超过Java 8并行流约4倍。线程访问者将split()和merge()方法添加到提醒map / reduce之一的单线程访问者:
static class ThreadedSummingVisitor<K> extends ThreadedMapVisitor<K, Long> {
private long sum;
// This is idiomatic
long getSum(VisitableMap<K, Long> map) {
sum = 0;
map.getVisitable().visit(this);
return sum;
}
@Override
public void visit(Object k, Long v) {
sum += ((Long)v).longValue();
}
@Override
public ThreadedMapVisitor<K, Long> split() {
return new ThreadedSummingVisitor<K>();
}
@Override
public void merge(ThreadedMapVisitor<K, Long> visitor) {
sum += ((ThreadedSummingVisitor<K>)visitor).sum;
}
}
...
// The threaded summer can be re-used in one line now.
long sum = new ThreadedSummingVisitor().getSum((VisitableMap)map);
答案 8 :(得分:0)
我也很感兴趣,只是为了一个实验,我创建了一个地图,该地图仅在字段中存储键和值,最多允许5个条目。它消耗的内存少了4个,并且比HashMap快16倍 https://github.com/stokito/jsmallmap