【问题标题】:Java: filter 2 collections of different objects by an attribute valueJava:按属性值过滤2个不同对象的集合
【发布时间】:2021-06-17 07:02:26
【问题描述】:

给定 2 个包含 2 种不同对象的列表(就像您从 API 获取一个集合以创建客户端或更新(如果存在)):

public static void main(String[] args) {
        List<ClientA> clientsA = new ArrayList<>();
        List<ClientB> clientsB = new ArrayList<>();
        for (int i = 1; i <=5; i++) {
            clientsA.add(new ClientA("JohnA-" + i, "DoeA-" + i, "A-" + i));
            clientsB.add(new ClientB("JohnB-" + i, "DoeB-" + i, "B-" + i));
        }
    }

    @Getter
    @Setter
    @AllArgsConstructor
    static class ClientA {
        private String firstName;
        private String lastName;
        private String ssNumber;

    }

    @Getter
    @Setter
    @AllArgsConstructor
    static class ClientB {
        private String firstName;
        private String lastName;
        private String security;
    }

目的是建立一个新的ClientA对象列表:

  • 如果clientsA列表中有一个条目,其ssNumber值等于ClientB列表中客户端的security值,则更新找到的条目firstNamelastName属性;
  • 否则,从clientsB列表中创建一个具有相同属性/值的新ClientA对象,分配firstName-> firstName, lastName -> lastName, securityNumber -> @ 987654336@.

我打算使用containsretainAll 方法,但它需要覆盖上述类的equalshashCode,这是我做不到的。

我希望有这样的东西:

public void process() {
        List<ClientA> clientsA = new ArrayList<>();
        List<ClientB> clientsB = new ArrayList<>();
        for (int i = 1; i <=5; i++) {
            clientsA.add(new ClientA("John-" + i, "Doe-" + i, "A-" + i));
            clientsB.add(new ClientB("JohnB-" + i, "DoeB-" + i, "B-" + i));
        }

        clientsA.add(new ClientA("Samantha", "Smith", "123456789"));
        clientsB.add(new ClientB("Michael", "Smith", "123456789"));

        findExistingEClientsA(clientsA, clientsB);
        findNewClientsB(clientsA, clientsB);
    }

    private void findNewClientsB(List<ClientA> clientsA, List<ClientB> clientsB) {
        Set resultSet = new HashSet();
        for (ClientA clientA : clientsA) {
            List<ClientB> collect = clientsB.stream().filter(c -> !c.getSecurity().equals(clientA.getSsNumber())).collect(Collectors.toList());
            resultSet.addAll(collect);
        }
        System.out.println("+++++++ New clients B +++++++");
        System.out.println(resultSet);
    }

    private void findExistingEClientsA(List<ClientA> clientsA, List<ClientB> clientsB) {
        Set resultSet = new HashSet();
        for (ClientA clientA : clientsA) {
            List<ClientB> collect = clientsB.stream().filter(c -> c.getSecurity().equals(clientA.getSsNumber())).collect(Collectors.toList());
            resultSet.addAll(collect);
        }

        System.out.println("++++++ existing clients B +++++++ ");
        System.out.println(resultSet);
    }        

返回以下结果的内容:

++++++ existing clients B +++++++ 
[ClientB{firstName='Michael', lastName='Smith', security='123456789'}]
+++++++ New clients B +++++++
[ClientB{firstName='JohnB-4', lastName='DoeB-4', security='B-4'}, ClientB{firstName='JohnB-2', lastName='DoeB-2', security='B-2'}, ClientB{firstName='JohnB-5', lastName='DoeB-5', security='B-5'}, ClientB{firstName='JohnB-3', lastName='DoeB-3', security='B-3'}, ClientB{firstName='JohnB-1', lastName='DoeB-1', security='B-1'}, ClientB{firstName='Michael', lastName='Smith', security='123456789'}]

这是一个好的解决方案还是有更好的解决方案?

但还是没有成功。

【问题讨论】:

  • 为什么不自己循环浏览集合?
  • 刚刚更新了帖子:)。

标签: java collections java-stream


【解决方案1】:

似乎对于这两种情况都应该从ClientB 实例创建ClientA 的实例并收集到一个新列表中,因此,应该将以下构造函数添加到ClientA

static class ClientA {
    public ClientA(ClientB b) {
        this(b.getFirstName(), b.getLastName(), b.getSecurity());
    }
}

所以转换将是一个简单的重新映射:

List<ClientA> newA = clientsB.stream().map(ClientA::new).collect(Collectors.toList());

如果“更新”表示需要将变更提升到clientsA列表,可以先建一张ClientA by ssNumber的map:

Map<String, ClientA> map = clientsA.stream()
        .collect(Collectors.toMap(
            ClientA::getSsNumber, clientA -> clientA, 
            (c1, c2) -> c1  // select first clientB if duplicate entries are detected
        ));

然后可以像这样创建一个新列表:

List<ClientA> newA = new ArrayList<>();

clientsB.forEach(b -> {
    ClientA a = map.getOrDefault(b.getSecurity(), new ClientA(b));
    a.setFirstName(b.getFirstName());
    a.setLastName(b.getLastName());
    newA.add(a);
});

或者应该实现一个辅助方法(可能添加到ClientA)来复制ClientB中的值:

public static ClientA copyToA(ClientA a, ClientB b) {
    Objects.requireNonNull(a);
    Objects.requireNonNull(b);
    a.setFirstName(b.getFirstName());
    a.setLastName(b.getLastName());
    return a;
}

那么ClientA的新列表可能会以更streamed的方式构建:

List<ClientA> newClientsA = clientsB.stream()
        .map(b -> map.containsKey(b.getSecurity()) 
            ? copyToA(map.get(b.getSecurity()), b)
            : new ClientA(b)
        )
        .collect(Collectors.toList());

【讨论】:

  • 感谢亚历克斯的回复。是的,更新的意思是:如果有一个客户端A 与ClientBsecurity 具有相同的ssNumber,则通过从对应的@987654343 中分配firstNamelastName 的值来更新这个ClientA 实例@实例。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-05-04
  • 2016-07-19
  • 2015-08-23
  • 1970-01-01
  • 2021-12-18
  • 2023-04-03
  • 2017-11-03
相关资源
最近更新 更多