class Human {
Long humanId;
String name;
Long age;
}
class SuperHuman {
Long superHumanId;
Long humanId;
String name;
Long age;
}
我有两个清单。人类名单和superHumans名单。我想从两个中创建一个单独的列表,确保如果一个人是超人,它只会在使用java 8的列表中出现一次。有一个巧妙的方法吗? 更新:这些是不同的类,即不扩展另一类。我想最终的名单是超人。如果一个人已经是超人,我们就会忽视那个人类的对象。如果人类不是超人,我们将人类对象转换为超人类对象。理想情况下,我希望按年龄对他们进行排序,这样我就可以按降序排列按日期排序的超人名单。
答案 0 :(得分:6)
根据您更新的问题:
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
}
答案 1 :(得分:2)
关于如何重新考虑代码以使其更好,您还有其他建议,但以防无法做到这一点,有办法 - 通过自定义{ {1}}这并不复杂。
自定义收集器还为您提供了实际决定要保留哪个条目的优势 - 已收集的条目或遇到顺序的即将到来或最新的条目。它需要进行一些代码更改 - 但是如果您需要它可以做到这一点。
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"));
答案 2 :(得分:1)
试试这个。
List<Object> result = Stream.concat(
humans.stream()
.filter(h -> !superHumans.stream()
.anyMatch(s -> h.humanId == s.humanId)),
superHumans.stream())
.collect(Collectors.toList());
答案 3 :(得分:0)
假设两个类都没有继承另一个类。我可以想象你有两个清单:
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
存在的所有条目。最后,所有超人都加入了。
这种设计有几个问题:
humanId
和name
),这也表明这些类是相关的。假设这些类是相关的,绝对不是没有根据的。
正如其他评论者所说,你应该重新设计课程:
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