【问题标题】:confusion about HashMap's method containsValue对 HashMap 的方法 containsValue 的困惑
【发布时间】:2011-09-24 01:21:56
【问题描述】:

我正在尝试找到我的方法的简化版本,我想知道您是否有更好的意见。

基本上我有一个 HashMap 将键值存储为 String-String[]

我想要一个方法来确定新插入的 String[]-value 是否包含已存在于已存储 String[]-value 中的 String。

我写的“并且显然工作正常”是以下方法:

static Map<String,String[]> myMap=new HashMap<String,String[]>();

    public static boolean kijkContains(String[] syn){

for(String s:myMap.keySet()){

    String[]temp=myMap.get(s);

    for(int i=0; i<temp.length; i++){

        for(int k=0; k<syn.length; k++){

            if(temp[i].equals(syn[k])){

                return true;
            }
        }
    }
  }
return false;
}

我的疑惑是循环的数量,这显然是一种高内存消耗的方法,我想知道你是否能想到更好的版本。

我尝试过使用 Map 的 containsValue() 方法,但由于该方法将 String[] 视为值而不是读取数组,因此我不能真正将其用作比较器。

【问题讨论】:

    标签: java arrays string loops hashmap


    【解决方案1】:

    您的代码不会使用特别多的内存,因为您没有创建任何 String[] 的副本(您只是复制对它们的引用,这非常便宜)。

    然而,您需要遍历HashMap 中的所有值,这使得这个 O(n)(或者简单地说:慢)。

    如果这是一个比较少见的手术,那么这可能是可以接受的,我不会担心的。

    如果插入是一种常见的操作,那么您绝对应该为此考虑一个更好的数据结构(您需要告诉我们更多关于实际用例的信息,以便我们在这里提出好的建议)。

    【讨论】:

      【解决方案2】:

      对于您目前拥有的数据结构,没有比循环遍历所有这些值更好的方法了(至少您在第一次命中时很早就突破了)。

      如果性能确实成为问题,您可能希望保留第二个数据结构来索引已经存在的内容。如果您只需要知道给定的字符串是否在地图中(而不是在哪里)(深),那么 HashSet&lt;String&gt; 可能会起作用。

      这是一个内存权衡:添加的索引结构会占用额外的空间。 (顺便说一句,除了地图已经占用的空间之外,您现在所做的事情并没有使用任何空间,您只是通过引用迭代数组,没有制作额外的副本)。

      【讨论】:

      • 并且由于 HashSet 是作为具有虚拟值的 HashMap 实现的,因此您也可以使用 HashMap 将每个字符串映射到它出现的原始映射中的键。 (如果一个字符串只能出现一次,否则那将是另一个字符串数组)。请注意,第二个地图中的所有这些字符串都不会占用任何额外空间(因为它们已经在原始地图中),只有地图本身会。
      • 您可能还想看看各种 MultiMap 实现之一,例如来自 Guava 的那个:guava-libraries.googlecode.com/svn/tags/release03/javadoc/com/… 我怀疑他们做的事情与您的方法完全不同,但至少您不必自己编写相同的代码。例如,它确实有一个应该可以工作的containsValue
      【解决方案3】:

      嗯,除了循环遍历这些值之外,我不知道有什么不同的方法,但我建议使用更简单的代码:

      Map<String, String[]> map = new HashMap<String, String[]>();
      for (String[] strings : map.values()) {
          ArrayUtils.contains(strings, "foo");
      }
      

      ArrayUtils 是一个来自 apache commons 的帮助类,当然你也可以循环遍历字符串数组并每次调用 equals。请注意,这是区分大小写的。

      Map<String, String[]> map = new HashMap<String, String[]>();
      for (String[] strings : map.values()) {
          for (String string : strings) {
              if (string.equals("foo")) {
                  return true;
              }
          }
      }
      

      【讨论】:

      • 使用 map.values() 的好处是,您不必一直在映射中查找键(因此不要计算键的哈希值)哈希图)。
      • 他需要ArrayUtils.containsAny,不过(如果有这样的话)
      • 如果他愿意,否则他可以使用不属于任何第三方库的第二个代码。
      【解决方案4】:

      【讨论】:

      • containsValue 将不起作用。即使你传入完全相同的字符串数组也可能不会(因为在 Java 中数组的 equals 被破坏了),如果你只传入一个字符串,当然不会。
      【解决方案5】:

      您可以通过将syn 数组复制到HashSet 中来降低时间复杂度。然后,您可以使用 O(1) 的 HashSet#contains 方法,而不是一遍又一遍地迭代 syn

      public static boolean kijkContains(String[] syn){
        if (syn == null || syn.length == 0) return false; // that was missing
        Set<String> input = new HashSet<String>();
        for (String s:syn)
          input.add(s);         // will remove duplicates, another perfo improvement
      
        for(String key:myMap.keySet()){
          for(String s:myMap.get(key)){  // we don't need the loop variable
            if (input.contains(s)) {
              return true;
            }
          }
        }
        return false;
      }
      

      复杂度O(i*j*k),我已将其减少到O(i*j+k)(i 是地图的大小,j 是值的平均大小数组和 k 同步数组的大小)

      【讨论】:

        【解决方案6】:

        所以你想要的是 HashMap 的值中的每个字符串都必须是唯一的。 我认为如果您使用 HashSet 而不是 String[] 作为 Map 的值,它将起作用。

        导入 java.util.*;

        公共类 TestHashMap { 公共静态 void main(String args[]) {

        System.out.println("Hashmap Test");
        HashMap<String, HashSet> myMap = new HashMap<String, HashSet>();
        
        HashSet<String> s1 = new HashSet<String>();
        s1.add("one");
        s1.add("two");
        s1.add("three");
        
        myMap.put("1", s1);
        System.out.println("Value for key 1" + myMap.get("1"));
        

        } }

        我希望这是你想要的方式。

        【讨论】:

        • 这行不通,因为它使字符串只对给定键保持唯一,而不是在整个地图中全局。
        • 感谢 Thilo 指出错误。可能是我没有得到问题。
        猜你喜欢
        • 1970-01-01
        • 2017-03-04
        • 2018-02-06
        • 2019-06-13
        • 2013-07-27
        • 2021-03-30
        • 1970-01-01
        • 2021-03-24
        • 2020-09-22
        相关资源
        最近更新 更多