在HashMap中进行部分搜索

时间:2011-07-15 21:17:52

标签: java map filtering

我需要创建电话簿类的东西。它包含名称和数。现在当我键入字母时,应该返回匹配列表。对于下面给出的示例,当我键入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");

任何想法如何实现它? 提前致谢。

5 个答案:

答案 0 :(得分:29)

是的,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)。

答案 1 :(得分:9)

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

答案 2 :(得分:0)

将它全部放在MultiMap中(或者只是将List存储为HashMap中的值)。对于“布朗”,请存储:

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

如果您以后添加“Bradley”:

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

等...

然后有另一张地图将“Brown”或“Bradley”映射到电话号码。

答案 3 :(得分:0)

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

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()));

答案 4 :(得分:-1)

使用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+"]";
      }

}