क्या जावा में रिवर्स लुकअप के साथ हाशप है?


98

मेरे पास डेटा है जो "की-वैल्यू" के बजाय "की-की" फॉर्मेट के प्रकार में व्यवस्थित है। यह एक HashMap की तरह है, लेकिन मुझे दोनों दिशाओं में O (1) लुकअप की आवश्यकता होगी। क्या इस प्रकार की डेटा संरचना के लिए एक नाम है, और क्या जावा की मानक पुस्तकालयों में ऐसा कुछ भी शामिल है? (या शायद अपाचे कॉमन्स?)

मैं अपनी खुद की क्लास लिख सकता था जो मूल रूप से दो मिरर किए गए मैप्स का उपयोग करता है, लेकिन मैं पहिया को सुदृढ़ नहीं करूंगा (यदि यह पहले से मौजूद है लेकिन मैं सिर्फ सही अवधि के लिए नहीं खोज रहा हूं)।

जवाबों:


106

जावा एपीआई में ऐसा कोई वर्ग नहीं है। अपाचे कॉमन्स वर्ग जो आप चाहते हैं, वह बिडिएप के कार्यान्वयन में से एक होने जा रहा है ।

एक गणितज्ञ के रूप में, मैं इस तरह की संरचना को एक आपत्ति कहूंगा।


83
एक गैर-गणितज्ञ के रूप में मैं इस तरह की संरचना को "एक नक्शा कहता हूं जो आपको कुंजी या अन्य तरीके से मूल्यों को देखने की अनुमति देता है"
Dónal

4
बहुत बुरा यह जेनरिक के लिए कोई समर्थन नहीं है, लगता है कि अमरूद करता है।
एरन मेडन

2
github.com/megamattron/collections-generic ने जेनिक्स सपोर्ट के साथ बिडिओपार्ट
Kenston Choi

1
@ डॉन "बीड़ी" -> "द्वि-दिशात्मक"
ryvantage

3
@ डोनल हाँ, लेकिन पूरी आईटी गणित पर आधारित है
एलेक्स

75

Apache Commons के अलावा, Guava में एक BiMap भी है ।


जानकारी के लिए धन्यवाद! मैं समय के लिए अपाचे के साथ चिपका रहा हूँ, हालांकि (जब तक कुछ अच्छे कारण नहीं हैं?)
किप

मैं अपाचे संग्रहों के लिए एक अच्छी तुलना की पेशकश नहीं कर सकता, लेकिन Google संग्रह में बहुत अच्छा सामान है जो मुझे लगता है कि इसे देखने के लायक बना देगा।
कॉलिन डी

16
Google कलेक्शंस का एक फायदा यह है कि इसमें जेनेरिक हैं जबकि कॉमन्स कलेक्शन नहीं है।
मार्क

3
दो कामों की तुलना के लिए, इस उत्तर में उद्धरण देखें: stackoverflow.com/questions/787446/… (और मूल साक्षात्कार)। यह स्पष्ट कारणों के लिए Google के पक्षपाती है, लेकिन फिर भी मुझे लगता है कि यह कहना सुरक्षित है कि आप आजकल Google संग्रह के साथ बेहतर हैं।
जोनीक

1
BiMap लिंक टूट गया है। कृपया इसका उपयोग करें
महसा 2

20

यहाँ एक सरल वर्ग है जिसका उपयोग मैं यह करने के लिए करता था (मैं अभी तक एक और तृतीय पक्ष निर्भरता नहीं चाहता था)। यह मैप्स में उपलब्ध सभी सुविधाओं की पेशकश नहीं करता है लेकिन यह एक अच्छी शुरुआत है।

    public class BidirectionalMap<KeyType, ValueType>{
        private Map<KeyType, ValueType> keyToValueMap = new ConcurrentHashMap<KeyType, ValueType>();
        private Map<ValueType, KeyType> valueToKeyMap = new ConcurrentHashMap<ValueType, KeyType>();

        synchronized public void put(KeyType key, ValueType value){
            keyToValueMap.put(key, value);
            valueToKeyMap.put(value, key);
        }

        synchronized public ValueType removeByKey(KeyType key){
            ValueType removedValue = keyToValueMap.remove(key);
            valueToKeyMap.remove(removedValue);
            return removedValue;
        }

        synchronized public KeyType removeByValue(ValueType value){
            KeyType removedKey = valueToKeyMap.remove(value);
            keyToValueMap.remove(removedKey);
            return removedKey;
        }

        public boolean containsKey(KeyType key){
            return keyToValueMap.containsKey(key);
        }

        public boolean containsValue(ValueType value){
            return keyToValueMap.containsValue(value);
        }

        public KeyType getKey(ValueType value){
            return valueToKeyMap.get(value);
        }

        public ValueType get(KeyType key){
            return keyToValueMap.get(key);
        }
    }

5
आप इसमें ValueVoueeyMap.containsKey (मान)
JT

मैं इस मानचित्र का उपयोग नहीं करूंगा क्योंकि यह वर्तमान में है क्योंकि द्वि-दिशात्मकता टूट जाती है यदि एक कुंजी (या मूल्य) को एक अलग मूल्य (या कुंजी) के साथ फिर से जोड़ा जाता है, जो कि एक प्रमुख IMO को अपडेट करने के लिए मान्य उपयोग होगा।
Qw3ry

11

यदि कोई टक्कर नहीं होती है, तो आप हमेशा दोनों दिशाओं को एक ही HashMap :-) में जोड़ सकते हैं


6
@ किप: क्यों? कुछ संदर्भों में यह पूरी तरह से वैध समाधान है। तो दो हैश नक्शे होंगे।
लॉरेंस Dol

7
नहीं, यह एक बदसूरत, नाजुक हैक है। इसे प्रत्येक प्राप्त () और पुट () पर द्वि-दिशात्मक संपत्ति के रखरखाव की आवश्यकता होती है, और इसे अन्य तरीकों से पारित किया जा सकता है जो द्वि-दिशात्मक संपत्ति के बारे में जानने के बिना भी नक्शे को संशोधित करता है। शायद यह एक विधि के अंदर एक स्थानीय चर के रूप में ठीक होगा जो कहीं भी पारित नहीं हुआ है, या अगर यह सृजन के तुरंत बाद अप्रमाणिक बना दिया गया था। लेकिन फिर भी, यह नाजुक है (कोई व्यक्ति साथ आता है और उस फ़ंक्शन को घुमाता है और एक तरह से द्विदिशता को तोड़ता है जो हमेशा तुरंत खुद को एक समस्या नहीं दिखाएगा)
किप

1
@ किप, मैं मानता हूं कि इस तरह के उपयोग को उस नक्शे का उपयोग करके कक्षा में आंतरिक रखा जाना चाहिए, लेकिन आपकी अंतिम टिप्पणी केवल तभी सही होगी जब संबंधित JUnit परीक्षण मैला हो :-)
9

अगर मैं इस तरह के कार्यान्वयन का बहुत वैध उपयोग कर सकता हूं तो विधानसभा भाषा के निर्देशों के डिकोडिंग / एन्कोडिंग के लिए एक मानचित्र की आवश्यकता है, यहां आप नक्शे की स्थिति को भी कभी नहीं बदलेंगे, एक दिशा में कुंजी निर्देश हैं जो अन्य बाइनरी मानों को तार करते हैं। इसलिए आपको कभी भी संघर्ष नहीं करना चाहिए।
एमके

एक छोटे पैमाने के लुकअप उद्देश्य के लिए, वह हैक मेरी समस्या को हल करता है।
दूधसरैक

5

यहाँ मेरे 2 सेंट।

या आप जेनरिक के साथ एक सरल विधि का उपयोग कर सकते हैं। केक का टुकड़ा।

public static <K,V> Map<V, K> invertMap(Map<K, V> toInvert) {
    Map<V, K> result = new HashMap<V, K>();
    for(K k: toInvert.keySet()){
        result.put(toInvert.get(k), k);
    }
    return result;
}

बेशक आप अद्वितीय मूल्यों के साथ एक नक्शा होना चाहिए। अन्यथा, उनमें से एक को बदल दिया जाएगा।


1

गीता के उत्तर से प्रेरित होकर मैंने कुछ सुधारों के साथ खुद भी ऐसा ही कुछ लिखने का फैसला किया:

  • वर्ग लागू कर रहा है Map<K,V>-इंटरफेस
  • एक मूल्य बदलते समय put(इसकी कम से कम मैं इसे गारंटी देने की उम्मीद करता हूं) इसकी देखभाल करके वास्तव में इसकी गारंटी दी जाती है

मानचित्रण कॉल पर रिवर्स दृश्य प्राप्त करने के लिए उपयोग सामान्य मानचित्र की तरह है getReverseView()। सामग्री की प्रतिलिपि नहीं बनाई गई है, केवल एक दृश्य लौटाया गया है।

मुझे यकीन नहीं है कि यह पूरी तरह से मूर्खतापूर्ण है (वास्तव में, यह शायद नहीं है), इसलिए टिप्पणी करने के लिए स्वतंत्र महसूस करें यदि आपको कोई दोष दिखाई देता है और मैं उत्तर को अपडेट करूंगा।

public class BidirectionalMap<Key, Value> implements Map<Key, Value> {

    private final Map<Key, Value> map;
    private final Map<Value, Key> revMap;

    public BidirectionalMap() {
        this(16, 0.75f);
    }

    public BidirectionalMap(int initialCapacity) {
        this(initialCapacity, 0.75f);
    }

    public BidirectionalMap(int initialCapacity, float loadFactor) {
        this.map = new HashMap<>(initialCapacity, loadFactor);
        this.revMap = new HashMap<>(initialCapacity, loadFactor);
    }

    private BidirectionalMap(Map<Key, Value> map, Map<Value, Key> reverseMap) {
        this.map = map;
        this.revMap = reverseMap;
    }

    @Override
    public void clear() {
        map.clear();
        revMap.clear();
    }

    @Override
    public boolean containsKey(Object key) {
        return map.containsKey(key);
    }

    @Override
    public boolean containsValue(Object value) {
        return revMap.containsKey(value);
    }

    @Override
    public Set<java.util.Map.Entry<Key, Value>> entrySet() {
        return Collections.unmodifiableSet(map.entrySet());
    }

    @Override
    public boolean isEmpty() {
        return map.isEmpty();
    }

    @Override
    public Set<Key> keySet() {
        return Collections.unmodifiableSet(map.keySet());
    }

    @Override
    public void putAll(Map<? extends Key, ? extends Value> m) {
        m.entrySet().forEach(e -> put(e.getKey(), e.getValue()));
    }

    @Override
    public int size() {
        return map.size();
    }

    @Override
    public Collection<Value> values() {
        return Collections.unmodifiableCollection(map.values());
    }

    @Override
    public Value get(Object key) {
        return map.get(key);
    }

    @Override
    public Value put(Key key, Value value) {
        Value v = remove(key);
        getReverseView().remove(value);
        map.put(key, value);
        revMap.put(value, key);
        return v;
    }

    public Map<Value, Key> getReverseView() {
        return new BidirectionalMap<>(revMap, map);
    }

    @Override
    public Value remove(Object key) {
        if (containsKey(key)) {
            Value v = map.remove(key);
            revMap.remove(v);
            return v;
        } else {
            return null;
        }
    }

}

ध्यान दें कि बस BiMap और BidiMap के रूप में, यह एक ऐसी आपत्ति है जो एक ही मूल्य के साथ कई कुंजी रखने की अनुमति नहीं देता है। (getReverseView ()। get (v) हमेशा केवल एक कुंजी लौटाएगा)।
डोनाटेलो

यह सच है, लेकिन
ओटीएच

मुझे यकीन नहीं है कि उन्होंने कहा कि इसका डेटा इस बाधा से मेल खाता है, लेकिन वैसे भी, यह किसी और को बेहतर समझने में मदद कर सकता है!
डोनाटेलो

0

यहाँ एक पुराना सवाल है, लेकिन अगर किसी और के मस्तिष्क ब्लॉक है जैसे मैंने अभी किया और इस पर ठोकर खाई, उम्मीद है कि इससे मदद मिलेगी।

मैं भी एक द्वि-दिशात्मक हैशपॉप की तलाश में था, कभी-कभी यह उन उत्तरों में सबसे सरल होता है जो सबसे उपयोगी होते हैं।

यदि आप पहिया का फिर से आविष्कार नहीं करना चाहते हैं और अपनी परियोजना में अन्य पुस्तकालयों या परियोजनाओं को जोड़ना पसंद नहीं करते हैं, तो समानांतर सरणियों के एक साधारण कार्यान्वयन के बारे में कैसे (या यदि आपका डिज़ाइन इसकी मांग करता है तो ArrayLists)।

SomeType[] keys1 = new SomeType[NUM_PAIRS];
OtherType[] keys2 = new OtherType[NUM_PAIRS];

जैसे ही आपको दो चाबियों में से 1 का इंडेक्स पता चलता है आप आसानी से दूसरे से अनुरोध कर सकते हैं। तो आपके देखने के तरीके कुछ इस तरह दिख सकते हैं:

SomeType getKey1(OtherType ot);
SomeType getKey1ByIndex(int key2Idx);
OtherType getKey2(SomeType st); 
OtherType getKey2ByIndex(int key2Idx);

यह मान रहा है कि आप उचित ऑब्जेक्ट ओरिएंटेड स्ट्रक्चर्स का उपयोग कर रहे हैं, जहाँ केवल तरीके इन सरणियों / ArrayLists को संशोधित कर रहे हैं, उन्हें समानांतर रखना बहुत सरल होगा। एक ArrayList के लिए और भी आसान है क्योंकि आपको सरणियों का आकार बदलने पर पुनर्निर्माण नहीं करना होगा, इसलिए जब तक आप अग्रानुक्रम में जोड़ते / हटाते हैं।


3
आप हैशमैप की एक महत्वपूर्ण विशेषता खो रहे हैं, अर्थात्, ओ (1) लुकअप। इस तरह के क्रियान्वयन के लिए किसी एक सरणियों के माध्यम से स्कैनिंग की आवश्यकता होती है, जब तक कि आप उस आइटम का सूचकांक नहीं ढूंढते हैं, जो आप देख रहे हैं, जो O (n)
किप

हाँ, यह बहुत सच है और एक बड़ी खामी है। हालाँकि मेरी व्यक्तिगत स्थिति में मैं वास्तव में एक त्रि-दिशात्मक कुंजी सूची की आवश्यकता के साथ काम कर रहा था और मुझे हमेशा समय से पहले कम से कम 1 कुंजी पता होगी, इसलिए मेरे लिए व्यक्तिगत रूप से यह एक मुद्दा नहीं था। यह इंगित करने के लिए धन्यवाद, हालांकि, मुझे लगता है कि मेरे मूल पद में उस महत्वपूर्ण तथ्य को छोड़ दिया है।
ThatOneGuy
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.