1. 程式人生 > >關於集合中元素的有序無序的易混淆點

關於集合中元素的有序無序的易混淆點

最近在整理Java基礎知識的面試題,看到了一個題目的答案不夠準確,這裡跟大家分享一下。

一、面試題的小錯誤

 對於TreeSet和TreeMap來說,元素應該是無序(指元素的存取)而不是有序的,而在表中它可能想表達的是可以排序,不夠嚴謹,嚴格來講

元素的有序≠可以排序。元素的存取有序和排序本質上應該是兩碼事,不應該混為一聽。

二、元素的有序和無序

   下面具體來講講,集合中元素的存取有序的問題。 

  眾所周知,List的特點:    ①元素可重複 ②元素是有序的

  相對地,Set的特點是:    ①元素唯一    ②元素是無序的

  首先,我們來明確這裡的有序/無序的準確定義。

  有序是指元素的存取順序是一致的,即你以什麼順序將元素存入List,比如1,3,4,13,那麼取(遍歷)出來的順序也是1,3,4,13。

  無序是指元素的存取順序是不一致的(這裡注意,事實上會有小概率可能,你可能碰巧發現你的set中元素的存取順序很巧合地一致了,不符合這個定義了,那麼set中的元素究竟是不是一致的呢?我們下面會講解)

 三、具體例子

  ①List

  List中元素的存取是有序的,有序很好理解,比如一個數組arr(以ArrayList為例),該陣列的輸出順序是跟你存入(add)的順序是一致的,為 15 , 2, 3, 9。

import java.util.ArrayList;
import java.util.List;
public class Test {

    public static void main(String[] args) {
          List<Integer> arr=new ArrayList();
            arr.add(15);
            arr.add(2);
            arr.add(3);
            arr.add(9);
             for(int a:arr) {
            System.out.println(a);
        }
    // 輸出結果   15   2    3   9 
     //arraylist的元素存取是有序的
    }
}

 ②Set

import java.util.HashSet;
import java.util.Set;

public class Test {

	public static void main(String[] args) {
		Set<Integer> set=new HashSet();
		set.add(122);
		set.add(234);
		set.add(2);
		set.add(12);
for(Integer s:set) { System.out.println(s+" "+s.hashCode()); } // 輸出為 2 122 234 12 //HashSet是元素無序的 } }

  Set中元素的存取是無序的。那麼為什麼會出現小概率的“有序”情況呢?

  以HashSet來講,HashSet的底層是Hash表,我們每次add元素進去後,每個元素將呼叫hashcode方法(匹配1個Hash函式)得到一個對應的hashcode值,然後根據這個hashcode值在hash表中對應的位置存入元素。舉例來說,我們現在存的順序是123,22,12,3。而取出來的時候是按照這些元素在Hash表中的真正的儲存位置順序(或者逆序或者某種規則)遍歷,所以便會出現存取順序不一致的情況。也就是說,Hash表內部的排序是根據hash值而不是add的先後順序來排序的,而上面出現的特殊情況,也就是說正好你的元素的add順序和它對應的Hash值在Hash表中的排列順序剛好一致,讓你感覺上好像變成有序了。其實,從Hash表的角度上來講,HashSet中元素的存取並不是完全無序(隨機)的,只不過它是按照自己的規則來存入和輸出元素(Hash表中的Hash值的大小順序)。

  同理,對於TreeMap來說,其底層為紅黑樹。add元素的先後順序並不重要,真正的儲存順序是按照二叉樹的規則儲存的,所以最後遍歷都是自然排序後的輸出。

四、總結

  最後總結:從本質上講,List和Set內元素有序/無序的核心區別

  List的元素有序是因為它的底層是線性結構的陣列(或者連結串列),元素的存取是和add的先後(時間)次序有關的——本質上與時間相關

  Set的元素無序是因為它的底層是非線性結構的雜湊表/二叉樹,元素的存取是和add的先後(時間)順序無關的——你先add還是後add,都是存在Hash表中的同一個位置/在二叉樹中,無論你先add還是後add,最後都輸出的是排序(預設自然排序)後的陣列——本質上和時間無關