Java 8: Как элегантно удалить избыточные внешние объекты на основе внутреннего идентификатора объекта и даты в Java 8?
Существует ObjectOuter, который содержит другой объект с именем ObjectInner, имеющий идентификатор. Нам нужно удалить Redundant ObjectOuter с дублирующимися идентификаторами ObjectInner. (У нас есть другой объект DateFinish на картинке)
public static List<ObjectOuter> removeRedundantObject(List<ObjectOuter> ObjectOuterOriginal){
if(ObjectOuterOriginal.size() == 1) {
return ObjectOuterOriginal;
}
List<ObjectInner> allObjectInner = ObjectOuterOriginal.stream().map(ObjectOuter::getObjectInner).collect(toList());
List<Long> allObjectInnerIds = allObjectInner.stream().map(ObjectInner::getObjectInnerId).collect(toList());
List<ObjectOuter> myFinalObjectOuter = new ArrayList<>();
if(ObjectOuterOriginal.size() == allObjectInnerIds.stream().distinct().collect(Collectors.toList()).size()){
return ObjectOuterOriginal;
}
Set<Long> duplicateObjectOuter = CommonUtils.getDuplicateNumbers(allObjectInnerIds); //Returns numbers which are duplicate in set
if(SetUtils.emptyIfNull(duplicateObjectOuter).isEmpty()){
return ObjectOuterOriginal;
} else {
duplicateObjectOuter.forEach((objectInnerId) -> {
List<ObjectOuter> myOwnObjectOuter = ObjectOuterOriginal.stream().filter(d -> d.getObjectInner().getObjectInnerId().equals(objectInnerId) && d.getDateFinish()==null).collect(Collectors.toList());
if(ListUtils.emptyIfNull(myOwnObjectOuter).isEmpty()) {
LocalDate maxDate = ObjectOuterOriginal.stream().filter(d -> d.getObjectInner().getObjectInnerId().equals(objectInnerId) && d.getDateFinish()!=null).map(u -> u.getDateFinish()).max(LocalDate::compareTo).get();
List<ObjectOuter> ownObjectOuter = ObjectOuterOriginal.stream().filter(d -> d.getObjectInner().getObjectInnerId().equals(objectInnerId) && d.getDateFinish()!=null).filter(d -> d.getDateFinish().compareTo(maxDate) == 0).collect(toList());
myFinalObjectOuter.addAll(ownObjectOuter);
} else {
myFinalObjectOuter.addAll(myOwnObjectOuter);
}
});
duplicateObjectOuter.forEach((objectInnerId) -> {
ObjectOuterOriginal.removeIf(d -> d.getObjectInner().getObjectInnerId().compareTo(objectInnerId) == 0);
});
ObjectOuterOriginal.addAll(myFinalObjectOuter);
}
return ObjectOuterOriginal;
}
Также нам нужно использовать фильтр на innerObject, чтобы выбрать только те идентификаторы, чья дата равна NULL или имеет максимальную дату среди повторяющихся элементов; где дата находится во внешнем объекте.
Вышеупомянутый код выполняется должным образом, но в Java 8 рекомендуется обрабатывать его более элегантным способом. Я могу только думать о том, чтобы опустить сначала оператор if. но есть ли возможность слияния операторов в Java-8 для приведенного выше фрагмента?
2 ответа
Если ваши идентификаторы уже уникальны, вы можете просто сделать:
public static void main(String[] args) {
Outer[] outers = {
new Outer(new Inner("a")),
new Outer(new Inner("b")),
new Outer(new Inner("c")),
new Outer(new Inner("a")),
new Outer(new Inner("b")),
new Outer(new Inner("c")),
};
Map<String, Outer> OutersById = Arrays.stream(outers).collect(Collectors.toMap(outer -> outer.inner.id, outer -> outer));
OutersById.forEach((k,v)->System.out.println(k+", "+v));
}
Но это приведет к исключению дубликатов ключей.
Для того, чтобы управлять вашими дубликатами и использовать вашу собственную стратегию устранения неоднозначности,
Map<String, Outer> OutersById = Arrays.stream(outers).collect(Collectors.toMap(outer -> outer.inner.id, outer -> outer));
Может быть изменено на
Map<String, Outer> OutersById = Arrays.stream(outers)
.collect(
Collectors.toMap(
outer -> outer.inner.id,
outer -> outer,
(a, b) -> a.hashCode() > b.hashCode() ? a : b
));
Или какую бы стратегию вы ни выбрали, возможно, у external есть дата, которую можно сравнить?
Может быть, a и b можно объединить в новый объект ab?
Обновить:
public static void main(String[] args) {
Instant now = Instant.now();
Instant nxtWeek= now.plus(Duration.ofDays(7));
Outer[] outers = {
new Outer(new Inner("a"), null),
new Outer(new Inner("b"), Date.from(now)),
new Outer(new Inner("c"), Date.from(nxtWeek)),
new Outer(new Inner("a"), Date.from(now)),
new Outer(new Inner("b"), Date.from(nxtWeek)),
new Outer(new Inner("c"), null),
};
Comparator<Outer> outerRanker = Comparator.comparing(Outer::getFinishDate, Comparator.nullsLast(Date::compareTo));
Map<String, Outer> OutersById = Arrays.stream(outers)
.collect(
Collectors.toMap(
outer -> outer.inner.id,
outer -> outer,
(a, b) -> outerRanker.compare(a,b) > 0 ? a : b
));
System.out.println("today: "+Date.from(now));
OutersById.forEach((k,v)->System.out.println(k+", "+v));
}
Результаты в
today: Thu Jun 07 15:11:12 ACST 2018
a, Outer{inner=Inner{id='a'}, FinishDate=null}
b, Outer{inner=Inner{id='b'}, FinishDate=Thu Jun 14 15:11:12 ACST 2018}
c, Outer{inner=Inner{id='c'}, FinishDate=null}
Process finished with exit code 0
Вы можете просто использовать метод фильтра потока.
Set<Long> ids = new HashSet<>();
// Note: add returns true if element was not in the set
// so first occurrence will pass through filter,
// subsequent duplicates will be filtered out.
return ObjectOuterOriginal.stream()
.filter( o -> ids.add(o.getObjectInner().getObjectInnerId()) )
.collect( Collections.toList() );
Да, это операция с состоянием в вызове фильтра, поэтому она, вероятно, считается "плохой формой", но она выполняет свою работу, является короткой, простой, быстрой и понятной.