Класс Java, который реализует Map и сохраняет порядок вставки?

463

Я ищу класс в Java, который имеет связь ключ-значение, но без использования хэшей. Вот что я сейчас делаю:

  1. Добавить значения в Hashtable.
  2. Получить итератор для Hashtable.entrySet().
  3. Переберите все значения и:
    1. Получить Map.Entryдля итератора.
    2. Создайте объект типа Module(пользовательский класс) на основе значения.
    3. Добавьте класс в JPanel.
  4. Показать панель.

Проблема в том, что у меня нет контроля над порядком получения значений, поэтому я не могу отобразить значения в заданном порядке (без жесткого кодирования порядка).

Я бы использовал ArrayListили Vectorдля этого, но позже в коде мне нужно получить Moduleобъект для данного ключа, что я не могу сделать с ArrayListили Vector.

Кто-нибудь знает о свободном / с открытым исходным кодом Java-классе, который это сделает, или о способе получения значений на Hashtableоснове того, когда они были добавлены?

Спасибо!

Шейн
источник
1
Вам не нужно использовать entryset / map.entry. Вы можете перебирать ключи и значения, используя hashtable.keys в качестве перечисления или используя hashtable.keyset.iterator.
Джон Гарднер
5
Я позволил себе сменить заголовок, поскольку проблема не в использовании хешей, а в сохранении порядка вставки.
Иоахим Зауэр
Аналогичный вопрос, Java заказал карту
Василий Бурк

Ответы:

734

Я предлагаю LinkedHashMapили TreeMap. A LinkedHashMapсохраняет ключи в том порядке, в котором они были вставлены, в то время как a TreeMapсортируется с помощью Comparatorили естественного Comparableупорядочения элементов.

Поскольку элементы не LinkedHashMapдолжны сортироваться, в большинстве случаев они должны выполняться быстрее; TreeMapимеет O(log n)производительность containsKey, get, put, и remove, в соответствии с Javadocs, в то время как LinkedHashMapэто O(1)для каждого.

Если ваш API, который ожидает только предсказуемый порядок сортировки, в отличие от определенного порядка сортировки, рассмотрите возможность использования интерфейсов, которые реализуют эти два класса, NavigableMapили SortedMap. Это позволит вам не пропускать конкретные реализации в ваш API и впоследствии переключаться на эти конкретные классы или на совершенно другую реализацию.

Майкл Майерс
источник
2
Это не будет работать для меня, потому что, согласно javadocs, это дает только упорядоченные значения (через вызов values ​​()). Есть ли способ получить заказанные экземпляры Map.Entry?
Кори Кендалл
1
@CoryKendall: TreeMap не работает? Он должен быть отсортирован по ключам, а не по значениям.
Майкл Майерс
1
Моя ошибка, я думал, что наборы были не отсортированы.
Кори Кендалл
61
Обратите внимание: сортировка TreeMap основана на естественном порядке ключей: «Карта сортируется в соответствии с естественным порядком ее ключей». LinkedHashMap сортируется в порядке вставки. Большая разница!
Джоп ван Раайдж
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

Если неизменяемая карта соответствует вашим потребностям, есть библиотека от Google, которая называется guava (см. Также вопросы по guava )

Guava предоставляет ImmutableMap с надежным заданным пользователем порядком итераций. Этот ImmutableMap имеет производительность O (1) для containsKey, get. Очевидно, положить и удалить не поддерживаются.

Объекты ImmutableMap создаются с использованием элегантных статических вспомогательных методов () и copyOf () или объекта Builder .

jvdneste
источник
6

Вы можете поддерживать Map(для быстрого поиска) и List(для заказа), но LinkedHashMapможет быть самым простым. Вы также можете попробовать, SortedMapнапример TreeMap, какой-либо заказ вы указали.

Питер Лори
источник
1

Я не знаю, является ли это открытым исходным кодом, но после небольшого поиска в Google я нашел эту реализацию Map, используя ArrayList . Похоже, что это Java до версии 1.5, так что вы можете обобщить ее, что должно быть легко. Обратите внимание, что эта реализация имеет O (N) доступ, но это не должно быть проблемой, если вы не добавляете сотни JPG-элементов в свой виджет, что в любом случае не следует.

jpalecek
источник
1

Всякий раз, когда мне нужно поддерживать естественный порядок вещей, которые известны заранее, я использую EnumMap

ключи будут перечислениями, и вы можете вставлять их в любом порядке, который вы хотите, но при повторении он будет повторяться в порядке перечисления (естественный порядок).

Также при использовании EnumMap не должно быть столкновений, которые могут быть более эффективными.

Я действительно считаю, что использование enumMap делает для чистого читаемого кода. Вот пример

j2emanue
источник
1

Вы можете использовать LinkedHashMap для основного порядка вставки в карте

Важными моментами о классе Java LinkedHashMap являются:

  1. Содержит только уникальные элементы.
  2. LinkedHashMap содержит значения, основанные на ключе 3. Он может иметь один нулевой ключ и несколько нулевых значений. 4. Он такой же, как HashMap, вместо этого поддерживает порядок вставки

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

Но если вы хотите отсортировать значения в карте, используя определяемый пользователем объект или любой примитивный ключ типа данных, то вам следует использовать TreeMap. Для получения дополнительной информации перейдите по этой ссылке.

Панкадж Гоял
источник
0

Либо Вы можете использовать, LinkedHashMap<K, V>либо можете реализовать свою собственную CustomMap, которая поддерживает порядок вставки.

Вы можете использовать следующее CustomHashMapсо следующими функциями:

  • Порядок вставки поддерживается внутренним использованием LinkedHashMap.
  • Ключи с null или пустыми строками не допускаются.
  • Как только ключ со значением создан, мы не переопределяем его значение.

HashMapпротив LinkedHashMapпротивCustomHashMap

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 файлы

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");
Яши
источник