treemap

来源:互联网 发布:电脑速录软件 编辑:程序博客网 时间:2024/05/16 08:38

我们知道HashMap的存储位置是按照key这个对象的hashCode来存放的,而TreeMap则是不是按照hashCode来存放,他是按照实现的Comparable接口的compareTo这个方法来存储的,
只要compareTo的返回结果为0就表示两个对象相等,那么就存不进去两个对象,后put的就把前面的覆盖掉,甚至我们都不用重写equasls和hashCode方法,而
只需要实现Comparable接口来重写comparareTo方法就行了,但是我们不能保证在应用中不会用到HashMap,所以保持良好的习惯,当我们定义了一个对象之后习惯性的
重写equals和hashCode方法。本文比较详细的解释了TreeMap、Comparable、Comparator这三者的关联。

  测试Comparable接口:

  第一次比较:定义一个User类,实现Comparable接口,按照年龄排序,我们让equals为true,而hashCode也始终相等。

public class User implements Comparable<User> {    private String id;    private String name;    private Integer age;    public User() {    }    public User(String id, String name, Integer age) {        super();        this.id = id;        this.name = name;        this.age = age;    }    public String getId() {        return id;    }    public void setId(String id) {        this.id = id;    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    public Integer getAge() {        return age;    }    public void setAge(Integer age) {        this.age = age;    }    @Override    public String toString() {        return "User [name=" + name + ", age=" + age + "]";    }    @Override    public boolean equals(Object obj) {        return true;    }    @Override    public int hashCode() {        return 0;    }    public int compareTo(User o) {        return this.age > o.getAge() ? 1 : this.age == o.getAge() ? 0 : -1;    }}

  测试代码:

public class TestUser {    public static void main(String[] args) {        Map<User, Integer> userHashMap = new HashMap<User, Integer>();        User user1 = new User("1", "Jay", 30);        User user2 = new User("2", "Jolin", 21);        User user3 = new User("3", "Jack Cheng", 22);        User user4 = new User("4", "Bruce Lee", 22);        userHashMap.put(user1, 100);        userHashMap.put(user2, 200);        userHashMap.put(user3, 300);        userHashMap.put(user4, 400);        System.out.println(userHashMap);        Map<User, Integer>  userTreeMap = new TreeMap<User, Integer>();        userTreeMap.put(user1, 100);        userTreeMap.put(user2, 200);        userTreeMap.put(user3, 300);        userTreeMap.put(user4, 400);        System.out.println(userTreeMap);    }}

  
结果

{User [name=Jay, age=30]=400}{User [name=Jolin, age=21]=200, User [name=Jack Cheng, age=22]=400, User [name=Jay, age=30]=100}

  结论:对于HashMap而言,只要key的equals相等就表示两个元素相等,HashMap就存不进去;而TreeMap是不管equals和hashCode的,只要compareTo相等就表示两个元素相同,就存不进去。

  2.第二次比较:现在我们按照id来重写hashCode和equals方法,如下:

    @Override    public int hashCode() {        final int prime = 31;        int result = 1;        result = prime * result + ((id == null) ? 0 : id.hashCode());        return result;    }    @Override    public boolean equals(Object obj) {        if (this == obj)            return true;        if (obj == null)            return false;        if (getClass() != obj.getClass())            return false;        User other = (User) obj;        if (id == null) {            if (other.id != null)                return false;        } else if (!id.equals(other.id))            return false;        return true;    }

测试代码不变,还是上面的测试代码,结果:

{User [name=Jolin, age=21]=200, User [name=Jay, age=30]=100, User [name=Bruce Lee, age=22]=400, User [name=Jack Cheng, age=22]=300}{User [name=Jolin, age=21]=200, User [name=Jack Cheng, age=22]=400, User [name=Jay, age=30]=100}

说明:HashMap只要equals不等那就表示不等,而对于TreeMap如果compareTo相等,那么2个元素就相等,并且排序是按照compareTo方法定义的排序规则。

接下来再在测试代码里面添加List测试:

        List<User> userList = new ArrayList<User>();        userList.add(user1);        userList.add(user2);        userList.add(user3);        userList.add(user4);        System.out.println(userList);        Collections.sort(userList);        System.out.println(userList);

结果:

{User [name=Jolin, age=21]=200, User [name=Jay, age=30]=100, User [name=Bruce Lee, age=22]=400, User [name=Jack Cheng, age=22]=300}{User [name=Jolin, age=21]=200, User [name=Jack Cheng, age=22]=400, User [name=Jay, age=30]=100}[User [name=Jay, age=30], User [name=Jolin, age=21], User [name=Jack Cheng, age=22], User [name=Bruce Lee, age=22]][User [name=Jolin, age=21], User [name=Jack Cheng, age=22], User [name=Bruce Lee, age=22], User [name=Jay, age=30]]

当调用sort方法之后List里面的元素就按照age排序了。

对于Comparator,一种叫做策略模式的设计模式,我们在User中实现Comparable接口,重写里面的方法,可以实现对User按age排序,这个排序算法是
存在于User内部的,换句话说User和排序算法是相互依存的,User只能用compareTo这个算法,而compareTo算法也只能为User服务,那么如果我们其他
地方也有同样的需求,就只能再实现一次Comparable,来为另外一个对象服务,这就有点重复的感觉,而且代码没有得到复用,样子对象和算法混在一
起耦合性很强,于是希望把算法和模型分离出来,让算法单独存在,不同的对象可以使用同一个算法,并且二者是分离的,没有混为一体。当然可以分
开那必然还是可以放在一起的。用一句比较专业点的话来讲就是策略模式有两个特点:1.封装变化。2.变成中使用接口而不实现接口。
如果我再说得直白一点,策略模式就是一种面向接口编程的思想。这点在多线程里面也有所体现,就是为什么我们在开启新线程的时候要new Thread(Runnable接口),
参数传接口而不推荐去继承Thread,虽然也可以继承,这也是体现面向对象的封装特性,将run的算法和线程分离,可以实现run所在类的复用,虽然一般都不会去复用,
当然这里还有一点就是Java只能单继承,如果继承了Thread就不能在继承其他的类。此时我们的User是按照age排序,那如果想按照名字排序呢,那就没办法了,
但是如果将算法分离出来,需要用名字排序我们就传用名字排序的算法进去,需要用age排序就传age排序算法进去。环境持有接口引用,通过set方法将接口实现
传入到环境,在环境中来调用接口方法,这完完全全是面向接口的特点。下面的第一个例子就是存放在一起的,因为TreeMap的构造方法其中就有一个是待Comparator
接口参数的构造方法,该参数只要实现了Comparator接口就行,如下:

public TreeMap(Comparator<? super K> comparator) {        this.comparator = comparator;    }

举例:

有一个Person类,实现了Comparator接口,并且按照岁数排序:

public class Person implements Comparator<Person> {    private String id;    private String name;    private Integer age;    public Person() {    }    public Person(String id, String name, Integer age) {        this.id = id;        this.name = name;        this.age = age;    }    public String getId() {        return id;    }    public void setId(String id) {        this.id = id;    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    public Integer getAge() {        return age;    }    public void setAge(Integer age) {        this.age = age;    }    @Override    public String toString() {        return "Person [name=" + name + ", age=" + age + "]";    }    @Override    public int hashCode() {        final int prime = 31;        int result = 1;        result = prime * result + ((id == null) ? 0 : id.hashCode());        return result;    }    @Override    public boolean equals(Object obj) {        if (this == obj)            return true;        if (obj == null)            return false;        if (getClass() != obj.getClass())            return false;        Person other = (Person) obj;        if (id == null) {            if (other.id != null)                return false;        } else if (!id.equals(other.id))            return false;        return true;    }    public int compare(Person o1, Person o2) {        return o1.getAge() > o2.getAge() ? 1 : o1.getAge() == o2.getAge() ? 0 : -1;    }}

测试:

public class TestPerson {    public static void main(String[] args) {        Map<Person, Integer> personHashMap = new HashMap<Person, Integer>();        Person person1 = new Person("1", "Jay", 30);        Person person2 = new Person("2", "Jolin", 21);        Person person3 = new Person("3", "Jack Cheng", 22);        Person person4 = new Person("4", "Bruce Lee", 22);        personHashMap.put(person1, 100);        personHashMap.put(person2, 200);        personHashMap.put(person3, 300);        personHashMap.put(person4, 400);        System.out.println(personHashMap);        Map<Person, Integer>  personTreeMap = new TreeMap<Person, Integer>(new Person());        personTreeMap.put(person1, 100);        personTreeMap.put(person2, 200);        personTreeMap.put(person3, 300);        personTreeMap.put(person4, 400);        System.out.println(personTreeMap);    }}

结果:

{Person [name=Jolin, age=21]=200, Person [name=Jay, age=30]=100, Person [name=Bruce Lee, age=22]=400, Person [name=Jack Cheng, age=22]=300}{Person [name=Jolin, age=21]=200, Person [name=Jack Cheng, age=22]=400, Person [name=Jay, age=30]=100}

依然是compara方法相等的元素表示相同,只能存放一个进去,并且是按照age排序的。

在List中的结果:

public class TestPerson {    public static void main(String[] args) {        Map<Person, Integer> personHashMap = new HashMap<Person, Integer>();        Person person1 = new Person("1", "Jay", 30);        Person person2 = new Person("2", "Jolin", 21);        Person person3 = new Person("3", "Jack Cheng", 22);        Person person4 = new Person("4", "Bruce Lee", 22);        personHashMap.put(person1, 100);        personHashMap.put(person2, 200);        personHashMap.put(person3, 300);        personHashMap.put(person4, 400);        System.out.println(personHashMap);        Map<Person, Integer>  personTreeMap = new TreeMap<Person, Integer>(new Person());        personTreeMap.put(person1, 100);        personTreeMap.put(person2, 200);        personTreeMap.put(person3, 300);        personTreeMap.put(person4, 400);        System.out.println(personTreeMap);        List<Person> personList = new ArrayList<Person>();        personList.add(person1);        personList.add(person2);        personList.add(person3);        personList.add(person4);        System.out.println(personList);        Collections.sort(personList, new Person());        System.out.println(personList);    }}

结果:

{Person [name=Jolin, age=21]=200, Person [name=Jay, age=30]=100, Person [name=Bruce Lee, age=22]=400, Person [name=Jack Cheng, age=22]=300}{Person [name=Jolin, age=21]=200, Person [name=Jack Cheng, age=22]=400, Person [name=Jay, age=30]=100}[Person [name=Jay, age=30], Person [name=Jolin, age=21], Person [name=Jack Cheng, age=22], Person [name=Bruce Lee, age=22]][Person [name=Jolin, age=21], Person [name=Jack Cheng, age=22], Person [name=Bruce Lee, age=22], Person [name=Jay, age=30]]

调用sort方法前没排序,而调用之后按照age排序了。

其实将算法和模型分离也非常类似,只需要将实现Comparator接口的类单独拿出去就ok了。现在定义Person类,不实现Comparator接口,而将SortPerson类来
实现该接口,其实就是用SortPerson类代替之前TreeMap中的参数,之前的参数是实现了Comparator接口的Person对象。

Person类:

public class Person {    private String id;    private String name;    private Integer age;    public Person() {    }    public Person(String id, String name, Integer age) {        this.id = id;        this.name = name;        this.age = age;    }    public String getId() {        return id;    }    public void setId(String id) {        this.id = id;    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    public Integer getAge() {        return age;    }    public void setAge(Integer age) {        this.age = age;    }    @Override    public String toString() {        return "Person [name=" + name + ", age=" + age + "]";    }    @Override    public int hashCode() {        final int prime = 31;        int result = 1;        result = prime * result + ((id == null) ? 0 : id.hashCode());        return result;    }    @Override    public boolean equals(Object obj) {        if (this == obj)            return true;        if (obj == null)            return false;        if (getClass() != obj.getClass())            return false;        Person other = (Person) obj;        if (id == null) {            if (other.id != null)                return false;        } else if (!id.equals(other.id))            return false;        return true;    }}

SortPerson类:

public class SortPerson implements Comparator<Person> {    public int compare(Person o1, Person o2) {        return o1.getAge() > o2.getAge() ? 1 : o1.getAge() == o2.getAge() ? 0 : -1;    }}

测试:

public class TestPerson {    public static void main(String[] args) {        Map<Person, Integer> personHashMap = new HashMap<Person, Integer>();        Person person1 = new Person("1", "Jay", 30);        Person person2 = new Person("2", "Jolin", 21);        Person person3 = new Person("3", "Jack Cheng", 22);        Person person4 = new Person("4", "Bruce Lee", 22);        personHashMap.put(person1, 100);        personHashMap.put(person2, 200);        personHashMap.put(person3, 300);        personHashMap.put(person4, 400);        System.out.println(personHashMap);        Map<Person, Integer>  personTreeMap = new TreeMap<Person, Integer>(new SortPerson());        personTreeMap.put(person1, 100);        personTreeMap.put(person2, 200);        personTreeMap.put(person3, 300);        personTreeMap.put(person4, 400);        System.out.println(personTreeMap);        List<Person> personList = new ArrayList<Person>();        personList.add(person1);        personList.add(person2);        personList.add(person3);        personList.add(person4);        System.out.println(personList);        Collections.sort(personList, new SortPerson());        System.out.println(personList);    }}

结果:

{Person [name=Jolin, age=21]=200, Person [name=Jay, age=30]=100, Person [name=Bruce Lee, age=22]=400, Person [name=Jack Cheng, age=22]=300}{Person [name=Jolin, age=21]=200, Person [name=Jack Cheng, age=22]=400, Person [name=Jay, age=30]=100}[Person [name=Jay, age=30], Person [name=Jolin, age=21], Person [name=Jack Cheng, age=22], Person [name=Bruce Lee, age=22]][Person [name=Jolin, age=21], Person [name=Jack Cheng, age=22], Person [name=Bruce Lee, age=22], Person [name=Jay, age=30]]

很明显:结果与之前的将算法实现在Person类里面的结果一致的。

写的太多了,就不从JDK源码的角度来看问题了,如果有兴趣可以看看JDK源码,大致思路是才用红黑树来存数据,通过比较器的比较结果来判断当前需要保存的
元素位于什么地方,还是那句话,既然底层用到了比较排序,那么性能肯定是比较差的,可以说是牺牲性能来做到排序功能,这个很容易理解,因为Java里面或者说
计算机语言里面都是这个思路,要获得某种特定的能力,必然是需要牺牲性能,当然有时候为了获得良好的性能,那就必须要牺牲内存或者硬盘空间。比如HashMap
为了获得非常强大的根据key来读取数据的性能,采用hash算法来牺牲一定的内存空间,再比如,数据库里面为了获得查询性能,给某些列添加索引,牺牲硬盘存储
空间来达到效果,当然像数据库的为了获得查询和统计等性能为某些列添加聚簇索引,这种对硬盘空间的牺牲那是相当的惊人。索引占用空间比表里面的数据还
大,但是获得的性能也是非常优秀的,所以,从某种意义上说,为了获取某种特性来牺牲另外一些资源那也是值得的。

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 诸天仙神热搜:主神老婆竟是魔尊 团宠小福妻又娇又软 替嫁医妃有空间 重生之农门贵夫 野性偏爱 嫁给病弱太子后我躺赢了 咸鱼娘娘一心只想翻墙 救命!破产后高冷总裁处处招惹我 冷酷将军每天都想要贴贴 何以赎光 甜腻!病娇傅少竟然暗恋我! 天价萌妻:偏执帝少心尖宠 有读心术后,战神把娘子宠上天 穿书后,我刷错了反派的好感度 摄政王怀里的团宠美人娇又软 满级千金不想掉马 女帝她就是个卖药的! 华娱激荡年代 她来看我的演唱会 快穿:娇养反派大佬做替身 不努力种田就要和相公继承皇位了 穿书末世之我是金手指 转生恶少后的魔幻日常 穿书后我成了反派男二的铁血妈粉 带着超市重返年代 新婚夜,我被冷冰冰的王爷读心了 修仙女配拿了反派剧本 影后的假面童话 克死前夫后我成了心软的神 新婚夜,我治好了失明太子的隐疾 沦陷野玫瑰 完球!我的崽居然是反派 重生后她被憨憨相公娇宠了 师祖回归,徒弟各个是疯批大佬 植物人老公苏醒后,她只想逃 他怎么可能是魔尊 农家小团宠:带着空间撩大佬 快穿做个好婆婆 医路芳华 满级真千金:一品女法医 惊!举重妖精被电竞奶狗缠上了