Java集合中的TreeMap解读
作者:蕾峰
TreeMap解读
TreeSet的底层是TreeMap。
public boolean add(E e) { return m.put(e, PRESENT)==null; //其中的e为我们放进去的元素,作为key来存放的。 //而后面的value为PRESENT为一个固定的值, }
而其中的PRESENT是在treeSet里面创建的静态的final类型的Object.
private static final Object PRESENT = new Object();
如果我们使用TreeMap,里面放置的是(e为Key,PRESENT为对应的那一个值)。
我们通过具体的代码设计如下所示:
package com.rgf.map; import java.util.TreeMap; @SuppressWarnings({"all"}) public class TreeMap_ { public static void main(String[] args) { //使用默认的构造器,创建TreeMap,是无序的,是指输入输出的顺序不一致 TreeMap treeMap = new TreeMap(); treeMap.put("jack","杰克"); treeMap.put("tom","汤姆"); treeMap.put("kristina","克瑞斯提诺"); treeMap.put("smith","斯密斯"); System.out.println("treeMap="+treeMap); } } }
运行界面如下所示:
我们查看如下所示:
我们发现TreeMap类里面除了提供无参的类以外,也有TreeMap(Comparator <K>)这样子的构造器。TreeMap可以传入一个实现了 Comparator接口的一个匿名内部类,匿名内部类里面我们仍然可以去指定添加我们的键值对的这种排序规则。
我们设计排序规则进行如下代码所示:
package com.rgf.map; import java.util.Comparator; import java.util.TreeMap; @SuppressWarnings({"all"}) public class TreeMap_ { public static void main(String[] args) { //使用默认的构造器,创建TreeMap,是无序的,是指输入输出的顺序不一致 //我们按照传入的k(String)的大小进行排序。 TreeMap treeMap = new TreeMap(new Comparator() { @Override public int compare(Object o1, Object o2) { //我们按照传入的k(String)的大小进行排序。 return ((String) o1).compareTo((String)o2); } }); treeMap.put("jack","杰克"); treeMap.put("tom","汤姆"); treeMap.put("Kristina","克瑞斯提诺"); treeMap.put("smith","斯密斯"); System.out.println("treeMap="+treeMap); } }
我们运行之后如下所示:
以上排序为从小到大,我们可以将排序修改为从大到小,我们进行修改代码如下所示:
package com.rgf.map; import java.util.Comparator; import java.util.TreeMap; @SuppressWarnings({"all"}) public class TreeMap_ { public static void main(String[] args) { //使用默认的构造器,创建TreeMap,是无序的,是指输入输出的顺序不一致 //我们按照传入的k(String)的大小进行排序。 TreeMap treeMap = new TreeMap(new Comparator() { @Override public int compare(Object o1, Object o2) { //我们按照传入的k(String)的大小进行排序。(从小到大) return ((String) o2).compareTo((String)o1); } }) ; treeMap.put("jack","杰克"); treeMap.put("tom","汤姆"); treeMap.put("kristina","克瑞斯提诺"); treeMap.put("smith","斯密斯"); System.out.println("treeMap="+treeMap); } }
运行界面如下所示:
我们可以按照长度进行排序(从小到大),我们将代码修改如下所示:
package com.rgf.map; import java.util.Comparator; import java.util.TreeMap; @SuppressWarnings({"all"}) public class TreeMap_ { public static void main(String[] args) { //使用默认的构造器,创建TreeMap,是无序的,是指输入输出的顺序不一致 //我们按照传入的k(String)的大小进行排序。 TreeMap treeMap = new TreeMap(new Comparator() { @Override public int compare(Object o1, Object o2) { //我们按照传入的k(String)的长度大小进行排序。(从小到大) return ((String) o1).length()-((String) o2).length(); } }) ; treeMap.put("jack","杰克"); treeMap.put("tom","汤姆"); treeMap.put("kristina","克瑞斯提诺"); treeMap.put("smith","斯密斯"); System.out.println("treeMap="+treeMap); } }
我们的运行界面如下所示:
我们也可以将排序找到长度从大到小,我们将代码修改如下所示:
package com.rgf.map; import java.util.Comparator; import java.util.TreeMap; @SuppressWarnings({"all"}) public class TreeMap_ { public static void main(String[] args) { //使用默认的构造器,创建TreeMap,是无序的,是指输入输出的顺序不一致 //我们按照传入的k(String)的大小进行排序。 TreeMap treeMap = new TreeMap(new Comparator() { @Override public int compare(Object o1, Object o2) { //我们按照传入的k(String)的长度大小进行排序。(从小到大) return ((String) o2).length()-((String) o1).length(); } }) ; treeMap.put("jack","杰克"); treeMap.put("tom","汤姆"); treeMap.put("kristina","克瑞斯提诺"); treeMap.put("smith","斯密斯"); System.out.println("treeMap="+treeMap); } }
运行界面如下所示:
我们来进行Debug,我们从如下代码开始进行debug:
TreeMap treeMap = new TreeMap(new Comparator()
源码
1.构造器
把传入的实现了Comparator接口的匿名内部类(对象),传给了TreeMap的comparator属性。
//他的底层仍然是TreeMap,把传入的匿名内部类comparator赋给TreeMap的一个属性this.comparator. public TreeMap(Comparator<? super K> comparator) { this.comparator = comparator; }
我们退出去之后,进去put方法:
2.调用put方法
第一次添加,把k-v封装到Entry对象,放入root.
源码如下所示;
public V put(K key, V value) { Entry<K,V> t = root;//此时root为空,将root赋值给t. //我们第一次进去,所以还没有进行初始化,第一次初始化为空。 if (t == null) {//此时t为空,从而进入里面代码。 compare(key, key); // type (and possibly null) check //比较key值是否相同,构造器判断规则不同,key值不同,当前规则为比较长度大小。则长度为key. //TreeMap底层为Entry,Entry为TreeMap里面的一个内部类。 root = new Entry<>(key, value, null); size = 1; modCount++; return null; } int cmp; Entry<K,V> parent; // split comparator and comparable paths Comparator<? super K> cpr = comparator; if (cpr != null) { do { parent = t; cmp = cpr.compare(key, t.key); if (cmp < 0) t = t.left; else if (cmp > 0) t = t.right; else return t.setValue(value); } while (t != null); }
我们进行第一次添加之后,我们如下所示:
以后添加,即要使用比较器进行比较后添加。
此时root里面已经有值了。此时添加第一个值得时候,没有添加比较器。我们继续进行添加,查看源码。
public V put(K key, V value) { Entry<K,V> t = root; //由于此前已经有一个值了,此时root不为null.t也不为空,即不再进入下面第一个if语句。 if (t == null) { //第一次进去也调用了compare方法,即动态绑定了我们的匿名内部类的compare方法,传入的是两个key,即第一次添加的key加了两份进去。目的是为了判定是否为null值。 compare(key, key); // type (and possibly null) check root = new Entry<>(key, value, null); size = 1; modCount++; return null; } int cmp; Entry<K,V> parent; // split comparator and comparable paths //由于已经有一个值了,所以我们会将比较器拿到,进入以下代码。 Comparator<? super K> cpr = comparator;//比较器传入的值赋值给cpr if (cpr != null) { do { //遍历所有的key,给当前key找到适当的位置 parent = t; //我们传入的comparator赋值给parent cmp = cpr.compare(key, t.key); //动态绑定到我们的匿名内部类的compare方法。 if (cmp < 0) t = t.left; else if (cmp > 0) t = t.right; else //如果遍历过程中,发现准备添加key和当前已有的key相等,就不添加了,但是会进行覆盖原来的值。是由我们自定义的compare方法来决定的。 return t.setValue(value); } while (t != null); }
我们会动态绑定到我们的匿名内部类:
我们调用了compare方法了:
我们继续往进走:
final int compare(Object k1, Object k2) { //如果comparator不等于null的时候,我们进行动态绑定到我们的匿名内部类的compare方法。 return comparator==null ? ((Comparable<? super K>)k1).compareTo((K)k2) : comparator.compare((K)k1, (K)k2); }
我们会动态绑定到我们的匿名内部类:
此时的o1和o2都是jack.但是这个结果对是否添加当前的value值有任何影响。主要是为了检测是否为null。如果为null,则会在comparator方法里面抛出异常。
这里的compare作用是查看key是否为空,还有就是判断当前key是否实现了compareable接口是否是可以进行比较的。
我们进行排序的示例如下所示:
package com.rgf.jihe; import org.junit.Test; import java.util.*; /** * 向TreeMap中添加key-value,要求key必须是由同一个类创建的对象 * 因为要按照key进行排序:自然排序、定制排序 * */ public class TreeMapTest { public static void main(String[] args) { //自然排序 TreeMap map = new TreeMap(); User u1 = new User("Tom",23); User u2 = new User("Jerry",32); User u3 = new User("Jack",20); User u4 = new User("Rose",18); map.put(u1,98); map.put(u2,89); map.put(u3,76); map.put(u4,100); Set set = map.entrySet(); Iterator iterator = set.iterator(); while (iterator.hasNext()){ Object next = iterator.next(); System.out.println(next); } } //定制排序 @Test public void test2(){ TreeMap map = new TreeMap(new Comparator() { @Override public int compare(Object o1, Object o2) { if(o1 instanceof User && o2 instanceof User){ User user=(User) o1; User user1=(User) o2; return Integer.compare(user.getAge(),user1.getAge()); } throw new RuntimeException("输入的类型不匹配"); } }); User user = new User("rgf",123); User user1 = new User("woer",456); User user2 = new User("fed",789); map.put(user1,55); map.put(user,66); map.put(user2,77); Set set = map.entrySet(); Iterator iterator = set.iterator(); while (iterator.hasNext()){ Object next = iterator.next(); System.out.println(next); } } } class User implements Comparable{ private String name; private int age; public User() { } public User(String name, int age) { this.name = name; this.age = age; } //我们写出来该方法则不会再继续报错。 //按照姓名从大到小排列,年龄从小到大排列 @Override public int compareTo(Object o) { if(o instanceof User){ User User=(com.rgf.jihe.User)o; //return -this.name.compareTo(user.name);为了可以确保相同的值可以进行排序,我们进行二级排序 int compare=-this.name.compareTo(User.name); if(compare!=0){//如果相等,则我们进行比较年龄 return compare; }else { return Integer.compare(this.age,User.age); } }else { throw new RuntimeException("输入的类型不匹配"); } } 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 "User{" + "name='" + name + '\'' + ", age=" + age + '}'; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; User User = (com.rgf.jihe.User) o; if (age != User.age) return false; return Objects.equals(name, User.name); } @Override public int hashCode() { int result = name != null ? name.hashCode() : 0; result = 31 * result + age; return result; } }
运行之后如下所示:
到此这篇关于Java集合中的TreeMap解读的文章就介绍到这了,更多相关TreeMap解读内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!