I have a class for a string-number pair. This class has the method compareTo implemented.
A method of another class returns a collection of elements of the pair type.
I wanted to perform a unit test on this method, and therefore wrote the following:
#Test
public void testWeight() {
Collection<StringNumber<BigDecimal>> expected = new Vector<StringNumber<BigDecimal>>();
expected.add(new StringNumber<BigDecimal>("a", BigDecimal.ONE));
expected.add(new StringNumber<BigDecimal>("b", BigDecimal.ONE));
Collection<StringNumber<BigDecimal>> actual = new Vector<StringNumber<BigDecimal>>();
expected.add(new StringNumber<BigDecimal>("a", BigDecimal.ONE));
expected.add(new StringNumber<BigDecimal>("b", BigDecimal.ONE));
//Collection<StringNumber<BigDecimal>> actual = A.f();
assertEquals(expected, actual);
}
But as you can see, the assertion fails, even though the elements in the collections are identical. What can be the reason?
The error I get is
java.lang.AssertionError: expected: java.util.Vector<[a:1, b:1]>
but was: java.util.Vector<[a:1, b:1]>
Which does not make scene to me.
Your StringNumber class requires equals() method. Then it will work. Assuming this class contains string and number fields (auto-generated by my IDE):
#Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof StringNumber)) {
return false;
}
StringNumber that = (StringNumber) o;
if (number != null ? !number.equals(that.number) : that.number != null) {
return false;
}
return !(string != null ? !string.equals(that.string) : that.string != null);
}
#Override
public int hashCode() {
int result = string != null ? string.hashCode() : 0;
result = 31 * result + (number != null ? number.hashCode() : 0);
return result;
}
Few remarks:
Two Vector's (why are you using such archaic data structure) are equal if:
both [...] have the same size, and all corresponding pairs of elements in the two lists are equal. (Two elements e1 and e2 are equal if (e1==null ? e2==null : e1.equals(e2)).)
That's why overriding equals() is required.
when implementing equals() you must implement hashCode(). Not required here, but better be safe than sorry: What issues should be considered when overriding equals and hashCode in Java?.
Related
I know that the below code gives the index of that particular element in java.
List<String> list = new ArrayList<>();
list .add("100");
Log.d("TAG",String.valueOf(list.indexOf("300")));
But how to get the index of an element while using a helper Class?
List<HelperClass> Arraylist= new ArrayList<>();
Arraylist.add(new HelperClass(name, email, phoneno));
Log.d("TAG", String.valueOf(new HelperClass(Arraylist.indexOf(name,email,phoneno))));
I searched everywhere for this but couldn't find. Can someone tell me how to find index of a particular item in arraylist while using modal to add data?
Obviously what I have tried is wrong and it shows red line under the whole line but I just typed that code for your understanding of what I want to achieve. Can someone give me a way please?
Helper
#Override
public int hashCode() {
int result = getName() != null ? getName().hashCode() : 0;
result = 31 * result + (Email != null ? Emaail.hashCode() : 0);
result = 31 * result + (PhoneNo!= null ? PhoneNo.hashCode() : 0);
return result;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Helper)) return false;
Helperthat = (Helper) o;
if (getName() != null ? !getName().equals(that.getName()) : that.getName() != null)
return false;
if (Email != null ? !Email.equals(that.Email) : that.Email != null)
return false;
if (PhoneNo != null ? !PhoneNo.equals(that.PhoneNo) : that.PhoneNo != null)
return false;
}
ArrayList#indexOf uses the Object#equals comparison method.
If you want to be able to lookup a HelperClass instance inside a Collection, you need to provide your own, overridden, equals method, and possibly also the hashCode one, for use with other, specific, Collection implementations (Map, Set, etc.).
class HelperClass {
...
#Override
public boolean equals(final Object object) {
if (object == this) {
return true;
}
if (!(object instance of HelperClass)) {
return false;
}
final HelperClass other = (HelperClass) object;
return name.equals(other.name) &&
email.equals(other.email) &&
phone.equals(other.phone);
}
}
You obviously need to have an appropriate HelperClass instance to find a match.
final String name = "Name";
final String email = "Email";
final String phone = "Phone";
final HelperClass first = new HelperClass(name, email, phone);
final HelperClass second = new HelperClass(name, email, phone);
final List<HelperClass> helpers = new ArrayList<>(8);
helpers.add(first);
final int index = helpers.indexOf(second); // index = 0
indexOf requires the object as input. If it does not find the object you are passing in, it will return -1. You need to pass the object whose location in the arraylist you are looking for as the input into the indexOf function.
Solution :
create a HelperClass to pass into the indexOf method:
.indexOf(new HelperClass(name, email, phoneno));
However that change by itself will still return -1. See the api doc for indexOf:
public int indexOf(Object o)
Returns the index of the first occurrence of the specified element in
this list, or -1 if this list does not contain the element. More
formally, returns the lowest index i such that (o==null ? get(i)==null
: o.equals(get(i))), or -1 if there is no such index.
It's using equals to decide whether it's found a match. You should have overridden the equals method on your HelperClass class, so it's using the default implementation in java.lang.Object, which compares the references, and only returns true if the two references HelperClass to the same object.
Override equals and hashcode on your HelperClass class, like:
#Override public boolean equals(Object other) {
if (!(other instanceof HelperClass)) {
return false;
}
HelperClass otherHelperClass = (HelperClass)other;
return otherHelperClass.x == this.x && otherHelperClass.y == this.y;
}
#Override public int hashCode() {
return x + y; // same values should hash to the same number
}
I have a Map in Java like so,
private HashMap<String, Object[][]> theMap;
Where the key is a String and the entry is going to be something along the line of,
theMap = new HashMap<>();
Object[][] theData = {
{Boolean.FALSE, "Text"}
};
theMap.put("Key1", theData);
Somewhere along the line I would like to check if an entry in the map is equivalent to another object. Currently I am doing it like this,
Object[][] tempData = {
{Boolean.FALSE, "Text"}
};
for(Object key: entries.keySet()) {
if(entries.get(key).equals(tempData)) {
entries.remove(key);
}
}
And it is not working.
I would prefer the comparison to be done with an object rather than with another map. I'm wondering what I'm doing wrong with this comparison here?
The reason you are not getting equality is that arrays inherit Object#equals() which is based on identity, not equality of contents. You could consider using java.util.Arrays.deepEquals(Object[], Object[]) to compare.
That is the answer to the immediate question. However, using a 2-dimensional array of Object to hold a boolean and a String is really bad code smell and indicates you need to encapsulate what you are putting in the array.
Identity vs Equivalence
Please make sure that you understand that by default the equals() method of Object checks on whether two object references are referring to the same object (identity), which is not what your code is checking.
Instead, your code is checking whether the two objects (the values you put on the map) are having the same value (equivalence).
Here are two articles about this topic:
What is the difference between identity and equality in OOP?
Overriding equals method in Java
In this particular problem of yours, I think the solution involves two steps:
Your tempData and theData does not seems to be an array
of elements of the same type (it does not appear to be a 2-dimensional
array either). Instead, it contains a Boolean value and then a
String value. In this case, I think you really should think
through what this thingy is and design a class for it (I am showing
an example below)
The class should override the equals() (and hashCode()) methods
so that you can use its equals() for equivalence checking.
Note also that your IDE (e.g. Eclipse) probably can generate a template for equals() and hashCode() for you.
Example: (here I assume your Boolean represents a condition, and your String represents a message)
class MyRecord {
private Boolean condition;
private String message;
public Boolean getCondition() {
return condition;
}
public void setCondition(Boolean condition) {
this.condition = condition;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result
+ ((condition == null) ? 0 : condition.hashCode());
result = prime * result
+ ((message == null) ? 0 : message.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;
MyRecord other = (MyRecord) obj;
if (condition == null) {
if (other.condition != null)
return false;
} else if (!condition.equals(other.condition))
return false;
if (message == null) {
if (other.message != null)
return false;
} else if (!message.equals(other.message))
return false;
return true;
}
}
I have a Class Levels which has a hashMap declared.
public class Levels{
private final Map<Unit, Object1> rateUnitCost;
public Levels(Map<Unit, Object1> levels) {
this.rateUnitCost = new HashMap<Unit, Object1>(levels);
}
public Object1 getCoverageLevel(Unit unit, Phase aP) {
return rateUnitCost.get(unit);
}
}
I am calling getCoverageLevel() method from other class and i am instantiating the Levels class rateUnitCost property as well from another class.
When seeing in debugger i am finding this value for rateUnitCost and unit object.
rateUnitCost: - Hash Map Values
rateUnitCost HashMap<K,V> (id=1248)
[0] HashMap$Node<K,V> (id=1266)
key >Unit (id=1249)
amount Money (id=1267)
flags ArrayList<E> (id=1268)
procedureId 7156
ParticipationId 104152413
value >Object1 (id=1250)
Now value of unit object is below :-
unit Unit (id=1251)
amount Money (id=1258)
flags ArrayList<E> (id=1259)
procedureId 7156
ParticipationId 104152413
when i match the value of key with this object then its matching .
But at the time of rateUnitCost.get(unit) its returning null even though Object1 is set. Object1 is getting returned from other class using below line: -
return new Object1();
Can anyone please help me to resolve this mystery.?
BasicUnit is a class which is implementing the Unit interface. BasicUnit have equals method as below :-
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
BasicUnit basicUnit = (BasicUnit) o;
if (flags != basicUnit.flags) return false;
if (procedureId != basicUnit.procedureId) return false;
if (ParticipationId != basicUnit.ParticipationId) return false;
if (amount != null ? !amount.equals(basicUnit.amount) : basicUnit.amount != null) return false;
return true;
}
and HashCode :-
public int hashCode() {
int result = procedureId;
result = 31 * result + ParticipationId;
result = 31 * result + (amount != null ? amount.hashCode() : 0);
result = 31 * result + (flags == null ? null : flags.hashCode());
return result;
}
if (flags != basicUnit.flags) return false;
You are checking for whether your Unit objects have exactly the same ArrayList of flags. This is not an equals()-type equality check; this is checking for literally the same ArrayList of flags. Now, you haven't provided the constructor etc, but I highly doubt you are reusing the same ArrayList.
Check for !(flags.equals(basicUnit.flags)) instead. Do note that ArrayList.equals() uses the E.equals() implementation, so be sure that that is implemented.
Also, note that ArrayList.equals() checks for the same list entries in the same order. I don't know if the order of your flags matters but I suspect it probably does not. You might consider making your flag collection a Set if this is the case.
i have a problem with the contains() method of TreeSet. As I understand it, contains() should call equals() of the contained Objects as the javadoc says:
boolean java.util.TreeSet.contains(Object o): Returns true if this set
contains the specified element. More formally, returns true if and
only if this set contains an element e such that (o==null ? e==null :
o.equals(e)).
What I try to do:
I have a list of TreeSets with Result Objects that have a member String baseword. Now I want to compare each TreeSet with all Others, and make for each pair a list of basewords they share. For this, I iterate over the list once for a treeSet1 and a second time for a treeSet2, then I iterate over all ResultObjects in treeSet2 and run treeSet1.contains(ResultObject) for each, to see if treeSet1 contains a Result Object with this wordbase. I adjusted the compareTo and equals methods of the ResultObject. But it seems that my equals is never called.
Can anyone explain me why this doesn't work?
Greetings,
Daniel
public static void getIntersection(ArrayList<TreeSet<Result>> list, int value){
for (TreeSet<Result> treeSet : list){
//for each treeSet, we iterate again through the list of TreeSet, starting at the TreeSet that is next
//to the one we got in the outer loop
for (TreeSet<Result> treeSet2 : list.subList((list.indexOf(treeSet))+1, list.size())){
//so at this point, we got 2 different TreeSets
HashSet<String> intersection = new HashSet<String>();
for (Result result : treeSet){
//we iterate over each result in the first treeSet and see if the wordbase exists also in the second one
//!!!
if (treeSet2.contains(result)){
intersection.add(result.wordbase);
}
}
if (!intersection.isEmpty()){
intersections.add(intersection);
}
}
}
public class Result implements Comparable<Result>{
public Result(String wordbase, double result[]){
this.result = result;
this.wordbase = wordbase;
}
public String wordbase;
public double[] result;
public int compareTo(DifferenceAnalysisResult o) {
if (o == null) return 0;
return this.wordbase.compareTo(o.wordbase);
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result
+ ((wordbase == null) ? 0 : wordbase.hashCode());
return result;
}
//never called
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
DifferenceAnalysisResult other = (DifferenceAnalysisResult) obj;
if (wordbase == null) {
if (other.wordbase != null)
return false;
} else if (!wordbase.equals(other.wordbase))
return false;
return true;
}
}
As I understand it, contains() should call equals() of the contained Objects
Not for TreeSet, no. It calls compare:
A NavigableSet implementation based on a TreeMap. The elements are ordered using their natural ordering, or by a Comparator provided at set creation time, depending on which constructor is used.
...
Note that the ordering maintained by a set (whether or not an explicit comparator is provided) must be consistent with equals if it is to correctly implement the Set interface.
Your compareTo method isn't currently consistent with equals - x.compareTo(null) returns 0, whereas x.equals(null) returns false. Maybe you're okay with that, but you shouldn't expect equals to be called.
If I have a map and an object as map key, are the default hash and equals methods enough?
class EventInfo{
private String name;
private Map<String, Integer> info
}
Then I want to create a map:
Map<EventInfo, String> map = new HashMap<EventInfo, String>();
Do I have to explicitly implement hashCode() and equals()? Thanks.
Yes, you do. HashMaps work by computing the hash code of the key and using that as a base point. If the hashCode function isn't overriden (by you), then it will use the memory address, and equals will be the same as ==.
If you're in Eclipse, it'll generate them for you. Click Source menu → Generate hashCode() and equals().
If you don't have Eclipse, here's some that should work. (I generated these in Eclipse, as described above.)
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((info == null) ? 0 : info.hashCode());
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!(obj instanceof EventInfo)) {
return false;
}
EventInfo other = (EventInfo) obj;
if (info == null) {
if (other.info != null) {
return false;
}
} else if (!info.equals(other.info)) {
return false;
}
if (name == null) {
if (other.name != null) {
return false;
}
} else if (!name.equals(other.name)) {
return false;
}
return true;
}
Yes, you need them else you won't be able to compare two EventInfo (and your map won't work).
Strictly speaking, no. The default implementations of hashCode() and equals() will produce results that ought to work. See http://docs.oracle.com/javase/6/docs/api/java/lang/Object.html#hashCode()
My understanding is that the default implementation of hashCode() works by taking the object's address in memory and converting to integer, and the default implementation of equals() returns true only if the two objects are actually the same object.
In practice, you could (and should) probably improve on both of those implementations. For example, both methods should ignore object members that aren't important. In addition, equals() might want to recursively compare references in the object.
In your particular case, you might define equals() as true if the two objects refer to the same string or the two strings are equal and the two maps are the same or they are equal. I think WChargin gave you pretty good implementations.
Depends on what you want to happen. If two different EventInfo instances with the same name and info should result in two different keys, then you don't need to implement equals and hashCode.
So
EventInfo info1 = new EventInfo();
info1.setName("myname");
info1.setInfo(null);
EventInfo info2 = new EventInfo();
info2.setName("myname");
info2.setInfo(null);
info1.equals(info2) would return false and info1.hashCode() would return a different value to info2.hashCode().
Therefore, when you are adding them to your map:
map.put(info1, "test1");
map.put(info2, "test2");
you would have two different entries.
Now, that may be desired behaviour. For example, if your EventInfo is collecting different events, two distinct events with the same data may well want to be desired to be two different entries.
The equals and hashCode contracts is also applicable in a Set.
So for example, if your event info contains mouse clicks, it may well be desired that you would want to end up with:
Set<EventInfo> collectedEvents = new HashSet<EventInfo>();
collectedEvents.add(info1);
collectedEvents.add(info2);
2 collected events instead of just 1...
Hope I'm making sense here...
EDIT:
If however, the above set and map should only contain a single entry, then you could use apache commons EqualsBuilder and HashCodeBuilder to simplify the implementation of equals and hashCode:
#Override
public boolean equals(Object obj) {
if (obj instanceof EventInfo) {
EventInfo other = (EventInfo) obj;
EqualsBuilder builder = new EqualsBuilder();
builder.append(name, other.name);
builder.append(info, other.info);
return builder.isEquals();
}
return false;
}
#Override
public int hashCode() {
HashCodeBuilder builder = new HashCodeBuilder();
builder.append(name);
builder.append(info);
return builder.toHashCode();
}
EDIT2:
It could also be appropriate if two EventInfo instances are considered the same, if they have the same name, for example if the name is some unique identifier (I know it's a bit far fetched with your specific object, but I'm generalising here...)