1.TreeMap简单介绍
TreeMap是一个有序的key-value集合,是非线程安全的,基于红黑树(Red-Black tree)实现。其映射根据键的自然顺序进行排序,或者根据创建映射时提供的 Comparator 进行排序,具体取决于使用的构造方法。
当自定义比较器时,需要自定义类实现java.lang.Comparable接口,并重写compareTo()方法。
2.TreeMap基本使用
根据键的自然顺序进行排序
public static void main(String[] args) {
Map map = new TreeMap<>();
map.put("name", "zzc");
map.put("age", "18");
map.put("sex", "m");
map.put("name", "wzc");
map.put("angle", "yhp");
System.out.println(map);
}
如何排序呢?进入到TreeMap中的put(key, value)
cmp = k.compareTo(t.key);
通过Comparable中的compareTo()方法进行排序的。
这里的key是String类型的,String类实现了Comparable接口并且重写了compareTo()方法:
public int compareTo(String anotherString) {
int len1 = value.length;
int len2 = anotherString.value.length;
int lim = Math.min(len1, len2);
char v1[] = value;
char v2[] = anotherString.value;
int k = 0;
while (k < lim) {
char c1 = v1[k];
char c2 = v2[k];
if (c1 != c2) {
return c1 - c2;
}
k++;
}
return len1 - len2;
}
字符串比较大小就是通过比较字符串中的字符大小,这样就可以实现排序。
【注意】:如果TreeMap中存储的元素是自定义类并且没有实现Comparable接口会怎么样?下面就尝试一下:
自定义一个User类:
public class User {
private Integer age;
private String name;
private String email;
public User(Integer age, String name, String email) {
this.age = age;
this.name = name;
this.email = email;
}
}
测试类:
public class Test {
public static void main(String[] args) {
Map map = new TreeMap<>();
User u1 = new User(19, "zzc", "[email protected]");
User u2 = new User(15, "wac", "[email protected]");
User u3 = new User(20, "zhangy", "[email protected]");
map.put(u1, "1");
map.put(u2, "2");
map.put(u3, "3");
System.out.println(map);
}
}
测试结果:
抛出异常:java.lang.ClassCastException: com.zzc.test.User cannot be cast to java.lang.Comparable。
【注意】:这里的key是User对象
很显然,这里User类因未实现Comparable接口,因而无法比较对象大小,所以抛出运行时异常。
接下来使User类实现Comparable接口并重写compareTo()方法:
这里我以age属性进行排序(当然,你也可以使用其他属性进行排序,比较大小逻辑直接写在compare()方法中即可)
public class User implements Comparable<User>{
...
@Override
public int compareTo(User user) {
return compare(this.age, user.getAge());
}
public static int compare(Integer age1, Integer age2) {
return (age1 > age2 ? 1 : (age1 == age2) ? 0 : -1 );
}
}
测试类Test:
public class Test {
public static void main(String[] args) {
Map map = new TreeMap<>();
User u1 = new User(19, "zzc", "[email protected]");
User u2 = new User(15, "wac", "[email protected]");
User u3 = new User(20, "zhangy", "[email protected]");
map.put(u1, "1");
map.put(u2, "2");
map.put(u3, "3");
User key = null;
Integer value = null;
Iterator iterator = map.keySet().iterator();
while (iterator.hasNext()) {
key = (User) iterator.next();
// 重写User类中的toString()方法
System.out.println(key);
}
}
}
测试结果:
年龄从小到大排序。
我们再往测试类Test中的map添加一条数据,并且这次也把value打印出来:
User u4 = new User(19, "zzc", "[email protected]");
map.put(u4, "4");
这个u4对象和u1对象是一样的。
Test类:
public class Test {
public static void main(String[] args) {
Map map = new TreeMap<>();
User u1 = new User(19, "zzc", "[email protected]");
User u2 = new User(15, "wac", "[email protected]");
User u3 = new User(20, "zhangy", "[email protected]");
User u4 = new User(19, "zzc", "[email protected]");
map.put(u1, "1");
map.put(u2, "2");
map.put(u3, "3");
map.put(u4, "4");
User key = null;
String value = null;
Iterator iterator = map.keySet().iterator();
while (iterator.hasNext()) {
key = (User) iterator.next();
value = (String) map.get(key);
System.out.println(key + "=====" + value);
}
System.out.println(u1.equals(u4));
}
}
测试结果:
虽然也是三条数据,但很明显,u4对象把u1对象给覆盖了。也就是说key相同,value就会被覆盖。实际上,确实如此。TreeMap中的put()方法中:
parent = t;
cmp = k.compareTo(t.key); // cmp == 0
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else
return t.setValue(value);
key一样,新值会覆盖原值。但是key真的是一样吗?那我们来测试下:
添加一条语句:
System.out.println(u1.equals(u4));
使用equals()方法来判断它们是否相同。
结果很意外,equals()方法返回false。但上面说对象u1和对象u4一样,但它们确实也一样啊,为何会返回false呢????
【注意】:那是因为User类没有重写equals()方法,当然,也要重写hashCode()方法,如果没有重写,那调用的就是父类Object类的equals()
Object中的equals()方法:
public boolean equals(Object obj) {
return (this == obj);
}
是用来判断两个对象的内存地址是否一样。对象u1和u4都是new出来的,内存地址肯定不一样啊,所以equals()方法返回false
所以,应该要在User类中重写hashCode()和equals()方法。
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (null == obj) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
User u = (User) obj;
if (this.getAge() != u.getAge()) {
return false;
}
if (null == this.getName()) {
if (u.getName() != null) {
return false;
}
} else if (!this.getName().equals(u.getName())) {
return false;
}
return true;
}
再次进行测试:
这样逻辑就说的通了。
使用自定义比较器进行排序
public class TmTest {
public static void main(String[] args) {
TreeMap<String, Integer> map = new TreeMap<>(new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o1.compareTo(o2);
}
});
map.put("age", 23);
map.put("lvo", 18);
map.put("nou", 99);
map.put("cao", 99);
String key = null;
Integer value = null;
Iterator iterator = map.keySet().iterator();
while (iterator.hasNext()) {
key = (String) iterator.next();
value = (Integer) map.get(key);
System.out.println(key + "=====" + value);
}
}
}