Map全家桶
TreeMap特点
- TreeMap是一个基于红黑树实现的有序映射,可以保证元素按照键的自然顺序或者自定义顺序进行排序。
- TreeMap中的键和值都可以为,但是键不能重复,如果插入重复的键,则会覆盖原有的值。
- TreeMap支持高效的插入、删除和查找操作,时间复杂度为O(log n),支持范围查询。
- TreeMap提供了一些有用的方法,如firstKey()、lastKey()、lowerKey()、higherKey()等,可以方便地获取键的最小值、最大值、小于某个键的最大键、大于某个键的最小键等。
- TreeMap是线程不安全的,如果需要在多线程环境下使用,需要进行同步处理。
- TreeMap的迭代器是有序的,可以按照键的自然顺序或者自定义顺序进行遍历。
TreeMap和HashMap区别
reeMap和HashMap都是Java中的Map接口的实现类,但它们有以下区别:
- 数据结构不同:HashMap是基于哈希表实现的,而TreeMap是基于红黑树实现的。
- 排序方式不同:HashMap不保证元素的顺序,而TreeMap可以按照键的自然顺序或者自定义顺序进行排序。
- 性能不同:HashMap的插入、删除、查找操作的时间复杂度都是O(1),而TreeMap的时间复杂度是O(log n)。
- 空间占用不同:HashMap的空间占用比TreeMap小,因为HashMap不需要维护红黑树的结构。
- 线程安全性不同:HashMap是非线程安全的,而TreeMap可以通过Collections.synchronizedMap方法来实现线程安全。
综上所述,如果需要快速的插入、删除、查找操作,并且不需要排序,可以使用HashMap;如果需要排序或者需要按照自定义顺序进行操作,可以使用TreeMap。
TreeMap和HashMap使用案例
假设我们要统计一篇文章中每个单词出现的次数,并按照出现次数从大到小排序输出。我们可以使用 TreeMap 来实现。
import java.util.*;
public class WordCount {
public static void main(String[] args) {
String text = "This is a sample text with several words. " +
"We will count the frequency of each word " +
"and sort them in descending order.";
// 将文本转换为单词列表
List<String> words = Arrays.asList(text.split("\W+"));
// 统计每个单词出现的次数
Map<String, Integer> wordCount = new TreeMap<>();
for (String word : words) {
wordCount.put(word, wordCount.getOrDefault(word, 0) + 1);
}
// 按照出现次数从大到小排序
List<Map.Entry<String, Integer>> sortedList = new ArrayList<>(wordCount.entrySet());
sortedList.sort(Map.Entry.<String, Integer>comparingByValue().reversed());
// 输出结果
for (Map.Entry<String, Integer> entry : sortedList) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
}
}
TreeMap源码分析
//它继承了AbstractMap类,并实现了NavigableMap、Cloneable和Serializable接口。
//其中,K和V分别表示TreeMap中键和值的类型
//NavigableMap是Java中的一个接口,它继承了SortedMap接口,提供了一些方法
//如获取最接近给定键的键、获取大于或小于给定键的键等
public class TreeMap<K,V>
extends AbstractMap<K,V>
implements NavigableMap<K,V>, Cloneable, java.io.Serializable
//这个变量是用来比较键值的,如果没有指定比较器,那么就使用键值的自然顺序
private final Comparator<? super K> comparator;
//这个变量是红黑树的根节点,`Entry`是红黑树节点的内部类
private transient Entry<K,V> root;
//这个变量是TreeMap中键值对的数量
private transient int size = 0;
//这个变量是`TreeMap`被修改的次数,用于迭代器检测是否并发修改
private transient int modCount = 0;
//这是一个无参构造函数,用于创建一个空的TreeMap对象。在构造函数中,将comparator设置为null,表示使用默认的自然排序方式。
public TreeMap() {
comparator = null;
}
//这是一个构造函数,用于创建一个新的TreeMap实例。它有一个参数comparator,它是一个比较器,用于比较Map中的键。如果comparator为空,则使用键的自然顺序进行排序。如果comparator不为空,则使用它来进行排序。这个构造函数的作用是初始化TreeMap的比较器。
public TreeMap(Comparator<? super K> comparator) {
this.comparator = comparator;
}
//构造函数创建了一个新的TreeMap对象,并将其初始化为一个空的映射。
//它使用默认的比较器(自然排序)来对键进行排序。然后,它将指定的map中的所有键值对添加到新的TreeMap对象中
public TreeMap(Map<? extends K, ? extends V> m) {
comparator = null;
putAll(m);
}
//是将指定的Map中的map关系复制到此新Map中
public void putAll(Map<? extends K, ? extends V> map) {
int mapSize = map.size();
//如果当前 TreeMap 的大小为 0,且传入的 map 的大小不为 0
//且传入的 map 是一个 SortedMap 类型的对象。
//SortedMap 是一个有序的 Map 集合,它按照键的自然顺序或者指定的比较器进行排序。
if (size==0 && mapSize!=0 && map instanceof SortedMap) {
//获取传入的 SortedMap 对象的比较器,将其赋值给一个名为 c 的变量。
//<?> 表示这里使用了通配符,表示这个比较器可以接受任何类型的参数
Comparator<?> c = ((SortedMap<?,?>)map).comparator();
//这个条件语句检查 map 中的比较器是否与当前 TreeMap 中的比较器相同。
//如果相同,那么就可以利用 map中的元素来构建一个新的 TreeMap。
//如果不同,那么就调用 super.putAll(map)方法,将 map 中的元素插入到当前 TreeMap 中
if (c == comparator || (c != null && c.equals(comparator))) {
++modCount;
try {
//根据传入的迭代器和比较器构建一个新的 TreeMap。
//这个方法会从迭代器中获取键值对,然后按照比较器的顺序将它们插入到 TreeMap 中。
buildFromSorted(mapSize, map.entrySet().iterator(),
null, null);
} catch (java.io.IOException cannotHappen) {
} catch (ClassNotFoundException cannotHappen) {
}
return;
}
}
super.putAll(map);
}
将指定的 Map 中的所有map关系复制到当前 TreeMap 中。
//它遍历了 m 中的每个映射关系,然后调用 put 方法将其添加到当前TreeMap中。
其中,e.getKey() 返回当前map的键,e.getValue()返回值
public void putAll(Map<? extends K, ? extends V> m) {
for (Map.Entry<? extends K, ? extends V> e : m.entrySet())
put(e.getKey(), e.getValue());
}
//用于从已排序的数据中构建一棵红黑树
private void buildFromSorted(int size, Iterator<?> it,
java.io.ObjectInputStream str,
V defaultVal)
throws java.io.IOException, ClassNotFoundException {
this.size = size;
//用于从已排序的数据中构建一棵红黑树。它接受四个参数:size 表示数据的大小,it 表示数据的迭代器
//str 表示一个对象输入流,defaultVal 表示默认值,将返回的根节点赋值给 TreeMap 对象的 root 属性
root = buildFromSorted(0, 0, size-1, computeRedLevel(size),
it, str, defaultVal);
}
}
//它采用了二分查找的策略,将集合中的元素递归地分成左右两个子树,然后将中间元素作为根节点,构建一棵红黑树。在构建过程中,如果当前节点是红色的,那么它的子节点必须是黑色的。如果当前节点是黑色的,那么它的子节点可以是红色的,也可以是黑色的。这样可以保证红黑树的平衡性和性能
private final Entry<K,V> buildFromSorted(int level, int lo, int hi,
int redLevel,
Iterator<?> it,
java.io.ObjectInputStream str,
V defaultVal)
throws java.io.IOException, ClassNotFoundException {
//hi 和 lo 是要查找的区间的上下界
if (hi < lo) return null;
// 是区间的中间位置,通过将上下界相加再除以2得到;
int mid = (lo + hi) >>> 1;
Entry<K,V> left = null;
if (lo < mid)
left = buildFromSorted(level+1, lo, mid - 1, redLevel,
it, str, defaultVal);
// extract key and/or value from iterator or stream
K key;
V value;
if (it != null) {
if (defaultVal==null) {
Map.Entry<?,?> entry = (Map.Entry<?,?>)it.next();
key = (K)entry.getKey();
value = (V)entry.getValue();
} else {
key = (K)it.next();
value = defaultVal;
}
} else { // use stream
key = (K) str.readObject();
value = (defaultVal != null ? defaultVal : (V) str.readObject());
}
Entry<K,V> middle = new Entry<>(key, value, null);
// color nodes in non-full bottommost level red
if (level == redLevel)
middle.color = RED;
if (left != null) {
middle.left = left;
left.parent = middle;
}
if (mid < hi) {
Entry<K,V> right = buildFromSorted(level+1, mid+1, hi, redLevel,
it, str, defaultVal);
middle.right = right;
right.parent = middle;
}
return middle;
}