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而为空。