插入(put)

源码

  1. public V put(K key, V value) {
  2. Entry<K,V> t = root;
  3. //如果是第一次put, 根节点肯定为null
  4. if (t == null) {
  5. compare(key, key); // type (and possibly null) check
  6. root = new Entry<>(key, value, null);
  7. size = 1;
  8. modCount++;
  9. return null;
  10. }
  11. int cmp;
  12. Entry<K,V> parent;
  13. // split comparator and comparable paths
  14. Comparator<? super K> cpr = comparator;
  15. if (cpr != null) {
  16. do {
  17. parent = t;
  18. cmp = cpr.compare(key, t.key);
  19. if (cmp < 0)
  20. t = t.left;
  21. else if (cmp > 0)
  22. t = t.right;
  23. else
  24. return t.setValue(value);
  25. } while (t != null);
  26. }
  27. else {
  28. if (key == null)
  29. throw new NullPointerException();
  30. @SuppressWarnings("unchecked")
  31. Comparable<? super K> k = (Comparable<? super K>) key;
  32. do {
  33. parent = t;
  34. cmp = k.compareTo(t.key);
  35. if (cmp < 0)
  36. t = t.left;
  37. else if (cmp > 0)
  38. t = t.right;
  39. else
  40. return t.setValue(value);
  41. } while (t != null);
  42. }
  43. Entry<K,V> e = new Entry<>(key, value, parent);
  44. if (cmp < 0)
  45. parent.left = e;
  46. else
  47. parent.right = e;
  48. fixAfterInsertion(e);
  49. size++;
  50. modCount++;
  51. return null;
  52. }

记录点

  • 在第一次插入的时候为何进行compare操作? ```java public V put(K key, V value) {

    1. Entry<K,V> t = root;
    2. if (t == null) {
    3. //第一次put,为啥还要进行compare呢?原因是需要校验K是否是可比较的Type(即是否实现了Comparable)
    4. //如果不是,则会报错(转换异常ClassCastException)
    5. compare(key, key); // type (and possibly null) check
    6. //...
    7. }
    8. //...

    }

    @SuppressWarnings(“unchecked”) final int compare(Object k1, Object k2) {

    1. return comparator==null ? ((Comparable<? super K>)k1).compareTo((K)k2) : comparator.compare((K)k1, (K)k2);

    }

  1. > 另外,idea里面会有相应提示,如图:
  2. ![image.png](https://cdn.nlark.com/yuque/0/2021/png/876305/1612517483848-6202933f-0a9b-4607-98e4-c6a068dc2d7f.png#align=left&display=inline&height=109&margin=%5Bobject%20Object%5D&name=image.png&originHeight=109&originWidth=781&size=16185&status=done&style=none&width=781)
  3. > 说明:有序的集合构造使用了不可比较的元素,也就是说:作为TreeMapkey的泛型类,需要实现Comparable
  4. - 如果未指定比较器,当非第一次put的时候,put null,此时会抛 NPE
  5. ```java
  6. public V put(K key, V value) {
  7. //...
  8. Comparator<? super K> cpr = comparator;
  9. if (cpr != null) {
  10. //...
  11. }
  12. else {
  13. if (key == null)
  14. throw new NullPointerException();
  15. @SuppressWarnings("unchecked")
  16. Comparable<? super K> k = (Comparable<? super K>) key;
  17. //...
  18. }

则说明:如果比较器未对 null值做特殊处理,就会报NPE;也就是说,如果不能保证key不为null,比较器必须对null做处理!

删除(remove)

源码

  1. public V remove(Object key) {
  2. Entry<K,V> p = getEntry(key);
  3. if (p == null)
  4. return null;
  5. V oldValue = p.value;
  6. deleteEntry(p);
  7. return oldValue;
  8. }
  9. /**
  10. * Delete node p, and then rebalance the tree.
  11. */
  12. private void deleteEntry(Entry<K,V> p) {
  13. modCount++;
  14. size--;
  15. // If strictly internal, copy successor's element to p and then make p
  16. // point to successor.
  17. if (p.left != null && p.right != null) {
  18. Entry<K,V> s = successor(p);
  19. p.key = s.key;
  20. p.value = s.value;
  21. p = s;
  22. } // p has 2 children
  23. // Start fixup at replacement node, if it exists.
  24. Entry<K,V> replacement = (p.left != null ? p.left : p.right);
  25. if (replacement != null) {
  26. // Link replacement to parent
  27. replacement.parent = p.parent;
  28. if (p.parent == null)
  29. root = replacement;
  30. else if (p == p.parent.left)
  31. p.parent.left = replacement;
  32. else
  33. p.parent.right = replacement;
  34. // Null out links so they are OK to use by fixAfterDeletion.
  35. p.left = p.right = p.parent = null;
  36. // Fix replacement
  37. if (p.color == BLACK)
  38. fixAfterDeletion(replacement);
  39. } else if (p.parent == null) { // return if we are the only node.
  40. root = null;
  41. } else { // No children. Use self as phantom replacement and unlink.
  42. if (p.color == BLACK)
  43. fixAfterDeletion(p);
  44. if (p.parent != null) {
  45. if (p == p.parent.left)
  46. p.parent.left = null;
  47. else if (p == p.parent.right)
  48. p.parent.right = null;
  49. p.parent = null;
  50. }
  51. }
  52. }

查询(get)

源码

  1. public V get(Object key) {
  2. Entry<K,V> p = getEntry(key);
  3. return (p==null ? null : p.value);
  4. }
  5. final Entry<K,V> getEntry(Object key) {
  6. // Offload comparator-based version for sake of performance
  7. if (comparator != null)
  8. return getEntryUsingComparator(key);
  9. if (key == null)
  10. throw new NullPointerException();
  11. @SuppressWarnings("unchecked")
  12. Comparable<? super K> k = (Comparable<? super K>) key;
  13. Entry<K,V> p = root;
  14. while (p != null) {
  15. int cmp = k.compareTo(p.key);
  16. if (cmp < 0)
  17. p = p.left;
  18. else if (cmp > 0)
  19. p = p.right;
  20. else
  21. return p;
  22. }
  23. return null;
  24. }

总结:

  1. 数据结构:红黑树,使用红黑树保证树的平衡,同时能够把查找时间控制在 O(logn);
  2. put和remove操作会触发调整红黑树