当前位置: 首页 > 知识库问答 >
问题:

优雅地组合两个列表的元素,使它们在某个属性值中是唯一的?

子车安和
2023-03-14

假设我有这个Java 8代码:

public class Foo {
    private long id;
    public getId() {
        return id;
    }

    //--snip--
}


//Somewhere else...

List<Foo> listA = getListA();
List<Foo> listB = getListB();

List<Foo> uniqueFoos = ???;

列表中

当然有简单的老迭代,但我认为应该有更优雅的东西(可能涉及流,但不是强制性的),但我不太明白...

我可以想到一些好的解决方案,包括覆盖equals()方法以基本上返回id==other.id;并使用Set()。不幸的是,我不能覆盖equals(),因为对象相等性不能改变。

实现这一目标的明确有效方法是什么?

共有3个答案

彭雨华
2023-03-14

你可以写这个。由于filter()和使用Set来存储遇到的id,这将跳过具有相同id的第二个和下一个元素:

    Set<Long> ids = new HashSet<>();
    List<Foo> uniqueFoos = Stream.concat(getListA().stream(), getListB().stream())
                                 .filter(f -> ids.add(f.getId()))
                                 .collect(Collectors.toList());

这不是一个完整的流解决方案,但它是相当直接和可读性。

岳正阳
2023-03-14

像这样的事情就可以了。

 List<Foo> uniqueFoos = Stream.concat(listA.stream(), listB.stream())
                              .filter(distinctByKey(Foo::getId))
                              .collect(Collectors.toList());


 public <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
    Set<Object> seen = ConcurrentHashMap.newKeySet();
    return t -> seen.add(keyExtractor.apply(t));
  }
薛楷
2023-03-14

您可以使用Collectors.toMap

Collection<Foo> uniqueFoos = Stream.concat(listA.stream(), listB.stream())
    .collect(Collectors.toMap(
        Foo::getId,
        f -> f,
        (oldFoo, newFoo) -> oldFoo))
    .values();

如果您需要列表而不是集合,只需执行以下操作:

List<Foo> listUniqueFoos = new ArrayList<>(uniqueFoos);

如果还需要保留元素的相遇顺序,则可以使用<code>收集器的重载版本。toMap接受返回地图的供应商

Collection<Foo> uniqueFoos = Stream.concat(listA.stream(), listB.stream())
    .collect(Collectors.toMap(
        Foo::getId,
        f -> f,
        (oldFoo, newFoo) -> oldFoo,
        LinkedHashMap::new))
    .values();

我认为值得添加一个非流变量:

Map<Long, Foo> map = new LinkedHashMap<>();
listA.forEach(f -> map.merge(g.getId(), f, (oldFoo, newFoo) -> oldFoo));
listB.forEach(f -> map.merge(g.getId(), f, (oldFoo, newFoo) -> oldFoo));

Collection<Foo> uniqueFoos = map.values();

这可以重构为通用方法以不重复代码:

static <T, K> Collection<T> uniqueBy(Function<T, K> groupBy, List<T>... lists) {
    Map<K, T> map = new LinkedHashMap<>();
    for (List<T> l : lists) {
        l.forEach(e -> map.merge(groupBy.apply(e), e, (o, n) -> o));
    }
    return map.values();
}

您可以按如下方式使用它:

Collection<Foo> uniqueFoos = uniqueBy(Foo::getId, listA, listB);

此方法使用 Map.merge 方法。

 类似资料:
  • 问题内容: 写一个方法 公共静态ArrayList merge(ArrayList a,ArrayList b) 合并两个数组列表,两个数组列表中的元素交替出现。如果一个数组列表短于另一个数组列表,则请尽可能长地交替,然后附加较长数组列表中的其余元素。例如,如果a是 1 4 9 16 b是 9 7 4 9 11 然后合并返回数组列表 1 9 4 7 9 4 16 9 11 我尝试做的是编写一个带i

  • 我想在java中创建一个方法,该方法接收两个字符串列表:

  • 我今天用Java编写了一个网关(我是一个初学者),允许接收一个包含以下信息的帧:ETQ | RGS | B | MESSAGE | ETX | CHECKSUM。我想解析帧以获得RGS和消息,我对这一部分进行了编码,但我想在同一个列表中添加这两个元素和属性。这可能吗? 这是我的代码: 非常感谢您的帮助

  • 问题内容: 如果我有两个清单 什么是获取熊猫数据框的最优雅的方式,如下所示: 注意,第一列是索引。 问题答案: 使用于:

  • 假设我有一个由n个字符串列表组成的列表: result->包含所有输出列表(所有组合) current->是当前的组合 用上述相同示例调用此函数时的输出:

  • 问题内容: 在最近的一次采访中有人问我这个问题。 您将获得一个包含一百万个元素的数组。除了一个元素外,所有元素都是重复的。我的任务是找到独特的元素。 我的做法是要经过在整个数组循环,然后创建一个索引作为数组中和的数组中出现的次数。然后再次遍历我们的地图,并返回值为1的索引。 我说我的方法会花费时间。面试官告诉我要以低于复杂度的方式对其进行优化。我说过,我们不能,因为我们必须遍历具有一百万个元素的整