Android高级面试题详解之hashmap put()底层原理与ArrayList 如何保证线程安全

正文

在这里插入图片描述

请说一说hashmap put()底层原理,发生冲突时,如何去添加(顺着链表去遍历,挨个比较key值是否一致,如果一致,就覆盖替换,不一致遍历结束后,插入该位置) ?

这道题想考察什么?

1、Hashmap的put函数基础原理?

考察的知识点

HashMap底层的源码

考生如何回答
HashMap put函数的底层源码解析
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) //首次放入元素时,分配table空间-默认size=16
            n = (tab = resize()).length;
 
        if ((p = tab[i = (n - 1) & hash]) == null) // 算出新node在table中的位置,若对应位置为null,新建一个node并放入对应位置.
        // 注意:  (n - 1) & hash 求余操作 等价于 hash%n 
            tab[i] = newNode(hash, key, value, null);
        else {
    
                                          //在table对应位置有node时
            Node<K,V> e; K k;
            if (p.hash == hash &&                   // key一样 (hash值相同,且key 一样,相同实例或者满足Object.equals方法)
                ((k = p.key) == key || (key != null && key.equals(k)))) // 不满足此条件则发生hash碰撞
                e = p;
            else if (p instanceof TreeNode)                // hash碰撞的情况下,用链表解决,链表大于8时,改为红黑树. 当node为treeNode时,putTreeVal->红黑树
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            else {
    
                                                                                   //hash
                for (int binCount = 0; ; ++binCount) {
    
                              //用for循环将链表指针后移,将新node在链表加在尾部
                    if ((e = p.next) == null) {
    
    
                        p.next = newNode(hash, key, value, null);
                        if (binCount >= TREEIFY_THRESHOLD - 1) // 当链表长度大于8时,转成红黑树
                            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)  //当node中旧的值null或者onlyIfAbsent==false时,将新的value替换原来的value.
                    e.value = value;
                afterNodeAccess(e); //新node塞入table后做的做的事情,在HashMap中是一个空方法(LinkedHashMap中有有使用, move node to last)
                return oldValue;
            }
        }
        ++modCount;
        if (++size > threshold)  //新的size大于阈值(默认0.75*table.length)时,扩容.
            resize();
        afterNodeInsertion(evict);
        return null;
    }

思路如下:

  • 对key的hashCode()进行hash后计算数组下标index;
  • 如果当前数组table为null,进行resize()初始化;
  • 如果没碰撞直接放到对应下标的bucket里;
  • 如果碰撞了,且节点已经存在,就替换掉 value;
  • 如果碰撞后发现为树结构,挂载到树上。
  • 如果碰撞后为链表,添加到链表尾,并判断链表如果过长(大于等于TREEIFY_THRESHOLD,默认8),就把链表转换成树结构;
  • 数据put后,如果数据量超过threshold,就要resize。

通过上面的思路我们可以看到,HashMap的put函数在添加的时候会判断碰撞后是否为链表,如果是链表就添加到链表尾,并判断链表如果过长(大于等于TREEIFY_THRESHOLD,默认8),就把链表转换成树结构。

请说一说ArrayList 如何保证线程安全,除了加关键字的方式 ?

这道题想考察什么?

1、ArrayList的底层原理?

考察的知识点

ArrayList底层的源码的理解

你如何回答
ArrayList 如何保证线程安全
  • 继承Arraylist,然后重写或按需求编写自己的方法,这些方法要写成synchronized,在这些synchronized的方法中调用ArrayList的方法。
  • 可以使用Collections.synchronizedList()这个函数,使用方法如下
List<Map<String,Object>> data=new ArrayList<Map<String,Object>>();
List<Map<String,Object>> data=Collections.synchronizedList(new ArrayList<Map<String,Object>>());

在这里插入图片描述

  • 使用CopyOnWriteArrayList
List<String> k=new CopyOnWriteArrayList<>();

文末

更多的Android面试题可以扫描免费领取!

↓↓↓【预览】↓↓↓

img

猜你喜欢

转载自blog.csdn.net/Android_XG/article/details/130862418