java集合之TreeMap
- 基于红黑树(Red-Black tree)的 NavigableMap 实现。 映射根据其键的自然顺序进行排序,或者通过映射创建时提供的 Comparator 进行排序,具体取决于使用的构造函数。
- 此实现为 containsKey、get、put 和 remove 操作提供有保证的 log(n) 时间成本。 算法是对 Cormen、Leiserson 和 Rivest 的算法导论中的算法的改编。
- 注意,如果要正确实现
Map
接口,则有序映射所保持的顺序(无论是否明确提供了比较器)都必须与equals
一致。(请参阅 Comparable 或 Comparator 以获取与 equals 一致的精确定义。)之所以如此,是因为 Map 接口是根据 equals 操作定义的,但有序映射使用它的compareTo
(或compare
)方法对所有键进行比较,因此从有序映射的观点来看,此方法认为相等的两个键就是相等的。即使排序与 equals 不一致,有序映射的行为仍然是 定义良好的,只不过没有遵守Map
接口的常规协定。 - 请注意,此实现不是同步的。 如果多个线程同时访问一个映射,并且至少有一个线程在结构上修改了映射,则必须在外部进行同步。 (结构修改是添加或删除一个或多个映射的任何操作;仅更改与现有键关联的值不是结构修改。)这通常是通过同步一些自然封装映射的对象来完成的。 如果不存在这样的对象,则应使用 Collections.synchronizedSortedMap 方法“包装”地图。 这最好在创建时完成,以防止对地图的意外不同步访问:
SortedMap m = Collections.synchronizedSortedMap(new TreeMap(...));
- collection(由此类所有的“collection 视图方法”返回)的
iterator
方法返回的迭代器都是快速失败 的:如果在迭代器创建后的任何时间以任何方式修改映射结构,除了通过迭代器自己的remove
方法,迭代器将抛出 ConcurrentModificationException。 因此,面对并发修改,迭代器很快就完全失败,而不会冒着在将来不确定的时间发生不确定行为的风险。
- 请注意,无法保证迭代器的快速失败行为,因为一般来说,在存在非同步并发修改的情况下不可能做出任何硬保证。 快速失败的迭代器会尽最大努力抛出 ConcurrentModificationException。 因此,编写一个依赖此异常来确保其正确性的程序是错误的:迭代器的快速失败行为应该仅用于检测错误。
- 此类中的方法及其视图返回的所有 Map.Entry 对都表示映射生成时的快照。 它们不支持 Entry.setValue 方法。 (但请注意,可以使用 put 更改关联映射中的映射。)
构造函数
- TreeMap()
- 使用其键的自然顺序构造一个新的空树映射。
- TreeMap (Comparator<? super K> Comparator)
- 构造一个新的空树图,根据给定的比较器排序。
- TreeMap (Map<? extends K, ? extends V> m)
- 构造一个包含与给定映射相同的映射的新树映射,根据其键的自然顺序进行排序。
- TreeMap (SortedMap<K, ? extends V> m)
- 构造一个新的树图,其中包含与指定的排序图相同的映射并使用相同的顺序。
方法
Modifier and Type | Method | Description |
Map.Entry<K, V> | ceilingEntry(K key) | 返回与大于或等于给定键的最小键关联的键值映射,如果没有这样的键,则返回 null。 |
K | ceilingKey(K key) | 返回大于或等于给定键的最小键,如果没有这样的键,则返回 null。 |
void | clear() | 从此映射中删除所有映射。 |
Object | clone() | 返回此 TreeMap 实例的浅表副本。 |
Comparator<? super K> | comparator() | 返回用于对此映射中的键进行排序的比较器,如果此映射使用其键的自然顺序,则返回 null。 |
boolean | containsKey (Object key) | 如果此映射包含指定键的映射,则返回 true。 |
boolean | containsValue (Object value) | 如果此映射将一个或多个键映射到指定值,则返回 true。 |
NavigableSet | descendingKeySet() | 返回此映射中包含的键的逆序 NavigableSet 视图。 |
NavigableMap<K, V> | descendingMap() | 返回此映射中所包含映射关系的逆序视图。 |
Set<Map.Entry<K, V>> | entrySet() | 返回此映射中包含的映射的 Set 视图。 |
Map.Entry<K, V> | firstEntry() | 返回与此映射中最小键关联的键值映射,如果映射为空,则返回 null。 |
K | firstKey() | 返回当前在此映射中的第一个(最低)键。 |
Map.Entry<K, V> | floorEntry(K key) | 返回与小于或等于给定键的最大键关联的键值映射,如果没有这样的键,则返回 null。 |
K | floorKey(K key) | 返回小于或等于给定键的最大键,如果没有这样的键,则返回 null。 |
V | get(Object key) | 返回指定键映射到的值,如果此映射不包含该键的映射,则返回 null。 |
SortedMap<K, V> | headMap(K toKey) | 返回键严格小于 toKey 的映射部分的视图。 |
NavigableMap<K, V> | headMap(K toKey, boolean inclusive) | 返回此映射的部分视图,其键小于(或等于,如果 inclusive 为 true)toKey。 |
Map.Entry<K, V> | highEntry(K key) | 返回与严格大于给定键的最小键关联的键值映射,如果没有这样的键,则返回 null。 |
K | highKey(K key) | 返回严格大于给定键的最小键,如果没有这样的键,则返回 null。 |
Set | keySet() | 返回此映射中包含的键的 Set 视图。 |
Map.Entry<K, V> | lastEntry() | 返回与此映射中最大键关联的键值映射,如果映射为空,则返回 null。 |
K | lastKey() | 返回当前在此映射中的最后一个(最高)键。 |
Map.Entry<K, V> | lowerEntry(K key) | 返回严格小于给定键的最大键关联的键值映射,如果没有这样的键,则返回 null。 |
K | lowerKey(K key) | 返回严格小于给定键的最大键,如果没有这样的键,则返回 null。 |
NavigableSet | navigableKeySet() | 返回此映射中包含的键的 NavigableSet 视图。 |
Map.Entry<K, V> | pollFirstEntry() | 移除并返回与此映射中最小键关联的键值映射,如果映射为空,则返回 null。 |
Map.Entry<K, V> | pollLastEntry() | 移除并返回与此映射中最大键关联的键值映射,如果映射为空,则返回 null。 |
V | put(K key, V value) | 将指定值与此映射中的指定键关联。 |
void | putAll(Map<? extends K, ? extends V> map) | 将所有映射从指定映射复制到此映射。 |
V | remove(Object key) | 从此 TreeMap 中删除此键的映射(如果存在)。 |
int | size() | 返回此映射中键值映射的数量。 |
SortedMap<K, V> | subMap(K fromKey, K toKey) | 返回此映射部分的视图,其键范围从 fromKey(含)到 toKey(不包括在内)。 |
SortedMap<K, V> | tailMap(K fromKey) | 返回此映射中键大于或等于 fromKey 的部分的视图。 |
NavigableMap<K, V> | tailMap(K fromKey, boolean inclusive) | 返回此映射部分的视图,其键大于(或等于,如果 inclusive 为true)fromKey。 |
Collection | values() | 返回此映射中包含的值的集合视图。 |
- V compute (K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction)
- 尝试计算指定键及其当前映射值的映射(如果没有当前映射,则为 null)。
- V computeIfAbsent (K key, Function<? super K, ? extends V> mappingFunction)
- 如果指定的键尚未与值关联(或映射为 null),则尝试使用给定的映射函数计算其值,并且 除非为空,否则将其输入此地图。
- V computeIfPresent (K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction)
- 如果指定键的值存在且非空,则尝试计算给定键及其的新映射 当前映射值。
- V merge (K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction)
- 如果指定的键尚未与值关联或与空值关联,则将其与给定的值关联 非空值。
- NavigableMap<K, V> subMap (K fromKey, boolean fromInclusive, K toKey, boolean toInclusive)
- fromKey - 返回映射中键的低端点;fromInclusive - 如果低端点要包含在返回的视图中,则为 true;toKey - 返回映射中键的高端点;toInclusive - 如果高端点要包含在返回的视图中,则为 true
- 返回此映射的部分视图,其键的范围从 fromKey 到 toKey。如果 fromKey 和 toKey 相等,则返回的映射为空,除非 fromExclusive 和 toExclusive 都为 true。返回的映射受此映射支持,因此返回映射中的更改将反映在此映射中,反之亦然。返回的映射支持此映射支持的所有可选映射操作。
- 如果试图在返回映射的范围之外插入一个键,或者构造一个任一端点位于其范围之外的子映射,则返回的映射将抛出 IllegalArgumentException。
实例
TreeMap<String, Integer> map = new TreeMap<>();
map.put("fyz", 11);
map.put("yjk", 22);
map.put("fyz", 16);
map.put("xhr", 33);
map.put("zsh", 44);
System.out.println(map.size()); //4
System.out.println(map); //{fyz=16, xhr=33, yjk=22, zsh=44}
System.out.println(map.entrySet()); //{fyz=16, xhr=33, yjk=22, zsh=44}
System.out.println(map.get("fyz")); //16
System.out.println(map.keySet()); //[fyz, xhr, yjk, zsh]
System.out.println(map.values()); //[16, 33, 22, 44]
System.out.println(map.firstEntry()); //fyz=16
System.out.println(map.firstKey()); //fyz
System.out.println(map.lastEntry()); //zsh=44
System.out.println(map.floorEntry("zsh")); //zsh=44
System.out.println(map.lowerEntry("zsh")); //yjk=22
System.out.println(map.containsKey("fyz")); //true
System.out.println(map.containsValue(5)); //false
System.out.println("判断是否为空:"+map.isEmpty()); //判断是否为空:false
大致原理
相关源码
public class TreeMap<K,V>{
//重要属性:
private final Comparator<? super K> comparator;//外部比较器:
private transient Entry<K,V> root = null;//树的根节点:
private transient int size = 0;//集合中元素的数量:
public TreeMap() { //空构造器:
comparator = null;//如果使用空构造器,那么底层就不使用外部比较器
}
//有参构造器:
public TreeMap(Comparator<? super K> comparator) {
this.comparator = comparator;//如果使用有参构造器,那么就相当于指定了外部比较器
}
public V put(K key, V value) {//k,V的类型在创建对象的时候确定了
//如果放入的是第一对元素,那么t的值为null
Entry<K,V> t = root;//在放入第二个节点的时候,root已经是根节点了
//如果放入的是第一个元素的话,走入这个if中:
if (t == null) {//自己跟自己比
compare(key, key); // type (and possibly null) check
root = new Entry<>(key, value, null);//根节点确定为root
size = 1;//size值变为1
modCount++;
return null;
}
int cmp;
Entry<K,V> parent;
// split comparator and comparable paths
//将外部比较器赋给cpr:
Comparator<? super K> cpr = comparator;
//cpr不等于null,意味着你刚才创建对象的时候调用了有参构造器,指定了外部比较器
if (cpr != null) {
do {
parent = t;
cmp = cpr.compare(key, t.key);//将元素的key值做比较
//cmp返回的值就是int类型的数据:
//要是这个值《0 =0 》0
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else//cpm==0
//如果key的值一样,那么新的value替换老的value 但是key不变 因为key是唯一的
return t.setValue(value);
} while (t != null);
} else {//cpr等于null,意味着你刚才创建对象的时候调用了空构造器,没有指定外部比较器,使用内部比较器
if (key == null)
throw new NullPointerException();
Comparable<? super K> k = (Comparable<? super K>) key;
do {
parent = t;
cmp = k.compareTo(t.key);//将元素的key值做比较
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else
return t.setValue(value);
} while (t != null);
}
Entry<K,V> e = new Entry<>(key, value, parent);
if (cmp < 0)
parent.left = e;
else
parent.right = e;
fixAfterInsertion(e);
size++;//size加1 操作
modCount++;
return null;
}
}
static final class Entry<K,V> implements Map.Entry<K,V> {
K key;
V value;
Entry<K,V> left = null;
Entry<K,V> right = null;
Entry<K,V> parent;
boolean color = BLACK;
}
自定义类
内部比较器
public class Student implements Comparable<Student> {
private int age;
private String name;
private double height;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getHeight() {
return height;
}
public void setHeight(double height) {
this.height = height;
}
public Student(int age, String name, double height) {
this.age = age;
this.name = name;
this.height = height;
}
@Override
public String toString() {
return "\nStudent{" +
"age=" + age +
", name='" + name + '\'' +
", height=" + height +
'}';
}
@Override
public int compareTo(Student o) {
/* return this.getAge()-o.getAge();*/
return this.getName().compareTo(o.getName());
}
}
class StCpInT{
public static void main(String[] args) {
Map<Student, Integer> map = new TreeMap<>();
map.put(new Student(19, "fyz", 178.8), 11);
map.put(new Student(18, "yjk", 178.4), 22);
map.put(new Student(19, "fyz", 178.8), 16);
map.put(new Student(17, "xhr", 178.6), 33);
map.put(new Student(10, "zsh", 178.5), 44);
System.out.println(map);
/*{
Student{age=19, name='fyz', height=178.8}=16,
Student{age=17, name='xhr', height=178.6}=33,
Student{age=18, name='yjk', height=178.4}=22,
Student{age=10, name='zsh', height=178.5}=44}*/
System.out.println(map.size()); //4
}
}
外部比较器
public class SCpOT {
public static void main(String[] args) {
Map<Student, Integer> map = new TreeMap<>(new Comparator<Student>() {//匿名类
@Override
public int compare(Student o1, Student o2) {
return ((Double) (o1.getHeight())).compareTo((Double) (o2.getHeight()));
}
});
map.put(new Student(19, "fyz", 178.8), 11);
map.put(new Student(18, "yjk", 178.8), 22);
map.put(new Student(19, "fyz", 178.8), 16);
map.put(new Student(10, "zsh", 178.5), 44);
System.out.println(map);
/*{
Student{age=10, name='zsh', height=178.5}=44,
Student{age=19, name='fyz', height=178.8}=16}*/
System.out.println(map.size()); //2
}
}