进入官方网站浏览器,网站模板html,做网站比较专业的有哪些公司,室内设计女孩子学难吗1. Map
1.1 HashMap 和 Hashtable 的区别
线程是否安全#xff1a; HashMap 是非线程安全的#xff0c;Hashtable 是线程安全的,因为 Hashtable 内部的方法基本都经过synchronized 修饰。#xff08;如果你要保证线程安全的话就使用 ConcurrentHashMap 吧#xff01; HashMap 是非线程安全的Hashtable 是线程安全的,因为 Hashtable 内部的方法基本都经过synchronized 修饰。如果你要保证线程安全的话就使用 ConcurrentHashMap 吧效率 因为线程安全的问题HashMap 要比 Hashtable 效率高一点。另外Hashtable 基本被淘汰不要在代码中使用它对 Null key 和 Null value 的支持 HashMap 可以存储 null 的 key 和 value但 null 作为键只能有一个null 作为值可以有多个Hashtable 不允许有 null 键和 null 值否则会抛出 NullPointerException。初始容量大小和每次扩充容量大小的不同 ① 创建时如果不指定容量初始值Hashtable 默认的初始大小为 11之后每次扩充容量变为原来的 2n1。HashMap 默认的初始化大小为 16。之后每次扩充容量变为原来的 2 倍。② 创建时如果给定了容量初始值那么 Hashtable 会直接使用你给定的大小而 HashMap 会将其扩充为 2 的幂次方大小HashMap 中的tableSizeFor()方法保证下面给出了源代码。也就是说 HashMap 总是使用 2 的幂作为哈希表的大小,后面会介绍到为什么是 2 的幂次方。底层数据结构 JDK1.8 以后的 HashMap 在解决哈希冲突时有了较大的变化当链表长度大于阈值默认为 8时将链表转化为红黑树将链表转换成红黑树前会判断如果当前数组的长度小于 64那么会选择先进行数组扩容而不是转换为红黑树以减少搜索时间后文中我会结合源码对这一过程进行分析。Hashtable 没有这样的机制。
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);}public HashMap(int initialCapacity) {this(initialCapacity, DEFAULT_LOAD_FACTOR);}下面这个方法保证了 HashMap 总是使用 2 的幂作为哈希表的大小。 /*** Returns a power of two size for the given target capacity.*/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;}1.2 HashMap 和 HashSet 区别
如果你看过 HashSet 源码的话就应该知道HashSet 底层就是基于 HashMap 实现的。HashSet 的源码非常非常少因为除了 clone()、writeObject()、readObject()是 HashSet 自己不得不实现之外其他方法都是直接调用 HashMap 中的方法。
方法都是直接调用 HashMap 中的方法。
HashMapHashSet实现了 Map 接口实现 Set 接口存储键值对仅存储对象调用 put()向 map 中添加元素调用 add()方法向 Set 中添加元素HashMap 使用键Key计算 hashcodeHashSet 使用成员对象来计算 hashcode 值对于两个对象来说 hashcode 可能相同所以equals()方法用来判断对象的相等性
1.3 HashMap 和 TreeMap 区别
TreeMap 和HashMap 都继承自AbstractMap 但是需要注意的是TreeMap它还实现了NavigableMap接口和SortedMap 接口。 实现 NavigableMap 接口让 TreeMap 有了对集合内元素的搜索的能力。
实现SortedMap接口让 TreeMap 有了对集合中的元素根据键排序的能力。默认是按 key 的升序排序不过我们也可以指定排序的比较器。示例代码如下
/*** author shuang.kou* createTime 2020年06月15日 17:02:00*/
public class Person {private Integer age;public Person(Integer age) {this.age age;}public Integer getAge() {return age;}public static void main(String[] args) {TreeMapPerson, String treeMap new TreeMap(new ComparatorPerson() {Overridepublic int compare(Person person1, Person person2) {int num person1.getAge() - person2.getAge();return Integer.compare(num, 0);}});treeMap.put(new Person(3), person1);treeMap.put(new Person(18), person2);treeMap.put(new Person(35), person3);treeMap.put(new Person(16), person4);treeMap.entrySet().stream().forEach(personStringEntry - {System.out.println(personStringEntry.getValue());});}
}person1 person4 person2 person3 可以看出TreeMap 中的元素已经是按照 Person 的 age 字段的升序来排列了。
上面我们是通过传入匿名内部类的方式实现的你可以将代码替换成 Lambda 表达式实现的方式
TreeMapPerson, String treeMap new TreeMap((person1, person2) - {int num person1.getAge() - person2.getAge();return Integer.compare(num, 0);
});综上相比于HashMap来说 TreeMap 主要多了对集合中的元素根据键排序的能力以及对集合内元素的搜索的能力。
1.4 HashSet 如何检查重复? 当你把对象加入HashSet时HashSet 会先计算对象的hashcode值来判断对象加入的位置同时也会与其他加入的对象的 hashcode 值作比较如果没有相符的 hashcodeHashSet 会假设对象没有重复出现。但是如果发现有相同 hashcode 值的对象这时会调用equals()方法来检查 hashcode 相等的对象是否真的相同。如果两者相同HashSet 就不会让加入操作成功。 在 JDK1.8 中HashSet的add()方法只是简单的调用了HashMap的put()方法并且判断了一下返回值以确保是否有重复元素。直接看一下HashSet中的源码
// Returns: true if this set did not already contain the specified element
// 返回值当 set 中没有包含 add 的元素时返回真
public boolean add(E e) {return map.put(e, PRESENT)null;
}而在HashMap的putVal()方法中也能看到如下说明
// Returns : previous value, or null if none
// 返回值如果插入位置没有元素返回null否则返回上一个元素
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict) {
...
}也就是说在 JDK1.8 中实际上无论HashSet中是否已经存在了某元素HashSet都会直接插入只是会在add()方法的返回值处告诉我们插入前是否存在相同元素。
1.5 HashMap 的底层实现
1.5.1 JDK1.8 之前
JDK1.8 之前 HashMap 底层是 数组和链表 结合在一起使用也就是 链表散列。HashMap 通过 key 的 hashcode 经过扰动函数处理过后得到 hash 值然后通过 (n - 1) hash 判断当前元素存放的位置这里的 n 指的是数组的长度如果当前位置存在元素的话就判断该元素与要存入的元素的 hash 值以及 key 是否相同如果相同的话直接覆盖不相同就通过拉链法解决冲突。
所谓扰动函数指的就是 HashMap 的 hash 方法。使用 hash 方法也就是扰动函数是为了防止一些实现比较差的 hashCode() 方法 换句话说使用扰动函数之后可以减少碰撞。