Search code examples
javacollectors

Consider null and empty records as same in Collectors.groupingBy


I have a list of objects where a few records can have empty value property and a few can have null value property. Using Collectors.groupingBy I need both the records to be considered as same.

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

class Code {
    private String type;
    private String description;

    public static void main(String[] args) {
        List<Code> codeList = new ArrayList<>();
        Code c = new Code();
        c.setDescription("abc");
        c.setType("");
        codeList.add(c);
        Code c1 = new Code();
        c1.setDescription("abc");
        c1.setType(null);
        codeList.add(c1);

        Map<String, List<Code>> codeMap = codeList.stream()
                                                  .collect(Collectors.groupingBy(code -> getGroupingKey(code)));
        System.out.println(codeMap);
        System.out.println(codeMap.size());

    }

    private static String getGroupingKey(Code code) {
        return code.getDescription() +
                "~" + code.getType();
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }
}

The result of codeMap will have two records since it considers the empty string and the null value in the Type property as different. How can I achieve getting a single record here by considering both the null and empty records as same.


Solution

  • You can modify your getGroupingKey method like this:

    private static String getGroupingKey(Code code) {
        return code.getDescription() + "~" + (code.getType() == null ? "" : code.getType());
    }
    

    or like this:

    private static String getGroupingKey(Code code) {
        return code.getDescription() + "~" + Optional.ofNullable(code.getType()).orElse("");
    }
    

    or you might as well modify your getType() method directly as in:

    public String getType() {
        return type == null ? "" : type;
    }
    

    or:

    public String getType() {
        return Optional.ofNullable(type).orElse("");
    }
    

    Either should work the same. Pick one depending on your requirements I guess..

    If you add the following toString method to your Code class:

    @Override
    public String toString() {
        return "Code{" +
                "type='" + type + '\'' +
                ", description='" + description + '\'' +
                '}';
    } 
    

    .. with the modified getGroupingKey method (or the getType method) the output should be as follows:

    {abc~=[Code{type='', description='abc'}, Code{type='null', description='abc'}]}
    1
    

    Edit: You can also considering initializing the type to an empty String instead of null, then you would not need to modify anything:

    private String type = "";
    

    That might be an option too..