java 面试总结(集合)

   1.Set, List, Map 的联系和区别?

      答:1)List,Set都是继承自Collection接口,Map则不是

             2)List特点:元素有放入顺序,元素可重复,Set特点:元素无放入顺序,元素不可重复,重复元素会覆盖掉,(注意:元素虽然无放入顺序,但是元素在set中的位置是由该元素的HashCode决定的,其位置其实是固定的,加入Set的Object必须定义equals()方法,另外list支持for循环,也就是是通过下标来遍历,也可以用迭代器,但是set只能用迭代,因为它是无序的,无法用下标来取得想要的值。)

             3)Set和List对比:

             Set:检索效率低下,删除和插入效率高,插入和删除不会引起元素的位置变化。

             List:和数组类似,List可以动态增长,查找元素效率高,插入删除元素效率低,因为会引起其他元素的位置变化。

            4)Map适合存储键值对的数据。

            5)线程安全集合类和非线程安全集合类

                LinkedList,ArrayList,HashSet是非线程安全的,Vector是线程安全的;

                HashMap是非线程安全的,HashTable 是线程安全的;

                StringBuilder是非线程安全的,StringBuffer是线程安全的。

      2. 遍历Set有哪几种方法。

             

  3.HashMap 和 Hashtable的区别,HashMap底层数据结构是什么?

     1)HashMap的工作原理

          HashMap基于hashing原理,我们通过put()和get()方法存储和获取对象。当我们将键值对传递给put()方法时,它调用键对象的hashCode()方法来计算hashcode,然后找到bucket位置来存储值对象。当获取对象时,通过键对象的equals()方法找到正确的键值对,然后返回值对象。HashMap使用链表来解决碰撞为题,当发生碰撞了,对象将会存储在链表的下个节点中。HashMap在每个链表节点中存储键值对对象。

      2)HashMap和HashTable的区别

           HashMap和Hashtable都实现了Map接口,但决定用哪一个之前先要弄清楚它们之间的分别。主要的区别有:线程安全性,同步(synchronization),以及速度。HashMap几乎可以等价于Hashtable,除了HashMap是非synchronized的,并可以接受null(HashMap可以接受为null的键值(key)和值(value),而Hashtable则不行)。HashMap是非synchronized,而Hashtable是synchronized,这意味着Hashtable是线程安全的,多个线程可以共享一个Hashtable;而如果没有正确的同步的话,多个线程是不能共享HashMap的。Java 5提供了ConcurrentHashMap,它是HashTable的替代,比HashTable的扩展性更好。
            另一个区别是HashMap的迭代器(Iterator)是fail-fast迭代器,而Hashtable的enumerator迭代器不是fail-fast的。所以当有其它线程改变了HashMap的结构(增加或者移除元素),将会抛出ConcurrentModificationException,但迭代器本身的remove()方法移除元素则不会抛出ConcurrentModificationException异常。但这并不是一个一定发生的行为,要看JVM。这条同样也是Enumeration和Iterator的区别。
             由于Hashtable是线程安全的也是synchronized,所以在单线程环境下它比HashMap要慢。如果你不需要同步,只需要单一线程,那么使用HashMap性能要好过Hashtable。
             HashMap不能保证随着时间的推移Map中的元素次序是不变的。

  4.给一个HashMap对象,如何使这个HashMap对象线程安全?

     

//Hashtable
Map<String, String> hashtable = new Hashtable<>();
//synchronizedMap
Map<String, String> synchronizedHashMap = Collections.synchronizedMap(new HashMap<String, String>());
//ConcurrentHashMap
Map<String, String> concurrentHashMap = new ConcurrentHashMap<>();

HashTable 中使用synchronized来保证线程安全,显然效率很低,不能

 

public synchronized V get(Object key) {
    // 省略实现
}
public synchronized V put(K key, V value) {
    // 省略实现
}

ConcurrentHashMap(简称CHM)

CHM是在JDK1.5之后引入的,用来替代HashTable,是concurrent包的重要成员,在JDK1.5之前如果要保证线程安全的使用map,只能选择HashTable或者SynchronizedMap,但是在操作map时,HashTable和SynchronizedMap会锁住整个map,引入了ConcurrentHashMap之后,在操作map时,只锁住了部分,允许多线程的并发读操作,同时通过同步锁保证了,数据的完整性。

CHM允许多线程的读操作,同时根据默认并发级别(可以在构造函数中传入,默认16)个数,允许16个线程同时进入操作map,而SynchronizedMap和HashTable只允许一个线程进入操作map,但是如果是更新操作,比如remove,put,clear可以并发执行,所以不能保证最后返回的结果是最新的。如果只有很少的线程操作map,建议将并发级别设置少一些。

有时候我们在往map中put元素时,我们需要预判断一下是否存在该元素,再执行put操作,同时为了并发操作,我们可能会加锁避免并发get和put。如下:

 

synchronized(map){
    if (map.get(key) == null){
    return map.put(key, value);
    } else{
    return map.get(key);
    }
    }

但是我们可以使用chm的putIfAbsent(key,value)方法可以保证数据的原子性,以及避免线程的竞争。

什么时候使用CHM,建议读的线程数量大于写的线程数量时使用,如果写的数量大于读的数量,比如达到了并发级别,会锁住整个map,读操作要等写操作完成才能执行,所以CHM适用于做缓存,在容器启动时可以被多个线程请求访问。

SynchronizedMap

 

private static class SynchronizedMap<K,V>
    implements Map<K,V>, Serializable {
    private static final long serialVersionUID = 1978198479659022715L;

    private final Map<K,V> m;     // Backing Map
    final Object      mutex;        // Object on which to synchronize

    SynchronizedMap(Map<K,V> m) {
        this.m = Objects.requireNonNull(m);
        mutex = this;
    }

    SynchronizedMap(Map<K,V> m, Object mutex) {
        this.m = m;
        this.mutex = mutex;
    }

    public int size() {
        synchronized (mutex) {return m.size();}
    }
    public boolean isEmpty() {
        synchronized (mutex) {return m.isEmpty();}
    }
    public boolean containsKey(Object key) {
        synchronized (mutex) {return m.containsKey(key);}
    }
    public boolean containsValue(Object value) {
        synchronized (mutex) {return m.containsValue(value);}
    }
    public V get(Object key) {
        synchronized (mutex) {return m.get(key);}
    }

    public V put(K key, V value) {
        synchronized (mutex) {return m.put(key, value);}
    }
    public V remove(Object key) {
        synchronized (mutex) {return m.remove(key);}
    }
    public void putAll(Map<? extends K, ? extends V> map) {
        synchronized (mutex) {m.putAll(map);}
    }

SynchronizedMap中的方法也加了synChronized关键字来保证线程对map的操作是线程安全的。 --------------------- 本文来自 baisq2017 的CSDN 博客 ,全文地址请点击:https://blog.csdn.net/bai_bug/article/details/79326322?utm_source=copy

      5. ArrayList 和LinkedList 和 Vector的区别?

         存储结构

        ArrayList和Vector是按照顺序将元素存储(从下表为0开始),删除元素时,删除操作完成后,需要使部分元素移位,默认的初始容量都是10.

        ArrayList和Vector是基于数组实现的,LinkedList是基于双向链表实现的(含有头结点)。

        线程安全性

        ArrayList不具有有线程安全性,在单线程的环境中,LinkedList也是线程不安全的,如果在并发环境下使用它们,可以用Collections类中的静态方法synchronizedList()对ArrayList和LinkedList进行调用即可。

        Vector实现线程安全的,即它大部分的方法都包含关键字synchronized,但是Vector的效率没有ArraykList和LinkedList高。

        增删改差的效率

        ArrayList和Vector中,从指定的位置检索一个对象,或在集合的末尾插入、删除一个元素的时间是一样的,时间复杂度都是O(1)。但是如果在其他位置增加或者删除元素花费的时间是O(n),LinkedList中,在插入、删除任何位置的元素所花费的时间都是一样的,时间复杂度都为O(1),但是他在检索一个元素的时间复杂度为O(n).

       所以如果只是查找特定位置的元素或只在集合的末端增加移动元素,那么使用ArrayList或Vector都是一样的。如果是在指定位置的插入、删除元素,最好选择LinkedList

     6.为什么HashTable的键和值不能为null?

        因为hashTable是线程安全,用户多线程的,如果用map.get(key)得到了null,不能判断是到底是映射的value是null,还是因为没有找到对应的key而为空。

   

    

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值