Issue
I have the following objects:
abstract class Fizz {
}
class Foo extends Fizz {
}
class Bar extends Fizz {
}
class Buzz extends Fizz {
}
Then i have a list of Fizz objects
List<Fizz> elements;
I want to group this elements in a
Map <Fizz, List<Fizz>> mapFizz;
And in the end be able to retrieve Map values as following:
List<Foo> fooList = mapFizz.get(Foo.class);
List<Bar> barList = mapFizz.get(Bar.class);
List<Buzz> buzzList = mapFizz.get(Buzz.class);
I want to be able to iterate over the Map keySet and check if an instance of the Buzz exist and then do something:
boolean buzzExists = mapFizz.keySet().stream()
.anyMatch(theKey -> instanceof Buzz.class)
buzzExists = true
when i am able to do
List<Buzz> buzzList = mapFizz.get(Buzz.class)
and also be able to add new key that extends Fizz;
class Look extends Fizz{
}
mapFizz.keyValues().add(Look.class);
What i've tried is this, and it works partially:
List<Fizz> elements = a list with many Fizz elements;
Map<? extends Class<? extends Fizz>, List<Fizz>> mapedElements = elements.stream()
.collect(
Collectors.groupingBy(
Fizz::getClass
)
)
The above solution partially works. I can retrieve the elements of the map with:
List<Foo> fooList = mapedElements.get(Foo.class);
But all of the keys are instance of the Class.class and i can't do:
mapedElements.keySet().stream()
.anyMatch( aKey -> aKey instanceof Foo.class) //always false
even if I am able to do
List<Foo> fooElements = mapedElements.get(Foo.class)
The workaround that i've found would be:
Map<String, List<Fizz>> mapedElements = elements.stream()
.collect(
Collectors.groupingBy(
element -> element.getClass().getName()
)
)
and then the key would be the import path of each class that extends Fizz: com.package.folder.Foo
But i wanted to check if maybe there is a cleaner solution Thanks
Solution
Your attempt is almost there:
Map<Class<? extends Fizz>, List<Fizz>> map = elements.stream().collect(
Collectors.groupingBy(Fizz::getClass, HashMap::new, Collectors.toList())
);
You don't need ? extends Class<? extends Fizz>
. Just Class<? extends Fizz>
is fine. Also note that you should use the overload of groupingBy
that allows you to specify a map supplier, since by default, the map returned by groupingBy
is not guaranteed to be mutable, and you seem to want to add things to the map later on.
Just to clarify, this is how you could add a new key to the map:
map.put(AnotherFizzSubclass.class, new ArrayList<>());
There is no keyValues()
method in Map
:)
To check if a key is in the map, just use containsKey
:
if (map.containsKey(Foo.class)) {
doSomething();
}
This will doSomething()
only if the map has exactly Foo.class
. If you also want to doSomething
in case the map has a subclass of Foo
(similar to the behaviour of instanceof
), then you can do:
if (map.keySet().stream().anyMatch(Foo.class::isAssignableFrom)) {
doSomething();
}
Answered By - Sweeper
Answer Checked By - Pedro (JavaFixing Volunteer)