TreeMap和TreeSet的异同:
相同点:
- TreeMap和TreeSet都是有序的集合,也就是说他们存储的值都是拍好序的。
- TreeMap和TreeSet都是非同步集合,因此他们不能在多线程之间共享,不过可以使用方法Collections.synchroinzedMap()来实现同步
- 运行速度都要比Hash集合慢,他们内部对元素的操作时间复杂度为O(logN),而HashMap/HashSet则为O(1)。
不同点:
- 最主要的区别就是TreeSet和TreeMap非别实现Set和Map接口
- TreeSet只存储一个对象,而TreeMap存储两个对象Key和Value(仅仅key对象有序)
- TreeSet中不能有重复对象,而TreeMap中可以存在
TreeSet的是NavigableSet的实现类,NavigableSet继承了SortedSet接口,SortedSet是Set的子接口;
1 public class TreeSet<E> extends AbstractSet<E> 2 implements NavigableSet<E>, Cloneable, java.io.Serializable 3 { 4 /** 5 * The backing map. 6 */ 7 private transient NavigableMap<E,Object> m; 8 9 // Dummy value to associate with an Object in the backing Map 10 private static final Object PRESENT = new Object(); 11 12 /** 13 * Constructs a set backed by the specified navigable map. 14 */ 15 TreeSet(NavigableMap<E,Object> m) { 16 this.m = m; 17 } 18 19 /** 20 * Constructs a new, empty tree set, sorted according to the 21 * natural ordering of its elements. All elements inserted into 22 * the set must implement the {@link Comparable} interface. 23 * Furthermore, all such elements must be <i>mutually 24 * comparable</i>: {@code e1.compareTo(e2)} must not throw a 25 * {@code ClassCastException} for any elements {@code e1} and 26 * {@code e2} in the set. If the user attempts to add an element 27 * to the set that violates this constraint (for example, the user 28 * attempts to add a string element to a set whose elements are 29 * integers), the {@code add} call will throw a 30 * {@code ClassCastException}. 31 */ 32 public TreeSet() { 33 this(new TreeMap<E,Object>()); 34 } 35 ....... 36 }
由上面的TreeSet的源码可以看出,TreeSet的底层实现是通过TreeMap实现的,而TreeMap的底层又是如何实现的呢?
1 public TreeMap() { 2 comparator = null; 3 } 4 5 public TreeMap(Comparator<? super K> comparator) { 6 this.comparator = comparator; 7 } 8 .....(其他构造方法不一一列举) 9 //这里列举put方法详细讲解 10 public V put(K key, V value) { 11 Entry<K,V> t = root; 12 if (t == null) { 13 compare(key, key); // type (and possibly null) check 14 15 root = new Entry<>(key, value, null); 16 size = 1; 17 modCount++; 18 return null; 19 } 20 int cmp; 21 Entry<K,V> parent; 22 // split comparator and comparable paths 23 Comparator<? super K> cpr = comparator; 24 if (cpr != null) { 25 do { 26 parent = t; 27 cmp = cpr.compare(key, t.key); 28 if (cmp < 0) 29 t = t.left; 30 else if (cmp > 0) 31 t = t.right; 32 else 33 return t.setValue(value); 34 } while (t != null); 35 } 36 else { 37 if (key == null) 38 throw new NullPointerException(); 39 Comparable<? super K> k = (Comparable<? super K>) key; 40 do { 41 parent = t; 42 cmp = k.compareTo(t.key); 43 if (cmp < 0) 44 t = t.left; 45 else if (cmp > 0) 46 t = t.right; 47 else 48 return t.setValue(value); 49 } while (t != null); 50 } 51 Entry<K,V> e = new Entry<>(key, value, parent); 52 if (cmp < 0) 53 parent.left = e; 54 else 55 parent.right = e; 56 fixAfterInsertion(e); 57 size++; 58 modCount++; 59 return null; 60 }
从上面的TreeMap的两个构造方法和插入方法可以看出当第一次插入时,返回null,插入值不同时返回null;否则返回值不为null;这里需要注意以下几点:
1、创 建TreeSet或者TreeMap时候采用有参构造函数并且参数是Comparator时候,参数必须是Comparator的实现子类;而利用无参构 造函数时,向TreeSet或者TreeMap添加元素是需要特别注意所添加的对象必须是实现了Comparable接口的子类否则会报错(对象类型 cannot be cast to java.lang.Comparable),这也是TreeMap的put方法中实现的原因,这是多态的表现,父类对象指向子类引用;
Comparable<? super K> k = (Comparable<? super K>) key;
2、由于TreeSet和TreeMap的底层都是树形结构,而且每一个节点的对象是Entry对象
1 K key; 2 V value; 3 Entry<K,V> left = null; 4 Entry<K,V> right = null; 5 Entry<K,V> parent;
这是Entry的结构,是一个类似链表节点的树形结构;
3、TreeSet和TreeMap的底层都是树形结构是一个二叉查找树,并且是一个红黑平衡树,实现方法:
fixAfterInsertion(e);