I need to store lots of objects that belong to different classes:
ClassA {...}
ClassA1 extends ClassA {...}
ClassA2 extends ClassA {...}
ClassA2a extends ClassA2 {...}
ClassB {...}
Now I need to find a way to store all these objects in a way that allows me to efficiently get all objects that belong to a particular class and its inherited child classes. For example, this imaginary code
getObjects(ClassA2)
would return a list of all stored objects that belong to ClassA2 or ClassA2a.
I believe a tree collection of some sort would be suitable, but I can't think of any way to implement it. Any ideas?
(Background: I am creating a simple java game, in which there's number of sprites that I need to manage, while some of those sprites share similar properties. When I check for events like collisions, I need to get all objects that extend EnemySprite and compare their coordinates with the player's sprite.)
There are several ways how to approach this. One would be, e.g., to generate strings like ParentClass1:ChildClass2:ChildClass1: for every object and use them as a key to a TreeMap or Trie which you would then traverse.
Here is a simpler solution, though. The following class contains a map from class to all objects implementing it. The only trick is adding an object to all buckets where it belongs:
public class HierarchyMap {
private final Map<Class<?>, List<Object>> map = new HashMap<>();
public void add(Object o) {
Class<?> clazz = o.getClass();
while (clazz != Object.class) {
List<Object> list = map.computeIfAbsent(clazz, c -> new ArrayList<>());
list.add(o);
clazz = clazz.getSuperclass();
}
}
public List<Object> getByClass(Class<?> clazz) {
return map.get(clazz);
}
}
Usage:
public class A { public String toString() { return "A"; } }
public class B extends A{ public String toString() { return "B"; } }
public class C extends B { public String toString() { return "C"; } }
// ...
HierarchyMap hierarchyMap = new HierarchyMap();
hierarchyMap.add(new A());
hierarchyMap.add(new B());
hierarchyMap.add(new C());
System.out.println(hierarchyMap.getByClass(B.class));
// prints [B, C]
Mifeet seems to have literally answered your question, but I suspect you shouldn't be trying to do what you're proposing to do. Why not just have a master list of all objects that might collide, then filter it as needed using instanceof?
This is conceptually a lot easier than what you're proposing to do, and the efficiency impact probably isn't that big. (In general, you will probably hear or have heard the mantra: Don't try to optimize too early.)
To be honest, I'm not sure you realize that filtering for EnemySprite will get you all object instances of its subclasses as well.
public class CollisionChecker(){
private List colliders;
public CollisionChecker(){
colliders = new ArrayList<Object>();
}
public void addCollider(Object o){
colliders.add(o);
}
public List<EnemySprite> getEnemySprites(){
List<EnemySprite> enemies = new ArrayList<EnemySprite>();
for (Object o : colliders)
if (o instanceof EnemySprite)
enemies.add((EnemySprite)o);
return enemies;
}
}
If you're storing the objects in a List<Object>, call Class#isInstance() on each element, adding them to another List if isInstance() returns true.
List<Object> objects = new ArrayList<>();
public <T> List<T> getObjects(Class<T> desiredClass) {
List<T> desiredObjects = new ArrayList<>();
for (Object o : objects)
if (desiredClass.isInstance(o))
desiredObjects.add((T)o);
return desiredObjects;
}
getObjects(EnemySprite.class); // call it like this
If you just want collision detection, then I would add them to a static collection in the ancestor class. This would be the most efficient solution.
If you want to all descendants for a class you should check out the reflection APIs. Yes, they're said to be slow, but I have doubts that it matters enough for things that aren't computed for every frame. And for things that you need in every frame tree traversal would inefficient anyway. (#Miffet's suggestion of string comparison would probably be even slower than regular reflection.)
Related
I have the following HashMap:
HashMap<Integer, Object> ItemsData = new HashMap<Integer, Object>();
In the HashMap, I have objects of various classes, such as
ClassOne
ClassTwo
ClassThree
Each class has its own methods, attributes etc.
How do I refer to proper method, depending on the instance of the class?
For example:
ItemsData.get(5).bark() -> because item 5 is instance of class 1 which has method bark
ItemsData.get(2).jump() -> because item 2 is instance of class 2 which has method jump
ItemsData.get(6).fire() -> because item 6 is instance of class 3 which has method fire
Generally having such an untyped map in the first place is a code smell. You didn't explain enough of the underlying problem you're trying to solve to give you good advice on a nicer solution; merely to point out that this solution probably is severely suboptimal.
If you insist on using it, you can use instanceof to check if a value is of a certain type, and use a cast operator to treat it as a certain type:
Map<Integer, Object> badIdea = new ....;
...
Object secondValue = badIdea.get(1);
if (secondValue instanceof Dog) {
Dog dog = (Dog) secondValue;
dog.bark();
}
Create an interface, eg:
interface Actor {
void act();
}
And keep a collection of those:
Map<Integer, Actor> ItemsData = new HashMap<>();
Have your classes implement Actor, either directly:
public ClassOne implements Actor {
public void act() {
// make a barking noise
}
// other fields and methods
}
or indirectly:
public ClassOne implements Actor {
public void act() {
bark();
}
public void bark() {
// make a barking noise
}
// other fields and methods
}
Then invoke Actor's method instead of the original specific method:
itemsData.get(5).act();
You can also invoke all conveniently:
itemsData.forEach(Actor::act);
For a full discussion of this idea, read Liskov substitution principle.
Ok so here is my problem.
I have a "superclass" that I will call "Animal". Each "Animal" possesses a unique ID
public class Animal {
#Id
private ObjectId id;
}
I have several classes which extend "Animal" (Lion, Zebra, Penguin...)
public class Penguin extends Animal...
Finally, I have a class that I will call "NoahsArk" which possesses specific "Animal" attributes, such as
public class NoahsArk {
private Zebra zebra;
private Lion lion;
private Penguin penguin;
}
Finally, I want to do a few things :
Being able to return the list of "Animals" of my "NoahsArk"
Being able to return the "Animal" whose ID equals a given ID (given #1, I know how to do this)
Being able to remove (set as null) the "Animal" whose ID equals a given ID
I know that in a such a simple case, for #1, I would just have to do something like
public List<Animal> getAnimals() {
List<Animal> animals= new ArrayList<>();
if (lion != null) {
animals.add(lion);
}
...
return animals;
}
And for #2, I can simply do this
public Animal getAnimalById(String id) {
return getAnimals().stream().filter(a -> a.getId().toString().equals(id)).findFirst().orElse(null);
}
But let's assume that NoahsArk has a lot of "Animals", which are not stored in a List<Animal> but declared separately.
In such a case , is there an efficient way, at least in terms of written lines of code, to do things #1 and #3 ?
My first instinct, for #3, was to use the output of #1 and then set the "Animal" whose ID matches the input to null, but that just won't work.
Just because you have to provide getter and setter methods, you don't have to store each value into its own field. Being free in choosing an internal representation is what encapsulation is all about:
public class NoahsArk {
private final Map<Class<? extends Animal>, Animal> stored = new HashMap<>();
private <A extends Animal> A get(Class<A> type) {
return type.cast(stored.get(type));
}
private <A extends Animal> void set(Class<A> type, A animal) {
if(animal == null) stored.remove(type);
else stored.put(type, animal);
}
public Zebra getZebra() {
return get(Zebra.class);
}
public void setZebra(Zebra zebra) {
set(Zebra.class, zebra);
}
public Lion getLion() {
return get(Lion.class);
}
public void setLion(Lion lion) {
set(Lion.class, lion);
}
public Penguin getPenguin() {
return get(Penguin.class);
}
public void setPenguin(Penguin penguin) {
set(Penguin.class, penguin);
}
public List<Animal> getAll() {
return new ArrayList<>(stored.values());
}
public void remove(ObjectId id) {
stored.values().removeIf(a -> a.getId().equals(id));
}
}
Instead of having a map from type to animal, you could have a map from id to animal, making remove more efficient, at the expense of having less efficient getters and setters. Or you have both maps, making all operations fast, but having a higher memory consumption and needing more care to have both in a consistent state.
In real life, I'd ask the customer for clarification first. Shouldn't there be two of each? Otherwise, you may end up with a software fulfilling the specification literally, but still having a dissatisfied customer...
You can use reflection.
NoahsArk.class.getDeclaredFields() will give you all the Field objects in NoahsArk, and since the fields that we are talking about are private, you might have to do field.setAccessible( true ); on each one of them before trying to do anything with it.
For more information, see https://docs.oracle.com/javase/7/docs/api/java/lang/reflect/Field.html, but in just a few words, here is what you have at your disposal using reflection:
field.getDeclaredAnnotations() will give you the annotations on that field so that you can check whether it has the #Id annotation, or field.getAnnotation( Id.class ) will give you the Id annotation or null if the field does not have that annotation.
field.get( instanceOfNoahsArk ) will give you the value of a field.
field.set( instanceOfNoahsArk, null ) will set a field to null.
Given the silly nature of the exercise that you have been set, I suspect that the people marking your solution would be happy with simple, easy to understand code. Basically, like your original idea, or simpler.
It would also be possible to use Java reflection to implement the methods in parts 1 to 3 of your question. The code would be complicated and slower. But it would certainly be doable. If you want to learn more, I recommend that you start with the Oracle tutorials on this subject:
The Java™ Tutorials: Trail: The Reflection API
But would it be more efficient "in terms of lines of code"?
Lines of code is not a measure of efficiency. Not of code performance or of programmer efficiency. And this is not a sensible thing to optimize for. (You shouldn't get bonus marks for fewer lines of code, IMO, unless the code is actually more maintainable. It probably won't be in this case.)
If you actually mean programmer efficiency, it is debatable. You are trading off the complexity of reflection against the repetitiousness of the simple version. Taking a section of code that works on one field and then cut-and-pasting-and-modifying it for multiple fields is not hard work. Unless you have a really large number of these fields, cut and paste may be more efficient.
A question for you to ask yourself: given what they have taught you so far, to you think they are expecting a complicated solution? Have they taught you about reflection (yet)? Is it even on the syllabus for your course?
In the real world ... you would not solve this problem this way. You would redesign the API to be more practical.
Here's a simple solution, based on an internal array:
class NoahsArk
{
private static final int ZEBRA = 0;
private static final int LION = 1;
private static final int PENGUIN = 2;
private Animal[] animals = new Animal[3];
public Zebra getZebra()
{
return (Zebra)animals[ZEBRA];
}
public void setZebra(Zebra zebra)
{
animals[ZEBRA] = zebra;
}
public Lion getLion()
{
return (Lion)animals[LION];
}
public void setLion(Lion lion)
{
animals[LION] = lion;
}
public Penguin getPenguin()
{
return (Penguin)animals[PENGUIN];
}
public void setPenguin(Penguin penguin)
{
animals[PENGUIN] = penguin;
}
public List<Animal> getList()
{
ArrayList<Animal> list = new ArrayList<Animal>();
for(Animal animal : animals)
{
if(animal!=null)
list.add(animal);
}
return list;
}
public Animal getById(ObjectId id)
{
for(Animal animal : animals)
{
if(animal!=null && animal.getId().equals(id))
return animal;
}
return null;
}
public void removeById(ObjectId id)
{
for(int i=0;i<animals.length;i++)
{
if(animals[i]!=null && animals[i].getId().equals(id))
{
animals[i] = null;
return;
}
}
}
}
The whole time I thought, if I am using a List like List<Thing> things = new ArrayList<>() all items in this list are of Type Thing. Yesterday i was taught the other way.
I've created the following stuff and wonder why it is like it is.
An Interface Thing
public interface Thing {
String getType();
String getName();
}
A class ObjectA
public class ObjectA implements Thing {
private static final String TYPE = "Object A";
private String name;
public ObjectA(String name) {
this.name = name;
}
#Override
public String toString() {
final StringBuffer sb = new StringBuffer("ObjectA{");
sb.append("name='").append(name).append('\'');
sb.append('}');
return sb.toString();
}
#Override
public String getType() {
return TYPE;
}
#Override
public String getName() {
return name;
}
// equals and hashCode + getter and setter
}
A class ObjectB
public class ObjectB implements Thing {
private static final String TYPE = "Object B";
private String name;
private int value1;
private String value2;
private boolean value3;
public ObjectB(String name, int value1, String value2, boolean value3) {
this.name = name;
this.value1 = value1;
this.value2 = value2;
this.value3 = value3;
}
#Override
public String getType() {
return TYPE;
}
#Override
public String getName() {
return name;
}
#Override
public String toString() {
final StringBuffer sb = new StringBuffer("ObjectB{");
sb.append("name='").append(name).append('\'');
sb.append(", value1=").append(value1);
sb.append(", value2='").append(value2).append('\'');
sb.append(", value3=").append(value3);
sb.append('}');
return sb.toString();
}
// equals and hashCode + getter and setter
}
The main method
public static void main(String[] args) {
final List<Thing> things = new ArrayList<>();
final ObjectA objA = new ObjectA("Thing 1");
final ObjectB objB = new ObjectB("Thing 2", 123, "extra", true);
things.add(objA);
things.add(objB);
// The List doesn't contain Thing entities, it contains ObjectA and ObjectB entities
System.out.println(things);
for(final Thing thing : things) {
if (thing instanceof ObjectA) {
System.out.println("Found Object A: " + thing);
final ObjectA object = (ObjectA) thing;
}
if (thing instanceof ObjectB) {
System.out.println("Found Object B: " + thing);
}
}
}
The output of this method is:
[ObjectA{name='Thing 1'}, ObjectB{name='Thing 2', value1=123, value2='extra', value3=true}]
So i assume i've ObjectA entities and ObjectB entities in my List<Thing>.
Question: Can someone provide a link (or some keywords which can be used for searching), which explain this behavior, or can explain it to me?
additional Question: I've started to filter this List<Thing> with instanceof but i have read instanceof and casting are bad practice (e.g. no good model design). Is the are "good" way to filter this List for all Types of ObjectA to perform only on these objects some operations?
You should avoid instanceof check in your additional question example. When you work with List items, it should be sufficient to have interface methods available. If you need to do something with only ObjectA or ObjectB, I suggest to use another List with only ObjectA or ObjectB. For example you can define different methods to do Thing specific job and ObjectB specific job:
public void processThings(List<Thing> things) {
for(final Thing thing : things) {
// we work only with methods that provided by interface Thing
System.out.println(thing.getType());
System.out.println(thing.getName());
}
}
public void processObjectsB(List<ObjectB> objectsB) {
// here we do some specific things with only B objects,
// assuming class ObjectB has an additional method doSomeSpecificB()
for(final ObjectB objectB : objectsB) {
objectB.doSomeSpecificB();
}
}
I have a garden that contains potatoes, carrots and broccoli. I have a very strict rule - I won't plant anything in the garden that I can't eat. So no poison ivy here!
So this is a Garden<Edible> - everything I plant in the garden has to be edible.
Now class Potato implements Edible means that every potato is edible. But it also means that I can plant a potato in my garden. Likewise, class Carrot implements Edible - all carrots are edible, and I'm allowed to plant carrots.
It's a dark night, and I'm hungry. I go out to my garden and put my hand on something in the garden. I can't see what it is, but I know that everything in my garden is edible. So I pull it out of the garden, and take it inside to cook and eat. It doesn't matter what I've grabbed - I know it will be something I can eat.
Because this is a Garden<Edible>. It may or may not contain Potato objects. It may or may not contain Broccoli objects. It does not contain PoisonIvy objects.
Now, translate that all to your example. You have class ObjectA implements Thing - which means that every ObjectA is a Thing. You have class ObjectB implements Thing - which means that every ObjectB is a Thing. And you have a List<Thing> - a List that can contain ObjectA objects, ObjectB objects, and any other object of any class that implements Thing. What you can't put in it is an object of any class that doesn't implement Thing.
Can someone provide a link (or some keywords which can be used for searching), which explain this behavior, or can explain it to me?
This behaviour is called "polymorphism". Basically, since ObjectA and ObjectB implements Thing, instances of ObjectA and ObjectB can be used like a Thing object. In your code, you added them to a list that can contain Thing objects.
Note how even if those objects are now of (compile time) type Thing, at runtime they still know what they are. When you call toString on them, the respective overridden toString methods in ObjectA and ObjectB will be called. It is as if the Thing "morphs" into ObjectA or ObjectB.
Is the are "good" way to filter this List for all Types of ObjectA to perform only on these objects some operations?
The reason why people say this is bad practice is because if you want to do different things depending whether the object is ObjectA or ObjectB, why did you make them implement Thing and make a list of Thing to store them? You could have just used a List<Object>. The real advantage of using List<Thing> is that you avoid knowing what actual objects are in there when you are working with the list. All you know is that the things inside the list implement Thing and you can call methods declared in Thing.
So if you need to filter the list to separate the two types, you could have just created two lists to store them in the first place. One for ObjectA and one for ObjectB. Obviously, this is not always possible, especially if the list comes from somewhere else (like a external library). In that case, your current code is fine.
things is a List<Thing>. That means that at compile time, Java will ensure that any object that you write to things is a Thing. As ObjectA and ObjectB both implement Thing the actual implementation of any member of things can be ObjectA or ObjectB. This is by design and that feature is called polymorphism: object of different classes share a common interface and can be accessed thrrough that interface independently of their actual type. For example you could use:
for(final Thing thing : things) {
System.stdout.println("Found a " + thing.getType() + " named " + thing.getName());
}
Using instanceof and casting is not necessarily bad practice and can have correct use case. But it is often a hint that the hierachy of classes and interfaces has not been properly designed. Ideally, if you have to process a Thing, you should not wonder about its actual class: you have a Thing, and using Thing methods should be enough.
In that sense, instanceof is at the same level as reflection is: it is a low level tool that allows to see what is hidden under the hood. And any time you use it, you should ask you whether polymorphism could not be enough.
I'm working on a project where I've got multiple classes to hold data loaded from an xml file. The problem I would like to solve is that almost all of the classes have these methods:
addSingle[objectName]
addMultiple[objectName]
However, there may be more types of objects in a class that need to be added this way. For example:
class Airspace {
private List airports;
private List waypoints;
...
public void addSingleAirport(Airport a) {...}
public void addMultipleAirports(Airport[] a {...}
public void addSingleWaypoint(Waypoint w) {...}
public void addMultipleWaypoints(Waypoint w) {...}
}
I was thinking of putting those addSingle and addMultiple methods into an interface and then decide, when implementing the methods, according to the objects type to which list it should be added, but wouldn't that be stupid?
Is there any more efficient way to do this? I want to avoid writing these methods into every class that needs them with the specific objects because they all do exactly the same.
I don't think you approach is stupid but it is true this type checking can affect performance.
Nevertheless, expanding your approach, I would keep a map of lists indexed by Class:
class Airspace {
private Map<Class, List<Object>> lists = new HashMap();
public void addSingle(Object o)
{
List<Object> list = lists.get(o.getClass());
if(list == null)
{
list = new ArrayList();
lists.put(o.getClass(), list);
}
list.add(o);
}
...
...
public void addAirports(Airport... a ){...}
should be able to handle all cases - or did I miss something?
A very general
public void addThings( Object... o )
can be written, but do you want to lose readability and type checking? Runtime errors if there's no matching Foo-list in an object? Shudder.
You can use an Abstract class with generics then extends it from you class. Something like this:
public abstract class AbstractSomeclass<E>{
public void addObject(E... obj, List<E>){
//yourcode here
}
}
Then in your class:
public class MyClass extends AbstractSomeclass<Airport>{
public List<Airport> list;
//.....
public void someMethod(){
super.addObject( airportObject, list );
}
}
Maybe there is some minor errors (on this code), but hold on with the idea.
I'm attempting to make a class that will convert ArrayLists of objects into ArrayLists of other objects. i.e.
ArrayList<Foo> convert(ArrayList<Bar> input){
//conversion logic
}
ArrayList<Bar> convert(ArrayList<Foo> input){
//conversion logic
}
Unfortunately Java doesn't want to have two functions with the same name and what it believes to be the same inputs and outputs.
I'm attempting to go a different route. Instead of multiple functions with the same name, I want to make one function that accepts an ArrayList, determines which type of object is inside, does the proper conversion, and returns an ArrayList:
ArrayList convert(ArrayList input){
//conversion logic for Foo
//conversion logic for Bar
}
Is something like this possible?
How about an interface:
public class Converter<From, To> {
List<To> convert(List<From> input);
}
And then have as many implementations as you want. For example:
private static final Converter<Foo, Bar> fooToBarConverter = new Converter<Foo, Bar>() {
public List<Bar> convert(List<Foo> input) {
...
}
}
Try:
public <T, U> ArrayList<U> convert(Class<T> typeIn, ArrayList<T> input){
// dispatch on typeIn
}
Or better yet
public <T, U, V extends ArrayList<U>> V convert(Class<T> typeIn,
Class<V> typeOut, ArrayList<T> input){
// dispatch on typeIn
return typeOut.cast(yourConversionResult);
}
Because you might return ArrayList<Foo> or ArrayList<Bar> within the same method and having the proper cast will help you return them without compiler warnings.
Edit: The return type cast for the second sample wasn't going to work. Tried to fix it
You are basically describing a "map" in terms of functional programming. Take a list of objects, apply some operation to each object and accumulate the results in another list. There are libraries out there that implement this stuff already, although i haven't looked recently. I know commons collections has this for pre-generics collections.
the gist of the solution is (similar to mmeyers solution):
public interface Function<From,To> {
public To apply(From);
}
public <From,To> List<To> map(List<From> fromList, Function<From,To> fun) {
// call fun.apply() on every element in fromList and return a new result list ...
}