Search code examples
java-8java-stream

Filter values from HashMap<Integer, List<Object>> using Java8 concepts


My input:
A hashmap with structure HashMap<Integer, List<Parent>> where List<Parent> can have instances of Child objects inside it.

My Expectation:
Using Java8 streams concept extract a subset of the HashMap<Integer, List<Parent>> where objects inside the List are instaceOf Child Class and finder attribute of Child class has a specific value (ex. test)

Example:
Input

{
1=[Child [finder=test], Parent [], Child [finder=test]],
2=[Child [finder=text], Parent [], Parent []],
3=[Child [finder=test], Child [finder=test], Parent []]
}

Output

{
1=[Child [finder=test], Child [finder=test]],
3=[Child [finder=test], Child [finder=test]]
}

Code

Below is my class structure where there is Parent class and a Child class. Also there is Hashmap object where key is an Integer and values as List<Parent>.

class Parent {

    @Override
    public String toString() {
        return "Parent []";
    }
}

class Child extends Parent{
    public String finder;
    
    public Child(String f) {
        this.finder = f;
    }
    @Override
    public String toString() {
        return "Child [finder=" + finder + "]";
    }
}

Solution

  • Test data

    Map<Integer, List<Parent>> map = new HashMap<>();
    map.put(1, List.of(new Child("test"),new Parent(),
            new Child("test"), new Child("Foo"), new Parent()));
    map.put(2, List.of(new Parent(), new Child("text")));
    map.put(3, List.of(new Parent(), new Child("Bar"), 
            new Child("test"), new Parent()));
    
    

    Process

    • first map the map entries to individual entries of the key and each value element of the list.
    • then filter on instance of Child and finder type.
    • the group on key (integer) and put children into a list
    Map<Integer, List<Parent>> map2 = map.entrySet().stream()
            .flatMap((Entry<Integer, List<Parent>> e) -> e
                    .getValue().stream()
                    .map(v -> new AbstractMap.SimpleEntry<>(
                            e.getKey(), v)))
            .filter(obj -> obj.getValue() instanceof Child && 
                    ((Child)obj.getValue()).getFinder().equals("test"))
            .collect(Collectors.groupingBy(Entry::getKey,
                    Collectors.mapping(Entry::getValue,
                            Collectors.toList())));
    
    map2.entrySet().forEach(System.out::println);
    

    Prints

    1=[Child [finder=test], Child [finder=test]]
    3=[Child [finder=test]]
    

    I added a getter to the Child class to retrieve the finder value.