Home > other >  How to use merge function with collections as a map value in Java
How to use merge function with collections as a map value in Java

Time:01-14

I am trying to understand better how to work with collections and functions without streams and I have the next code where I am trying to add a new value to an existing key in a Map where value is a Collection.

But I am stuck with what exactly parameters I need to pass to the merge function in addNewPhoneNumbers() method. So I need some help with this particular code piece and maybe some explanation how it should work with merge or compute functions.

import java.util.*;

class PhoneBook {
    private final Map<String, Collection<PhoneNumber>> nameToPhoneNumbersMap = new HashMap<>();

    public void addNewPhoneNumbers(String name, Collection<PhoneNumber> numbers) {
        if (!nameToPhoneNumbersMap.containsKey(name)) {
            nameToPhoneNumbersMap.put(name, numbers);
        } else{
            System.out.println("name is already in the map, adding new phone...");
            nameToPhoneNumbersMap.merge(name, ??? , ???);
        }
    }

    public void printPhoneBook() {
        nameToPhoneNumbersMap.forEach((k, v) -> {
            System.out.println(k);
            v.forEach(vv -> System.out.println(vv.getType()   ": "   vv.getNumber()));
        });
        System.out.println("------");
    }
}

enum PhoneNumberType {
    MOBILE, HOME, WORK,
}

class PhoneNumber {

    private PhoneNumberType type;
    private String number;

    public PhoneNumber(PhoneNumberType type, String number) {
        this.type = type;
        this.number = number;
    }

    public PhoneNumberType getType() {
        return type;
    }

    public String getNumber() {
        return number;
    }


    public static void main(String[] args) {
        PhoneBook phoneBook = new PhoneBook();

        List<PhoneNumber> saraPhoneNumbers = new ArrayList<>();
        List<PhoneNumber> johnPhoneNumbers = new ArrayList<>();

        saraPhoneNumbers.add(new PhoneNumber(PhoneNumberType.HOME, "1234567"));
        johnPhoneNumbers.add(new PhoneNumber(PhoneNumberType.WORK, "8910"));

        phoneBook.addNewPhoneNumbers("Sara", saraPhoneNumbers);
        phoneBook.printPhoneBook();

        phoneBook.addNewPhoneNumbers("John", johnPhoneNumbers);
        phoneBook.printPhoneBook();

        //add a new name with a new phone number
        phoneBook.addNewPhoneNumbers("Tom", List.of(new PhoneNumber(PhoneNumberType.WORK, "11121314")));
        phoneBook.printPhoneBook();

        //add a new phone with a new phone type for an existing name
        phoneBook.addNewPhoneNumbers("Sara", List.of(new PhoneNumber(PhoneNumberType.MOBILE, "15161718")));
        phoneBook.printPhoneBook();
    }

}

For now the last sout result doesn't contain Sara's mobile phone number, but it should be like this:

name is already in the map, adding new phone...

Tom

WORK: 11121314

John

WORK: 8910

Sara

HOME: 1234567

MOBILE: 15161718

CodePudding user response:

Use computeIfAbsent to add a new collection if key is absent or add values if it exists

public void addNewPhoneNumbers(String name, Collection<PhoneNumber> numbers) {
    if (nameToPhoneNumbersMap.containsKey(name)) {
        System.out.println(name   " is already in the map, adding new phone...");
    }

    nameToPhoneNumbersMap.computeIfAbsent(name, k -> new ArrayList<>()).addAll(numbers);
}

You can achieve the same with merge, but need to take care of the fact that Collection.addAll returns a boolean. That is : in case the key already exists you need to add the new numbers to the old list and return the old list or the other way round.

public void addNewPhoneNumbers(String name, Collection<PhoneNumber> numbers) {
    if (nameToPhoneNumbersMap.containsKey(name)) {
        System.out.println(name   " is already in the map, adding new phone...");
    }
    nameToPhoneNumbersMap.merge(name, numbers, (oldValue,newValue) -> {
        oldValue.addAll(newValue);
        return oldValue;
    });
}

CodePudding user response:

With merge you don't have to distinguish wether the name already exists or not:

public void addNewPhoneNumbers(String name, Collection<PhoneNumber> numbers) {
    if (nameToPhoneNumbersMap.containsKey(name)) {
        System.out.println("name is already in the map, adding new phone...");
    }
    nameToPhoneNumbersMap.merge(name, numbers ,
        (oldValue , newValue) -> { oldValue.addAll(newValue); return oldValue; }); 
}

Parameter 2 is what to add if name is a new value and parameter 3 the function how to merge the old with the new entry. You may want to override equals and hashCode of PhoneNumber, so you could write

        (oldValue , newValue) -> { oldValue.addAll(newValue); return new HashSet(oldValue); }); 

generating no duplicates.

  • Related