Skip to content

Can I write a single List Stream for this?

Say, I have a Config class as below (I have included the class below)

The (parentType, subType) pair can have one of these {(0, 0), (X, 0), (X, Y)} Where X is > 0, and Y > 0. A value of 0 for, either parenttype or subtype, means it is a common configuration. A non-zero value is a specific configuration. Common configuration is applicable to all types, especially if there is no specific configuration.

If I have a list (List) of Config objects with the following values, values given in this format (category, parenttype, subtype, value):

("A", 0, 0, 5), ("A", 2, 0, 6), ("A", 2, 1, 9)
("B", 0, 0, 5), ("B", 2, 0, 6), 
("C", 0, 0, 5), ("C", 4, 0, 13)
("D", 4, 1, 11), ("D", 4, 0, 9)

If I am searching for configs for parent type = 2, and sub type = 1, I would like the following list as the result list. ("A", 2, 1, 9), ("B", 2, 0, 6), ("C", 0, 0, 5)

We do not get “D” objects because, under “D”, we do not have specific parent type 2, and it does not have common parent type 0.

Similarly, If I am searching for configs for parent type = 4, and sub type = 1, I would like the following list as the result list. ("A", 0, 0, 5), ("B", 0, 0, 5), ("C", 4, 0, 13), ("D", 4, 1, 11)

For parent type 4, and sub type 1 (just an idea is coded below) list.stream().filter(oneConf -> (4.equals(oneConfig.getParentType()) && 1.equals(oneConfig.getSubType()))).findFirst().orElse(null)

Above will give only 4 and 1 configuration. I could have two more list.stream().filter(condition for 4 and 0)… list.stream().filter(condition for 0 and 0)…

Given parenttype 4 and subtype 1, can we get the following result list, with Java 8 features, without writing many loops of code. (“A”, 0, 0, 5), (“B”, 0, 0, 5), (“C”, 4, 0, 13), (“D”, 4, 1, 11)

public class Config {
    String category;
    Integer parentType;
    Integer subType;
    Integer value;
    
    public Config() {}
    public Config(String pCategory, Integer pParentType, Integer pSubType, Integer pValue) {
        category = pCategory;
        parentType = pParentType;
        subType = pSubType;
        value = pValue;
    }
    
    
}

Answer

Looks like you want to match each type (parent/sub) where value matches input or where the value is 0. I also make the guess that you only want one Config from each category.

This might do what you want:

private static List<Config> filterConfig(List<Config> list, int parentType, int subType) {
    return new ArrayList<>(list.stream()
            .filter(c -> c.parentType == 0 || c.parentType == parentType)
            .filter(c -> c.subType == 0 || c.subType == subType)
            .collect(Collectors.toMap(
                    c -> c.category,
                    Function.identity(),
                    (p, q) -> p.parentType != 0 ? (p.subType != 0 ? p : q) : q
            )).values());

}

Test:

System.out.println("2/1");
filterConfig(list, 2, 1).forEach(System.out::println);
System.out.println("4/1");
filterConfig(list, 4, 1).forEach(System.out::println);

Output:

2/1
Config{category='A', parentType=2, subType=1, value=9}
Config{category='B', parentType=2, subType=0, value=6}
Config{category='C', parentType=0, subType=0, value=5}
4/1
Config{category='A', parentType=0, subType=0, value=5}
Config{category='B', parentType=0, subType=0, value=5}
Config{category='C', parentType=4, subType=0, value=13}
Config{category='D', parentType=4, subType=1, value=11}

Explanation

The first filter finds any Config matching parentType or where parentType is 0 (and removes any non hits).

The second filter finds any Config matching subType or where subType is 0.

The Collector.toMap() puts the objects in a map with category as the key. This makes sure we find at most 1 Config per category. The value is the Config object itself.

The third parameter to toMap() is a selector which picks one out of two objects when there is a key collision. The lambda I put should first pick whichever one has a non 0 parentType and secondly a non 0 subType. If non was found, pick the second one.

Disclaimer: I have only tested it for your input so that lambda might need tweaking.