繁体   English   中英

使用Java 8将两个列表中的对象与唯一ID合并

[英]Merging objects from two list with unique id using Java 8

class Human {
    Long humanId;
    String name;
    Long age;
}


class SuperHuman {
    Long superHumanId;
    Long humanId;
    String name;
    Long age;
}

我有两个清单。 人类名单和superHumans名单。 我想从两个中创建一个单独的列表,确保如果一个人是超人,它只会在使用java 8的列表中出现一次。有一个巧妙的方法吗? 更新:这些是不同的类,即不扩展另一个。 我想最终的名单是超人。 如果一个人已经是超人,我们就会忽视那个人类的对象。 如果人类不是超人,我们将人类对象转换为超人类对象。 理想情况下,我希望按照他们的年龄对它们进行排序,以便获得按日期降序排序的超人列表。

根据您更新的问题:

List<Human> humans = ... // init 
List<SuperHuman> superHumans = ... // init

Set<Long> superHumanIds = superHumans.stream()
    .map(SuperHuman::getHumanId)
    .collect(toSet());

humans.stream()
    .filter(human -> superHumanIds.contains(human.getHumanId()))
    .map(this::convert)
    .forEach(superHumans::add);

superHumans.sort(Comparator.comparing(SuperHuman::getAge));

假设此类具有另一个具有以下签名的方法:

private Superhuman convert(Human human) {
    // mapping logic
}

关于如何重新考虑代码以使其更好,您还有其他建议,但是如果您不能这样做,有一种方法 - 通过自定义Collector并不复杂。

自定义收集器还为您提供了实际决定要保留哪个条目的优势 - 已经收集的条目或者遇到订单获胜的最新条目。 它需要一些代码更改 - 但是如果您需要它可以实现。

 private static <T> Collector<Human, ?, List<Human>> noDupCollector(List<SuperHuman> superHumans) {
    class Acc {

        ArrayList<Long> superIds = superHumans.stream()
                .map(SuperHuman::getHumanId)
                .collect(Collectors.toCollection(ArrayList::new));

        ArrayList<Long> seen = new ArrayList<>();

        ArrayList<Human> noDup = new ArrayList<>();

        void add(Human elem) {
            if (superIds.contains(elem.getHumanId())) {

                if (!seen.contains(elem.getHumanId())) {
                    noDup.add(elem);
                    seen.add(elem.getHumanId());
                }

            } else {
                noDup.add(elem);
            }
        }

        Acc merge(Acc right) {
            noDup.addAll(right.noDup);
            return this;
        }

        public List<Human> finisher() {
            return noDup;
        }

    }
    return Collector.of(Acc::new, Acc::add, Acc::merge, Acc::finisher);
}

假设您有这些条目:

List<SuperHuman> superHumans = Arrays.asList(
            new SuperHuman(1L, 1L, "Superman"));
    //
    List<Human> humans = Arrays.asList(
            new Human(1L, "Bob"),
            new Human(1L, "Tylor"),
            new Human(2L, "John"));

这样做:

List<Human> noDup = humans.stream()
            .collect(noDupCollector(superHumans));

System.out.println(noDup); // [Bob, Tylor]

尝试这个。

List<Object> result = Stream.concat(
    humans.stream()
        .filter(h -> !superHumans.stream()
            .anyMatch(s -> h.humanId == s.humanId)),
    superHumans.stream())
    .collect(Collectors.toList());

假设这两个类都没有继承另一个类。 我可以想象你有两个清单:

Human alan = new Human(1, "Alan");
Human bertha = new Human(2, "Bertha");
Human carl = new Human(3, "Carl");
Human david = new Human(4, "David");
SuperHuman carlS = new SuperHuman(1, 3, "Carl");
SuperHuman eliseS = new SuperHuman(2, 0, "Elise");

List<Human> humans = new ArrayList<>(Arrays.asList(alan, bertha, carl, david));
List<SuperHuman> superHumans = new ArrayList<>(Arrays.asList(carlS, eliseS));

我们看到卡尔被列为人类,也是超人。 存在两个相同的卡尔实例。

List<Object> newList = humans.stream()
    .filter((Human h) -> {
        return !superHumans.stream()
            .anyMatch(s -> s.getHumanId() == h.getHumanId());
    })
    .collect(Collectors.toList());
newList.addAll(superHumans);

此代码试图筛选人的名单,但不包括其所有条目humanId存在于超人的列表。 最后,所有超人都加入了。


这个设计有几个问题:

  • 直观地说,这些类是相关的,但是你的代码却另有说法。 您尝试合并的事实表明类型相关的。
  • 这些类具有重叠属性( humanIdname ),这也表明这些类是相关的。

假设这些类是相关的,绝对不是没有根据的。

正如其他评论者建议的那样,您应该重新设计课程:

class Human {
    long id; // The name 'humanId' is redundant, just 'id' is fine
    String name;
}

class SuperHuman extends Human {
    long superHumanId; // I'm not sure why you want this...
}
Human alan = new Human(1, "Alan");
Human bertha = new Human(2, "Bertha");
Human carl = new SuperHuman(3, "Carl");
Human david = new Human(4, "David");
Human elise = new SuperHuman(5, "Elise");

List<Human> people = Arrays.asList(alan, bertha, carl, david, elise);

然后,每个人都有一个单独的实例。 你有没有从列表中获得所有超人,只需使用:

List<Human> superHumans = people.stream()
    .filter((Human t) -> t instanceof SuperHuman)
    .collect(Collectors.toList());

superHumans.forEach(System.out::println); // Carl, Elise

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM