Classe Java che implementa Map e mantiene l'ordine di inserimento?


463

Sto cercando una classe in Java che abbia un'associazione valore-chiave, ma senza usare gli hash. Ecco cosa sto facendo attualmente:

  1. Aggiungi valori a Hashtable.
  2. Ottieni un iteratore per Hashtable.entrySet().
  3. Scorrere tutti i valori e:
    1. Ottieni un Map.Entryper l'iteratore.
    2. Creare un oggetto di tipo Module(una classe personalizzata) in base al valore.
    3. Aggiungi la classe a un JPanel.
  4. Mostra il pannello.

Il problema con questo è che non ho il controllo sull'ordine in cui ottengo i valori indietro, quindi non posso visualizzare i valori in un determinato ordine (senza codificare l'ordine).

Vorrei usare un ArrayListo Vectorper questo, ma più avanti nel codice devo prendere l' Moduleoggetto per una determinata Chiave, che non posso fare con un ArrayListo Vector.

Qualcuno sa di una classe Java gratuita / open-source che lo farà, o un modo per ottenere valori da un Hashtablebasato su quando sono stati aggiunti?

Grazie!


1
Non è necessario utilizzare entryset / map.entry. puoi iterare su chiavi e valori usando hashtable.keys come enumerazione o usando hashtable.keyset.iterator.
John Gardner,

5
Mi sono preso la libertà di cambiare il titolo, poiché non usare gli hash non è in realtà il problema, ma mantenere l'ordine di inserimento.
Joachim Sauer,

Risposte:


734

Suggerisco a LinkedHashMapo a TreeMap. A LinkedHashMapmantiene le chiavi nell'ordine in cui sono state inserite, mentre a TreeMapviene mantenuta ordinata tramite un ordinamento Comparatornaturale Comparabledegli elementi.

Dal momento che non deve mantenere gli elementi ordinati, LinkedHashMapdovrebbe essere più veloce nella maggior parte dei casi; TreeMapha O(log n)prestazioni per containsKey, get, put, e remove, secondo il Javadocs, mentre LinkedHashMapè O(1)per ciascuno.

Se l'API che prevede solo un ordinamento prevedibile, al contrario di un ordinamento specifico, prendere in considerazione l'utilizzo delle interfacce implementate da queste due classi NavigableMapo SortedMap. Ciò ti consentirà di non trapelare implementazioni specifiche nella tua API e passare successivamente a una di quelle classi specifiche oa un'implementazione completamente diversa.


2
Questo non funzionerà per me perché, come per javadocs, questo fornisce solo valori ordinati (attraverso la chiamata valori ()). C'è un modo per ottenere istanze Map.Entry ordinate?
Cory Kendall,

1
@CoryKendall: TreeMap non funziona? Dovrebbe essere ordinato per chiavi, non per valori.
Michael Myers

1
Errore mio, pensavo che gli insiemi non fossero ordinati.
Cory Kendall,

61
Nota: l'ordinamento di una TreeMap si basa sull'ordine naturale delle chiavi: "La mappa è ordinata in base all'ordinamento naturale delle sue chiavi". LinkedHashMap è ordinata per ordine di inserimento. Grande differenza!
Jop van Raaij,

3
@AlexR: Questo è vero solo se LinkedHashMap è stato creato utilizzando il costruttore speciale fornito a tale scopo. Per impostazione predefinita, l'iterazione è in ordine di inserimento.
Michael Myers

22

LinkedHashMap restituirà gli elementi nell'ordine in cui sono stati inseriti nella mappa quando si esegue l'iterazione su keySet (), entrySet () o valori () della mappa.

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

Questo stamperà gli elementi nell'ordine in cui sono stati inseriti nella mappa:

id = 1
name = rohan 
age = 26 


6

È possibile mantenere un Map(per una rapida ricerca) e List(per un ordine) ma a LinkedHashMappotrebbe essere il più semplice. Puoi anche provare un SortedMapesempio TreeMap, che ha qualsiasi ordine specificato.


1

Non so se si tratta di opensource, ma dopo un po 'di ricerche su Google ho trovato questa implementazione di Map usando ArrayList . Sembra essere pre-1.5 Java, quindi potresti voler generarlo, il che dovrebbe essere facile. Nota che questa implementazione ha accesso O (N), ma questo non dovrebbe essere un problema se non aggiungi centinaia di widget al tuo JPanel, cosa che non dovresti comunque.



1

Ogni volta che ho bisogno di mantenere l'ordine naturale delle cose che sono conosciute in anticipo, uso a EnumMap

le chiavi saranno enum e puoi inserirle nell'ordine che desideri ma quando esegui l'iterazione, itererà nell'ordine enum (l'ordine naturale).

Inoltre, quando si utilizza EnumMap non dovrebbero esserci collisioni che possono essere più efficienti.

Trovo davvero che l'uso di enumMap renda il codice leggibile pulito. Ecco un esempio


1

Puoi utilizzare LinkedHashMap per l'ordine di inserimento principale in Mappa

I punti importanti sulla classe Java LinkedHashMap sono:

  1. Contiene solo elementi unici.
  2. Una LinkedHashMap contiene valori basati sulla chiave 3. Può avere una chiave null e più valori null. 4.È uguale a HashMap invece mantiene l'ordine di inserimento

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

Ma se si desidera ordinare i valori nella mappa utilizzando l'oggetto definito dall'utente o qualsiasi chiave di tipo di dati primitivo, è necessario utilizzare TreeMap Per ulteriori informazioni, fare riferimento a questo collegamento


0

Puoi usare LinkedHashMap<K, V>o puoi implementare la tua CustomMap che mantiene l'ordine di inserimento.

È possibile utilizzare quanto segue CustomHashMapcon le seguenti funzionalità:

  • L'ordine di inserimento viene mantenuto utilizzando LinkedHashMap internamente.
  • Tasti con null o stringhe vuote non sono consentite.
  • Una volta creata la chiave con valore, non ne sostituiamo il valore.

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

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

OPERAZIONE:

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

Se sai che i KEY sono corretti, puoi usare EnumMap. Ottieni i valori dai file Proprietà / XML

EX:

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");
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.