Подтвердить что ты не робот

Как проверить ключ на карте, независимо от случая?

Я хочу знать, присутствует ли конкретный ключ в HashMap, поэтому я использую метод containsKey (key). Но он чувствителен к регистру, то есть он не возвращает true, если есть ключ с именем, и я ищу имя. Так что я могу узнать, не беспокоясь о случае ключа?

спасибо

4b9b3361

Ответ 1

Не с обычными картами.

"abc" представляет собой отличную строку из "ABC", их хэш-коды различны, а методы equals() возвращают false относительно друг друга.

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

Если вы хотите сохранить случай ключа, как предусмотрено, но с нечувствительным к регистру сравнением, вы можете изучить TreeMap и поставляя свой собственный компаратор, который будет сравнивать регистр без учета регистра. Однако задумайтесь, прежде чем спуститься по этому маршруту, так как вы столкнетесь с некоторыми непримиримыми несоответствиями - если кто-то называет map.put("abc", 1), а затем map.put("ABC", 2), в каком случае ключ хранится на карте? Можете ли вы это сделать? Вам комфортно с тем, что если кто-то обернет вашу карту стандартным, например, HashMap вы потеряете функциональность? Или, если кто-то все равно выполняет итерацию с помощью вашего набора ключей и делает свою собственную "проверку" с помощью equals(), вы получите непоследовательные результаты? Там будет много других подобных случаев. Обратите внимание, что вы нарушаете договор Map, делая это (поскольку ключевым равенством является определенный в терминах метода equals() на ключи), поэтому он действительно не работает в каком-либо смысле.

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

Ответ 2

Вы можете использовать TreeMap с пользовательским, нечувствительным к регистру Comparator (который использует String.compareToIgnoreCase())

Например:

Map<String, Something> map = 
    new TreeMap<String, Something>(CaseInsensitiveComparator.INSTANCE);

class CaseInsensitiveComparator implements Comparator<String> {
    public static final CaseInsensitiveComparator INSTANCE = 
           new CaseInsensitiveComparator();

    public int compare(String first, String second) {
         // some null checks
         return first.compareToIgnoreCase(second);
    }
}

Обновление: кажется, что String уже определил этот Comparator как константу.

Ответ 3

Используйте TreeMap, который построен с String#CASE_INSENSITIVE_ORDER.

Map<String, String> map = new TreeMap<String, String>(String.CASE_INSENSITIVE_ORDER);
map.put("FOO", "FOO");

System.out.println(map.get("foo")); // FOO
System.out.println(map.get("Foo")); // FOO
System.out.println(map.get("FOO")); // FOO

Ответ 5

Чтобы сохранить инварианты Map, вы можете просто создать свои собственные ключи. Внедрите разумный hashCode/equals, и вы хорошо пойдете:

final class CaseInsensitive {
    private final String s;
    private final Local lc;
    public CaseInsensitive (String s, Locale lc) { 
        if (lc == null) throw new NullPointerException();
        this.s = s; 
        this.lc = lc; 
    }

    private s(){ return s == null ? null : s.toUpperCase(lc); }

    @Override
    public int hashCode(){ 
        String u = s();
        return (u == null) ? 0 : u.hashCode(); 
    }

    @Override
    public boolean equals(Object o){ 
        if (!getClass().isInstance(o)) return false;
        String ts = s(), os = ((CaseInsensitive)other).s();
        if (ts == null) return os == null;
        return ts.equals(os);
    }
}

// Usage:
Map<CaseInsensitive, Integer> map = ...;
map.put(new CaseInsensitive("hax", Locale.ROOT), 1337);
assert map.get(new CaseInsensitive("HAX", Locale.ROOT) == 1337;

Примечание. Не все во всем мире согласны с тем, что является прописным словом того, что - известный пример - то, что версия "i" в верхнем регистре на турецком языке является "İ", а не "I".

Ответ 6

Map использует equals и hashCode для проверки равенства ключей, и вы не можете перезаписать их для String. Что вы можете сделать, так это определить свой собственный класс Key, который содержит строковое значение, но реализует equals и hashCode нечувствительным к регистру образом.

Ответ 7

Самый простой способ - сложить ключи самостоятельно, вставляя их и просматривая их. То есть.

map.put(key.toLowerCase(), value);

и

map.get(key.toLowerCase());

Вы можете подклассом, например. HashMap, чтобы получить свой собственный класс, если вы хотите, чтобы это было сделано автоматически.

Ответ 8

создайте свою собственную оболочку строкового класса, реализуйте equals и hashcode, используйте это как ключ в hashmap:

   class MyStringKey
   {
      private String string;
      public String getString()
      {
         return string;
      }
      public void setString(String string)
      {
         this.string = string;
      }

      public boolean equals(Object o)
      {
         return o instanceof MyStringKey && this.equalsIgnoreCase(((MyStringKey)o).getString());
      }

      public boolean hashCode()
      {
         return string.toLowerCase().hashcode(); //STRING and string may not have same hashcode
      }
   }

Ответ 9

В попытке представить ответ, соответствующий вашему запросу , не беспокоя случай ключа...

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

boolean caseInsensitiveMatch = false;
for (Map.Entry<String, Character> entry : MyServer.allCharacterMap.entrySet()) {
    if (entry.getKey().toLowerCase().equals(charNameToCreate.toLowerCase())){
        caseInsensitiveMatch = true;
        break;
    }
}

Конечно, для этого требуется цикл через мой большой ConcurrentHashMap, но работает для меня.