Java集合中contains方法的效率对比分析

 更新时间:2021年05月26日 15:13:19   作者:zhulj625  
这篇文章主要介绍了Java集合中contains方法的效率对比分析,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

最近让部门技术大佬帮忙代码review的时候,他给我指出了一个小的技术细节,就是对于集合的contains方法尽量选用Set而不是List,平时没怎么注意,仔细看了下源码,大佬就是大佬,技术细节也把握的死死的。

Java集合List、Set中均有对集合中元素是否存在的判断方法contains(Object o);Map中有对key及value是否存在的判断方法containsKey(Object key)和containsValue(Object value)。

1.ArrayList

在ArrayList中contains方法通过遍历list中的元素,利用==或equals来判断是否存在目标元素,复杂度为O(N)

public boolean contains(Object o) {
    return indexOf(o) >= 0;
}
public int indexOf(Object o) {
    if (o == null) {
        for (int i = 0; i < size; i++)
            if (elementData[i]==null)
                return i;
    } else {
        for (int i = 0; i < size; i++)
            if (o.equals(elementData[i]))
                return i;
    }
    return -1;
}

2.HashSet

HashSet中元素以Key的形式存于HashMap中,判断元素是否存在即是判断对应Map中key是否存在。

HashSet:
    private transient HashMap<E,Object> map; //将不需要序列化的属性前添加关键字transient,序列化对象的时候,这个属性就不会被序列化。
    /**
     * Constructs a new, empty set; the backing <tt>HashMap</tt> instance has
     * default initial capacity (16) and load factor (0.75).
     */
    public HashSet() {
        map = new HashMap<>();
    }
public boolean contains(Object o) {
    return map.containsKey(o);
}

3.HashMap

HashMap中有两个contains方法,一个判断key是否存在,一个判断value是否存在。

HashMap的底层主要是基于数组和链表(散列表或者叫哈希表)来实现的,它之所以有相当快的查询速度主要是因为它是通过计算散列码来决定存储的位置。

所以containsKey通过key的哈希值直接查找key是否存在,时间复杂度为O(1),响应的HashSet查找元素的时间复杂度也是O(1)。

对于containsValue方法判断map中是否存在value的判断,其方法为将map中的Node数组进行遍历,然后判断是否存在。

transient Node<K,V>[] table;
public boolean containsKey(Object key) {
    return getNode(hash(key), key) != null;
}
final Node<K,V> getNode(int hash, Object key) {
        Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
        if ((tab = table) != null && (n = tab.length) > 0 &&
            (first = tab[(n - 1) & hash]) != null) {
            if (first.hash == hash && // always check first node
                ((k = first.key) == key || (key != null && key.equals(k))))
                return first;
            if ((e = first.next) != null) {
                if (first instanceof TreeNode)
                    return ((TreeNode<K,V>)first).getTreeNode(hash, key);
                do {
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        return e;
                } while ((e = e.next) != null);
            }
        }
        return null;
}
public boolean containsValue(Object value) {
        Node<K,V>[] tab; V v;
        if ((tab = table) != null && size > 0) {
            for (int i = 0; i < tab.length; ++i) {
                for (Node<K,V> e = tab[i]; e != null; e = e.next) {
                    if ((v = e.value) == value ||
                        (value != null && value.equals(v)))
                        return true;
                }
            }
        }
        return false;
}

4.总结

集合各方法的时间复杂度 contains containskey containsValue
ArrayList O(N)
HashSet O(1)
HashKey O(1) O(N)

对于这种技术细节需要平时注意和积累,不断学习和记录,应用于实际开发中,不断提高运行效率。后续也会将这些技术细节记录下来,在日常开发中加以运用。

补充:ArrayList的contains方法的效率果然不高

看代码吧~

之前做的一个项目,数据库抽出了40多万条数据,然后从csv文件抽出了大概也是40多万条数据,进行对比分析 之前代码如下:

List<String> keys = new ArrayList<String>();
   int isize = msTaiyousr.size();
   for (int i=0;i<isize;i++) {
    Map<String, Object> msyaiyousr = msTaiyousr.get(i);
    String id = (String) msyaiyousr.get("taiyousrid");
    String usrtorokukbn = (String) msyaiyousr.get("usrtorokukbn");
    keys.add(usrtorokukbn+":"+id);
   }   
  
   int jsize = wkTaiyousr.size();
   for (int j=0;j<jsize;j++) {
    Map<String, Object> wktaiyousr = wkTaiyousr.get(j);
    String id = (String) wktaiyousr.get("taiyousrid");
    String usrtorokukbn = (String) wktaiyousr.get("usrtorokukbn");
    if (keys.contains(usrtorokukbn+":"+id)) {
      updateList.add(wktaiyousr);
     } else {
      insertList.add(wktaiyousr);
    }
   }

由于 第二个for循环使用了 ArrayList的contains方法,跑完第二个for循环使用了 12分钟左右,我的个天,第一个循环不到1秒。然后使用了 HashSet 代替 ArrayList 代码如下:

Set<String> keys = new HashSet<String>();
   int isize = msTaiyousr.size();
   for (int i=0;i<isize;i++) {
    Map<String, Object> msyaiyousr = msTaiyousr.get(i);
    String id = (String) msyaiyousr.get("taiyousrid");
    String usrtorokukbn = (String) msyaiyousr.get("usrtorokukbn");
    keys.add(usrtorokukbn+":"+id);
   }
  
   int jsize = wkTaiyousr.size();
   for (int j=0;j<jsize;j++) {
    Map<String, Object> wktaiyousr = wkTaiyousr.get(j);
    String id = (String) wktaiyousr.get("taiyousrid");
    String usrtorokukbn = (String) wktaiyousr.get("usrtorokukbn");
    if (keys.contains(usrtorokukbn+":"+id)) {
      updateList.add(wktaiyousr);
     } else {
      insertList.add(wktaiyousr);
    }
   }

结果不到1秒,两个for循环瞬间跑完。果然大数据的时候还是不要用到ArrayList的contains方法,改用HashSet的吧。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • SpringBoot多环境切换的配置实现

    SpringBoot多环境切换的配置实现

    在日常的开发中,一般都会分好几种环境,本文就来介绍一下SpringBoot多环境切换的配置实现,具有一定的参考价值,感兴趣的可以了解一下
    2024-03-03
  • Java反射获取实例的速度对比分析

    Java反射获取实例的速度对比分析

    这篇文章主要介绍了Java反射获取实例的速度对比分析,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-09-09
  • SpringBoot解析JSON数据的三种方案

    SpringBoot解析JSON数据的三种方案

    JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式,易于人阅读和编写,同时也易于机器解析和生成,本文给大家介绍了SpringBoot解析JSON数据的三种方案,需要的朋友可以参考下
    2024-03-03
  • Redis Java 集成到 Spring Boot的详细过程

    Redis Java 集成到 Spring Boot的详细过程

    本文介绍了如何使用SpringBoot连接Redis,并展示了如何配置Redis服务地址、创建Controller类以及进行基本的Redis操作,如字符串、列表、集合、哈希和有序集合,感兴趣的朋友跟随小编一起看看吧
    2024-12-12
  • JDBC 实现通用的增删改查基础类方法

    JDBC 实现通用的增删改查基础类方法

    下面小编就为大家分享一篇JDBC 实现通用的增删改查基础类方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-01-01
  • Java中IO流使用FileWriter写数据基本操作详解

    Java中IO流使用FileWriter写数据基本操作详解

    这篇文章主要介绍了Java中IO流FileWriter写数据操作,FileWriter类提供了多种写入字符的方法,包括写入单个字符、写入字符数组和写入字符串等,它还提供了一些其他的方法,如刷新缓冲区、关闭文件等,需要的朋友可以参考下
    2023-10-10
  • Springboot中@Transactional注解与异常处理机制方式

    Springboot中@Transactional注解与异常处理机制方式

    这篇文章主要介绍了Springboot中@Transactional注解与异常处理机制方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-08-08
  • Spring中@Scope的几种取值方式

    Spring中@Scope的几种取值方式

    这篇文章主要介绍了Spring中@Scope的几种取值方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-06-06
  • 一文带你掌握Java开发者如何接入并使用DeepSeek

    一文带你掌握Java开发者如何接入并使用DeepSeek

    对于Java开发者来说,将DeepSeek集成到项目中,可以极大地提升数据处理和分析的效率,下面小编就来为大家介绍一下具体的调用方法吧
    2025-03-03
  • 你可能真没用过这些 IDEA 插件(建议收藏)

    你可能真没用过这些 IDEA 插件(建议收藏)

    IDEA 全称 IntelliJ IDEA,是java编程语言开发的集成环境。IntelliJ在业界被公认为最好的java开发工具。这篇文章主要介绍 IDEA 必用插件的安装及用法,需要的朋友可以参考下
    2020-08-08

最新评论