HashMap com vários valores sob a mesma chave

199

É possível implementar um HashMap com uma chave e dois valores. Assim como o HashMap?

Por favor, me ajude, dizendo também (se não houver maneira) qualquer outra maneira de implementar o armazenamento de três valores com um como chave?

vidhya
fonte
2
possível duplicado de Como armazenar mais de uma string em um Mapa?
Joachim Sauer
Obrigado amigos ... mas eu tenho algumas limitações no uso MultiHashMap
Vidhya
Possível duplicado de implementação Mapa com chaves duplicadas
Steve Chambers

Respostas:

266

Você poderia:

  1. Use um mapa que tenha uma lista como valor. Map<KeyType, List<ValueType>>.
  2. Crie uma nova classe de wrapper e coloque instâncias desse wrapper no mapa. Map<KeyType, WrapperType>.
  3. Use uma tupla como classe (economiza a criação de muitos wrappers). Map<KeyType, Tuple<Value1Type, Value2Type>>.
  4. Use vários mapas lado a lado.

Exemplos

1. Mapa com lista como o valor

// 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];

A desvantagem dessa abordagem é que a lista não está vinculada exatamente a dois valores.

2. Usando a classe wrapper

// 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;

A desvantagem dessa abordagem é que você precisa escrever muito código da placa de caldeira para todas essas classes de contêineres muito simples.

3. Usando uma tupla

// 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;

Esta é a melhor solução na minha opinião.

4. Vários mapas

// 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"];

A desvantagem desta solução é que não é óbvio que os dois mapas estão relacionados; um erro programático pode fazer com que os dois mapas fiquem fora de sincronia.

Paul Ruane
fonte
Oi Paul ... você pode deixar um pouco mais claro ...? Por um exemplo ...?
Vidhya
@vidhya: o que se encaixa em particular no seu problema? Seus objetos múltiplos são do mesmo tipo ou diferentes?
Paul Ruane
Exemplo seria ótimo, na verdade.
Xonatron
@ Paulo, qualquer código exemplo simples para # 3 Map<KeyType, Tuple<Value1Type, Value2Type>>
Joarder Kamal
@CoolMind Tenho certeza de que as pessoas podem contornar os erros: ou talvez você possa corrigi-los?
Paul Ruane
61

Não, não apenas como HashMap. Você precisaria basicamente HashMapde uma chave para uma coleção de valores.

Se você gosta de usar bibliotecas externas, o Guava tem exatamente esse conceito em Multimapimplementações como ArrayListMultimape HashMultimap.

Jon Skeet
fonte
@ Jon, você poderia fornecer witha exemplo de trabalho em Java para a pergunta acima Perguntado por OP.Highly apreciado se você poderia postá-lo
Deepak
2
@Deepak: pesquise exemplos de multimapas de goiaba e você encontrará um código de amostra.
Jon Skeet
1
@Deepak: Basicamente, você constrói algo como ArrayListMultimapvocê ... ou simplesmente usa um HashMap<String, List<Integer>>ou o que quer. Você precisaria criar uma lista vazia sempre que um valor fosse adicionado pela primeira vez, basicamente.
Jon Skeet
1
você tem um exemplo de trabalho paraHashMap<String, List<Integer>>
Deepak
9
@ Deepak: Eu sugiro que você tente criar um exemplo, e se você ficar preso, faça uma pergunta, incluindo o código, na medida do possível. Você aprenderá muito mais dessa maneira.
Jon Skeet
23

Outra boa opção é usar o MultiValuedMap do Apache Commons. Dê uma olhada nas Classes de implementação conhecidas na parte superior da página para implementações especializadas.

Exemplo:

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

poderia ser substituído por

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

Assim,

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

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

resultaria em uma coleção collcontendo "A", "B" e "C".

Matthew Steven Monkan
fonte
13

Dê uma olhada nas Multimapbibliotecas da goiaba e sua implementação -HashMultimap

Uma coleção semelhante a um mapa, mas que pode associar vários valores a uma única chave. Se você chamar put (K, V) duas vezes, com a mesma chave, mas com valores diferentes, o multimapa conterá mapeamentos da chave para os dois valores.

Bozho
fonte
7

eu uso Map<KeyType, Object[]> para associar vários valores a uma chave em um mapa. Dessa forma, eu posso armazenar vários valores de diferentes tipos associados a uma chave. Você deve tomar cuidado, mantendo a ordem correta de inserção e recuperação de Object [].

Exemplo: considere, queremos armazenar informações do aluno. A chave é id, enquanto gostaríamos de armazenar o nome, endereço e e-mail associado ao aluno.

       //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
fonte
1
Para mim, esta é a melhor resposta. É mais simples, mais conciso e menos abstrato.
Morey
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);
Janarthanan Ramu
fonte
4

Apenas para constar, a solução JDK8 pura seria usar o Map::computemétodo:

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

Tal como

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);
}

com saída:

bar: foo
first: hello, foo, hello

Observe que, para garantir consistência, caso vários threads acessem essa estrutura de dados ConcurrentHashMape , CopyOnWriteArrayListpor exemplo, precisem ser usados.

Stepan Vavra
fonte
É melhor usar computeIfAbsent. map.computeIfAbsent(key, k -> new ArrayList<>()).add(value);
saka1029
3

Se você usa o Spring Framework . Há sim:org.springframework.util.MultiValueMap .

Para criar um mapa de múltiplos valores não modificável:

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

Ou use org.springframework.util.LinkedMultiValueMap

Igor Rybak
fonte
2

Sim e não. A solução é criar um clas Wrapper para seus valores que contenha os 2 (3 ou mais) valores que correspondem à sua chave.

Nicolas
fonte
2

A maneira mais fácil seria usar uma biblioteca de coleções do 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
    }

}

link da maven: https://mvnrepository.com/artifact/com.google.collections/google-collections/1.0-rc2

mais sobre isso: http://tomjefferys.blogspot.be/2011/09/multimaps-google-guava.html

Jorciney
fonte
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);
}

Eu tenho os melhores resultados.

Você apenas tem que criar novos HashMapcomo

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

no seu forloop. Terá o mesmo efeito que a mesma chave e vários valores.

Shahid Ahmad
fonte
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"));
   }                                                                                            
 }                                                                    

Resultado:

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

      ["111"]

Esta é a biblioteca Google-Guava para funcionalidades de utilitários. Esta é a solução necessária.

Ank_247shbm
fonte
É uma solução válida e eu usei essa abordagem em várias ocasiões.
letowianka
sim, está funcionando, mas está mostrando dados no formato [] eu quero esses itens, um por um, como obter esse pls emperrado aqui
Sunil Chaudhary
0

Não consegui postar uma resposta no comentário de Paul, por isso estou criando um novo comentário para Vidhya aqui:

O wrapper será um SuperClasspara as duas classes que queremos armazenar como um valor.

e dentro da classe wrapper, podemos colocar as associações como objetos de variável de instância para os dois objetos de classe.

por exemplo

class MyWrapper {

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

e no HashMap , podemos colocar dessa maneira,

Map<KeyObject, WrapperObject> 

WrapperObj terá variáveis ​​de classe:class1Obj, class2Obj

Dhruva Mistry
fonte
0

Você pode fazer isso implicitamente.

// 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";

Isso é muito simples e eficaz. Se você deseja valores de classes diferentes, pode fazer o seguinte:

HashMap<Integer, Object[]> map = new HashMap<Integer, Object[]>();
Veiga
fonte
0

Pode ser feito usando um identityHashMap, sujeito à condição de que a comparação de chaves seja feita pelo operador == e não seja igual a ().

jayendra bhatt
fonte
0

Eu prefiro o seguinte para armazenar qualquer número de variáveis ​​sem precisar criar uma classe separada.

final public static Map<String, Map<String, Float>> myMap    = new HashMap<String, Map<String, Float>>();
ozgeneral
fonte
0

Estou tão acostumado a fazer isso com um Dicionário de dados no Objetivo C. Foi mais difícil obter um resultado semelhante no Java para Android. Acabei criando uma classe personalizada e depois fazendo um mapa de hash da minha classe personalizada.

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;
    }

}
Vette
fonte
0

Usando Java Collectors

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

onde o departamento é sua chave

0cnLaroche
fonte
-9

Experimente o LinkedHashMap , exemplo:

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

resultado:

chaves: 1,1,2,3

valores: vinculado, hash, mapa, java

d.gjinovci
fonte
7
Isso não vai funcionar. linkednão existirá mais no mapa porque você o substituiu hash.
Jeff Mercado