HashMap
- 用于私聊服务器存副线程,并将该线程转发给聊天对象
-
hashmap 他是一个允许空的entry(key-value)键值对,,以键值对存储数据。
-
是一个非线程安全的容器,如果多个线程同时影响了 HashMap ,并且至少一个线程修改了 HashMap 的结构,那么必须对 HashMap 进行同步操作
可以使用
Collections.synchronizedMap(new HashMap)
来创建一个线程安全的 Map。*想构造安全的map可以使用 ConcurrentHashMap。 -
hashmap是无序的,无法保证内部存储的键值对的有序性。
-
hashmap底层数据结构是数组+链表的集合体,数组在 HashMap 中又被称为
桶(bucket)
。遍历 HashMap 需要的时间损耗为 HashMap 实例桶的数量 + (key - value 映射) 的数量 -
hashmap 有两个重要因素,初始容量和负载因子,初始容量指的就是 hash 表桶的数量,负载因子是一种衡量哈希表填充程度的标准,当哈希表中存在足够数量的 entry,以至于超过了负载因子和当前容量,这个哈希表会进行 rehash 操作,内部的数据结构重新 rebuilt。
-
HashMap 会导致除了迭代器本身的 remove 外,外部 remove 方法都可能会导致 fail-fast 机制,因此尽量要用迭代器自己的 remove 方法。如果在迭代器创建的过程中修改了 map 的结构,就会抛出 ConcurrentModificationException 异常。
-
迭代器
-
iterator
hashmap与hashTable
相同
HashMap 和 HashTable 都是基于哈希表实现的,其内部每个元素都是 key-value
键值对,都实现了 Map、Cloneable、Serializable 接口。
不同
-
父类不同
- hashmap继承了AbstractMap类
- HashTable 继承了
Dictionary
类
-
空值不同
- HashMap 允许空的 key 和 value 值,把 Null key 当做普通的 key 对待
- HashTable 不允许空的 key 和 value 值,不允许 null key 重复,如果重复则会抛出空指针异常
-
线程安全
- HashMap 不是线程安全的,如果多个外部操作同时修改 HashMap 的数据结构比如 add 或者是 delete,必须进行同步操作,仅仅对 key 或者 value 的修改不是改变数据结构的操作。可以选择构造线程安全的 Map 比如 Collections.synchronizedMap 或者是 ConcurrentHashMap。
- HashTable 本身就是线程安全的容器。
-
性能不同
- HashMap 进行 put 或者 get 操作,可以达到常数时间的性能
- HashTable 的 put 和 get 操作都是加了
synchronized
锁的,所以效率很差。
-
初始容量不同
- HashMap 的初始长度为16,之后每次扩充变为原来的两倍。创建时,如果给定了容量初始值,那么HashTable 会直接使用你给定的大小,而 HashMap 会将其扩充为2的幂次方大小。
- HashTable 的初始长度是11,之后每次扩充容量变为之前的 2n+1(n为上一次的长度)
hashmap与hashSet
HashSet 继承于 AbstractSet 接口,实现了 Set、Cloneable,、java.io.Serializable 接口。
HashSet 不允许集合中出现重复的值。
HashSet 底层其实就是 HashMap,所以对 HashSet 的操作其实就是对 HashMap 的操作。
所以 HashSet 也不保证集合的顺序。
hashMap底层结构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ncU7x9vH-1629378660323)(D:\笔记\JAVA学习笔记\14笔记\image-20210819145626779.png)]
AbstractMap类
- 该类是Map接口的主要实现。
- 为不修改map,必须继承该类并且提供entrySet方法即可。返回的集合将在AbstractSet 之上实现。这个set不应该支持 add 或者 remove 方法,并且它的迭代器也不支持 remove 方法。
- 为了实现可修改的 map必须**重写这个类的 put 方法(否则就会抛出UnsupportedOperationException),**并且 entrySet.iterator() 返回的 iterator 必须实现 remove() 方法。、
Map接口
-
定义了 key-value 键值对的标准,一个对象支持 key-value 存储。Map不能包含重复的 key,每个键最多映射一个值
-
代替了Dictionary 类,Dictionary是一个抽象类而不是接口
-
提供了三个集合的构造器,map的顺序定义为map映射集合上的迭代器返回其元素的顺序
-
map实现:
TreeMap类,保证了map的有序性
HashMap,则没有保证。
LinkedHashMap继承HashMap,元素既可以按照它们插入图的顺序排序,也可以按它们最后一次被访问的顺序排序。
内部类和接口
Node接口
-
存储HashMap的实例,实现了Map.Entry接口
-
存储四个属性,
- hash值:final int hash
- 键 :final K key
- 值:V value
- 指向下一个Node节点的Node类型:Node next
-
Map.Entry 是一条条entry 链连接在一起的,所以Node节点也是一条条entry链。
构造一个新的HashMap实例的时候,会把这四个属性值分为传入
Node(int hash, K key, V value, Node next) {this.hash =hash; this.key = key; this.value = value; this.next = next; }
KeySet类
- keySet 类继承于 AbstractSet 抽象类,它是由 HashMap 中的
keyset()
方法来创建 KeySet 实例的,旨在对HashMap 中的key键进行操作 - map.keySet() 其实是返回了一个 Set 接口,KeySet() 是在 Map 接口中进行定义的,不过是被HashMap 进行了实现操作
返回一个set视图,这个视图中包含了map中的key。
public Set<K> keySet() {// keySet 指向的是 AbstractMap 中的 keyset
Set<K> ks = keySet;
if (ks == null) {// 如果 ks 为空,就创建一个 KeySet 对象// 并对 ks 赋值
ks = new KeySet();
keySet = ks;
}
return ks;
}
4.所以KeySet都是对Map中的Key进行操作
Values类
- Values 旨在对
key-value
键值对中的 value 值进行使用,看一下代码示例:
public Collection values() {// values 其实是 AbstractMap 中的 values
Collection vs = values;if (vs == null) {
vs = new Values();
values = vs;
}return vs;
}
- 所有的 values 其实都存储在 AbstractMap 中,而 Values 类其实也是实现了 Map 中的 Values 接口,源码操作方法:
final class Values extends AbstractCollection<V> {
public final int size() { return size; }
public final void clear() { HashMap.this.clear(); }
public final Iterator<V> iterator() { return new ValueIterator(); }
public final boolean contains(Object o) { return containsValue(o); }
public final Spliterator<V> spliterator() {
return new ValueSpliterator<>(HashMap.this, 0, -1, 0, 0);
}
public final void forEach(Consumer<? super V> action) {
Node<K,V>[] tab;
if (action == null)
throw new NullPointerException();
if (size > 0 && (tab = table) != null) {
int mc = modCount;
for (int i = 0; i < tab.length; ++i) {
for (Node<K,V> e = tab[i]; e != null; e = e.next)
action.accept(e.value);
}
if (modCount != mc)
throw new ConcurrentModificationException();
}
}
}
EntrySet内部类
- 对
key-value
键值对进行操作的内部类
// 返回一个 set 视图,此视图包含了 map 中的key-value 键值对
public Set<> entrySet() {
Set> es;return (es = entrySet) == null ? (entrySet = new EntrySet()) : es;
}