1. 程式人生 > >Java中如何高效的判斷陣列中是否包含某個元素

Java中如何高效的判斷陣列中是否包含某個元素

如何檢查一個數組(無序)是否包含一個特定的值?這是一個在Java中經常用到的並且非常有用的操作。同時,這個問題在Stack Overflow中也是一個非常熱門的問題。在投票比較高的幾個答案中給出了幾種不同的方法,但是他們的時間複雜度也是各不相同的。本文將分析幾種常見用法及其時間成本。

檢查陣列是否包含某個值的方法

使用List

publicstaticboolean useList(String[] arr,String targetValue){returnArrays.asList(arr).contains(targetValue);}

使用Set

publicstaticboolean
useSet(String[] arr,String targetValue){Set<String>set=newHashSet<String>(Arrays.asList(arr));returnset.contains(targetValue);}

使用迴圈判斷

publicstaticboolean useLoop(String[] arr,String targetValue){for(String s: arr){if(s.equals(targetValue))returntrue;}returnfalse;}

使用Arrays.binarySearch()

Arrays.binarySearch()方法只能用於有序陣列!!!如果陣列無序的話得到的結果就會很奇怪。

查詢有序陣列中是否包含某個值的用法如下:

publicstaticboolean useArraysBinarySearch(String[] arr,String targetValue){int a =Arrays.binarySearch(arr, targetValue);if(a >0)returntrue;elsereturnfalse;}

時間複雜度

下面的程式碼可以大概的得出各種方法的時間成本。基本思想就是從陣列中查詢某個值,陣列的大小分別是5、1k、10k。這種方法得到的結果可能並不精確,但是是最簡單清晰的方式。

publicstaticvoid main(String[] args){String[] arr =newString[]{"CD","BC","EF","DE","AB"};//use listlong startTime =System.nanoTime();for(int i =0; i <100000; i++){
        useList(arr,"A");}long endTime =System.nanoTime();long duration = endTime - startTime;System.out.println("useList:  "+ duration /1000000);//use set
    startTime =System.nanoTime();for(int i =0; i <100000; i++){
        useSet(arr,"A");}
    endTime =System.nanoTime();
    duration = endTime - startTime;System.out.println("useSet:  "+ duration /1000000);//use loop
    startTime =System.nanoTime();for(int i =0; i <100000; i++){
        useLoop(arr,"A");}
    endTime =System.nanoTime();
    duration = endTime - startTime;System.out.println("useLoop:  "+ duration /1000000);//use Arrays.binarySearch()
    startTime =System.nanoTime();for(int i =0; i <100000; i++){
        useArraysBinarySearch(arr,"A");}
    endTime =System.nanoTime();
    duration = endTime - startTime;System.out.println("useArrayBinary:  "+ duration /1000000);}

執行結果:

useList:13
useSet:72
useLoop:5
useArraysBinarySearch:9

使用一個長度為1k的陣列

String[] arr =newString[1000];Random s =newRandom();for(int i=0; i<1000; i++){
    arr[i]=String.valueOf(s.nextInt());}

結果:

useList:112
useSet:2055
useLoop:99
useArrayBinary:12

使用一個長度為10k的陣列

String[] arr =newString[10000];Random s =newRandom();for(int i=0; i<10000; i++){
    arr[i]=String.valueOf(s.nextInt());}

結果:

useList:1590
useSet:23819
useLoop:1526
useArrayBinary:12

總結

顯然,使用一個簡單的迴圈方法比使用任何集合都更加高效。許多開發人員為了方便,都使用第一種方法,但是他的效率也相對較低。因為將陣列壓入Collection型別中,首先要將陣列元素遍歷一遍,然後再使用集合類做其他操作。

如果使用Arrays.binarySearch()方法,陣列必須是已排序的。由於上面的陣列並沒有進行排序,所以該方法不可使用。

實際上,如果你需要藉助陣列或者集合類高效地檢查陣列中是否包含特定值,一個已排序的列表或樹可以做到時間複雜度為O(log(n)),hashset可以達到O(1)。

(英文原文結束,以下是譯者注)

使用ArrayUtils

除了以上幾種以外,Apache Commons類庫中還提供了一個ArrayUtils類,可以使用其contains方法判斷陣列和值的關係。

import org.apache.commons.lang3.ArrayUtils;publicstaticboolean useArrayUtils(String[] arr,String targetValue){returnArrayUtils.contains(arr,targetValue);}

同樣使用以上幾種長度的陣列進行測試,得出的結果是該方法的效率介於使用集合和使用迴圈判斷之間(有的時候結果甚至比使用迴圈要理想)。

useList:323
useSet:3028
useLoop:141
useArrayBinary:12
useArrayUtils:181-------
useList:3703
useSet:35183
useLoop:3218
useArrayBinary:14
useArrayUtils:3125

其實,如果檢視ArrayUtils.contains的原始碼可以發現,他判斷一個元素是否包含在陣列中其實也是使用迴圈判斷的方式。

部分程式碼如下:

if(array ==null){return-1;}else{if(startIndex <0){
            startIndex =0;}int i;if(objectToFind ==null){for(i = startIndex; i < array.length;++i){if(array[i]==null){return i;}}}elseif(array.getClass().getComponentType().isInstance(objectToFind)){for(i =