【问题标题】:How can i 'break' when first occurence in a HashMap value is met?当 HashMap 值第一次出现时,我如何“中断”?
【发布时间】:2019-09-25 21:41:51
【问题描述】:

您可以在下面看到我的代码 sn-p,我在其中尝试识别给定电话号码的原产国。问题是它总是返回比较字符串值的最后一个键。

我已按 desc 顺序按值对 HashMap 进行排序,然后使用 startWith 方法将给定的 String 与 HashMap 中的每个值进行比较。

import java.util.Comparator;
import java.util.Map;
import java.util.HashMap;
import java.util.Map.Entry;
import javax.swing.JOptionPane;

public class CountryFinder {

    static Map<String, String> countriesNamesAndCodes;

    public static void main(String[] args) {

        countriesNamesAndCodes = new HashMap<>();
        countriesNamesAndCodes.put("Greece", "30");
        countriesNamesAndCodes.put("Italy", "39");
        countriesNamesAndCodes.put("Germany", "49");
        countriesNamesAndCodes.put("USA", "1");
        countriesNamesAndCodes.put("UK", "44");
        countriesNamesAndCodes.put("Bahamas", "1-242");
        countriesNamesAndCodes.put("ExampleCountry", "301");

        for (Entry<String, String> entry : countriesNamesAndCodes.entrySet()) {
            if (entry.getValue().contains("-")) {
                String tempValue = entry.getValue().replaceAll("-", "");
                entry.setValue(tempValue);
            }
        }

        countriesNamesAndCodes.entrySet().stream().sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))
                .forEach(System.out::println);

        String input = String.valueOf(JOptionPane.showInputDialog("Type a telephone number"));
        System.out.println(input);
        System.out.println("Origin Country: " + getCountry(input));

    }

    private static String getCountry(String telephoneNumber) {
        for (Entry<String, String> entry : countriesNamesAndCodes.entrySet()){
            if (telephoneNumber.startsWith(entry.getValue())) {
                return (entry.getKey());
            }
        }
        return null;
    }
}

当输入为 1242888999 或 1-242888999 时,我希望输出为 "Bahamas" ,但实际输出为 "USA"。输入 301555666 也是如此,我希望使用“ExampleCountry”而不是“Greece”。

【问题讨论】:

    标签: java hashmap return


    【解决方案1】:

    HashMap 未排序。你不能依赖它的顺序,因为它主要是基于hashCode()

    但是您的问题与顺序无关,这是因为您没有选择最长的前缀(长度最长的值):如果您查找 1-242,那么 USA (1) 和 Bahamas (1-242 ) 有效。 ExampleCountry (301) 和希腊 (30) 也是如此。

    您的算法应该是这样的:对于电话号码以条目值开头的每个条目,我们会记住“最佳匹配”条目,并在从未找到时更新它(初始情况)或其值为比以前匹配的要大。

    private static String getCountry(String telephoneNumber) {
        var bestMatch = null; // Map.Entry<String,String>
        for (Entry<String, String> entry : countriesNamesAndCodes.entrySet()){
          if (telephoneNumber.startsWith(entry.getValue()) {
            if (bestMatch == null || entry.getValue().length() > bestMatch.getValue().length()) {
              bestMatch = entry;
            }
          }
        }
        return null != bestMatch ? bestMatch.getKey():null;
    }
    

    【讨论】:

      【解决方案2】:

      你的代码完全按照它的编写目的执行,你的问题就在这里

         private static String getCountry(String telephoneNumber) {
              for (Entry<String, String> entry : countriesNamesAndCodes.entrySet()){
                  if (telephoneNumber.startsWith(entry.getValue())) {
                      return (entry.getKey());
                  }
              }
              return null;
          }
      

      您正在提取第一个搜索匹配条目值,在您的情况下 (1-242888999) 以 1 开头,因此它将带来条目 = 1,即美国。

      原因是您正在使用就地流进行订购,当您进入 getCountry 时再次出现故障,因此您需要先保存您的订购。

      【讨论】:

        【解决方案3】:

        结合其他几个答案中的元素,您可能希望在国家/地区代码和国家/地区名称之间保留SortedMap,按长度降序排序(按字母顺序划分平局)。

        这样,总是首先检查最长的匹配,所以你不会遇到相同的前缀问题。

        import java.util.Map;
        import java.util.SortedMap;
        import java.util.TreeMap;
        import static java.util.Map.Entry;
        import static java.util.Comparator.comparingInt;
        import static java.util.Comparator.naturalOrder;
        
        class Example {
          public static void main(String[] args) {
        
            SortedMap<String, String> codeCountries = new TreeMap<>(
                comparingInt(String::length)
                  .reversed()
                  .thenComparing(naturalOrder()));
        
            codeCountries.putAll(Map.of(
                "1", "United States",
                "1241", "Bahamas",
            ));
        
            for (String name : codeCountries.values) {
                System.out.println(name);
                // Prints "United States", then "Bahamas"
            }
          }
        }
        

        您需要进行的唯一其他更改是,您必须在将国家代码 放入地图之前从国家代码中删除破折号。如果不这样做,这两个键都将保留在地图中。

        作为一种类似的、可能更快(但速度不会快到真正重要)的替代方案,您可以保留一个未排序的地图,其中国家代码作为其键,并且每当您收到输入时,您都可以尝试获取地图中的所有前缀:

        Map<String, String> countryCodes = Map.of("1242", "Bahamas", "1", "United States");
        
        String input = /* get some input */;
        
        for (int i = input.length; i > 0; --i) {
            String country = countryCodes.get(input.substring(0, i));
            if (country != null) {
                return country;
            }
        }
        // If you get here, no country code was found in the map.
        
        // Alternatively, with streams:
        IntStream.range(0, input.length())
          .mapToObj(i -> input.substring(0, input.length() - i))
          .filter(countryCodes::containsKey)
          .findFirst();
        

        但是,比担心解析国家代码或检测它们之间的重叠(尤其是如果您的用户有时忘记包含他们的国家代码)更可靠的方法是先询问国家代码,然后再询问其余数字。然后您可以直接查找国家代码,而不必担心重叠。

        【讨论】:

          【解决方案4】:

          我不相信这一行会让HashMap 保持排序:它只对流进行排序以进行打印。

          countriesNamesAndCodes.entrySet().stream()
             .sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))
             .forEach(System.out::println);
          

          这意味着当您稍后循环遍历映射的条目集时,您将受制于它通过哈希映射的顺序,Java 不保证该顺序(the documentation 甚至说顺序可能会改变时间)。

          您可能想改为查看SortedMap

          【讨论】:

            【解决方案5】:

            我编写了一个新方法并尝试将临时 Stream 排序保存在另一个 Map 中。现在可以了!

                private static Map<String, String> sortMapByValuesDesc(Map<String, String> unsortedMap) {
            
                return sortedMap = unsortedMap
                        .entrySet()
                        .stream()
                        .sorted(Collections.reverseOrder(Map.Entry.comparingByValue()))
                        .collect(
                                toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e2, LinkedHashMap::new));
            
            }
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2013-12-09
              • 1970-01-01
              • 1970-01-01
              • 2018-11-09
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多