HashMap在同一键下具有多个值


199

我们是否可以用一个键和两个值来实现HashMap。就像HashMap一样?

还请告诉我(如果没有办法)通过其他任何方法来实现三个值的存储(以一个为键)的方法,对我有帮助吗?



谢谢朋友...但是我在使用MultiHashMap时有一些限制
vidhya 2011年

Answers:


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 2011年

@vidhya:哪个特别适合您的问题?您的多种对象是相同类型还是不同类型?
Paul Ruane

榜样实际上会很棒。
Xonatron

@ Paul,#3的任何简单示例代码Map<KeyType, Tuple<Value1Type, Value2Type>>
Joarder Kamal

@CoolMind我确定人们可以解决这些错误:或者您可以纠正它们?
Paul Ruane 2015年

61

不,不只是一个HashMap。您基本上需要一个HashMap从键到值的集合。

如果您乐于使用外部库,那么GuavaMultimap诸如ArrayListMultimap和的实现中都有这个概念HashMultimap


@Jon,您能为OP提出的上述问题提供Java的工作示例吗?非常感谢您可以将其发布
Deepak

2
@Deepak:搜索番石榴多图示例,您将找到示例代码。
乔恩·斯基特

1
@Deepak:基本上,您会构建ArrayListMultimap自己的东西……或者只是使用a HashMap<String, List<Integer>>或其他东西。基本上,每次添加值时,您都需要创建一个空列表。
乔恩·斯基特

1
您是否有一个适用的示例HashMap<String, List<Integer>>
Deepak

9
@Deepak:我建议您尝试自己创建一个示例,如果遇到困难,请提出一个问题,包括您所拥有的代码。这样您将学到更多。
乔恩·斯基特

23

另一个不错的选择是使用来自Apache Commons的MultiValuedMap。查看页面顶部的所有已知实现类以了解专门的实现。

例:

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

看一下Multimap番石榴库及其实现-HashMultimap

一个类似于Map的集合,但是可以将多个值与单个键相关联。如果使用相同的键但值不同两次调用put(K,V),则多映射将包含从键到两个值的映射。


7

Map<KeyType, Object[]>用于将多个值与Map中的键相关联。这样,我可以存储与键关联的不同类型的多个值。您必须通过保持正确的从Object []插入和检索的顺序来保重。

示例:考虑,我们要存储学生信息。密钥是ID,而我们希望存储与该学生相关的姓名,地址和电子邮件。

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

1
对我来说,这是最好的答案。它更简单,更简洁,更抽象。
Morey


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例如需要使用。


最好使用computeIfAbsentmap.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 clas,其中包含与您的密钥相对应的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


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库。这是必需的解决方案。


这是一个有效的解决方案,我已经多次使用这种方法。
letowianka

是的,它可以正常工作,但是它以[]格式显示数据。我希望这些项目一目了然地显示在此处
Sunil Chaudhary

0

我无法对Paul的评论发表评论,所以我在这里为Vidhya创建新评论:

包装器是SuperClass我们要存储为值的两个类的a。

在包装器类内部,我们可以将关联作为两个类对象的实例变量对象。

例如

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[]>();

0

可以使用identityHashMap来完成,条件是键比较将由==运算符而不是equals()完成。


0

我更喜欢以下内容来存储任意数量的变量,而不必创建单独的类。

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

0

我习惯在Objective C中使用数据字典来执行此操作。要在Java for 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

使用Java收集器

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

部门是您的关键


-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


7
那行不通。 linked将不再存在于地图中,因为您已将其替换为hash
Jeff Mercado 2013年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.