0

Given 2 Lists containing 2 kinds of different objects (as if you are getting a collection from an API to create a client or update if it exists):

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;
    }

The purpose is to build a new list of ClientA objects:

  • if there is an entry in clientsA list with the same ssNumber value equal to the security value of the clients in ClientB list, update the found entry firstNameand lastName attributes;
  • otherwise, create a new ClientA object with the same attributes/values from the clientsB list, assigning firstName-> firstName, lastName -> lastName, securityNumber -> ssNumber.

I was going to use contains or retainAll methods, but it requires overriding equals and hashCode of the above classes what I can't do.

I expecting to have something like this:

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);
    }        

What returns the below result:

++++++ 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'}]

Is it a good solution or there is a better one?

But still no success.

2
  • Why not just loop through the collection yourself? Commented Mar 19, 2021 at 21:16
  • Just updated the post :). Commented Mar 19, 2021 at 21:19

1 Answer 1

1

It seems that for both cases an instance of ClientA should be created from ClientB instances and collected into a new list, thus, the following constructor should be added to ClientA:

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

So the conversion would be a simple remapping:

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

If the "update" means that the change needs to be promoted to clientsA list, a map of ClientA by ssNumber can be built first:

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

Then a new list can be created like this:

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);
});

Or a helper method should be implemented (possibly added to ClientA) to copy the values from 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;
}

Then the new list of ClientA may be built in a more streamed way:

List<ClientA> newClientsA = clientsB.stream()
        .map(b -> map.containsKey(b.getSecurity()) 
            ? copyToA(map.get(b.getSecurity()), b)
            : new ClientA(b)
        )
        .collect(Collectors.toList());
Sign up to request clarification or add additional context in comments.

1 Comment

Thank you Alex for the response. Yes, the update means: if there is a clientA with the same ssNumber as ClientB's security, update this ClientA instance by assigning the values of firstName, lastName from the corresponding ClientB instance.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.