面試官常問的 Java 基礎題 51-60
- 51.設計 4 個線程,其中兩個線程每次對 j 增加 1,另外兩個線程對 j 每次減少 1。寫出程序。
- 52.ArrayList 和 Vector 的區別
- 53.HashTable 和 HashMap 的區別
- 54.List 和 Map 的區別?
- 55.List, Set, Map 是否繼承自 Collection 接口?
- 56.List、Map、Set 三個接口,存取元素時,各有什麼特點?
- 57.說出 ArrayList,Vector, LinkedList 的存儲性能和特性
- 58.去掉一個 Vector 集合中重複的元素
- 59.Collection 和 Collections 的區別。
- 60.Set 裏的元素是用什麼方法來區分重複的呢? 是用==還是 equals()? 它們有何區別?
51.設計 4 個線程,其中兩個線程每次對 j 增加 1,另外兩個線程對 j 每次減少 1。寫出程序。
52.ArrayList 和 Vector 的區別
ArrayList 、Vector 都實現了 List 接口
ArrayList 線程不安全,線程異步,查詢快,增長率爲目前數組長度的 1.5 倍,不可設置增長空間大小;
Vector 線程安全,線程同步,增刪改快,增長率爲目前數組長度的 2 倍,可設置增長空間大小。
ArrayList | Vector |
---|---|
線程不安全,線程異步 | 線程安全,線程同步 |
增長率爲目前數組長度的 1.5 倍 | 增長率爲目前數組長度的 2 倍 |
不可設置增長的空間大小 | 可設置增長的空間大小 |
增刪改慢,查詢快 | 查詢慢,增刪改快 |
這兩個類都實現了 List 接口(List 接口繼承了 Collection 接口),他們都是 有序集合,即存儲在這兩個集合中的元素的位置都是有順序的, 相當於一種 動態的數組,我們以後可以 按位置索引號 取出某個元素,並且其中的 數據允許重複,這是 HashSet 之類的集合的最大不同處,HashSet 之類的集合不可以按索引號去檢索其中的元素,也不允許有重複的元素。
(本來題目問的與 hashset 沒有任何關係,但 爲了說清楚 ArrayList 與 Vector 的功能,我們使用對比方式,更有利於說明問題) 。
接着才說 ArrayList 與 Vector 的區別,這主要包括兩個方面:.
-
(1) 同步性: Vector 是線程安全的,也就是說是它的方法之間是線程同步的,而 ArrayList 是線程序不安全的,它的方法之間是線程不同步的。
如果只有一個線程會訪問到集合,建議使用 ArrayList,因爲它不考慮線程安全,效率更高;
如果有多個線程會訪問到集合,建議使用 Vector,因爲不需要我們自己再去考慮和編寫線程安全的代碼。 -
(2) 數據增長: ArrayList 與 Vector 都有一個初始的容量大小;
當存儲進它們裏面的元素的個數超過了容量時,就需要增加 ArrayList 與 Vector 的存儲空間,每次要 增加存儲空間 時,不是隻增加一個存儲單元,而是 增加多個存儲單元 ,每次增加的存儲單元的個數在內存空間利用與程序效率之間要取得一定的平衡。
Vector 默認增長爲原來兩倍,而 ArrayList 的增長策略在文檔中沒有明確規定(從源代碼看到的是 增長爲原來的 1.5 倍)。
ArrayList 與 Vector 都可以設置初始的空間大小,Vector 還可以設置增長的空間大小,而 ArrayList 沒有提供設置增長空間的方法。
53.HashTable 和 HashMap 的區別
Hashtable 產生於JDK 1.1,基於 Dictionary 類,線程安全,線程同步,效率較低,不可將空值作爲一個表單條目的key或value。HashMap 是 Java1.2 引進的 Map 接口的一個實現 ,線程不安全,線程不同步,可以將空值作爲一個表單條目的key或value。
HashTable產生於JDK 1.1,而HashMap產生於JDK 1.2。
HashMap 是 Hashtable 的輕量級實現(非線程安全的實現),他們都完成了 Map 接口,主要區別在於 HashMap 允許空(null)鍵值(key) , 由於非線程安全,在只有一個線程訪問的情況下,HashMap效率要高於 Hashtable。
HashMap 允許將 null 作爲一個 entry 的 key 或者 value,而 Hashtable 不允許。
HashMap 把 Hashtable 的 contains 方法去掉了,改成 containsvalue 和 containsKey,因爲 contains 方法容易讓人引起誤解。
Hashtable 繼承自 Dictionary 類,而 HashMap 是 Java1.2 引進的 Mapinterface 的一個實現。
最大的不同是,Hashtable 的方法是 Synchronize 的,而 HashMap 不是,在多個線程訪問 Hashtable 時,不需要自己爲它的方法實現同步, 而 HashMap 就必須爲之提供外同步。 Hashtable 和 HashMap 採用的 hash/rehash 算法都大概一樣,所以性能不會有很大的差異。
雖然 Hashtable 比 HashMap 出現的早一些,但是現在 Hashtable 基本上已經被棄用了。而 HashMap 已經成爲應用最爲廣泛的一種數據類型了。原因:一方面是因爲 Hashtable 是線程安全的,效率比較低。也可能是Hashtable開始設計的時候沒有遵循駝峯命名法(手動)。
-
1、父類不同:
HashMap是繼承自AbstractMap類,而HashTable是繼承自Dictionary(已被廢棄,詳情看源代碼)。不過它們都實現了同時實現了map、Cloneable(可複製)、Serializable(可序列化)這三個接口。
Hashtable比HashMap多提供了elments() 和contains() 兩個方法。
elments() 方法繼承自Hashtable的父類Dictionnary。elements() 方法用於返回此Hashtable中的value的枚舉。
contains()方法判斷該Hashtable是否包含傳入的value。它的作用與containsValue()一致。事實上,contansValue() 就只是調用了一下contains() 方法。
-
2、null值問題
Hashtable既不支持Null key也不支持Null value。Hashtable的put()方法的註釋中有說明 。
HashMap中,null可以作爲鍵,這樣的鍵只有一個;可以有一個或多個鍵所對應的值爲null。當get()方法返回null值時,可能是 HashMap中沒有該鍵,也可能使該鍵所對應的值爲null。因此,在HashMap中不能由get()方法來判斷HashMap中是否存在某個鍵, 而應該用containsKey()方法來判斷。
-
3、線程安全性
Hashtable是線程安全的,它的每個方法中都加入了Synchronize方法。在多線程併發的環境下,可以直接使用Hashtable,不需要自己爲它的方法實現同步
HashMap不是線程安全的,在多線程併發的環境下,可能會產生死鎖等問題。具體的原因在下一篇文章中會詳細進行分析。使用HashMap時就必須要自己增加同步處理,
雖然HashMap不是線程安全的,但是它的效率會比Hashtable要好很多。這樣設計是合理的。在我們的日常使用當中,大部分時間是單線程操作的。HashMap把這部分操作解放出來了。當需要多線程操作的時候可以使用線程安全的ConcurrentHashMap。ConcurrentHashMap雖然也是線程安全的,但是它的效率比Hashtable要高好多倍。因爲ConcurrentHashMap使用了分段鎖,並不對整個數據進行鎖定。
tip:HashMap是JDk1.2之後有的,而在JDK1.5中,偉大的Doug Lea給我們帶來了concurrent包,從此Map也有安全的了。也就就是有了ConcurrentHashMap(關於這個的理解下次有機會再寫,或自行百度)
-
4、遍歷方式不同
Hashtable、HashMap都使用了Iterator。而由於歷史原因,Hashtable還使用了Enumeration的方式 。
HashMap的Iterator是fail-fast迭代器。當有其它線程改變了HashMap的結構(增加,刪除,修改元素),將會拋出ConcurrentModificationException。不過,通過Iterator的remove()方法移除元素則不會拋出ConcurrentModificationException異常。但這並不是一個一定發生的行爲,要看JVM。
JDK8之前的版本中,Hashtable是沒有fast-fail機制的。在JDK8及以後的版本中 ,Hashtable也是使用fast-fail的。(此處可以去看一下1.5和1.8JDK源碼的對比)
-
5、初始容量不同
Hashtable的初始長度是11,之後每次擴充容量變爲之前的2n+1(n爲上一次的長度)
而HashMap的初始長度爲16,之後每次擴充變爲原來的兩倍
創建時,如果給定了容量初始值,那麼Hashtable會直接使用你給定的大小,而HashMap會將其擴充爲2的冪次方大小。
-
6、計算哈希值的方法不同
爲了得到元素的位置,首先需要根據元素的 KEY計算出一個hash值,然後再用這個hash值來計算得到最終的位置
Hashtable直接使用對象的hashCode。hashCode是JDK根據對象的地址或者字符串或者數字算出來的int類型的數值。然後再使用除留餘數發來獲得最終的位置。 然而除法運算是非常耗費時間的。效率很低
HashMap爲了提高計算效率,將哈希表的大小固定爲了2的冪,這樣在取模預算時,不需要做除法,只需要做位運算。位運算比除法的效率要高很多。
54.List 和 Map 的區別?
List 是存儲單列數據的集合,存儲的數據有序且可重複;
map 是雙列數據集合,存儲的數據無序,鍵不可重複,值可重複。
List是存儲單列數據的集合,Map是存儲鍵和值這樣的雙列數據的集合;
List 中存儲的數據是有順序,並且允許重複;Map 中存儲 的數據是沒有順序的,其鍵是不能重複的,它的值是可以有重複的。
55.List, Set, Map 是否繼承自 Collection 接口?
List,Set 是繼承自 Collection 接口,Map 不繼承自 Collection 接口。
Collection集合的子接口有 Set、List、Queue 三種子接口 。 我們比較常用的是Set、List,還有Map接口,不過Map不是collection的子接口。
List:
- 可以允許重複的對象。
- 可以插入多個null元素。
- 是一個有序容器,保持了每個元素的插入順序,輸出的順序就是插入的順序。
- 常用的實現類有 ArrayList、LinkedList 和 Vector。
Set:
- 不允許重複對象
- 無序容器,你無法保證每個元素的存儲順序,TreeSet通過 Comparator 或者 Comparable 維護了一個排序順序。
- 只允許一個 null 元素
- Set 接口最流行的幾個實現類是 HashSet、LinkedHashSet 以及 TreeSet。
Map:
- Map不是collection的子接口或者實現類。Map是一個接口。
- Map 的 每個 Entry 都持有兩個對象,也就是一個鍵一個值(鍵值對),Map 可能會持有相同的值對象但鍵對象必須是唯一的。
- TreeMap 也通過 Comparator 或者 Comparable 維護了一個排序順序。
- Map 裏你可以擁有隨意個 null 值但最多隻能有一個 null 鍵。
- Map 接口最流行的幾個實現類是 HashMap、LinkedHashMap、Hashtable 和 TreeMap。(HashMap、TreeMap最常用)
56.List、Map、Set 三個接口,存取元素時,各有什麼特點?
List 按序來持有元素,可有重複元素。Set 無法擁有重複元素,內部排序。Map 保存鍵值對,value可多值。
List :元素有放入順序,元素可重複
Set :元素無放入順序,元素不可重複(注意:元素雖然無放入順序,但是元素在set中的位置是有該元素的HashCode決定的,其位置其實是固定的)
Map :元素按鍵值對存儲,無放入順序,鍵不可重複,值可重複
List 接口有三個實現類:LinkedList,ArrayList,Vector
LinkedList :底層基於鏈表實現,鏈表內存是散亂的,每一個元素存儲本身內存地址的同時還存儲下一個元素的地址。鏈表增刪快,查找慢。
ArrayList 和Vector 的區別:ArrayList是非線程安全的,效率高;Vector是基於線程安全的,效率低。
Set 接口有兩個實現類:HashSet(底層由HashMap實現),LinkedHashSet
SortedSet 接口有一個實現類:TreeSet(底層由平衡二叉樹實現)
Query 接口有一個實現類:LinkList
Map 接口有三個實現類:HashMap,HashTable,LinkeHashMap
HashMap 非線程安全,高效,支持null;HashTable線程安全,低效,不支持null
SortedMap 有一個實現類:TreeMap
57.說出 ArrayList,Vector, LinkedList 的存儲性能和特性
ArrayList 和 Vector 都是使用數組方式存儲數據,此數組元素數大於實際存儲的數據以便增加和插入元素,它們都允許直接按序號索引元素,但是插入元素要涉及數組元素移動等內存操作,所以索引數據快,插入數據慢;
Vector 由於使用了synchronized 方法(線程安全),通常性能上較ArrayList 差;
LinkedList 使用雙向鏈表實現存儲,按序號索引數據需要進行前向或後向遍歷,但是插入數據時只需要記錄本項的前後項即可,所以插入速度較快。
ArrayList 和 Vector 他們底層的實現都是一樣的,都是使用數組方式存儲數據,此數組元素數大於實際存儲的數據以便增加和插入元素,它們都允許直接按序號索引元素,但是插入元素要涉及數組元素移動等內存操作,所以索引數據快而插入數據慢。
Vector 中的方法由於添加了 synchronized 修飾,因此 Vector 是線程安全的容器,但性能上較 ArrayList 差,因此已經是Java中的遺留容器。
LinkedList使用雙向鏈表實現存儲(將內存中零散的內存單元通過附加的引用關聯起來,形成一個可以按序號索引的線性結構,這種鏈式存儲方式與數組的連續存儲方式相比,內存的利用率更高),按序號索引數據需要進行前向或後向遍歷,但是插入數據時只需要記錄本項的前後項即可,所以插入速度較快。
補充:Vector 屬於遺留容器
(Java早期的版本中提供的容器,除此之外,Hashtable、Dictionary、BitSet、Stack、Properties都是遺留容器),已經不推薦使用,但是由於 ArrayList 和 LinkedListed 都是非線程安全的,如果遇到多個線程操作同一個容器的場景,則可以通過工具類 Collections 中的 synchronizedList 方法將其轉換成線程安全的容器後再使用(這是對裝潢模式的應用,將已有對象傳入另一個類的構造器中創建新的對象來增強實現)。
58.去掉一個 Vector 集合中重複的元素
59.Collection 和 Collections 的區別。
Collection是個java.util下的接口,它是各種集合結構的父接口,繼承與他的接口主要有 Set 和 List。
Collections是個java.util下針對集合類的一個幫助類,它包含各種與集合有關的搜索、排序、線程安全化等操作的靜態方法。
60.Set 裏的元素是用什麼方法來區分重複的呢? 是用==還是 equals()? 它們有何區別?
Set裏的元素可以通過迭代器遍歷出來,Set判斷元素是否重複是使用 equals()方法進行判斷的。
equals() 和 == 方法決定引用值是否指向同一對象,equals()在類中被覆蓋,爲的是當兩個分離的對象的內容和類型相配的話,返回真值。