2016-12-19 3 views
9

Я столкнулся с сценарием, в котором я хочу зачеркнуть все ключи HashMap (не спрашивайте, почему, я просто должен это сделать). У HashMap есть несколько миллионов записей.В нижнем регистре все ключи HashMap

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

Map<String, Long> lowerCaseMap = new HashMap<>(myMap.size()); 
for (Map.Entry<String, Long> entry : myMap.entrySet()) { 
    lowerCaseMap.put(entry.getKey().toLowerCase(), entry.getValue()); 
} 

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

Теперь мой вопрос: как я могу выполнить эту задачу с наименьшим объемом памяти?

Удалил бы каждый ключ после нижнего регистра - добавил к новой подсказке карты?

Могу ли я использовать потоки java8, чтобы сделать это быстрее? (Например, что-то вроде этого)

Map<String, Long> lowerCaseMap = myMap.entrySet().parallelStream().collect(Collectors.toMap(entry -> entry.getKey().toLowerCase(), Map.Entry::getValue)); 

Update Кажется, что это Collections.unmodifiableMap, так что я не имеет возможности

удаления каждый ключа после строчного - добавляемого к новой карте

+2

не могли бы вы вставить ключи в нижнем регистре, в первую очередь? – Eran

+2

Нет ... Я использую API для получения этой Карты - это не мой код. – sestus

+0

Сохраняется ли чувствительность оригиналов к регистру? – davidxxx

ответ

13

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

Map<String, Long> map = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); 
map.putAll(myMap); 

После того, как вы построили эту карту, put() и get() будет вести себя независимо от регистра, так что вы можете сохранять и получать значения, используя Все- строчные ключи.Итерация по клавишам вернет их в свои оригинальные, возможно, формы в верхнем регистре.

Вот некоторые подобные вопросы:

+0

Это действительно интересный подход. Это не изменит API, для этого просто потребуется другое создание карты. Я пытаюсь его использовать. – sestus

+0

Так что кажется, что это сработало. Изменение было тривиальным - только изменение Карты было изменено. Спасибо! – sestus

3

Вы не можете удалить запись во время итерации по карте. Если вы попытаетесь это сделать, у вас будет ConcurentModificationException.

Поскольку проблема с OutOfMemoryError, а не с ошибкой производительности, использование параллельного потока тоже не поможет.

Несмотря на некоторую задачу в Stream API, будет сделано в последнее время, это все равно приведет к тому, что в какой-то момент в памяти будет две карты, поэтому у вас все еще будет проблема.

Чтобы обойти это, я видел только два пути:

  • дать больше памяти для вашего процесса (за счет увеличения -Xmx в командной строке Java). Память дешевая в эти дни;)
  • Разделить карту и работать в кусках: например, вы делите размер карты на десять, и вы обрабатываете один чанк за раз и удаляете обработанные записи перед обработкой нового фрагмента. При этом вместо того, чтобы иметь в два раза карту в памяти, у вас будет всего 1,1 раз карта.

Для алгоритма разделенного, вы можете попробовать коснуться, как это с помощью потока API:

Map<String, String> toMap = new HashMap<>();    
int chunk = fromMap.size()/10; 
for(int i = 1; i<= 10; i++){ 
    //process the chunk 
    List<Entry<String, String>> subEntries = fromMap.entrySet().stream().limit(chunk) 
     .collect(Collectors.toList()); 

    for(Entry<String, String> entry : subEntries){ 
     toMap.put(entry.getKey().toLowerCase(), entry.getValue()); 
     fromMap.remove(entry.getKey()); 
    } 
} 
+2

Я просто собираюсь процитировать @xenteros. Если на карте есть клавиша «key» и ключ «Key», вся логика наличия единственной строчной карты потерпит неудачу, и задача больше не будет иметь никакого смысла, поскольку конечное состояние не должно быть достижимо (если вы просто не удаляете один из них) – SomeJavaGuy

+0

Предполагается, что исходная карта не используется в течение этого времени. – davidxxx

+3

Вопрос (обновленный) говорит, что исходная карта вообще не изменена (т.е. карта, возвращаемая 'Collections.unmodifiableMap'), поэтому не имеет значения, пытаетесь ли вы удалить куски или отдельные записи (итерация и удаление в то же время работает, если вы используете 'Iterator.remove()'). – Holger

Смежные вопросы