一、list.contains(Obj)
源码
public boolean contains(Object o) {
return indexOf(o) >= 0;
}
public int indexOf(Object o) {
if (o == null) {//#1
for (int i = 0; i < size; i++)//#2
if (elementData[i]==null)//#3
return i;//#4
} else {//#5
for (int i = 0; i < size; i++)//#6
if (o.equals(elementData[i]))//#7
return i;//#8
}
return -1;//#9
}
#1判断指定对象类型是否为null,如果为空进行#2;如果不为空,进行#5。
#2进行for循环,size指的是当前ArrayList大小
#3判断ArrayList中元素是否为空,elementData是ArrayList里的元素。如果为空进行#4.
#4返回i,说明在ArrayList中找到了为null的位置。
#5如果指定对象(indexOf传入的参数)不为空,则进行#6;
#6进行for循环。
#7判断elementData数组中是否存在与指定元素相等的元素,如果有,则进行#8。这里的equals与指定值类型和列表存储值类型有关。
#8返回与指定元素相等的索引位置。
#9如果没有找到,则返回-1。
二、map.containsKey(Str)和map.containsValus(Obj)
//传入参数:key值
public boolean containsKey(Object key)
{
//调用核心方法getNode,判断是否存在对应节点
return getNode(hash(key), key) != null;
}
这段有点看不懂,我吐了
//传入参数:1.根据key散列计算得到的哈希值 2.key值
final Node<K,V> getNode(int hash, Object key)
{
Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
//判断表是否为空,判断key对应的链表节点是否存在
if ((tab = table) != null && (n = tab.length) > 0&&(first = tab[(n - 1) & hash]) != null)
{
//判断链表的头部元素是否是key值对应的真实value,对比项:1.hash 2.key值
if (first.hash == hash && ((k = first.key) == key || (key != null && key.equals(k))))
return first;
if ((e = first.next) != null)
{
//判断是否是红黑树
if (first instanceof TreeNode)
//走红黑树逻辑,从红黑树中获取对应的value
return ((TreeNode<K,V>)first).getTreeNode(hash, key);
//遍历链表
do
{
//判断链表下个元素是否是key值对应的真实value,对比项:1.hash 2.key值
if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k))))
return e;
} while ((e = e.next) != null);
}
}
//没有找到对应节点则返回null
return null;
}
三、数组
在java中,我们如何判断一个未排序数组中是否包含一个特定的值?这在java中是一个频繁非常实用的操作。那么什么样的方法才是最高效的方式?当然,这个问题在Stack Overflow也是得票率非常高的一个问答。得票率排在最前的几个答案给出集中不同的方法,但是他们的时间复杂度却相差甚远。
本文将详细的探讨主流的方法,并给出他们各自的时间损耗。
四种方法
1、List (快的,最常用的)
public static boolean useList(String[] arr,String value){
return Arrays.asList(arr).contains(value);
}
2、Set (慢的)
public static boolean useSet(String[] arr,String value){
return sets.contains(value)
}
3、loop (很快的)
public static boolean useLoop(String[] arr,String value){
for(String s:arr){
if(s.equals(value))
return true;
}
return false;
}
4、binarySearch (有序数组,最快的)
public static boolean useBinarySearch(String[] arr,String value){
int result=Arrays.binarySearch(arr,value);
if(result>0)
return true;
else
return false;
}
此方法是不正确的,因为Arrays的binarySearch方法必须应用于有序数组。
性能对比
如果读者熟悉以上java代码片段中出现的集中数据结构,那么可以利用时间复杂度计算标准,
先推算这四种方式的性能对比的大致结果。当然,我们这里不采用这种方式,而是直接运用
如下测试代码对比这四种方式的时间损耗情况。为了使得我们的测试结果更具有代表性,我们
针对不同的数据量做了多组测试。也许,这个测量方式并不精确,但是测量结果是清晰和可
信任的。测试的示例代码如下:
public static void main(String[] args) {
String[] arr = new String[] { “www.”, “tiantian”, “bian”, “ma”, “.com”};
long startTime = System.nanoTime();
for (int i = 0; i < 100000; i++) {
// use list
useList(arr, “天天编码”);
// use set
//useSet(arr, “天天编码”);
// use loop
//useLoop(arr, “天天编码”);
// use binarySearch
//useBinarySearch(arr, “天天编码”);
long endTime = System.nanoTime();
long duration = endTime = startTime;
System.out.pri}
数组长度 方法 运行耗时 数组长度 方法 运行耗时
5 list 13 100 list 50
5 set 72 100 set 668
5 loop 5 100 loop 47
5 binarySearch 100 inarySearch 8
1k list 112 10k list 1590
1k set 2055 10k set 23819
1k loop 99 10k loop 1526
1k binarySearch 12 10k binarySearch 12
总结
参照这个表格,结论已经很明显了。最简单的Loop方法比其他任何使用集合容器的方法都更加高效。
很多的开源项目代码显示,很多Java开发者喜欢使用第一种方法(list),实际上,该方法的性能并不好。
该方法把一个数组的元素转移到一个新的集合容器中,显然,在所有的元素转移完成之前,新的集合容器处于不可用的状态。
该表格还反映出一个事实:Arrays.binarySearch()方法的性能是最好的,特别是对于数组长度很大的数组。
但是该方法要求数组必须有序,这限制住了该方法的使用场景,本文实例代码中的数组并不是有序的,
所以不应该使用该方法。
实际上,如果你确实需要高效地检查某个特定值是否被包含在某些数组或者集合容器中,
你应该考虑使用有序列表或有序树,这些集合容器查找特定值的时间复杂度是 O(log(n))。
当然,如果使用哈希集合,时间复杂度下降为 O(1)。