Java - List 的 contains 方法的性能

Java - List 的 contains 方法的性能

有一个需求,对一个List中的元素,获取的所有Record字段,要求去重,并作为List返回。现在有两个方案,一个是使用ArrayList(LinkedList类似),另一个是使用HashSetArrayList使用其contains()方法来去重,HashSet调用add()方法自然会去重。具体实现如下:

package com.example.collection;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * @author youngbear
 * @email [email protected]
 * @date 2018/8/15 22:17
 * @blog https://blog.csdn.net/next_second
 * @github https://github.com/YoungBear
 * @description ArrayList 的contains方法的性能问题
 */
public class ContainsTest {

    public static final int COLUMNS = 30;
    public static final int NUMBERS = 10000;
    public static void main(String[] args) {
        List pairs = generateData();
        System.out.println("pairs.size(): " + pairs.size());

        long beginList = System.currentTimeMillis();
        List recordUsingList = getRecordUsingList(pairs);
        long endList = System.currentTimeMillis();

        long beginSet = System.currentTimeMillis();
        List recordUsingSet = getRecordUsingSet(pairs);
        long endSet = System.currentTimeMillis();

        System.out.println("list: " + (endList - beginList) + " ms" + ", size: " + recordUsingList.size());
        System.out.println("set: " + (endSet - beginSet) + " ms" + ", size: " + recordUsingSet.size());

    }

    /**
     * 生成测试数据
     * @return
     */
    static List generateData() {
        List pairs = new ArrayList();
        for (int i = 0; i < NUMBERS; i++) {
            for (int j = 0; j < COLUMNS; j++) {
                Pair pair = new Pair();
                pair.setRecord((long)i);
                pair.setName("name: " + i + ", " + j);
                pairs.add(pair);
            }
        }
        return pairs;
    }

    /**
     * 使用列表去重
     * @param list
     * @return
     */
    static List getRecordUsingList(List list) {
        List results = new ArrayList();
        for (Pair pair : list) {
            if (!results.contains(pair.getRecord())) {
                results.add(pair.getRecord());
            }
        }
        return results;
    }

    /**
     * 使用Set去重
     * @param list
     * @return
     */
    static List getRecordUsingSet(List list) {
        Set set = new HashSet();
        for (Pair pair : list) {
            set.add(pair.getRecord());
        }
        List results = new ArrayList(set);
        return results;
    }


    static class Pair{
        private Long record;
        private String name;

        public Long getRecord() {
            return record;
        }

        public void setRecord(Long record) {
            this.record = record;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }
    }
}


输出结果为:

pairs.size(): 300000
list: 13908 ms, size: 10000
set: 31 ms, size: 10000

分析

List

由于数据中存在重复元素,所以使用contains()方法,但是,ArrayListcontains()方法会调用其indexOf()方法,在indexOf()方法里边,有一个for循环,所以,ArrayListcontains()方法的时间复杂度是O(n*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;
    }    

HashSet

对于HashSet,它的add()方法会自动去重,它调用的是一个mapput方法,其时间复杂度是O(1)

    public boolean add(E e) {
        return map.put(e, PRESENT)==null;
    }

结论

所以,在大量的数据的时候,不要使用Listcontains()方法,其效率很低,可以考虑使用Set来实现。

参考

HashSet vs. List performance

HashSet vs ArrayList contains performance

更多文章

你可能感兴趣的:(java基础)