首页 > 代码库 > 如何高效的检测一个数组是否包含某一个值

如何高效的检测一个数组是否包含某一个值

如何检测一个数组(未排序)是否包含一个指定的值?这在Java中是一个非常有用且常见的操作。这还是一个在stackoverflow投票最多的一个问题。在投票最多的答案中,有几种不同的方式来完成这个问题。但是时间复杂度存在很大的差异。下面,我将展示每个方法所花费的时间。

1.检测数组中是否包含某一个值的四种方式

1)使用List

public static boolean useList(String[] arr, String targetValue) {
	return Arrays.asList(arr).contains(targetValue);
}

2)使用Set

public static boolean useSet(String[] arr, String targetValue) {
	Set<String> set = new HashSet<String>(Arrays.asList(arr));
	return set.contains(targetValue);
}

3)使用一个简单的循环

public static boolean useLoop(String[] arr, String targetValue) {
	for(String s: arr){
		if(s.equals(targetValue))
			return true;
	}
	return false;
}

4)使用Arrays.binarySearch()

*下面的代码时错误的,为了完整性我们列举了出来。一个排序后的数组才能使用binarySearch()方法。运行下面的代码你会发现结果有点怪异。

public static boolean useArraysBinarySearch(String[] arr, String targetValue) {	
	int a =  Arrays.binarySearch(arr, targetValue);
	if(a > 0)
		return true;
	else
		return false;
}

2.时间复杂度

利用下面的代码我们可以计算出每种方式大致花费的时间。基本思路就是在大小分别为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);
}

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

使用更大一点的数组(1k):
String[] arr = new String[1000];
 
Random s = new Random();
for(int i=0; i< 1000; i++){
	arr[i] = String.valueOf(s.nextInt());
}

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

使用更大一点的数组(10k):
String[] arr = new String[10000];
 
Random s = new Random();
for(int i=0; i< 10000; i++){
	arr[i] = String.valueOf(s.nextInt());
}

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

明显:使用循环比使用集合效率要高。很多开发者使用第一种方法,但它是低效的。Pushing the array to another collection requires spin through all elements to read them in before doing anything with the collection type。
如果使用Arrays.binarySearch()方法,必须先对数组进行排序。在这例子中,数组并没有排序,所有不应该binarySearch()这个方法。
实际上,如果你确实需要检测一些数组/集合中是否包含某个值,你可以使用一个排序的列表或树,其时间复杂度为O(log(n))或者使用hashset,其时间复杂度为O(1)。

如何高效的检测一个数组是否包含某一个值