【达内课程】集合之 TreeMap

TreeMap

介绍
我们已经知道,HashMap 是一种以空间换时间的映射表,它的实现原理决定了内部的 Key 是无序的,即遍历 HashMap 的 Key 时,其顺序是不可预测的(但每个 Key都会遍历一次且仅遍历一次)。

还有一种 Map,它在内部会对 Key 进行排序,这种 Map 就是 SortedMap。注意到 SortedMap 是接口,它的实现类是 TreeMap。

SortedMap 保证遍历时以 Key 的顺序来进行排序。例如,放入的 Key 是 “apple”、“pear”、“orange”,遍历的顺序一定是 “apple”、“orange”、“pear”,因为 String 默认按字母排序。

创建对象

TreeMap map = new TreeMap();
TreeMap map = new TreeMap(比较器);

Treemap 特有方法

Treemap 的方法是在 Hashmap 的基础上进行补充的,下面介绍的是 Treemap 里的特有方法,至于基础方法和 Hashmap 一模一样,只是没有 compute 和 merge。

1、查找限定范围内的一个 key 或 entry,之所以会有这些方法,是因为 Treemap 是有序的。

firstKey() //查找map中最小的键(第一个)
lastKey() 查找map中最大的键(最后一个)
higherKey() //大于这个 key 的键
lowerKey() //大于这个 key 的键
ceilingKey(k key) //大于等于这个key的元素
floorKey(K key) //返回小于等于给定键的最大键;如果不存在这样的键,则返回null

firstEntry() //返回集合中的第一个元素
lastEntry() //返回集合中的最后的元素
higherEntry() //返回一个最小键大于key的条目;如果没有这样的键,则返回null
lowerEntry() //返回最大键小于键的条目;如果没有这样的键,则返回null
ceilingEntry() //返回与该键至少大于或等于给定键,如果不存在这样的键的键 - 值映射,则返回null相关联
lowerEntry() //返回该 map 中小于指定 key 的最大的 key-value 键值对,若 map 为空,则返回为null

2、删除最大或最小键值对
pollFirstEntry() //返回并删除与此TreeMap中存在的最低键元素值链接的条目(键值对)
pollLastEntry() //回然后删除与此TreeMap中存在的最大键元素值链接的条目(键值对)

3、获取 Treemap 的子集或 变种
descendingKeySet //返回集合的全部Key,并且是逆序的
descendingMap //把集合逆序返回
subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) //截取集合中 Key 从 fromKey 到 toKey 的元素,否是截取他们本身,取决于 true 或者 false
subMap(K fromKey, K toKey) //截取集合中 Key 从 fromKey 到 toKey 的元素,包括 fromKey,不包括 toKey
tailMap(K fromKey, boolean inclusive) // 当 inclusive 为 true 时,截取 Key 大于等于 fromKey 的所有元素,否则截取 Key 大于 fromKey 的所有元素
tailMap(K fromKey) //截取 Key 大于等于 fromKey 的所有元素
headMap(K toKey, boolean inclusive) //当 inclusive 为 true 时,就是返回 Key 小于等于 toKey 的所有元素
headMap(K toKey) //返回 Key 小于 toKey 的所有元素

举例:TreeMap使用练习

TreeMap<Integer, String> map = new TreeMap<>();
map.put(999, "9");
map.put(555, "5");
map.put(777, "7");
map.put(222, "2");
map.put(666, "6");
map.put(888, "8");
map.put(333, "3");
map.put(111, "1");
System.out.println(map.size());//8
System.out.println(map);//{111=1, 222=2, 333=3, 555=5, 666=6, 777=7, 888=8, 999=9}
System.out.println(map.get(666));//6
System.out.println(map.containsKey(999));//true
System.out.println(map.containsValue("9"));//true
System.out.println(map.remove(222));//2
System.out.println(map);//{111=1, 333=3, 555=5, 666=6, 777=7, 888=8, 999=9}

从打印结果可以看出是 TreeMap 是按照键值从小到大的顺序排列的。

举例:用销售额模型练习使用 Comparable

我们用横坐标代表月份,纵坐标代表销售额:
(1,35)表示1月份销售3.5亿
(2,42)表示2月份销售4.2亿

然后将这两个坐标点放入一个 TreeMap 中,键放入坐标点,值放入表示的销售额字符串。新建 Point 类来表示坐标点。

Point 类

class Point {
    private int x;
    private int y;

    public Point() {
        super();
    }

    public Point(int x, int y) {
        super();
        this.x = x;
        this.y = y;
    }

    public int getX() {
        return x;
    }

    public void setX(int x) {
        this.x = x;
    }

    public int getY() {
        return y;
    }

    public void setY(int y) {
        this.y = y;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) return false;
        if (obj == this) return true;
        if (!(obj instanceof Point)) return false;
        Point p = (Point) obj;
        return
                this.x == p.x && this.y == p.y;
    }

    @Override
    public String toString() {
        return "(" + x + "," + y + ")";
    }
}

Main.java

public class Main {
    public static void main(String[] args) {
        //分别代表月份和销售额
        //1月销售3.5亿/横坐标1,纵坐标35
        //2月销售4.2亿/横坐标2,纵坐标42
        Point a = new Point(1, 35);
        Point b = new Point(2, 42);

        //(键是坐标,值是字符串)
        //(Point,String)
        TreeMap<Point, String> map = new TreeMap<>();
        map.put(a, "3.5亿");
        map.put(b, "4.2亿");

        System.out.println(map);
    }
}

运行结果

Exception in thread “main” java.lang.ClassCastException: class com.company.Point cannot be cast to class java.lang.Comparable (com.company.Point is in unnamed module of loader ‘app’; java.lang.Comparable is in module java.base of loader ‘bootstrap’)
at java.base/java.util.TreeMap.compare(TreeMap.java:1291)
at java.base/java.util.TreeMap.put(TreeMap.java:536)
at com.company.Main.main(Main.java:16)

这样写会出错的原因是,放入 TreeMap 的键是要比较大小的,小的放在左边,大的放在右边。

使用 TreeMap 时,放入的 Key 必须实现 Comparable 接口。String、Integer 这些类已经实现了 Comparable 接口,因此可以直接作为 Key 使用。作为 Value 的对象则没有任何要求。

知道了这点,所以 Point 必须实现 Comparable 接口,因此修改Point类

class Point implements Comparable<Point> {
    ......(同上)

    /*
     * 当前对象与参数对象o比较大小
     * 当前对象大,正数
     * 当前对象小,负数
     * 相同,0
     */
    @Override
    public int compareTo(Point o) {
        //这里按照月份比较大小,
        return x - o.x;
    }
}

运行结果

{(1,35)=3.5亿, (2,42)=4.2亿}

比较器

比较大小有两种方式
1、实现 Comparable 接口
2、外接 Comparator 比较器(优先执行)

如果作为 Key 的 class 没有实现 Comparable 接口,那么,必须在创建 TreeMap 时同时指定一个自定义排序算法:

public class Main {
    public static void main(String[] args) {
        Point a = new Point(1, 35);
        Point b = new Point(2, 42);

        //接口是不能创建实例的,但可以创建匿名类的实例
        Comparator<Point> comparator = new Comparator<Point>() {
            /*
             * o1 o2比较大小
             * o1大 正数
             * o1小 负数
             * 相同 0
             */
            @Override
            public int compare(Point o1, Point o2) {
                //TreeMap的排序规则是从小到大
                //但是我们的要求是按照销售额从大到小排列
                //所以反过来相减
                return o2.getY() - o1.getY();
            }
        };

        //(键是坐标,值是字符串)
        //(Point,String)
        TreeMap<Point, String> map = new TreeMap<>(comparator);
        map.put(a, "3.5亿");
        map.put(b, "4.2亿");

        System.out.println(map);
    }
}

之前结果

{(1,35)=3.5亿, (2,42)=4.2亿}

之后结果

{(2,42)=4.2亿, (1,35)=3.5亿}

注意到Comparator接口要求实现一个比较方法,它负责比较传入的两个元素 a 和 b,如果 a<b,则返回负数,通常是 -1,如果 a==b,则返回 0,如果 a>b,则返回正数,通常是 1。TreeMap 内部根据比较结果对 Key 进行排序。

有比较器执行比较器,没有比较器执行 Comparable

其中 TreeMap 的比较器是我们自己程序实现的,然后被 TreeMap 内部调用,这种实现模式称为回调模式 Callback

参考
java中TreeMap常用方法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值