I have tried a number of ways to compare two strings of 'phone numbers' and none seem to pass the tests.
the code I am currently using:
public boolean equals (PhoneNumber other) {
boolean results = other.equals(digits);
return results;
}
The test I am trying to pass without hard coding for it:
p = new PhoneNumber("5551212");
p2 = new PhoneNumber("5551212", "Home");
displayResults(p.equals(p2));
p.setDigits("1234123");
displayResults(p.equals(p2) == false);
Two separate problems:
[1] digits is a string; other is a phonenumber. these will NEVER be equal. What you're presumably looking for, is other.digits.equals(this.digits). Now we're comparing the digits of phone number A with the digits of phone number B: Apples and apples. That has a chance of working out.
[2] the equals method's signature is public boolean equals(Object other). Your equals method has a different signature, which means, it is an entirely different method with no relation whatsoever to the real equals method.. other than the coincidence that it so happens to share a name with it. It means your 'fake' equals method would not be used by other code that uses equals methods. Such as the contains method of an arraylist, for example. The solution is to make that equals method have as argument Object other.
[3] when you do that, you won't be able to call .digits on other; after all, only phonenumbers have digits; any random object isn't guaranteed to. The solution is to cast the other, but only do that after checking if it is a phonenumber:
public boolean equals(PhoneNumber other) {
if (!(other instanceof PhoneNumber)) {
// somebody is comparing apples to oranges. Obviously, then...
return false;
}
return ((PhoneNumber) other).digits.equals(this.digits);
}
note also that for proper operation of such an object in, say, HashSet, you must always override both the hashCode and the equals method, or neither.
Project Lombok automates all this stuff, you may be interested in this.
Related
A GUI program that displays a label at the top with "Order not added -duplicate" when a duplicate order is attemped to be added by the user.
for the order in orderList to be considered duplicate all inputs in textfields (when the button is clicked) have to be the same.
I suppose that I have to use a for loop to check if the order is already in the arrayList but I'm not sure how, I'm pretty new to java.
Instead of an array you should be using a Set which is a collection specifically designed to prevent duplicates:
A Set is a Collection that cannot contain duplicate elements. It models the mathematical set abstraction. The Set interface contains only methods inherited from Collection and adds the restriction that duplicate elements are prohibited. Set also adds a stronger contract on the behavior of the equals and hashCode operations, allowing Set instances to be compared meaningfully even if their implementation types differ. Two Set instances are equal if they contain the same elements.
Since you are new to Java language take a step back and first learn about basic collections provided by the standard library: List, Set and Map. Once you understand this abstractions and their implementation variants you will be able to solve a lot of common day-to-day problems.
While a Set would be good in a real world scenario, this appears to be a homework assignment with arbitrary limitations so let's see how it can be done using an ArrayList.
First thing I would recommend doing is overriding your .equals() method on your Order object to properly compare field values. Something like...
#Override
public boolean equals(Object obj) {
//Same reference so same object in every sense of the word
if(obj == this)
return true;
//Not instanced nor the same class so clearly not equal
if(obj == null || obj.getClass() != getClass())
return false;
//Cast it to the same object type
Order o = (Order) obj;
//Start comparing every variable, will return false if any of them don't match
//Be sure to use the equals operation for any variable objects such as String
return this.name.equals(o.name) && this.value == o.value && this.someVar == o.someVar; //ect...
}
Then you just need to take your fields and turn it into a object like normal...
Order order = new Order(field1.getText(), field2.getText(), field3.getText()); //ect...
And run it in a compare against the existing values. I recommend making a separate method for this...
public static boolean orderExists(Order order) {
for(Order otherOrder : orderArrayList) {
if(order.equals(otherOrder))
return true;
}
return false;
}
From there, the orderExists() method will tell you if it's already in there and you can implement your code for displaying the error. Hope this helps you get set up enough to handle the rest on your own alright!
I have studied similar examples and override these methods like this, but still get added to the HashSet<NamedObject> objects with the equal names BUT different id.
public class NamedObject {
String name;
BigInteger id;
#Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
NamedObject that = (NamedObject) o;
return this.name.equals(that.getName());
}
#Override
public int hashCode() {
return id.hashCode();
}
}
I have studied similar examples and override these methods like this, but still get added to the HashSet<NamedObject> objects with the equal names BUT different id.
Yes, because you're only using the id in the hash code, and only using the name in the equality check. HashSet<> will only call equals() when it finds an existing object with the same hash code as the one you're trying to add... if your IDs are different, it's highly unlikely that the hash codes will be different, so it's never calling equals with the object that has the same name.
Your hashCode() and equals() methods must be consistent with each other. You need to decide whether equality is based on name, ID, or both - and then use that in both hashCode() and equals().
From the Object.hashCode() documentation:
The general contract of hashCode is:
Whenever it is invoked on the same object more than once during an execution of a Java application, the hashCode method must consistently return the same integer, provided no information used in equals comparisons on the object is modified. This integer need not remain consistent from one execution of an application to another execution of the same application.
If two objects are equal according to the equals(Object) method, then calling the hashCode method on each of the two objects must produce the same integer result.
It is not required that if two objects are unequal according to the equals(java.lang.Object) method, then calling the hashCode method on each of the two objects must produce distinct integer results. However, the programmer should be aware that producing distinct integer results for unequal objects may improve the performance of hash tables.
Your methods break the middle of these requirements at the moment.
I have one class having two variables named as x and y. In this class I have overrided the equals and hashCode methods to compare two object of this class. But our requirement is to compare two object of this class sometimes on the basis of x and sometimes on the basis of y. Is it possible dynamically in Java?
Edit:
I have one more class named as B, in this class there is two method m1 and m2 and I want to compare the above class object in such a way that when we call from m1 (for sorting) the above objects will be compared on the basis of x (means compare object by compare x variable) and when we call from m2 (for sorting) then we compare according to y.
Changing behavior based on last method to call your method is possible, but you shouldn't do it for a lot of reasons.
it violates the equals contract, thus breaking the functionality of several algorithms designed to handle collections
result of the comparison cannot be anymore known without knowing the caller, which is a hard dependency that's prone to break
However, if you insist you need it, you can do like
StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
if (stackTraceElements.length < 3)
{
// do something when last method to call is not available
// probably you'll want to return something
}
String callerMethod = stackTraceElements[2].getMethodName();
if (callerMethod.equals("m1"))
{
// something
} else
{
// something else
}
This example is simplified as it assumes the method calling this method is the candidate - it can be some method further down the call stack.
As noted, this is not recommended. Rather use different kind of comparators for the purpose, and give a relevant comparator to the sort method to have different kind of sorting per context.
Depending on the complexity of the comparison, you can either do this within the class or use two seperate comparator classes.
public boolean equals(Object other){
if(condition == true){
return x==x;
}else{
return y==y;
}
}
or
public boolean equals(Object other){
if(condition == true){
return new CompareX(this, other).compare();
}else{
return new CompareY(this, other).compare();
}
}
You have to extend the comparison logic to a valid one, of course.
Oh and, the same principle applies to the hashCode.
It's not possible, to change the behaviour of equals dynamically. You have to use Comparator to provide the comparison from the outside of the class.
Since Java8 with Lambdas it is easy to use Comparators.
There is a method comparing. You can create Comparators out of Methods, which you want to compare.
// A comparator comparing on x
Comparator<A> comp1 = comparing (a -> a.x);
// A comparator comparing on the output of m1
Comparator<A> comp2 = comparing (A::m1);
// A comparator comparing on the output of m1 and when equals, comparing on x
Comparator<A> comp2 = comparing (A::m1).thenComparing (a -> a.x);
From the external point you can decide, which comparator to use.
There's a new way to sort your data in Java8, too:
List<A> data;
data.stream ().sorted (comparing (a -> a.x));
Of course you have to be allowed to use Java8 for this.
If you can add flag setting code to m1 and m2 you can modify eis answer to get rid of the kludgy stacktrace stuff.
It is still kludgy.
The output of the below code is false
String str = "3456";
String str1 = "3456";
System.out.println(Integer.valueOf(str).equals(str1));
I didn't understand it. I thought it will return true. As I am preparing for SCJP, understanding the reason behind it may be helpful. Can someone please help?
An Integer will never be equal to a String.
Both classes have very strict equals() definitions that only accept objects of their respective types.
Integer.equals():
The result is true if and only if the argument is not null and is an Integer object that contains the same int value as this object.
String.equals():
The result is true if and only if the argument is not null and is a String object that represents the same sequence of characters as this object.
That's actually a quite common way to implement equals(): only objects of the same class (and occasionally subclasses) can be equal. Other implementations are possible, but are the exception.
One common exception are the collections such as List: every List implementation that follows the convention will return true when compared to any other implementation, if it has the same content in the same order.
Usually, when implementing equals(), one of the first things to do is to check whether the objects are of one and the same type.
public boolean equals(Object obj) {
if (!(obj instanceof SomeType)) return false;
...
}
This is also applied in the Integer and String classes, which answers the question why do you receive false as a result.
The general contract of the equals() methods states (among other things) that the objects that are being compared need to be of the same class. That's why you'll never be able to compare Apples with Oranges.
For the full contract of the equals() method, see the javadocs.
a Integer Object can't equals with String Object
use :
boolean a = str.equals(str1);
OR
boolean a = (Integer.parseInt(str) == Integer.parseInt(str1));
Is there any Equalator mechanism like Comparator so I can have different equals for coparing lists?
EDIT: My goal is to differentiate between current list1.equals(list2) which checks if its a shallow copy or also a deep copy with all objects a.equals(b) and list1.identical(list2) which checks if its simply shallow copy with unmodified listing
All these lists are from the same model. Some are copies of themselves so they hold the pointer to same objects, and others are deep copies so hierarchy is totally replicated, because they have updates in content, not just in structure.
I find myself oftenly makin list1.equals(list2) but I need a mechanism for telling if both are TOTAL copies (same objects in same order for collections) or sometimes if they are LOGICAL copies (through my own implemented logic equals), so list would call equals and objects should implement something more than a==b.
My problem is there is no Equalator interface, and if I override objects equals to I loose the capability of comparing by TOTAL EQUAL (a==b)
For example, this would be nice;
Collections.equal(l1,l2,new Equalator(){
#Override public boolean equals(Obj1,Obj2){
//Default lists comparison plus commparison of objects based on
return (obj1.propertyX() == obj2.propertyX());
}
});
and still I could do list1.equals(list2) so they use default equals (obj1==obj2) and this would be only true if contained objects are exactly the same.
First operation is useful for checking if list (which could be an updated list with totally recreated objects from the model) is still equals to the old list.
Second operation is useful for checking if list (which was a shallow copy of the old current version of data model), it does not contain any transcendent change from moving it around inside the code when it was the udpdated version.
EDIT: A very good example would be having a list of Point(x,y). We should be able to know if both list are equal because they are exactly same set of points or equal because the points they contain are equal in a logical way. If we could implement both phyEqual and logEqual to object, and have both methods in any object so list.phyEqual(list2) or list1.logEqual(list2)
Your question doesn't really come across clearly, at least to me. If this doesn't answer it properly, could you re-word a little bit?
Within a given Collection concrete type, most equals implementations already do what you hint at.
For instance:
public boolean equals(Object o) {
if (o == this)
return true;
In this case, something like this might make sense.
You can easily override this:
#Override
public boolean equals(Object o) {
if (o.getPrimaryKey() == this.getPrimaryKey())
return true;
return super().equals(o);
[test for nulls should be added]
If you are creating standard collections, you can even anonymously override the equals method during construction.
If this doesn't do what you want, you could extend Collections yourself and override any of the methods there to do a similar thing.
Does that help?
A late answer, but maybe it will be useful for someone...
The Guava Equivalence class is the same for equivalence as Comparator for comparing. You would need to write your own method for comparing the lists (there is no support in Guava for that), but then you could call this method with various equivalence-definitions.
Or you can roll your own interface:
interface Equalator<T> {
boolean equals(T o1, T o2);
}
Again, you need to write your (trivial) method
boolean <T> listEquals(List<T> list1, List<T> list2, Equalator<T> equalator) {
...
}
...but then you can reuse it with different ListEqualator implementations.