我习惯了无所谓,却不是真的什么都不在乎。 请关注:源码猎人 目录 LinkedHashMap、HashSet、LinkedHashSet都扩展了HashMap。它们是HashMap的二次封装,LinkedHashMap加入了一个单独链表存所有数据,并且完整保留HashMap结构。 HashSet 实际上只用到了HashMap的键,值为常量;LinkedHashSet 继承HashSet,并且和LinkedHashMap一样内部维护一个链表。 从构造函数中可以看出,LinkedHashMap几乎完全使用HashMap的构造方法初始化(参照HashMap类篇),至于初始长度和加载因子也是延用HashMap,LinkedHashMap类自身只关注链表结构,数字加链表结构交给父类去维护 根据键取值 实际上就是调用HashMap的getNode方法, afterNodeAccess(e)方法只是为了维护顺序,除了这个方法LinkedHashMap没有添加删除方法,那么他是怎么维护链表结构的呢?是否还记得在HashMap篇有几个方法要放在LinkedHashMap讲 这几个方法出现在HashMap元素增加或减少之后,就是为了给hashMap子类使用 父类元素变动后方法 HashMap删除元素成功后会调用此方法afterNodeRemoval afterNodeInsertion方法是在哈希表中插入了一个新节点时调用的,它会把链表的头节点删除掉,删除的方式是通过调用HashMap的removeNode方法。我们要使用此功能必须重写removeEldestEntry方法 当accessOrder设置为true时,把当前节点e移至链表的尾部,在HashMap的putVal方法中,会调用此方法 此内部类在HashMap中也用到,HashMap的TreeNode就是继承此类(是不是很绕) 跟ArraryList相比没有实现RandomAccess接口,接下来看成员变量和构造函数 通过构造函数可以看出HashSet的初始化时内部构建了一个HashMap对象 此构造函数安全级别为默认,只有自己或同包子类可以调用。HashSet并没有调用它,它是给子类使用的,跟上面构造函数唯一的区别就是内部Map换成LinkedHashMap<>对象 HashSet只是用了HashMap的key,而值是一个固定的常量,所以HashMap的key拥有哪些特性HashSet就拥有哪些特性。 LinkedHashSet完成继承HashSet,内部只有构造函数 LinkedHashSet 所有构造函数都调用HashSet的HashSet(int initialCapacity, float loadFactor, boolean dummy)构造函数,内部使用LinkedHashMap对象 1.说一下LinkedHashMap的数据结构 LinkedHashMap继承自HashMap,并维持了一个双向链表。插入节点时,将节点追加到双向链表尾部,从而实现按照插入顺序的有序访问。也可以在初始化LinkedHashMap对象时设定为按照访问顺序排序,此时每当访问一个节点,afternodeaccess方法就会将该节点放到双向链表的尾部,从而实现按照访问顺序的有序遍历访问。 2、请说说HashSet原理 HashSet在存元素时,会调用对象的hashCode方法计算出存储位置,然后和该位置上所有的元素进行equals比较, 3、为什么HashMap中String、Integer这样的包装类适合作为K? String、Integer等包装类的特性能够保证Hash值的不可更改性和计算准确性,能够有效的减少Hash碰撞的几率 如果Object作为键,那么需要重写 4、HashMap 和 LinkedHashMap 有什么区别? 5、ArrayList 与 LinkedList 有什么区别 ?
简介
LinkedHashMap 源码解读
public class LinkedHashMap<K,V> extends HashMap<K,V> implements Map<K,V>
LinkedHashMap属性
// 链表头结点 transient LinkedHashMap.Entry<K,V> head; // 链表尾结点 transient LinkedHashMap.Entry<K,V> tail; // LRU算法相关 false 基于插入顺序,true 基于访问顺序 final boolean accessOrder;
LinkedHashMap构造函数
public LinkedHashMap(int initialCapacity, float loadFactor) { super(initialCapacity, loadFactor); // 默认使用插入顺序 accessOrder = false; } public LinkedHashMap(int initialCapacity) { super(initialCapacity); // 默认使用插入顺序 accessOrder = false; } public LinkedHashMap() { super(); // 默认使用插入顺序 accessOrder = false; } public LinkedHashMap(Map<? extends K, ? extends V> m) { super(); accessOrder = false; // 默认使用插入顺序 putMapEntries(m, false); } public LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder) { super(initialCapacity, loadFactor); // 自定义顺序 this.accessOrder = accessOrder; }
LinkedHashMap 方法
public V get(Object key) { Node<K,V> e; if ((e = getNode(hash(key), key)) == null) return null; if (accessOrder) afterNodeAccess(e); return e.value; }
void afterNodeAccess(Node<K,V> p) { } void afterNodeInsertion(boolean evict) { } void afterNodeRemoval(Node<K,V> p) { }
// HashMap删除节点后 void afterNodeRemoval(Node<K,V> e) { LinkedHashMap.Entry<K,V> p = (LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after; p.before = p.after = null; if (b == null) // 如果是头节点,则把头节的下一个节点设为头节点 head = a; else // 否则,把前一个节点的下一个节点指向当前下一个节点 b.after = a; if (a == null) // 如果是尾节点,设置当前节点前一个节点为尾节点 tail = b; else // 否则把后面节点的前面执行当前节点的前面 a.before = b; }
// 插入成功删除头节点 void afterNodeInsertion(boolean evict) { // possibly remove eldest LinkedHashMap.Entry<K,V> first; if (evict && (first = head) != null && removeEldestEntry(first)) { K key = first.key; removeNode(hash(key), key, null, false, true); } }
// accessOrder为true时将节点移到最后 void afterNodeAccess(Node<K,V> e) { // move node to last LinkedHashMap.Entry<K,V> last; if (accessOrder && (last = tail) != e) { LinkedHashMap.Entry<K,V> p = (LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after; p.after = null; if (b == null) head = a; else b.after = a; if (a != null) a.before = b; else last = b; if (last == null) head = p; else { p.before = last; last.after = p; } tail = p; ++modCount; } }
LinkedHashMap 内部类
LinkedHashMap.Entry<K,V>
static class Entry<K,V> extends HashMap.Node<K,V> { // 前一个结点,后一个节点 Entry<K,V> before, after; Entry(int hash, K key, V value, Node<K,V> next) { super(hash, key, value, next); } }
HashSet 类源码解读
public class HashSet<E> extends AbstractSet<E> implements Set<E>, Cloneable, java.io.Serializable
HashSet 属性
// 底层是HashMap实现的 private transient HashMap<E,Object> map; // PRESENT是一个假的value值,帮助用HashMap实现HashSet。 private static final Object PRESENT = new Object();
HashSet 构造函数
public HashSet() { map = new HashMap<>(); } public HashSet(Collection<? extends E> c) { map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16)); addAll(c); } public HashSet(int initialCapacity, float loadFactor) { map = new HashMap<>(initialCapacity, loadFactor); } public HashSet(int initialCapacity) { map = new HashMap<>(initialCapacity); } public HashSet(Collection<? extends E> c) { map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16)); addAll(c); }
HashSet(int initialCapacity, float loadFactor, boolean dummy) { map = new LinkedHashMap<>(initialCapacity, loadFactor); }
HashSet 方法
// 获取长度 public int size() { return map.size(); } // 是否为空 public boolean isEmpty() { return map.isEmpty(); } // 是否包含 public boolean contains(Object o) { return map.containsKey(o); } // 添加元素 public boolean add(E e) { // 添加元素时,元素为键,值为固定常量存入Map return map.put(e, PRESENT)==null; } // 删除元素 public boolean remove(Object o) { return map.remove(o)==PRESENT; } // 清空 public void clear() { map.clear(); }
LinkedHashSet 类源码解读
public class LinkedHashSet<E> extends HashSet<E> implements Set<E>, Cloneable, java.io.Serializable
LinkedHashSet 构造函数
public LinkedHashSet(int initialCapacity, float loadFactor) { super(initialCapacity, loadFactor, true); } public LinkedHashSet(int initialCapacity) { super(initialCapacity, .75f, true); } public LinkedHashSet() { super(16, .75f, true); } public LinkedHashSet(Collection<? extends E> c) { super(Math.max(2*c.size(), 11), .75f, true); addAll(c); }
常见面试题
如果该位置没有其他元素或者比较的结果都为false就存进去,否则就不存。这样的原理注定了元素是按照哈希值来找存储位置,所有无序,而且可以保证无重复元素,我们在往HashSet集合存储元素时,对象应该正确重写Object类的hashCode和equals方法
正因为这样的原理,HashSet集合是非常高效的。
equals()
、hashCode()
等方法,遵守了HashMap内部的规范(不清楚可以去上面看看putValue的过程),不容易出现Hash值计算错误的情况hashCode()
和equals()
方法
hashCode()
是因为需要计算存储数据的存储位置,需要注意不要试图从散列码计算中排除掉一个对象的关键部分来提高性能,这样虽然能更快但可能会导致更多的Hash碰撞equals()
方法,需要遵守自反性、对称性、传递性、一致性以及对于任何非null的引用值x,x.equals(null)必须返回false的这几个特性,目的是为了保证key在哈希表中的唯一性
本网页所有视频内容由 imoviebox边看边下-网页视频下载, iurlBox网页地址收藏管理器 下载并得到。
ImovieBox网页视频下载器 下载地址: ImovieBox网页视频下载器-最新版本下载
本文章由: imapbox邮箱云存储,邮箱网盘,ImageBox 图片批量下载器,网页图片批量下载专家,网页图片批量下载器,获取到文章图片,imoviebox网页视频批量下载器,下载视频内容,为您提供.
阅读和此文章类似的: 全球云计算