중복 키로 구현 매핑


116

중복 키가있는지도를 갖고 싶습니다.

많은지도 구현이 있다는 것을 알고 있습니다 (Eclipse는 약 50 개를 보여줍니다). 그래서 이것을 허용하는 것이 있어야합니다. 이 작업을 수행하는 자신의지도를 작성하는 것이 쉽다는 것을 알고 있지만 기존 솔루션을 사용하고 싶습니다.

커먼즈 컬렉션이나 구글 컬렉션에있는 것일까 요?


4
어떻게 작동해야합니까? 키와 관련된 값을 요청하고이 키가 맵에 여러 번 존재하는 경우 어떤 값을 반환해야합니까?
Mnementh 2009-06-30

get 예외가 발생할 수 있습니다.이 맵은 반복에만 필요합니다.
IAdapter 2009-06-30

6
반복에만 필요한 경우, 애초에 맵이 필요한 이유는 무엇입니까? 쌍 또는 무언가의 목록을 사용하십시오 ...
Tal Pressman 2009-06-30

2
내 전체 프로그램이 이미 Map과 함께 작동하기 때문에 데이터가 중복 키를 가질 수 있음을 발견했습니다. Map을 다른 방식으로 사용하는 것이 너무 잘못 되었다면 50 개 이상이 아닌 5 개의 Map 구현 만있을 것입니다.
IAdapter 2009-06-30

답변:


90

당신은 멀티 맵을 찾고 있으며, 실제로 commons-collections와 Guava는 여러 가지 구현을 가지고 있습니다. 멀티 맵은 키당 값 컬렉션을 유지하여 여러 키를 허용합니다. 즉, 단일 객체를 맵에 넣을 수 있지만 컬렉션을 검색합니다.

Java 5를 사용할 수 있다면 Multimap제네릭을 인식 하는 Guava를 선호합니다 .


3
또한이 멀티 맵은 아파치가하는 것처럼 맵인 척하지 않습니다.
Kevin Bourrillion 2009

7
구글 컬렉션은 구아바로 대체 된 것을 참고, 그래서 여기 Multimap과의 구아바 버전에 대한 링크입니다 : code.google.com/p/guava-libraries/wiki/...
조쉬 글로버에게

그러나 Multimap과 완전히 직렬화 할 수없는, 쓸모 직렬화 된 인스턴스를 렌더링 과도 회원이
dschulten

@dschulten 음, Multimap은 인터페이스이며 의미하는 구현을 지정하지 않습니다. ArrayListMultimap 및 Immutable {List, Set} Multimap과 마찬가지로 / 메서드 com.google.common.collect.HashMultimap가 있습니다. 쓸모없는 deserialized 인스턴스는보고 할 가치가있는 버그라고 생각합니다. readObjectwriteObject
nd.

1
아파치 컬렉션 4.0 지원 제네릭 commons.apache.org/proper/commons-collections/javadocs/...
kervin

35

Google 컬렉션 외부 라이브러리에 의존 할 필요가 없습니다. 다음 맵을 간단히 구현할 수 있습니다.

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

코드를 미세 조정하십시오.


14
물론 Guava의 Multimap에 의존 할 필요는 없습니다. 당신이 등 시험 그들, 그들을 다시 구현하지 않는 한 그것은 단지, 당신의 인생을 용이하게
PhiLho

이것은 모든 쌍에 대해 원활한 반복을 허용하지 않습니다. 확실히 더 많은 단점이 있습니다. 나는 또한 하나의 추가 클래스가 필요한 내 솔루션을 제안하려고했지만 @Mnementh의 대답은 바로 그 것입니다.
Mark Jeronimus

2
기본 코드를 작성하는 것이 항상 그렇게 영리하지는 않습니다. 구글은 가능성이 더 나은 테스트하는 것입니다
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));

출력은 다음과 같습니다.

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

참고 : 라이브러리 파일을 가져와야합니다.

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

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

또는 https://commons.apache.org/proper/commons-collections/download_collections.cgi

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

2
좋은 제안, 내 프로젝트에서 Spring을 사용하고 있기 때문에 문서 http://docs.spring.io/spring-framework/docs/current/javadoc-api/org/
ajup

18

일반 HashMap의 값에 대한 값 배열을 전달하여 중복 키를 시뮬레이션 할 수 있으며 사용할 데이터를 결정하는 것은 사용자에게 달려 있습니다.

중복 키에 대한 아이디어가 마음에 들지 않지만 MultiMap을 사용할 수도 있습니다 .


감사합니다! 사용하여 TreeMap<String, ArrayList<MyClass>>중복 키 요구 사항을 해결했습니다.
Joe

10

(주석에 쓴 것처럼) 키-값 쌍 목록에 대해 반복하려면 목록 또는 배열이 더 좋습니다. 먼저 키와 값을 결합하십시오.

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

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

}

Class1 및 Class2를 키 및 값에 사용할 유형으로 바꿉니다.

이제 배열이나 목록에 넣고 반복 할 수 있습니다.

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

add () 또는 put ()을 어떻게 구현합니까? 하드 코어 차원 수를 원하지 않습니다.
Amit Kumar Gupta

2
이 경우 목록을 사용하십시오. 두 번째 샘플은 List <Pair> 쌍으로 변경됩니다. = new List <Pair> (); for 루프는 동일하게 유지됩니다. 다음 명령을 사용하여 쌍을 추가 할 수 있습니다. pairs.add (pair);
Mnementh

이것은 아마도 정직한 최선의 대답 일 것입니다.
PaulBGD

6

이 문제는 맵 항목 목록으로 해결할 수 있습니다 List<Map.Entry<K,V>>. 외부 라이브러리 나 새로운 Map 구현을 사용할 필요가 없습니다. 다음과 같이 맵 항목을 만들 수 있습니다. Map.Entry<String, Integer> entry = new AbstractMap.SimpleEntry<String, Integer>("key", 1);



3

내 실수로부터 배우십시오 ... 이것을 스스로 구현하지 마십시오. 구아바 멀티 맵은 갈 길입니다.

멀티 맵에 필요한 일반적인 개선 사항은 중복 키-값 쌍을 허용하지 않는 것입니다.

구현에서 이것을 구현 / 변경하는 것은 성 가실 수 있습니다.

구아바에서는 다음과 같이 간단합니다.

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

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

2

이 문제에 대해 약간 다른 변형이있었습니다. 두 개의 다른 값을 동일한 키에 연결해야했습니다. 다른 사람들에게 도움이 될 수 있도록 여기에 게시하기 만하면 HashMap을 값으로 도입했습니다.

/* @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 );
        }
}

위의 코드에서 key frameID는 각 줄에있는 입력 파일의 첫 번째 문자열에서 읽히고, frameTypeHash의 값은 나머지 줄을 분할하여 구성되며 파일이 여러 줄로 시작된 기간 동안 원래 String 개체로 저장되었습니다. 다른 값으로) 동일한 frameID 키와 연결되어 있으므로 frameTypeHash는 값으로 마지막 줄을 덮어 씁니다. String 개체를 값 필드로 다른 HashMap 개체로 대체하여 다른 값 매핑에 대한 단일 키를 유지하는 데 도움이되었습니다.


2

멋진 라이브러리가 필요하지 않습니다. 맵은 고유 키로 정의되므로 구부리지 말고 목록을 사용하십시오. 스트림은 강력합니다.

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"),
);

그리고 그게 다야. 사용 예 :

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

@daspilker 감사합니다. 지금은 편집 내용 만보고 있습니다. 누군가가 내 스 니펫이 편집되면 가치가 있음을 찾는 것을 봅니다.
Gabrial David

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

이 장황한 솔루션에는 여러 가지 단점이 있으며 오류가 발생하기 쉽습니다. 모든 값에 대해 컬렉션을 인스턴스화하고, 값을 추가하거나 제거하기 전에 그 존재를 확인하고, 값이 남아 있지 않을 때 수동으로 삭제하는 등의 작업이 필요함을 의미합니다.

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

자바 맵 중복 키


1

그런 MultiMap impl은 어떻습니까?

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

중복 키를 사용하여 맵을 구현하려는 컨텍스트를 설명해 주시겠습니까? 더 나은 해결책이있을 수 있다고 확신합니다. 지도는 정당한 이유로 고유 키를 유지하기위한 것입니다. 정말로하고 싶다면; 충돌 완화 기능이 있고 동일한 키로 여러 항목을 유지할 수있는 간단한 사용자 지정 맵 클래스를 작성하여 항상 클래스를 확장 할 수 있습니다.

참고 : 충돌 키가 "항상"고유 한 집합으로 변환되도록 충돌 완화 기능을 구현해야합니다. 객체 해시 코드 등으로 키를 추가하는 것과 같은 간단한 것입니까?


1
컨텍스트는 키가 고유 할 것이라고 생각했지만 그렇지 않을 수도 있다는 것이 밝혀졌습니다. 자주 사용되지 않기 때문에 모든 것을 리팩토링하고 싶지 않습니다.
IAdapter 2009-06-30

작은 XML 파일을 데이터 유형과 같은 해시 맵으로 변환하고 싶습니다. 문제는 XML 파일의 구조가 수정되지 않았다는 것입니다
Amit Kumar Gupta

0

완료하기 위해 Apache Commons Collections에는 MultiMap도 있습니다. 물론 단점은 Apache Commons가 Generics를 사용하지 않는다는 것입니다.


1
MultiMap은 Map을 구현하지만 Map 메서드의 계약을 위반합니다. 그것은 나를 괴롭힌다.
Kevin Bourrillion 2009

0

약간의 해킹으로 HashSet을 중복 키로 사용할 수 있습니다. 경고 : 이것은 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

중복 키가있는 경우 키는 둘 이상의 값에 해당 할 수 있습니다. 확실한 해결책은 이러한 값 목록에 키를 매핑하는 것입니다.

예를 들어 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

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.