使用 Java8 比较两种不同类型的列表

发布于 2025-01-11 02:42:19 字数 1432 浏览 0 评论 0 原文

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

a√萤火虫的光℡ 2025-01-18 02:42:19

目前尚不清楚 PersonCompanyuniqueIdentificationNumber 是如何关联的。值得细化这些以更好的方式表示它们之间的关系(也许公司可以保存对客户列表的引用)。并且不要过度使用 setter,如果 id 是唯一的,则无需允许更改它。

尽管由于类设计的缺陷,技术上尚不清楚这些值是如何连接的,但它是可行的。

返回与Id匹配的客户对象

为此,您需要创建两个映射,将这些标识符公司关联起来人。然后在其中一个映射上创建一个流,并检查每个是否包含在另一个映射中>。然后检索 Person 对象以查找已过滤的键,并将结果收集到列表中。

    Map<Integer, Person> personById =
            persons.stream()
                    .collect(Collectors.toMap(Person::getUniqueIdentificationNumber,
                                              Function.identity()));
    Map<Integer, Company> companyById =
            companies.stream()
                     .collect(Collectors.toMap(Company::getUniqueIdentificationNumber,
                                               Function.identity()));

    List<Person> customers =
            personById.keySet().stream()
                    .filter(companyById::containsKey) // checking whether id is present in the company map
                    .map(personById::get) // retrieving customers
                    .collect(Collectors.toList());

更新

让我重新表述一下问题的描述。

有两个不相关的类 AB。这两个类都有 两个 int 类型的字段,比方说 val1val2 (也许还有更多字段,但我们对它们不感兴趣)。

我们有一个对象列表 A 和一个对象列表 B。目标是找到一个对象 A,该对象存在一个对象 B,并且 val1val2 具有相同的值code> (为了让事情简单,我建议坚持使用这个例子)。

有两种方法可用于此目的:

  • 创建一个具有两个字段 val1val2 的辅助类,并关联 A 的每个实例和 B 以及此类的实例;
  • 创建一个嵌套映射 Map>,如果您需要将对象进行三倍、四倍等比较,此解决方案会更复杂且不太灵活. 字段的代码很快就会变得难以理解。

所以我会坚持第一种方法。我们需要声明具有两个字段的 ValueHolder 类,并基于这些字段实现 equals/hashCode 合约。对于Java 16及更高版本,我们可以利用记录来实现此目的,并使用equals()hashCode ,由编译器提供的 getter。带有记录的选项将如下所示:

public record ValueHolder(int val1, int val2) {} // equals/hashCode, constructor and getters provided by the compiler

AB

public class A {
    private int val1;
    private int val2;

    // constructor and getters
}

public class B {
    private int val1;
    private int val2;

    // constructor and getters
}

以及接受两个列表的方法:List >列出,并将结果作为Optional返回。因为匹配元素可能存在也可能不存在,并且在这种情况下返回可选对象是一个好习惯,而不是在未找到结果时返回 null。它提供了更大的灵活性,而这正是该选项设计的目的。

public Optional<A> getMatchingItem(List<A> listA, List<B> listB) {

    Map<ValueHolder, A> aByValue = listA.stream()
            .collect(Collectors.toMap(a -> new ValueHolder(a.getVal1(), a.getVal2()),
                    Function.identity()));

    Map<ValueHolder, B> bByValue = listB.stream()
            .collect(Collectors.toMap(b -> new ValueHolder(b.getVal1(), b.getVal2()),
                    Function.identity()));

    return aByValue.keySet().stream()
            .filter(bByValue::containsKey)
            .findFirst()
            .map(aByValue::get);
}

It doesn't clear how uniqueIdentificationNumber of the Person and Company are related. It's worth to refine these classes to represent the relationship between them in a better way (maybe a company can hold a reference to a list of customers). And don't overuse setters, if id is unique there's no need to allow it to be changed.

Although it's not clear how these values are connected because of the drawbacks of your class design technically it's doable.

return the customer object which is matching with Id

For that, you need to create two maps that will associate these identifiers with companies and persons. Then create a stream over the keys of one of these maps and check for every key whether if contained in another map. And then retrieve the Person objects for filtered keys and collect the result into a list.

    Map<Integer, Person> personById =
            persons.stream()
                    .collect(Collectors.toMap(Person::getUniqueIdentificationNumber,
                                              Function.identity()));
    Map<Integer, Company> companyById =
            companies.stream()
                     .collect(Collectors.toMap(Company::getUniqueIdentificationNumber,
                                               Function.identity()));

    List<Person> customers =
            personById.keySet().stream()
                    .filter(companyById::containsKey) // checking whether id is present in the company map
                    .map(personById::get) // retrieving customers
                    .collect(Collectors.toList());

Update

Let me rephrase the description of the problem.

There are two unrelated classes A and B. Both classes have two fields of type int, let's say val1 and val2 (and maybe a few more fields but we are not interested in them).

We have a list of objects A and a list of objects B. The goal is to find a single object A for which exists an object B with the same values for both val1 and val2 (in order to keep things simple I propose to stick with this example).

There are two approaches that could be used for that purpose:

  • create an auxiliary class with two fields val1 and val2, and associate every instance of A and B with instances of this class;
  • create a nested map Map<Integer, Map<Integer, *targetClass*>>, this solution is more complicated and less flexible, if you'll need to compare objects by three, four, etc. fields the code will quickly become incomprehensible.

So I'll stick with the first approach. We need to declare the ValueHolder class with two fields and implement the equals/hashCode contract based on these fields. For Java 16 and above we can utilize a record for that purpose and make use of equals(), hashCode, getters provided by the compiler. The option with the record will look like this:

public record ValueHolder(int val1, int val2) {} // equals/hashCode, constructor and getters provided by the compiler

Classes A and B

public class A {
    private int val1;
    private int val2;

    // constructor and getters
}

public class B {
    private int val1;
    private int val2;

    // constructor and getters
}

And a method that accepts two lists: List<A> and List<B>, and return a result as Optional<A>. Because the matching element may or may not be present and it's a good practice to return an optional object in such cases instead of returning null in case the result was not found. It provides more flexibility and that's precisely the case for which the optional was designed.

public Optional<A> getMatchingItem(List<A> listA, List<B> listB) {

    Map<ValueHolder, A> aByValue = listA.stream()
            .collect(Collectors.toMap(a -> new ValueHolder(a.getVal1(), a.getVal2()),
                    Function.identity()));

    Map<ValueHolder, B> bByValue = listB.stream()
            .collect(Collectors.toMap(b -> new ValueHolder(b.getVal1(), b.getVal2()),
                    Function.identity()));

    return aByValue.keySet().stream()
            .filter(bByValue::containsKey)
            .findFirst()
            .map(aByValue::get);
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文