¿Clase de Java que implementa Map y mantiene el orden de inserción?

463

Estoy buscando una clase en java que tenga asociación clave-valor, pero sin usar hashes. Esto es lo que estoy haciendo actualmente:

  1. Agregar valores a a Hashtable.
  2. Obtenga un iterador para el Hashtable.entrySet().
  3. Iterar a través de todos los valores y:
    1. Obtenga un Map.Entrypara el iterador.
    2. Cree un objeto de tipo Module(una clase personalizada) basado en el valor.
    3. Agregue la clase a un JPanel.
  4. Mostrar el panel.

El problema con esto es que no tengo control sobre el orden en que recupero los valores, por lo que no puedo mostrar los valores en un orden dado (sin codificar el orden).

Usaría un ArrayListo Vectorpara esto, pero más adelante en el código necesito tomar el Moduleobjeto para una Clave dada, lo que no puedo hacer con un ArrayListo Vector.

¿Alguien sabe de una clase Java libre / de código abierto que haga esto, o una forma de obtener valores de un Hashtable base en función de cuándo se agregaron?

¡Gracias!

Shane
fuente
1
No necesita usar entryset / map.entry. puede iterar sobre claves y valores usando hashtable.keys como enumeración o usando hashtable.keyset.iterator.
John Gardner el
55
Me tomé la libertad de cambiar el título, ya que no usar hashes no es realmente el problema, sino mantener el orden de inserción.
Joachim Sauer el
Pregunta similar, Mapa ordenado de Java
Basil Bourque

Respuestas:

734

Sugiero a LinkedHashMapo a TreeMap. A LinkedHashMapmantiene las claves en el orden en que se insertaron, mientras que a TreeMapse mantiene ordenado a través de a Comparatoro el Comparableordenamiento natural de los elementos.

Como no tiene que mantener los elementos ordenados, LinkedHashMapdebería ser más rápido en la mayoría de los casos; TreeMaptiene O(log n)rendimiento para containsKey, get, put, y remove, de acuerdo con la Javadocs, mientras que LinkedHashMapes O(1)para cada uno.

Si su API que solo espera un orden de clasificación predecible, a diferencia de un orden de clasificación específico, considere usar las interfaces que implementan estas dos clases, NavigableMapo SortedMap. Esto le permitirá no filtrar implementaciones específicas en su API y cambiar a cualquiera de esas clases específicas o una implementación completamente diferente a voluntad después.

Michael Myers
fuente
2
Esto no funcionará para mí porque, según javadocs, solo da valores ordenados (a través de la llamada a valores ()). ¿Hay alguna manera de obtener instancias de Map.Entry ordenadas?
Cory Kendall
1
@CoryKendall: ¿TreeMap no funciona? Se supone que está ordenado por claves, no por valores.
Michael Myers
1
Mi error, pensé que los Sets no estaban clasificados.
Cory Kendall
61
Tenga en cuenta: la clasificación de un TreeMap se basa en el orden natural de las claves: "El mapa se clasifica de acuerdo con el orden natural de sus claves". LinkedHashMap se ordena por orden de inserción. ¡Gran diferencia!
Jop van Raaij
3
@AlexR: Eso solo es cierto si LinkedHashMap se creó utilizando el constructor especial que se proporciona para ese propósito. Por defecto, la iteración está en orden de inserción.
Michael Myers
22

LinkedHashMap devolverá los elementos en el orden en que se insertaron en el mapa cuando itera sobre keySet (), entrySet () o valores () del mapa.

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

Esto imprimirá los elementos en el orden en que se colocaron en el mapa:

id = 1
name = rohan 
age = 26 
Praveen Kishor
fuente
16

Si un mapa inmutable se ajusta a sus necesidades, entonces hay una biblioteca de google llamada guava (vea también las preguntas sobre guayaba )

Guava proporciona un ImmutableMap con un orden de iteración confiable especificado por el usuario. Este ImmutableMap tiene un rendimiento O (1) para contieneKey, get. Obviamente, poner y quitar no son compatibles.

Los objetos ImmutableMap se construyen utilizando los elegantes métodos de conveniencia estática de () y copyOf () o un objeto Builder .

jvdneste
fuente
6

Puede mantener un Map(para una búsqueda rápida) y List(para un pedido) pero a LinkedHashMappuede ser el más simple. También puede probar un SortedMapeg TreeMap, que tiene cualquier orden que especifique.

Peter Lawrey
fuente
1

No sé si es de código abierto, pero después de buscar un poco en Google, encontré esta implementación de Map usando ArrayList . Parece ser Java anterior a 1.5, por lo que es posible que desee genérico, lo que debería ser fácil. Tenga en cuenta que esta implementación tiene acceso O (N), pero esto no debería ser un problema si no agrega cientos de widgets a su JPanel, que de todos modos no debería agregar.

jpalecek
fuente
1

Siempre que necesito mantener el orden natural de las cosas que se conocen con anticipación, uso un EnumMap

las claves serán enumeraciones y puede insertarlas en el orden que desee, pero cuando itera, iterará en el orden de enumeración (el orden natural).

Además, cuando se utiliza EnumMap no debe haber colisiones que puedan ser más eficientes.

Realmente encuentro que usar enumMap crea un código limpio y legible. Aquí hay un ejemplo

j2emanue
fuente
1

Puede usar LinkedHashMap para el orden de inserción principal en el Mapa

Los puntos importantes sobre la clase Java LinkedHashMap son:

  1. Contiene solo elementos únicos.
  2. Un LinkedHashMap contiene valores basados ​​en la clave 3. Puede tener una clave nula y múltiples valores nulos. 4. Es lo mismo que HashMap en su lugar mantiene el orden de inserción

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

Pero si desea ordenar los valores en el mapa usando un objeto definido por el usuario o cualquier clave de tipo de datos primitiva, entonces debe usar TreeMap. Para obtener más información, consulte este enlace

Pankaj Goyal
fuente
0

Puede usar LinkedHashMap<K, V>o puede implementar su propio CustomMap que mantiene el orden de inserción.

Puede usar el siguiente CustomHashMapcon las siguientes características:

  • El orden de inserción se mantiene utilizando LinkedHashMap internamente.
  • nullNo se permiten llaves con o cadenas vacías.
  • Una vez que se crea la clave con valor, no estamos anulando su valor.

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

Uso de 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             |

Si sabe que las CLAVES son fijas, puede usar EnumMap. Obtener los valores de las propiedades / archivos 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");
Yash
fuente