Map集合体系全景图(一)

157 阅读6分钟

Map全家桶

image.png

TreeMap特点

  1. TreeMap是一个基于红黑树实现的有序映射,可以保证元素按照键的自然顺序或者自定义顺序进行排序。
  2. TreeMap中的键和值都可以为,但是键不能重复,如果插入重复的键,则会覆盖原有的值。
  3. TreeMap支持高效的插入、删除和查找操作,时间复杂度为O(log n),支持范围查询。
  4. TreeMap提供了一些有用的方法,如firstKey()、lastKey()、lowerKey()、higherKey()等,可以方便地获取键的最小值、最大值、小于某个键的最大键、大于某个键的最小键等。
  5. TreeMap是线程不安全的,如果需要在多线程环境下使用,需要进行同步处理。
  6. TreeMap的迭代器是有序的,可以按照键的自然顺序或者自定义顺序进行遍历。

TreeMap和HashMap区别

reeMap和HashMap都是Java中的Map接口的实现类,但它们有以下区别:

  1. 数据结构不同:HashMap是基于哈希表实现的,而TreeMap是基于红黑树实现的。
  2. 排序方式不同:HashMap不保证元素的顺序,而TreeMap可以按照键的自然顺序或者自定义顺序进行排序。
  3. 性能不同:HashMap的插入、删除、查找操作的时间复杂度都是O(1),而TreeMap的时间复杂度是O(log n)。
  4. 空间占用不同:HashMap的空间占用比TreeMap小,因为HashMap不需要维护红黑树的结构。
  5. 线程安全性不同: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;  
}