【问题标题】:Java Class that implements Map and keeps insertion order?实现 Map 并保持插入顺序的 Java 类?
【发布时间】:2010-10-15 13:45:50
【问题描述】:

我正在寻找具有键值关联但不使用散列的 Java 类。这是我目前正在做的事情:

  1. 将值添加到Hashtable
  2. 获取Hashtable.entrySet() 的迭代器。
  3. 遍历所有值并:
    1. 为迭代器获取Map.Entry
    2. 根据值创建Module(自定义类)类型的对象。
    3. 将类添加到 JPanel。
  4. 显示面板。

问题在于我无法控制取回值的顺序,因此我无法按给定顺序显示值(没有硬编码顺序)。

我会为此使用ArrayListVector,但稍后在代码中我需要为给定的键获取Module 对象,而ArrayList 或@987654330 无法做到这一点@。

有没有人知道可以执行此操作的免费/开源 Java 类,或者根据添加时间从 Hashtable 中获取值的方法?

谢谢!

【问题讨论】:

  • 你不需要使用entryset/map.entry。您可以使用 hashtable.keys 作为枚举或使用 hashtable.keyset.iterator 来迭代键和值。
  • 我冒昧地更改了标题,因为不使用哈希实际上不是问题,而是保持插入顺序。
  • 类似问题,Java Ordered Map

标签: java dictionary key-value


【解决方案1】:

您可以使用 LinkedHashMap<K, V> 或者您可以实现自己的 CustomMap 来维护插入顺序。

您可以使用具有以下功能的以下 CustomHashMap

  • 通过在内部使用 LinkedHashMap 来维护插入顺序。
  • 不允许使用带有null 或空字符串的键。
  • 一旦创建了带值的键,我们就不会覆盖它的值。

HashMap vs LinkedHashMap vs 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 文件中获取值

前:

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");

【讨论】:

    【解决方案2】:

    您可以使用 LinkedHashMap 到 Map 中的主要插入顺序

    关于Java LinkedHashMap类的重点是:

    1. 它只包含独特的元素。
    2. LinkedHashMap 包含基于键的值 3.它可能有一个空键和多个空值。 4.和HashMap一样,而是维护插入顺序

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

    但是,如果您想使用用户定义的对象或任何原始数据类型键对映射中的值进行排序,那么您应该使用 TreeMap 更多信息,请参阅this link

    【讨论】:

      【解决方案3】:

      每当我需要维护提前知道的事物的自然顺序时,我都会使用EnumMap

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

      此外,在使用 EnumMap 时,应该没有冲突,这样会更有效。

      我真的发现使用 enumMap 可以生成清晰可读的代码。 这是example

      【讨论】:

        【解决方案4】:

        如果不可变地图满足您的需求,那么谷歌提供了一个名为 guava 的库(另请参阅 guava questions

        Guava 提供具有可靠的用户指定迭代顺序的ImmutableMap。这个ImmutableMap 对 containsKey 有 O(1) 的性能,获取。显然不支持放置和删除。

        ImmutableMap 对象是使用优雅的静态便捷方法of()copyOf()Builder 对象构造的。

        【讨论】:

          【解决方案5】:

          当您遍历地图的 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(用于订购),但LinkedHashMap 可能是最简单的。您也可以尝试SortedMap,例如TreeMap,有你指定的任何顺序。

            【讨论】:

              【解决方案7】:

              我建议LinkedHashMapTreeMapLinkedHashMap 保持键的插入顺序,而TreeMap 保持通过Comparator 或自然的Comparable 元素排序。

              由于它不必保持元素排序,LinkedHashMap 在大多数情况下应该更快;根据 Javadocs,TreeMapcontainsKeygetputremove 具有 O(log n) 性能,而 LinkedHashMapO(1)

              如果您的 API 只需要可预测的排序顺序,而不是特定的排序顺序,请考虑使用这两个类实现的接口,NavigableMapSortedMap。这将允许您不会将特定实现泄漏到您的 API 中,并在之后随意切换到这些特定类或完全不同的实现。

              【讨论】:

              • 这对我不起作用,因为根据 javadocs,这只会给出有序值(通过 values() 调用)。有没有办法获得有序的 Map.Entry 实例?
              • @CoryKendall:TreeMap 不起作用吗?它应该按键排序,而不是按值排序。
              • 请注意:TreeMap 的排序是基于键的自然顺序:“地图是根据其键的自然顺序排序的”。 LinkedHashMap 是按插入顺序排序的。大不同!
              • 我相信LinkedHashMap 没有实现NavigableMapSortedMap
              • @AlexR:仅当 LinkedHashMap 是使用为此目的提供的special constructor 创建时才成立。默认情况下,迭代是按插入顺序进行的。
              【解决方案8】:

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

              【讨论】:

                猜你喜欢
                • 2011-09-21
                • 2012-03-08
                • 1970-01-01
                • 2021-08-19
                • 2010-12-09
                • 2013-10-21
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多