Java基础知识之TreeMap

一、TreeMap的介绍

TreeMap是NavigableMap接口的直接实现类,其底层数据结构是红黑树结构(而HashMap则是Map接口的直接实现类),不过因为NavigableMap接口也间接继承了Map接口,因此Map拥有的特点TreeMap也同样拥有。并且TreeMap基本没有自身特点,我们可以直接认为Map的特点就是TreeMap的特点。

请添加图片描述

Map是用于保存具有映射关系的数据集合,它具有双列存储的特点,即一次必须添加两个元素,这样的两个元素被称为键值对==><Key,Value>。其中Key的值不可重复(当Key的值重复的时候,后面插入的对象会将之前插入的具有相同的Key值的对象覆盖掉,即更新Value的值);而对于不同的Key值,其Value的值可重复。同样TreeMap的特点也是如此。

其中<Key,Value>键值对,在Java语言中又被称之为Entry或者entry。Map.Entry就相当于Student.name。若name的数据类型为String,则Student.name的数据类型为String;同理若<key,value>中key的数据类型为Integer,value的数据类型为String,则Map.Entry的数据类型为<Integer,String>。在HashMap与TreeMap中均可使用Map.Entry来获取键值对的数据类型。

其中在HashMap的方法中可以直接使用HashMap.Entry,但在TreeMap中不可以使用TreeMap.Entry,因为TreeMap.Entry它是私有的,我们无法通过创建TreeMap对象来直接使用TreeMap.Entry,因此TreeMap只能使用Map.Entry。

在这里插入图片描述

接下来我们将结合代码来具体学习TreeMap的相关知识。


1.1 TreeMap底层原理(红黑树)

Java基础之哈希表与红黑树


二、TreeMap的基本操作 TreeMap入门

2.1、Value值可重复

首先我们来讨论对于不同的Key值,Value的值可重复这一问题
Key数据类型为Integer类型,Value数据类型为String类型的TreeMap

public static void main(String[] args) {
        //创建一个Key为Integer数据类型,Value为String数据类型的TreeMap对象
        //若要使用TreeMap,则需要导入import java.util.TreeMap;
        TreeMap<Integer,String> treeMap=new TreeMap<>();

        //使用put()方法添加Entry元素
        treeMap.put(18,"xiaoming");//第一对象
        treeMap.put(19,"xiaoming");//第二对象
        treeMap.put(20,"wangwu");//第三对象

        //普通打印
        System.out.println(treeMap);
    }

运行结果:从运行结果中,我们发现对于不同的Key值,Value值可以重复
在这里插入图片描述


2.2、Key值不可重复

接下来我们探讨Key的值不可重复(当Key的值重复的时候,后面插入的对象会将之前插入的具有相同的Key值的对象覆盖掉,即更新Value的值)此问题

Key数据类型为Integer类型,Value数据类型为String类型的TreeMap

其中第三对象与第四对象的Key值相同,Value值不同。

public static void main(String[] args) {
        //创建一个Key为Integer数据类型,Value为String数据类型的TreeMap对象
        //若要使用TreeMap,则需要导入import java.util.TreeMap;
        TreeMap<Integer,String> treeMap=new TreeMap<>();

        //使用put()方法添加元素
        treeMap.put(18,"xiaoming");//第一对象
        treeMap.put(19,"xiaoming");//第二对象
        treeMap.put(20,"wangwu");//第三对象
        treeMap.put(20,"lisi");//第四对象

        //普通打印
        System.out.println(treeMap);
    }

运行结果:从运行结果中我们发现后面插入的第四对象将之前插入的第三对象给覆盖掉了,即更新了Value值

在这里插入图片描述


三、TreeMap的遍历 TreeMap基础

TreeMap的遍历分为调用keySet()方法遍历和entrySet()方法遍历两种

3.1、TreeMap调用keySet()方法遍历

public static void main(String[] args) {
        //创建一个Key为Integer数据类型,Value为String数据类型的TreeMap对象
        //若要使用TreeMap,则需要导入import java.util.TreeMap;
        TreeMap<Integer,String> treeMap=new TreeMap<>();

        //使用put()方法添加元素
        treeMap.put(18,"xiaoming");//第一对象
        treeMap.put(19,"xiaoming");//第二对象
        treeMap.put(20,"wangwu");//第三对象

        //调用keySet()方法遍历
        //在TreeMap遍历中Key占据着主导地位,可以通过Key值找到对应的Value值
        //调用keySet()方法,Set<>泛型约束应与Key的数据类型一致
        //例如在本代码中,TreeMap<Integer, String>,Key的数据类型为Integer,因此Set<>泛型约束也应当为Integer
        //Set<Integer> set=treeMap.keySet();代码的意思为将TreeMap中所有Key值存入Set集合(18,19,20)
        //那么set即为Key值集合
        Set<Integer> set=treeMap.keySet();
        //使用forEach()语句遍历,Integer为set的数据类型,i为set的复用名(相当于set)
        //那么i就成为了Key值
        for(Integer i:set){
            //在TreeMap遍历中Key占据着主导地位,可以通过Key值找到对应的Value值
            //接下来我们要根据Key值来查找各个Key值对应的Value值,即treeMap.get(i)来获取key对应的value
            //Value数据类型为String,设置一个String变量str来存储Value
            //treeMap.get(i);代码意思为根据i(Key值)找到相对应的Value值
            String str=treeMap.get(i);
            //打印输出
            System.out.println("Key的值为:"+i+"    "+"Value的值为:"+str);
        }
    }

运行结果:

在这里插入图片描述


3.2、TreeMap调用entrySet()方法遍历

public static void main(String[] args) {
        //若要使用TreeMap,我们需要导入import java.util.TreeMap;
        //创建一个Key为Integer数据类型,Value为String数据类型的TreeMap对象
        TreeMap<Integer,String> treeMap=new TreeMap<>();
        //通过put()方法来添加数据
        treeMap.put(18,"xiaoming");
        treeMap.put(19,"xiaoming");
        treeMap.put(20,"wangwu");
        //TreeMap通过调用entrySet()方法遍历
        //这时我们需要导入Map的包:import java.util.Map;和Set的包:import java.util.Set;
        //调用entrySet()方法,Set<>泛型约束应与Map.Entry的数据类型一致,即<Integer, String>
        //<Key,Value>键值对,在Java语言中又被称之为Entry/entry,Map.Entry就相当于Student.name,若name的数据类型为String,则Student.name的数据类型为String,同理若<key,value>中key的数据类型为Integer,value的数据类型为String,则Map.Entry的数据类型为<Integer,String>,在这里就是<Integer, String>。
        //Set<Map.Entry<Integer, String>> set=treeMap.entrySet();代码的意思为将TreeMap中所有(Key,Value)值存入Set集合[(18,"xiaoming"),(19,“xiaoming”),(20,“wangwu”)]
        //那么set即为(Key,Value)值集合
        Set<Map.Entry<Integer,String>> set=treeMap.entrySet();
        //使用forEach()语句遍历,Map.Entry<Integer,String>为set的数据类型,i为set的复用名(相当于set)
        //那么i就成为了(Key,Value)值
        for(Map.Entry<Integer,String> i:set){
            //打印输出,直接调用getKey()方法得到Key值,直接调用getValue()得到Value值
            System.out.println("Key的值为:"+i.getKey()+"    "+"Value的值为:"+i.getValue());
        }
    }

运行结果:

在这里插入图片描述


四、TreeMap集合遍历自定义类型数据经典bug TreeMap经典bug

需求:创建一个TreeMap集合,Key键是学生对象(Student),Value值是籍贯(String)。

要求:存储三个键值对元素(Entry),遍历,并按学生年龄排序遍历,若年龄相同则按姓名排序。

4.1、TreeMap经典bug复现

原始Student类

public class Student {
    private String name;
    private int age;

    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

TreeMap遍历输出bug

public static void main(String[] args) {
        //若要使用TreeMap,我们需要导入import java.util.TreeMap;
        //创建一个TreeMap空参构造器(new TreeMap<>()此即为引用空参构造创建一个对象),同时使用泛型来约束treeMap的数据类型,Key的数据类型为Student,Value的数据类型为String
        TreeMap<Student,String> treeMap=new TreeMap<>();
        //创建Student对象
        Student student=new Student("zhangsan",18);
        Student student1=new Student("lisi",19);
        Student student2=new Student("wangwu",20);
        Student student3=new Student("zhaoliu",18);
        通过put()方法来添加数据
        treeMap.put(student,"北京");
        treeMap.put(student1,"深圳");
        treeMap.put(student2,"上海");
        treeMap.put(student3,"广州");
        //调用keySet方法遍历
        //这时我们需要导入Set的包:import java.util.Set;
        //在TreeMap遍历中Key占据着主导地位,可以通过Key值找到对应的Value值
        //调用keySet()方法,Set<>泛型约束应与Key的数据类型一致
        //例如在本代码中,TreeMap<Student, String>,Key的数据类型为Student,因此Set<>泛型约束也应当为Student
        //Set<Student> set=treeMap.keySet();代码的意思为将treeMap中所有Key值存入Set集合(student,student1,student2,student3)
        //那么set即为Key值集合
        Set<Student> set=treeMap.keySet();
        //使用forEach()语句遍历,Student为set数据类型,i为set的复用名(相当于set)
        //那么i就成为了Key值
        for(Student stu:set){
            //在TreeMap遍历中Key占据着主导地位,可以通过Key值找到对应的Value值
            //接下来我们要根据Key值来查找各个Key值对应的Value值
            //Value数据类型为String,设置一个String变量来存储Value
            //treeMap.get(i);代码意思为根据i(Key值)找到相对应的Value值
            String str=treeMap.get(stu);
            //打印输出
            System.out.println("Key的值为:"+stu+"    "+"Value的值为:"+str);
        }

    }

运行结果:从运行结果中我们发现出现了bug,bug显示的信息是Student类没有重写Comparable的方法
在这里插入图片描述


4.2、TreeMap经典bug解决方法

在TreeMap或者TreeSet中遍历输出自定义类时,如果我们想要正常输出,有两种方法:①在自定义类中重写Comparable中的compareTo()方法;②在main方法中的TreeMap构造器中使用Comparator比较器编写匿名内部类并重写compare()方法。

4.2.1、自定义类重写Comparable接口的compareTo()方法

重写了Comparable中的compareTo()方法的Student类

public class Student implements Comparable<Student>{
    private String name;
    private int age;
    @Override
    public int compareTo(Student o) {
    	//在这里我们定义排序规则:主要根据age进行排序
    	/**
         * 例如我们首先插入了一个("zhangsan",18),那么毫无疑问它会排在第一位
         * 然后我们插入("lisi",19),那么此时this.age=18;o.getAge()=19,那么("lisi",19)就要插入到("zhangsan",18)之后
         * 随后我们插入("zhaoliu",19),那么它会先于("zhangsan",18).age进行比较,那么("zhaoliu",19)就要插入到("zhangsan",18)后面,之后它再与("lisi",19).age进行比较,age相同,比较name,zhaoliu排在lisi之后
         * 因此最后排序结果为("zhangsan",18),("lisi",19),("zhaoliu",19)
         * */
        int result=this.getAge()-o.getAge();
        //如果两个对象的年龄相同,我们再根据姓名进行排序
        result=result==0?this.getName().compareTo(o.getName()):result;
        return result;
    }
    public Student() {
    }
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

TreeMap遍历输出

public static void main(String[] args) {
        //若要使用TreeMap,我们需要导入import java.util.TreeMap;
        //创建一个TreeMap空参构造器(new TreeMap<>()此即为引用空参构造创建一个对象),同时使用泛型来约束treeMap的数据类型,Key的数据类型为Student,Value的数据类型为String
        TreeMap<Student,String> treeMap=new TreeMap<>();
        //创建Student对象
        Student student=new Student("zhangsan",18);
        Student student1=new Student("lisi",19);
        Student student2=new Student("wangwu",20);
        Student student3=new Student("zhaoliu",18);
        通过put()方法来添加数据
        treeMap.put(student,"北京");
        treeMap.put(student1,"深圳");
        treeMap.put(student2,"上海");
        treeMap.put(student3,"广州");
        //调用keySet方法遍历
        //这时我们需要导入Set的包:import java.util.Set;
        //在TreeMap遍历中Key占据着主导地位,可以通过Key值找到对应的Value值
        //调用keySet()方法,Set<>泛型约束应与Key的数据类型一致
        //例如在本代码中,TreeMap<Student, String>,Key的数据类型为Student,因此Set<>泛型约束也应当为Student
        //Set<Student> set=treeMap.keySet();代码的意思为将treeMap中所有Key值存入Set集合(student,student1,student2,student3)
        //那么set即为Key值集合
        Set<Student> set=treeMap.keySet();
        //使用forEach()语句遍历,Student为set数据类型,i为set的复用名(相当于set)
        //那么i就成为了Key值
        for(Student stu:set){
            //在TreeMap遍历中Key占据着主导地位,可以通过Key值找到对应的Value值
            //接下来我们要根据Key值来查找各个Key值对应的Value值
            //Value数据类型为String,设置一个String变量来存储Value
            //treeMap.get(i);代码意思为根据i(Key值)找到相对应的Value值
            String str=treeMap.get(stu);
            //打印输出
            System.out.println("Key的值为:"+stu+"    "+"Value的值为:"+str);
        }
    }

运行结果:(成功输出!!!)
在这里插入图片描述


4.2.2、TreeMap构造器使用Comparator比较器重写compare()方法

原始Student类

public class Student{
    private String name;
    private int age;

    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

在main方法中的TreeMap构造器中编写匿名内部类并重写compare()方法的TreeMap遍历输出

public static void main(String[] args) {
        //若要使用TreeMap,我们需要导入import java.util.TreeMap;
        //编写此匿名内部类,我们需要导入import java.util.Comparator;
        //创建一个TreeMap空参构造器(new TreeMap<>()此即为引用空参构造创建一个对象),同时使用泛型来约束treeMap的数据类型,Key的数据类型为Student,Value的数据类型为String
        TreeMap<Student,String> treeMap=new TreeMap<>(new Comparator<Student>() {
            @Override
            public int compare(Student o1, Student o2) {
                //在这里我们定义排序规则:主要根据age进行排序
                /**
                 * 例如我们首先插入了一个("zhangsan",18),那么毫无疑问它会排在第一位
                 * 然后我们插入("lisi",19),那么此时o1.getAge()=18;o2.getAge()=19,那么("lisi",19)就要插入到("zhangsan",18)之后
                 * 随后我们插入("zhaoliu",19),那么它会先于("zhangsan",18).getAge()进行比较,那么("zhaoliu",19)就要插入到("zhangsan",18)后面,之后它再与("lisi",19).getAge()进行比较,age相同,比较name,zhaoliu排在lisi之后
                 * 因此最后排序结果为("zhangsan",18),("lisi",19),("zhaoliu",19)
                 * */
                int result=o1.getAge()-o2.getAge();
                //如果两个对象的年龄相同,我们再根据姓名进行排序
                result= result==0 ? o1.getName().compareTo(o2.getName()) : result;
                return result;
            }
        });
        //创建Student对象
        Student student=new Student("zhangsan",18);
        Student student1=new Student("lisi",19);
        Student student2=new Student("wangwu",20);
        Student student3=new Student("zhaoliu",18);
        通过put()方法来添加数据
        treeMap.put(student,"北京");
        treeMap.put(student1,"深圳");
        treeMap.put(student2,"上海");
        treeMap.put(student3,"广州");
        //调用keySet方法遍历
        //这时我们需要导入Set的包:import java.util.Set;
        //在TreeMap遍历中Key占据着主导地位,可以通过Key值找到对应的Value值
        //调用keySet()方法,Set<>泛型约束应与Key的数据类型一致
        //例如在本代码中,TreeMap<Student, String>,Key的数据类型为Student,因此Set<>泛型约束也应当为Student
        //Set<Student> set=treeMap.keySet();代码的意思为将treeMap中所有Key值存入Set集合(student,student1,student2,student3)
        //那么set即为Key值集合
        Set<Student> set=treeMap.keySet();
        //使用forEach()语句遍历,Student为set数据类型,i为set的复用名(相当于set)
        //那么i就成为了Key值
        for(Student stu:set){
            //在TreeMap遍历中Key占据着主导地位,可以通过Key值找到对应的Value值
            //接下来我们要根据Key值来查找各个Key值对应的Value值
            //Value数据类型为String,设置一个String变量来存储Value
            //treeMap.get(i);代码意思为根据i(Key值)找到相对应的Value值
            String str=treeMap.get(stu);
            //打印输出
            System.out.println("Key的值为:"+stu+"    "+"Value的值为:"+str);
        }

    }

运行结果:(成功输出!!!)
在这里插入图片描述


OK!!!TreeMap介绍结束!!!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值