HashMap с несколькими значениями под одним ключом

199

Можем ли мы реализовать HashMap с одним ключом и двумя значениями. Так же, как HashMap?

Пожалуйста, помогите мне, также рассказав (если нет никакого способа) какой-либо другой способ реализовать хранение трех значений с одним в качестве ключа?

Vidhya
источник
2
Возможный дубликат Как сохранить более одной строки на карте?
Йоахим Зауэр
Спасибо, друзья ... но у меня есть некоторые ограничения в использовании MultiHashMap
vidhya
Возможный дубликат реализации Map с дубликатами ключей
Стив Чамберс

Ответы:

266

Ты мог:

  1. Используйте карту со списком в качестве значения. Map<KeyType, List<ValueType>>,
  2. Создайте новый класс оболочки и поместите экземпляры этой оболочки на карту. Map<KeyType, WrapperType>,
  3. Используйте класс, подобный кортежу (экономит создание множества оберток). Map<KeyType, Tuple<Value1Type, Value2Type>>,
  4. Используйте несколько карт бок о бок.

Примеры

1. Карта со списком в качестве значения

// create our map
Map<String, List<Person>> peopleByForename = new HashMap<>();    

// populate it
List<Person> people = new ArrayList<>();
people.add(new Person("Bob Smith"));
people.add(new Person("Bob Jones"));
peopleByForename.put("Bob", people);

// read from it
List<Person> bobs = peopleByForename["Bob"];
Person bob1 = bobs[0];
Person bob2 = bobs[1];

Недостаток этого подхода заключается в том, что список не привязан к точно двум значениям.

2. Использование класса-обертки

// define our wrapper
class Wrapper {
    public Wrapper(Person person1, Person person2) {
       this.person1 = person1;
       this.person2 = person2;
    }

    public Person getPerson1 { return this.person1; }
    public Person getPerson2 { return this.person2; }

    private Person person1;
    private Person person2;
}

// create our map
Map<String, Wrapper> peopleByForename = new HashMap<>();

// populate it
Wrapper people = new Wrapper();
peopleByForename.put("Bob", new Wrapper(new Person("Bob Smith"),
                                        new Person("Bob Jones"));

// read from it
Wrapper bobs = peopleByForename.get("Bob");
Person bob1 = bobs.getPerson1;
Person bob2 = bobs.getPerson2;

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

3. Использование кортежа

// you'll have to write or download a Tuple class in Java, (.NET ships with one)

// create our map
Map<String, Tuple2<Person, Person> peopleByForename = new HashMap<>();

// populate it
peopleByForename.put("Bob", new Tuple2(new Person("Bob Smith",
                                       new Person("Bob Jones"));

// read from it
Tuple<Person, Person> bobs = peopleByForename["Bob"];
Person bob1 = bobs.Item1;
Person bob2 = bobs.Item2;

Это лучшее решение на мой взгляд.

4. Несколько карт

// create our maps
Map<String, Person> firstPersonByForename = new HashMap<>();
Map<String, Person> secondPersonByForename = new HashMap<>();

// populate them
firstPersonByForename.put("Bob", new Person("Bob Smith"));
secondPersonByForename.put("Bob", new Person("Bob Jones"));

// read from them
Person bob1 = firstPersonByForename["Bob"];
Person bob2 = secondPersonByForename["Bob"];

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

Пол Руане
источник
Привет, Пол ... ты можешь сделать это немного более понятным ...? Примером ...?
Видхья
@vidhya: что конкретно подходит для вашей проблемы? Ваши несколько объектов одного типа или разные?
Пол Руане
Пример был бы великолепен на самом деле.
Xonatron
@Paul, любой простой пример кода для # 3 Map<KeyType, Tuple<Value1Type, Value2Type>>
Joarder Kamal
@CoolMind Я уверен, что люди могут обойти ошибки: или вы могли бы их исправить?
Пол Руане
61

Нет, не просто так HashMap. Вам в основном нужен HashMapключ от набора значений.

Если вы счастливы использовать внешние библиотеки, у Guava есть именно эта концепция Multimapс такими реализациями, как ArrayListMultimapи HashMultimap.

Джон Скит
источник
@ Jon, не могли бы вы предоставить рабочий пример на Java для вышеупомянутого вопроса, заданного OP. Высоко признателен, если вы могли бы опубликовать его
Deepak
2
@Deepak: поиск примеров мультикарты гуавы, и вы найдете пример кода.
Джон Скит
1
@Deepak: По сути, вы бы построили что-то вроде ArrayListMultimapсебя ... или просто использовали HashMap<String, List<Integer>>или что-то еще. Вам нужно будет создать пустой список каждый раз, когда значение добавляется в первый раз, в основном.
Джон Скит
1
у вас есть рабочий пример дляHashMap<String, List<Integer>>
Дипак
9
@Deepak: Я предлагаю вам попробовать создать пример самостоятельно, и если вы застряли, задайте вопрос, включая код, насколько это у вас есть. Вы узнаете намного больше таким образом.
Джон Скит
23

Другим хорошим выбором является использование MultiValuedMap от Apache Commons. Взгляните на Все известные классы реализации в верхней части страницы для специализированных реализаций.

Пример:

HashMap<K, ArrayList<String>> map = new HashMap<K, ArrayList<String>>()

может быть заменено на

MultiValuedMap<K, String> map = new MultiValuedHashMap<K, String>();

Так,

map.put(key, "A");
map.put(key, "B");
map.put(key, "C");

Collection<String> coll = map.get(key);

приведет к коллекции, collсодержащей «A», «B» и «C».

Мэтью Стивен Монкан
источник
13

Взгляните на Multimapguava-библиотеки и их реализацию -HashMultimap

Коллекция, похожая на карту, но которая может связывать несколько значений с одним ключом. Если вы вызываете put (K, V) дважды, с одним и тем же ключом, но разными значениями, мультикарта содержит сопоставления ключа с обоими значениями.

Bozho
источник
7

Я использую Map<KeyType, Object[]>для связывания нескольких значений с ключом в карте. Таким образом, я могу хранить несколько значений разных типов, связанных с ключом. Вы должны позаботиться о поддержании правильного порядка вставки и извлечения из Object [].

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

       //To make entry into Map
        Map<Integer, String[]> studenMap = new HashMap<Integer, String[]>();
        String[] studentInformationArray = new String[]{"name", "address", "email"};
        int studenId = 1;
        studenMap.put(studenId, studentInformationArray);

        //To retrieve values from Map
        String name = studenMap.get(studenId)[1];
        String address = studenMap.get(studenId)[2];
        String email = studenMap.get(studenId)[3];
Sudarshan_SMD
источник
1
Для меня это лучший ответ. Это проще, более кратко и менее абстрактно.
Мори
6
HashMap<Integer,ArrayList<String>> map = new    HashMap<Integer,ArrayList<String>>();

ArrayList<String> list = new ArrayList<String>();
list.add("abc");
list.add("xyz");
map.put(100,list);
Джанартанан Раму
источник
4

Для справки, чистым решением JDK8 было бы использование Map::computeметода:

map.compute(key, (s, strings) -> strings == null ? new ArrayList<>() : strings).add(value);

Такие как

public static void main(String[] args) {
    Map<String, List<String>> map = new HashMap<>();

    put(map, "first", "hello");
    put(map, "first", "foo");
    put(map, "bar", "foo");
    put(map, "first", "hello");

    map.forEach((s, strings) -> {
        System.out.print(s + ": ");
        System.out.println(strings.stream().collect(Collectors.joining(", ")));
    });
}

private static <KEY, VALUE> void put(Map<KEY, List<VALUE>> map, KEY key, VALUE value) {
    map.compute(key, (s, strings) -> strings == null ? new ArrayList<>() : strings).add(value);
}

с выходом:

bar: foo
first: hello, foo, hello

Обратите внимание, что для обеспечения согласованности в случае, если несколько потоков обращаются к этой структуре данных, ConcurrentHashMapи, CopyOnWriteArrayListнапример, необходимо использовать.

Степан Вавра
источник
Это лучше использовать computeIfAbsent. map.computeIfAbsent(key, k -> new ArrayList<>()).add(value);
saka1029
3

Если вы используете Spring Framework . Существует: org.springframework.util.MultiValueMap.

Чтобы создать неизменяемую многозначную карту:

Map<String,List<String>> map = ...
MultiValueMap<String, String> multiValueMap = CollectionUtils.toMultiValueMap(map);

Или использовать org.springframework.util.LinkedMultiValueMap

Игорь Рыбак
источник
2

Да и нет. Решение состоит в том, чтобы создать классы Wrapper для ваших значений, которые содержат 2 (3 или более) значения, которые соответствуют вашему ключу.

никола
источник
2

Самый простой способ - использовать библиотеку коллекции Google:

import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;

public class Test {

    public static void main(final String[] args) {

        // multimap can handle one key with a list of values
        final Multimap<String, String> cars = ArrayListMultimap.create();
        cars.put("Nissan", "Qashqai");
        cars.put("Nissan", "Juke");
        cars.put("Bmw", "M3");
        cars.put("Bmw", "330E");
        cars.put("Bmw", "X6");
        cars.put("Bmw", "X5");

        cars.get("Bmw").forEach(System.out::println);

        // It will print the:
        // M3
        // 330E
        // X6
        // X5
    }

}

ссылка maven: https://mvnrepository.com/artifact/com.google.collections/google-collections/1.0-rc2

подробнее об этом: http://tomjefferys.blogspot.be/2011/09/multimaps-google-guava.html

Jorciney
источник
1
String key= "services_servicename"

ArrayList<String> data;

for(int i = 0; i lessthen data.size(); i++) {
    HashMap<String, String> servicesNameHashmap = new HashMap<String, String>();
    servicesNameHashmap.put(key,data.get(i).getServiceName());
    mServiceNameArray.add(i,servicesNameHashmap);
}

Я получил лучшие результаты.

Вам просто нужно создать новый HashMapлайк

HashMap<String, String> servicesNameHashmap = new HashMap<String, String>();

в вашей forпетле. Он будет иметь тот же эффект, что и тот же ключ и несколько значений.

Шахид Ахмад
источник
1
 import java.io.*;
 import java.util.*;

 import com.google.common.collect.*;

 class finTech{
public static void main(String args[]){
       Multimap<String, String> multimap = ArrayListMultimap.create();
       multimap.put("1","11");
       multimap.put("1","14");
       multimap.put("1","12");
       multimap.put("1","13");
       multimap.put("11","111");
       multimap.put("12","121");
        System.out.println(multimap);
        System.out.println(multimap.get("11"));
   }                                                                                            
 }                                                                    

Вывод:

     {"1"=["11","12","13","14"],"11"=["111"],"12"=["121"]}

      ["111"]

Это библиотека Google-Guava для служебных функций. Это необходимое решение.

Ank_247shbm
источник
Это правильное решение, и я использовал этот подход несколько раз.
Летовянка
да, это работает, но он показывает данные в [] формате, я хочу, чтобы эти элементы один за другим, как получить, пожалуйста, я застрял здесь
Сунил Чаудхари
0

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

Wrapper будет SuperClassдля двух классов, которые мы хотим сохранить в качестве значения.

а внутри класса-обертки мы можем поместить ассоциации в качестве объектов переменных экземпляра для двух объектов класса.

например

class MyWrapper {

 Class1 class1obj = new Class1();
 Class2 class2obj = new Class2();
...
}

и в HashMap мы можем положить таким образом,

Map<KeyObject, WrapperObject> 

WrapperObj будет иметь переменные класса:class1Obj, class2Obj

Дхрува Мистри
источник
0

Вы можете сделать это неявно.

// Create the map. There is no restriction to the size that the array String can have
HashMap<Integer, String[]> map = new HashMap<Integer, String[]>();

//initialize a key chosing the array of String you want for your values
map.put(1, new String[] { "name1", "name2" });

//edit value of a key
map.get(1)[0] = "othername";

Это очень просто и эффективно. Если вы хотите использовать значения разных классов, вы можете сделать следующее:

HashMap<Integer, Object[]> map = new HashMap<Integer, Object[]>();
Veiga
источник
0

Это можно сделать с помощью identityHashMap, при условии, что сравнение ключей будет выполнено оператором ==, а не equals ().

Джаендра Бхатт
источник
0

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

final public static Map<String, Map<String, Float>> myMap    = new HashMap<String, Map<String, Float>>();
ozgeneral
источник
0

Я так привык просто делать это с помощью словаря данных в Objective C. Было сложнее получить подобный результат в Java для Android. В итоге я создал собственный класс, а затем просто создал хэш-карту своего пользовательского класса.

public class Test1 {
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.addview);

//create the datastring
    HashMap<Integer, myClass> hm = new HashMap<Integer, myClass>();
    hm.put(1, new myClass("Car", "Small", 3000));
    hm.put(2, new myClass("Truck", "Large", 4000));
    hm.put(3, new myClass("Motorcycle", "Small", 1000));

//pull the datastring back for a specific item.
//also can edit the data using the set methods.  this just shows getting it for display.
    myClass test1 = hm.get(1);
    String testitem = test1.getItem();
    int testprice = test1.getPrice();
    Log.i("Class Info Example",testitem+Integer.toString(testprice));
}
}

//custom class.  You could make it public to use on several activities, or just include in the activity if using only here
class myClass{
    private String item;
    private String type;
    private int price;

    public myClass(String itm, String ty, int pr){
        this.item = itm;
        this.price = pr;
        this.type = ty;
    }

    public String getItem() {
        return item;
    }

    public void setItem(String item) {
        this.item = item;
    }

    public String getType() {
        return item;
    }

    public void setType(String type) {
        this.type = type;
    }

    public int getPrice() {
        return price;
    }

    public void setPrice(int price) {
        this.price = price;
    }

}
Корвет
источник
0

Мы можем создать класс, имеющий несколько ключей или значений, и объект этого класса можно использовать в качестве параметра на карте. Вы можете обратиться к https://stackoverflow.com/a/44181931/8065321

Викас Пал
источник
0

Использование Java Collectors

// Group employees by department
Map<Department, List<Employee>> byDept = employees.stream()
                    .collect(Collectors.groupingBy(Employee::getDepartment));

где отдел твой ключ

0cnLaroche
источник
-9

Попробуйте LinkedHashMap , пример:

Map<String,String> map = new LinkedHashMap<String,String>();    
map.put('1','linked');map.put('1','hash');    
map.put('2','map');map.put('3','java');.. 

вывод:

ключи: 1,1,2,3

значения: связанные, хэш, карта, Java

d.gjinovci
источник
7
Это не сработает. linkedбольше не будет существовать на карте, потому что вы заменили его на hash.
Джефф Меркадо