从集合中获取对象而不在java中循环

时间:2016-01-15 09:03:48

标签: java optimization collections

我需要反复(成千上万次)从包含数十万个对象的Collection中检索一个元素(每次不同)。

执行此检索操作的最快方法是什么?目前我的Collection是一个List并且我一直在迭代它直到找到元素,但有更快的方法吗?可能使用Map?我当时想要这样做:

  • 将对象放在Map中,其中键是Object的id字段,而Object本身就是值。
  • 然后在get(id)上执行Map应该比循环List快得多。
  • 如果这是一种正确的方法,我应该使用HashMap还是TreeMap? - 我的对象没有特别的顺序。

对此事的任何建议都将不胜感激!

最后注意:如果外部图书馆提供了回答这个问题的工具,我很乐意接受它!

4 个答案:

答案 0 :(得分:5)

根据Tree Map的文件(强调我自己):

  

地图根据其键的自然顺序排序,   或者在地图创建时提供的比较器,具体取决于哪个   使用构造函数。

在您的情况下,您声明项目没有特定的顺序,并且您似乎没有遵循任何特定的顺序,而是能够尽快检索数据。

HashMaps提供不断的阅读时间,但不保证顺序,所以我认为你应该选择HashMaps

  

此课程不保证地图的顺序;在   特别是,它不保证订单将保持不变   随着时间的推移。 此实现提供了恒定时间性能   假设哈希函数,基本操作(获取和放置)   将元素正确地分散在桶中

作为旁注,此内存占用量可以非常快,因此查看数据库方法也许是一个好主意,也许可以使用类似缓存的机制来处理更频繁使用的信息。

答案 1 :(得分:3)

我已经创建了代码,用于测试BinarySearch,TreeMap和HashMap对于给定问题的性能。

如果您每次都在重建集合,HashMap是最快的(即使使用标准的Object&hashCode()实现!),sort +数组二进制搜索排在第二位,TreeMap排在最后(由于复杂的重建)过程)。

proc array: 2395
proc tree : 4413
proc hash : 1325

如果你没有重建集合,HashMap仍然是最快的,数组的二进制搜索是第二个,而TreeMap是最慢的,但速度只比数组略低。

proc array: 506
proc tree : 561
proc hash : 122

测试代码:

public class SearchSpeedTest {

    private List<DataObject> data;
    private List<Long> ids;
    private Map<Long, DataObject> hashMap;
    private Map<Long, DataObject> treeMap;
    private int numRep;
    private int dataAmount;
    private boolean rebuildEachTime;

    static class DataObject implements Comparable<DataObject>{
        Long id;

        public DataObject(Long id) {
            super();
            this.id = id;
        }

        public DataObject() {
            // TODO Auto-generated constructor stub
        }

        @Override
        public final int compareTo(DataObject o) {
            return Long.compare(id, o.id);
        }

        public Long getId() {
            return id;
        }

        public void setId(Long id) {
            this.id = id;
        }

        public void dummyCode() {

        }

    }

    @FunctionalInterface
    public interface Procedure {
        void execute();
    }

        public void testSpeeds() {

            rebuildEachTime = true;
            numRep = 100;
            dataAmount = 60_000;

            data = new ArrayList<>(dataAmount);
            ids = new ArrayList<>(dataAmount);
            Random gen = new Random();

            for (int i=0; i< dataAmount; i++) {
                long id = i*7+gen.nextInt(7);
                ids.add(id);
                data.add(new DataObject(id));
            }
            Collections.sort(data);


            treeMap = new TreeMap<Long, DataObject>();
            populateMap(treeMap);


            hashMap = new HashMap<Long, SearchSpeedTest.DataObject>();
            populateMap(hashMap);


            Procedure[] procedures = new Procedure[] {this::testArray, this::testTreeMap, this::testHashMap}; 
            String[] names = new String[]                {"array",       "tree ",            "hash "};


            for (int n=0; n<procedures.length; n++) {
                Procedure proc = procedures[n];
                long startTime = System.nanoTime();
                for (int i=0; i<numRep; i++) {
                    if (rebuildEachTime) {
                        Collections.shuffle(data);
                    }
                    proc.execute();
                }
                long endTime = System.nanoTime();
                long diff = endTime - startTime;

                System.out.println("proc "+names[n]+":\t"+(diff/1_000_000));
            }

        }

        void testHashMap() {
            if (rebuildEachTime) {
                hashMap = new HashMap<Long, SearchSpeedTest.DataObject>();
                populateMap(hashMap);
            }

            testMap(hashMap);
        }

        void testTreeMap() {

            if (rebuildEachTime) {
                treeMap = new TreeMap<Long, SearchSpeedTest.DataObject>();
                populateMap(treeMap);
            }

            testMap(treeMap);
        }

        void testMap(Map<Long, DataObject> map) {

            for (Long id: ids) {
                DataObject ret = map.get(id);
                ret.dummyCode();
            }

        }

        void populateMap(Map<Long, DataObject> map) {
            for (DataObject dataObj : data) {
                map.put(dataObj.getId(), dataObj);
            }
        }

        void testArray() {
            if (rebuildEachTime) {
                Collections.sort(data);
            }
            DataObject key = new DataObject();
            for (Long id: ids) {
                key.setId(id);
                DataObject ret = data.get(Collections.binarySearch(data, key));
                ret.dummyCode();
            }
        }

        public static void main(String[] args) {
            new SearchSpeedTest().testSpeeds();
        }
    }

答案 2 :(得分:2)

HashMap一般来说效率更高,所以每当你不关心键的顺序时就使用它。

当你想要按照键排序的Map中的条目而不是使用TreeMap时,但是在你的情况下排序将是开销,因为你不需要任何微粒订单。

答案 3 :(得分:0)

如果您有一个很好的方法来定义地图的关键字,您可以使用地图。在最坏的情况下,您可以将对象用作键和值。

由于订购并不重要,请使用HashMap。要在TreeMap中维护订单,添加元素会产生额外费用,因为必须将其添加到正确的位置。