Java中常用的Map有以下几种:
- HashMap:无序,基于哈希表实现,允许空键值,非线程安全;
- LinkedHashMap:有序,基于哈希表实现,维护插入顺序或者访问顺序,允许空键值,非线程安全;
- TreeMap:有序,基于红黑树实现,按照键的自然顺序或者比较器顺序排序,不允许空键值,非线程安全;
- ConcurrentHashMap:无序,基于分段锁实现,线程安全,支持高并发;
- WeakHashMap:无序,基于哈希表实现,弱键引用,允许空键值,非线程安全;
- IdentityHashMap:无序,基于哈希表实现,比较键的内存地址,不比较键的equals()方法,不允许空键值,非线程安全。
除了以上常用的Map实现类,Java8中还引入了新的类型:ConcurrentSkipListMap,它是一个并发的有序map,基于跳表(skip list)实现,支持高并发,也支持排序。
HashMap
HashMap 是基于哈希表的 Map 实现,它不保证元素的顺序,可以存储 null 键和 null 值。它的优点是插入和查找元素的时间复杂度都是常数级别的 O(1),适合在需要快速查找元素时使用。但是,由于它的实现机制是哈希表,它的缺点是当哈希冲突发生时,会导致查找性能下降,尤其在哈希表的负载因子超过某个阈值时,会导致哈希表扩容,重新计算哈希值,可能会影响性能。因此,它适用于无序存储、快速查找的场景。
TreeMap
TreeMap 是基于红黑树的 Map 实现,它可以保证元素的顺序,支持自然排序和自定义排序,但不支持 null 键,而且插入和查找元素的时间复杂度是 O(logn),适合在需要排序和范围查找的场景中使用。
LinkedHashMap
LinkedHashMap 是 HashMap 的一个子类,它在 HashMap 的基础上维护了一个双向链表,可以保证元素的顺序,有点类似于 LRU 缓存算法的实现。它也可以存储 null 键和 null 值,但是,它相比 HashMap 多了一些维护链表的操作,所以性能略低于 HashMap,但是相比 TreeMap,它的插入和查找元素的时间复杂度仍然是常数级别的 O(1),适合在需要按照插入顺序或者访问顺序存储元素的场景中使用。
ConcurrentHashMap
ConcurrentHashMap 是线程安全的 Map 实现,它采用了分段锁的机制,使得在多线程环境下,多个线程可以同时对不同的段进行操作,提高了并发度,从而提高了性能。它的优点是线程安全,插入和查找元素的时间复杂度都是 O(1),适合在多线程环境下进行并发操作的场景中使用。
优点:
- 高并发性:ConcurrentHashMap 内部采用了分段锁机制,不同的线程可以同时操作不同的段,这样可以提高并发性能。
- 高效性:ConcurrentHashMap 在获取元素的时候可以不用加锁,这样可以减少线程间的竞争,提高了并发性能。
- 可伸缩性:由于 ConcurrentHashMap 支持并发访问,所以可以适应多线程的环境,支持高并发的操作。
缺点:
- 内存消耗:ConcurrentHashMap 内部需要维护 Segment 数组,因此在存储空间方面比普通的 HashMap 稍微占用一些内存。(java8后已经不存在了)
- 无序性:ConcurrentHashMap 内部不保证存储的元素的顺序。
适用场景:
- 多线程访问频繁:在高并发环境下需要频繁读写 Map 的场景。ConcurrentHashMap 适用于在多线程环境下进行并发操作的场景中,因为它采用了分段锁的机制,所以在多线程访问频繁的情况下可以提高性能。
- 高并发的数据操作:ConcurrentHashMap 适用于需要高效并发操作的场景,比如缓存、高并发的数据统计等。
- 数据量较大:ConcurrentHashMap 内部采用分段锁机制,因此可以支持较大规模的数据量。
需要注意的是,虽然 ConcurrentHashMap 支持并发访问,但是它并不能保证并发修改的安全性,因此在多线程并发修改的时候,仍然需要考虑同步和原子性的问题。
WeakHashMap
WeakHashMap 是一种特殊的 Map 实现,它的键是弱引用类型,当某个键不再被程序中的其他部分引用时,这个键所对应的键值对就可以被垃圾回收器回收。它的缺点是性能略低于 HashMap,适用于需要缓存对象的场景,避免内存泄漏。
优缺点:
- 它的优点是可以避免内存泄漏,因为它的键是弱引用类型,当对象不再被引用时,会被垃圾回收器回收,从而自动删除对应的键值对。
- 它的缺点是性能略低于 HashMap,因为它的实现需要频繁地进行垃圾回收。
场景:
WeakHashMap 适用于需要缓存对象的场景,例如缓存图片、音频等资源,因为这些资源通常是比较大的,如果使用强引用类型的 Map,容易导致内存溢出。使用 WeakHashMap 可以避免这个问题,因为当资源不再被程序中的其他部分引用时,可以自动回收对应的键值对,释放内存空间。
IdentityHashMap
IdentityHashMap 是一种基于对象引用比较相等的 Map 实现,它的比较方式是通过使用 == 操作符来比较键的引用是否相等,而不是使用 equals() 方法。即,只有当两个键引用同一个对象时,它们才被认为是相等的。
IdentityHashMap 的优点是:
与其他 Map 实现相比,它的键比较方式更加灵活,可以自定义比较规则。
在某些情况下,使用 IdentityHashMap 可以提高性能,例如当需要以对象引用作为键的时候,就可以避免 equals() 方法的开销。
IdentityHashMap 的缺点是:
由于使用引用比较相等的方式,因此在处理字符串和基本数据类型等对象时,可能会得到不符合预期的结果。
在多线程环境下使用 IdentityHashMap 可能会导致并发问题,需要使用同步机制进行保护。
IdentityHashMap 的适用场景是:
当需要以对象引用作为键时,可以使用 IdentityHashMap 来避免 equals() 方法的开销,从而提高性能。
当需要自定义比较规则时,也可以使用 IdentityHashMap 来实现自定义的比较方式。