You could add two constructors and a merge method to the Account class that would combine contacts:
public class Account {
private final Integer accountId;
private List<String> contacts = new ArrayList<>();
public Account(Integer accountId) {
this.accountId = accountId;
}
// Copy constructor
public Account(Account another) {
this.accountId = another.accountId;
this.contacts = new ArrayList<>(another.contacts);
}
public Account merge(Account another) {
this.contacts.addAll(another.contacts);
return this;
}
// TODO getters and setters
}
Then, you have a few alternatives. One is to use Collectors.toMap to collect accounts to a map, grouping by accountId and merging the contacts of the accounts with equal accountId by means of the Account.merge method. Finally, get the values of the map:
Collection<Account> result = accounts.stream()
.collect(Collectors.toMap(
Account::getAccountId, // group by accountId (keys)
Account::new, // use copy constructor (values)
Account::merge)) // merge values with equal key
.values();
You need to use the copy constructor for the values, otherwise you would mutate the accounts of the original list when Account.merge is invoked.
An equivalent way (without streams) would be to use the Map.merge method:
Map<Integer, Account> map = new HashMap<>();
accounts.forEach(a ->
map.merge(a.getAccountId(), new Account(a), Account::merge));
Collection<Account> result = map.values();
Again, you need to use the copy constructor to avoid undesired mutations on the accounts of the original list.
A third alternative which is more optimized (because it doesn't create a new account for every element of the list) consists of using the Map.computeIfAbsent method:
Map<Integer, Account> map = new HashMap<>();
accounts.forEach(a -> map.computeIfAbsent(
a.getAccountId(), // group by accountId (keys)
Account::new) // invoke new Account(accountId) if absent
.merge(a)); // merge account's contacts
Collection<Account> result = map.values();
All the alternatives above return a Collection<Account>. If you need a List<Account> instead, you can do:
List<Account> list = new ArrayList<>(result);