您的位置:首页 > 编程语言 > Java开发

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

2016-09-18 12:05 363 查看

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)相同点:

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

(2)不同点:

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

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

HashMap和Hashtable的比较

(1)相同点:

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

(2)不同点:

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

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

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

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

HashMap和TreeMap的比较

(1)相同点:

都不是线程安全类

(2)不同点:

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

HashMap采用数组+链表的方式进行存储,因此会在元素增多时扩展数组的长度;TreeMap直接采用红黑树进行存储,因此在插入或删除元素时要对红黑树的结构进行调整
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息