JavaGuide八股学习--MAP

HashMap和Hashtable的区别

· 线程安全:HashMap是非线程安全的,Hashtable是线程安全的,因为Hashtble内部的方法基本上都经过synchronized修饰的。

· 对应Null key 和Null value的支持: HashMap是可以存储null的key和value,但是只能有一个null键,Hashtable是不支持null键和null值的,会抛出NullPointerException

· 初始化容量大小和每次扩容容量的大小的不同:1)如果没有初始化容量的大小,则Hashtable的默认初始化大小是11,之后每次扩容为原来容量的2n+1倍,HashMap的默认初始化大小是16,每次扩容为原来的2倍。2)如果给定了初始化大小,则Hashtable为给定的大小,HashMap则是为给定大小的2的幂次方

· 底层数据结构不同

HashMap和HashSet的区别

HashSet是基于HashMap实现的

HashSet如何检查重复?

当把对象加入到HashSet中,HashSet先会计算对象的HashCode值判断对象加入的位置以及判断对象有没有重复出现。如果重复出现,调用equals()方法来检查对象是否相同,如果相同则加入失败。

// 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;
}
// Returns : previous value, or null if none
// 返回值:如果插入位置没有元素返回null,否则返回上一个元素
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
...
}

也就是说,无论HashSet中是否存在相同的元素,HashSet都会执行add()方法,只是会在返回值处告诉我们该元素是否已经存在

HashMap的底层实现

·JDK1.8之前:

JDK1.8之前HashMap是由数组和链表实现的。HashMap 通过 key 的 hashcode 经过扰动函数处理过后得到 hash 值,然后通过 (n - 1) & hash 判断当前元素存放的位置。

    static final int hash(Object key) {
      int h;
      // key.hashCode():返回散列值也就是hashcode
      // ^:按位异或
      // >>>:无符号右移,忽略符号位,空位都以0补齐
      return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
  }

具体是采用了“拉链法”,也就是将链表和数组相结合,当创建一个链表数组时,数组的每一个就是一个链表。若遇到了冲突,则将冲突的值加入链表中即可。

·JDK1.8之前:

JDK1.8之后对解决冲突进行了优化。当链表长度大于阈值(8)时(将链表转换成红黑树前会判断,如果当前数组的长度小于 64,那么会选择先进行数组扩容,而不是转换为红黑树)),将链表转化为红黑树,以减少冲突的出现。

HashMap链表到红黑树转换的源码:

1)在putVal方法中判断链表长度是否大于8,满足则执行treeifybin(转换红黑树)

// 遍历链表
for (int binCount = 0; ; ++binCount) {
    // 遍历到链表最后一个节点
    if ((e = p.next) == null) {
        p.next = newNode(hash, key, value, null);
        // 如果链表元素个数大于等于TREEIFY_THRESHOLD(8)
        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;
}

2)treeifyBin方法中国判断数组长度并扩容转换成红黑树

final void treeifyBin(Node<K,V>[] tab, int hash) {
    int n, index; Node<K,V> e;
    // 判断当前数组的长度是否小于 64
    if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)
        // 如果当前数组的长度小于 64,那么会选择先进行数组扩容
        resize();
    else if ((e = tab[index = (n - 1) & hash]) != null) {
        // 否则才将列表转换为红黑树

        TreeNode<K,V> hd = null, tl = null;
        do {
            TreeNode<K,V> p = replacementTreeNode(e, null);
            if (tl == null)
                hd = p;
            else {
                p.prev = tl;
                tl.next = p;
            }
            tl = p;
        } while ((e = e.next) != null);
        if ((tab[index] = hd) != null)
            hd.treeify(tab);
    }
}

ConcurrentHashMap

HashMap的线程安全的、支持高效并发的版本。在ConcurrentHashMap中,进行读操作的时几乎不加锁,而在写操作时通过锁分段技术只对所操作的段加锁而不影响客户端对其他段的访问。在理想状态下,ConCurrentHashMap可以支持16个线程执行并发写操作,及任意数量线程的读操作。

ConcurrentHashMap线程安全的具体实现方式/底层具体实现

· jdk1.8之前,ConcurrentHashMap是由Segment数组结构和HashEntry数组结构组成。

首先是把数据分为一段一段的存储。然后给每一段数据加上一把锁,当一个线程访问其中一个数据锁的时候,其他段的数据也能被其他线程访问。

Segment继承了ReentrantLock,所以Segment是一种可重入锁,扮演锁的角色,而HashEntry数组用于存储键值对数据。每个Segment守护一个HashEntry数组里的元素,

· jdk1.8后,取消了Segment分段锁,采用node+CAS+synchronized来保证并发安全。在链表长度超过一定阈值(8)时将链表(寻址时间复杂度为 O(N))转换为红黑树(寻址时间复杂度为 O(log(N)))。

Java 8 中,锁粒度更细,synchronized 只锁定当前链表或红黑二叉树的首节点,这样只要 hash 不冲突,就不会产生并发,就不会影响其他 Node 的读写,效率大幅提升。

  • 11
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
当面试官要求你回答一个关于 Python 的典型问题时,下面是一个可能的八股文回答模板: 首先,我会对问题进行理解和分析,确保我准确地理解了问题的要求和背景。 然后,我会按照以下结构来回答问题: 1. **问题背景和解决思路**:我会简要介绍问题的背景和解决思路。例如,如果问题是关于列表的操作,我可能会解释什么是列表以及如何使用它们来存储和操作数据。 2. **核心代码实现**:接下来,我会给出一个基本的代码实现,以展示我对问题的理解和能力。我会尽量保持代码简洁而清晰,并结合注释来解释代码的每一步。 3. **优化和扩展**:在展示基本实现之后,我还可以讨论如何优化代码以提高性能或满足更复杂的需求。我可以提出一些常见的优化策略,例如使用生成器、使用递归等。 4. **错误处理和异常处理**:此外,我还可以谈谈在代码中处理错误和异常的重要性。我会提到一些常见的错误和异常类型,并解释如何使用 try-except 语句来捕获和处理它们。 5. **应用场景和实际例子**:最后,我会给出一些实际的应用场景和示例,以展示我对 Python 的灵活运用能力。我可以谈论一些常见的 Python 库和框架,以及它们在实际项目中的使用案例。 通过以上结构,我可以清晰地展示我对 Python 的理解、能力和经验,并给面试官留下一个积极的印象。当然,在回答问题时,我也会根据具体问题的要求进行适当调整和补充。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值