WedX - журнал о программировании и компьютерных науках

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

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

Благодарность

22.06.2010

Ответы:


1

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

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

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

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

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

22.06.2010
  • Согласитесь, лучший способ - просто вставить, используя верхний или нижний регистр. 22.06.2010
  • К сожалению, преобразование в верхний/нижний регистр не проходит тест Турции (погуглите и посмотрите, что происходит с буквой «i»). Если важна интернационализация, лучше всего использовать TreeMap и да - помнить о проблемах с нарушением контракта. (IIRC, вызов remove/retainAll с определенными классами коллекций может дать неожиданные результаты.) Еще лучше, по возможности, использовать ImmutableSortedMap/Set Guava, что позволяет избежать непоследовательного поведения при использовании пользовательского компаратора. 28.12.2015
  • stackoverflow.com/questions/796986/what-is-the -индейка-тест 30.03.2018

  • 2

    Используйте 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
    
    22.06.2010
  • Здесь скрывается ошибка, необычная, но тем не менее сломанная: вызов map.keyset().removeAll(c) может использовать или не использовать компаратор карты - в зависимости от размера c! См. Sun bug 6394757. (Я все еще использую его.) В стороне: коллекции ImmutableSorted(Map|Set) Гуавы успокаивают вас, строго используя только Компаратор, никогда не равный. 05.10.2013
  • Используйте TreeMap, если вас не волнует сложность, производительность равна O(log n) по сравнению с O(1) для HashMap 03.04.2018

  • 3

    Вы можете использовать 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 как константу.

    22.06.2010
  • TreeMap немного хитрит в этом случае, так как легко нарушить контракт Map. containsKey() должен возвращать true тогда и только тогда, когда эта карта содержит сопоставление для ключа k, такого как (key==null ? k==null : key.equals(k)), но, конечно, в этом случае этого не произойдет. Это приведет к несоответствиям в какой-то момент, если карта используется всеми способами, кроме самых тривиальных. 22.06.2010
  • @Andrzej правда, но не хуже, чем некоторые другие нарушения контракта Map, например. IdentityHashMap<Object, V>, TreeMap<Double, V> 22.06.2010
  • @Andrzej: Это нормально, если вы признаете, что у него не будет семантики, определенной Map. Что уже указано в SortedMap javadoc: Обратите внимание, что порядок, поддерживаемый отсортированной картой (независимо от того, предоставлен явный компаратор или нет), должен соответствовать равенству, если отсортированная карта должна правильно реализовать интерфейс карты.. Он по-прежнему работает, если они не согласованы, он просто больше не подчиняется Map. 22.06.2010
  • Забавно, что вы упомянули, что String определяет это Comparator, но не говорите нам, что это такое: String.CASE_INSENSITIVE_ORDER 22.06.2010

  • 4

    В Apache Commons есть класс CaseInsensitiveMap.

    https://commons.apache.org/collections/

    22.06.2010
  • плохая вещь о коллекциях общего пользования заключается в том, что они не поддерживают дженерики, но хорошо не изобретать велосипед. 22.06.2010

  • 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".

    22.06.2010
  • Добавьте параметр Locale, и я проголосую за это 22.06.2010
  • Сделанный. У меня даже был такой в ​​более ранней версии... 22.06.2010
  • Маленькие (и, наверное, наивные) вопросы: 1. Почему бы не использовать ts.s().equalsIgnoreCase()? 2. Почему не оператор instanceof? 23.06.2010
  • @Nivas, на самом деле никаких особых причин. Просто так получилось, что код вышел. 23.06.2010

  • 6

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

    22.06.2010

    7

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

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

    и

    map.get(key.toLowerCase());
    

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

    22.06.2010

    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
          }
       }
    
    22.06.2010
  • Может быть лучше сделать ключ неизменяемым 22.06.2010

  • 9

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

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

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

    Конечно, это требует перебора моей большой ConcurrentHashMap, но у меня работает.

    18.06.2014
    Новые материалы

    Объяснение документов 02: BERT
    BERT представил двухступенчатую структуру обучения: предварительное обучение и тонкая настройка. Во время предварительного обучения модель обучается на неразмеченных данных с помощью..

    Как проанализировать работу вашего классификатора?
    Не всегда просто знать, какие показатели использовать С развитием глубокого обучения все больше и больше людей учатся обучать свой первый классификатор. Но как только вы закончите..

    Работа с цепями Маркова, часть 4 (Машинное обучение)
    Нелинейные цепи Маркова с агрегатором и их приложения (arXiv) Автор : Бар Лайт Аннотация: Изучаются свойства подкласса случайных процессов, называемых дискретными нелинейными цепями Маркова..

    Crazy Laravel Livewire упростил мне создание электронной коммерции (панель администратора и API) [Часть 3]
    Как вы сегодня, ребята? В этой части мы создадим CRUD для данных о продукте. Думаю, в этой части я не буду слишком много делиться теорией, но чаще буду делиться своим кодом. Потому что..

    Использование машинного обучения и Python для классификации 1000 сезонов новичков MLB Hitter
    Чему может научиться машина, глядя на сезоны новичков 1000 игроков MLB? Это то, что исследует это приложение. В этом процессе мы будем использовать неконтролируемое обучение, чтобы..

    Учебные заметки: создание моего первого пакета Node.js
    Это мои обучающие заметки, когда я научился создавать свой самый первый пакет Node.js, распространяемый через npm. Оглавление Глоссарий I. Новый пакет 1.1 советы по инициализации..

    Забудьте о Matplotlib: улучшите визуализацию данных с помощью умопомрачительных функций Seaborn!
    Примечание. Эта запись в блоге предполагает базовое знакомство с Python и концепциями анализа данных. Привет, энтузиасты данных! Добро пожаловать в мой блог, где я расскажу о невероятных..


    © 2024 wedx.ru, WedX - журнал о программировании и компьютерных науках
    Для любых предложений по сайту: [email protected]