分析LinkedHashSet
Set<String> set = new LinkedHashSet<>(); // extends HashSet<E>,implements Set<E>
-----------之后的执行过程-------------
public LinkedHashSet() {
super(16, .75f, true);
}
//调用了父类的构造方法
HashSet(int initialCapacity, float loadFactor, boolean dummy) {
map = new LinkedHashMap<>(initialCapacity, loadFactor);
}
//调用了LinkedHashMap的构造方法
public LinkedHashMap(int initialCapacity, float loadFactor) {
super(initialCapacity, loadFactor);
accessOrder = false;
}
//又调用到了HashMap的构造方法
public HashMap(int initialCapacity, float loadFactor) {
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal initial capacity: " +
initialCapacity);
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal load factor: " +
loadFactor);
this.loadFactor = loadFactor;
this.threshold = tableSizeFor(initialCapacity);
}
//
static final int tableSizeFor(int cap) {
int n = cap - 1;
n |= n >>> 1;
n |= n >>> 2;
n |= n >>> 4;
n |= n >>> 8;
n |= n >>> 16;
return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
}
//执行完tableSizeFor以后threshold的值为16,即初始容量与threshold都为16
//其中LinkedHashSet的add方法使用的是HashSet的add方法,本质上是HashMap
结论:通过上述分析知道LinkedHashSet使用的是父类的构造方法,而HashSet的map对象是LinkedHashMap类型的,before和after来维护LinkedHasMap元素的插入顺序,所以区别就是LinkedHashSet是有顺序的,会维护数据插入的顺序。但是HashMap是无序的,LinkedHashMap实际上也是类似于双向链表的类型,LinkedHashMap用额外的链表保证插入顺序
分析TreeSet
Set<String> set = new TreeSet<>(); // extends AbstractSet<E>
-----------之后的执行过程-------------
public TreeSet() {
this(new TreeMap<E,Object>());
}
TreeSet(NavigableMap<E,Object> m) {
this.m = m;
}
//NavigableMap接收了一个TreeMap对象,其中m就是NavigableMap的接口对象
set.add("jack");
public boolean add(E e) {
return m.put(e, PRESENT)==null;
}
TreeMap的put方法,用的是红黑树存储的
public V put(K key, V value) {
Entry<K,V> t = root; // Entry<K,V> root
if (t == null) { // 第一次添加时root为空,因此直接让root指向新节点
compare(key, key); // type (and possibly null) check
root = new Entry<>(key, value, null);
size = 1;
modCount++;
return null;
}
int cmp;
Entry<K,V> parent;
// split comparator and comparable paths
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 = (Comparable<? super K>) 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(e);
size++;
modCount++;
return null;
}
由于用TreeMap的put方法时,会根据传进来的比较器来看插入的顺序如何保证,如果为空,则按照默认的比较器进行判断顺序
Map集合
- HashMap
- Hashtable
- LinkedHashMap
- TreeMap
- Properties
分析HashMap
Map<Integer, String> map = new HashMap<>();
public HashMap() {
this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
}
//余下的过程和HashSet的一样,就不展示了~~
分析Hashtable
值不能为空,Key也不能为空
注意this(11, 0.75f);初始容量为11,threshold为8,当加入第九个元素时就会对table进行扩容,扩容机制为2n+1
Map<Integer, String> map = new Hashtable<>(); // implements Map<K,V>
-----------之后的执行过程-------------
public Hashtable() {
this(11, 0.75f);
}
public Hashtable(int initialCapacity, float loadFactor) {
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal Load: "+loadFactor);
if (initialCapacity==0) // 如果初始给的0,那么就给1
initialCapacity = 1;
this.loadFactor = loadFactor;
table = new Entry<?,?>[initialCapacity];
threshold = (int)Math.min(initialCapacity * loadFactor, MAX_ARRAY_SIZE + 1); // threshold给了8
}
map.put(1, "jack");
-----------之后的执行过程-------------
public synchronized V put(K key, V value) {
// Make sure the value is not null
if (value == null) {
throw new NullPointerException();
}
// Makes sure the key is not already in the hashtable.
Entry<?,?> tab[] = table;
int hash = key.hashCode(); // 检查了key不能为空
int index = (hash & 0x7FFFFFFF) % tab.length;
@SuppressWarnings("unchecked")
Entry<K,V> entry = (Entry<K,V>)tab[index];
for(; entry != null ; entry = entry.next) {
if ((entry.hash == hash) && entry.key.equals(key)) {
V old = entry.value;
entry.value = value;
return old;
}
}
addEntry(hash, key, value, index);
return null;
}
private void addEntry(int hash, K key, V value, int index) {
modCount++;
Entry<?,?> tab[] = table;
if (count >= threshold) {
// Rehash the table if the threshold is exceeded
rehash();
tab = table;
hash = key.hashCode();
index = (hash & 0x7FFFFFFF) % tab.length;
}
// Creates the new entry.
@SuppressWarnings("unchecked")
Entry<K,V> e = (Entry<K,V>) tab[index];
tab[index] = new Entry<>(hash, key, value, e);
count++;
}
分析LinkedHashMap
Map<String, String> map = new LinkedHashMap<>();
-----------之后的执行过程-------------
public LinkedHashMap() {
super(); // 调用了父类的构造方法
accessOrder = false;
}
public HashMap() {
this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
}
而执行LinkedHashMap的put方法时,仍旧时调用HashMap的put方法,因而没有什么不同,但是内部仍然维护了链表结构,保证是有序的(双向链表)
分析TreeMap
Map<String, String> map = new TreeMap<>();
-----------之后的执行过程-------------
public TreeMap() {
comparator = null; // Comparator<? super K> comparator;
}
map.put("1", "jack");
-----------之后的执行过程-------------
public V put(K key, V value) {
Entry<K,V> t = root;
if (t == null) {
compare(key, key); // type (and possibly null) check
root = new Entry<>(key, value, null);
size = 1;
modCount++;
return null;
}
int cmp;
Entry<K,V> parent;
// split comparator and comparable paths
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 = (Comparable<? super K>) 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(e);
size++;
modCount++;
return null;
}
与TreeSet基本类似
Properties
Properties类继承自Hashtable类并且实现了Map接口,也是使用一种键值对的形式来保存数据。因此key和value都不能为空
Properties 还可以用于从 xxx.properties文件中,加载数据到Properties类对象,并进行读取和修改(常用)
Map map = new Properties(); // Properties extends Hashtable<Object,Object>