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
Related
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
So I am doing a junit test in Java.
I am supposed to test two concrete types
public class BoolValue implements Value{
private boolean item;
//the Constructor
public BoolValue(boolean data){
item = data;
}
//checks to see if the current object is of the same type as the parameter
public boolean equals(Value v){
boolean result = false;
if (v instanceof BoolValue) {
if(this == v)
result = true;
}
return result; //true if equal
}
// returns current state string
public String toString(){
return " "+item;
}
}
This is the test case in my test file
#Test
public void testBoolean(){
BoolValue value = new BoolValue(true);
BoolValue value2 = new BoolValue(true);
assertEquals(true, value.equals(value2));
}
It returns false instead of the expected true
In the equals method, I want to compare two boolean types but I can't use the java.object equals() because the program reads it as the class equals() so its recursive.
How do I call the java.object equals() for comparing boolean types. Also, am I going about this the right way
if (v instanceof BoolValue) {
if(this == v)
result = true;
}
You cannot use == when you want new BoolValue(true).equals(new BoolValue(true)). You need to compare the two item inside them.
Also, you need to accept Object to get a "proper" equals method.
#Override
public boolean equals(Object v){
return (v instanceof BoolValue && ((BoolValue)v).item == this.item);
}
And when you override equals, you also need to override hashCode.
#Override
public int hashCode(){ return item ? 0 : 1; }
Finally (unless you intend to make these "values" mutable), since there are only two possible values, you should make the constructor private and provide the two values as static fields instead (you could also use an enum).
public class BoolValue implements Value {
private final boolean item;
private BoolValue(boolean v){
this.item = v;
}
public static final BoolValue TRUE = new BoolValue(true);
public static final BoolValue FALSE = new BoolValue(false);
public static BoolValue valueOf(boolean x){
return x ? TRUE : FALSE;
}
}
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());
}
}
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.
I have
String selectedName = "ABC";
List<object> pgetName;
where object has variables such as id, name, version
I want to do the equivalent of
int first = pgetName.indexOf(selectedName);
int last = pgetName.lastIndexOf(selectedName);
as used for simple String Arrays. I've tried
int first = pgetName.getProperty("name").indexOf(processToStart);
and
int first = pgetName[].getName().indexOf(processToStart);
for example but they don't work. How do I do what I want to do? This is advanced Java for me being a noob...
Thanks in advance,
Here's an other approach (might be a little overkill but it shows you an other way). The idea is to override the indexOf and lastIndexOf method so it would verify against your field "name":
private static class TestObject {
String id, name, version;
public TestObject(String id, String name, String version) {
super();
this.id = id;
this.name = name;
this.version = version;
}
public String getId() {
return id;
}
public String getName() {
return name;
}
public String getVersion() {
return version;
}
}
public static void main(String[] args) {
List<TestObject> pgetName = new ArrayList<TestObject>() {
#Override
public int indexOf(Object o) {
if (o == null || this.isEmpty()) {
return -1;
}
int counter=0;
for (TestObject current : this) {
if (o.equals(current.getName())) {
return counter;
}
counter++;
}
return -1;
}
#Override
public int lastIndexOf(Object o) {
if (o == null || this.isEmpty()) {
return -1;
}
for (int i=this.size()-1; i>=0;i--) {
TestObject current = get(i);
if (o.equals(current.getName())) {
return i;
}
}
return -1;
}
};
pgetName.add(new TestObject("1", "name1", "ver1"));
pgetName.add(new TestObject("2", "name2", "ver2"));
pgetName.add(new TestObject("3", "name3", "ver3"));
pgetName.add(new TestObject("4", "name1", "ver4"));
int first = pgetName.indexOf("name1");
int last = pgetName.lastIndexOf("name1");
System.out.println("First: " + first + " - Last: " + last);
}
Result is:
First: 0 - Last: 3
For any Java object you can override the methods equals and hashCode (this is not really used but it is generally a good practice to implement both methods) in order to use the indexOf and lastIndexOf functions of java.util.List.
The contextual menu of eclipse generates a default implementation of both methods, letting you choose on which field the comparison should be done. Give it a try.
After the implementation of the methods above, you can use indexOf on List.
If I understand your question, you want to "find the index of an Object where one of the properties of the object is a specific value".
This isn't directly possible in Java (or most languages FWIW). You can achieve it pretty simply with a for loop, however:
public MyObject findObjectByName(MyObject[] objects, String name) {
for (MyObject object: objects) {
if (object.name.equal(name) {
return object;
}
}
return null;
}
If you want to find the index, you can do something similar:
public int findObjectIndex(MyObject[] objects, String name) {
for (int i = 0; i < objects.length; ++i)
if (objects[i].name.equal(name) {
return i;
}
}
return -1;
}
Now, this is the most naive approach you can take, and is often, but not always, the best approach. If you have a large number of objects, and you need to look up a lot by name, then you could be better off building an index once, and then look them up by the index:
public class MyObjectIndex {
final Map<String, MyObject> byName = new HashMap<String, MyObject>();
public MyObjectIndex(MyObject[] objects) {
for (MyObject object: objects) {
byName.put(object.getName(), object);
}
}
public getMyObjectWithName(String name) {
return byName.get(name);
}
}