Mapを実装し、挿入順序を維持するJavaクラス?


463

キーと値の関連付けがあるが、ハッシュを使用しないJavaのクラスを探しています。これが私が現在やっていることです:

  1. に値を追加しますHashtable
  2. のイテレータを取得しますHashtable.entrySet()
  3. すべての値を反復処理し、
    1. Map.Entryイテレータのを取得します。
    2. Module値に基づいてタイプのオブジェクト(カスタムクラス)を作成します。
    3. クラスをJPanelに追加します。
  4. パネルを表示します。

これの問題は、値を取得する順序を制御できないため、特定の順序で値を表示できないことです(順序をハードコーディングしないと)。

私はこれにArrayListor Vectorを使用しますが、コードの後半で、Module指定されたKeyのオブジェクトを取得する必要があります。これはArrayListorで行うことはできませんVector

これを行うフリー/オープンソースのJavaクラス、またはHashtable追加されたときに基づいてから値を取得する方法を知っている人はいますか?

ありがとう!


1
entryset / map.entryを使用する必要はありません。列挙型としてhashtable.keysを使用するか、hashtable.keyset.iteratorを使用して、キーと値を反復処理できます。
ジョンガードナー

5
ハッシュを使用しないことは実際には問題ではないので、自由にタイトルを変更しましたが、挿入順序を維持しました。
ヨアヒムザウアー

回答:


734

LinkedHashMapまたはをお勧めしTreeMapます。A LinkedHashMapは挿入された順序でキーを保持し、A はTreeMapa ComparatorまたはComparable要素の自然順序付けによってソートされたままになります。

要素をソートしたままにするLinkedHashMap必要がないため、ほとんどの場合は高速になります。TreeMap有するO(log n)のパフォーマンスをcontainsKeygetput、とremove、のJavadocによると、一方がLinkedHashMapあるO(1)ごとに。

あなたのAPIがあることが唯一の予測可能なソート順を想定している場合、特定のソート順とは反対に、これら2つのクラスが実装するインタフェースを使用することを検討、NavigableMapまたはSortedMap。これにより、特定の実装をAPIにリークせず、後でそれらの特定のクラスまたは完全に異なる実装のいずれかに切り替えることができます。


2
javadocsによると、これは(values()呼び出しを介して)順序付けされた値しか提供しないため、これは私には機能しません。注文したMap.Entryインスタンスを取得する方法はありますか?
Cory Kendall

1
@CoryKendall:TreeMapは機能しませんか?値ではなくキーでソートされることになっています。
マイケルマイヤーズ

1
私の間違いは、セットがソートされていないと思ったからです。
Cory Kendall

61
注意:TreeMapのソートはキーの自然な順序に基づいています:「マップはキーの自然な順序に従ってソートされています」。LinkedHashMapは、BJ挿入順序でソートされます。大きな違い!
Jop van Raaij 2013

3
@AlexR:これは、LinkedHashMapがその目的のために提供されている特別なコンストラクターを使用して作成された場合にのみ当てはまります。デフォルトでは、反復は挿入順になります。
マイケルマイヤーズ

22

LinkedHashMapは、マップのkeySet()、entrySet()またはvalues()を反復処理するときに、マップに挿入された順序で要素を返します。

Map<String, String> map = new LinkedHashMap<String, String>();

map.put("id", "1");
map.put("name", "rohan");
map.put("age", "26");

for (Map.Entry<String, String> entry : map.entrySet()) {
    System.out.println(entry.getKey() + " = " + entry.getValue());
}

これにより、要素がマップに配置された順序で要素が印刷されます。

id = 1
name = rohan 
age = 26 

16

不変マップがあなたのニーズに合う場合、グアバと呼ばれるグーグルによるライブラリがありますグアバの質問も参照してください)

Guavaは、ImmutableMapに信頼性の高いユーザー指定の反復順序を提供します。このImmutableMapは、containsKeyのO(1)パフォーマンスを備えています。明らかに、配置と削除はサポートされていません。

ImmutableMapオブジェクトは、エレガントな静的簡易メソッドof()およびcopyOf()またはビルダーオブジェクトを使用して構築されます。


6

Map(高速検索用)とList(順序用)を維持できますが、a LinkedHashMapが最も簡単な場合があります。また、指定した任意の順序を持​​つSortedMapegを試すこともTreeMapできます。


1

それがオープンソースかどうかはわかりませんが、少しグーグルで調べたところ、ArrayListを使用したMapのこの実装を見つけました。それは1.5より前のJavaのようですので、それを一般化したいかもしれませんが、それは簡単なはずです。この実装にはO(N)アクセスがありますが、何百ものウィジェットをJPanelに追加しなくても問題ありません。



1

事前にわかっているものの自然な順序を維持する必要があるときはいつでも、EnumMapを使用します

キーは列挙型であり、任意の順序で挿入できますが、反復すると、列挙型の順序(自然順序)で反復されます。

また、EnumMapを使用する場合は、衝突が発生しないようにする必要があります。

enumMapを使用すると、コードが読みやすくなることがわかります。ここにがあります


1

LinkedHashMapを使用して、マップのメインの挿入順序を指定できます

Java LinkedHashMapクラスに関する重要なポイントは次のとおりです。

  1. 一意の要素のみが含まれます。
  2. LinkedHashMapには、キー3に基づく値が含まれます。1つのnullキーと複数のnull値が含まれる場合があります。4.HashMapと同じですが、挿入順序を維持します

    public class LinkedHashMap<K,V> extends HashMap<K,V> implements Map<K,V> 

ただし、ユーザー定義オブジェクトまたは任意のプリミティブデータタイプキーを使用してマップ内のソート値が必要な場合は、TreeMapを使用する必要があります詳細については、このリンクを参照してください


0

挿入順序を維持LinkedHashMap<K, V>する独自のCustomMapを使用するか、実装することができます。

CustomHashMap以下を次の機能とともに使用できます。

  • LinkedHashMapを内部で使用することにより、挿入順序が維持されます。
  • nullまたは空の文字列を持つキーは許可されていません。
  • 値を持つキーが作成されたら、その値をオーバーライドすることはありません。

HashMapvs LinkedHashMapvsCustomHashMap

interface CustomMap<K, V> extends Map<K, V> {
    public boolean insertionRule(K key, V value);
}

@SuppressWarnings({ "rawtypes", "unchecked" })
public class CustomHashMap<K, V> implements CustomMap<K, V> {
    private Map<K, V> entryMap;
    // SET: Adds the specified element to this set if it is not already present.
    private Set<K> entrySet;

    public CustomHashMap() {
        super();
        entryMap = new LinkedHashMap<K, V>();
        entrySet = new HashSet();
    }

    @Override
    public boolean insertionRule(K key, V value) {
        // KEY as null and EMPTY String is not allowed.
        if (key == null || (key instanceof String && ((String) key).trim().equals("") ) ) {
            return false;
        }

        // If key already available then, we are not overriding its value.
        if (entrySet.contains(key)) { // Then override its value, but we are not allowing
            return false;
        } else { // Add the entry
            entrySet.add(key);
            entryMap.put(key, value);
            return true;
        }
    }
    public V put(K key, V value) {
        V oldValue = entryMap.get(key);
        insertionRule(key, value);
        return oldValue;
    }
    public void putAll(Map<? extends K, ? extends V> t) {
        for (Iterator i = t.keySet().iterator(); i.hasNext();) {
            K key = (K) i.next();
            insertionRule(key, t.get(key));
        }
    }

    public void clear() {
        entryMap.clear();
        entrySet.clear();
    }
    public boolean containsKey(Object key) {
        return entryMap.containsKey(key);
    }
    public boolean containsValue(Object value) {
        return entryMap.containsValue(value);
    }
    public Set entrySet() {
        return entryMap.entrySet();
    }
    public boolean equals(Object o) {
        return entryMap.equals(o);
    }
    public V get(Object key) {
        return entryMap.get(key);
    }
    public int hashCode() {
        return entryMap.hashCode();
    }
    public boolean isEmpty() {
        return entryMap.isEmpty();
    }
    public Set keySet() {
        return entrySet;
    }
    public V remove(Object key) {
        entrySet.remove(key);
        return entryMap.remove(key);
    }
    public int size() {
        return entryMap.size();
    }
    public Collection values() {
        return entryMap.values();
    }
}

の使用法CustomHashMap

public static void main(String[] args) {
    System.out.println("== LinkedHashMap ==");
    Map<Object, String> map2 = new LinkedHashMap<Object, String>();
    addData(map2);

    System.out.println("== CustomHashMap ==");
    Map<Object, String> map = new CustomHashMap<Object, String>();
    addData(map);
}
public static void addData(Map<Object, String> map) {
    map.put(null, "1");
    map.put("name", "Yash");
    map.put("1", "1 - Str");
    map.put("1", "2 - Str"); // Overriding value
    map.put("", "1"); // Empty String
    map.put(" ", "1"); // Empty String
    map.put(1, "Int");
    map.put(null, "2"); // Null

    for (Map.Entry<Object, String> entry : map.entrySet()) {
        System.out.println(entry.getKey() + " = " + entry.getValue());
    }
}

O / P:

== LinkedHashMap == | == CustomHashMap ==
null = 2            | name = Yash
name = Yash         | 1 = 1 - Str
1 = 2 - Str         | 1 = Int
 = 1                |
  = 1               |
1 = Int             |

KEYが修正されていることがわかっている場合は、EnumMapを使用できます。プロパティ/ XMLファイルから値を取得する

例:

enum ORACLE {
    IP, URL, USER_NAME, PASSWORD, DB_Name;
}

EnumMap<ORACLE, String> props = new EnumMap<ORACLE, String>(ORACLE.class);
props.put(ORACLE.IP, "127.0.0.1");
props.put(ORACLE.URL, "...");
props.put(ORACLE.USER_NAME, "Scott");
props.put(ORACLE.PASSWORD, "Tiget");
props.put(ORACLE.DB_Name, "MyDB");
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.