【问题标题】:How do I find a point in a Map and search left and right如何在地图中找到一个点并左右搜索
【发布时间】:2012-07-25 03:12:37
【问题描述】:

我有一系列对象沿着由LinkedHashMap<Foo, Double> 表示的线设置,其中第一个字段是对象,第二个字段是它与测量原点的距离。我知道元素是通过增加距离来排序的。我希望能够选择某个位置 x 并左右搜索 Foo 的实例,其中 foo.isInteresting() 返回 true 而无需遍历整个地图。

我的第一个想法是做类似的事情:

  • 遍历所有条目以找到距离大于x的第一个条目
  • 从现在开始向左看所有条目直到foo.isInteresting()
  • 从这一点开始,查看所有条目直到foo.isInteresting()

但据我所知,没有办法从某个起点迭代Map。从我的地图中创建两个List 对象并使用ListIterator 是否明智?

交换键和值也不完全明智,因为我需要在我的应用程序的其他地方通过Foo 进行搜索。

【问题讨论】:

    标签: java search map iterator


    【解决方案1】:

    考虑indexed-tree-map,您将能够通过索引访问元素并获取元素的索引,同时保持排序顺序。可以将重复项作为同一键下的值放入数组中。

    【讨论】:

      【解决方案2】:

      您可以使用数组[2][n] 或两个数组[n],其中第一行是距离值,第二行是您的对象。因此,您将能够根据距离对其进行排序并使用二进制搜索。当您找到具有所需距离的元素时,您将能够向左或向右遍历。

      【讨论】:

      • 这是我在问题中概述的最初想法,但我更喜欢列表而不是数组。
      【解决方案3】:

      使用两个地图(同时更新)是不是一个糟糕的主意:

      LinkedHashMap<Foo, Double> basicMap;
      SortedMap<Double, Set<Foo>> distMap; // may have same Double for two 'Foo's.
      

      编辑以在 SortedMap 中更改

      【讨论】:

        【解决方案4】:

        您可以使用TreeSet&lt;Foo&gt;,在Foo 内保持距离并准备一个比较器。您还可以创建一个包含 Foo 和 distance 且具有可比性的包装器对象,并将其保存在 TreeSet&lt;Wrapper&gt; 中。然后你可以使用NavigableSetlowerhigher方法。

        class Wrapper implements Comparable<Wrapper> {
        
            public Foo foo;
            public Double distance;
        
            public Wrapper(Foo foo, Double distance) {
                this.foo = foo;
                this.distance = distance;
            }
        
            /**
             * Use only Foo for hashcode and equals 
             */
            @Override
            public int hashCode() {
                final int prime = 31;
                int result = 1;
                result = prime * result + ((foo == null) ? 0 : foo.hashCode());
                return result;
            }
        
            @Override
            public boolean equals(Object obj) {
                if (this == obj)
                    return true;
                if (obj == null)
                    return false;
                if (getClass() != obj.getClass())
                    return false;
                Wrapper other = (Wrapper) obj;
                if (foo == null) {
                    if (other.foo != null)
                        return false;
                } else if (!foo.equals(other.foo))
                    return false;
                return true;
            }
        
            @Override
            public int compareTo(Wrapper o) {
                return distance.compareTo(o.distance);
            }
        
        }@Override
        public boolean equals(Object obj) {
            if (this == obj)
                return true;
            if (obj == null)
                return false;
            if (getClass() != obj.getClass())
                return false;
            Wrapper other = (Wrapper) obj;
            if (foo == null) {
                if (other.foo != null)
                    return false;
            } else if (!foo.equals(other.foo))
                return false;
            return true;
        }
        
        @Override
        public int compareTo(Wrapper o) {
            return distance.compareTo(o.distance);
        }
        
        }
        

        这让您可以按 Foo 搜索并按距离排序。

        【讨论】:

        • +1,我喜欢这个解决方案。 lower()higher() 只会得到两个周围的值。然后您可以递归进行迭代,但这将是M log N 性能,其中 M 是您要查看的项目数。使用subSet() 可能更容易。此外,将距离保持在Foo 内也不是很重要,只要您的比较器知道如何获得距离 given 一个 Foo。
        • 为给定的Foo 查找距离的自定义比较器是个好主意。至于子集,我知道我们正在寻找距离给定 foo 左右最近的 interesting foo。
        • 我也喜欢这个解决方案。我认为subSet() 会更好用,因为最近的有趣 foo 可能在任何距离之外。当我找到第一个有趣的 foo 时,我可以遍历 subSet() 并中断。
        • @Peter:其实我知道你来自哪里。我认为最佳迭代策略是(假设“pos”是包含所需点的Wrapper)迭代foos.tailSet(pos)foos.descendingSet().tailSet(pos),在它们之间交替。
        • @PeterGwiazda 除了TreeSet.contains(Object o) 我认为使用了e.equals(o) 之外,您的解决方案运行良好。但是这个:Wrapper a = new Wrapper(new Foo("a"), 0.0); foos.add(a); foos.contains(a); foos.contains(new Wrapper(new Foo("a"), 0.0); foos.contains(new Wrapper(new Foo("a"), 1.0); 返回truetruefalse。但是a.equals(new Wrapper(new Foo("a"), 1.0)) 返回true
        【解决方案5】:

        我认为Map 不应该是您在这里选择的数据类型。相反,创建一个ArrayList&lt;Foo&gt; 并使用与原点的距离作为比较器对其进行排序。然后,您可以使用Collections.binarySearch() 快速找到最接近您所需距离的索引。完成此操作后,您可以使用 List.subList() 迭代一组特定的值。

        TreeSet(使用TreeSet.subSet())的工作方式类似,但您需要使Comparator 更复杂一些,因为它需要能够比较Foos 作为辅助比较(对于当两个元素到原点的距离相等时)。

        【讨论】:

          【解决方案6】:

          navigablemaptreemap 可能会有所帮助,特别是 tailmapsubmap 方法

          【讨论】:

          • NavigableMap(例如 TreeMap)使用键的自然排序,而不是示例中的值。这可以通过TreeMap&lt;Double, List&lt;Foo&gt;&gt; 来实现
          • Peter,用户自定义比较器没问题。
          • 是的,您可以提供自定义比较器,但仍可用于键。在示例中,Foo 是一个键,据我所知,它不会保持距离。您需要在Foo 中输入距离。看我的回答。
          猜你喜欢
          • 2022-10-30
          • 1970-01-01
          • 1970-01-01
          • 2010-09-29
          • 1970-01-01
          • 1970-01-01
          • 2012-12-31
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多