2

I have a Map like Map idMap = new Hashhap<String, AnyClass>; where the key is a specific Id (we deal with many ids), let's call it MyEntityId.

So for readability and in order to avoid problems in the usage of this map, I would like to transform it in a nicer: Map idMap = new Hashmap<MyEntityId, AnyClass>

now, the implementation of MyEntityId, using lombock, would just be:

@Data
@AllArgumentsConstructor
public class MyEntityId {
    private String id; 
}

The thing is that now every time i have a String representing MyEntityId i have to transform it new MyEntityId(myString) and doing so, i am losing the advantages of the internal representation of the String. So if before for the String "123abc" i had only one object in the JVM, now i have as many objects as the new MyEntityId(id) constructor is invoked.

How can i solve this?

7
  • 2
    In what way is HashMap<MyEntityId, AnyClass> nicer than HashMap<String, AnyClass>? Commented Oct 18, 2018 at 8:42
  • You know what you have to use as a key to access that map. You don't have to add a comment in the map declaration saying "hey this string is actually a MyEntityId", or let the other developers (or even you, tomorrow) looking around for usages for understanding what is that key. Other languages have solved this problem... Scala for instance have case classes. Commented Oct 18, 2018 at 8:49
  • Hmm, I'm not sure how a Scala case class would help with this - you'd still need to call MyCaseClass(theString). Maybe a type alias is what you mean? type MyEntityId = String Commented Oct 18, 2018 at 8:58
  • So you would create a wrapper type for each type of String in your system? Reminds me of ADA. Commented Oct 18, 2018 at 8:59
  • 2
    What you want is "typedef" but Java doesn't have it. Your work-around is adding a new wrapper object that at least adds 16 bytes for every key and some run-time overhead to access it, plus the overhead of newing the wrapper objects. Or you could just add a comment and accept the Java limitation. Commented Oct 18, 2018 at 9:17

3 Answers 3

4

Another way to solve readability problems would be to give your map a 'better' name.

Some options may be:

Map anyClassValueByMyEntityIDMap = new HashMap<String, AnyClass>();

Map anyClassValueForMyEntityIDMap = new HashMap<String, AnyClass>();

Map myEntityIDToAnyClassValueMap = new HashMap<String, AnyClass>();

This way it becomes clear that you have to use the MyEntityID to access the map without losing the advantages of the internal representation of the String.

Sign up to request clarification or add additional context in comments.

1 Comment

yeah, but you know, this id naming problem is present also in method signatures, and of course a good name helps, but was wondering if it is possible to do something more OO (like if you don't have MyEntityId object as a parameter, you cannot call my method)
0

You have to override both hashCode() and equals() methods:

public class MyEntityId {

    private String id;

    @Override
    public int hashCode() {
        return id.hashCode();
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        MyEntityId that = (MyEntityId) o;

        return id.equals(that.id);
    }
}

Notes:

3 Comments

overriding equals and hashcode will we have: new MyEntityId("abc") == new MyEntityId("abc") ?? I don't think so....
No for sure. But it will keep only one instance of MyEntityId("abc") in your map.
yes, this is sure. The problem i was rising is that i wouldn't have too many MyEntityId objects mapping always the same key...
0

You can always do what Java does for Strings - keep a map of existing instances, and use a factory method:

public class MyEntityId {
    private static Map<String, MyEntityId> instances = new HashMap<>();
    private MyEntityId(String id) { ... }
    public static MyEntityId valueOf(String id) { /* Get or create instance for that ID */ }
}

You still shouldn't rely on == on those, but then again you shouldn't rely on == for strings either.

I'm not sure it's worth the trouble though. Java is designed to prefer verbosity over performance (with the philosophy of "it's cheaper to buy better hardware than to hire better programmers"). If you agree with that reasoning, it may make sense to just use your wrapper and only optimize when you have evidence that it's a problem; if you disagree, using a good name as proposed in another answer might be enough.

1 Comment

And this is what I was thinking... But to avoid the map to grow too much, I should have a policy to evict elements from the map from time to time, sort of garbage collector, which brings me to use a cache, like guava, instead of a simple map, and the whole thing becomes definitely overengineered... I think this will remain a good intention.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.