TreeSet:
-
TreeSet是能够实现自动排序的集合,将整数、小数、字符串等不同类型的数据分别存储进各自的TreeSet后,TreeSet会将它们根据字典顺序进行排序。
-
使用TreeSet实现自定义类型的属性排序:
-
实现Comparable接口
-
重写compareTo()方法
// 年龄升序,年龄相同,按姓名升序9
@Override
public int compareTo(Emp o) {
return o.age == this.age ? this.name.compareTo(o.name) : this.age - o.age;
}
TreeSet对象丢失问题的解决方法
一如下代码为例
package treeMap;
import java.util.Collections;
import java.util.Set;
import java.util.TreeSet;
public class Demo1 {
public static void main(String[] args) {
Set<Stu> stuSet = new TreeSet<>();
Stu stu1 = new Stu(1, "zs", 18);
Stu stu2 = new Stu(2, "ls", 28);
Stu stu3 = new Stu(3, "ws", 18);
Stu stu4 = new Stu(4, "zl", 8);
Collections.addAll(stuSet, stu1, stu2, stu3, stu4);
for (Stu stu : stuSet) {
System.out.println(stu);
}
}
}
class Stu implements Comparable {
Integer id;
String name;
Integer age;
public Stu() {
}
public Stu(Integer id, String name, Integer age) {
this.id = id;
this.name = name;
this.age = age;
}
@Override
public int compareTo(Object o) {
Stu stu = (Stu) o;
return this.age - stu.age;
}
@Override
public String toString() {
return "Stu{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
public Integer getId() {
return id;
}
public void setId(Integer 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;
}
}
输出结果
Stu{id=4, name='zl', age=8}
Stu{id=1, name='zs', age=18}
Stu{id=2, name='ls', age=28}
可以发现在我们存进去的四个对象,少了一个name=“ws”
源码剖析:
-
进入TreeSet的add();方法
public boolean add(E e) { return m.put(e, PRESENT)==null; }
此方法中的m
/** * The backing map. */ private transient NavigableMap<E,Object> m; // Dummy value to associate with an Object in the backing Map private static final Object PRESENT = new Object(); /** * Constructs a set backed by the specified navigable map. */ TreeSet(NavigableMap<E,Object> m) { this.m = m; } /** * Constructs a new, empty tree set, sorted according to the * natural ordering of its elements. All elements inserted into * the set must implement the {@link Comparable} interface. * Furthermore, all such elements must be <i>mutually * comparable</i>: {@code e1.compareTo(e2)} must not throw a * {@code ClassCastException} for any elements {@code e1} and * {@code e2} in the set. If the user attempts to add an element * to the set that violates this constraint (for example, the user * attempts to add a string element to a set whose elements are * integers), the {@code add} call will throw a * {@code ClassCastException}. */ public TreeSet() { this(new TreeMap<E,Object>()); }
可以发现在创建TreeSet对象时,其无参构造函数调用了本身的有参构造函数,并将TreeMap赋值给本身的NavigableMap<E,Object> m,所以m本质上是一个TreeMap,也就是说TreeSet是以TreeMap为容器,其添加数据的方式遵循map集合添加元素的规则
-
TreeMap是双列集合,不允许键重复,且会对键进行排序,当键是自定义对象时,需要实现Comparator接口,并实现比较方法compare(T o1,T o2);
-
进入TreeMap的put()方法
public V put(K key, V value) { Entry<K,V> t = root; if (t == null) { compare(key, key); // type (and po root = new Entry<>(key, value, nul size = 1; modCount++; return null; } int cmp; Entry<K,V> parent; // split comparator and comparable pat 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); } else { if (key == null) throw new NullPointerException @SuppressWarnings("unchecked") Comparable<? super K> k = (Com do { parent = t; cmp = k.compareTo(t.key);//Comparable比较器的比较方法 if (cmp < 0) t = t.left; else if (cmp > 0) t = t.right; else return t.setValue(value); } while (t != null); } Entry<K,V> e = new Entry<>(key, value, if (cmp < 0) parent.left = e; else parent.right = e; fixAfterInsertion(e); size++; modCount++; return null; }
private final Comparator<? super K> comparator; //其无参构造函数 public TreeMap() { comparator = null; }
从源码中可以看到,put第一个值时,直接放入TreeMap中。第二个开始就会调用比较器compare方法。比较结果小于0就将当前的数据放在节点的左侧;如果大于0,就放在节点的右侧;等于0时,也就是比较的结果就会覆盖当前值。
-
我的自定义对象重写的方法只对age做了比较,当返回值为0时,之前的值被覆盖,所以对象并不是丢失,而是覆盖了前一个返回值为0的对象
-
所以接下来的操作即是对自定义对象重写的比较方法完善规则即可;