I want to store certain objects in a HashMap. The problem is, usually you just use a single object as a key. (You can, for example, use a String.) What I want to do it to use multiple object. For example, a Class and a String. Is there a simple and clean way to implement that?
You key must implement the hashCode and equals. If it is a SortedMap, it must also implements the Comparable interface
public class MyKey implements Comparable<MyKey>
{
private Integer i;
private String s;
public MyKey(Integer i,String s)
{
this.i=i;
this.s=s;
}
public Integer getI() { return i;}
public String getS() { return s;}
#Override
public int hashcode()
{
return i.hashcode()+31*s.hashcode();
}
#Override
public boolean equals(Object o)
{
if(o==this) return true;
if(o==null || !(o instanceof MyKey)) return false;
MyKey cp= MyKey.class.cast(o);
return i.equals(cp.i) && s.equals(cp.s);
}
public int compareTo(MyKey cp)
{
if(cp==this) return 0;
int i= i.compareTo(cp.i);
if(i!=0) return i;
return s.compareTo(cp.s);
}
#Override
public String toString()
{
return "("+i+";"+s+")";
}
}
public Map<MyKey,String> map= new HashMap<MyKey,String>();
map.put(new MyKey(1,"Hello"),"world");
I tend to use a list
map.put(Arrays.asList(keyClass, keyString), value)
The easiest way that I know of is to make a wrapper class and override hashmap and equals. For instance:
public class KeyClass {
private String element1;
private String element2;
//boilerplate code here
#Override
public boolean equals(Object obj) {
if (obj instanceof KeyClass) {
return element1.equals(((KeyClass)obj).element1) &&
element2.equals(((KeyClass)obj).element2);
}
return false;
}
#Override
public int hashCode() {
return (element1 + element2).hashcode();
}
}
OF course, I would recommend using a StringBuilder and whatever else, but this way you have overridden the equals and hashcode, thereby allowing a hash and equality check on your multiple keys.
Also, I would recommend making the objects immutable (not editable) for safety's sake, but that is purely preference.
Apache Commons Collections has a multikey map which might do the trick for you:
https://commons.apache.org/proper/commons-collections/apidocs/org/apache/commons/collections4/keyvalue/MultiKey.html
It looks like it will handle up to 5 "keys".
Do you mean that the object will be keyed by two keys, or rather a key which consists of two things.
If you want the first case. That is, an objected keyed by two keys, say a class or an object, you need to use two maps.
Map<Key1, value>
Map<Key2, value>
In the second case you need a map of maps, so:
Map<Key1, Map<Key2, value>>
You could create a holder class that contains the class and string that you want as the keys.
public class Key {
public MyClass key_class;
public String key_string;
public Key(){
key_class = new MyClass();
key_string = "";
}
}
Probably not the best solution, but a possibility.
There are a few places where people suggest creating a "Key" class containing the others, I totally agree. Just thought I'd add a helpful hint.
If you use eclipse or netbeans, they have a nice option--you can tell Eclipse to create equals and hashcode methods based on one or more members. So you just select the member (or members) you want to retrieve by and NB creates most of the code you'd need to write for you.
of course when I just want to retrieve by one object, I often just delegate the hashcode and equals methods to that object (delegating equals might be problematic because it would mean that one of your "Key holder" classes would be equal to the object that is it's key, but that's pretty easily fixed (and wouldn't usually effect anything anyway)
so off the top of my head:
class KeyHolder {
public final String key;
public final Object storeMe;
public KeyHolder(String key, Object storeMe) {
this.key=key;
this.storeMe=storeMe;
}
public equals(Object o) {
return (o instanceof KeyHolder && ((KeyHolder)o).key.equals(key));
}
public hashcode() {
return key.hashCode();
}
}
That's all there is to it, and eclipse will do the last two for you if you ask it to.
By the way, I know that I have public members, a public final member is exactly the same thing as having a getter--not really a terrible idea. I'm starting to use this pattern on small utility classes like this a lot more lately. If the member wasn't final, it would be worse because it would be like having a setter (Something I try to avoid these days).
One can solve this issue using apache's commons collection lib's MultiKey class.
Here is a simple example:
import org.apache.commons.collections.keyvalue.MultiKey;
HashMap map = new HashMap();
MultiKey multiKey = new MultiKey(key1, key2);
map.put(multikey,value);
//to get
map.get(new MultiKey(key1,key2));
Related
A lot of times I find myself (what appears to be) combining composition and inheritance when inheriting from abstract collection classes and I can't help but feel like it's technically conflicting design paradigms while I do it.
/** Custom String List implementation */
public class MyStringList extends AbstractList<String>
{
private final AbstractList<String> _myStringList;
public MyStringList()
{
this._myStringList = new ArrayList<String>();
}
public MyStringList(Collection<String> stringCollection)
{
if (stringCollection.contains(null))
{
throw new NullPointerException("Null values not permitted in MyStringList");
}
else
{
this._myStringList = new ArrayList<String>(stringCollection);
}
}
#Override
public String get(int index)
{
return this._myStringList.get(index);
}
#Override
public int size()
{
return this._myStringList.size();
}
#Override
public String set(int index, String aString)
{
++this.modCount;
return this._myStringList.set(index, Objects.requireNonNull(aString));
}
#Override
public void add(int index, String aString)
{
++this.modCount;
this._myStringList.add(index, Objects.requireNonNull(aString));
}
#Override
public boolean add(String aString)
{
++this.modCount;
return this._myStringList.add(Objects.requireNonNull(aString));
}
#Override
public String remove(int index)
{
++this.modCount;
return this._myStringList.remove(index);
}
// Method to make this list implementation distinct from ArrayList for the sake of
// this StackOverflow question example
public String[] getNumericContaining()
{
return this._myStringList.stream()
.filter(aString -> aString.codePoints().anyMatch(Character::isDigit))
.toArray(String[]::new);
}
// Another method to make this list implementation distinct from ArrayList for
// the sake of this StackOverflow question example
public String[] getUpperCaseContaining()
{
return this._myStringList.stream()
.filter(aString -> aString.codePoints().anyMatch(Character::isUpperCase))
.toArray(String[]::new);
}
}
Is this design of having an internal abstract collection object being the backing object (composition) of the class this object inherits from (inheritance) considered the "correct" way of leveraging the various abstract collection classes defined in the java.util package?
You're combining things unnecessarily. If you want to have what you have in the example, go with Lino's suggestion:
public class MyStringList extends ArrayList<String> {
public String[] getNumericContaining() {
return this.stream()
.filter(aString -> aString.codePoints().anyMatch(Character::isDigit))
.toArray(String[]::new);
}
public String[] getUpperCaseContaining() {
return this.stream()
.filter(aString -> aString.codePoints().anyMatch(Character::isUpperCase))
.toArray(String[]::new);
}
}
however many times a regular List<String> is all you need, since you can extract extra behaviour to the class that encapsulates the list, e.g.
public class MySomeService {
// This could be passed in, loaded from somewhere, etc.
private List<String> foo;
public void serviceMethod() {
doSomething();
String[] numbers = getNumericContaining();
doSomethingElse(numbers);
}
// The previous methods
private String[] getNumericContaining() {
foo.stream(). // and so on
}
It depends on whether your list really is a class in its own right, or whether it's just a special flavor that you need at some point. If it's the first, it may deserve its own class, if it's the latter, it doesn't.
Finally, even though OOP tells you to put data and behaviour in the same place, sometimes it makes sense to keep them in different places, so a method or a function can be used. Just pass the list as a parameter, and it'll work for all Collection classes not just your special one.
The problem I'm having has already been asked before: How to implement an interface with an enum, where the interface extends Comparable?
However, none of the solutions solve my exact problem, which is this:
I have a value object, similar to BigDecimal. Sometimes this value will not be set with a real object, because that value is not yet known. So I want to use the Null Object Pattern to represent the times this object is not defined. This is all not a problem, until I try to make my Null Object implement the Comparable interface. Here's an SSCCE to illustrate:
public class ComparableEnumHarness {
public static interface Foo extends Comparable<Foo> {
int getValue();
}
public static class VerySimpleFoo implements Foo {
private final int value;
public VerySimpleFoo(int value) {
this.value = value;
}
#Override
public int compareTo(Foo f) {
return Integer.valueOf(value).compareTo(f.getValue());
}
#Override
public int getValue() {
return value;
}
}
// Error is in the following line:
// The interface Comparable cannot be implemented more than once with different arguments:
// Comparable<ComparableEnumHarness.NullFoo> and Comparable<ComparableEnumHarness.Foo>
public static enum NullFoo implements Foo {
INSTANCE;
#Override
public int compareTo(Foo f) {
return f == this ? 0 : -1; // NullFoo is less than everything except itself
}
#Override
public int getValue() {
return Integer.MIN_VALUE;
}
}
}
Other concerns:
In the real example, there are multiple subclasses of what I'm calling Foo here.
I could probably work around this by having NullFoo not be an enum, but then I can't guarantee there is ever only exactly one instance of it, i.e. Effective Java Item 3, pg. 17-18
I don't recommend the NullObject pattern because I always find myself in one of these 2 situations:
it does not make sense to use NullObject like an object, and it should stay null
NullObject has too much meaning to be just a NullObject, and should be a true object itself (for instance, when it acts like a fully functional default value)
According to our discussion in the comments, it seems to me that your NullObject behaves very much like the 0 value of your normal objects.
What I would do is actually use 0 (or whatever default value makes more sense), and put a flag if you really need to know whether it has been initialized. This way, you will have 2 things to consider:
all uninitialized values won't share the same instance with my solution
for the very same reason, you are now able to initialize your object later without having to create a new instance
Here is the kind of code I think of:
public static class VerySimpleFoo implements Foo {
private int value;
private boolean initialized;
public VerySimpleFoo() {
this.value = 0; // whatever default value makes more sense
this.initialized = false;
}
public VerySimpleFoo(int value) {
this.value = value;
this.initialized = true;
}
#Override
public int compareTo(Foo f) {
// possibly need some distinction here, depending on your default value
// and the behavior you expect
return Integer.valueOf(value).compareTo(f.getValue());
}
#Override
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
this.initialized = true;
}
public boolean isInitialized() {
return initialized;
}
}
As you suggested, I believe one solution would be to use a class instead of an enum:
public class NullFoo implements Foo {
private NullFoo() {
}
public static final Foo INSTANCE = new NullFoo();
#Override
public int compareTo(Foo f) {
return f == this ? 0 : -1;
}
#Override
public int getValue() {
return 0;
}
}
This mimics an enum behavior, but it allows you to implement your Foo interface. The class is not instantiable because of the private constructor, so the only instance available is the one accessible via NullFoo.INSTANCE, which is thread-safe (thanks to the final modifier).
The thing is that Enum already implements Comparable natively, and since the generics are just a sugar code, and lost after compilation, effectively you want to implement the same method twice for the same interface.
I would drop enum, for NullFoo, converting it to class (like you suggested), and make final public static INSTANCE reference with private constructor, (This is not as good as using an enum, but acceptable, in most cases).
I have a Java Set<MyClass> on which I've overridden equals and hashCode to use the String name; variable.
public class MyClass{
final String name;
public boolean equals(Object o){...}
public int hashCode(){return name.hashCode();}
}
Is there anyway I can get my Object out of the HashSet using something like
MyClass o = set.get("nameofmyobject");
Is there a way to do this in Java, or a datastructure? or do I need to change up all of my Sets to Maps?
No. You need to change to a Map. None of the methods of Set return an element.
Addendum A
If you don't care about speed you can always search manually:
MyClass find(String name, Set<MyClass> set)
{
MyClass wrapper = new MyClass(name);
for (MyClass e : set) {
if (wrapper.equals(e)) {
return e;
}
}
return null;
}
Addendum B
If you use a TreeSet you can use floor:
MyClass find(String name, TreeSet<MyClass> set)
{
MyClass wrapper = new MyClass(name);
MyClass candidate = set.floor(wrapper);
if (candidate != null && wrapper.equals(candidate)) {
return candidate;
} else {
return null;
}
}
Have a look at this question. The answer is no. Sets are not for getting elements, but to look for equality. Use a Map or List insteed.
As Tim said you can't. And if so you would have to call it like set.get(myClassInstance); and not set.get(some member of the stored instance)
Use
Map<String, MyClass> myMap = new HashMap<String, MyClass>();
I want to create a map that will provide the benefits of generics, whilst supporting multiple different types of values. I consider the following to be the two key advantages of generic collections:
compile time warnings on putting wrong things into the collection
no need to cast when getting things out of collections
So what I want is a map:
which supports multiple value objects,
checks values put into the map (preferably at compile-time)
knows what object values are when getting from the map.
The base case, using generics, is:
Map<MyKey, Object> map = new HashMap<MyKey, Object>();
// No type checking on put();
map.put(MyKey.A, "A");
map.put(MyKey.B, 10);
// Need to cast from get();
Object a = map.get(MyKey.A);
String aStr = (String) map.get(MyKey.A);
I've found a way to resolve the second issue, by creating an AbstractKey, which is generified by the class of values associated with this key:
public interface AbstractKey<K> {
}
public enum StringKey implements AbstractKey<String>{
A,B;
}
public enum IntegerKey implements AbstractKey<Integer>{
C,D;
}
I can then create a TypedMap, and override the put() and get() methods:
public class TypedMap extends HashMap<AbstractKey, Object> {
public <K> K put(AbstractKey<K> key, K value) {
return (K) super.put(key, value);
}
public <K> K get(AbstractKey<K> key){
return (K) super.get(key);
}
}
This allows the following:
TypedMap map = new TypedMap();
map.put(StringKey.A, "A");
String a = map.get(StringKey.A);
However, I don't get any compile errors if I put in the wrong value for the key. Instead, I get a runtime ClassCastException on get().
map.put(StringKey.A, 10); // why doesn't this cause a compile error?
String a = map.get(StringKey.A); // throws a ClassCastException
It would be ideal if this .put() could give a compile error.
As a current second best, I can get the runtime ClassCastException to be thrown in the put() method.
// adding this method to the AbstractKey interface:
public Class getValueClass();
// for example, in the StringKey enum implementation:
public Class getValueClass(){
return String.class;
}
// and override the put() method in TypedMap:
public <K> K put(AbstractKey<K> key, K value){
Object v = key.getValueClass().cast(value);
return (K) super.put(key, v);
}
Now, the ClassCastException is thrown when put into the map, as follows. This is preferable, as it allows easier/faster debugging to identify where an incorrect key/value combination has been put into the TypedMap.
map.put(StringKey.A, 10); // now throws a ClassCastException
So, I'd like to know:
Why doesn't map.put(StringKey.A, 10) cause a compile error?
How could I adapt this design to get meaningful compile errors on put, where the value is not of the associated generic type of the key?
Is this is a suitable design to achieve what I want (see top)? (Any other thoughts/comments/warnings would also be appreciated...)
Are there alternative designs that I could use to achieve what I want?
EDIT - clarifications:
If you think this is a bad design - can you explain why?
I've used String and Integer as example value types - in reality I have a multitude of different Key / value type pairs that I would like to be able to use. I want to use these in a single map - that's the objective.
You are messing with generics and overloading in a bad way. You are extending HashMap<AbstractKey, Object> and so your class is inheriting the method Object put(AbstractKey k, Object v). In your class you are defining another put method with a different signature, which means you are just overloading the put method, instead of overriding it.
When you write map.put(StringKey.A, 10), the compiler tries to find a method that conforms to the argument types put(StringKey, Integer). Your method's signature doesn't apply, but the inherited put's does -- StringKey is compatible with AbstractKey and Integer is compatible with Object. So it compiles that code as a call to HashMap.put.
A way to fix this: rename put to some custom name, like typedPut.
BTW talking from experience your approach is very fun and engaging, but in real life it just isn't worth the trouble.
Item 29: Consider typesafe heterogeneous containers.—Joshua Bloch, Effective Java, Second Edition, Chapter 5: Generics.
IMHO, every problem comes from the original design smell: wanting to put values of different types into the map. I would wrap your Integer and String values into a common Value type instead. Something like this:
public class Value {
private enum Type {
STRING, INTEGER;
}
private Type type;
private Object value;
private Value(Object value, Type type) {
this.value = value;
this.type = type;
}
public static Value fromString(String s) {
return new Value(s, Type.STRING);
}
public static Value fromInteger(Integer i) {
return new Value(i, Type.INTEGER);
}
public Type getType() {
return this.type;
}
public String getStringValue() {
return (String) value;
}
public Integer getIntegerValue() {
return (Integer) value;
}
// equals, hashCode
}
This way, you just need a Map<SomeKey, Value>, and you can safely get the value from the map:
Value v = map.get(someKey);
if (v.getType() == Type.STRING) {
String s = v.getStringValue();
}
else if (v.getType() == Type.INTEGER) {
Integer i = v.getIntegerValue();
}
Consider this: (Thanks to Effective java)
public class TypedMap {
private Map<AbstractKey<?>, Object> map = new HashMap<AbstractKey<?>, Object>();
public <T> T get(AbstractKey<T> key) {
return key.getType().cast(map.get(key));
}
public <T> T put(AbstractKey<T> key, T value) {
return key.getType().cast(map.put(key, key.getType().cast(value)));
}
public static interface AbstractKey<K> {
Class<K> getType();
}
public static enum StringKey implements AbstractKey<String> {
A, B;
public Class<String> getType() {
return String.class;
}
}
public static enum IntegerKey implements AbstractKey<Integer> {
C, D;
public Class<Integer> getType() {
return Integer.class;
}
}
}
This generate compile time error
TypedMap map = new TypedMap();
TypedMap.AbstractKey<Integer> intKey = TypedMap.IntegerKey.C;
TypedMap.AbstractKey<String> strKey = TypedMap.StringKey.A;
map.put(strKey, "A");
map.put(intKey, 10);
map.put(strKey, 10); // this cause a compile error?
I have an object which will be stored in two different data structures.
I am using generics, and each structure has to print out the object in a different way. Is there a way to have multiple toString() methods that will invoke accordingly?
I can't create two different methods that return a string, as custom methods are not defined in the generic type, so you can't call them unless you cast it, which breaks the point behind generics.
Should I just create two different objects with different toString() methods for each data structure? Or is there an alternative?
Thanks
I think you need to use the toString() of the different objects, but you should still be able to do this with generics in place.
I imagine your generic class has at least one instance of the generic type. If that is the case, override your generic classes toString() to delegate to the generic type object instance.
If your generic class only has a single instance, you can do something like this:
class Holder<T> {
private T instance;
public Holder(T _instance) {
instance = _instance;
}
public String toString() {
return instance.toString();
}
}
And if your class has multiple instances of the generic type (as with many Collection's), you can delegate to each instance of the type.
class CollectionHolder<T> {
private Collection<T> collection;
public CollectionHolder(Collection<T> _collection) {
collection= _collection;
}
public String toString() {
StringBuilder builder = new StringBuilder();
for(T t : collection) {
builder.append(t); //same as builder.append(t.toString())
}
return builder.toString();
}
}
If you need to call it on both you need to add it to your interface. It's entirely possible you don't want to use toString() for it, since toString() is supposed to be a human readable representation.
class Nay1 {
String someCrazyMethod() { return "ugh"; }
}
class Nay2 {
String someRandomMethod() { return "ook"; }
}
Well this sucks. You wouldn't want a Nay1 to ook, or a Nay2 to ugh. But you need something uniform!
interface NayInterface {
String nayRepresentation();
}
And your new classes:
class Nay1 implements NayInterface {
String someCrazyMethod() { return "ugh"; }
public String nayRepresentation() { return someCrazyMethod(); }
}
class Nay2 implements NayInterface {
String someRandomMethod() { return "ook"; }
public String nayRepresentation() { return someRandomMethod(); }
}
Now no matter what, you can call myNayInterface.nayRepresentation(); to get your string representation that matches the appropriate class without doing any casting.
class ActuallyDoesSomething<T extends NayInterface> {
String foo;
public ActuallyDoesSomething(T t) {
this.foo = t.nayRepresentation();
}
public String foo() { return foo; }
}
Obviously if you want to use toString() instead of nayRepresentation, you can do that. But this will allow you to not have to consume toString and keep that instead for debugging purposes.