标签:之间 was bin factor jdb 填充 creates 原理 set
Map 与 Collection 并列存在,用于保存具有映射关系的数据:key-valueMap 中的 key 和 value 都可以是任何引用类型的数据 // 常用 String 作为 Map 的“键”Map 中的 key 用 Set 来存放,不允许重复,即同一个 Map 对象所对应的类,须重写hashCode() 和 equals()key 和 value 之间存在单向一对一关系,即通过指定的 key 总能找到唯一的、确定的 valueMap<I> 的常用实现类:HashMap、TreeMap、LinkedHashMap 和 Properties。其中,HashMap 是 Map 接口使用频率最高的实现类
HashMap 线程不安全,效率高;可以存储 null 值的 key 和 value;JDK 7实现方式是 {数组 + 链表},JDK 8实现方式是 {数组 + 链表 + 红黑树}LinkedHashMap 是通过 {双向链表} 和 {散列表} 这两种数据结构组合实现的。LinkedHashMap 中的“Linked”实际上是指的是 {双向链表},并非指 {用链表法解决散列冲突}TreeMap 保证按照添加的 key-value进行排序(按key排序),实现有序遍历;底层使用 {红黑树}Hashtable 作为古老的实现类;线程安全,效率低;不能存储 null 值的 key 和 valueProperties 常用来处理配置文件;key 和 value 都是 String 类型key-value 的特点
Set 存储所有的 keyCollection 存储所有的 valuekey-value 构成了一个 Entry 对象HashMap 是 Map<I> 使用频率最高的实现类null键 和 null值,与 HashSet 一样,不保证映射的顺序key 构成的集合是 Set:无序的、不可重复的。所以,key 所在的类要重写:equals() 和 hashCode()value 构成的集合是 Collection:无序的、可以重复的。所以,value 所在的类 要重写:equals()key-value 构成 一个entry,所有的 entry 构成的集合是Set:无序的、不可重复的equals() 方法返回 true, hashCode 值也相等equals() 方法返回 true
public class HashMap<K,V>
extends AbstractMap<K,V>
implements Map<K,V>, Cloneable, Serializable
{
// The default initial capacity - MUST be a power of two.
static final int DEFAULT_INITIAL_CAPACITY = 16;
// The load factor used when none specified in constructor.
static final float DEFAULT_LOAD_FACTOR = 0.75f;
transient Entry<K,V>[] table;
// The number of key-value mappings contained in this map.
transient int size;
// The next size value at which to resize (capacity * load factor).
int threshold;
final float loadFactor;
// Constructs an empty HashMap with the default initial capacity (16)
// and the default load factor (0.75).
public HashMap() {
this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);
}
// Constructs an empty HashMap with the specified initial capacity
// and the default load factor (0.75).
public HashMap(int initialCapacity) {
this(initialCapacity, DEFAULT_LOAD_FACTOR);
}
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);
// Find a power of 2 >= initialCapacity
int capacity = 1;
while (capacity < initialCapacity)
capacity <<= 1;
this.loadFactor = loadFactor;
threshold = (int)Math.min(capacity * loadFactor, MAXIMUM_CAPACITY + 1);
table = new Entry[capacity];
useAltHashing = sun.misc.VM.isBooted() &&
(capacity >= Holder.ALTERNATIVE_HASHING_THRESHOLD);
init();
}
public V put(K key, V value) {
if (key == null)
return putForNullKey(value);
int hash = hash(key);
int i = indexFor(hash, table.length);
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
modCount++;
addEntry(hash, key, value, i);
return null;
}
final int hash(Object k) {
int h = 0;
if (useAltHashing) {
if (k instanceof String) {
return sun.misc.Hashing.stringHash32((String) k);
}
h = hashSeed;
}
h ^= k.hashCode();
// This function ensures that hashCodes that differ only by
// constant multiples at each bit position have a bounded
// number of collisions (approximately 8 at default load factor).
h ^= (h >>> 20) ^ (h >>> 12);
return h ^ (h >>> 7) ^ (h >>> 4);
}
static int indexFor(int h, int length) {
return h & (length-1); // A % B = A & (B-1)
}
void addEntry(int hash, K key, V value, int bucketIndex) {
if ((size >= threshold) && (null != table[bucketIndex])) {
resize(2 * table.length);
hash = (null != key) ? hash(key) : 0;
bucketIndex = indexFor(hash, table.length);
}
createEntry(hash, key, value, bucketIndex);
}
void createEntry(int hash, K key, V value, int bucketIndex) {
Entry<K,V> e = table[bucketIndex];
table[bucketIndex] = new Entry<>(hash, key, value, e);
size++;
}
static class Entry<K,V> implements Map.Entry<K,V> {
final K key;
V value;
Entry<K,V> next;
int hash;
// Creates new entry.
Entry(int h, K k, V v, Entry<K,V> n) {
value = v;
next = n;
key = k;
hash = h;
}
}
void resize(int newCapacity) {
Entry[] oldTable = table;
int oldCapacity = oldTable.length;
if (oldCapacity == MAXIMUM_CAPACITY) {
threshold = Integer.MAX_VALUE;
return;
}
Entry[] newTable = new Entry[newCapacity];
boolean oldAltHashing = useAltHashing;
useAltHashing |= sun.misc.VM.isBooted() &&
(newCapacity >= Holder.ALTERNATIVE_HASHING_THRESHOLD);
boolean rehash = oldAltHashing ^ useAltHashing;
transfer(newTable, rehash);
table = newTable;
threshold = (int)Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1);
}
// Transfers all entries from current table to newTable.
void transfer(Entry[] newTable, boolean rehash) {
int newCapacity = newTable.length;
for (Entry<K,V> e : table) {
while(null != e) {
Entry<K,V> next = e.next;
if (rehash) {
e.hash = null == e.key ? 0 : hash(e.key);
}
int i = indexFor(e.hash, newCapacity);
e.next = newTable[i];
newTable[i] = e;
e = next;
}
}
}
}
public class HashMap<K,V> extends AbstractMap<K,V>
implements Map<K,V>, Cloneable, Serializable {
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
static final float DEFAULT_LOAD_FACTOR = 0.75f;
static final int TREEIFY_THRESHOLD = 8;
static final int UNTREEIFY_THRESHOLD = 6;
static final int MIN_TREEIFY_CAPACITY = 64;
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
V value;
Node<K,V> next;
Node(int hash, K key, V value, Node<K,V> next) {
this.hash = hash;
this.key = key;
this.value = value;
this.next = next;
}
}
static final class TreeNode<K,V> extends LinkedHashMap.Entry<K,V> {
TreeNode<K,V> parent; // red-black tree links
TreeNode<K,V> left;
TreeNode<K,V> right;
TreeNode<K,V> prev; // needed to unlink next upon deletion
boolean red;
TreeNode(int hash, K key, V val, Node<K,V> next) {
super(hash, key, val, next);
}
}
public HashMap() {
this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
}
public HashMap(int initialCapacity) {
this(initialCapacity, DEFAULT_LOAD_FACTOR);
}
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);
}
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
else {
Node<K,V> e; K k;
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
else if (p instanceof TreeNode)
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else {
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash); // 树化
break;
}
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
}
}
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue;
}
}
++modCount;
if (++size > threshold)
resize();
afterNodeInsertion(evict);
return null;
}
final Node<K,V>[] resize() {
Node<K,V>[] oldTab = table;
int oldCap = (oldTab == null) ? 0 : oldTab.length;
int oldThr = threshold;
int newCap, newThr = 0;
if (oldCap > 0) {
if (oldCap >= MAXIMUM_CAPACITY) {
threshold = Integer.MAX_VALUE;
return oldTab;
}
else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
oldCap >= DEFAULT_INITIAL_CAPACITY)
newThr = oldThr << 1; // double threshold
}
else if (oldThr > 0) // initial capacity was placed in threshold
newCap = oldThr;
else { // zero initial threshold signifies using defaults
newCap = DEFAULT_INITIAL_CAPACITY;
newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
}
if (newThr == 0) {
float ft = (float)newCap * loadFactor;
newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
(int)ft : Integer.MAX_VALUE);
}
threshold = newThr;
@SuppressWarnings({"rawtypes","unchecked"})
Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
table = newTab;
if (oldTab != null) {
for (int j = 0; j < oldCap; ++j) {
Node<K,V> e;
if ((e = oldTab[j]) != null) {
oldTab[j] = null;
if (e.next == null)
newTab[e.hash & (newCap - 1)] = e;
else if (e instanceof TreeNode)
((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
else { // preserve order
Node<K,V> loHead = null, loTail = null;
Node<K,V> hiHead = null, hiTail = null;
Node<K,V> next;
do {
next = e.next;
if ((e.hash & oldCap) == 0) {
if (loTail == null)
loHead = e;
else
loTail.next = e;
loTail = e;
}
else {
if (hiTail == null)
hiHead = e;
else
hiTail.next = e;
hiTail = e;
}
} while ((e = next) != null);
if (loTail != null) {
loTail.next = null;
newTab[j] = loHead;
}
if (hiTail != null) {
hiTail.next = null;
newTab[j + oldCap] = hiHead;
}
}
}
}
}
return newTab;
}
public V remove(Object key) {
Node<K,V> e;
return (e = removeNode(hash(key), key, null, false, true)) == null ?
null : e.value;
}
final Node<K,V> removeNode(int hash, Object key, Object value,
boolean matchValue, boolean movable) {
Node<K,V>[] tab; Node<K,V> p; int n, index;
if ((tab = table) != null && (n = tab.length) > 0 &&
(p = tab[index = (n - 1) & hash]) != null) {
Node<K,V> node = null, e; K k; V v;
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
node = p;
else if ((e = p.next) != null) {
if (p instanceof TreeNode)
node = ((TreeNode<K,V>)p).getTreeNode(hash, key);
else {
do {
if (e.hash == hash &&
((k = e.key) == key ||
(key != null && key.equals(k)))) {
node = e;
break;
}
p = e;
} while ((e = e.next) != null);
}
}
if (node != null && (!matchValue || (v = node.value) == value ||
(value != null && value.equals(v)))) {
if (node instanceof TreeNode)
((TreeNode<K,V>)node).removeTreeNode(this, tab, movable);
else if (node == p)
tab[index] = node.next;
else
p.next = node.next;
++modCount;
--size;
afterNodeRemoval(node);
return node;
}
}
return null;
}
}
DEFAULT_INITIAL_CAPACITY:HashMap 的默认容量,16MAXIMUM_CAPACITY:HashMap 的最大支持容量,2^30DEFAULT_LOAD_FACTOR:HashMap 的默认加载因子TREEIFY_THRESHOLD:Bucket 中链表长度大于该默认值8,转化为红黑树UNTREEIFY_THRESHOLD:Bucket 中红黑树存储的 Node 小于该默认值6,转化为链表MIN_TREEIFY_CAPACITY:桶中的 Node 被树化时最小的 hash表容量64(当桶中 Node 的数量大到需要变红黑树时,若 hash 表容量小于 MIN_TREEIFY_CAPACITY 时,此时应执行 resize 扩容操作这个 MIN_TREEIFY_CAPACITY 的值至少是 TREEIFY_THRESHOLD 的 4 倍)table:存储元素的数组,总是 2 的 n 次幂entrySet:存储具体元素的集size:HashMap 中存储的键值对的数量modCount:HashMap 扩容和结构改变的次数threshold:扩容的临界值,= 容量 * 填充因子loadFactor:填充因子HashMap map = new HashMap() 在实例化以后,底层创建了长度是16的一维数组 Entry[] table。当执行 put(key1,value1) 操作:
hashCode() 计算 key1 哈希值,此哈希值再经过散列函数 hash() 计算以后,得到在 Entry[] 中的存放位置key1-value1 添加成功 → 【情况1】key1-value1 添加成功 → 【情况2】key2-value2 的哈希值相同,继续比较:调用 key1 所在类的equals(key2) 方法,比较:
equals() 返回 false:此时 key1-value1 添加成功 → 【情况3】equals() 返回 true:使用 value1 替换 value2关于 HashMap 的扩容:
resize() 要做的事负载因子值的大小,对HashMap有什么影响?
new HashMap():底层没有创建一个长度为 16 的数组Node[] 而非 Entry[]。每一个 Node 对象可以带 一个引用变量 next,用于指向下一个元素,因此,在一个桶中,就有可能 生成一个 Node 链。也可能是一个一个 TreeNode 对象,每一个 TreeNode 对象可以有两个叶子结点 left 和 right,因此,在一个桶中,就有可能生成一个 TreeNode 树。而新添加的元素作为链表的 last,或树的叶子结点put() 时,底层创建长度为 16 的数组链表形式存在的数据个数 > 8 && 当前数组的长度 > 64 时,此索引位置上的所数据改为使用红黑树存储resize() 时判断树的结点个数低于 6 个,会由红黑树转变成链表,当 HashMap 中结点数为 6 时,链表的平均查找 6/2=3。当 HashMap 中结点多于 8 个时,会由链表转化成红黑树,红黑树的平均查找为 log(8)=3。如果继续使用链表,平均查找长度为 8/2=4,这才有转换为树的必要return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16) 的设计:
equals() 必须重写 hashcode() 的重要原因hashcode ^ (hashcode >>> 16),这一步甚是巧妙,是将 高16位 移到 低16位,这样计算出来的整型值将“具有”高位和低位的性质A % B = A & (B - 1),所以,p = tab[i = ((tab = resize()).length - 1) & hash],可以看出这里本质上是使用了「除留余数法」映射关系存储到 HashMap 中会存储 key 的 hash 值,这样就不用在每次查找时重新计算每一个 Entry 或 Node(TreeNode) 的 hash 值了,因此如果已经 put 到 Map 中的映射关系,再修改 key 的属性,而这个属性又参与 hashcode 值的计算,最终会导致匹配不上。
LinkedHashMap 是 HashMap 的子类HashMap 存储结构的基础上,使用了一对双向链表来记录添加元素的顺序LinkedHashSet 类似,LinkedHashMap 可以维护 Map 的迭代顺序:迭代顺序与 Key-Value 对的插入顺序一致
因为我们的散列表是通过链表法解决散列冲突的,所以每个 Entry 会在两条链中。一个链是刚刚我们提到的双向链表,另一个链是散列表中的拉链。前驱和后继指针是为了将 Entry 串在双向链表中,hnext 指针是为了将结点串在散列表的拉链中。
JDK 8
public class LinkedHashMap<K,V>
extends HashMap<K,V>
implements Map<K,V>
{
transient LinkedHashMap.Entry<K,V> head;
transient LinkedHashMap.Entry<K,V> tail;
final boolean accessOrder;
public LinkedHashMap() {
super();
accessOrder = false;
}
public LinkedHashMap(int initialCapacity) {
super(initialCapacity);
accessOrder = false;
}
public LinkedHashMap(int initialCapacity, float loadFactor) {
super(initialCapacity, loadFactor);
accessOrder = false;
}
Node<K,V> newNode(int hash, K key, V value, Node<K,V> e) {
LinkedHashMap.Entry<K,V> p =
new LinkedHashMap.Entry<K,V>(hash, key, value, e);
linkNodeLast(p);
return p;
}
private void linkNodeLast(LinkedHashMap.Entry<K,V> p) {
LinkedHashMap.Entry<K,V> last = tail;
tail = p;
if (last == null)
head = p;
else {
p.before = last;
last.after = p;
}
}
}

public void test() {
Map map = new HashMap();
map.put("1001", "白世珠");
map.put("1002", "金敏喜");
map.put("1003", "刘心悠");
// 遍历所有key。但要注意!keySet 既不是 HashSet,也不
// 是 TreeSet,而是实现了 Set<I> 的某个其他类的对象
Set set = map.keySet();
Iterator it = set.iterator();
while(it.hasNext())
System.out.println(it.next());
// 遍历所有 value
Collection c = map.values();
for(Object o : c)
System.out.println(o);
// 遍历 key - value
Set entrySet = map.entrySet();
Iterator iterator = entrySet.iterator();
while(iterator.hasNext()) {
Map.Entry entry = (Map.Entry)iterator.next();
System.out.println(entry.getKey() + "-" + entry.getValue());
}
// 遍历 key : value
Set keySet = map.keySet();
Iterator i = keySet.iterator();
while(i.hasNext()) {
Object key = i.next();
Object value = map.get(key);
System.out.println(key + ":" + value);
}
}
Key-Value 对时,需要根据 key-value 对进行排序Key-Value 对处于有序状态Comparable 接口,而且所有的 Key 应该是同一个类的对象,否则将会抛出 ClassCastExceptionComparator 对象,该对象负责对 TreeMap 中的所有 key 进行排序。此时不需要 Map 的 Key 实现 Comparable<I>compareTo() 或者 compare() 返回 0Hashtable 是个古老的 Map 实现类,JDK1.0 就提供了。不同于 HashMap, Hashtable 是线程安全的Hashtable 实现原理和 HashMap 相同,功能相同。底层都使用哈希表结构,查询速度快,很多情况下可以互用HashMap 不同,Hashtable 不允许使用 null 作为 key 和 valueHashMap 一样,Hashtable 也不能保证其中 Key-Value 对的顺序Hashtable 判断两个 key 相等、两个 value 相等的标准,与 HashMap 一致setProperty(String key, String value) 和 getProperty(String key)public void test() {
FileInputStream fis = null;
try {
Properties prop = new Properties();
fis = new FileInputStream("jdbc.properties");
prop.load(fis);
System.out.println(prop.getProperty("name"));
System.out.println(prop.getProperty("password"));
} catch (IOException e) {
e.printStackTrace();
} finally {
if(fis != null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
读取项目下的 jdbc.properties 文件下的内容,抛了 FileNotFoundException,而导致相对路径找不到文件的原因是因为当前执行类的工作目录不是当前项目路径。现作出如下修改:


Collections 是一个操作 Set、List 和 Map 等集合的工具类Collections 中提供了一系列静态的方法对集合元素进行排序、查询和修改等操作, 还提供了对集合对象设置不可变、对集合对象实现同步控制等方法void reverse(List<?> list) 反转指定列表中元素的顺序void shuffle(List<?> list) 使用默认随机源对指定列表进行置换<T extends Comparable<? super T>> void sort(List<T> list) 根据元素的自然顺序 对指定列表按升序进行排序<T> void sort(List<T> list, Comparator<? super T> c) 根据指定比较器产生的顺序对指定列表进行排序void swap(List<?> list, int i, int j) 在指定列表的指定位置处交换元素<T extends Object & Comparable<? super T>> T max(Collection<? extends T> coll) 根据元素的自然顺序,返回给定 collection 的最大元素<T> T max(Collection<? extends T> coll, Comparator<? super T> comp) 根据指定比较器产生的顺序,返回给定 collection 的最大元素<T extends Object & Comparable<? super T>> T min(Collection<? extends T> coll) 根据元素的自然顺序返回给定 collection 的最小元素<T> T min(Collection<? extends T> coll, Comparator<? super T> comp) 根据指定比较器产生的顺序,返回给定 collection 的最小元素int frequency(Collection<?> c, Object o) 返回 collection 中满足 (o == null ? e == null : o.equals(e)) 的 e 元素的数量<T> void copy(List<? super T> dest, List<? extends T> src) 将所有元素从一个列表复制到另一个列表。执行此操作后,目标列表中每个已复制元素的索引将等同于源列表中该元素的索引。目标列表的长度至少必须等于源列表。如果目标列表更长一些,也不会影响目标列表中的其余元素<T> boolean replaceAll(List<T> list, T oldVal, T newVal) 使用另一个值替换列表中出现的所有某一指定值
标签:之间 was bin factor jdb 填充 creates 原理 set
原文地址:https://www.cnblogs.com/liujiaqi1101/p/13289284.html