Overriding hashCode when using isAssignableFrom on equals method - java

I need to find a way to cache Methods(java.lang.reflect.Method) in such a way that whenever I call a function with the class (Class) methodName(String) and arguments(T[]) the function will return the cached method if exists or find the method, add it to the cache and return it.
I want to use HashMap for caching so I can find the method in O(1) but the problem is that I need to useisAssignableFrom when I override the equals method:
public class A1 extends AParent {}
public class A2 extends AParent {}
public class AParent {}
public class Temp{
public void testFunc(AParent a){}
}
This is the class I use for the keys in the HashMap :
import java.util.Arrays;
class MethodAbs{
Class c;
String methodName;
Class<?>[] argsTypes;
public MethodAbs(Class c, String methodName, Class<?>[] argsTypes){
this.c = c;
this.methodName = methodName;
this.argsTypes = argsTypes;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
MethodAbs methodAbs = (MethodAbs) o;
if (c != null ? !c.equals(methodAbs.c) : methodAbs.c != null) return false;
if (methodName != null ? !methodName.equals(methodAbs.methodName) : methodAbs.methodName != null)
return false;
return isArgsTypesEq(argsTypes, methodAbs.argsTypes);
}
//a method is equals to the one cached if the arguments types
// can be cast to the ones that are saved on the map,
// i.e the ones on the method declaration
private boolean isArgsTypesEq(Class<?>[] at1, Class<?>[] at2){
boolean res = at1.length == at2.length;
for(int i = 0; i<at1.length && res; i++){
if(!at1[i].isAssignableFrom(at2[i])) res = false;
}
return res;
}
//default implementation (not working properly!)
#Override
public int hashCode() {
int result = c != null ? c.hashCode() : 0;
result = 31 * result + (methodName != null ? methodName.hashCode() : 0);
result = 31 * result + Arrays.hashCode(argsTypes);
return result;
}
}
The class I use for caching
class Run{
public Map<MethodAbs, Method> map = new HashMap<>();
public<T> Method myFunc(Class c, String methodName, T[] args){
MethodAbs ma = new MethodAbs(c, methodName, getTypes(args));
if(map.containsKey(ma)){
return map.get(ma);
}
else{
for(Method method: c.getMethods()){
MethodAbs currMethodAbs = new MethodAbs(c, method.getName(), method.getParameterTypes());
if(!map.containsKey(currMethodAbs))
map.put(currMethodAbs, method);
if(currMethodAbs.equals(ma)) break;
}
}
return map.get(ma);
}
private<T> Class<?>[] getTypes(T[] args) {
Class<?>[] types = new Class<?>[args.length];
for(int i = 0; i< args.length; i++){
types[i] = args[i].getClass();
}
return types;
}
}
And Main:
public static void main(String[] args){
Run r = new Run();
Object [] arr = new Object[1];
arr[0] = new A1();
r.myFunc(Temp.class, "testFunc", arr);
arr[0] = new A2();
r.myFunc(Temp.class, "testFunc", arr);
}
In the scenario above after calling r.myFunc for the first time, the map looks like this:
MethodAbs(Temp.class, "testFunc", [AParent.class])
on the second time map.containsKey will return false(because AParent.hashCode != A2.hashCode) but they are equals.
The hierarchy shown in the example will not necessarily look like that(for example A2 can be a grandchild of AParent)
I know I can use the Class and method name as keys and the value will be a list of methods which I'll need to iterate and compare with equals but I'm trying to find a better way...

Unfortunately, you're equals method is fundamentally broken, for at least two reasons.
It is not symmetric, see the following code snippet:
public static void main(String... args) {
MethodAbs methodValueOfObject = new MethodAbs(String.class, "valueOf", new Class<?>[] { Object.class });
MethodAbs methodValueOfCharArrays = new MethodAbs(String.class, "valueOf", new Class<?>[] { char[].class });
System.out.println(methodValueOfObject.equals(methodValueOfCharArrays)); // prints "true"
System.out.println(methodValueOfCharArrays.equals(methodValueOfObject)); // prints "false"
}
It equates methods which you probably don't mean to be considered equal. To see that imagine that your Temp class has two testFunc methods, public void testFunc(A1 a) and public void testFunc(A2 a). The corresponding MethodAbs objects shouldn't be equal, but according to your implementation, they indeed are.
I think that the best solution for you is to just get rid of the cache altogether. Just use
public Method getMethod(Class<?> c, String methodName, Class<?>... paramClasses) {
try {
return c.getDeclaredMethod(methodName, paramClasses);
} catch (NoSuchMethodException | SecurityException e) {
// Your exception handling goes here
return null;
}
}
Class objects are already cached by the classloader, so the performance loss is negligible.

Related

java: issue while access value from TreeMap in multithreading

I have use TreeMap to store key value.
For key using custom object.
But once I have faced very strange issue, I am not able to get value which I have set earlier(with same key).
below is my code
public final class TestOptions implements Cloneable {
private Map<StorageSystemOptionKey, Object> options = new TreeMap<StorageSystemOptionKey, Object>();
private static final class StorageSystemOptionKey implements Comparable<StorageSystemOptionKey> {
/** Constant used to create hashcode */
private static final int HASH = 31;
private final Class<? extends StorageRepository> storageRepositoryClass;
/** The option name */
private final String name;
private StorageSystemOptionKey(Class<? extends StorageRepository> storageRepositoryClass, String name) {
this.storageRepositoryClass = storageRepositoryClass;
this.name = name;
}
public int compareTo(StorageSystemOptionKey o) {
int ret = storageRepositoryClass.getName().compareTo(o.storageRepositoryClass.getName());
if (ret != 0) {
return ret;
}
return name.compareTo(o.name);
}
#Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
final StorageSystemOptionKey that = (StorageSystemOptionKey) o;
if (!storageRepositoryClass.equals(that.storageRepositoryClass)) {
return false;
}
if (!name.equals(that.name)) {
return false;
}
return true;
}
#Override
public int hashCode() {
int result;
result = storageRepositoryClass.hashCode();
result = HASH * result + name.hashCode();
return result;
}
}
void setOption(Class<? extends StorageRepository> fileSystemClass, String name, Object value) {
options.put(new StorageSystemOptionKey(fileSystemClass, name), value);
}
Object getOption(Class<? extends StorageRepository> fileSystemClass, String name) {
StorageSystemOptionKey key = new StorageSystemOptionKey(fileSystemClass, name);
return options.get(key);
}
boolean hasOption(Class<? extends StorageRepository> fileSystemClass, String name) {
StorageSystemOptionKey key = new StorageSystemOptionKey(fileSystemClass, name);
return options.containsKey(key);
}
public int compareTo(TestOptions other) {
if (this == other) {
return 0;
}
int propsSz = options == null ? 0 : options.size();
int propsFkSz = other.options == null ? 0 : other.options.size();
if (propsSz < propsFkSz) {
return -1;
}
if (propsSz > propsFkSz) {
return 1;
}
if (propsSz == 0) {
return 0;
}
int hash = options.hashCode();
int hashFk = other.options.hashCode();
if (hash < hashFk) {
return -1;
}
if (hash > hashFk) {
return 1;
}
return 0;
}
#Override
public Object clone() {
TestOptions clone = new TestOptions();
clone.options = new TreeMap<StorageSystemOptionKey, Object>(options);
return clone;
}
}
calling method to set and get like
public abstract Class<? extends StorageRepository> getStorageRepositoryClass();
public Class<? extends StorageRepository> getStorageRepositoryClass() {
return MyImpl.class;
}
TestOptions opt =new TestOptions(); // shared accross all Threads
Object getProperty(String name) {
return opt.getOption(getStorageRepositoryClass(), name);
}
void setProperty(String name, Object value) {
opt.setOption(getStorageRepositoryClass(), name, value);
}
Using set and get method in multi-threaded application.
queries:
I am calling set/get in multiple time then also I was not able to get value which was set earlier(same key)
Is this due to Treeset implementation is not synchronized
or problem with hashCode, equals or compareTo method implementation?
On a quick glance your compareTo(), equals() and hashCode() look fine. Note that TreeMap will mostly use compareTo() to find elements so that method needs to be correct (your's looks technically correct).
However, TreeMap and TreeSet (as well as other basic collections and maps) are not thread-safe and thus concurrent modifications can cause all kinds of unexpected behavior. We once had a case where 2 threads were trying to add a single element to a hashmap and the threads ended up in an endless loop because the internal list to resolve clashes produced a cycle (due to the concurrent put).
So either use the ConcurrentXxxx maps and collections or synchronize access to yours.
TreeSet is not synchronized. I belive ConcurrentSkipListMap might be better.
Check also your hashCode, equals implementation

Check if a list of objects contains another object in Java

Why does ts.contains(t) return false and how can I fix it?
Have a look at my code, please:
class MyList {
private String x;
public MyList (String x) {
this .x = x;
}
public String toString () {
return x;
}
public static void main ( String [] args ) {
List<MyList> ts = new ArrayList<MyList>();
ts.add (new MyList ("one"));
ts.add (new MyList ("two"));
ts.add (new MyList ("three"));
MyList t = new MyList("one");
System.out.println ("Is t in ts? " + ts.contains(t));
}
}
Thank you all for the help. Both SamzSakerz and michaeak answers work correctly.
Just implement the equals() method:
class MyList {
private String x;
public MyList (String x) {
this .x = x;
}
#Override
public String toString () {
return x;
}
public static void main ( String [] args ) {
List<MyList> ts = new ArrayList<MyList>();
ts.add (new MyList ("one"));
ts.add (new MyList ("two"));
ts.add (new MyList ("three"));
MyList t = new MyList("one");
System.out.println ("Is t in ts? " + ts.contains(t));
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((x == null) ? 0 : x.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
MyList other = (MyList) obj;
if (x == null) {
if (other.x != null) {
return false;
}
} else if (!x.equals(other.x)) {
return false;
}
return true;
}
}
Output Is t in ts? true
The equals() Method is defined for the class Object which is the top class for every class. The contains() Method contractually checks, if the requested object a is contained in the list (i.e. same object is contained in a list) or if an equal object b (i.e. a.equals(b) is true) is contained in the list.
For List.contains(obj) the hashCode method is not required to be implemented, however, it is recommended to implement hashCode() whenever you implement equals() and make sure to depend on the same attributes in both methods.
You have to override the equals and hashCode methods.
contains relies on equals, and the default implementation of equals is that its identity is compared. Then equals only returns true if it is the very same object.
In order to implement the equals method, you have to decide when two objects are considered equal. In your case, I assume that if the object's only field s is equal to the other, then you want them to be considered equal.
More:
Overriding the java equals() method - not working?
What issues should be considered when overriding equals and hashCode in Java?
You can check that list have object with specific property using
System.out.println("Is t in ts? " + ts.stream().anyMatch(x -> x.x.equals("one")));
Like others have pointed you need to override equals and hashcode we can do this in 1 line.
#Override
public int hashCode() {
return toString().hashCode();
}
#Override
public boolean equals(Object obj) {
return this == obj || obj != null && getClass() == obj.getClass() && toString().equals(obj.toString());
}
and now the output we get is
Is t in ts? true
Here is the full code:
import java.util.ArrayList;
import java.util.List;
class MyList {
private String x;
public MyList(String x) {
this.x = x;
}
public static void main(String[] args) {
List<MyList> ts = new ArrayList<MyList>();
ts.add(new MyList("one"));
ts.add(new MyList("two"));
ts.add(new MyList("three"));
MyList t = new MyList("one");
System.out.println("Is t in ts? " + ts.contains(t));
}
#Override
public String toString() {
return x;
}
#Override
public int hashCode() {
return toString().hashCode();
}
#Override
public boolean equals(Object obj) {
return this == obj || obj != null && getClass() == obj.getClass() && toString().equals(obj.toString());
}
}

Java - Overriding hashCode and toString

When two objects have same value of ele in class A then those two objects are equal. So I have overridden toString and hashCode to return the object's ele (not considering the value of s anywhere).
public class A {
private int ele;
private String s;
public int getEle() {
return ele;
}
public void setEle(int ele) {
this.ele = ele;
}
public String getS() {
return s;
}
public void setS(String s) {
this.s = s;
}
#Override
public int hashCode(){
return ele;
}
#Override
public String toString() {
return String.valueOf(ele);
}
}
public static void main(String[] args) {
Map<A, String> map = new HashMap<>();
A a1 = new A();
a1.setEle(10);
a1.setS("abc");
A a2 = new A();
a2.setEle(10);
a2.setS("efg");
map.put(a1, "val1");
map.put(a2, "val2");
System.out.println(map.get(a1));
System.out.println(map.get(a2));
}
Output:
val1
val2
But if I put value of a1 and a2 in a map, I was expecting either val1 or val2 to be returned for both map.get(a1) and map.get(a2).
Sure, a1 and a2 have the same hash code, but they weren't considered equal because you didn't override equals to consider two A objects with the same ele to be equal. A map will use equals to the final ruler on equality after it uses the hash code. The map will place both objects in the same bucket, but because they aren't equal, it will keep both.
Override equals so that it returns true if the other object is an A and they both have the same ele. Then you will see that val2 will be returned for both get calls.
You need to implement equals() to take ele value into consideration when adding to a map, i.e.:
public class A {
private int ele;
private String s;
public int getEle() {
return ele;
}
public void setEle(int ele) {
this.ele = ele;
}
public String getS() {
return s;
}
public void setS(String s) {
this.s = s;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
A a = (A) o;
return ele == a.ele;
}
#Override
public int hashCode() {
return ele;
}
}
This will make you return only one value as you want.
every time you use new keyword it makes a new object in heap Memory. So, a1 and a2 both are different Object in actual.
Please Refer this for more info about new keyword What New keyword do Internally in Java

Avoid unchecked warning in following

I've done some fancy wrapping to avoid unchecked warnings in the past, but after 90 mins of poring over http://www.angelikalanger.com/GenericsFAQ/JavaGenericsFAQ.html, I can't write the findMatch method below and make it work without #SuppressWarnings("unchecked"). The parameterized class isn't known at compile time.
public interface Matchable<T>
{
public boolean matches(T toMatch);
}
public class PlaceForMatching
{
public static Object findMatch(Object toMatch, Object[] toSearch)
{
if(!(toMatch instanceof Matchable)) return null;
Matchable matchObj = (Matchable)toMatch;
Class<?> matchClass = matchObj.getClass();
for(Object obj : toSearch)
{
/**
* Check here verifies that the search list object we're about
* to check is the same class as the toMatch object.
* This means Matchable will work without a ClassCastException.
**/
if(matchClass.isInstance(obj) && matchObj.matches(obj))
return obj;
}
//Didn't find it
return null;
}
}
Note the code works because in every case Matchable is implemented by T.
Apple implements Matchable<Apple>
Orange implements Matchable<Orange>
EDIT: Add some test code
public static void main(String[] args)
{
Object[] randomList = createAppleArray();
Object apple = new Apple("Red");
Object match = findMatch(apple, randomList);
}
private static Object[] createAppleArray()
{
return new Object[] { new Apple("Pink"), new Apple("Red"), new Apple("Green") };
}
public class Apple implements Matchable<Apple>
{
String color;
public Apple(String color)
{
this.color = color;
}
public boolean matches(Apple apple)
{
return color.equals(apple.color);
}
}
public static <T extends Matchable<T>> T findMatch(T toMatch, T[] toSearch) {
if (toMatch == null)
return null;
Matchable<T> matchObj = toMatch;
Class<?> matchClass = matchObj.getClass();
for (T obj : toSearch) {
if (matchClass.isInstance(obj) && matchObj.matches(obj))
return obj;
}
return null;
}

Determine Correct Method Signature During Runtime

I am using the following the following class org.apache.poi.hssf.usermodel.HSSFCell, with a list of the following methods:
void setCellValue(boolean value)
void setCellValue(java.util.Calendar value)
void setCellValue(java.util.Date value)
void setCellValue(double value)
void setCellValue(HSSFRichTextString value)
void setCellValue(java.util.Calendar value)
void setCellValue(HSSFRichTextString value)
Take note that, there are no methods with Object as method parameter.
Now, I have no way to determine my value class type during compiled time. I can only determine my value class type during runtime. Hence, how I can determine the correct method to be called, if I do not know the method signature during compiled time?
My code is below:
final int rowCount = tableModel.getRowCount();
for (int i = 0; i < rowCount; i++) {
final HSSFRow row = sheet.createRow(i + 1);
for (int j = 0; j < columnCount; j++) {
final Object object = tableModel.getValueAt(i, j);
final Class myClass = tableModel.getColumnClass(j);
// How to perform casting during compiled time, and invoke
// the setCellValue with correct signature?
if (object != null) {
row.createCell(j).setCellValue(??); // Does not accept Object!
}
}
}
Perhaps ugly if...else with instanceof would resolve my problem. However, if I do not want the ugly if...else with instanceof, is there any better way to do so?
One way of handling this is to load that list of methods at runtime into a Map and then for each call, use the Map. That is, something like this (where this code is simplified and omits error checking):
Map<? extends Object, Method> map;
Method[] methods = Setters.class.getMethods();
for (Method method : methods) {
if (method.getName().equals("setCellValue")) {
map.put(method.getParameterTypes()[0], method);
}
}
then when you want to call this, look up the Method in the map by argument type and use that instance.
To show this off, again with simplified but this time full code. Note that to be fully general, the code gets a little more complicated, as is shown below. If you don't have to worry about primitives (which depends on your usage) or if you don't have to worry about interfaces or superclasses, then you can simplify the example below.
Also, if you can guarantee that there will be no overlap in interfaces or superclasses in the arguments that you have to worry about, you can move all of the complicated logic into initialization (which doesn't matter if it takes 1 ms longer). In this case, all of the logic in findMethodToInvoke() would be moved into the constructor, where you would loop over all interfaces and superclasses of each method you find and add them to your parameterTypeMap. If you do this optimization, then findMethodToInvoke() becomes a single line:
return parameterTypeMap.get(test.getClass());
but without this optimization and with full generality, here's my example of how to do this:
import java.lang.reflect.*;
import java.util.*;
public class Test {
private final Map<Object, Method> parameterTypeMap = new HashMap<Object, Method>();
private final Object[] tests = {Double.valueOf(3.1415),
Boolean.TRUE,
new Date(),
new GregorianCalendar(),
new HashMap<Object, Object>()};
public Test() {
Method[] methods = Setters.class.getMethods();
for (Method method : methods) {
if (method.getName().equals("setCellValue")) {
Class<?>[] clazzes = method.getParameterTypes();
if (clazzes.length != 1) {
continue;
}
if (clazzes[0].isPrimitive()) {
handlePrimitive(method, clazzes[0]);
}
parameterTypeMap.put(clazzes[0], method);
}
}
}
// See http://java.sun.com/javase/6/docs/api/java/lang/Class.html#isPrimitive()
private void handlePrimitive(Method method, Class<?> clazz) {
if (clazz == Boolean.TYPE) {
parameterTypeMap.put(Boolean.class, method);
} else if (clazz == Double.TYPE) {
parameterTypeMap.put(Double.class, method);
} // ... and so on for the other six primitive types (void doesn't matter)
}
public void doTests(Setters setter) {
for (Object test : tests) {
Method method = findMethodToInvoke(test);
if (method == null) {
System.out.println("Nothing found for " + test.getClass());
continue;
}
try {
method.invoke(setter, test);
} catch (Exception e) {
e.printStackTrace();
}
}
}
private Method findMethodToInvoke(Object test) {
Method method = parameterTypeMap.get(test.getClass());
if (method != null) {
return method;
}
// Look for superclasses
Class<?> x = test.getClass().getSuperclass();
while (x != null && x != Object.class) {
method = parameterTypeMap.get(x);
if (method != null) {
return method;
}
x = x.getSuperclass();
}
// Look for interfaces
for (Class<?> i : test.getClass().getInterfaces()) {
method = parameterTypeMap.get(i);
if (method != null) {
return method;
}
}
return null;
}
public static void main(String[] args) {
Test test = new Test();
test.doTests(new Setters());
}
}
class Setters {
public void setCellValue(boolean value) {
System.out.println("boolean " + value);
}
public void setCellValue(double value) {
System.out.println("double " + value);
}
public void setCellValue(Calendar value) {
System.out.println("Calendar " + value);
}
public void setCellValue(Date value) {
System.out.println("Date " + value);
}
public void setCellValue(Map<?, ?> value) {
System.out.println("Map " + value);
}
}
If you don't have subclasses (you can still do it if you do, but it will be harder, let me know if you do) you could use reflection:
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Main
{
public static void main(final String[] argv)
throws NoSuchMethodException,
IllegalAccessException,
IllegalArgumentException,
InvocationTargetException
{
final Object o;
if(argv.length == 0)
{
o = "Hello";
}
else
{
o = Integer.valueOf(42);
}
callFoo(o);
}
private static void callFoo(final Object o)
throws NoSuchMethodException,
IllegalAccessException,
IllegalArgumentException,
InvocationTargetException
{
Method method;
method = Main.class.getDeclaredMethod("foo", o.getClass());
method.invoke(null, o);
}
private static void foo(final String val)
{
System.out.println("foo(String) -> " + val);
}
private static void foo(final Integer val)
{
System.out.println("foo(Integer) -> " + val);
}
}
The downside is that you don't have the compiler tell you if you try to call a method that does not exist.
The exception handling in the code above is utter crap, but I wanted to focus on the reflection part.
Using instance of is better from the point of view that it has compile time type safety. Reflection will not have to be updated if new methods are added.
I think the instanceof is the way to go. If you think it makes your code ugly extract that instanceof expressions into an auxiliary method:
public void setCellValue(HSSFCell cell, Object value) {
if (null == cell)
throw new IllegalArgumentException("cell");
if (null == value)
throw new IllegalArgumentException("value");
if (value instanceof Double)
cell.setCellValue((Double)value); // auto-boxing will handle this
else if (value instanceof Boolean) {
cell.setCellValue((Boolean)value); // auto-boxing will handle this
} else if (value instanceof Calendar) {
cell.setCellValue((Calendar)value);
} else if ...
.....
} else {
throw new UnsupportedTypeException("Object of class " + Value.class.getName() + " not supported.");
}
}
Alternately you can use reflection. Even with reflection I think you still have to do some customization for the primitive types because the auto-boxing doesn't work for getMethod() ...
public void invokeSetCellValue(HSSFCell cell, Object obj) {
try {
Class<?> clazz = obj.getClass();
if (obj instanceof Double) {
clazz = double.class;
} else if (obj instanceof Boolean) {
clazz = boolean.class;
}
Method m = HSSFCell.class.getMethod("setCellValue", clazz);
m.invoke(cell, obj);
} catch (SecurityException e) {
} catch (NoSuchMethodException e) {
} catch (IllegalArgumentException e) {
} catch (IllegalAccessException e) {
} catch (InvocationTargetException e) {
}
}

Categories

Resources