怎樣判斷一個無序數組是否包含某個特定值?這在JAVA中是一個非常實用的操作,在Stack Overflow問答網站中也同樣是一個熱門問題;
要完成這個判斷,可以通過若干種不同的方式實現,每種實現方式對應的時間復雜讀會有很大的不同;
接下來我將展示不同實現方式的時間開銷。
public static boolean useList(String[] arr, String targetValue) { return Arrays.asList(arr).contains(targetValue); }
public static boolean useSet(String[] arr, String targetValue) { Set<String> set = new HashSet<String>(Arrays.asList(arr)); return set.contains(targetValue); }
public static boolean useLoop(String[] arr, String targetValue) { for (String s : arr) { if (s.equals(targetValue)) return true; } return false; }
下面的代碼是錯誤的,之所以列在下面是出於完整性考慮(四種判斷方式),binarySearch()二分查找只能用於有序數組。
運行下面程序,你有可能會得到異常結果;
public static boolean useArraysBinarySearch(String[] arr, String targetValue) { int a = Arrays.binarySearch(arr, targetValue); if (a > 0) return true; else return false; }
以下代碼可計算出以上四種實現方式大致的時間消耗,基本策略是使用不同大小的數組(5,1k,10k)做測試,可能不是很精准,但這種方式很簡單;
public static void main(String[] args) { String[] arr = new String[] { "CD", "BC", "EF", "DE", "AB" }; // use list long 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); }
運行結果:
String[] arr = new String[1000]; Random s = new Random(); for (int i = 0; i < 1000; i++) { arr[i] = String.valueOf(s.nextInt()); }
運行結果:
String[] arr = new String[10000]; Random s = new Random(); for (int i = 0; i < 10000; i++) { arr[i] = String.valueOf(s.nextInt()); }
運行結果:
從測試結果可以看出,使用簡單的循環語句比使用任何集合都高效,很大一部分開發人員選擇使用第一種方法(List),但這種方法其實是相對低效的。在使用集合提供的API前,需要把一個數組放到集合裡,這需要消耗一定的時間,特別是對於Set集合;(注:其實ArrayList集合的性能跟普通的循環語句差不多,因為對於ArrayList,轉換成集合的時候,僅僅是改變了內部的數組索引,遍歷判斷的時候,跟普通的循環語句類似);
如果要使用Arrays.binarySearch()方法,前提是數組要有序,在這個測試demo中,很顯然數組是無序的,因此不該被使用;
事實上,如果你確實需要高效的去檢查數組或集合中是否包含某個值,一個有序列表或者有序樹能把時間復雜度降低到O(log(n)),或者使用散列集合,時間復雜度為O(1);
譯文鏈接:http://www.programcreek.com/2014/04/check-if-array-contains-a-value-java/