【问题标题】:Check if a List is a value in a HashMap检查 List 是否是 HashMap 中的值
【发布时间】:2015-12-31 17:15:58
【问题描述】:

我想检查一个值是否存在。

我的代码:

public static final Map<String, List<String>> m = new HashMap<>();

if(m.containsValue("gummi")) {...}

我的问题是该值是一个列表。 如何查看列表?

【问题讨论】:

  • 这有什么更新吗?这是一个非常有趣的问题。 Tunaki 通过假设方法的调用者知道列表中的项目而走捷径,这违背了目的。提问者问的是完全不同的东西。
  • @HorseVoice 你会说 OP 到底在问什么?我还不清楚。他们是在问Map 中是否有任何List 包含"gummi",还是在问Map 中是否存在 包含"gummi"List?我很乐意写一个适合这两种情况的答案。
  • 我猜是前者;是否有任何列表包含“gummi”?
  • @HorseVoice - 这是 Java 7 版本(根据要求):stackoverflow.com/questions/32925049/…
  • 这个问题本身并不清楚。要检查该值是否存在,需要检查类似的类型。尽可能接近它可以根据答案解释,它应该读取如果对象存在于Map...的任何值中...

标签: java list hashmap


【解决方案1】:

引用List.equals Javadoc:

换句话说,如果两个列表包含相同顺序的相同元素,则它们被定义为相等。

因此,要检查一个值是否存在,您可以创建一个列表并使用该List 调用containsValue,因为此方法将调用equals 来确定相等性。如果相同元素的顺序相同,则会找到匹配项。

您可以使用Arrays.asList 从一组值轻松创建List

if(m.containsValue(Arrays.asList("gummi"))) {...}

示例代码:

Map<String, List<String>> m = new HashMap<>();
List<String> list = new ArrayList<>();

list.add("1");
list.add("2");
m.put("key", list);

if (m.containsValue(Arrays.asList("1", "2"))) {
    System.out.println("contains value");
}

【讨论】:

  • 这要求调用者知道列表的所有成员是一个值。
  • @Tunaki,这不是提问者要问的。如果您已经知道数组的内容,那么这个问题是微不足道的。如果您不知道列表的内容将包含什么,则需要解决一个有趣的问题。您的回答走捷径,并没有完全回答问题。我已经为此设置了赏金。
【解决方案2】:

所以我要解释这个问题,想知道Map 的任何值是否包含目标String

可能有更合适的第三方双向多地图实现。如果您将来阅读此内容,Java SE 中可能会出现此类类型 - 请编辑此答案。

鉴于数据结构,这将需要搜索整个数据。以另一种方式映射Map 可能会更好。让我们忽略它。

streams 单行表达式解决方案:

m.values().stream().anyMatch(list -> list.contains("gummi"))

编辑:对于 Horse Voice,非流媒体版本。

flatContains(m.values(), "gummi")

在哪里

public static boolean flatContains(
    Iterable<? extends Collection<?>> collections,
    Object value
) {
    for (Collection<?> collection : collections) {
        if (collection.contains(value)) {
            return true;
        }
    }
    return false;
}

通常避免使用流可以使代码更具可读性(并且速度更快)。但是,在这种情况下,我会直奔溪流。如果涉及到flatMap,选择会更接近。

【讨论】:

  • 你能在不包括 lambdas 的 java 7 中给出相同的答案吗?
  • @HorseVoice:在此处添加 Java 7 版本:stackoverflow.com/a/58871387/2015408
  • 或执行flatMap 以避免contains 覆盖列表,m.values().stream().flatMap(Collection::stream).anyMatch(str -&gt; str.equals("gummi"))
  • @Naman 你可以,但我认为在这种情况下不会有太多优势。 flatMap 让它不太清楚,我认为性能会更差。但是,它确实允许您将谓词切换为更有趣的东西。
【解决方案3】:
Map<String, List<String>> m = new HashMap<>();

    List<String> list1 = new ArrayList<>();
    List<String> list2 = new ArrayList<>();
    List<String> list3 = new ArrayList<>();

    m.put("1", list1);
    m.put("2", list2);

    System.out.println(m.containsValue(list1));
    System.out.println(m.containsValue(list2));
    System.out.println(m.containsValue(list3));

输出

true
true
true

说明

只要新列表为空,哈希码值始终为 1。所以 list1,list2 和 list3 的 hashcode 值都是 1。基本意思是,

list1 = list2 = list3

所以即使我们检查 m.containsValue(list3);它将返回 true。但是如果列表不为空,每个列表都会有一个唯一的哈希码。因此,只要您有一个包含非空列表的地图,那么我们就可以使用 containsValue(listObject) 来返回正确的结果。下面的例子,

Map<String, List<String>> m = new HashMap<>();

    List<String> list1 = new ArrayList<>();
    List<String> list2 = new ArrayList<>();
    List<String> list3 = new ArrayList<>();

    list1.add("apple");
    list1.add("orange");

    list2.add("mobile");
    list2.add("computer");

    m.put("1", list1);
    m.put("2", list2);

    System.out.println(m.containsValue(list1));
    System.out.println(m.containsValue(list2));
    System.out.println(m.containsValue(list3));

输出:

true
true
false

其他用例:

如果我们想检查映射中的值是否属于列表类型,或者我们想检查一个值是否存在于列表之一中,那么我们可以如下迭代映射,

for (String key : m.keySet()) {
        if (m.get(key) instanceof List) {
            for (String listItem : m.get(key)) {
                if ("valueWeSearchFor".equalsIgnoreCase(listItem)) {
                    break;
                }
            }
        }else{
            //If the value in the map is not of type list
            //What to do!!
        }
    }

【讨论】:

    【解决方案4】:

    @Tom 的回答最好不过,@HorseVoice 要求 Java 7 版本,如下:

    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    /**
     * https://stackoverflow.com/questions/32925049/check-if-a-list-is-a-value-in-a-hashmap
     *
     * @author surender.kumar
     */
    public class CheckListValueInHashMapExists {
    
        public static final Map<String, List<String>> m = new HashMap<>();
    
        public static void main(String[] args) {
            System.out.println("Running main ...");
            initM();
            String gummi = "gummi";
    
            System.out.println("Do the list contains value gummi? " + checkValueExistsJava7());
        }
    
        private static boolean checkValueExistsJava7() {
            for (List<String> list : m.values()) {
                if (list.contains("gummi")) {
                    return true;
                }
            }
            return false;
        }
    
        private static void initM() {
            List<String> list1 = new ArrayList<>();
            List<String> list2 = new ArrayList<>();
            list1.add("value1a");
            list1.add("value1b");
            list2.add("value2");
            list2.add("gummi");
            m.put("1", list1);
            m.put("2", list2);
        }
    }
    

    输出:

    Running main ...
    Do the list contains value gummi? true
    

    【讨论】:

    • 如果你不打算缓存,那么当你找到一个匹配的时候你肯定会短路!您可能会在第一个列表中找到“gummi”!
    【解决方案5】:

    hashMap 是键值存储,当您想将值搜索到值时速度很慢,因此首先规范化您的地图以喜欢这种对键和值的交换

    Map<String, String> searchMap = m.entrySet().stream()
                    .flatMap(entry -> entry.getValue().stream().map(etn -> new AbstractMap.SimpleEntry<>(etn, entry.getKey())))
                    .collect(Collectors.toMap(AbstractMap.SimpleEntry::getKey, AbstractMap.SimpleEntry::getValue));
    

    在这样的搜索值之后

    String mkey = searchMap.get("gummi");
    

    现在 mkey 是原始映射键。 if is null 在原始映射中的任何值中都不存在,如果键不为 null,您可以像这样访问已建立的列表

    List<String> mValue = m.get(mkey);
    

    【讨论】:

    • 嗯,我确定它有效。它仅适用于您将在不更新基础数据的情况下进行多次搜索的情况,这似乎很适合。如果你想要那个映射,可能保持这种方式。话虽如此,在存在错误的情况下维护多个数据副本是很麻烦的。 / 通常Map&lt;K, List&lt;V&gt;&gt; 的反向映射将是Map&lt;V, Collection&lt;K&gt;&gt; 而不是Map&lt;V, K&gt;Collectors.groupingBy 可能有用也可能没用 此外,还有来自 ye olde 的 Map.entry,不是由 Oracle 公开维护的,Java SE 9。(不是我的 -1,顺便说一句。)
    • @TomHawtin-tackline 感谢您的评论。我只是虽然搜索和你的 cmets 绝对正确
    【解决方案6】:

    Java 7:contains 代表Map&lt;String, List&lt;String&gt;&gt;

    假设和 Tom 一样,这里是 Java 7 版本(应Horse Voice 的要求):

    // Live version
    static boolean contains(Map<String, List<String>> map, String value) {
        for (List<String> list : map.values()) {
            if(list.contains(value)) return true;
        }
        return false;
    }
    

    如果您的源地图没有改变,可能最好创建一个搜索缓存:

    // Cached version
    private List<String> allValues = getInnerValues(map);
    private List<String> getInnerValues(Map<String, List<String>> map) {
        List<String> result = new ArrayList<>();
        for (List<String> list : map.values()) {
            result.addAll(list);
        }
        return result;
    }
    
    static boolean contains(String value) {
        return allValues.contains(value);
    }
    

    Java 7:anyKeyForValuekeysForValue

    我认为有些人会想要与值匹配的键:

    // Live version
    static String anyKeyForValue(Map<String, List<String>> map, String value) {
        for (Entry<String, List<String>> entry : map.entrySet()) {
            if(entry.getValue().contains(value)) return entry.getKey();
        }
        return null;
    }
    
    static List<String> keysForValue(Map<String, List<String>> map, String value) {
        List<String> result = new ArrayList<>();
        for (Entry<String, List<String>> entry : map.entrySet()) {
            if(entry.getValue().contains(value)) result.add(entry.getKey());
        }
        return result;
    }
    

    缓存有点困难;我们需要反转地图:

    // Cached version
    private Map<String, List<String>> valueKeys = invert(map);
    private Map<String, List<String>> invert(Map<String, List<String>> map) {
        Map<String, List<String>> result = new HashMap<>();
        for (Entry<String, List<String>> entry : map.entrySet()) {
            for (value : entry.getValue()) {
                List<String> keys;
                if (result.containsKey(value)) {
                    keys = result.get(value);
                } else {
                    keys = new ArrayList<String>();
                    result.put(value, keys);
                }
                keys.add(entry.getKey());
            }
        }
        return result;
    }
    
    static List<String> keysForValue(String value) {
        return valueKeys.get(value);
    }
    

    【讨论】:

    • 我认为这个代码是正确的答案,你有没有试过这个代码@max
    【解决方案7】:

    我认为 Surender Kherwa 的回答符合条件,但我认为当您在集合中查找元素时,最好使用 while 循环而不是 for,这是示例(针对 java 7):

    String gummi = "gummi";
    Map<String,List<String>> map = new HashMap<String,List<String>>() {
    {
    
    put("KEY1", new ArrayList<String>(){{
        add("check");
        add("if");
        add("a");
        add("list");
        add("is");
    }});
    
    put("KEY2", new ArrayList<String>(){{
        add("a");
        add("gummi");
        add("value");
        add("in");
        add("a");
    }});
    
    put("KEY3", new ArrayList<String>(){{
        add("HashMap");
        add("java");
        add("7");
        add("while");
        add("loop");
        add("version");
    }});
    
    }};
    
    Iterator<Map.Entry<String, List<String>>> iterator = map.entrySet().iterator();
    while (iterator.hasNext()) {
      Map.Entry<String, List<String>> entry = iterator.next();
      if (entry.getValue().contains(gummi)) {
        System.out.println("Found in : " + entry.getKey());
        break;
      }
    }
    

    【讨论】:

    • 我不明白为什么你认为while 循环更好。除非您打算采用某种 SESE(单进单出)风格:while (iterator.hasNext() &amp;&amp; (entry = iterator.next()).getValue().equals(gummi)) {。 (不是我特别喜欢的风格,但看看它来自哪里,但在这里不是特别有用。)
    • 除了之前的评论。不要使用双括号初始化,我们不需要无用的匿名类。
    【解决方案8】:

    如果值是一个列表,可以使用 List 的 contains 方法来检查是否 列表具有所需的字符串。

    public static void main(String args[]){
        public static final Map<String, List<String>> m = new HashMap<>();
        checkStringInMapOfLists("gummi",m);
    }
    

    下面的方法遍历 hashmap 中的值并检查 hashmap 中的任何列表是否包含所需的 String

    private boolean checkStringInMapOfLists(String input,Map<String, List<String>> m){
        for(List<String> list : m.values()){
            if(list.contains(input))
                return true;
        }
        return false;
    }
    

    【讨论】:

      【解决方案9】:

      我已经检查了两个使用 java 8 的场景。下面的分析是针对最坏情况的。

      场景 1:地图包含 1000000 个键

      似乎在这种情况下,java流API比循环慢。该地图包含 1000000 个键。 Streams 花了 107543400 纳秒来完成任务。循环耗时 73688600 纳秒。但是,并行流只用了 51464500 纳秒。

      查看以下解决方案代码。

      public static void main(String[] args) {
          try {
              App app = new App();
              String parameter = "invalid value";
              int numberOfKeys = 1000000;
      
              Map<String, List<String>> m = new HashMap<>();
      
              // adding values to map
              for (int i = 0; i < numberOfKeys ; i++) {
                  m.put(i + "",
                          Arrays.asList(i * 10 + "", i * 11 + "", i * 12 + "", i * 13 + "", i * 14 + "", i * 15 + ""));
              }
      
              /** check time difference **/
      
              System.out.println("Loops");
              long begin = System.nanoTime();
              boolean b = app.containsValue_loops(m, parameter);
              System.out.println(System.nanoTime() - begin);
              System.out.println(b);
      
              System.out.println("\nJava 8 - stream");
              begin = System.nanoTime();
              boolean a = app.containsValue_java8(m, parameter);
              System.out.println(System.nanoTime() - begin);
              System.out.println(a);
      
              System.out.println("\nJava 8 - parallel stream");
              begin = System.nanoTime();
              boolean c = app.containsValue_java8_parallel(m, parameter);
              System.out.println(System.nanoTime() - begin);
              System.out.println(c);
      
          } catch (Exception e) {
              System.out.println(e);
          }
      }
      
      
      /** Checks parameter contains in the list using loops **/
      public boolean containsValue_loops(Map<String, List<String>> m, String parameter) {
          for (List<String> list : m.values()) {
              if (list.contains(parameter)) {
                  // return immediately if value contains in the list
                  return true;
              }
          }
          return false;
      }
      
      /** Checks parameter contains in the list using streams **/
      public boolean containsValue_java8(Map<String, List<String>> m, String parameter) {
          return m.values().stream().anyMatch(list -> list.contains(parameter));
      }
      
      /** Checks parameter contains in the list using parallel streams **/
      public boolean containsValue_java8_parallel(Map<String, List<String>> m, String parameter) {
          return m.values().parallelStream().anyMatch(list -> list.contains(parameter));
      }
      

      这是输出:

      Loops
      73688600
      false
      
      Java 8 - stream
      107543400
      false
      
      Java 8 - parallel stream
      51464500
      false
      

      场景 2:地图只包含 10 个键

      这是输出:

      Loops
      185000
      false
      
      Java 8 - stream
      31721800
      false
      
      Java 8 - parallel stream
      2987000
      false
      

      在这种情况下,for 循环比流花费的时间更少。它也比并行流更快。

      因此,作为结论,我的建议是对小集合使用循环,如果集合较大,则使用并行流。

      【讨论】:

        【解决方案10】:

        迭代 Map 集合以检查 inputString 与每个映射值,以便您可以使用匹配找到的键以供进一步使用。

            Map<String, List<String>> m = new HashMap<>();
            String searchStr="a";
            List<String> list1 = new ArrayList<>();
            list1.add("a");
            list1.add("b");
            m.put("1", list1);
            list1 = new ArrayList<>();
            list1.add("c");
            m.put("2", list1);
            list1 = new ArrayList<>();
            list1.add("a");
            list1.add("d");
            m.put("3", list1);
        
            for (Entry<String, List<String>> entry : m.entrySet()) {
                if (entry.getValue().contains(searchStr)) {
                    System.out.println(searchStr+" found for map key="+entry.getKey());
                }
            }
        

        【讨论】:

          【解决方案11】:

          您必须问自己要浪费多少资源,以及您可以允许执行此任务的运行时间是多少。因此,假设您想要一个直截了当的解决方案,解决方案就变得非常简单。由于您使用的是哈希映射,因此键没有按任何排序,因此您可以迭代键并查看列表是否包含该值,如果是则中断循环。由于这个解决方案太简单了,听起来像一个家庭作业,我将把解决方案留给你。这只是几行代码。从我的头顶看,它看起来像:

          for(String key:m.keyset()){
          if(m.get(key).contains("gummi")) return true;
          }
          return false;
          

          此外,如果键和列表上下文之间存在任何联系,则有多种方法可以找到解决方案。一种是使用人工智能。和回归/训练,因此您无需查看列表即可猜测列表的内容。

          另一种解决方案是用于多核和并行处理的 map-reduce。

          你看,你的问题太棒了,不同背景的不同人可以做出不同的回答。问题是,您还没有定义所需的性能、您将要处理的数据量等参数,所以任何有效的都是正确的答案。

          此外,在指针的世界中,您可以设计自己的数据结构来以最佳性能解决这个问题。否则,您可以使用树木和其他杂技……但您必须描述问题的框架。

          https://plato.stanford.edu/entries/frame-problem/

          您也可以在简单地将对象作为字节数组读取时使用 o(n)。

          【讨论】:

            【解决方案12】:

            如果您正在检查 Map 的任何值中是否存在对象。使用涉及 Java-8 及更高版本的Stream 的方法,您可以利用flatMap 和收集toSet 在扁平化集合上执行有效的contains

            boolean presentInAnyValue = m.values().stream()
                    .flatMap(Collection::stream)
                    .collect(Collectors.toSet())
                    .contains("gummi");
            

            此代码的 Java-7 转换如下所示:

            Set<String> set = new HashSet<>();
            for (List<String> strings : m.values()) {
                set.addAll(strings);
            }
            boolean presentInAnyValue = set.contains("gummi");
            

            【讨论】:

            • ... 否决票的原因是? (只是对downvoter的评论,并不是我真的希望得到回复:))
            【解决方案13】:

            这将获取地图中列表中包含的任何值的键。 Java 7

            public String getKeyForValue(String value) {
                for (String key : map.keySet())
                    if (map.get(key).contains(value))
                        return key;
                return null;
            }
            

            如果值包含在哈希映射 (Java 7) 的列表中的任何位置,这将返回 true 或 false。

            public boolean mapContains(String value) {
                for (String key : map.keySet())
                    if (map.get(key).contains(value))
                        return true;
                return false;
            }
            

            【讨论】:

              【解决方案14】:

              这就是我所做的,只是将对象类型更改为您需要的任何类型

              private static boolean isInListOfHash(Map<List<String>, ICommand> hashMap, String word) {
                  for (List<String> list : hashMap.keySet()) {
                      for (String string : list)
                          if (string.equalsIgnoreCase(word))
                              return true;
                  }
                  return false;
              }
              

              【讨论】:

                猜你喜欢
                • 2021-07-16
                • 1970-01-01
                • 1970-01-01
                • 2013-04-09
                • 1970-01-01
                • 2014-03-15
                • 2021-01-10
                • 1970-01-01
                • 2022-01-09
                相关资源
                最近更新 更多