浅析Java集合类源码(三)--- TreeSet, TreeMap 及 常见集合类的比较

6.TreeSet

TreeSet继承AbstractSet,实现NavigableSet,Cloneable,Serializable接口,TreeSet不是线程安全类。

public class TreeSet<E> extends AbstractSet<E>
    implements NavigableSet<E>, Cloneable, java.io.Serializable

和HashSet类似,TreeSet使用TreeMap的数据结构来保存元素。键Key是需要保存的元素,值Value是一个静态对象PRESENT

private static final Object PRESENT = new Object();

(1)初始化

由于TreeSet使用TreeMap对其元素进行保存,因此调用TreeMap的初始化方法

private transient NavigableMap<E,Object> m;
public TreeSet() {
    this(new TreeMap<E,Object>());
}
TreeSet(NavigableMap<E,Object> m) {
    this.m = m;
}

(2)添加

TreeSet调用TreeMap的put()方法来添加元素

public boolean add(E e) {
    return m.put(e, PRESENT)==null;
}

(3)删除

TreeSet调用TreeMap的remove()方法来移除元素

public boolean remove(Object o) {
    return m.remove(o)==PRESENT;
}


7.TreeMap

TreeMap继承AbstractMap,实现NavigableMap,Cloneable,Serializable接口。TreeMap不是线程安全类。

public class TreeMap<K,V>
    extends AbstractMap<K,V>
    implements NavigableMap<K,V>, Cloneable, java.io.Serializable

TreeMap使用Entry数据结构保存键Key和值Value。并使用数据结构红黑树(Red-Black Tree)结构保存节点Entry。

static final class Entry<K,V> implements Map.Entry<K,V> {
    K key;
    V value;
    Entry<K,V> left;
    Entry<K,V> right;
    Entry<K,V> parent;
    boolean color = BLACK;
}

因为TreeMap使用红黑树的数据结构对Entry进行保存,这就要Entry的key是可以比较的。因此,键key的类必须实现Comparable接口或Comparator接口。


(1)查找

通过get(Object key)方法来查找元素,会调用getEntry(Object key)方法来查找元素。

public V get(Object key) {
    Entry<K,V> p = getEntry(key);
    return (p==null ? null : p.value);
}

getEntry先判断Key是否实现Comparator接口,若实现,则调用getEntryUsingComparator方法,否则使用Comparable的compareTo方法进行Key的比较。两种方法的比较都差不多,当Key比节点小,往左走;当Key比节点大,往右走;相等则返回。

Comparable<? super K> k = (Comparable<? super K>) key;
Entry<K,V> p = root;
while (p != null) {
    int cmp = k.compareTo(p.key);
    if (cmp < 0)
        p = p.left;
    else if (cmp > 0)
        p = p.right;
    else
        return p;
}
return null;

(2)添加

添加和查找一样,先判断Key是否实现Comparator接口,若实现,使用compare()方法进行比较。

parent = t;
cmp = cpr.compare(key, t.key);

否则,使用Comparable的compareTo方法进行比较。

parent = t;
cmp = k.compareTo(t.key);

若Key节点比当前节点小,往左走。若比当前节点大,往右走。若相同,则更改节点的值。否则,直到达底部空节点,并插入新节点。

do {
    parent = t;
    cmp = k.compareTo(t.key);
    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, parent);
if (cmp < 0)
    parent.left = e;
else
    parent.right = e;

接下来调用fixAfterInsertion方法,按照CLR的Introduction to Algorithms的红黑树结构进行调整。


(3)删除

remove方法先调用查找的getEntry方法查找到需要删除的节点,然后调用deleteEntry方法删除该节点

public V remove(Object key) {
    Entry<K,V> p = getEntry(key);
    if (p == null)
        return null;

    V oldValue = p.value;
    deleteEntry(p);
    return oldValue;
}

deleteEntry方法先调用successor方法找到被删除节点p的后继节点s,然后将的key和value都设为s节点的key和value,再将s节点赋值给p节点,然后调整p节点

Entry<K,V> s = successor(p);
p.key = s.key;
p.value = s.value;
p = s;

接下来调用fixAfterDeletion方法,按照CLR的Introduction to Algorithms的红黑树结构进行调整。



常见集合类的比较:


Vector和ArrayList的比较

(1)相同点:

  1. Vector和ArrayList均是采用数组结构保存元素

(2)不同点:

  1. Vector在增长时增长为原数组的2倍;ArrayList在增长时增长为原数组的1.5倍;

  2. Vector是线程安全类;ArrayList不是线程安全类



HashMap和Hashtable的比较

(1)相同点:

  1. 采用索引数组+链表的方式对元素进行存储

(2)不同点:

  1. Hashtable扩展时增长为(当前数组*2+1);HashMap扩展时增长为(当前数组*2);

  2. 插入元素时,Hashtable插入至链表的头部;HashMap插入至链表的尾部

  3. Hashtable的hash值计算:(元素hash值 % 当前数组容量);HashMap的hash值计算:(元素hash值 & (当前数组容量 -1))

  4. Hashtable是线程安全类;HashMap不是线程安全类



HashMap和TreeMap的比较

(1)相同点:

  1. 都不是线程安全类

(2)不同点:

  1. HashMap是根据key的hash值来计算key在Hash表中的位置,因此Key的类型应该重写hashCode方法,并且应该重写equals方法,以便对key本身是否相同进行比较;TreeMap是根据key本身的大小进行比较,因此应该实现Comparator接口并重写compare方法,或实现Comparable接口并重写compareTo方法

  2. HashMap采用数组+链表的方式进行存储,因此会在元素增多时扩展数组的长度;TreeMap直接采用红黑树进行存储,因此在插入或删除元素时要对红黑树的结构进行调整

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值