实现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
我自由更改标题,因为实际上不使用散列并不是问题,而是保持插入顺序。
Joachim Sauer,2009年

相似的问题,Java命令地图
Basil Bourque,

Answers:


734

我建议一个LinkedHashMap或一个TreeMap。A LinkedHashMap保持键的插入顺序,而a TreeMap则通过元素ComparatorComparable元素的自然顺序进行排序。

由于不必对元素进行排序,LinkedHashMap因此在大多数情况下应该更快。TreeMapO(log n)表现为containsKeygetput,和remove,根据的Javadoc,而LinkedHashMapO(1)对每个。

如果您的API仅期望可预测的排序顺序,而不是特定的排序顺序,请考虑使用这两个类实现的接口,NavigableMapSortedMap。这样一来,您就不会将特定的实现泄漏到您的API中,之后便可以随意切换到那些特定的类或完全不同的实现。


2
这对我不起作用,因为根据javadocs,这只能给出有序的值(通过values()调用)。有没有办法获得有序的Map.Entry实例?
科里·肯德尔

1
@CoryKendall:TreeMap不起作用吗?它应该按键而不是按值排序。
迈克尔·迈尔斯

1
我的错,我以为Sets没有排序。
科里·肯德尔

61
请注意:TreeMap的排序基于键的自然顺序:“地图是根据其键的自然顺序排序的”。LinkedHashMap按插入顺序排序。很大的区别!
Jop van Raaij 2013年

3
@AlexR:只有使用为此目的提供的特殊构造函数创建LinkedHashMap时,这才是正确的。默认情况下,迭代按插入顺序进行。
迈克尔·迈尔斯

22

当您遍历地图的keySet(),entrySet()或values()时,LinkedHashMap将按插入地图的顺序返回元素。

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 


6

您可以维护Map(用于快速查找)和List(用于订单),但是a LinkedHashMap可能是最简单的。您也可以尝试使用SortedMapeg TreeMap,它具有您指定的任何顺序。


1

我不知道它是否是开源的,但是经过一番谷歌搜索之后,我发现了使用ArrayList的Map实现。它似乎是Java 1.5之前的版本,因此您可能希望对其进行泛化,这应该很容易。请注意,此实现具有O(N)访问权限,但是如果您不向JPanel添加数百个小部件,这也不是问题,无论如何都不应该这样做。



1

每当我需要保持事物的自然顺序时,我都会使用EnumMap

键将是枚举,您可以按所需的任何顺序插入,但是当您进行迭代时,它将以枚举顺序(自然顺序)进行迭代。

同样,在使用EnumMap时,不应存在任何会更有效的冲突。

我真的发现使用enumMap可以使代码清晰可读。这是一个例子


1

您可以使用LinkedHashMap来映射Map中的主要插入顺序

关于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不允许或为空字符串的。
  • 创建具有值的键后,我们不会覆盖其值。

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");
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.