任务:
我有2个包含Entrys的列表(Id + DateTime) 具有不同日期时间的ID可以是多个。
我需要一个ID列表,其中包含以下条件:
问题: 我怎么能用Java 8 Streams做到这一点?
ExampleCode:
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;
public class LambdaFilter
{
public static void main(final String[] args)
{
final LambdaFilter lf = new LambdaFilter();
lf.start();
}
private void start()
{
final List<Entry> list1 = Arrays.asList(
new Entry(15, new DateTime(2012, 6, 29, 0, 0, 0, 0)),
new Entry(101, new DateTime(2012, 3, 12, 0, 0, 0, 0)),
new Entry(101, new DateTime(2012, 3, 12, 0, 0, 0, 0)),
new Entry(68691, new DateTime(2015, 2, 12, 0, 0, 0, 0)),
new Entry(68691, new DateTime(2015, 2, 12, 0, 0, 0, 0)),
new Entry(68691, new DateTime(2015, 5, 01, 0, 0, 0, 0)),
new Entry(70738, new DateTime(2016, 1, 26, 0, 0, 0, 0)));
final List<Entry> list2 = Arrays.asList(
new Entry(15, new DateTime(2012, 6, 29, 0, 0, 0, 0)),
new Entry(101, new DateTime(2012, 3, 12, 0, 0, 0, 0)),
new Entry(68691, new DateTime(2015, 2, 12, 0, 0, 0, 0)),
new Entry(68691, new DateTime(2015, 2, 12, 0, 0, 0, 0)),
new Entry(70738, new DateTime(2015, 7, 30, 0, 0, 0, 0)));
System.out.println(list1);
System.out.println(list2);
// MAIN-GOAL: Get a list of ID's from list1 which have a higher Date or doesnt exists in list2
// Filter list1 so every ID is unique (with highest Date)
final Map<Integer, DateTime> list1UniqueIdMap = new HashMap<Integer, DateTime>();
for (final Entry e : list1)
{
if (!list1UniqueIdMap.containsKey(e.getId()))
{
list1UniqueIdMap.put(e.getId(), e.getDate());
}
else
{
final DateTime dateFromMap = list1UniqueIdMap.get(e.getId());
if (e.getDate().isAfter(dateFromMap))
{
list1UniqueIdMap.put(e.getId(), e.getDate());
}
}
}
// Filter list2 so every ID is unique (with highest Date)
final Map<Integer, DateTime> list2UniqueIdMap = new HashMap<Integer, DateTime>();
for (final Entry e : list2)
{
if (!list2UniqueIdMap.containsKey(e.getId()))
{
list2UniqueIdMap.put(e.getId(), e.getDate());
}
else
{
final DateTime dateFromMap = list2UniqueIdMap.get(e.getId());
if (e.getDate().isAfter(dateFromMap))
{
list2UniqueIdMap.put(e.getId(), e.getDate());
}
}
}
System.out.println(list1UniqueIdMap);
System.out.println(list2UniqueIdMap);
// Get List of ID's which are in list1 but not in list2, or, if they are in list2, if they have a higher date
// Furthermore, the the ID's of list1 which have a higher count then in list2
final Set<Integer> resultSet = new HashSet<Integer>();
for (final Integer id : list1UniqueIdMap.keySet())
{
if (!list2UniqueIdMap.containsKey(id))
{
resultSet.add(id);
}
else
{
final DateTime dateList1 = list1UniqueIdMap.get(id);
final DateTime dateList2 = list2UniqueIdMap.get(id);
if (dateList1.isAfter(dateList2))
{
resultSet.add(id);
}
}
if (getCount(list1, id) > getCount(list2, id))
{
resultSet.add(id);
}
}
// Result
System.out.println(resultSet);
}
private int getCount(final List<Entry> list, final int id)
{
int count = 0;
for (final Entry e : list)
{
if (e.getId() == id)
{
count++;
}
}
return count;
}
private class Entry
{
private int id;
private DateTime date;
public Entry(final int id, final DateTime date)
{
this.id = id;
this.date = date;
}
public int getId()
{
return id;
}
public void setId(final int id)
{
this.id = id;
}
public DateTime getDate()
{
return date;
}
public String getFormattedLastChangeDat()
{
return DateTimeFormat.forPattern("dd.MM.yyyy").print(getDate());
}
public void setDate(final DateTime date)
{
this.date = date;
}
@Override
public String toString()
{
return this.getClass().getSimpleName() + "[id: " + this.getId() + " , date: " + this.getFormattedLastChangeDat() + "]";
}
}
}
我的示例输出:
List1
[
Entry[id: 15 , date: 29.06.2012],
Entry[id: 101 , date: 13.03.2012],
Entry[id: 101 , date: 13.03.2012],
Entry[id: 68691 , date: 12.02.2015],
Entry[id: 68691 , date: 12.02.2015],
Entry[id: 68691 , date: 01.05.2015],
Entry[id: 70738 , date: 26.01.2016]]
List2:
[
Entry[id: 15 , date: 29.06.2012],
Entry[id: 101 , date: 13.03.2012],
Entry[id: 68691 , date: 12.02.2015],
Entry[id: 68691 , date: 12.02.2015],
Entry[id: 70738 , date: 30.07.2015]]
List1UniqueIdMap:
{
101=2012-03-12T00:00:00.000+01:00,
70738=2016-01-26T00:00:00.000+01:00,
68691=2015-05-01T00:00:00.000+02:00,
15=2012-06-29T00:00:00.000+02:00}
List2UniqueIdMap:
{
101=2012-03-12T00:00:00.000+01:00,
70738=2015-07-30T00:00:00.000+02:00,
68691=2015-02-12T00:00:00.000+01:00,
15=2012-06-29T00:00:00.000+02:00}
Result:
[101, 68691, 70738]
答案 0 :(得分:3)
首先,您想要的是从Map<Integer, DateTime>
创建一个中间list2
,其中每个条目的ID都映射到最大日期。这样,我们只需要将list1
中的每个ID与此最大日期进行比较,看看它是否在之后。
要考虑您的更新,您还需要保留list1
比list2
更高的ID,我们还需要创建另外两个存储计数的Map<Integer, Long>
list1
和list2
创建此地图可以通过list2
对条目ID进行分组来完成。我们使用groupingBy(classifier, downstream)
,分类器是方法引用Entry::getId
,返回条目的id。下游收集器用于将具有相同id的所有值收集到单个结果中;在这种情况下,我们使用maxBy
收集器将每个条目的日期与comparing(keyExtractor)
进行比较。由于这个比较器返回一个Optional
(为了处理我们将要收集的情况,因此没有最大值),它被包装到collectingAndThen
中,它应用了一个修整器操作,在这种情况下,可选值并从中检索日期。计数映射的想法是相同的,不同之处在于,下次收集器是counting()
,它计算具有相同密钥的值的数量。
Map<Integer, DateTime> map =
list2.stream()
.collect(groupingBy(
Entry::getId,
collectingAndThen(maxBy(comparing(Entry::getDate)), e -> e.get().getDate())
));
Map<Integer, Long> mapCount2 = list2.stream().collect(groupingBy(Entry::getId, counting()));
Map<Integer, Long> mapCount1 = list1.stream().collect(groupingBy(Entry::getId, counting()));
有了这个中间地图,我们就可以轻松过滤list1
:我们只保留地图不包含当前ID的元素,或者如果有的话,当前条目的日期是在存储在地图中的那个。由于我们对重复项不感兴趣,因此会将其收集到Set
。
Set<Integer> ids =
list1.stream()
.filter(e -> !mapDate.containsKey(e.getId()) ||
e.getDate().isAfter(mapDate.get(e.getId())) ||
mapCount1.get(e.getId()) > mapCount2.get(e.getId()))
.map(Entry::getId)
.collect(toSet());
用于使代码更清晰的静态导入:
import static java.util.Comparator.comparing;
import static java.util.stream.Collectors.collectingAndThen;
import static java.util.stream.Collectors.counting;
import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.maxBy;
import static java.util.stream.Collectors.toSet;