JAVA集合的相關知識
什麼是集合
集合是JAVA存放資料容器,集合類存放於java.util包中。
集合類存放的都是物件的引用,而非物件本身,出於表達上的便利,我們稱集合中的物件就是指集合中物件的引用(reference)
集合有哪些
按照儲存的方式主要分為兩大類Map(存鍵值對),collection(存單個元素)。
collection:有三個介面list、set、queue
Iterable
集合的根介面(map除外)
public interface Iterable<T> { Iterator<T> iterator(); }
iterator是集合的迭代器,用於遍歷集合的元素。
主要hashNext(),next(),remove()三種方法,ListIterator繼承於Iterable新增了add(),previous(),hasPrevious()方法。
Collection
List 介面和 Set 介面的父介面
public interface Collection<E> extends Iterable<E> {
}
List
有序,可以重複的集合。 繼承collection所以他有collection中的方法
list 的實現類有 ArrayList、LinkedList、Vetor
①、List list1 = new ArrayList();
底層資料結構是陣列,查詢快,增刪慢;執行緒不安全,效率高
②、List list2 = new Vector();
底層資料結構是陣列,查詢快,增刪慢;執行緒安全(synchronized),效率低,幾乎已經淘汰了這個集合,子類stack
③、List list3 = new LinkedList();
底層資料結構是連結串列,查詢慢,增刪快;執行緒不安全,效率高
Set
無序、不可重複的集合,具體實現類hashSet(LinkedHashSet)、SortedSet(TreeSet)
①、Set hashSet = new HashSet();
HashSet:不能保證元素的順序;不可重複;不是執行緒安全的;集合元素可以為 NULL,底層採用hash表的演算法;
②、Set linkedHashSet = new LinkedHashSet();
因為底層採用 連結串列 和 雜湊表的演算法。連結串列保證元素的新增順序,雜湊表保證元素的唯一性
③、Set treeSet = new TreeSet();
TreeSet:有序;不可重複,底層使用 紅黑樹演算法,擅長於範圍查詢。
Map
key-value 的鍵值對,key 不允許重複,value 可以重複
因為 Map 集合即沒有實現於 Collection 介面,也沒有實現 Iterable 介面,所以不能對 Map 集合進行 for-each 遍歷。
遍歷map
map中 interface Entry<K,V> 屬性。
HashMap< String, String> map = new HashMap<String, String>();
//先獲取key的Set,再通過key獲取value
for (String key : map.keySet()) {
map.get(key);
}
// 將map的entry作為值放入set中,遍歷entry
for (Map.Entry<String, String> entry : map.entrySet()) {
entry.getKey();
entry.getValue();
}
}
MAP的子類
HashMap,HashTable,LinkedHashMap,TreeMap
集合的面試問題
-
1 泛型的作用
Java1.5引入了泛型,所有的集合介面和實現都大量地使用它。泛型允許我們為集合提供一個可以容納的物件型別,因此,如果你新增其它型別的任何元素,它會在編譯時報錯。這避免了在執行時出現ClassCastException,因為你將會在編譯時得到報錯資訊。泛型也使得程式碼整潔,我們不需要使用顯式轉換和instanceOf操作符。它也給執行時帶來好處,因為不會產生型別檢查的位元組碼指令。 -
2 集合中那些實現類是執行緒安全的
Vector 使用synchronized
Stack 繼承Vector 所以也是執行緒安全的
Hashtable
ConcurrentHashMap
Enumeration -
Arraylist 與 LinkedList 、Vector區別
講清楚arraylist和linkedlist的區別,得先從陣列與連結串列講起
陣列是將元素在記憶體中連續存放,由於每個元素佔用記憶體相同,所以你可以通過下標迅速訪問陣列中任何元素。但是如果你要在陣列中增加一個元素,你需要移動大量元素,在記憶體中空出一個元素的空間,然後將要增加的元素放在其中。同樣的道理,如果你想刪除一個元素,你同樣需要移動大量元素去填掉被移動的元素。
連結串列中的元素在記憶體中不是順序儲存的,而是通過存在元素中的指標聯絡到一起。比如:上一個元素有個指標指到下一個元素,以此類推,直到最後一個元素。如果你要訪問連結串列中一個元素,你需要從第一個元素開始,一直找到你需要的元素位置。但是增加和刪除一個元素對於連結串列資料結構就非常簡單了, 只要修改元素中的指標就可以了。
綜上可以得出,基於陣列實現的arraylist在查詢上比較方便,而增刪上比較慢。而基於連結串列實現的linkedlist在查詢上比較慢需要從頭開始搜尋,而在元素增刪的操作上比較快。
Vector底層與arraylist相似,但是其方法上有synchronized 以實現執行緒同步,所以在效率上比arraylist要低,但是執行緒安全。
- HashMap 和 Hashtable 的區別
點這裡
在使用HashMap和Hashtable時要先了解其實現原理,下面會介紹,這裡只講區別
1、 Hashtable在其方法上加上了synchronized 使得其在多執行緒中是安全的,而HashMap是非synchronized的。
我們能否讓HashMap同步?
Map m = Collections.synchronizeMap(hashMap);
2 、 HashMap可以有空鍵值對,但是Hashtable不可以有空鍵值對
HashMap< String, String> map = new HashMap<String, String>();
map.put(null, null);
Hashtable<String, String> tHashtable = new Hashtable<String, String>();
try {
tHashtable.put(null, "");
} catch (NullPointerException e) {
System.err.println( "Hashtable 不允許空鍵值對");
}
結果:
- comparable 和 comparator的區別?
HashMap 的工作原理及程式碼實現
我們都知道map裡元素是以鍵值對Entry<key,value>儲存的,那我們具體怎麼將Entry放入到bucket中呢
public V put(K key, V value) {
if (table == EMPTY_TABLE) {
inflateTable(threshold);
}
if (key == null)
return putForNullKey(value);
int hash = hash(key);
int i = indexFor(hash, table.length);
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
modCount++;
addEntry(hash, key, value, i);
return null;
}
我們先通過key計算出hash值 int hash = hash(key);,然後通過hash和length獲取bucket的索引值
Entry<K,V> e = table[i]; e != null; e = e.next,獲取索引i的元素entry,如果entry有值就next,直到key與e.key相等位置。然後把value值替換。
我們再來看get方法
public V get(Object key) {
if (key == null)
return getForNullKey();
Entry<K,V> entry = getEntry(key);
return null == entry ? null : entry.getValue();
}
如果key為null則返回null,
getEntry(key) 如果indexFor(hash, table.length)索引沒有找到,則返回null,如果有值則把key相等的value返回。
ConcurrentHashMap 的工作原理及程式碼實現
我們都知道hashmap是不安全的,而hashtable效率低
concurrenthashmap是執行緒安全又效率較高的一種選擇。
ConcurrentHashMap所使用的是鎖分段技術,首先將資料分成一段一段的儲存,然後給每一段資料配一把鎖,當一個執行緒佔用鎖訪問其中一個段資料的時候,其他段的資料也能被其他執行緒訪問。