【问题标题】:Partial search in HashMapHashMap 中的部分搜索
【发布时间】:2011-10-06 11:52:07
【问题描述】:

我需要创建电话簿之类的东西。它包含名称和编号。现在当我输入字母匹配列表时应该返回。对于下面给出的示例,当我键入 H 时,应返回包含 Harmer、Harris、Hawken、Hosler 的列表。当键入 Ha 时,应返回仅包含 Harmer、Harris、Hawken 的列表。

  Map<String, String> nameNum = new HashMap<String, String>();

  nameNum.put("Brown", "+1236389023");
  nameNum.put("Bob", "+1236389023");
  nameNum.put("Harmer", "+1236389023");
  nameNum.put("Harris", "+1236389023");
  nameNum.put("Hawken", "+1236389023");
  nameNum.put("Hosler", "+1236389023");

知道如何实现吗? 提前致谢。

【问题讨论】:

  • 你确定使用HashMap 是一个好主意吗?我认为不同的数据结构可能会更好。
  • 您是只查找第一个字母,还是在您键入时消除了列表?例如,输入“Ha”会消除“Hosler”吗?

标签: java map filtering


【解决方案1】:

删除所有不包含关键部分的值:

yourMap.keySet().removeIf(key -> !key.contains(keyPart));

或正则表达式:

yourMap.keySet().removeIf(key -> !key.matches(".*keyPart.*"));

或过滤流并收集到新地图:

yourMap.entrySet().stream().filter(e -> e.getKey().contains(keyPart)).collect(Collectors.toMap(e -> e.getKey(), e -> e.getValue()));

【讨论】:

    【解决方案2】:

    是的,HashMap 不是正确的数据结构。正如 Bozho 所说,Trie 是正确的。

    借助 Java 的板载工具,可以使用 TreeMap(或任何 SortedMap,实际上):

    public <V> SortedMap<String, V> filterPrefix(SortedMap<String,V> baseMap, String prefix) {
        if(prefix.length() > 0) {
            char nextLetter = prefix.charAt(prefix.length() -1) + 1;
            String end = prefix.substring(0, prefix.length()-1) + nextLetter;
            return baseMap.subMap(prefix, end);
        }
        return baseMap;
    }
    

    输出甚至可以按键排序。

    这里是一个用法示例:

    SortedMap<String, String> nameNum = new TreeMap<String, String>();
    // put your phone numbers
    
    String prefix = ...;
    for(Map.Entry<String,String> entry : filterPrefix(nameNum, prefix).entrySet()) {
        System.out.println(entry);
    }
    

    如果您希望前缀过滤器不依赖于大小写差异,请为您的地图使用合适的比较器(例如具有合适强度设置的Collator,或String.CASE_INSENSITIVE_ORDER)。

    【讨论】:

    • @Paŭlo Ebermann :为什么选择 Trie,它如何节省空间 {stackoverflow.com/questions/8265476/trie-saves-space-but-how} ?
    • 您也可以使用前缀+“\uffff”作为结尾。
    • @PaŭloEbermann 我有同样的情况。但是现有的地图是一个 HashMap 实现(对于 10k+ 元素),它不能被改变。现在,为了按照上述解决方案实现这一点,如果我将 Hashmap 中包含的整个内容转储到 TreeMap 中,TreeMap 本身的构建将非常昂贵(因为它构建了一个排序结构),其余的可能会简单快捷.关于根据我的要求包装此解决方案的任何建议?
    • @abksrv 如果这只是一次搜索,那么对 HashMap 的所有条目进行一次迭代应该是最快的。如果您想更频繁地执行此操作,请将数据传输到更好的结构。 (另外,测量:也许对于您的数据集和硬件,甚至不需要优化。)
    【解决方案3】:

    这需要Trie 数据结构。有关 java 实现,请参阅this question。我用this one

    【讨论】:

    • 感谢 Bozho,您的链接很有用!但距离这个问题得到回答已经快 3 年了。现在有没有更好的解决方案,您可能知道?
    • 链接又断了 博卓
    【解决方案4】:

    使用guava Multimap 将简化您的解决方案。

    键是姓名的第一个字母,值是Collection,包含所有以键(第一个字母)开头的姓名-电话对。

    例子:

        public void test(){
          //firstLetter -> list of name-phone pair
          Multimap<String, Pair> mMap =  ArrayListMultimap.create();
    
          put(mMap, "Brown",  "+1236389023");
          put(mMap, "Bob",    "+1236389023");
          put(mMap, "Harmer", "+1236389023");
          put(mMap, "Harris", "+1236389023");
          put(mMap, "Hawken", "+1236389023");
          put(mMap, "Hosler", "+1236389023");
    
          //Test
          System.out.println(mMap.get("H"));
       }
    
       void put(Multimap<String, Pair> mMap, String name, String phone){
          mMap.put(name.substring(0,1), new Pair(name, phone));
       }
    
       public static class Pair{
          String name;
          String phone;
    
          public Pair(String name, String phone) {
             this.name = name;
             this.phone = phone;
          }
    
          @Override
          public String toString() {
             return "Pair [name="+name+", phone="+phone+"]";
          }
    

    }

    【讨论】:

      【解决方案5】:

      把它全部放在一个 MultiMap 中(或者只是将一个 List 作为值存储在你的 HashMap 中)。对于“棕色”,存储:

      "B"->["Brown"]
      "BR"->["Brown"]
      "BRO"->["Brown"]
      

      如果您稍后添加“Bradley”:

      "B"->["Brown", "Bradley"]
      "BR"->["Brown", "Bradley"]
      "BRO"->["Brown"]
      "BRA"->["Bradley"]
      

      等等……

      然后有另一张地图将“布朗”或“布拉德利”映射到电话号码。

      【讨论】:

      • 从这个数据结构中添加和删除东西会非常成本很高。
      • 我同意。但我们甚至不知道他的“电话簿之类的东西”有多大。我更喜欢先做一些简单的事情,然后再进行优化。这似乎是最简单的事情。
      • 访问将是 O(1),而对于树,它将是 log(n)。如果您正在执行诸如自动完成之类的操作,那不是更重要吗?数据集多久更新一次?如果获取比集合更频繁,谁在乎添加/删除有多慢。我认为在这里添加和删除并没有那么糟糕。
      猜你喜欢
      • 1970-01-01
      • 2013-05-15
      • 1970-01-01
      • 2016-01-02
      • 2023-03-28
      • 1970-01-01
      • 2011-07-05
      • 2021-04-23
      • 2015-02-07
      相关资源
      最近更新 更多