여러 키를 사용하여 맵을 구현하는 방법
Map처럼 동작하지만 여러 개의 (다른 유형의) 키를 사용하여 값에 액세스할 수 있는 데이터 구조가 필요합니다.
(너무 일반적이지 않게 두 개의 키를 예로 들어 보겠습니다)
키는 고유함을 보증합니다.
예를 들어 다음과 같습니다.
MyMap<K1,K2,V> ...
다음과 같은 방법을 사용합니다.
getByKey1(K1 key)...
getByKey2(K2 key)...
containsKey1(K1 key)...
containsKey2(K2 key)...
제안할 것이 있습니까?
제가 생각할 수 있는 건
내부적으로 두 개의 맵을 사용하는 클래스를 작성합니다.
편집 Java Map의 키로 Tuple이나 Pair 등을 사용할 것을 제안하는 사람도 있습니다만, 저는 이 방법을 사용할 수 없습니다.
위와 같이 지정된 두 키 중 하나로만 값을 검색할 수 있어야 합니다.
맵은 키의 해시 코드를 사용하여 동일한 키를 확인합니다.
하나, 하나.Map<K1, V>
1개의 ★★★★★★★★★★★★★★.Map<K2, V>
단일 인터페이스가 필요한 경우 해당 메서드를 구현하는 래퍼 클래스를 작성합니다.
Commons-Collections는 고객이 원하는 것을 제공합니다.https://commons.apache.org/proper/commons-collections/apidocs/
이제 공용 컬렉션이 입력된 것 같네요.
입력된 버전은 https://github.com/megamattron/collections-generic 에서 구할 수 있습니다.
이것은, 유스케이스를 정확하게 서포트합니다.
MultiKeyMap<k1,k2,...,kn,v> multiMap = ??
저는 여전히 2가지 맵 솔루션을 제안할 것입니다. 하지만 수정사항으로
Map<K2, K1> m2;
Map<K1, V> m1;
이 방식을 사용하면 임의의 수의 키 "에일리어스"를 가질 수 있습니다.
또한 맵이 동기화되지 않고 임의의 키를 통해 값을 업데이트할 수도 있습니다.
또 다른 해결책은 구글의 Guava를 사용하는 것이다.
import com.google.common.collect.Table;
import com.google.common.collect.HashBasedTable;
Table<String, String, Integer> table = HashBasedTable.create();
사용법은 매우 간단합니다.
String row = "a";
String column = "b";
int value = 1;
if (!table.contains(row, column)) {
table.put(row, column, value);
}
System.out.println("value = " + table.get(row, column));
법HashBasedTable.create()
기본적으로 다음과 같은 작업을 수행합니다.
Table<String, String, Integer> table = Tables.newCustomTable(
Maps.<String, Map<String, Integer>>newHashMap(),
new Supplier<Map<String, Integer>>() {
public Map<String, Integer> get() {
return Maps.newHashMap();
}
});
커스텀 맵을 작성하려면 두 번째 옵션(@Karathodory에서 권장하는 바와 같이)을 선택해야 합니다.그렇지 않으면 첫 번째 옵션도 괜찮습니다.
다음의 「키」클래스를 선언해 주세요.
public class Key {
public Object key1, key2, ..., keyN;
public Key(Object key1, Object key2, ..., Object keyN) {
this.key1 = key1;
this.key2 = key2;
...
this.keyN = keyN;
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof Key))
return false;
Key ref = (Key) obj;
return this.key1.equals(ref.key1) &&
this.key2.equals(ref.key2) &&
...
this.keyN.equals(ref.keyN)
}
@Override
public int hashCode() {
return key1.hashCode() ^ key2.hashCode() ^
... ^ keyN.hashCode();
}
}
맵 선언
Map<Key, Double> map = new HashMap<Key,Double>();
키 오브젝트 선언
Key key = new Key(key1, key2, ..., keyN)
지도 채우기
map.put(key, new Double(0))
맵에서 개체 가져오기
Double result = map.get(key);
일부 응답자가 제안한 제안:
public interface IDualMap<K1, K2, V> {
/**
* @return Unmodifiable version of underlying map1
*/
Map<K1, V> getMap1();
/**
* @return Unmodifiable version of underlying map2
*/
Map<K2, V> getMap2();
void put(K1 key1, K2 key2, V value);
}
public final class DualMap<K1, K2, V>
implements IDualMap<K1, K2, V> {
private final Map<K1, V> map1 = new HashMap<K1, V>();
private final Map<K2, V> map2 = new HashMap<K2, V>();
@Override
public Map<K1, V> getMap1() {
return Collections.unmodifiableMap(map1);
}
@Override
public Map<K2, V> getMap2() {
return Collections.unmodifiableMap(map2);
}
@Override
public void put(K1 key1, K2 key2, V value) {
map1.put(key1, value);
map2.put(key2, value);
}
}
키가 특정 유형이어야 한다는 요건을 없애면 됩니다.즉, Map<Object,V>를 사용하면 됩니다.
때로는 제네릭 제품이 추가 작업의 가치가 없습니다.
다음과 같은 것을 추천합니다.
public class MyMap {
Map<Object, V> map = new HashMap<Object, V>();
public V put(K1 key,V value){
return map.put(key, value);
}
public V put(K2 key,V value){
return map.put(key, value);
}
public V get(K1 key){
return map.get(key);
}
public V get(K2 key){
return map.get(key);
}
//Same for conatains
}
그런 다음 다음과 같이 사용할 수 있습니다.
myMap.put(k1,value)
★★★★★★★★★★★★★★★★★」myMap.put(k2,value)
장점:단순하고 유형 안전성을 적용하며 반복된 데이터를 저장하지 않습니다(두 맵 솔루션처럼 중복된 값은 여전히 저장).
결점:범용이 아닙니다.
다음의 어프로치를 확인할 수 있습니다.
a) 2개의 다른 맵을 사용합니다.제안하신 대로 클래스로 포장할 수 있지만, 그마저도 과잉 살상일 수 있습니다.맵을 직접 사용합니다.key1Map.getValue(k1), key2Map.getValue(k2)
b) 타입 인식 키클래스를 생성하여 (테스트되지 않은) 사용할 수 있습니다.
public class Key {
public static enum KeyType { KEY_1, KEY_2 }
public final Object k;
public final KeyType t;
public Key(Object k, KeyType t) {
this.k = k;
this.t= t;
}
public boolean equals(Object obj) {
KeyType kt = (KeyType)obj;
return k.equals(kt.k) && t == kt.t;
}
public int hashCode() {
return k.hashCode() ^ t.hashCode();
}
}
많은 , 런데, of of, 간 of의 이 있습니다.key1
ㅇㅇㅇㅇ의 key2
교차하지 않습니다.그런 경우에는 실제로 특별한 것을 할 필요가 없습니다.엔트리가 만 하면 .key1=>v
만 아니라key2=>v
sol: 두 키를 조합하여 최종 키를 만듭니다.이 키를 키로 사용합니다.
키 값의 경우,
ket-1과 key-2를 bitween에서 come " , "로 연결합니다.이것을 원래 키로서 사용합니다.
키 = 키-1 + "", + 키-2;
myMap.put(키, 값);
값을 검색하는 동안에도 마찬가지입니다.
모든 멀티 키가 실패할 수 있습니다.그 때문에, put([key1, key2], val) 및 get([key2, key2)은, [key1, key2] 및 [key2,]와 같은 것을 사용하게 됩니다.백업 맵에 키마다 해시 버킷이 포함되어 있지 않으면 룩업이 매우 느려집니다.
인덱스 데코레이터(위의 key1, key2 예 참조)를 사용하는 것이 좋다고 생각합니다.추가 인덱스 키가 저장된 값의 속성인 경우 속성 이름과 리플렉션으로 두 번째 맵을 작성할 수 있습니다(key, val), 추가 메서드 get(keyname, property value).
get(propertyname, propertyvalue)의 반환 유형은 컬렉션이 될 수 있으므로 고유 키도 인덱싱되지 않습니다.
저는 비슷한 문제를 해결하기 위해 이것을 만들었습니다.
데이터 구조
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
public class HashBucket {
HashMap<Object, ArrayList<Object>> hmap;
public HashBucket() {
hmap = new HashMap<Object, ArrayList<Object>>();
}
public void add(Object key, Object value) {
if (hmap.containsKey(key)) {
ArrayList al = hmap.get(key);
al.add(value);
} else {
ArrayList al = new ArrayList<Object>();
al.add(value);
hmap.put(key, al);
}
}
public Iterator getIterator(Object key) {
ArrayList al = hmap.get(key);
return hmap.get(key).iterator();
}
}
값을 가져옵니다.
(주* 삽입된 유형으로 오브젝트를 다시 캐스팅합니다.내 경우 이벤트 오브젝트였습니다.)
public Iterator getIterator(Object key) {
ArrayList al = hmap.get(key);
if (al != null) {
return hmap.get(key).iterator();
} else {
List<Object> empty = Collections.emptyList();
return empty.iterator();
}
}
삽입
Event e1 = new Event();
e1.setName("Bob");
e1.setTitle("Test");
map.add("key",e1);
Commons 또는 Guava의 MultiMap 또는 MultiKeyMap이 모두 작동합니다.
단, 키가 원시 유형임을 감안하여 Map 클래스 구입을 연장하여 컴포지트 키를 직접 처리하는 것이 빠르고 간단한 해결책이 될 수 있습니다.
Python 태플처럼 들리네요.그 정신에 따라 Comparable을 구현하는 자신만의 불변의 클래스를 만들 수 있습니다.그러면 그것을 얻을 수 있습니다.
이러한 실장은 여러 주요 오브젝트에 사용하였습니다.지도에 무수한 키를 사용할 수 있습니다.확장 가능하고 매우 간단합니다.단, Constructor의 인수 순서에 따라 키가 정렬되며 Arrays.equals()를 사용하기 때문에 2D 어레이에서는 사용할 수 없습니다.이 문제를 해결하려면 어레이를 사용할 수 있습니다.deepEquals();
도움이 되길 바랍니다.이러한 문제에 대한 해결책으로 사용할 수 없는 이유를 알고 있다면 알려주세요!
public class Test {
private static Map<InnumerableKey, Object> sampleMap = new HashMap<InnumerableKey, Object>();
private static class InnumerableKey {
private final Object[] keyParts;
private InnumerableKey(Object... keyParts) {
this.keyParts = keyParts;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof InnumerableKey)) return false;
InnumerableKey key = (InnumerableKey) o;
if (!Arrays.equals(keyParts, key.keyParts)) return false;
return true;
}
@Override
public int hashCode() {
return keyParts != null ? Arrays.hashCode(keyParts) : 0;
}
}
public static void main(String... args) {
boolean keyBoolean = true;
double keyDouble = 1d;
Object keyObject = new Object();
InnumerableKey doubleKey = new InnumerableKey(keyBoolean, keyDouble);
InnumerableKey tripleKey = new InnumerableKey(keyBoolean, keyDouble, keyObject);
sampleMap.put(doubleKey, "DOUBLE KEY");
sampleMap.put(tripleKey, "TRIPLE KEY");
// prints "DOUBLE KEY"
System.out.println(sampleMap.get(new InnumerableKey(true, 1d)));
// prints "TRIPLE KEY"
System.out.println(sampleMap.get(new InnumerableKey(true, 1d, keyObject)));
// prints null
System.out.println(sampleMap.get(new InnumerableKey(keyObject, 1d, true)));
}
}
이런 건 어때?
그의 진술에 따르면 키는 Unique이므로 동일한 값 개체를 다른 키에 저장하는 것은 매우 가능하며, 위의 값과 일치하는 키를 전송하면 값 개체로 돌아갈 수 있습니다.
아래 코드 참조:
값 오브젝트 클래스,
public class Bond {
public Bond() {
System.out.println("The Name is Bond... James Bond...");
}
private String name;
public String getName() { return name;}
public void setName(String name) { this.name = name; }
}
public class HashMapValueTest {
public static void main(String[] args) {
String key1 = "A";
String key2 = "B";
String key3 = "C";
Bond bond = new Bond();
bond.setName("James Bond Mutual Fund");
Map<String, Bond> bondsById = new HashMap<>();
bondsById.put(key1, bond);
bondsById.put(key2, bond);
bondsById.put(key3, bond);
bond.setName("Alfred Hitchcock");
for (Map.Entry<String, Bond> entry : bondsById.entrySet()) {
System.out.println(entry.getValue().getName());
}
}
}
결과는 다음과 같습니다.
The Name is Bond... James Bond...
Alfred HitchCock
Alfred HitchCock
Alfred HitchCock
키가 고유할 경우 2개의 맵, 맵 맵, map Of What를 사용할 필요가 없습니다.있어.맵은 1개뿐이며 키와 값을 맵에 넣는 간단한 래퍼 방식만 있으면 됩니다.예제:
Map<String, String> map = new HashMap<>();
public void addKeysAndValue(String key1, String key2, String value){
map.put(key1, value);
map.put(key2, value);
}
public void testIt(){
addKeysAndValue("behemoth", "hipopotam", "hornless rhino");
}
그럼 평상시처럼 지도를 사용하세요.그런 화려한 getByKeyN과 KeyN이 필요 없습니다.
K1 및 K2 인스턴스가 있는 클래스를 정의합니다.그런 다음 키 유형만큼 클래스를 사용합니다.
Google 컬렉션을 참조하십시오.또는 제안하신 대로 내부적으로 지도를 사용하여 해당 지도에 쌍을 사용하도록 하십시오.페어를 작성하거나 검색해야 합니다.이것은 매우 간단하지만 표준 컬렉션의 일부가 아닙니다.
당신의 솔루션이 이 요구에 대해 꽤 그럴듯하게 들리는군요.솔직히 당신의 두 가지 주요 유형이 정말 다르다면 문제없을 것 같습니다.이것에 대한 독자적인 실장을 작성해, 필요에 따라서 동기 문제에 대처할 수 있습니다.
여러 키의 조합을 하나로 사용하려는 경우 Apache 통신사 MultiKey가 친구일 수 있습니다.하나씩은 안 될 것 같은데...
, 의 맵을 할 수 .Map<K1, V>
★★★★★★★★★★★★★★★★★」Map<K2, V>
의 맵을 하여 '2개의 맵'을 사용합니다.Map<K1, V>
그리고.Map<K2, K1>
하나의 키가 다른 키보다 영속적인 경우 두 번째 옵션이 더 적절할 수 있습니다.
제가 보기에 당신이 원하는 방법은 Map에서 직접 지원하는 것 같습니다.당신이 원하는 것 같은 것은
put(K1 key, K2 key, V value)
put(K1 key, V value)
put(K2 key, V value)
지도에서는get()
그리고.containsKey()
기타 모든 것Object
논쟁들.이 제품을 사용하는 것을 막을 수 있는 것은 없습니다.get()
조합하는 모든 컴포지트 맵에 위임하는 메서드(질문 및 기타 답변 참조)아마도 클래스 캐스팅 문제가 발생하지 않도록 유형 등록이 필요할 것입니다(특수 + 순진하게 구현된 경우).
입력된 기반 등록을 통해 사용할 "올바른" 지도를 검색할 수도 있습니다.
Map<T,V> getMapForKey(Class<T> keyClass){
//Completely naive implementation - you actually need to
//iterate through the keys of the maps, and see if the keyClass argument
//is a sub-class of the defined map type. And then ordering matters with
//classes that implement multiple interfaces...
Map<T,V> specificTypeMap = (Map<T,V) maps.get(keyClass);
if (specificTypeMap == null){
throw new IllegalArgumentException("There is no map keyed by class" + keyClass);
}
return maps.get(keyClass);
}
V put(Object key, V value) {
//This bit requires generic suppression magic - but
//nothing leaves this class and you're testing it right?
//(You can assert that it *is* type-safe)
Map map = getMapForKey(key.getClass());
map.put(object, key);
}
void put(Object[] keys, V value) { //Or put(V value, Object ... keys)
//Might want to catch exceptions for unsupported keys and log instead?
.....
}
몇 가지 아이디어만...
이 구조를 제안하고 싶습니다.
Map<K1, Map<K2, V>>
두 번째 키를 검색하는 것은 효율적이지 않을 수 있지만
보다 복잡한 키를 사용할 수 있는 또 다른 솔루션은 http://insidecoffe.blogspot.de/2013/04/indexable-hashmap-implementation.html에서 찾을 수 있습니다.
trie 데이터 구조를 사용하는 것은 어떻습니까?
http://en.wikipedia.org/wiki/Trie
트라이의 뿌리는 공백으로 남는다.첫 번째 레벨의 형제자매는 맵의 프라이머리 키가 되고 두 번째 레벨의 형제자매는 세컨더리 키가 되며 세 번째 레벨은 브랜치의 종료를 나타내는 값이 늘이 되는 터미널 노드가 됩니다.같은 방식을 사용하여 3개 이상의 키를 추가할 수도 있습니다.
검색은 단순한 DFS입니다.
예를 들어 맵을 정렬에만 사용할 경우 값이 존재하지 않을 때까지 키에 매우 작은 값을 추가하되 최소값(예: Double)을 추가하지 않는 것이 더티하고 간단한 해결책입니다.MIN_VALUE)가 버그를 일으키기 때문입니다.말씀드렸듯이, 이것은 매우 지저분한 해결책이지만 코드를 더 쉽게 만들 수 있습니다.
언급URL : https://stackoverflow.com/questions/822322/how-to-implement-a-map-with-multiple-keys
'source' 카테고리의 다른 글
스프링 테스트에서 환경 변수 또는 시스템 속성을 설정하는 방법은 무엇입니까? (0) | 2022.09.03 |
---|---|
Spring Security 5 : ID "null"에 매핑된 PasswordEncoder가 없습니다. (0) | 2022.09.03 |
소품 및 데이터 중복을 방지하려면 어떻게 해야 합니까? (0) | 2022.09.03 |
Java에서의 정적 할당 - 힙, 스택 및 영구 생성 (0) | 2022.09.03 |
python 확장 - swig 또는 Cython이 아닌 swig로 확장 (0) | 2022.09.03 |