Kev*_*inO 7 java java-8 java-stream
我试图过滤/减少其中包含一些重复条目的数据流.
从本质上讲,我试图找到一个比我实现的更好的过滤一组数据的解决方案.我们的数据在其基础上是这样的:
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
...
Run Code Online (Sandbox Code Playgroud)
我需要提取细节,以便:
这些数据被加载到对象中(每个"记录"一个实例),并且对象上还有其他字段,但它们与此过滤无关.细节存储为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;
}
Run Code Online (Sandbox Code Playgroud)
但问题是我必须使用这种pass()方法并在其中检查a Set<String>以维护给定的细节是否已经处理完毕.虽然这种方法有效,但似乎应该可以避免外部引用.
我尝试groupingBy在详细信息上使用a ,它将允许从列表中提取最早的条目,问题是我不再有日期排序,我不得不处理结果Map<String,List<Entry>>.
似乎有些减少操作(如果我正确地使用了这个术语)这里没有使用该pass()方法应该是可能的,但我正在努力获得更好的实现.
什么是更好的方法,以便.filter(e -> pass(e, update))可以删除?
谢谢!
在这个答案的两个解决方案,其中第二个明显更快.
Collection<Entry> result =
entries.stream().filter(e -> e.getAction() == 5)
.collect(Collectors.groupingBy(Entry::getDetail, Collectors.collectingAndThen(Collectors.minBy(Comparator.comparing(Entry::getDate)), Optional::get)))
.values();
Run Code Online (Sandbox Code Playgroud)
使用您的示例数据集(我选择GMT + 0作为时区):
Entry [action=5, date=2017-03-01T00:00Z[GMT], detail=D6]
Entry [action=5, date=2017-03-08T00:00Z[GMT], detail=D7]
Entry [action=5, date=2017-03-10T00:00Z[GMT], detail=D8]
Entry [action=5, date=2016-03-15T00:00Z[GMT], detail=D1]
Entry [action=5, date=2016-09-25T00:00Z[GMT], detail=D2]
Entry [action=5, date=2016-09-25T00:00Z[GMT], detail=D3]
Run Code Online (Sandbox Code Playgroud)
如果你坚持要List回来:
List<Entry> result = new ArrayList<>(entries.stream() ..... .values());
Run Code Online (Sandbox Code Playgroud)
如果您想获得原始订单,请使用3参数groupingBy:
...groupingBy(Entry::getDetail, LinkedHashMap::new, Collectors.collectingAndThen(...))
Run Code Online (Sandbox Code Playgroud)
使用toMap,更容易阅读,更快(请参阅holi-java对此答案的评论,以及下一个'部分'):
List<Entry> col = new ArrayList<>(
entries.stream().filter(e -> e.getAction() == 5)
.collect(Collectors.toMap(Entry::getDetail, Function.identity(), (a,b) -> a.getDate().compareTo(b.getDate()) >= 0 ? b : a))
.values());
Run Code Online (Sandbox Code Playgroud)
哪里(a,b) -> a.getDate().compareTo(b.getDate()) >= 0 ? b : a可以替代:
BinaryOperator.minBy(Comparator.comparing(Entry::getDate))
Run Code Online (Sandbox Code Playgroud)
如果您想在此解决方案中获得原始订单,请使用4参数toMap:
...toMap(Entry::getDetail, Function.identity(), (a,b) -> a.getDate().compareTo(b.getDate()) >= 0 ? b : a, LinkedHashMap::new)
Run Code Online (Sandbox Code Playgroud)
使用我为测试我的解决方案而创建的testdata,我检查了两个解决方案的运行时间.第一个解决方案平均需要67毫秒(仅运行20次,所以不要相信数字!),第二个解决方案平均需要2毫秒.如果有人想进行适当的性能比较,请将结果放在评论中,我会在此处添加.