HashMap 是存储键值对的集合,JDK1.7 底层是数组 + 链表,1.8 优化为数组 + 链表 + 红黑树。 工作流程:put 时先计算 key 的 hashCode,再转成数组下标;如果位置为空,直接存入;不为空就是哈希冲突。 冲突解决用链地址法(拉链法):把冲突元素挂在链表后面。JDK1.8 中,链表长度≥8 且数组容量≥64 时,转为红黑树提高查询效率;节点数≤6 时转回链表。 get 时根据 hash 定位,再遍历链表 / 红黑树找 equals 匹配的 key。默认容量 16,负载因子 0.75,达到阈值扩容为原来 2 倍。
两者都是键值对集合,核心区别在线程安全、效率、键值规则。 HashMap 线程不安全,HashTable 用 synchronized 修饰方法,线程安全但效率低。 HashMap 允许 key 和 value 为 null(只能一个 null key),HashTable 键值都不能为 null,否则抛空指针。 HashMap 初始容量 16,扩容 2 倍;HashTable 初始 11,扩容 2 倍 +1。 HashMap 继承 AbstractMap,HashTable 继承 Dictionary。 高并发不用 HashTable,用 ConcurrentHashMap,HashMap 适合单线程。
底层、有序性、key 要求是最大区别。 HashMap 底层数组 + 链表 + 红黑树,无序,key 只需重写 equals 和 hashCode,查询、插入效率 O(1)。 TreeMap 底层红黑树,key 有序(自然排序或自定义比较器),key 必须实现 Comparable 或传入 Comparator,否则报错。 效率上 HashMap 更高,TreeMap 适合需要按 key 排序遍历的场景。 HashMap 允许 null key,TreeMap 不允许,会抛空指针。日常用 HashMap,需要排序选 TreeMap。
Java 集合分两大体系:Collection 单值集合、Map 键值对集合。 Collection 下三大核心接口:
List:有序可重复,实现有 ArrayList、LinkedList、Vector;
Set:无序不重复,实现有 HashSet、LinkedHashSet、TreeSet;
Queue:队列,实现有 LinkedList、ArrayDeque。 Map 存储键值对,核心实现:HashMap、TreeMap、HashTable、ConcurrentHashMap。 顶层接口是 Collection 和 Map,ArrayList、HashMap 是开发最常用实现。
同步集合和并发集合都是线程安全的,但实现方式、并发性能天差地别。 同步集合:如 Vector、HashTable、Collections 工具类包装的集合,用 synchronized 独占锁,所有操作竞争同一把锁,并发度极低,多线程易阻塞,性能差。 并发集合:如 ConcurrentHashMap、CopyOnWriteArrayList,用 CAS+ 分段锁 / 锁细化,只锁部分数据,读操作大多无锁,高并发下性能极高。 简单说:低并发可用同步集合,高并发必须用并发集合。
CopyOnWrite 是写时复制的并发容器,典型如 CopyOnWriteArrayList、CopyOnWriteSet。 原理:读操作不加锁,写操作(增删改)加锁,复制一个新数组完成修改,再把引用指向新数组;读操作继续读旧数组,写完后再读新的。 优点:读无锁、高性能,读写分离;缺点:写会占内存,数据有短暂不一致。 适用场景:读多写少,比如白名单、配置列表、缓存,不适合频繁写入。
这两个方法是为集合(尤其是 HashMap、HashSet)服务的,有严格规范。 规范:
两个对象 equals 相等,hashCode 一定相等;
两个对象 hashCode 相等,equals 不一定相等(哈希冲突);
重写 equals 必须重写 hashCode,否则集合无法正常判断重复。 如果只重写 equals 不重写 hashCode,相同内容的对象会算出不同 hash,导致 HashMap 存重复 key、HashSet 存重复元素。 Object 类默认 equals 比地址,hashCode 用内存地址计算。