Java 8:如何在Java 8中根据内部对象ID和日期优雅地删除多余的外部对象?

Java 8: How to elegantly remove redundant outer objects on base of inner object ID and date in Java 8?

有一个 ObjectOuter,它包含另一个名为 ObjectInner 的对象,它有一个 ID。我们需要删除具有重复 ObjectInner ID 的冗余 ObjectOuter。 (图中还有一个 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 上使用过滤器以 select 仅那些日期为 NULL 或在重复元素中具有最大日期的 ID;日期位于外部对象中。

以上代码执行正常,但建议在Java中以更优雅的方式处理它 8.我只能想到省略第一个if语句。但是上面的片段在 Java-8 中是否有任何合并语句的范围?

您可以只使用流的过滤方法。

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() );

是的,这是过滤器调用中的有状态操作,因此可能被认为是“错误的形式”,但它完成了工作,简短、快速且易于理解。

如果您的 ID 已经是唯一的,您可以简单地执行以下操作:

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
                                ));

或者你想要的任何其他策略,也许外面有一个可以比较的日期?

也许 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