добавление нескольких записей в HashMap одновременно в одном операторе

139

Мне нужно инициализировать константу HashMap, и я хотел бы сделать это в одной строке. Как избежать такого:

  hashMap.put("One", new Integer(1)); // adding value into HashMap
  hashMap.put("Two", new Integer(2));      
  hashMap.put("Three", new Integer(3));

аналогично этому в цели C:

[NSDictionary dictionaryWithObjectsAndKeys:
@"w",[NSNumber numberWithInt:1],
@"K",[NSNumber numberWithInt:2],
@"e",[NSNumber numberWithInt:4],
@"z",[NSNumber numberWithInt:5],
@"l",[NSNumber numberWithInt:6],
nil] 

Я не нашел ни одного примера, показывающего, как это сделать, просмотрев так много.

user387184
источник

Ответы:

261

Вы можете использовать инициализацию двойной скобки, как показано ниже:

Map<String, Integer> hashMap = new HashMap<String, Integer>()
{{
     put("One", 1);
     put("Two", 2);
     put("Three", 3);
}};

В качестве предупреждения, пожалуйста, обратитесь к потоку Эффективность Java «Инициализация двойной скобки», чтобы узнать о возможных последствиях для производительности.

Eng.Fouad
источник
11
@ user387184 Да, они называют это "инициализатором двойных скобок". См. Эту тему: stackoverflow.com/questions/924285/…
Eng.Fouad
2
Я просто вставляю его в свой код и получаю это предупреждение / сообщение в строке: «Сериализуемый класс не объявляет статическое окончательное поле serialVersionUID типа long». Могу я просто проигнорировать это? что это значит? Спасибо
user387184
32
Вы не должны использовать этот метод. Он создает новый класс каждый раз, когда вы его используете, что имеет гораздо худшую производительность, чем простое создание карты. См stackoverflow.com/questions/924285/...
Timo Türschmann
7
Причина, по которой я проголосовал против этого, состоит в том, что он не объясняет, что это создает новый класс каждый раз, когда вы его используете. Я думаю, что люди должны знать о компромиссах, если делать это таким образом.
idungotnosn 08
7
@ TimoTürschmann Похоже, что если бы мне когда-нибудь понадобилась статическая инициализация такой карты, она также была бы статической, устраняя каждый раз, когда вы ее используете, снижение производительности - у вас будет этот штраф один раз. Я не вижу другого времени, когда можно было бы использовать такую ​​инициализацию без статической переменной (например, будет ли кто-нибудь когда-либо использовать это в цикле?). Хотя я могу ошибаться, программисты изобретательны.
Chris Cirefice
68

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

import com.google.common.collect.ImmutableMap;

// For up to five entries, use .of()
Map<String, Integer> littleMap = ImmutableMap.of(
    "One", Integer.valueOf(1),
    "Two", Integer.valueOf(2),
    "Three", Integer.valueOf(3)
);

// For more than five entries, use .builder()
Map<String, Integer> bigMap = ImmutableMap.<String, Integer>builder()
    .put("One", Integer.valueOf(1))
    .put("Two", Integer.valueOf(2))
    .put("Three", Integer.valueOf(3))
    .put("Four", Integer.valueOf(4))
    .put("Five", Integer.valueOf(5))
    .put("Six", Integer.valueOf(6))
    .build();

См. Также: http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/collect/ImmutableMap.html.

Несколько связанный вопрос: обходной путь ImmutableMap.of () для HashMap в Картах?

JimmyT
источник
Гуава огромна, я бы не стал использовать ее в своем приложении для Android без крайней необходимости,
Эрикн
4
Остерегайтесь того, что ImmutableMapне принимает nullключи или значения.
Вадим
@ericn ProGuard позволяет исключить любые части библиотеки, которые вы не используете.
dimo414 03
58

Начиная с Java 9, можно использовать Map.of(...), например, так:

Map<String, Integer> immutableMap = Map.of("One", 1, 
                                           "Two", 2, 
                                           "Three", 3);

Эта карта неизменна. Если вы хотите, чтобы карта была изменяемой, вам нужно добавить:

Map<String, Integer> hashMap = new HashMap<>(immutableMap);

Если вы не можете использовать Java 9, вам придется написать аналогичный вспомогательный метод самостоятельно или использовать стороннюю библиотеку (например, Guava ), чтобы добавить эту функциональность за вас.

Тимо Тюршманн
источник
После добавления 10 записей выдает странную ошибку «не удается разрешить метод», это ошибка этого метода?
vikramvi
3
@vikramvi да, если вы посмотрите документацию, Map.of сделано только до 10 записей, так как это довольно трудоемко
jolivier
9

Карты также имеют фабричные методы, добавленные в Java 9. До 10 записей Карты имеют перегруженные конструкторы, которые принимают пары ключей и значений. Например, мы могли бы построить карту различных городов и их населения (по данным Google на октябрь 2016 г.) следующим образом:

Map<String, Integer> cities = Map.of("Brussels", 1_139000, "Cardiff", 341_000);

Случай var-args для Map немного сложнее, вам нужны как ключи, так и значения, но в Java методы не могут иметь два параметра var-args. Таким образом, общий случай обрабатывается путем использования метода Map.Entry<K, V>объектов var-args и добавления статического entry()метода, который их конструирует. Например:

Map<String, Integer> cities = Map.ofEntries(
    entry("Brussels", 1139000), 
    entry("Cardiff", 341000)
);

Методы фабрики коллекций в Java 9

Садик Али
источник
Отлично, если бы вы могли использовать Java 9+. Также этот фабричный метод возвращает неизменяемую карту.
Сураб
8

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

Если вам нужен такой синтаксис, подумайте о Groovy, который совместим с Java и позволяет вам:

def map = [name:"Gromit", likes:"cheese", id:1234]
dfraser
источник
6

Вот простой класс, который выполнит то, что вы хотите

import java.util.HashMap;

public class QuickHash extends HashMap<String,String> {
    public QuickHash(String...KeyValuePairs) {
        super(KeyValuePairs.length/2);
        for(int i=0;i<KeyValuePairs.length;i+=2)
            put(KeyValuePairs[i], KeyValuePairs[i+1]);
    }
}

А потом использовать это

Map<String, String> Foo=QuickHash(
    "a", "1",
    "b", "2"
);

Это дает {a:1, b:2}

Дакусан
источник
5
    boolean x;
    for (x = false, 
        map.put("One", new Integer(1)), 
        map.put("Two", new Integer(2)),      
        map.put("Three", new Integer(3)); x;);

Игнорирование объявления x(что необходимо, чтобы избежать диагностики «недостижимый оператор»), технически это только один оператор.

Горячие лижет
источник
14
Это отвратительно взломано.
Micah Stairs,
1
@MicahStairs - Но это всего лишь одно утверждение.
Hot Licks
2
Верно, но я никогда не надеюсь встретить такой код в продакшене.
Micah Stairs,
@MicahStairs - я видел и похуже.
Hot Licks
1
Боже, я искал это сегодня, как этот код работает? Я добавил его в код для тестирования, но я не могу понять, как он работает внутри ... :)
GOXR3PLUS
1

Вы можете добавить эту служебную функцию в служебный класс:

public static <K, V> Map<K, V> mapOf(Object... keyValues) {
    Map<K, V> map = new HashMap<>();

    for (int index = 0; index < keyValues.length / 2; index++) {
        map.put((K)keyValues[index * 2], (V)keyValues[index * 2 + 1]);
    }

    return map;
}

Map<Integer, String> map1 = YourClass.mapOf(1, "value1", 2, "value2");
Map<String, String> map2 = YourClass.mapOf("key1", "value1", "key2", "value2");

Примечание: в Java 9вы можете использовать Map.of

Р. Остерхолт
источник
Этот метод не следует использовать, поскольку реализация небезопасна по типу! Пример: Map <Integer, String> map1 = mapOf (1, «значение1», «ключ», 2, 2L);
Juls
Что ж, я ожидаю, что программист, который определяет Map<Integer, String>, также предоставит эти типы ...
Р. Остерхолт,
-1

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

import java.util.HashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Example {
    public static void main (String[] args){
        HashMap<String,Integer> hashMapStringInteger = createHashMapStringIntegerInOneStat("'one' => '1', 'two' => '2' , 'three'=>'3'  ");

        System.out.println(hashMapStringInteger); // {one=1, two=2, three=3}
    }

    private static HashMap<String, Integer> createHashMapStringIntegerInOneStat(String str) {
        HashMap<String, Integer> returnVar = new HashMap<String, Integer>();

        String currentStr = str;
        Pattern pattern1 = Pattern.compile("^\\s*'([^']*)'\\s*=\\s*>\\s*'([^']*)'\\s*,?\\s*(.*)$");

        // Parse all elements in the given string.
        boolean thereIsMore = true;
        while (thereIsMore){
            Matcher matcher = pattern1.matcher(currentStr);
            if (matcher.find()) {
                returnVar.put(matcher.group(1),Integer.valueOf(matcher.group(2)));
                currentStr = matcher.group(3);
            }
            else{
                thereIsMore = false;
            }
        }

        // Validate that all elements in the given string were parsed properly
        if (currentStr.length() > 0){
            System.out.println("WARNING: Problematic string format. given String: " + str);
        }

        return returnVar;
    }
}
Ури Зив
источник