Điều gì xảy ra khi một khóa trùng lặp được đưa vào HashMap?


275

Nếu tôi vượt qua cùng một chìa khóa nhiều lần để HashMap's putphương pháp, những gì xảy ra với giá trị ban đầu? Và nếu giá trị lặp lại thì sao? Tôi đã không tìm thấy bất kỳ tài liệu về điều này.

Trường hợp 1: Ghi đè các giá trị cho khóa

Map mymap = new HashMap();
mymap.put("1","one");
mymap.put("1","not one");
mymap.put("1","surely not one");
System.out.println(mymap.get("1"));

Chúng tôi nhận được surely not one.

Trường hợp 2: Giá trị trùng lặp

Map mymap = new HashMap();
mymap.put("1","one");
mymap.put("1","not one");
mymap.put("1","surely not one");
// The following line was added:
mymap.put("1","one");
System.out.println(mymap.get("1"));

Chúng tôi nhận được one.

Nhưng điều gì xảy ra với các giá trị khác? Tôi đang dạy cơ bản cho một sinh viên và tôi đã được hỏi điều này. Là Mapgiống như một cái xô trong đó giá trị cuối cùng được tham chiếu (nhưng trong bộ nhớ)?


7
BTW, đây là một cơ hội tuyệt vời để thể hiện đa bản đồ là một phần của các lớp bộ sưu tập Jakarta ( commons.apache.org/collections ). Nó sẽ cho phép bạn có bất kỳ số lượng giá trị nào được liên kết với cùng một khóa cho những lúc bạn cần.
John Munsch

Câu trả lời:


302

Theo định nghĩa, putlệnh thay thế giá trị trước đó được liên kết với khóa đã cho trong bản đồ (về mặt khái niệm giống như một hoạt động lập chỉ mục mảng cho các kiểu nguyên thủy).

Bản đồ chỉ đơn giản là giảm tham chiếu của nó đến giá trị. Nếu không có gì khác giữ tham chiếu đến đối tượng, đối tượng đó sẽ đủ điều kiện để thu gom rác. Ngoài ra, Java trả về bất kỳ giá trị nào trước đó được liên kết với khóa đã cho (hoặc nullnếu không có), vì vậy bạn có thể xác định những gì đã có và duy trì tham chiếu nếu cần.

Thêm thông tin ở đây: HashMap Doc


Cảm ơn vì điều đó. Đọc mặc dù tài liệu Java này không được đề cập rõ ràng. Tôi đoán tác giả của tài liệu cho rằng đây là một giả định ngầm của tất cả các triển khai bản đồ băm.
Andrew S

76

Bạn có thể tìm thấy câu trả lời của mình trong javadoc of Map # put (K, V) (thực sự trả về một cái gì đó):

public V put(K key,
             V value)

Liên kết giá trị được chỉ định với khóa được chỉ định trong bản đồ này (thao tác tùy chọn). Nếu bản đồ trước đây chứa ánh xạ cho khóa này, giá trị cũ được thay thế bằng giá trị được chỉ định. (Một bản đồ mđược cho là chứa ánh xạ cho khóa knếu và chỉ khi m.containsKey(k)trở về true.)

Tham số:
key - khóa mà giá trị được chỉ định sẽ được liên kết.
value- giá trị được liên kết với khóa được chỉ định.

Trả về:
giá trị trước đó được liên kết với khóa được chỉ định hoặc nullnếu không có ánh xạ cho key. (Trả nullvề cũng có thể chỉ ra rằng bản đồ trước đây được liên kết nullvới chỉ định key, nếu việc triển khai hỗ trợ nullcác giá trị.)

Vì vậy, nếu bạn không chỉ định giá trị được trả về khi gọi mymap.put("1", "a string"), nó sẽ trở nên không được ước tính và do đó đủ điều kiện để thu gom rác.


3
Các giá trị trả vềgiá trị trước đó (hoặc null) như tài liệu ngay trên trong javadoc như vậy, vâng, đây là những gì tôi có ý nghĩa. Nó thực sự có thể được giải thích sai?
Pascal Thivent

Điều này rất hữu ích.
roottraveller

18

Giá trị trước cho khóa được loại bỏ và thay thế bằng giá trị mới.

Nếu bạn muốn giữ tất cả các giá trị mà một khóa được đưa ra, bạn có thể xem xét triển khai một cái gì đó như thế này:

import org.apache.commons.collections.MultiHashMap;
import java.util.Set;
import java.util.Map;
import java.util.Iterator;
import java.util.List;
public class MultiMapExample {

   public static void main(String[] args) {
      MultiHashMap mp=new MultiHashMap();
      mp.put("a", 10);
      mp.put("a", 11);
      mp.put("a", 12);
      mp.put("b", 13);
      mp.put("c", 14);
      mp.put("e", 15);
      List list = null;

      Set set = mp.entrySet();
      Iterator i = set.iterator();
      while(i.hasNext()) {
         Map.Entry me = (Map.Entry)i.next();
         list=(List)mp.get(me.getKey());

         for(int j=0;j<list.size();j++)
         {
          System.out.println(me.getKey()+": value :"+list.get(j));
         }
      }
   }
}

Giải pháp này được giải thích. MultiHashMap là một phần của apache.commons.collections chứ không phải java.
wikimix

17

đó là tính năng Khóa / Giá trị và bạn không thể có khóa trùng lặp cho một số giá trị vì khi bạn muốn nhận giá trị thực, một trong các giá trị thuộc về khóa đã nhập
trong ví dụ của bạn khi bạn muốn nhận giá trị là "1" nó?!
đó là lý do để có khóa duy nhất cho mọi giá trị nhưng bạn có thể có một mẹo theo java tiêu chuẩn lib:

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

public class DuplicateMap<K, V> {

    private Map<K, ArrayList<V>> m = new HashMap<>();

    public void put(K k, V v) {
        if (m.containsKey(k)) {
            m.get(k).add(v);
        } else {
            ArrayList<V> arr = new ArrayList<>();
            arr.add(v);
            m.put(k, arr);
        }
    }

     public ArrayList<V> get(K k) {
        return m.get(k);
    }

    public V get(K k, int index) {
        return m.get(k).size()-1 < index ? null : m.get(k).get(index);
    }
}


và bạn có thể sử dụng nó theo cách này:

    public static void main(String[] args) {
    DuplicateMap<String,String> dm=new DuplicateMap<>();
    dm.put("1", "one");
    dm.put("1", "not one");
    dm.put("1", "surely not one");
    System.out.println(dm.get("1"));
    System.out.println(dm.get("1",1));
    System.out.println(dm.get("1", 5));
}

và kết quả của bản in là:

[one, not one, surely not one]
not one
null

Câu trả lời chính xác! làm tốt lắm. Bạn thực sự tiết kiệm cuộc sống lập trình của tôi :).
Subin Babu

Cảm ơn tôi cũng vậy! Tôi đã phải thêm một phương thức "loại bỏ" để thực hiện chức năng tương tự như một Bản đồ bình thường nhưng hoạt động rất tốt!
JGlass

1
@Jlass kính chào bạn, nhưng đây không phải là giải pháp kỹ thuật, đó là những gì bạn có thể làm theo java tiêu chuẩn lib, trong vấn đề kỹ thuật bạn phải theo dõi vấn đề của mình, nếu bạn cần có hành vi này tôi chắc chắn đó không phải là giải pháp vì về khái niệm Khóa / Giá trị, và phải suy nghĩ về vấn đề và tìm cách hợp lý để giải quyết. dù sao chi tiết của tôi chỉ là cách thú vị để làm với java và trong sản xuất, các vấn đề và con đường giải quyết rất khác với công việc thú vị! nhưng bạn có thể sử dụng nó khi hành vi Khóa / Giá trị không phải là vấn đề của bạn và tìm cách có cấu trúc dữ liệu như thế.
java acm

13

Liên kết giá trị được chỉ định với khóa được chỉ định trong bản đồ này. Nếu bản đồ trước đây chứa ánh xạ cho khóa, giá trị cũ sẽ được thay thế.


12

thay thế giá trị hiện có trong bản đồ cho khóa tương ứng. Và nếu không có khóa nào tồn tại cùng tên thì nó sẽ tạo một khóa có giá trị được cung cấp. ví dụ:

Map mymap = new HashMap();
mymap.put("1","one");
mymap.put("1","two");

Khóa OUTPUT = "1", value = "hai"

Vì vậy, giá trị trước đó được ghi đè.


4

Đối với câu hỏi của bạn cho dù bản đồ giống như một cái xô: không.

Nó giống như một danh sách với name=valuecác cặp trong khi namekhông cần phải là Chuỗi (mặc dù vậy nó có thể).

Để có được một phần tử, bạn chuyển khóa của bạn cho phương thức get () - trả lại cho bạn đối tượng được gán.

Và bản đồ Hash có nghĩa là nếu bạn đang cố truy xuất đối tượng của mình bằng phương thức get, nó sẽ không so sánh đối tượng thực với đối tượng bạn cung cấp, bởi vì nó sẽ cần lặp lại qua danh sách của nó và so sánh () khóa bạn đã cung cấp với các yếu tố hiện tại.

Điều này sẽ không hiệu quả. Thay vào đó, bất kể đối tượng của bạn bao gồm những gì, nó sẽ tính toán một mã băm được gọi là từ cả hai đối tượng và so sánh chúng. Dễ dàng so sánh hai ints thay vì hai đối tượng (có thể rất phức tạp). Bạn có thể tưởng tượng mã băm giống như một bản tóm tắt có độ dài được xác định trước (int), do đó nó không phải là duy nhất và có va chạm. Bạn tìm các quy tắc cho mã băm trong tài liệu mà tôi đã chèn liên kết.

Nếu bạn muốn biết thêm về điều này, bạn có thể muốn xem các bài viết trên javapractices.comtechnofundo.com

Trân trọng


3

Tôi luôn luôn sử dụng:

HashMap<String, ArrayList<String>> hashy = new HashMap<String, ArrayList<String>>();

nếu tôi muốn áp dụng nhiều thứ cho một khóa xác định.

public void MultiHash(){
    HashMap<String, ArrayList<String>> hashy = new HashMap<String, ArrayList<String>>();
    String key = "Your key";

    ArrayList<String> yourarraylist = hashy.get(key);

    for(String valuessaved2key : yourarraylist){
        System.out.println(valuessaved2key);
    }

}

bạn luôn có thể làm một cái gì đó như thế này và tạo cho mình một mê cung!

public void LOOK_AT_ALL_THESE_HASHMAPS(){
    HashMap<String, HashMap<String, HashMap<String, HashMap<String, String>>>> theultimatehashmap = new HashMap <String, HashMap<String, HashMap<String, HashMap<String, String>>>>();
    String ballsdeep_into_the_hashmap = theultimatehashmap.get("firststring").get("secondstring").get("thirdstring").get("forthstring");
}

2

Bản đồ từ JDK không có nghĩa là để lưu trữ dữ liệu dưới các khóa trùng lặp.

  • Tốt nhất giá trị mới sẽ ghi đè lên những cái trước đó.

  • Kịch bản tệ hơn là ngoại lệ (ví dụ: khi bạn cố gắng thu thập nó dưới dạng luồng):

Không trùng lặp:

Stream.of("one").collect(Collectors.toMap(x -> x, x -> x))

Đồng ý. Bạn sẽ nhận được: $ 2 ==> {one = one}

Luồng trùng lặp:

Stream.of("one", "not one", "surely not one").collect(Collectors.toMap(x -> 1, x -> x))

Ngoại lệ java.lang.IllegalStateException: Khóa trùng lặp 1 (đã cố gắng hợp nhất các giá trị một và không phải một) | tại Collector.d repeatateKeyException (Collector.java:133) | tại Collector.lambda $ uniqKeysMapAccumulator $ 1 (Collector.java:180) | tại GiảmOps $ 3ReducingSink.accept (GiảmOps.java:169) | tại Spliterators $ ArraySpliterator.forEachRemained (Spliterators.java:948) | tại AbstractPipeline.copyInto (Tóm tắtPipeline.java:484) | tại AbstractPipeline.wrapAndCopyInto (Tóm tắtPipeline.java:474) | tại GiảmOps $ GiảmOp.evaluSequential (GiảmOps.java:913) | tại AbstractPipeline.evalu (AbstractPipeline.java:234) | tại ReferencePipeline.collect (ReferencePipeline.java giáp78) | tại (# 4: 1)

Để xử lý các khóa trùng lặp - sử dụng gói khác, ví dụ: https://google.github.io/guava/release/19.0/api/docs/com/google/common/collect/Multimap.html

Có rất nhiều triển khai khác liên quan đến các khóa trùng lặp. Những thứ này là cần thiết cho web (ví dụ: khóa cookie trùng lặp, tiêu đề http có thể có cùng các trường, ...)

Chúc may mắn! :)


hoạt động "ghi đè" có tốn kém không?
gaurav

Nó chỉ có thể được giải quyết bằng cách sử dụng JDK. Collectors.toMap()có đối số thứ ba - hàm hợp nhất. Nếu chúng ta muốn ghi đè phần tử trùng lặp cuối cùng : Stream.of("one", "two", "one").collect(Collectors.toMap(x -> x, x -> x, (key1, key2) -> key2)). liên kết
độc lập

Ngoài ra ví dụ mã thứ hai của bạn là không chính xác. Đầu vào này: "one", "not one", "surely not one"sẽ không tạo ra bất kỳ lỗi khóa trùng lặp nào vì tất cả các chuỗi khác nhau.
độc lập

Xin chào một mình. Vui lòng đọc kỹ chức năng ánh xạ (toMap).
Witold Kaczurba

Xin chào @WitoldKaczurba. Vui lòng biên dịch mã của bạn trước khi đăng.
độc lập


1

Có, điều này có nghĩa là tất cả 1 khóa có giá trị được ghi đè bằng giá trị được thêm vào cuối cùng và ở đây bạn thêm "chắc chắn không phải là một" vì vậy nó sẽ chỉ hiển thị "chắc chắn không phải là một".

Ngay cả khi bạn đang cố gắng hiển thị với một vòng lặp, nó cũng sẽ chỉ hiển thị một khóa và giá trị có cùng khóa.


0
         HashMap<Emp, Emp> empHashMap = new HashMap<Emp, Emp>();

         empHashMap.put(new Emp(1), new Emp(1));
         empHashMap.put(new Emp(1), new Emp(1));
         empHashMap.put(new Emp(1), new Emp());
         empHashMap.put(new Emp(1), new Emp());
         System.out.println(empHashMap.size());
    }
}

class Emp{
    public Emp(){   
    }
    public Emp(int id){
        this.id = id;
    }
    public int id;
    @Override
    public boolean equals(Object obj) {
        return this.id == ((Emp)obj).id;
    }

    @Override
    public int hashCode() {
        return id;
    }
}


OUTPUT : is 1

Có nghĩa là bản đồ băm sẽ không cho phép trùng lặp, nếu bạn đã ghi đè đúng phương thức bằng và phương thức hashCode ().

Hashset cũng sử dụng HashMap trong nội bộ, xem tài liệu nguồn

public class HashSet{
public HashSet() {
        map = new HashMap<>();
    }
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.