java 集合類基礎問題匯總


 1、Java集合類框架的基本接口有哪些?

參考答案

集合類接口指定了一組叫做元素的對象。集合類接口的每一種具體的實現類都可以選擇以它自己的方式對元素進行保存和排序。有的集合類允許重復的鍵,有些不允許。
Java集合類提供了一套設計良好的支持對一組對象進行操作的接口和類。Java集合類里面最基本的接口有:
Collection:代表一組對象,每一個對象都是它的子元素。
Set:不包含重復元素的Collection。
List:有順序的collection,並且可以包含重復元素。
Map:可以把鍵(key)映射到值(value)的對象,鍵不能重復。
 

2、為什么集合類沒有實現Cloneable和Serializable接口?

參考答案

克隆(cloning)或者是序列化(serialization)的語義和含義是跟具體的實現相關的。因此,應該由集合類的具體實現來決定如何被克隆或者是序列化。
 

3、什么是迭代器(Iterator)?

參考答案

Iterator接口提供了很多對集合元素進行迭代的方法。每一個集合類都包含了可以返回迭代器實例的
迭代方法。迭代器可以在迭代的過程中刪除底層集合的元素,但是不可以直接調用集合的
remove(Object Obj)刪除,可以通過迭代器的remove()方法刪除。
 

4、Iterator和ListIterator的區別是什么?

參考答案

下面列出了他們的區別:
Iterator可用來遍歷Set和List集合,但是ListIterator只能用來遍歷List。
Iterator對集合只能是前向遍歷,ListIterator既可以前向也可以后向。
ListIterator實現了Iterator接口,並包含其他的功能,比如:增加元素,替換元素,獲取前一個和后一個元素的索引,等等。
 

5、快速失敗(fail-fast)和安全失敗(fail-safe)的區別是什么?

參考答案

Iterator的安全失敗是基於對底層集合做拷貝,因此,它不受源集合上修改的影響。java.util包下面的所有的集合類都是快速失敗的,而java.util.concurrent包下面的所有的類都是安全失敗的。快速失敗的迭代器會拋出ConcurrentModificationException異常,而安全失敗的迭代器永遠不會拋出這樣的異常。
 

6、Java中的HashMap的工作原理是什么?

參考答案

Java中的HashMap是以鍵值對(key-value)的形式存儲元素的。HashMap需要一個hash函數,它使用hashCode()和equals()方法來向集合/從集合添加和檢索元素。當調用put()方法的時候,HashMap會計算key的hash值,然后把鍵值對存儲在集合中合適的索引上。如果key已經存在了,value會被更新成新值。HashMap的一些重要的特性是它的容量(capacity),負載因子(load factor)和擴容極限(threshold resizing)。
 

7、hashCode()和equals()方法的重要性體現在什么地方?

參考答案

Java中的HashMap使用hashCode()和equals()方法來確定鍵值對的索引,當根據鍵獲取值的時候也會用到這兩個方法。如果沒有正確的實現這兩個方法,兩個不同的鍵可能會有相同的hash值,因此,可能會被集合認為是相等的。而且,這兩個方法也用來發現重復元素。所以這兩個方法的實現對HashMap的精確性和正確性是至關重要的。
 
 

8、HashMap和Hashtable有什么區別?

參考答案

HashMap和Hashtable都實現了Map接口,因此很多特性非常相似。但是,他們有以下不同點:
HashMap允許鍵和值是null,而Hashtable不允許鍵或者值是null。
Hashtable是同步的,而HashMap不是。因此,HashMap更適合於單線程環境,而Hashtable適合於多線程環境。
HashMap提供了可供應用迭代的鍵的集合,因此,HashMap是快速失敗的。另一方面,Hashtable提供了對鍵的列舉(Enumeration)。
一般認為Hashtable是一個遺留的類。

適用場景分析:

HashMap和HashTable:HashMap去掉了HashTable的contains方法,但是加上了containsValue()和containsKey()方法。HashTable同步的,而HashMap是非同步的,效率上比HashTable要高。HashMap允許空鍵值,而HashTable不允許。

HashMap:適用於Map中插入、刪除和定位元素。

Treemap:適用於按自然順序或自定義順序遍歷鍵(key)。

 

9、數組(Array)和列表(ArrayList)有什么區別?什么時候應該使用Array而不是ArrayList?

參考答案

下面列出了Array和ArrayList的不同點:
Array可以包含基本類型和對象類型,ArrayList只能包含對象類型。
Array大小是固定的,ArrayList的大小是動態變化的。
ArrayList提供了更多的方法和特性,比如:addAll(),removeAll(),iterator()等等。
對於基本類型數據,集合使用自動裝箱來減少編碼工作量。但是,當處理固定大小的基本數據類型的時候,這種方式相對比較慢。
 

10、ArrayList和LinkedList有什么區別?

參考答案

ArrayList和LinkedList都實現了List接口,他們有以下的不同點:
ArrayList是基於索引的數據接口,它的底層是數組。它可以以O(1)時間復雜度對元素進行隨機訪問。與此對應,LinkedList是以元素列表的形式存儲它的數據,每一個元素都和它的前一個和后一個元素鏈接在一起,在這種情況下,查找某個元素的時間復雜度是O(n)。
相對於ArrayList,LinkedList的插入,添加,刪除操作速度更快,因為當元素被添加到集合任意位置的時候,不需要像數組那樣重新計算大小或者是更新索引。
LinkedList比ArrayList更占內存,因為LinkedList為每一個節點存儲了兩個引用,一個指向前一個元素,一個指向下一個元素。
也可以參考ArrayList vs. LinkedList。

適用場景分析:

當需要對數據進行對此訪問的情況下選用ArrayList,當需要對數據進行多次增加刪除修改時采用LinkedList。

 

11、Comparable和Comparator接口是干什么的?列出它們的區別。

參考答案

Java提供了只包含一個compareTo()方法的Comparable接口。這個方法可以個給兩個對象排序。具體來說,它返回負數,0,正數來表明已經存在的對象小於,等於,大於輸入對象。
Java提供了包含compare()和equals()兩個方法的Comparator接口。compare()方法用來給兩個輸入參數排序,返回負數,0,正數表明第一個參數是小於,等於,大於第二個參數。equals()方法需要一個對象作為參數,它用來決定輸入參數是否和comparator相等。只有當輸入參數也是一個comparator並且輸入參數和當前comparator的排序結果是相同的時候,這個方法才返回true。
 

12、什么是Java優先級隊列(Priority Queue)?

參考答案

PriorityQueue是一個基於優先級堆的無界隊列,它的元素是按照自然順序(natural order)排序的。在創建的時候,我們可以給它提供一個負責給元素排序的比較器。PriorityQueue不允許null值,因為他們沒有自然順序,或者說他們沒有任何的相關聯的比較器。最后,PriorityQueue不是線程安全的,入隊和出隊的時間復雜度是O(log(n))。
 

13、你了解大O符號(big-O notation)么?你能給出不同數據結構的例子么?

參考答案

大O符號描述了當數據結構里面的元素增加的時候,算法的規模或者是一個漸進上界 。
大O符號也可用來描述其他的行為,比如:內存消耗。因為集合類實際上是數據結構,我們一般使用大O符號基於時間,內存和性能來選擇最好的實現。大O符號可以對大量數據的性能給出一個很好的說明。
 

14、如何權衡是使用無序的數組還是有序的數組?

參考答案

有序數組最大的好處在於查找的時間復雜度是O(log n),而無序數組是O(n)。有序數組的缺點是插入操作的時間復雜度是O(n),因為值大的元素需要往后移動來給新元素騰位置。相反,無序數組的插入時間復雜度是常量O(1)。
 

15、Java集合類框架的最佳實踐有哪些?

參考答案

根據應用的需要正確選擇要使用的集合的類型對性能非常重要,比如:假如元素的數量是固定的,而且能事先知道,我們就應該用Array而不是ArrayList。
有些集合類允許指定初始容量。因此,如果我們能估計出存儲的元素的數目,我們可以設置初始容量來避免重新計算hash值或者是擴容。
為了類型安全,可讀性和健壯性的原因總是要使用泛型。同時,使用泛型還可以避免運行時的ClassCastException。
使用JDK提供的不變類(immutable class)作為Map的鍵可以避免為我們自己的類實現hashCode()和equals()方法。
編程的時候接口優於實現。
底層的集合實際上是空的情況下,返回長度是0的集合或者是數組,不要返回null。
 

16、Enumeration接口和Iterator接口的區別有哪些?

參考答案

Enumeration速度是Iterator的2倍,同時占用更少的內存。但是,Iterator遠遠比Enumeration安全,因為其他線程不能夠修改正在被iterator遍歷的集合里面的對象。同時,Iterator允許調用者刪除底層集合里面的元素,這對Enumeration來說是不可能的。

 

17、HashSet和TreeSet有什么區別?

參考答案

1.TreeSet 是二叉樹(紅黑樹的樹據結構)實現的,Treeset中的數據是自動排好序的,不允許放入null值。

2.HashSet 是哈希表實現的,HashSet中的數據是無序的,可以放入null,但只能放入一個null,兩者中的值都不能重復,就如數據庫中唯一約束。

3.HashSet要求放入的對象必須實現HashCode()方法,放入的對象,是以hashcode碼作為標識的,而具有相同內容的String對象,hashcode是一樣,所以放入的內容不能重復。但是同一個類的對象可以放入不同的實例。

HashSet是由一個hash表來實現的,因此,它的元素是無序的。add(),remove(),contains()方法的時間復雜度是O(1)。
另一方面,TreeSet是由一個樹形的結構來實現的,它里面的元素是有序的。因此,add(),remove(),contains()方法的時間復雜度是O(logn)。
 

適用場景分析:

HashSet是基於Hash算法實現的,其性能通常都優於TreeSet。為快速查找而設計的Set,我們通常都應該使用HashSet,在我們需要排序的功能時,我們才使用TreeSet。

 

18、集合框架,list,map,set都有哪些具體的實現類,區別都是什么?

參考答案 

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是線程安全的。

 

19、ArrayList與Vector的區別和適用場景

 參考答案 

ArrayList有三個構造方法:

public ArrayList(int initialCapacity)//構造一個具有指定初始容量的空列表。    
public ArrayList()//構造一個初始容量為10的空列表。    
public ArrayList(Collection<? extends E> c)//構造一個包含指定 collection 的元素的列表    

Vector有四個構造方法:

public Vector() // 使用指定的初始容量和等於零的容量增量構造一個空向量。    
public Vector(int initialCapacity) // 構造一個空向量,使其內部數據數組的大小,其標准容量增量為零。    
public Vector(Collection<? extends E> c)// 構造一個包含指定 collection 中的元素的向量    
public Vector(int initialCapacity,int capacityIncrement)//使用指定的初始容量和容量增量構造一個空的向量

ArrayList和Vector都是用數組實現的,主要有這么三個區別:

1).Vector是多線程安全的,線程安全就是說多線程訪問同一代碼,不會產生不確定的結果。而ArrayList不是,這個可以從源碼中看出,Vector類中的方法很多有synchronized進行修飾,這樣就導致了Vector在效率上無法與ArrayList相比;

2).兩個都是采用的線性連續空間存儲元素,但是當空間不足的時候,兩個類的增加方式是不同。

3).Vector可以設置增長因子,而ArrayList不可以。

4).Vector是一種老的動態數組,是線程同步的,效率很低,一般不贊成使用。

 

適用場景:

1.Vector是線程同步的,所以它也是線程安全的,而ArrayList是線程異步的,是不安全的。如果不考慮到線程的安全因素,一般用ArrayList效率比較高。

2.如果集合中的元素的數目大於目前集合數組的長度時,在集合中使用數據量比較大的數據,用Vector有一定的優勢。



class A {}
class B extends A {}
class C extends A {}
class D extends B {}
Which four statements are true ?
  • The type List<A>is assignable to List.
  • The type List<B>is assignable to List<A>.
  • The type List<Object>is assignable to List<?>.
  • The type List<D>is assignable to List<?extends B>.
  • The type List<?extends A>is assignable to List<A>.
  • The type List<Object>is assignable to any List reference.
  • The type List<?extends B>is assignable to List<?extends A>.
查看正確選項
正確答案:A C D G
 
1. 只看尖括號里邊的!!明確點和范圍兩個概念
2. 如果尖括號里的是一個類,那么尖括號里的就是一個點,比如List<A>,List<B>,List<Object>
3. 如果尖括號里面帶有問號,那么代表一個范圍,<? extends A> 代表小於等於A的范圍,<? super A>代表大於等於A的范圍,<?>代表全部范圍
4. 尖括號里的所有點之間互相賦值都是錯,除非是倆相同的點
5. 尖括號小范圍賦值給大范圍,對,大范圍賦值給小范圍,錯。如果某點包含在某個范圍里,那么可以賦值,否則,不能賦值
6. List<?>和List 是相等的,都代表最大范圍
----------------------------------------------------------------------------------
7.補充:List既是點也是范圍,當表示范圍時,表示最大范圍
 
下面貼代碼:(本來用的Eclipse截圖的,結果好像網站出來Bug,不能傳圖了o(╯□╰)o
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public static void main(String[] args) {
         List<A> a;
         List list;
         list = a;    //A對,因為List就是List<?>,代表最大的范圍,A只是其中的一個點,肯定被包含在內
         List<B> b;
         a = b;       //B錯,點之間不能相互賦值
         List<?> qm;
         List<Object> o;
         qm = o;      //C對,List<?>代表最大的范圍,List<Object>只是一個點,肯定被包含在內
         List<D> d;
         List<?  extends B> downB;
         downB = d;   //D對,List<? extends B>代表小於等於B的范圍,List<D>是一個點,在其中
         List<? extends A> downA;
         a = downA;   //E錯,范圍不能賦值給點
         a = o;       //F錯,List<Object>只是一個點
         downA = downB;   //G對,小於等於A的范圍包含小於等於B的范圍,因為B本來就比A小,B時A的子類嘛
     }

 

大概的觀點就是:java數組具有協變性,而java集合不是協變的;
什么意思呢?我舉幾個例子:
1. 假設有一個函數 fun(Animal animal),如果我們傳入一個Dog d 對象進去,編譯器是不會報錯的,這是多態的概念;
2. 假設有一個函數 fun(Animal[] animals),如果我們傳如一個Dog[] dogs數組進去,編譯器也不會報錯,這就是數組的協變性;
3. 假設有一個函數 fun(List<Animal>  animal), 如果我們傳如一個List <Dog>  dog 集合進去,編譯器就會報錯了,這就是集合泛型的不變性;
那么該怎么辦呢?我們可以將泛型改成這樣 fun (List <? extends Animal> ),這樣之后,當我們再 傳入一個List <Dog>  dog 集合進去,編譯器就就不會報錯了。也就是說可以傳入包含Animal的子類的List了。

 

 

在Java中,關於HashMap類的描述,以下錯誤的是()?

  • HashMap能夠保證其中元素的順序
  • HashMap允許將null用作值
  • HashMap允許將null用作鍵
  • HashMap使用鍵/值得形式保存數據
參考答案:A
HashMap的底層是由數組加鏈表實現的,對於每一個key值,都需要計算哈希值,然后通過哈希值來確定順序,並不是按照加入順序來存放的,因此可以認為是無序的,但不管是有序還是無序,它都一個自己的順序。故A錯。
最開始有Hashtable,Hashtable是不允許key和value的值為空的,但后來開發者認為有時候也會有key值為空的情況,因為可以允許null為空,通過查看HashMap的源代碼就知道:if(key = null) {putForNullKey(value);};
Map底層都是用key/value鍵值對的形式存放的
 
 
在jdk1.5的環境下,有如下4條語句:
Integer i01 =  59 ;
int i02 =  59 ;
Integer i03 =Integer.valueOf( 59 );
Integer i04 =  new Integer( 59 )。
 
以下輸出結果為false的是:

正確答案: C   

System.out.println(i01== i02);
System.out.println(i01== i03);
System.out.println(i03== i04);
System.out.println(i02== i04);

Integer i01=59 的時候,會調用 Integer 的 valueOf 方法,

1
2
3
4
5
   public static Integer valueOf( int i) {
      assert IntegerCache.high>=  127 ;
      if (i >= IntegerCache.low&& i <= IntegerCache.high)
      return IntegerCache.cache[i+ (-IntegerCache.low)];
      return new Integer(i); }

這個方法就是返回一個 Integer 對象,只是在返回之前,看作了一個判斷,判斷當前 i 的值是否在 [-128,127] 區別,且 IntegerCache 中是否存在此對象,如果存在,則直接返回引用,否則,創建一個新的對象。

在這里的話,因為程序初次運行,沒有 59 ,所以,直接創建了一個新的對象。

int i02=59 ,這是一個基本類型,存儲在棧中。

Integer i03 =Integer.valueOf(59); 因為 IntegerCache 中已經存在此對象,所以,直接返回引用。 

Integer i04 = new Integer(59) ;直接創建一個新的對象。

System. out .println(i01== i02); i01 是 Integer 對象, i02 是 int ,這里比較的不是地址,而是值。 Integer 會自動拆箱成 int ,然后進行值的比較。所以,為真。

System. out .println(i01== i03); 因為 i03 返回的是 i01 的引用,所以,為真。

System. out .println(i03==i04); 因為 i04 是重新創建的對象,所以 i03,i04 是指向不同的對象,因此比較結果為假。

System. out .println(i02== i04); 因為 i02 是基本類型,所以此時 i04 會自動拆箱,進行值比較,所以,結果為真。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM