【发布时间】:2017-05-04 11:13:40
【问题描述】:
我正在尝试过滤/减少其中包含一些重复条目的数据流。
本质上,我试图找到比我实现的更好的过滤一组数据的解决方案。我们的数据基本上是这样的:
Action | Date | Detail
15 | 2016-03-15 |
5 | 2016-03-15 | D1
5 | 2016-09-25 | D2 <--
5 | 2016-09-25 | D3 <-- same day, different detail
4 | 2017-02-08 | D4
4 | 2017-02-08 | D5
5 | 2017-03-01 | D6 <--
5 | 2017-03-05 | D6 <-- different day, same detail; need earliest
5 | 2017-03-08 | D7
5 | 2017-03-10 | D8
...
我需要提取这样的细节:
- 仅选择操作 5
- 如果详细信息相同(例如,D6 在不同的日子出现两次),则选择最早的日期
这些数据被加载到对象中(每个“记录”一个实例),对象上还有其他字段,但它们与此过滤无关。 Detail 存储为 String,Date 存储为 ZonedDateTime,Action 是 int(实际上是 enum,但这里显示为 int)。这些对象按时间顺序以List<Entry> 给出。
我能够通过以下方式获得一个有效但我认为不是最佳的解决方案:
List<Entry> entries = getEntries(); // retrieved from a server
final Set<String> update = new HashSet<>();
List<Entry> updates =
entries.stream()
.filter(e -> e.getType() == 5)
.filter(e -> pass(e, update))
.collect(Collectors.toList());
private boolean pass(Entry ehe, Set<String> update)
{
final String val = ehe.getDetail();
if (update.contains(val)) { return false; }
update.add(val);
return true;
}
但问题是我必须使用这个pass() 方法并在其中检查Set<String> 来维护是否已经处理了给定的详细信息。虽然这种方法有效,但似乎应该可以避免外部引用。
我尝试在详细信息上使用groupingBy,它允许从列表中提取最早的条目,问题是我不再有日期排序,我必须处理生成的Map<String,List<Entry>>。
在不使用pass() 方法的情况下,这里似乎可以进行一些减少操作(如果我正确使用了该术语),但我正在努力获得更好的实现。
有什么更好的方法可以删除.filter(e -> pass(e, update))?
谢谢!
【问题讨论】:
-
它几乎是Java Stream: get latest version of user records的副本。看看你是否可以使用my answer there解决它。或其他答案之一。
-
@OleV.V.,我去看看。我在搜索时没有看到此问答。
-
虽然确实不鼓励在 Streams 中使用这种
pass方法(并且您有更好的解决方案的答案),但处理Sets 的一般说明:Set.add已经定义作为“如果不存在则添加”,它将返回值是否已添加,因此,您可以使用return update.add(val);代替if (update.contains(val)) { return false; } update.add(val); return true;进行两次哈希查找,更短更高效。 -
@Holger,关于
Set和update.add()的要点很好。显然,我专注于流问题而没有清楚地考虑Set。我感谢详细的批评!
标签: java java-8 java-stream