java集合之TreeMap 构造器 方法 比较器

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
    }
}

猜你喜欢

转载自blog.csdn.net/m0_46530662/article/details/119792335