在 Java 集合框架中,HashMap
和 TreeMap
是两种常用的 Map 实现,它们有各自的特点和使用场景。理解它们的区别对于高效使用这两种数据结构至关重要。本篇博客将通过源码解读的方式详细解析 HashMap
和 TreeMap
的区别,并结合实际案例说明它们的应用场景。
类继承关系与接口实现
首先,我们来看一下 HashMap
和 TreeMap
的类继承关系:
java
public class HashMap<K,V> extends AbstractMap<K,V>
implements Map<K,V>, Cloneable, Serializable
public class TreeMap<K,V> extends AbstractMap<K,V>
implements NavigableMap<K,V>, Cloneable, Serializable
从类继承关系可以看出,HashMap
和 TreeMap
都继承自 AbstractMap
,但 TreeMap
还实现了 NavigableMap
和 SortedMap
接口。
NavigableMap 接口
实现 NavigableMap
接口让 TreeMap
具有对集合内元素进行搜索的能力。NavigableMap
提供了丰富的方法来操作键值对:
-
定向搜索:
java
Entry<K,V> ceilingEntry(K key); Entry<K,V> floorEntry(K key); Entry<K,V> higherEntry(K key); Entry<K,V> lowerEntry(K key);
这些方法用于定位大于、小于、大于等于、小于等于给定键的最接近的键值对。
-
子集操作:
java
NavigableMap<K,V> subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive); NavigableMap<K,V> headMap(K toKey, boolean inclusive); NavigableMap<K,V> tailMap(K fromKey, boolean inclusive);
这些方法可以高效地创建原集合的子集视图,而无需复制整个集合。
-
逆序视图:
java
NavigableMap<K,V> descendingMap();
返回一个逆序的
NavigableMap
视图,可以反向迭代整个TreeMap
。 -
边界操作:
java
Entry<K,V> firstEntry(); Entry<K,V> lastEntry(); Entry<K,V> pollFirstEntry(); Entry<K,V> pollLastEntry();
这些方法方便地访问和移除元素。
SortedMap 接口
实现 SortedMap
接口让 TreeMap
具有对集合中的元素根据键排序的能力。默认情况下,TreeMap
按照键的自然顺序(升序)排序,但也可以指定排序的比较器。
java
TreeMap<Person, String> treeMap = new TreeMap<>(new Comparator<Person>() {
@Override
public int compare(Person person1, Person person2) {
int num = person1.getAge() - person2.getAge();
return Integer.compare(num, 0);
}
});
实例代码
java
public class Person {
private Integer age;
public Person(Integer age) {
this.age = age;
}
public Integer getAge() {
return age;
}
public static void main(String[] args) {
TreeMap<Person, String> treeMap = new TreeMap<>(new Comparator<Person>() {
@Override
public int compare(Person person1, Person person2) {
int num = person1.getAge() - person2.getAge();
return Integer.compare(num, 0);
}
});
treeMap.put(new Person(3), "person1");
treeMap.put(new Person(18), "person2");
treeMap.put(new Person(35), "person3");
treeMap.put(new Person(16), "person4");
treeMap.entrySet().stream().forEach(entry -> {
System.out.println(entry.getValue());
});
}
}
输出结果:
txt
person1
person4
person2
person3
可以看出,TreeMap
中的元素已经按照 Person
的 age
字段的升序排列了。
Lambda 表达式实现
上面的 Comparator 可以用 Lambda 表达式简化:
java
TreeMap<Person, String> treeMap = new TreeMap<>((person1, person2) -> {
int num = person1.getAge() - person2.getAge();
return Integer.compare(num, 0);
});
HashMap vs TreeMap
1. 数据结构:
HashMap
使用哈希表实现,基于散列函数。TreeMap
使用红黑树实现,保证键的有序性。
2. 性能:
HashMap
的插入、删除和查找操作平均时间复杂度为 O(1)。TreeMap
的插入、删除和查找操作时间复杂度为 O(log n),因为红黑树需要保持平衡。
3. 有序性:
HashMap
不保证键的顺序。TreeMap
保证键的自然顺序,或者通过指定的比较器进行排序。
4. Null 键和 Null 值:
HashMap
允许一个 Null 键和多个 Null 值。TreeMap
不允许 Null 键,但允许 Null 值。
5. 应用场景:
- 使用
HashMap
在需要快速访问键值对且不关心顺序时。 - 使用
TreeMap
在需要按键排序或者需要范围搜索操作时。
总结
HashMap
和 TreeMap
各有优劣,选择它们取决于具体的应用场景和需求。通过理解它们的实现细节和性能特点,我们可以更有效地利用这两种数据结构来解决实际问题。