遇到首次在HashMap值中出现时,我该如何“中断”?

时间:2019-09-25 21:41:51

标签: java hashmap return

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

我已按值对我的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时,我希望输出为“巴哈马”,但实际输出为“美国”。输入301555666也是如此,我希望使用“ ExampleCountry”而不是“ Greece”。

5 个答案:

答案 0 :(得分:0)

我不认为这一行可以使HashMap保持排序:它仅对要打印的流进行排序。

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

这意味着以后再遍历地图的条目集时,您将受其遍历哈希地图的顺序的约束,而Java对此顺序不做任何保证(the documentation甚至说该顺序可能随时间变化)。

您可能想查看SortedMap

答案 1 :(得分:0)

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

但是,您的问题与订单无关,这是因为您没有选择最长的前缀(具有最大长度的值):如果您查找1-242,则查找USA(1)和巴哈马(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 :(得分:0)

您的代码完全按照编写的代码执行,您的问题在这里

   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,即USA。

原因是因为您正在使用流进行订购,并且当您输入getCountry时又再次出现了故障,因此您需要先保存订购。

答案 3 :(得分:0)

结合其他一些答案中的元素,您可能希望将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 :(得分:0)

我编写了一个新方法,并尝试将临时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));

}