ArrayList的contains()方法的性能问题及优化方法

简介: ArrayList的contains()方法的性能问题及优化方法

背景

今天定位一个接口耗时问题,通过日志定位到在数据库查询完毕后,中间一段逻辑耗时很长有十几秒的样子,发现是循环中使用ArraysList中的contains方法,当循环数量级变得很大时,执行时间变得不可控。

代码示例

// 有5万个门店
        List<Store> storeList = storeMapper.selectAll();
        // 有十万个用户
        List<User> userList = userMapper.selectAll();
        // 最坏情况循环5亿次
        for (user : userList){
            if (storeList.contains(user.getStoreCode())){
                doSth();
            }
        }

1. 原理说明

1.1 ArrayList

ArrayList中contains()方法的实现过程:

contains()方法调用了indexOf()方法,indexOf()具体实现如下。从源码可以看出,该方法通过遍历数据和比较元素的方式来判断是否存在给定元素。当ArrayList中存放的元素非常多时,这种实现方式来判断效率将非常低,后面通过实例来验证。

1.2 HashSet

既然ArrayList的contains()方法存在性能问题,那么就应该寻找改进的办法。这里推荐使用HashSet来代替ArrayList。下面介绍HashSet的contains()方法的实现过程:

HashSet将元素存放在HashMap中(HashMap的key)

contains()方法调用HashMap的containsKey()方法

containsKey()方法调用getEntry()方法。在该方法中,首先根据key计算hash值,然后从HashMap中取出该hash值对应的链表(链表的元素个数将很少),再通过变量该链表判断是否存在给定值。这种实现方式效率将比ArrayList的实现方法效率高非常多。

2. 实例验证

2.1 测试ArrayList

public static void main(String[] args) {
  ArrayList<String> arrayList = new ArrayList<>();
  // 存入100000个数据
  for (int i = 0; i < 100000; i++) {
    arrayList.add("test" + i);
  }
  // 验证300000个数据(其中200000不存在)   
  long beginTime = System.currentTimeMillis();    for (int i = 0; i < 300000; i++) {
    arrayList.contains("test" + i);
  }
  long endTime = System.currentTimeMillis();
  System.out.println("cost time: " + (endTime - beginTime) + "ms");
}
打印结果:
cost time: 182210ms

2.2 测试HashSet

public static void main(String[] args) {
  Set<String> hashSet = new HashSet<>();
  // 存入100000个数据
  for (int i = 0; i < 100000; i++) {
    hashSet.add("test" + i);
  }
  // 验证300000个数据(其中200000不存在)
  long beginTime = System.currentTimeMillis();
  for (int i = 0; i < 300000; i++) {
    hashSet.contains("test" + i);
  }
  long endTime = System.currentTimeMillis();
  System.out.println("cost time: " + (endTime - beginTime) + "ms");
}
打印结果:
cost time: 49ms

3. 总结

通过第二节的实例可以看出,使用ArrayList的contains()耗时是使用HashSet的contains()方法的30多倍。具体原因可以参考第一节中的原理分析。

本篇文章如有帮助到您,请给「翎野君」点个赞,感谢您的支持。


目录
相关文章
|
算法 Dubbo NoSQL
Java中5种List的去重方法及它们的效率对比,你用对了吗?
01、使用两个for循环实现List去重(有序) /**使用两个for循环实现List去重(有序) * * @param list * */ public static List removeDuplicationBy2For(List<Integer> list) { for (int i=0;i<list.size();i++) { for (int j=i+1;j<list.size();j++) { if(list.get(i).equa
24019 2
Java中5种List的去重方法及它们的效率对比,你用对了吗?
|
2月前
|
存储 安全 算法
【JAVA】HashMap扩容性能影响及优化策略
【JAVA】HashMap扩容性能影响及优化策略
|
6天前
|
存储 安全 算法
如何优化Java中的HashMap性能?
如何优化Java中的HashMap性能?
|
2月前
|
存储 缓存 安全
Java HashMap:哈希表原理、性能与优化
Java HashMap:哈希表原理、性能与优化
|
8月前
|
存储 缓存 Java
Java中使用HashMap时指定初始化容量性能一定会更好吗?
可以看出,容量16是个分水岭,当容量为16时,二者几乎没啥差异,这也很容易理解,当不指定容量时默认初始容量就是16。但容量大于16时,指定容量时的性能会高于不指定时的性能,随着数量的增加,前者会比后者性能高出50%。但当数据量小于16时,不指定容量大小反而性能更高,最多甚至相差2倍,这就和我们之前的认知不一样了。
65 0
|
11月前
|
存储 安全 Java
4.2 Java数组性能优化策略:使用ArrayList代替原生数组
4.2 Java数组性能优化策略:使用ArrayList代替原生数组
222 0
|
存储 缓存 JavaScript
优化SPA性能的方法
Web开发中,随着JavaScript的发展,越来越多的网站开始采用单页面应用程序(SPA)的方式来呈现内容。SPA相对于传统的多页面应用程序来说,具有更好的用户体验和更快的加载速度。但是,随着SPA的流行,页面越来越复杂,也面临着越来越多的性能问题。在这篇文章中,我们将讨论一些优化SPA性能的方法。
207 0
|
Java
ArrayList 分析以及相关方法介绍
java.util.ArrayList 是我们最常用的一个类,ArrayList 底层是动态数组,读者可以把它理解为数组的实现
84 0
ArrayList 分析以及相关方法介绍
再谈HashMap:使用map优化代码,你得学我这样做
我并没有和HashMap杠上,想着重新开始写点技术的东西,就拿HashMap开头了。最近开始重新学习数据结构和算法,其中有些东西学完之后,对于HashMap的理解和运用又有新的认识。虽然之前运用HashMap也有这样用过,但是知道了方法论,才发现这样使用的好处。 上一期我写过HashMap,写的是JDK8之前的Hash,现在都JDK15了,大家有兴趣可以去看一下源计划之从HashMap认识数据结构
|
存储 安全 C++
【HashMap优化使用】
【HashMap优化使用】