我有一个包含日期和金额的付款交易的数据集。我将这些存储在地图数据结构中,其中日期为关键,数量为值。
由于每个日期可能有多笔付款,我使用Google Guava库中的Multimap。
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MM/dd/yyyy");
Multimap<LocalDate,BigDecimal> payments = ArrayListMultimap.create();
payments.put(LocalDate.parse("12/25/2016",formatter), new BigDecimal("1000"));
payments.put(LocalDate.parse("01/15/2017",formatter), new BigDecimal("250"));
payments.put(LocalDate.parse("01/25/2017",formatter), new BigDecimal("500"));
payments.put(LocalDate.parse("03/20/2017",formatter), new BigDecimal("500"));
payments.put(LocalDate.parse("04/15/2017",formatter), new BigDecimal("1000"));
payments.put(LocalDate.parse("06/15/2017",formatter), new BigDecimal("1000"));
根据日期范围过滤此地图的推荐方法是什么?
例如,显示3/2 / 2017-3 / 31/2017之间的条目。
答案 0 :(得分:3)
Multimaps#filterKeys
方法允许您通过与您编码的任意Predicate
匹配的键过滤现有Multimap
。它的返回值是Multimap
,它只包含满足过滤谓词的条目。
首先,让我们定义一个辅助方法,可以创建Predicate
来检查日期是否在指定范围之间。
private static Predicate<LocalDate> between(final LocalDate begin, final LocalDate end) {
return new Predicate<LocalDate>() {
@Override
public boolean apply(LocalDate date) {
return (date.compareTo(begin) >= 0 && date.compareTo(end) <= 0);
}
};
}
之后,您可以使用谓词来过滤所需的范围。
void testFilterPayments() {
Multimap<LocalDate, BigDecimal> payments = ArrayListMultimap.create();
payments.put(LocalDate.parse("2016-12-25"), new BigDecimal("1000"));
payments.put(LocalDate.parse("2017-01-15"), new BigDecimal("250"));
payments.put(LocalDate.parse("2017-01-15"), new BigDecimal("1250"));
payments.put(LocalDate.parse("2017-01-15"), new BigDecimal("2250"));
payments.put(LocalDate.parse("2017-01-25"), new BigDecimal("500"));
payments.put(LocalDate.parse("2017-03-20"), new BigDecimal("500"));
payments.put(LocalDate.parse("2017-04-15"), new BigDecimal("1000"));
payments.put(LocalDate.parse("2017-06-15"), new BigDecimal("1000"));
System.out.println(Multimaps.filterKeys(payments,
between(LocalDate.parse("2017-01-01"), LocalDate.parse("2017-04-01"))));
// Output:
// {2017-01-25=[500], 2017-03-20=[500], 2017-01-15=[250, 1250, 2250]}
System.out.println(Multimaps.filterKeys(payments,
between(LocalDate.parse("2017-01-01"), LocalDate.parse("2017-01-15"))));
// Output:
// {2017-01-15=[250, 1250, 2250]}
System.out.println(Multimaps.filterKeys(payments,
between(LocalDate.parse("2016-01-01"), LocalDate.parse("2017-12-31"))));
// Output:
// {2017-06-15=[1000], 2017-01-25=[500], 2017-03-20=[500], 2016-12-25=[1000], 2017-01-15=[250, 1250, 2250], 2017-04-15=[1000]}
System.out.println(Multimaps.filterKeys(payments,
between(LocalDate.parse("2001-01-01"), LocalDate.parse("2015-12-31"))));
// Output:
// {}
}
我已简化此示例以使用默认日期格式进行解析。您的原始示例似乎使用自定义formatter
,但所有相同的技术都适用于谓词过滤。
我的谓词实现与begin
和end
的包含范围匹配。如果您的要求略有不同(例如排名范围,结果中不包含end
),那么您可以相应地调整apply
的实施。
请注意Multimap
方法返回的filterKeys
实例的JavaDocs中提到的一些详细信息,例如:
返回的多图是未过滤的实时视图;改变为一个影响另一个。
...
生成的多地图视图的迭代器不支持remove()...
...
返回的多图不是线程安全的或可序列化的,即使未经过滤也是如此。
...
许多过滤后的multimap的方法(例如size())遍历底层多图中的每个键/值映射,并确定哪个满足过滤器。当不需要实时视图时,复制过滤后的多图并使用副本可能会更快。
作为补充说明,通过更改方法签名以使用接受各种between
类型的泛型而不仅仅是Comparable
,可以使LocalDate
谓词更加灵活。
private static <T extends Comparable<? super T>> Predicate<T> between(final T begin, final T end) {
return new Predicate<T>() {
@Override
public boolean apply(T value) {
return (value.compareTo(begin) >= 0 && value.compareTo(end) <= 0);
}
};
}
答案 1 :(得分:2)
您需要对Multimap
进行排序,否则您必须全部迭代。幸运的是,有这样一个多图:
ListMultimap<LocalDate, BigDecimal> multimap =
MultimapBuilder.treeKeys().arrayListValues().build();
... fill
// This cast is safe.
SortedMap<LocalDate, Collection<BigDecimal>> asMap =
(SortedMap<LocalDate, Collection<BigDecimal>>) multimap.asMap();
SortedMap<LocalDate, Collection<BigDecimal>> subMap =
asMap.subMap(from, to);
for (Map.Entry<LocalDate, Collection<BigDecimal>> e : subMap.entrySet()) {
...
}