按日期对 HashMap 进行排序

Posted

技术标签:

【中文标题】按日期对 HashMap 进行排序【英文标题】:Sorting a HashMap by date 【发布时间】:2015-12-04 12:15:18 【问题描述】:

在 Java 类中,我有一个按日期重新排序现有 HashMap 的方法。 HashMap 的类型为<String, Object>,其中 Object 包含一个名为 expPayDate 的字段,而键字符串是一个序列号,变成了一个字符串。所以我需要遍历 sourceMap 中的项目并找到具有最新日期的项目然后以正确的顺序将其复制到 tempMap 中。我的问题是确定具有最新日期的项目的最佳方法是什么。

【问题讨论】:

HashMaps 没有顺序 您应该查看已排序的地图。 docs.oracle.com/javase/8/docs/api/java/util/SortedMap.html 你的泛型类型真的是Object 还是我们可以假设它是MyClass 之类的东西?我在问,因为Object 在它的接口方法中没有允许我们获取expPayDate,而MyClass 可能包含类似getExpPayDate() 的东西。 坚持使用非 Java 8 解决方案,因为我们还不能使用 Java 6 以外的任何东西。将您的 Map 重新定义为 TreeMap 或使用 Comparator 对我来说最有意义。如果您有一个列表形式的项目集合,我想知道 List 是否会更好地满足您的需求。根据您要执行的操作,您可能希望查看 each of the Java Collections do for you 的内容。关于将 ComparatorList 结合使用,I blogged about that in January. 【参考方案1】:

最好的办法是使用带有Comparator 接口的SortedMap。

这是一个例子:

public SortedMap<String, Object> getSortedMap(Map<String, Object> originalMap) 
    SortedMap<String, Object> tmpMap = new TreeMap<String, Object>(new Comparator<String>()
        @Override
        public int compare(String key1, String key2) 
            //logic for comparing dates
                   
    );
    tmpMap.putAll(originalMap);
    return tmpMap;

【讨论】:

值得注意的是,我们在 TreeMap 中使用的 Comparator 用于比较 keys,而不是 values,也不是 dates。我们需要每次使用 key 来获取 value,读取它的 date 属性。然后我们可以使用相同的第二个键和比较日期。 @Pshemo 不错。我将答案的泛型类型更改为使用 String 而不是 Object。 您也可以将compare方法参数的名称更改为key1key2【参考方案2】:

使用 TreeMap 而不是 HashMap。插入时会自动排序。

Map< Date, Object> m = new TreeMap< Date, Object>();

或者,如果您有一个现有的 HashMap 并希望基于它创建一个 TreeMap,请将其传递给构造函数:

Map< Date, Object> sortedMap = new TreeMap< Date, Object>(m);

希望对你有所帮助。

【讨论】:

日期是值的属性,而不是键。 我使用 HashMap 是因为我根据 ViewEntryCollection 的排序顺序加载它,但为了便于访问,我在 HashMap 中使用它并带有序号。我这样做是因为我使用 HashMap 作为重复控件的数据源。所以我可以使用重复中的索引来获取正确的 HashMap。因此,当我启动 Hashmap 时,它的顺序是正确的,但是如果我向 HashMap 添加一个新的“行项目”,它就会出现在列表的末尾。我不想从磁盘重新加载 HashMap。 所以我的解决方案是从 HashMap 中读取每个“PaymentItem”并将其存储在一个 TreeMap 的 tempMap 中。现在我的项目顺序正确,然后使用顺序键将它们复制回原始 HashMap 中。因为这一切都是在内存中完成的,所以它看起来非常快,并且不会重复读回服务器。上面的其他一些建议可能更有效,但这似乎效果很好。感谢您的所有反馈,非常感谢。【参考方案3】:

为简单起见,我假设您的地图类型更像Map&lt;String, MyClass&gt; map,其中MyClass 的方法类似于getDate(),它返回expPayDate

我的问题是确定最新日期的项目的最佳方法是什么。

如果您想查找 value 包含最大日期的单个地图条目 您不需要对整个地图进行排序,这最多会给您 O(n*logn )。您需要的是对 map 中的所有元素进行简单迭代,并将它们与当前最大值进行比较,这将是 O(n) 操作。

您可以使用stream()(Java 8 中添加的功能)及其max 方法。此方法需要Comparator,您可以通过使用comparing 方法并传递lambda 表达式轻松创建一个,该表达式将返回值,比较时应使用该值。

所以你的代码看起来像

//import java.util.Map.Entry;

Optional<Entry<String, MyClass>> max = map.entrySet().stream()
        .max(Comparator.comparing(e -> e.getValue().getDate()));

Entry<String, MyClass> entry = max.get();
MyClass maxMC = entry.getValue();

如果您不能使用 Java 8,您可以编写自己的方法来迭代元素并找到最大值。这种方法看起来像

public static <T> T max(Iterable<T> iterable, Comparator<T> comp) 
    Iterator<T> it = iterable.iterator();
    T max = null;
    if (it.hasNext()) 
        max = it.next();
    
    while (it.hasNext()) 
        T tmp = it.next();
        if (comp.compare(max, tmp) < 0)
            max = tmp;
    
    return max;

你可以像使用它一样

Comparator<Entry<String, MyClass>> myComparator = new Comparator<Entry<String, MyClass>>() 
    @Override
    public int compare(Entry<String, MyClass> o1, Entry<String, MyClass> o2) 
        return o1.getValue().getDate().compareTo(o2.getValue().getDate());
    
;
Entry<String, MyClass> maxEntry = max(map.entrySet(), myComparator);
MyClass max = maxEntry.getValue();

【讨论】:

OP 的问题是关于 JEE 实现,具体是 1.6(目前)。 XPages 运行时(还)不能从任何 Java 8 实现中受益。我收回了我的否决票,因为这对于非 XPages 开发人员来说并不明显,并且是平台所独有的,但我相信这为所有 Java 实现提供了一些合理的优点,并强调了将运行时更新到更高版本的 Java 的必要性版本。很好的输入,但@Bill 将无法使用它。 @EricMcCormick 如果问题仅出在 Java 8 上,那么这意味着没有真正的问题,因为该解决方案可以轻松地重写为旧版本的 Java。我的回答的主要部分是表明我们实际上不需要创建分离的排序映射(这将是 O(n*logn) 位,以防找到最大值,我们只需遍历所有元素一次,即 O(n) )。我将为旧版本的 Java 重写我的代码。 别担心,我同意。这是一个完全合理的答案……对于大多数情况;我对该平台的不满之一(目前,我们已经得到承诺会推出一个更新的 JVM)。【参考方案4】:

    调用Map的entrySet()方法获取所有Entries

    创建自定义 Comparator 以根据值对条目进行排序

    Entry设置为List

    通过传递您的值比较器,使用Collections.sort() 方法对条目列表进行排序

    通过按排序顺序添加条目来创建LinkedHashMap

看示例代码@Sort HasMap by value

【讨论】:

不要对未引用的文本使用引号格式。【参考方案5】:

如果您只需要最小或最大日期,一个简单的 for each 循环可能就足够了:

Date maxDate = null;
for (Entry<String, Object> item: hashMap.entrySet()) 
    if (maxDate == null || maxDate before((Date)item.getValue())) 
        maxDate = (Date)item.getValue();

这种方式的复杂度仅为 O(n),并且插入和删除操作比使用 sortedMap 便宜。无论如何,我认为 patstuart 的建议(使用sortedMap)更优雅。

【讨论】:

【参考方案6】:

正确的解决方案取决于您的性能限制。

如果您的问题只是找到具有最新日期的项目,那么如果 O(n) 性能还可以,您可以扫描 HashMap 中的 values() 并找到最小值。

这取决于相对于对数据结构的其他访问,您需要多久执行一次此操作。根据您对该数据结构的访问模式,使用 SortedMap 或使用诸如 PriorityQueue 之类的辅助数据结构(在日期上充当堆)是完全合理的。

【讨论】:

以上是关于按日期对 HashMap 进行排序的主要内容,如果未能解决你的问题,请参考以下文章

按字母顺序对HashMap程序的输出进行排序[重复]

hashmap按value排序

在 Android 中按字母顺序对 Hashmap 值类型进行排序

Java 如何对 HashMap 或 HashTable 中的项目进行排序?

如何按整数值对哈希图进行排序[重复]

Java HashMap 按键插入时间排序 - 创意顺序 [重复]