HashMap ที่มีหลายค่าภายใต้คีย์เดียวกัน


199

เป็นไปได้ไหมที่เราจะใช้ HashMap ด้วยหนึ่งคีย์และสองค่า เช่นเดียวกับ HashMap?

โปรดช่วยฉันด้วยโดยบอก (หากไม่มีวิธี) วิธีอื่น ๆ ในการใช้การจัดเก็บค่าสามค่าโดยมีค่าเป็นคีย์?



ขอบคุณเพื่อน ... แต่ฉันมีข้อ จำกัด ในการใช้ MultiHashMap
vidhya

คำตอบ:


266

คุณสามารถ:

  1. ใช้แผนที่ที่มีรายการเป็นค่า Map<KeyType, List<ValueType>>.
  2. สร้างคลาส wrapper ใหม่และวางอินสแตนซ์ของ wrapper นี้ในแผนที่ Map<KeyType, WrapperType>.
  3. ใช้ tuple like class (บันทึกการสร้าง wrappers จำนวนมาก) 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. ใช้คลาส 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;

ข้อเสียของวิธีนี้คือคุณต้องเขียนโค้ดจำนวนมากของ Boiler สำหรับคลาสคอนเทนเนอร์ที่เรียบง่ายเหล่านี้ทั้งหมด

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

ข้อเสียของการแก้ปัญหานี้ก็คือมันไม่ชัดเจนว่าทั้งสองแผนที่มีความเกี่ยวข้องข้อผิดพลาดทางโปรแกรมอาจเห็นว่าแผนที่ทั้งสองหายไป


สวัสดี Paul ... คุณสามารถทำให้ชัดเจนขึ้น ... โดยยกตัวอย่าง ...
vidhya

@ vidhya: โดยเฉพาะอย่างยิ่งที่เหมาะกับปัญหาของคุณ? วัตถุ mulitple ของคุณเหมือนหรือแตกต่างกันหรือไม่?
Paul Ruane

ตัวอย่างจะดีจริง
Xonatron

Map<KeyType, Tuple<Value1Type, Value2Type>>
@Paul

@CoolMind ฉันแน่ใจว่าผู้คนสามารถหลีกเลี่ยงความผิดพลาดได้หรือคุณอาจแก้ไขให้ถูกต้องได้บ้าง?
Paul Ruane

61

HashMapไม่มีไม่ได้เช่นเดียวกับ โดยพื้นฐานแล้วคุณต้องการรหัสHashMapจากกุญแจสู่ชุดของค่า

หากคุณมีความสุขในการใช้ห้องสมุดภายนอก, ฝรั่งมีตรงแนวคิดนี้Multimapกับการใช้งานเช่นและArrayListMultimapHashMultimap


@ จอนคุณสามารถให้ witha ตัวอย่างการทำงานใน Java สำหรับคำถามข้างต้นที่ถามโดย OP.Highly ชื่นชมถ้าคุณสามารถโพสต์
Deepak

2
@Deepak: ค้นหาตัวอย่าง guava multimap และคุณจะพบโค้ดตัวอย่าง
Jon Skeet

1
@Deepak: โดยทั่วไปคุณจะสร้างบางสิ่งบางอย่างเช่นArrayListMultimapตัวคุณเอง ... หรือเพียงแค่ใช้HashMap<String, List<Integer>>หรืออะไรก็ตาม คุณต้องสร้างรายการเปล่าทุกครั้งที่มีการเพิ่มมูลค่าเป็นครั้งแรกโดยทั่วไป
Jon Skeet

1
คุณมีตัวอย่างการทำงานสำหรับHashMap<String, List<Integer>>
Deepak

9
@Deepak: ฉันขอแนะนำให้คุณลองสร้างตัวอย่างด้วยตัวเองและหากคุณติดขัดให้ถามคำถามรวมถึงรหัสเท่าที่คุณมี คุณจะได้เรียนรู้มากขึ้นอย่างนั้น
Jon Skeet

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

ลองดูที่Multimapห้องสมุดฝรั่งและการนำไปใช้ -HashMultimap

คอลเลกชันที่คล้ายกับแผนที่ แต่อาจเชื่อมโยงค่าหลายค่ากับคีย์เดียว หากคุณเรียกใช้ put (K, V) สองครั้งโดยใช้คีย์เดียวกัน แต่มีค่าต่างกัน Multimap จะมีการแม็พจากคีย์เป็นทั้งสองค่า


7

ฉันใช้Map<KeyType, Object[]>สำหรับเชื่อมโยงค่าหลายค่ากับคีย์ในแผนที่ ด้วยวิธีนี้ฉันสามารถเก็บค่าหลายประเภทต่าง ๆ ที่เกี่ยวข้องกับคีย์ คุณต้องดูแลโดยรักษาลำดับที่เหมาะสมของการแทรกและดึงจาก 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

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

ถ้าคุณใช้ฤดูใบไม้ผลิกรอบ มี: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 collection:

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

ใช่มันใช้งานได้ แต่มันกำลังแสดงข้อมูลใน [] formate ฉันต้องการไอเท็มเหล่านั้นทีละตัวเพื่อรับ pls กรุณาติดอยู่ที่นี่
Sunil Chaudhary

0

ฉันไม่สามารถโพสต์ตอบกลับความคิดเห็นของ Paul ดังนั้นฉันกำลังสร้างความคิดเห็นใหม่สำหรับ Vidhya ที่นี่:

เสื้อคลุมจะเป็น SuperClassสองคลาสที่เราต้องการเก็บเป็นค่า

และในคลาส wrapper เราสามารถใส่ความสัมพันธ์เป็นวัตถุตัวแปรอินสแตนซ์สำหรับวัตถุคลาสสองคลาส

เช่น

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 ภายใต้เงื่อนไขที่การเปรียบเทียบคีย์จะกระทำโดย == โอเปอเรเตอร์และไม่เท่ากับ ()


0

ฉันชอบสิ่งต่อไปนี้เพื่อเก็บตัวแปรจำนวนเท่าใดก็ได้โดยไม่ต้องสร้างคลาสแยกต่างหาก

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

0

ฉันคุ้นเคยกับการทำเช่นนี้กับ Data Dictionary ใน Objective C มันยากที่จะได้รับผลลัพธ์ที่คล้ายกันใน Java สำหรับ Android ฉันลงเอยด้วยการสร้างคลาสที่กำหนดเองจากนั้นเพียงทำ hashmap ของคลาสที่กำหนดเองของฉัน

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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.