Klasa Java, która implementuje Mapę i utrzymuje kolejność wstawiania?

463

Szukam klasy w java, która ma powiązanie klucz-wartość, ale bez użycia skrótów. Oto co obecnie robię:

  1. Dodaj wartości do Hashtable.
  2. Uzyskaj iterator dla Hashtable.entrySet().
  3. Iteruj przez wszystkie wartości i:
    1. Zdobądź Map.Entryiterator.
    2. Utwórz obiekt typu Module(klasa niestandardowa) na podstawie wartości.
    3. Dodaj klasę do JPanel.
  4. Pokaż panel.

Problem polega na tym, że nie mam kontroli nad kolejnością, aby odzyskać wartości, więc nie mogę wyświetlić wartości w podanej kolejności (bez zakodowania kolejności na stałe).

Użyłbym do tego ArrayListlub Vector, ale później w kodzie muszę pobrać Moduleobiekt dla danego klucza, czego nie mogę zrobić z ArrayListlub Vector.

Czy ktoś wie o darmowej / otwartej klasie Java, która to zrobi, lub o sposobie uzyskania wartości z Hashtable podstawie tego, kiedy zostały dodane?

Dzięki!

Shane
źródło
1
Nie musisz używać entryset / map.entry. możesz iterować po kluczach i wartościach, używając hashtable.keys jako wyliczenia lub używając hashtable.keyset.iterator.
John Gardner,
5
Zezwoliłem na zmianę tytułu, ponieważ nieużywanie skrótów nie stanowi problemu, ale zachowanie kolejności wstawiania.
Joachim Sauer
Podobne pytanie, zamówiona mapa Java
Basil Bourque,

Odpowiedzi:

734

Proponuję a LinkedHashMaplub a TreeMap. A LinkedHashMaputrzymuje klucze w kolejności, w której zostały wstawione, a A TreeMapjest sortowane według a Comparatorlub naturalnej Comparablekolejności elementów.

Ponieważ nie musi utrzymywać sortowania elementów, LinkedHashMapw większości przypadków powinno być szybsze; TreeMapma O(log n)wydajność containsKey, get, puti remove, zgodnie z Javadocs, gdy LinkedHashMapjestO(1) dla każdego z nich.

Jeśli interfejs API, który oczekuje tylko przewidywalnego porządku sortowania, w przeciwieństwie do konkretnego porządku sortowania, rozważ użycie interfejsów implementowanych przez te dwie klasy NavigableMaplub SortedMap. To pozwoli ci nie wyciekać konkretnych implementacji do twojego API i przechodzić później do jednej z tych konkretnych klas lub zupełnie innej implementacji.

Michael Myers
źródło
2
Dla mnie to nie zadziała, ponieważ zgodnie z javadocs daje to tylko uporządkowane wartości (poprzez wywołanie wartości ()). Czy istnieje sposób na zamówienie instancji Map.Entry?
Cory Kendall,
1
@CoryKendall: Czy TreeMap nie działa? Powinien być sortowany według kluczy, a nie według wartości.
Michael Myers
1
Mój błąd, myślałem, że Zestawy były nieposortowane.
Cory Kendall,
61
Uwaga: Sortowanie mapy drzewa opiera się na naturalnej kolejności kluczy: „Mapa jest sortowana zgodnie z naturalną kolejnością klawiszy”. LinkedHashMap jest posortowana kolejność wstawiania bij. Duża różnica!
Jop van Raaij
3
@AlexR: Jest to prawdą tylko wtedy, gdy LinkedHashMap został utworzony przy użyciu specjalnego konstruktora, który jest przewidziany do tego celu. Domyślnie iteracja odbywa się w kolejności wstawiania.
Michael Myers
22

LinkedHashMap zwróci elementy w kolejności, w jakiej zostały wstawione do mapy podczas iteracji po keySet (), entrySet () lub wartościach () mapy.

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

Spowoduje to wydrukowanie elementów w kolejności, w jakiej zostały umieszczone na mapie:

id = 1
name = rohan 
age = 26 
Praveen Kishor
źródło
16

Jeśli niezmienna mapa pasuje do twoich potrzeb, istnieje biblioteka Google o nazwie guava (zobacz także pytania z guava )

Guava zapewnia ImmutableMap z niezawodną kolejnością iteracji określoną przez użytkownika. Ta ImmutableMap ma wydajność O (1) dla zawieraKey, get. Oczywiście wstawianie i usuwanie nie są obsługiwane.

Obiekty ImmutableMap są konstruowane przy użyciu eleganckich metod wygody statycznej () i copyOf () lub obiektu Builder .

jvdneste
źródło
6

Możesz zachować Map(dla szybkiego wyszukiwania) i List(dla porządku), ale LinkedHashMapmoże być najprostszy. Możesz także wypróbować SortedMapnp. TreeMap, Który ma dowolną określoną przez ciebie kolejność.

Peter Lawrey
źródło
1

Nie wiem, czy jest to program typu open source, ale po małym googlowaniu znalazłem tę implementację Map przy użyciu ArrayList . Wygląda na to, że jest wcześniejsza niż 1.5 Java, więc możesz chcieć ją uogólnić, co powinno być łatwe. Pamiętaj, że ta implementacja ma dostęp O (N), ale nie powinno to stanowić problemu, jeśli nie dodasz setek widżetów do JPanel, czego i tak nie powinieneś.

jpalecek
źródło
1

Ilekroć muszę zachować naturalny porządek rzeczy, które są znane z góry, używam EnumMap

klucze będą wyliczane i możesz wstawiać w dowolnej kolejności, ale po iteracji będzie to powtarzać w kolejności wyliczeniowej (naturalny).

Również podczas korzystania z EnumMap nie powinno być żadnych kolizji, które mogą być bardziej wydajne.

Naprawdę uważam, że użycie enumMap zapewnia czysty, czytelny kod. Oto przykład

j2emanue
źródło
1

Możesz użyć LinkedHashMap do głównego zamówienia reklamowego w Mapie

Ważne punkty dotyczące klasy Java LinkedHashMap to:

  1. Zawiera tylko unikalne elementy.
  2. LinkedHashMap zawiera wartości oparte na kluczu 3. Może mieć jeden klucz zerowy i wiele wartości zerowych. 4.To samo, co HashMap, zamiast tego utrzymuje porządek wstawiania

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

Ale jeśli chcesz sortować wartości na mapie za pomocą obiektu zdefiniowanego przez użytkownika lub dowolnego pierwotnego klucza typu danych, powinieneś użyć TreeMap Aby uzyskać więcej informacji, skorzystaj z tego łącza

Pankaj Goyal
źródło
0

Możesz użyć LinkedHashMap<K, V>lub wdrożyć własną mapę niestandardową, która utrzymuje kolejność wstawiania.

Możesz użyć CustomHashMapnastępujących elementów z następującymi funkcjami:

  • Kolejność wprowadzania jest utrzymywana poprzez wewnętrzne użycie LinkedHashMap.
  • Klawisze z null lub puste ciągi znaków są niedozwolone.
  • Po utworzeniu klucza z wartością nie zastępujemy jego wartości.

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

Zastosowanie 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             |

Jeśli wiesz, że KLUCZY są naprawione, możesz użyć EnumMap. Uzyskaj wartości z plików właściwości / XML

DAWNY:

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");
Yash
źródło