Triển khai bản đồ với các khóa trùng lặp


116

Tôi muốn có một bản đồ với các khóa trùng lặp.

Tôi biết có rất nhiều triển khai bản đồ (Eclipse cho tôi thấy khoảng 50), vì vậy tôi cá là phải có một bản đồ cho phép điều này. Tôi biết thật dễ dàng để viết bản đồ của riêng bạn thực hiện điều này, nhưng tôi muốn sử dụng một số giải pháp hiện có.

Có thể một cái gì đó trong bộ sưu tập chung hoặc bộ sưu tập google?


4
Làm thế nào nên làm việc này? Nếu bạn yêu cầu một giá trị được liên kết với một khóa và khóa này tồn tại nhiều lần trong bản đồ, thì giá trị nào sẽ được trả về?
Mnementh

get có thể ném ngoại lệ, tôi chỉ cần bản đồ này để lặp lại.
IAdapter

6
Nếu bạn chỉ cần nó để lặp lại, tại sao bạn cần một bản đồ ngay từ đầu? Sử dụng một danh sách các cặp hoặc một cái gì đó ...
Tal Pressman

2
Bởi vì toàn bộ chương trình của tôi đã hoạt động với Bản đồ và bây giờ tôi phát hiện ra rằng dữ liệu có thể có các khóa trùng lặp. Nếu sử dụng Bản đồ theo cách khác sẽ rất sai lầm, chúng tôi sẽ chỉ có 5 cách triển khai Bản đồ chứ không phải 50+.
IAdapter

Câu trả lời:


90

Bạn đang tìm kiếm một multimap và thực sự là cả commons-collection và Guava đều có một số cách triển khai cho điều đó. Đa bản đồ cho phép nhiều khóa bằng cách duy trì một tập hợp các giá trị trên mỗi khóa, tức là bạn có thể đưa một đối tượng vào bản đồ, nhưng bạn truy xuất một tập hợp.

Nếu bạn có thể sử dụng Java 5, tôi sẽ thích Guava hơn Multimapvì nó có nhận thức chung.


3
Ngoài ra, Multimap này không giả vờ là một Bản đồ như cách apache làm.
Kevin Bourrillion

7
Lưu ý rằng Google Bộ sưu tập đã được thay thế bởi ổi, vì vậy đây là các liên kết đến các phiên bản ổi của Multimap: code.google.com/p/guava-libraries/wiki/...
Josh Glover

Tuy nhiên, Multimap không hoàn toàn serializable, nó có thành viên thoáng qua mà làm cho một trường hợp deserialized vô dụng
dschulten

@dschulten Chà, Multimap là một giao diện và bạn không chỉ định cách triển khai của bạn. com.google.common.collect.HashMultimaphas readObject/ writeObjectmethod, cũng như ArrayListMultimap và Immutable {List, Set} Multimap. Tôi sẽ coi một phiên bản deserialized vô dụng là một lỗi đáng báo cáo.
nd.

1
Apache Collections 4.0 hỗ trợ chung commons.apache.org/proper/commons-collections/javadocs/…
kervin

35

Chúng tôi không cần phụ thuộc vào thư viện bên ngoài Bộ sưu tập của Google. Bạn chỉ cần triển khai Bản đồ sau:

Map<String, ArrayList<String>> hashMap = new HashMap<String, ArrayList>();

public static void main(String... arg) {
   // Add data with duplicate keys
   addValues("A", "a1");
   addValues("A", "a2");
   addValues("B", "b");
   // View data.
   Iterator it = hashMap.keySet().iterator();
   ArrayList tempList = null;

   while (it.hasNext()) {
      String key = it.next().toString();             
      tempList = hashMap.get(key);
      if (tempList != null) {
         for (String value: tempList) {
            System.out.println("Key : "+key+ " , Value : "+value);
         }
      }
   }
}

private void addValues(String key, String value) {
   ArrayList tempList = null;
   if (hashMap.containsKey(key)) {
      tempList = hashMap.get(key);
      if(tempList == null)
         tempList = new ArrayList();
      tempList.add(value);  
   } else {
      tempList = new ArrayList();
      tempList.add(value);               
   }
   hashMap.put(key,tempList);
}

Vui lòng đảm bảo tinh chỉnh mã.


14
Tất nhiên, bạn không cần phải dựa vào Multimap của Guava. Nó chỉ giúp giảm bớt cuộc sống của bạn, như bạn không cần phải tái thực hiện chúng, kiểm tra họ, vv
PhiLho

Điều này không cho phép lặp lại liền mạch trên tất cả các cặp. Chắc chắn còn nhiều thiếu sót. Tôi đã định đề xuất giải pháp của mình mà cũng yêu cầu một lớp học thêm, thì thấy câu trả lời của @ Mnementh chỉ có vậy.
Mark Jeronimus

2
viết mã cơ bản không phải lúc nào cũng thông minh. Google có nhiều khả năng sẽ có các bài kiểm tra tốt hơn
senseiwu

26
Multimap<Integer, String> multimap = ArrayListMultimap.create();

multimap.put(1, "A");
multimap.put(1, "B");
multimap.put(1, "C");
multimap.put(1, "A");

multimap.put(2, "A");
multimap.put(2, "B");
multimap.put(2, "C");

multimap.put(3, "A");

System.out.println(multimap.get(1));
System.out.println(multimap.get(2));       
System.out.println(multimap.get(3));

Đầu ra là:

[A,B,C,A]
[A,B,C]
[A]

Lưu ý: chúng ta cần nhập các tệp thư viện.

http://www.java2s.com/Code/Jar/g/Downloadgooglecollectionsjar.htm

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

hoặc https://commons.apache.org/proper/commons-collections/download_collections.cgi

import org.apache.commons.collections.MultiMap;
import org.apache.commons.collections.map.MultiValueMap;

2
Đề xuất tốt, vì tôi đang sử dụng Spring trong dự án của mình, tôi đã kết thúc bằng cách sử dụng hương vị Spring của MultiValueMap như được đề cập trong tài liệu http://docs.spring.io/spring-framework/docs/current/javadoc-api/org/ springframework / use / MultiValueMap.html
ajup

18

Bạn có thể chỉ cần chuyển một mảng giá trị cho giá trị trong HashMap thông thường, do đó mô phỏng các khóa trùng lặp và tùy thuộc vào bạn để quyết định dữ liệu nào sẽ sử dụng.

Bạn cũng có thể chỉ sử dụng MultiMap , mặc dù bản thân tôi không thích ý tưởng về các khóa trùng lặp.


Cảm ơn bạn! Sử dụng đã TreeMap<String, ArrayList<MyClass>>giải quyết được nhu cầu chính trùng lặp của tôi.
Joe

10

Nếu bạn muốn lặp lại danh sách các cặp khóa-giá trị (như bạn đã viết trong nhận xét), thì một Danh sách hoặc một mảng sẽ tốt hơn. Trước tiên hãy kết hợp các khóa và giá trị của bạn:

public class Pair
{
   public Class1 key;
   public Class2 value;

   public Pair(Class1 key, Class2 value)
   {
      this.key = key;
      this.value = value;
   }

}

Thay thế Class1 và Class2 bằng các loại bạn muốn sử dụng cho các khóa và giá trị.

Bây giờ bạn có thể đặt chúng vào một mảng hoặc một danh sách và lặp lại chúng:

Pair[] pairs = new Pair[10];
...
for (Pair pair : pairs)
{
   ...
}

Tôi sẽ triển khai add () hoặc put () như thế nào. Tôi không muốn quá nhiều chiều.
Amit Kumar Gupta

2
Trong trường hợp này, hãy sử dụng một Danh sách. Mẫu thứ hai thay đổi thành cặp List <Pair> = new List <Pair> (); Vòng lặp vẫn giữ nguyên. Bạn có thể thêm một cặp bằng lệnh này: pair.add (pair);
Mnementh

Đây có lẽ là câu trả lời tốt nhất để thành thật.
PaulBGD

6

Vấn đề này có thể được giải quyết bằng danh sách mục nhập bản đồ List<Map.Entry<K,V>>. Chúng ta không cần sử dụng các thư viện bên ngoài cũng như việc triển khai Bản đồ mới. Một mục bản đồ có thể được tạo như thế này: Map.Entry<String, Integer> entry = new AbstractMap.SimpleEntry<String, Integer>("key", 1);



3

Học hỏi từ những sai lầm của tôi ... vui lòng không thực hiện điều này của riêng bạn. Guava multimap là cách để đi.

Một tính năng nâng cao phổ biến được yêu cầu trong nhiều bản đồ là không cho phép các cặp khóa-giá trị trùng lặp.

Việc triển khai / thay đổi điều này trong quá trình triển khai của bạn có thể gây khó chịu.

Trong Ổi, nó đơn giản như:

HashMultimap<String, Integer> no_dupe_key_plus_val = HashMultimap.create();

ArrayListMultimap<String, Integer> allow_dupe_key_plus_val = ArrayListMultimap.create();

2

Tôi đã có một biến thể hơi khác của vấn đề này: Bắt buộc phải liên kết hai giá trị khác nhau với cùng một khóa. Chỉ đăng nó ở đây trong trường hợp nó giúp ích cho người khác, tôi đã giới thiệu HashMap như một giá trị:

/* @param frameTypeHash: Key -> Integer (frameID), Value -> HashMap (innerMap)
   @param innerMap: Key -> String (extIP), Value -> String
   If the key exists, retrieve the stored HashMap innerMap 
   and put the constructed key, value pair
*/
  if (frameTypeHash.containsKey(frameID)){
            //Key exists, add the key/value to innerHashMap
            HashMap innerMap = (HashMap)frameTypeHash.get(frameID);
            innerMap.put(extIP, connName+":"+frameType+":"+interfaceName);

        } else {
            HashMap<String, String> innerMap = new HashMap<String, String>();
            innerMap.put(extIP, connName+":"+frameType+":"+interfaceName);
            // This means the key doesn't exists, adding it for the first time
            frameTypeHash.put(frameID, innerMap );
        }
}

Trong đoạn mã trên, key frameID được đọc từ chuỗi đầu tiên của tệp đầu vào trong mỗi dòng, giá trị cho frameTypeHash được xây dựng bằng cách tách dòng còn lại và được lưu trữ dưới dạng đối tượng Chuỗi ban đầu, trong một khoảng thời gian tệp bắt đầu có nhiều dòng ( với các giá trị khác nhau) được liên kết với cùng một khóa frameID, vì vậy frameTypeHash đã được ghi đè bằng dòng cuối cùng làm giá trị. Tôi đã thay thế đối tượng String bằng một đối tượng HashMap khác làm trường giá trị, điều này giúp duy trì một khóa duy nhất để ánh xạ giá trị khác nhau.


2

Không yêu cầu lib ưa thích. Bản đồ được xác định bởi một khóa duy nhất, vì vậy đừng bẻ cong chúng, hãy sử dụng một danh sách. Những con suối rất hùng vĩ.

import java.util.AbstractMap.SimpleImmutableEntry;

List<SimpleImmutableEntry<String, String>> nameToLocationMap = Arrays.asList(
    new SimpleImmutableEntry<>("A", "A1"),
    new SimpleImmutableEntry<>("A", "A2"),
    new SimpleImmutableEntry<>("B", "B1"),
    new SimpleImmutableEntry<>("B", "B1"),
);

Và đó là nó. Các ví dụ sử dụng:

List<String> allBsLocations = nameToLocationMap.stream()
        .filter(x -> x.getKey().equals("B"))
        .map(x -> x.getValue())
        .collect(Collectors.toList());

nameToLocationMap.stream().forEach(x -> 
do stuff with: x.getKey()...x.getValue()...

1
class  DuplicateMap<K, V> 
{
    enum MapType
    {
        Hash,LinkedHash
    }

    int HashCode = 0;
    Map<Key<K>,V> map = null;

    DuplicateMap()
    {
        map = new HashMap<Key<K>,V>();
    }

    DuplicateMap( MapType maptype )
    {
        if ( maptype == MapType.Hash ) {
            map = new HashMap<Key<K>,V>();
        }
        else if ( maptype == MapType.LinkedHash ) {
            map = new LinkedHashMap<Key<K>,V>();
        }
        else
            map = new HashMap<Key<K>,V>();
    }

    V put( K key, V value  )
    {

        return map.put( new Key<K>( key , HashCode++ ), value );
    }

    void putAll( Map<K, V> map1 )
    {
        Map<Key<K>,V> map2 = new LinkedHashMap<Key<K>,V>();

        for ( Entry<K, V> entry : map1.entrySet() ) {
            map2.put( new Key<K>( entry.getKey() , HashCode++ ), entry.getValue());
        }
        map.putAll(map2);
    }

    Set<Entry<K, V>> entrySet()
    {
        Set<Entry<K, V>> entry = new LinkedHashSet<Map.Entry<K,V>>();
        for ( final Entry<Key<K>, V> entry1 : map.entrySet() ) {
            entry.add( new Entry<K, V>(){
                private K Key = entry1.getKey().Key();
                private V Value = entry1.getValue();

                @Override
                public K getKey() {
                    return Key;
                }

                @Override
                public V getValue() {
                    return Value;
                }

                @Override
                public V setValue(V value) {
                    return null;
                }});
        }

        return entry;
    }

    @Override
    public String toString() {
        StringBuilder builder = new  StringBuilder();
        builder.append("{");
        boolean FirstIteration = true;
        for ( Entry<K, V> entry : entrySet() ) {
            builder.append( ( (FirstIteration)? "" : "," ) + ((entry.getKey()==null) ? null :entry.getKey().toString() ) + "=" + ((entry.getValue()==null) ? null :entry.getValue().toString() )  );
            FirstIteration = false;
        }
        builder.append("}");
        return builder.toString();
    }

    class Key<K1>
    {
        K1 Key;
        int HashCode;

        public Key(K1 key, int hashCode) {
            super();
            Key = key;
            HashCode = hashCode;
        }

        public K1 Key() {
            return Key;
        }

        @Override
        public String toString() {
            return  Key.toString() ;
        }

        @Override
        public int hashCode() {

            return HashCode;
        }
    }

Cảm ơn bạn @daspilker. Tôi chỉ thấy bản chỉnh sửa của bạn. Thật vui khi thấy ai đó thấy đoạn trích của tôi xứng đáng nếu nó được chỉnh sửa.
Gabrial David,

1
 1, Map<String, List<String>> map = new HashMap<>();

giải pháp dài dòng này có nhiều nhược điểm và dễ xảy ra lỗi. Nó ngụ ý rằng chúng ta cần khởi tạo Bộ sưu tập cho mọi giá trị, kiểm tra sự hiện diện của nó trước khi thêm hoặc xóa giá trị, xóa thủ công khi không còn giá trị nào, vân vân.

2, org.apache.commons.collections4.MultiMap interface
3, com.google.common.collect.Multimap interface 

java-bản đồ-bản sao-khóa


1

điều gì về việc tích hợp MultiMap như vậy?

public class MultiMap<K, V> extends HashMap<K, Set<V>> {
  private static final long serialVersionUID = 1L;
  private Map<K, Set<V>> innerMap = new HashMap<>();

  public Set<V> put(K key, V value) {
    Set<V> valuesOld = this.innerMap.get(key);
    HashSet<V> valuesNewTotal = new HashSet<>();
    if (valuesOld != null) {
      valuesNewTotal.addAll(valuesOld);
    }
    valuesNewTotal.add(value);
    this.innerMap.put(key, valuesNewTotal);
    return valuesOld;
  }

  public void putAll(K key, Set<V> values) {
    for (V value : values) {
      put(key, value);
    }
  }

  @Override
  public Set<V> put(K key, Set<V> value) {
    Set<V> valuesOld = this.innerMap.get(key);
    putAll(key, value);
    return valuesOld;
  }

  @Override
  public void putAll(Map<? extends K, ? extends Set<V>> mapOfValues) {
    for (Map.Entry<? extends K, ? extends Set<V>> valueEntry : mapOfValues.entrySet()) {
      K key = valueEntry.getKey();
      Set<V> value = valueEntry.getValue();
      putAll(key, value);
    }
  }

  @Override
  public Set<V> putIfAbsent(K key, Set<V> value) {
    Set<V> valueOld = this.innerMap.get(key);
    if (valueOld == null) {
      putAll(key, value);
    }
    return valueOld;
  }

  @Override
  public Set<V> get(Object key) {
    return this.innerMap.get(key);
  }

  @Override
  etc. etc. override all public methods size(), clear() .....

}

0

Bạn cũng có thể giải thích bối cảnh mà bạn đang cố gắng triển khai bản đồ với các khóa trùng lặp không? Tôi chắc chắn có thể có một giải pháp tốt hơn. Bản đồ được thiết kế để giữ các khóa duy nhất vì lý do chính đáng. Mặc dù nếu bạn thực sự muốn làm điều đó; bạn luôn có thể mở rộng lớp viết một lớp bản đồ tùy chỉnh đơn giản có chức năng giảm thiểu va chạm và sẽ cho phép bạn giữ nhiều mục nhập với cùng một khóa.

Lưu ý: Bạn phải triển khai chức năng giảm thiểu va chạm để các phím va chạm được chuyển đổi thành bộ duy nhất "luôn luôn". Một cái gì đó đơn giản như, thêm khóa với mã băm đối tượng hay gì đó?


1
Bối cảnh là tôi nghĩ rằng các khóa sẽ là duy nhất, nhưng hóa ra có thể không. Tôi không muốn cấu trúc lại mọi thứ vì nó sẽ không được sử dụng thường xuyên.
IAdapter

tôi muốn chuyển đổi một tệp XML nhỏ thành bản đồ băm như kiểu dữ liệu. Chỉ có vấn đề là cấu trúc của tập tin XML không cố định
Amit Kumar Gupta

0

hoàn chỉnh, Apache Commons Collections cũng có MultiMap . Tất nhiên, nhược điểm là Apache Commons không sử dụng Generics.


1
Lưu ý rằng MultiMap của họ triển khai Bản đồ nhưng phá vỡ các hợp đồng của các phương thức Bản đồ. Điều đó làm tôi khó chịu.
Kevin Bourrillion

0

Với một chút hack, bạn có thể sử dụng HashSet với các khóa trùng lặp. CẢNH BÁO: điều này phụ thuộc nhiều vào việc triển khai HashSet.

class MultiKeyPair {
    Object key;
    Object value;

    public MultiKeyPair(Object key, Object value) {
        this.key = key;
        this.value = value;
    }

    @Override
    public int hashCode() {
        return key.hashCode();
    }
}

class MultiKeyList extends MultiKeyPair {
    ArrayList<MultiKeyPair> list = new ArrayList<MultiKeyPair>();

    public MultiKeyList(Object key) {
        super(key, null);
    }

    @Override
    public boolean equals(Object obj) {
        list.add((MultiKeyPair) obj);
        return false;
    }
}

public static void main(String[] args) {
    HashSet<MultiKeyPair> set = new HashSet<MultiKeyPair>();
    set.add(new MultiKeyPair("A","a1"));
    set.add(new MultiKeyPair("A","a2"));
    set.add(new MultiKeyPair("B","b1"));
    set.add(new MultiKeyPair("A","a3"));

    MultiKeyList o = new MultiKeyList("A");
    set.contains(o);

    for (MultiKeyPair pair : o.list) {
        System.out.println(pair.value);
    }
}

0

Nếu có các khóa trùng lặp thì một khóa có thể tương ứng với nhiều hơn một giá trị. Giải pháp rõ ràng là ánh xạ chìa khóa thành danh sách các giá trị này.

Ví dụ trong Python:

map = dict()
map["driver"] = list()
map["driver"].append("john")
map["driver"].append("mike")
print map["driver"]          # It shows john and mike
print map["driver"][0]       # It shows john
print map["driver"][1]       # It shows mike

0

Tôi đã sử dụng cái này:

java.util.List<java.util.Map.Entry<String,Integer>> pairList= new java.util.ArrayList<>();

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.