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的吧。

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

(0)

相关推荐

  • javaweb开发提高效率利器JRebel详解

    JRebel用了有一段时间了,发现确实好用,节省了很多不必要的时间,提高了开发效率.在这里记录一下他的安装和使用过程,希望能帮助到有需要的人. 官网:https://www.jrebel.com/ 一.JRebel简介 jrebel是国外公司perforce于2007年开发的一款java开发效率工具,旨在帮助java开发人员更快地编写更好的应用程序.jrebel提供了常用的开发IDE如IntelliJ Idea.eclipse.myEclipse.NetBeans等的jrebel插件,可以很方便

  • Java 集合的Contains和Remove方法

    一.包含与删除两种方法解析 1.boolean contains(Object o);判断集合中是否包含某个元素. package com.bjpowernode.java_learning; import java.util.*; ​ public class D85_1_ContainsMethod { public static void main(String[] args) { //创建集合 Collection c = new ArrayList(); //创建两个Integer类型

  • Java中List集合去重方法以及效率对比

    List集合相信大家在开发过程中几乎都会用到.有时候难免会遇到集合里的数据是重复的,需要进行去除.然而,去重方式有好几种方式,你用的是哪种方式呢?去重方式效率是否是最高效.最优的呢?今天就给大家讲解一下List集合去重的常见及常用的四种方式. 01 实现思路:使用两个for循环遍历集合所有元素,然后进行判断是否有相同元素,如果有,则去除.这种方式是大部分最先想到的,也是最简单的实现方式.其中,这种方式可以保证List集合原来的顺序不变. 代码实现: /** * notes:使用两个for循环实现

  • java与C 代码运行效率的对比(整理)

    1.Java 语言的概述 作为一种面向对象的程序设计语言,Java 与 C++极为 类似,但却要比 C++简单的多.它在集成其他语言的特点 和优势的同时又有自己独特的优势. Java 的主要特点如下: (1)简单性.Java 可以对内存中产生的垃圾进行自动收集, 大幅度降低了程序的复杂程度,此外,Java 添加了更为实 用的功能的,这使得程序开发更加简单可靠. (2)平台独 立性.Java 语言在程序编程过程中是先编译成中间码,然 后再进行装载与校验,最后通过翻译出来的不同的机器码 来执行.因此

  • Java contains用法示例

    学习Demo  contains方法:用于判断list集合是否包含某个元素 containsKey方法:用于判断Map键中是否包含某个键 containsValue方法:用于判断map中是否包含某个value值 码上行动 import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * contains方法用于判断list集合是否包含某个元素 * con

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

    最近让部门技术大佬帮忙代码review的时候,他给我指出了一个小的技术细节,就是对于集合的contains方法尽量选用Set而不是List,平时没怎么注意,仔细看了下源码,大佬就是大佬,技术细节也把握的死死的. Java集合List.Set中均有对集合中元素是否存在的判断方法contains(Object o):Map中有对key及value是否存在的判断方法containsKey(Object key)和containsValue(Object value). 1.ArrayList 在Arr

  • PHP遍历数组的三种方法及效率对比分析

    本文实例分析了PHP遍历数组的三种方法及效率对比.分享给大家供大家参考.具体分析如下: 今天有个朋友问我一个问题php遍历数组的方法,告诉她了几个.顺便写个文章总结下,如果总结不全还请朋友们指出 第一.foreach() foreach()是一个用来遍历数组中数据的最简单有效的方法. <?php $urls= array('aaa','bbb','ccc','ddd'); foreach ($urls as $url){ echo "This Site url is $url! <b

  • 用Java集合中的Collections.sort方法如何对list排序(两种方法)

    第一种是list中的对象实现Comparable接口,如下: /** * 根据order对User排序 */ public class User implements Comparable <user> { private String name; private Integer order; public String getName() { return name; } public void setName(String name) { this.name = name; } publi

  • 简单的理解java集合中的HashSet和HashTree几个重写方法

    Java中的set是无序的,但是是不可重复的 HashSet底层是哈希表,通过调用hashcode和equals方法实现去重 当我们HashSet里面存的是字符串时,就能默认去重了,因为String已经重写了hashcode和euqals方法 public static void main(String[] args) { HashSet<String> set = new HashSet(); set.add("java"); set.add("c")

  • Java 集合中的类关于线程安全

    Java集合中那些类是线程安全的 线程安全类 在集合框架中,有些类是线程安全的,这些都是jdk1.1中的出现的.在jdk1.2之后,就出现许许多多非线程安全的类. 下面是这些线程安全的同步的类: vector:就比arraylist多了个同步化机制(线程安全),因为效率较低,现在已经不太建议使用.在web应用中,特别是前台页面,往往效率(页面响应速度)是优先考虑的. statck:堆栈类,先进后出 hashtable:就比hashmap多了个线程安全 enumeration:枚举,相当于迭代器

  • java集合中的list详解

    1.List接口 该接口定义的元素是有序的且可重复的.相当于数学里面的数列,有序可重复 booleanaddAll(intindex,Collection<?extendsE>c);将指定集合中所有元素,插入至本集合第index个元素之后defaultvoidreplaceAll(UnaryOperatoroperator);替换集合中每一个元素值defaultvoidsort(Comparator<?superE>c);给集合中的元素进行排序Eget(intindex);获取集合

  • 基于java集合中的一些易混淆的知识点(详解)

    (一) collection和collections 这两者均位于java.util包下,不同的是: collection是一个集合接口,有ListSet等常见的子接口,是集合框架图的第一个节点,,提供了对集合对象进行基本操作的一系列方法. 常见的方法有: boolean add(E e) 往容器中添加元素:int size() 返回collection的元素数:boolean isEmpty() 判断此容器是否为空: boolean contains(Object o) 如果此collecti

  • java集合中list的用法代码示例

    List接口是Collection接口的子接口,List有一个重要的实现类--ArrayList类,List中的元素是有序排列的而且可重复,所以被称为是序列. List可以精确的控制每个元素的插入位置,或删除某个位置元素,它的实现类ArrayList底层是由数组实现的. List中有增删改查的方法,我们可以通过例子演示: 我们通过对学生选课,来演示List中对课程增删改查的方法 /** * 课程类 * @author lenovo * */ public class KeCheng { publ

  • Java.toCharArray()和charAt()的效率对比分析

    LeetCode中的一道算法题,使用toCharArray()时间超时,换成charAt()之后通过,所以测试一下两者的运行效率: public static void test() { String s = "a"; for(int i = 0; i < 100000; i++) { s += "a"; } long start1 = System.currentTimeMillis(); char[] cs = s.toCharArray(); for(c

随机推荐