ArrayList的contains方法和HasMap的containsKey效率差十倍

问题源自于leetcode上的一道题:

twoSum:

Given an array of integers, return indices of the two numbers such that they add up to a specific target.

You may assume that each input would have exactly one solution.

Example:

Given nums = [2, 7, 11, 15], target = 9,

Because nums[0] + nums[1] = 2 + 7 = 9,
return [0, 1].
这道题 一般的解决方法是的一个比较容易想到的解决方法是:进行两次遍历,但是复杂度会达到O(n2)。一个比较好的解决方法是使用HashMap复杂度可以降低到O(n)。一开始我根据类似用HashMap的思想,使用ArrayList来做,代码如下:

[java]  view plain  copy
  1. public int[] twoSum(int[] nums, int target) {  
  2.         ArrayList<Integer> list = new ArrayList<Integer>();  
  3.           
  4.         for(int i=0; i<nums.length; i++){  
  5.             if(list.contains(target-nums[i])){  
  6.                 return new int[]{list.indexOf(target-nums[i]), i};  
  7.             }  
  8.             list.add(nums[i]);  
  9.         }  
  10.         return null;  
  11.     }  

结果发现效率和笨方法差不多,运行时间根本没有得到改善,网站上的测试用例的运行时间是56ms,仅beat百分十几。后来用HashMap来做:

[java]  view plain  copy
  1. public int[] twoSum(int[] nums, int target) {  
  2.         HashMap<Integer, Integer> map = new HashMap<Integer, Integer>();          
  3.         for(int i=0; i<nums.length; i++){  
  4.             if(map.containsKey(target-nums[i])){  
  5.                 return new int[]{map.get(target-nums[i]), i};  
  6.             }  
  7.             map.put(nums[i], i);  
  8.         }  
  9.         return null;  
  10.     }  
效率得到明显改善:6ms,这两段代码唯一的不同之处就是一个用ArrayList存储元素,另一个用HashMap。效率相差了近10倍!

为什么会有这么大的差距呢:

原因时ArrayList使用的是数组来存储元素,而HashMap使用哈希表来存储元素。在上面两段代码中,效率拉开差距的是在contains和containsKey方法上。

ArrayList的contains方法是通过调用自己的index of方法来判断的:

[java]  view plain  copy
  1. public int indexOf(Object o) {  
  2.         if (o == null) {  
  3.             for (int i = 0; i < size; i++)  
  4.                 if (elementData[i]==null)  
  5.                     return i;  
  6.         } else {  
  7.             for (int i = 0; i < size; i++)  
  8.                 if (o.equals(elementData[i]))  
  9.                     return i;  
  10.         }  
  11.         return -1;  
  12.     }  
可以看到,在源码中,index方法把整个数组顺序遍历了一遍,这种查找方法无疑是效率最低的。

在HashMap,key被存储到hash表中,查找时是在hash表上进行查找,关于hash表的查找方法很简单这里就不赘述了。

[java]  view plain  copy
  1. public boolean containsKey(Object key) {  
  2.         return getNode(hash(key), key) != null;  
  3.     }  
[java]  view plain  copy
  1. final Node<K,V> getNode(int hash, Object key) {  
  2.         Node<K,V>[] tab; Node<K,V> first, e; int n; K k;  
  3.         if ((tab = table) != null && (n = tab.length) > 0 &&  
  4.             (first = tab[(n - 1) & hash]) != null) {  
  5.             if (first.hash == hash && // always check first node  
  6.                 ((k = first.key) == key || (key != null && key.equals(k))))  
  7.                 return first;  
  8.             if ((e = first.next) != null) {  
  9.                 if (first instanceof TreeNode)  
  10.                     return ((TreeNode<K,V>)first).getTreeNode(hash, key);  
  11.                 do {  
  12.                     if (e.hash == hash &&  
  13.                         ((k = e.key) == key || (key != null && key.equals(k))))  
  14.                         return e;  
  15.                 } while ((e = e.next) != null);  
  16.             }  
  17.         }  
  18.         return null;  
  19.     }  


总之,如果要用contains方法,用HashMap来代替要远远快于ArrayList,如果要遍历的话还是用ArrayList吧。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值