Search code examples
java-8java-stream

Pass a classifier function as a parameter to group data


I have 2 methods to group data based on a group of keys, i have a lot grouping criteria and i want to know if it is possible to use one method with multiples KeyGroups.

public Map<KeyGroup, List<Data>> groupData1(List<Data> data) {
            return data
            .stream()
            .collect(
                    Collectors.groupingBy(
                            d -> new KeyGroup(
                                    d.getCode(),
                                    d.getStartDate(),
                                    d.getEndDate()
                            )
                    )
            );
}


public Map<KeyGroup, List<Data>> groupData2(List<Data> data) {
            return data
            .stream()
            .collect(
                    Collectors.groupingBy(
                            d -> new KeyGroup(
                                    d.getCode()
            )
                    )
            );
}

Here is the KeyGroup class :

public class KeyGroup {

ArrayList<Object> keys;

public KeyGroup(Object... keys) {
    this.keys = new ArrayList<>();
    this.keys.addAll(Arrays.asList(keys));
}

public KeyGroup(ArrayList<Object> keys) {
    this.keys = keys;
}

public ArrayList<Object> getKeys() {
    return keys;
}

public void setKeys(ArrayList<Object> keys) {
    this.keys = keys;
}

@Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (!(o instanceof KeyGroup)) return false;
    KeyGroup keyGroup = (KeyGroup) o;
    return getKeys().equals(keyGroup.getKeys());
}

@Override
public int hashCode() {
    return Objects.hash(getKeys());
}

}

I'm wondering if it is possible to write one method to group Data and pass a classifier that way :

public Map<KeyGroup, List<Data>> groupData(List<Data> data, classifier) {
            return data
            .stream()
            .collect(Collectors.groupingBy(classifier)));
}

if yes how the classifier declaration will look like :

Function<? super Data, ? extends KeyGroup> classifier1 = ?;
Function<? super Data, ? extends KeyGroup> classifier2 = ?;

Call the method to group data

Map<KeyGroup, List<Data>> groupedData1 = groupData(data1, classifier1);
Map<KeyGroup, List<Data>> groupedData2 = groupData(data2, classifier2);

Thank you.


Solution

  • Yes. But did you really mean that any type would be a super class of Data? Here I use that T extends Data so Data would be the parent class. Actual examples of the hierarchical types involved would be helpful.

    • The classifier takes a Data object and returns a KeyGroup based on the fields.
    • Then the method is called with the Data list and the classifier.
    Function<Data, KeyGroup> classifier1 = d -> new KeyGroup(
            d.getCode(), d.getStartDate(), d.getEndDate());
    
    Function<Data, KeyGroup> classifier2 = d -> new KeyGroup(
                    d.getCode());
    
    Map<KeyGroup, List<Data>> map1 = groupData(data, classifier1);
    Map<KeyGroup, List<Data>> map2 = groupData(data, classifier2);
    
    public static <R extends KeyGroup, T extends Data> Map<R, List<T>> groupData(List<T> data,
            Function<T, R> classifier) {
        return data.stream().collect(Collectors.groupingBy(classifier));
    }