When should I create a class for a Map key? - java

I'm using Java 6.
Suppose I have a class which I would like to save its instances into a map. Later on I would like to retrieve instances using only the "key fields". I'll ignore field modifiers, getters, and setters for conciseness.
class A {
String field1;
String field2;
String field3;
String field4;
//more fields
public int hashCode(){
//uses only field1 and field2
}
public boolean equals(Object o){
//uses only field1 and field2
}
}
Since Java's standard API doesn't have the MultikeyMap and I don't want to use 3rd party libraries, I have a choice of
1) creating a new class KeyA to represent the key of a map
2) use A itself as the key and populate only the "key fields" when I need to retrieve objects from a map
3) nest the maps, e.g. HashMap<String, HashMap<String, A>>
4) other workarounds
What do people normally use and when?

Given your recent edit, you should be fine to use instances of class A as keys in this situation. Lookups will be done based on the semantics of equals() and hashCode(), so this will cause instances to be retrieved by only the "key fields". Hence the following code would work as you intend:
final Map<A, String> map = new HashMap<A, Object>();
final A first = new A("fe", "fi", "fo", "fum");
map.put(first, "success");
// later on
final A second = new A ("fe", "fi", "foo", "bar");
System.out.println(map.get(second)); // prints "success";
Having said that, your description of option 2 makes me a little concerned that this might not be the most sensible option. If you create a Map<A, String>, that's a mapping from instances of class A to strings. Yet your second point implies that you want to think of it as a mapping from pairs of key fields to strings. If you're going to usually look up values based on a couple of "raw" strings, then I'd advise against this. It feels wrong (to me), to create a "fake" instance of A just to do a lookup - so in this case, you probably should create a key class that embodies the pair of strings as described in option 1. (You could even embed instances of these within your A objects to hold the key fields).
There's a similar argument for or against option 3, too. If the strings really are conceptually hierarchical, then it might well make sense. For example, if field1 was Country, and field2 was Town, one could definitely argue that the nested maps make sense - you have a mapping from country, to the map of Town->A relations within that country. But if your keys don't naturally compose in this fashion (say, if they were (x, y) coordinates), this would again not be a very natural way to represent the data, and a single-level map from XYPoint to value would be more sensible. (Likewise, if you never use the two-level map except to always go straight through both layers, one could argue the one-level map still makes more sense.)
And finally, as for option 4 -if you're always mapping to A itself, and storing the key as its own value (e.g. if you want to canonicalise your A instances, a bit like String.intern()) then as was pointed out you needn't use a Map at all, and a Set will do the job here. The Map is useful when you want to establish relationships between different objects, whereas a Set automatically gives you the uniqueness of objects without any extra conceptual overhead.
If you do use the class itself as a key, be warned though that objects should only generally be used as keys if their hashCode (and the behaviour of equals) won't change over time. Typically this means the keys are immutable, though here you could afford to have mutable "non-key" fields. If you were to break this rule, you'd see odd behaviour such as the following:
// Populate a map, with an A as the key
final Map<A, String> map = new HashMap<A, Object>();
final A a = new A("one", "two", "three", "four");
map.put(a, "here");
// Mutate a
a.setField1("un");
// Now look up what we associated with it
System.out.println(map.get(a)); // prints "null" - huh?
System.out.println(map.containsKey(a)); // prints "false"

I'd create an Index class, something like this (warning: untested code), to abstract out the indexing functionality. Why Java doesn't have something like this already is puzzling to me.
interface Indexer<T, K>
{
/** extract key from index */
public K getIndexKey(T object);
}
class Index<T,K>
{
final private HashMap<K,List<T>> indexMap = new HashMap<K,List<T>>();
final private Indexer<T,K> indexer;
public Index(Indexer<T,K> indexer)
{
this.indexer = indexer;
}
public void add(T object) {
K key = this.indexer.getIndexKey(object);
List<T> values = this.indexMap.get(key);
if (values == null)
{
values = new ArrayList<T>();
this.indexMap.put(key, values);
}
values.add(object);
}
public void remove(T object) {
K key = this.indexer.getIndexKey(object);
List<T> values = this.indexMap.get(key);
if (values != null)
{
values.remove(object);
}
}
public List<T> lookup(K key) {
List<T> values = this.indexMap.get(key);
return values == null
? Collections.emptyList()
: Collections.unmodifiableList(values);
}
}
example relevant to your class A:
Index<A,String> index1 = new Index<A,String>(new Indexer<A,String>() {
#Override public String getIndexKey(A object)
{
return object.field1;
}
});
Index<A,String> index2 = new Index<A,String>(new Indexer<A,String>() {
#Override public String getIndexKey(A object)
{
return object.field2;
}
});
/* repeat for all desired fields */
You would manually have to add and remove entries from the indices, but all the grungework below those operations is handled by the Index class.

Your class has "key fields". I would suggest to create a parent class, ParentA, with those key fields (which certainly map to a concept in your domain) and inherit this class in your child class A.
Override hashCode() and equals() in the ParentA class.
Use a Map<ParentA, A> to store your A instances and give the instance as key and value.
To retrieve a specific A instance, create a new ParentA instance, pA, with your key fields set, and do
A a = map.get(pA);
That's it.
Another way is to create a AIdentifier class with key fields and add an instance as A property id. So you add your instance with map.put(a.id, a); That's inheritance vs composition pattern discussion :)

Related

Association Object-Enum, which is more efficient?

Let's say I have the following enum:
public enum Example{
A,
B,
C,
D,
E;
}
I need an association between an Object and the enum above.
In my specific case, one object must have only one Example association, except for Example.B and Example.C, because an Object could eventually have these two associations.
Right now my solution is: I have created an object wrapper of a five booleans, each boolean representing an enum. When it's true it means there is an association with the represented Example constant.
In the end I have an association between the Object and the Wrapper.
The thing is that I believe it's unnecessary to carry around these five booleans, because in most cases only boolean will be true and in very few cases only two booleans will be true. But never more than two.
Then I thought that maybe an association between an Object and an ArrayList<Example> would be more appropriate. Or maybe even better an array Example[] of size 2.
What do you think?
Please, if you have any different suggestions let me know.
You may add a field of type Set to the class you like to associate with one or more Examples. Add those Example values to the set you like to associate your type with.
Code Example
public class MyObject {
public static MyObject createAssociatedWithBAndC() {
return new MyObject(Example.B, Example.C);
}
public static MyObject create(Example example) {
return new MyObject(example);
}
private final Set<Example> examples = new HashSet<>();
private MyObject(Example... examples) {
for (Example example : examples) {
this.examples.add(example);
}
}
}
And in case you really, really need to avoid using too much memory try it this way (reflecting your requirements) which uses null as associated with B and C:
public class MyObject {
private final Example example;
private MyObject(Example example) {
this.example = example;
}
public Set<Example> getExamples() {
return example == null
? EnumSet.of(Example.B, Example.C)
: EnumSet.of(example);
}
}
Now you hold only one or none Examples while none means: associated with B and C. And if instantiation the EnumSet is as well to expansive try:
public class MyObject {
private final Set<Example> examples;
private MyObject(Example example) {
this.example = example == null
? EnumSet.of(Example.B, Example.C)
: EnumSet.of(example);
}
public Set<Example> getExamples() {
return examples;
}
}
Which is the goal to have boolean values to transform them into Example enum values ?
This is better :
Then I thought that maybe an association between an Object and an
ArrayList would be more appropriate. Or maybe even better an
array Example[] of size 2.
However, array use has limitations.
An array that has a variable size as in your case is harder to manipulate as you have to remember how many elements are effectively contained in (generally with a integer value).
Besides, if the size of 2 always goes to 3, you have to change its declaration.
I would prefer a List implementation or a Set implementation as the enum are constant and unique values. You could use an EnumSet implementation.
You could instantiate them like that :
EnumSet<Example> examples = EnumSet.of(Example.A, Example.B);

Java - Check if reference to object in Map exists

A few weeks back I wrote a Java class with the following behavior:
Each object contains a single final integer field
The class contains a static Map (Key: Integer, Content: MyClass)
Whenever an object of the class is instantiated a look-up is done, if an object with the wanted integer field already exists in the static map: return it, otherwise create one and put it in the map.
As code:
public class MyClass
{
private static Map<Integer, MyClass> map;
private final int field;
static
{
map = new HashMap<>();
}
private MyClass(int field)
{
this.field = field;
}
public static MyClass get(int field)
{
synchronized (map)
{
return map.computeIfAbsent(field, MyClass::new);
}
}
}
This way I can be sure, that only one object exists for each integer (as field). I'm currently concerned, that this will prevent the GC to collect objects, which I no longer need, since the objects are always stored in the map (a reference exists)...
If I wrote a loop like function like this:
public void myFunction() {
for (int i = 0; i < Integer.MAX_VALUE; i++) {
MyClass c = MyClass.get(i);
// DO STUFF
}
}
I would end up with Integer.MAX_VALUE objects in memory after calling the method. Is there a way I can check, whether references to objects in the map exists and otherwise remove them?
This looks like a typical case of the multiton pattern: You want to have at most one instance of MyClass for a given key. However, you also seem to want to limit the amount of instances created. This is very easy to do by lazily instantiating your MyClass instances as you need them. Additionally, you want to clean up unused instances:
Is there a way I can check, whether references to objects in the map exists and otherwise remove them?
This is exactly what the JVM's garbage collector is for; There is no reason to try to implement your own form of "garbage collection" when the Java core library already provides tools for marking certain references as "not strong", i.e. should refer to a given object only if there is a strong reference (i.e. in Java, a "normal" reference) somewhere referring to it.
Implementation using Reference objects
Instead of a Map<Integer, MyClass>, you should use a Map<Integer, WeakReference<MyClass>> or a Map<Integer, SoftReference<MyClass>>: Both WeakReference and SoftReference allow the MyClass instances they refer to to be garbage-collected if there are no strong (read: "normal") references to the object. The difference between the two is that the former releases the reference on the next garbage collection action after all strong references are gone, while the latter one only releases the reference when it "has to", i.e. at some point which is convenient for the JVM (see related SO question).
Plus, there is no need to synchronize your entire Map: You can simply use a ConcurrentHashMap (which implements ConcurrentMap), which handles multi-threading in a way much better than by locking all access to the entire map. Therefore, your MyClass.get(int) could look like this:
private static final ConcurrentMap<Integer, Reference<MyClass>> INSTANCES = new ConcurrentHashMap<>();
public static MyClass get(final int field) {
// ConcurrentHashMap.compute(...) is atomic <https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ConcurrentHashMap.html#compute-K-java.util.function.BiFunction->
final Reference<MyClass> ref = INSTANCES.compute(field, (key, oldValue) -> {
final Reference<MyClass> newValue;
if (oldValue == null) {
// No instance has yet been created; Create one
newValue = new SoftReference<>(new MyClass(key));
} else if (oldValue.get() == null) {
// The old instance has already been deleted; Replace it with a
// new reference to a new instance
newValue = new SoftReference<>(new MyClass(key));
} else {
// The existing instance has not yet been deleted; Re-use it
newValue = oldValue;
}
return newValue;
});
return ref.get();
}
Finally, in a comment above, you mentioned that you would "prefer to cache maybe up to say 1000 objects and after that only cache, what is currently required/referenced". Although I personally see little (good) reason for it, it is possible to perform eager instantiation on the "first"† 1000 objects by adding them to the INSTANCES map on creation:
private static final ConcurrentMap<Integer, Reference<MyClass>> INSTANCES = createInstanceMap();
private static ConcurrentMap<Integer, Reference<MyClass>> createInstanceMap() {
// The set of keys to eagerly initialize instances for
final Stream<Integer> keys = IntStream.range(0, 1000).boxed();
final Collector<Integer, ?, ConcurrentMap<Integer, Reference<MyClass>>> mapFactory = Collectors
.toConcurrentMap(Function.identity(), key -> new SoftReference<>(new MyClass(key)));
return keys.collect(mapFactory);
}
†How you define which objects are the "first" ones is up to you; Here, I'm just using the natural order of the integer keys because it's suitable for a simple example.
Your function for examining your cache is cringe worthy. First, as you said, it creates all the cache objects. Second, it iterates Integer.MAX_VALUE times.
Better would be:
public void myFunction() {
for(MyClass c : map.values()) {
// DO STUFF
}
}
To the issue at hand: Is it possible to find out whether an Object has references to it?
Yes. It is possible. But you won't like it.
http://docs.oracle.com/javase/1.5.0/docs/guide/jvmti/jvmti.html
jvmtiError
IterateOverReachableObjects(jvmtiEnv* env,
jvmtiHeapRootCallback heap_root_callback,
jvmtiStackReferenceCallback stack_ref_callback,
jvmtiObjectReferenceCallback object_ref_callback,
void* user_data)
Loop over all reachable objects in the heap. If a MyClass object is reachable, then, well, it is reachable.
Of course, by storing the object in your cache, you are making it reachable, so you'd have to change your cache to WeakReferences, and see if you can exclude those from the iteration.
And you're no longer using pure Java, and jvmti may not be supported by all VM's.
As I said, you won't like it.

Map which allows to provide the equals-comparator and the hashing function separately

While trying to model polynomials, in particular their multiplication, I run into the following problem. During the multiplication, the individual monomials of the two polynomials are multiplied and of course in can happen that I have (3x^2 y + 5x y^2) * (x + y). The result contains 3x^2 y^2 and 5 x^2 y^2, which I want to combine by addition right away.
Naturally I would like to use the part x^2 y^2 of the monomial as a key in a (hash) map to add up the different coefficients (3 and 5 in the example). But the monomial object as I envisage it should naturally also contain the coefficient, which should not be part of the map key.
Of course I could write equals/hashcode of the monomial object such that they ignore the coefficient. But this feels just so wrong, because mathematically a monomial clearly is only equal to another one if also the coefficients are equal.
Introducing a coefficient-free monomial object for intermediate operations does also not look right.
Instead of using the map, I could use a list and use a binary search with a dedicated comparator that ignores the coefficient.
Short of implementing a map which does not use the keys' equals/hashcode, but a dedicated one, are there any better ideas of how to fuse the monomials?
Since the JDK implementation of [Linked]HashMap does not permits you to override the equals/hashCode implementation, the only other ways are:
a wrapping object like this:
class A {
private final String fieldA; // equals/hashCode based on that field.
private final String fieldB; // equals/hashCode based on that field.
}
class B {
private A a;
public int hashCode() {return a.fieldA.hashCode();}
public boolean equals(Object o) {... the same ... }
}
Map<B, Value> map = new HashMap<B, Value>();
map.put(new B(new A("fieldA", "fieldB")), new Value(0));
Well, with more getters/constructors.
This can be annoying, and perhaps there exists some library (like Guava) that allows an equals/hashCode method to be given like you can give a Comparator to TreeMap.
You'll find below a sample implementation that point out what to do to decorate an existing map.
use a TreeMap with a specific Comparator. The other answer point it, but I'd say you'll need to correctly define a Comparator because this could lead to problems: if you compareTo method returns 0 when equality is reached, and 1 in other case, this means there is no natural ordering. You should try to find one, or use the wrapper object.
If you want to take the challenge, you can create a basic implementation using delegation/decoration over another HashMap (this could be another kind of map, like LinkedHashMap):
public class DelegatingHashMap<K,V> implements Map<K,V> {
private final BiPredicate<K,Object> equalsHandler;
private final IntFunction<K> hashCodeHandler;
private final Map<Wrapper<K>,V> impl = new HashMap<>();
public DelegatingHashMap(
BiPredicate<K,Object> equalsHandler,
IntFunction<K> hashCodeHandler
) {
this.equalsHandler = requireNonNull(equalsHandler, "equalsHandler");
this.hashCodeHandler= requireNonNull(hashCodeHandler, "hashCodeHandler");
}
public Object get(K key) {
Wrapper<K> wrap = new Wrapper<>(key);
return impl.get(wrap);
}
...
static class Wrapper<K2> {
private final K2 key;
private final BiPredicate<K> equalsHandler;
private final IntFunction<K> hashCodeHandler;
public int hashCode() {return hashCodeHandler.apply(key);}
public boolean equals(Object o) {
return equalsHandler.test(key, o);
}
}
}
And the code using the map:
DelegatingHashMap<String, Integer> map = new DelegatingHashMap<>(
(key, old) -> key.equalsIgnoreCase(Objects.toString(o, "")),
key -> key.toLowerCase().hashCode()
);
map.put("Foobar", 1);
map.put("foobar", 2);
System.out.println(map); // print {foobar: 2}
But perhaps the best (for the memory) would be to rewrite the HashMap to directly use the handler instead of a wrapper.
You could use a TreeMap with a custom comparator:
TreeMap(Comparator<? super K> comparator)
Constructs a new, empty tree map, ordered according to the given comparator.
(Source)
Consider using a TreeMap, which is a SortedMapand thus also a Map. You can provide a Comparator to its constructor. The sorted map will use that Comparator for sorting the map keys. But importantly, for your case, it will consuder keys to be equal if the Comparator returns 0. In your case that will require a Comparator that is not consustent with equals, which could cause you problems if you are not careful.
Another option is to introduce another class, which acts as an adaptor for a Mononomial and can be used as a map key having the properties you deserve.
I think it may be better to separate the monomial into 2 parts: the coefficient and the variable. That way you can use the variable part in your map as the key and the coefficient as the value (which can then up updated).
All this code should be implementation details inside a Polynomial object
I'm not sure why you think a coefficient-free monomial does not look right. You don't have to expose the object to the outside if you don't want. But it might be a nice way to have getters on your Polynomial to get the coefficients for each monomial.

Map with two Key for a value?

I want to create a map that has two key :
map.put (key1,key2,value1);// Insert into map
map.get(key1,key2); // return value1
i have looking into multikeyMap but i don't know how i will do it
Sounds like you just want a key which is created from two values. You may well find that those two values should naturally be encapsulated into another type anyway - or you could create a Key2<K1, K2> type. (The naming here would allow for Key3, Key4 etc. I wouldn't encourage you to go too far though.)
For something in between, you could create a private static class within the class where this is really needed (if it's only an internal implementation detail). If it's not a natural encapsulation (e.g. it's something like "name and population", which doesn't make sense outside this specific scenario) then it would be good in terms of keeping meaningful property names, but without exposing it publicly.
In any of these scenarios, you'll end up with a new type with two final variables which are initialized in the constructor, and which contribute to both equals and hashCode. For example:
public final class Key2<K1, K2> {
private final K1 part1;
private final K2 part2;
public Key2(K1 part1, K2 part2) {
this.part1 = part1;
this.part2 = part2;
}
#Override public boolean equals(Object other) {
if (!(other instanceof Key2)) {
return false;
}
// Can't find out the type arguments, unfortunately
Key2 rawOther = (Key2) other;
// TODO: Handle nullity
return part1.equals(rawOther.part1) &&
part2.equals(rawOther.part2);
}
#Override public int hashCode() {
// TODO: Handle nullity
int hash = 23;
hash = hash * 31 + part1.hashCode();
hash = hash * 31 + part2.hashCode();
return hash;
}
// TODO: Consider overriding toString and providing accessors.
}
The more situation-specific types would be slightly simpler as they wouldn't be generic - in particular this would mean you wouldn't need to worry about the type arguments, and you could give the variables better names.
How about
class Key{
private final String key1;
private final String key2;
//accessors + hashcode + equals()
}
and
Map<Key, Value> map
You might consider using one of Guava's Table implementations. From the documentation:
A collection that associates an ordered pair of keys, called a row key
and a column key, with a single value. A table may be sparse, with
only a small fraction of row key / column key pairs possessing a
corresponding value.
If you can use outside libraries, Guava provides exactly this as Table<R, C, V>, referring to the two keys as "row" and "column" respectively. (Disclosure: I contribute to Guava.)
Why not map key a String and concatinate key1+key2
If you always want to access via key1 and key2 together you could just concatenate them with a separator together as key and use a normal map.
It's unfortunate that Java does not support tuples at language level, and therefore you have to go for ad hoc structures like the ones showed in some answers here. This leads to atrocious amount of boilerplate and code duplication.
Functional Java has a library support for tuples. The class that fits the bill here is P2. The name means "product with 2 elements". (Product is just an algebraic term for composite types.) The library supports tuples of up to 8 elements. The P{n} classes override all the necessary methods
There is a class named P that provides a static factory method p for constructing tuples.
Usage:
import fj.P2;
import fj.Ord;
import fj.data.TreeMap;
import static fj.Ord.*;
import static fj.P.*;
TreeMap<P2<Integer, String>, String> m =
TreeMap.<P2<Integer, String>, String>empty(p2Ord(intOrd, stringOrd)).
set(p(1, "2"), "onetwo").
set(p(5, "3"), "fivethree");

Java Collection with 2 keys

I want to implement a HashMap with a key with 2 components. Ex.
Pseudocode:
Key = <Component1, Component2>
CollName<Key, Val> coll = new CollName<Key, Val>;
How can I implement this in Java, considering speed and size of the Collection. Thanks :D
You need a helper (composite key) class that holds your two keys
public class CompositeKey<... , ...> {
... component1;
... component2;
// getter , setter, ...
// equals
// hashcode()
}
and then you can use it as key:
CompositeKey cKey = new CompositeKey(1,2);
Map x.put(cKey,val);
It is very important here to implement equals() and hashCode() in a good fashion. Most IDEs can help you here. Important is for the hashCode that it returns a "unique" value in order to prevent hash collisions of the keys (i.e. returning a constant value is the worst case, as all values would end up in the same bucket). Many implementations of hashcode do something along
hashcode = component1.hashCode() + 37* component2.hashCode();
If you want more details, dig out any CS book that talks about hashing algorithms.
If you want to use that for persistence, also have a look at this blog post.
You can't directly create a Map with two keys, however you can combine the two.
The simplest method is to serialize them to a string and combine them.
String key = obj1.toString() + "-" + obj2.toString();
myMap.put( key, myValue );
presuming the objects can easily be serialized to a string that would be unique.
If this is not the case, then creating a wrapper object is your best option. You would need to define an object that overrides the equals() and hashCode() method.
As a rough example
class CombinedKey{
private MyClass object1;
private MyClass object2;
public CombinedKey( MyClass object1, MyClass object2 ){
this.object1 = object1;
this.object2 = object2;
}
public int hashCode(){
return object1.hashCode() + object2.hashCode();
}
#Override
public Boolean equals( Object otherObject ){
if( otherObject == null || otherObject.getObject1() == null ) return false;
return object1.equals( otherObject.getObject1() ) && object2.equals( otherObject.getObject2();
}
public MyClass getObject1() { return object1; }
public MyClass getObject2() { return object2; }
}
(You might also want to consider using Generics to define this class so it can be reused in other scenarios)
Usage:
Map<CombinedKey, Object> myMap = new HashMap<CombinedKey, Object>();
myMap.put( new CombinedKey(obj1, obj2), value );
Didn't see a Google Guava answer yet, so thought I'd point it out: I'd use Table.
It’s common usage of HashMap, the point is you have to define (override) equals() and hashCode() in class Key properly.
A good alternative to using a CompositKey/Pair/Tuple is to use a List. List implementations already have a properly defined equals() and hashCode() and are easy to create using Arrays.asList()

Categories

Resources