java面试——集合类基础知识

1)集合类:List和Set比较,各自的子类比较(ArrayList,Vector,LinkedList;HashSet,TreeSet)

List和Set的区别:

           List中元素可以重复,Set中的元素保证唯一性且在TreeSet中元素是有序的。

ArrayList,Vector,LinkedList区别:

           ArrayList是基于数组的形式实现的(初始容量为10),元素存在一片连续的内存中;LinkedList是基于链表的形式实现的,内存地址不一定连续。

          ArrayList可是随机访问,但是删除和插入元素效率低;LinkedList可以在O(1)实现元素的插入和删除,但是访问一元素的时间与该元素所在的位置有关。

          ArrayList和LinkedList在多线程的情况下,会出现线程安全的问题;Vector是线程安全的。

HashSet和TreeSet的区别:

          HashSet可以放入一个null的元素,TreeSet不允许放入null的元素。

          HashSet是通过hashcode()和equal()来保证元素的唯一性;TreeeSet是通过排序的方式来确保元素的唯一性的。

          HashSet是基于数组和链表实现的,TreeSet是基于红黑树实现的。

2)HashMap的底层实现

           HashMap是基于数组加链表实现的,通过拉链法解决hash值冲突的问题,当链表长度大于8时,链表转化为红黑树。默认长度为16,以后每次扩容的长度也为2的幂。默认的负载因子为0.75,HaspMap中的元素个数大于总容量*0.75时,就进行一次resize。

HashMap其他问题

HashMap的容量为什么都是2的幂函数?

           (1).这个方法非常巧妙,它通过 h & (table.length -1) 来得到该对象的保存位,而 HashMap 底层数组的长度总是 2 的 n 次方,2n-1 得到的二进制数的每个位上的值都为 1, 那么与全部为 1 的一个数进行与操作,速度会大大提升。

           (2).当 length 总是 2 的 n 次方时,h& (length-1)运算等价于对 length 取模,也就是 h%length,但是&比%具有更高的效率。

           (3).当数组长度为 2 的 n 次幂的时候,不同的 key 算得的 index 相同的几率较小,那么 数据在数组上分布就比较均匀,也就是说碰撞的几率小,相对的,查询的时候就不用遍历 某个位置上的链表,这样查询效率也就较高了

HashMap的初始容量为多大合适?

           (目标容量/0.75)+1    

              目标容量为你要放入元素的个数,0.75为默认的负载因子。假如现在现在hashMap的size为6,它的容量为8,当size大于6时,就要进行resize,我们假设要放7个人元素,按照公式初始化容量为10,但实际hashMap的容量为16,因为hashMap的初始化容量是根据我们传入的数字,会找到第一大于改数的2的幂,也就是16,这样就减少resize的发生的概率,大大提高效率。

下面这片文章关于HashMap初始化介绍的最仔细

https://mp.weixin.qq.com/s?__biz=MzI3NzE0NjcwMg==&mid=2650121359&idx=1&sn=c63d62be1a36db675c62e341044f10e0&chksm=f36bb9aec41c30b8b369428db1286d3de9bc04675057cde49632f3ba50db2d0a69451d6ec080&mpshare=1&scene=1&srcid=0529kU8fMyKTUrpKDwaLMJc6#rd

3)ConcurrentHashMap的底层实现

            ConcurrentHashMap和HashMap的数据结构一样都是依靠数组加链表,当链表长度大于8时转化为红黑树。

            ConcurrentHashMap在java7和java8差别很大

 

在jdk1.7中:ConcurrentHashMap 是一个 Segment 数组,Segment 通过继承 ReentrantLock 来进行加锁,所以每次需要加锁的操作锁住的是一个 segment,这样只要保证每 个 Segment 是线程安全的,也就实现了全局的线程安全。 

 在jdk1.8中:通过大量cas操作和Synalize关键字取消了原来的segment。

 public V put(K key, V value) {
        return putVal(key, value, false);
    }

    /** Implementation for put and putIfAbsent */
    final V putVal(K key, V value, boolean onlyIfAbsent) {
        if (key == null || value == null) throw new NullPointerException();
        int hash = spread(key.hashCode());
        int binCount = 0;
        for (Node<K,V>[] tab = table;;) {
            Node<K,V> f; int n, i, fh;
            if (tab == null || (n = tab.length) == 0)
                tab = initTable();
            else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {   //如果hash值所在的地方第一次插入节点,就进行cas操作
                if (casTabAt(tab, i, null,
                             new Node<K,V>(hash, key, value, null)))
                    break;                   // no lock when adding to empty bin
            }
            else if ((fh = f.hash) == MOVED)    //判断是否在及进行扩容
                tab = helpTransfer(tab, f);
            else {
                V oldVal = null;
                synchronized (f) {           //通过加入synchronized关键字,在链表的第一个节点出加上锁   
                    if (tabAt(tab, i) == f) {
                        if (fh >= 0) {
                            binCount = 1;
                            for (Node<K,V> e = f;; ++binCount) {
                                K ek;
                                if (e.hash == hash &&
                                    ((ek = e.key) == key ||
                                     (ek != null && key.equals(ek)))) {
                                    oldVal = e.val;
                                    if (!onlyIfAbsent)
                                        e.val = value;
                                    break;
                                }
                                Node<K,V> pred = e;
                                if ((e = e.next) == null) {
                                    pred.next = new Node<K,V>(hash, key,
                                                              value, null);
                                    break;
                                }
                            }
                        }
                        else if (f instanceof TreeBin) {
                            Node<K,V> p;
                            binCount = 2;
                            if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key,
                                                           value)) != null) {
                                oldVal = p.val;
                                if (!onlyIfAbsent)
                                    p.val = value;
                            }
                        }
                    }
                }
                if (binCount != 0) {
                    if (binCount >= TREEIFY_THRESHOLD)
                        treeifyBin(tab, i);
                    if (oldVal != null)
                        return oldVal;
                    break;
                }
            }
        }
        addCount(1L, binCount);
        return null;
    }

https://blog.csdn.net/u010412719/article/details/52145145(这篇文章介绍更加仔细)

4)ConcurrentHashMap和HashTable的区别

         HashTable底层数组+链表,线程安全,实现线程安全的方法是锁住整个hashtable,效率低。

  

 public synchronized V put(K key, V value)


 public synchronized V get(Object key)


 public synchronized boolean containsKey(Object key)


 public synchronized boolean contains(Object value)

 

         默认容量为11,负载因子为0.75,扩容方式为原来容量*2+1

 public Hashtable() {
        this(11, 0.75f);
    }

 int newCapacity = (oldCapacity << 1) + 1;

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值