【问题标题】:Sorting a HashMap by date按日期对 HashMap 进行排序
【发布时间】: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.

标签: java sorting hashmap xpages


【解决方案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】:
  1. 调用Map的entrySet()方法获取所有Entries

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

  3. Entry设置为List

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

  5. 通过按排序顺序添加条目来创建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 之类的辅助数据结构(在日期上充当堆)是完全合理的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-10-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-09-22
      相关资源
      最近更新 更多