Как я могу 'сломаться', когда встречается первый случай в значении HashMap? - PullRequest
0 голосов
/ 26 сентября 2019

Ниже вы можете увидеть мой фрагмент кода, где я пытаюсь определить страну происхождения данного телефонного номера.Проблема в том, что он всегда возвращает последний ключ сравниваемых строковых значений.

Я отсортировал мой HashMap по значению в порядке убывания, а затем сравнил заданную строку с каждым значением в HashMap, используя метод startWith.

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 голосов
/ 26 сентября 2019

Я написал новый метод и попытался сохранить временную последовательность потоков в другой карте.Теперь это работает!

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

}
0 голосов
/ 26 сентября 2019

Комбинируя элементы из нескольких других ответов, вы можете сохранить 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();

Однако, это более надежная идея, чем беспокоиться о разборе кодов стран или обнаружении совпадений междуони (особенно если ваши пользователи иногда забывают включить код своей страны) должны сначала запросить код страны, а затем остальную часть номера.Тогда вы можете просто посмотреть код страны, не беспокоясь о совпадениях.

0 голосов
/ 26 сентября 2019

HashMap не отсортировано.Вы не можете полагаться на его порядок, так как он в основном основан на hashCode().

Однако ваша проблема не связана с порядком, это потому, что вы не выбираете самый длинный префикс (значение с наибольшей длиной): если вы ищете 1-242, то США (1) и Багамские острова (1-242) работает.То же самое касается ExampleCountry (301) и Greece (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;
}
0 голосов
/ 26 сентября 2019

Ваш код выполнил именно то, для чего он был написан, и ваша проблема здесь

   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, вы снова вышли из строя, поэтому вам необходимо сначала сохранить свой заказ.

0 голосов
/ 26 сентября 2019

Я не верю, что эта строка сохраняет сортировку HashMap: она только сортирует поток для печати. ​​

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

Это означает, что когда вы зацикливаетесь на наборе записей карты, установленном позже, выв зависимости от порядка, в котором она перемещается по хеш-карте, и Java не дает никаких гарантий относительно этого порядка ( в документации даже говорится, что порядок может изменяться со временем).

Возможно, вы захотитевместо этого посмотрите SortedMap .

...