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<Pair> pairs = generateData();
        System.out.println("pairs.size(): " + pairs.size());

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

        long beginSet = System.currentTimeMillis();
        List<Long> 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<Pair> generateData() {
        List<Pair> pairs = new ArrayList<Pair>();
        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<Long> getRecordUsingList(List<Pair> list) {
        List<Long> results = new ArrayList<Long>();
        for (Pair pair : list) {
            if (!results.contains(pair.getRecord())) {
                results.add(pair.getRecord());
            }
        }
        return results;
    }

    /**
     * 使用Set去重
     * @param list
     * @return
     */
    static List<Long> getRecordUsingSet(List<Pair> list) {
        Set<Long> set = new HashSet<Long>();
        for (Pair pair : list) {
            set.add(pair.getRecord());
        }
        List<Long> results = new ArrayList<Long>(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

更多文章

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章