【问题标题】:How to get a submap with LinkedHashMap?如何使用 LinkedHashMap 获取子图?
【发布时间】:2015-02-24 19:13:40
【问题描述】:

目前,我使用TreeMap 存储一些x 和y 坐标,但与ArrayListHashMap 相比,迭代速度非常慢。我使用它是因为我需要 subMap() 方法,因此即使不存在确切的 X 值(键),我也可以获得确定范围内的 X 值。

LinkedHashMap 的速度与HashMap 几乎相同,我可以按插入顺序迭代键(我需要插入顺序或按比较器的顺序,因为它在 TreeMap 中完成)但我没有 @ 987654327@ 方法。在 TreeMap 中,我可以非常快速地生成子图。

是否有任何数据结构或某种方法可以比 TreeMap 更快地存储有序值(通过插入顺序或比较器),即使确切值不在地图中,也可以在某个范围内获取子图?我的意思是,也许我想要 2 到 25 之间的值,但 2 不存在,最接近的是 3,因此它将返回一个从 3 到 25 的子图。或者以某种方式将此功能添加到 LinkedHashMap

【问题讨论】:

    标签: java performance dictionary iteration


    【解决方案1】:

    听起来您需要一个 TreeMap,它的迭代速度并不比 LinkedHashMap 慢多少,并且可以满足您的需求。由于 HashMap 是无序的,所以 subMap 没有意义。

    【讨论】:

    • 我目前正在使用 TreeMap,但与具有 O(1) 访问权限的 HashMap 或 ArrayList 相比,它确实很慢
    • @Andres 是访问时间还是迭代时间?
    • 这是迭代时间,我正在迭代超过 500k 个条目。但是如果访问时间越短,迭代时间应该越短吗?
    • @Andres 迭代是与随机访问不同的操作,我不希望它是相同的。
    【解决方案2】:

    今天我终于得到了我的问题的答案。经过几次测试HashMapLinkedHashMapTreeMapArrayList 慢得多,我想用它们来创建subMaps()。所以我创建了一个扩展ArrayList 的新类,它给了我非常好的性能,并且在this 答案的帮助下,我创建了通过值而不是索引获取子列表的快速方法。这是完整的课程:

    /**
     * The purpose of this class is to be a faster replacement to a {@link java.util.TreeMap} with
     *  the ability to get sublist containing a range of x values. ArrayList access time is O(1) while
     *  {@link java.util.TreeMap} is O(log(n)). When large data is handled the impact on performance is
     *  noticeable.
     */
    public class XYDataset extends ArrayList<PointValue> {
    
        private final float COMPARISON_THRESHOLD = 0.01f;
    
        final Comparator<PointValue> comparator = new Comparator<PointValue>() {
            @Override
            public int compare(PointValue lhs, PointValue rhs) {
                if (Math.abs(lhs.getX() - rhs.getX()) < COMPARISON_THRESHOLD) return 0;
                return lhs.getX() < rhs.getX() ? -1 : 1;
            }
        };
    
        public XYDataset(int capacity) {
            super(capacity);
        }
    
        public XYDataset() {
        }
    
        public XYDataset(Collection<? extends PointValue> collection) {
            super(collection);
        }
    
        @Override
        public List<PointValue> subList(int start, int end) {
            return super.subList(start, end);
        }
    
        /**
         * Generate a sublist containing the range of x values passed
         * @param x1 lower x value
         * @param x2 upper x value
         * @return sublist containing x values from x1 to x2
         */
        public List<PointValue> subList(float x1, float x2){
            /**
             * Collections.binarySearch() returns the index of the search key, if it is contained in the list;
             *  otherwise it returns (-(insertion point) - 1).
             * The insertion point is defined as the point at which the key would be inserted into the list:
             *  the index of the first element greater than the key, or list.size() if all elements in the list
             *  are less than the specified key. Note that this guarantees that the return value will be >= 0 if
             *  and only if the key is found.
             */
            int n1 = Collections.binarySearch(this, new PointValue(x1, 0), comparator);
            int n2 = Collections.binarySearch(this, new PointValue(x2, 0), comparator);
    
            /**
             * Example, we assume the list is sorted. Based on (https://stackoverflow.com/questions/19198586/search-sorted-listlong-for-closest-and-less-than)
             *
             * long X = 500;
             * List<Long> foo = new Arraylist<>();
             * foo.add(450L);
             * foo.add(451L);
             * foo.add(499L);
             * foo.add(501L);
             * foo.add(550L);
             *
             * If we search for something that isn't in the list you can work backward from the return value
             *  to the index you want. If you search for 500 in your example list, the algorithm would return (-3 - 1) = -4.
             * Thus, you can add 1 to get back to the insertion point (-3), and then multiply by -1 and subtract 1 to get
             *  the index BEFORE the first element GREATER than the one you searched for, which will either be an index that
             *  meets your 2 criteria OR -1 if all elements in the list are greater than the one you searched for.
             */
            if(n1 < 0) n1 = -n1-1;
            if(n2 < 0) n2 = -n2-1;
    
            return this.subList(n1, n2);
        }
    }
    

    PointValue 只是一个包含 x 和 y 坐标的类。所以现在我只需调用 subList() 传递我想要的 x 坐标范围。在我的情况下,插入顺序也已排序,这对于使用 Collections.binarySearch()

    很重要

    【讨论】:

      猜你喜欢
      • 2010-11-01
      • 2021-12-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-05-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多