Why doesn't class ProcessBuilder override equals()? - java

I recently discovered that class ProcessBuilder in JDK6 does not override equals(). Is there a reason? Since the class is mutable, I can understand why it does not override hashCode().
I was surprised to see this code not work:
ProcessBuilder x = new ProcessBuilder("abc", "def");
ProcessBuilder y = new ProcessBuilder("abc", "def");
if (x.equals(y)) { // they are never equal
// something important here
}
I looked into the JDK6 source code for class ProcessBuilder, and I do not see an override for equals().
I have a feeling there is a deeper reason, beyond this one class. Perhaps this is intentional?

It is considered best practice to make mutable objects not equal unless they are the same object. This is because the object could change later. Consider the following
Set<ProcessBuilder> pbSet = new HashSet<>();
pbSet.add(x);
pbSet.add(y);
// if x and y were equal pbSet would have one element.
y.setSomething()
// should pbSet have one or two elements.
Worse than this is the opposite case where two object could be different but later made the same. This means the Set would have a duplicate object.
What is interesting is that collections are mutable but still have equals and hashCode. I think the reason this is the case is that there is no immutable collections. e.g. String override equals(), StringBuilder does not.

To complement #PeterLawrey's answer: for objects which are mutable by nature, implementing equals and hashcode is risky in any event. You have no guarantee that such objects are safely published at all. It therefore makes sense that the authors of such classes just "gave up" on equals and hashcode for such classes.
HOWEVER: if you are reasonably sure that you can control this equality, there is something for you: Guava's Equivalence. If you can ensure sufficiently controlled access to highly mutable classes, this allows you to define an equals/hashcode strategy for such objects so that you can even use them in, say, a HashSet.
More about this Equivalence: for an "unstable" class X which is mutable by nature, but for which you can guarantee equivalence in a given context, you implement an Equivalence<X>. Then you "wrap" these instances of X into, for instance, a:
Set<Equivalence.Wrapper<X>>
You'll then add to this set using:
set.add(eq.wrap(x));
where eq is your implementation of an Equivalence.

Related

Why java insist developers to add a comparison mechanism rather than using a default comparison mechanism while working on sorted collection?

Say we have a class Foo like
class Foo {
private int attr1;
private String attr2;
// getters and setters.
// hashCode and equals not overrided.
}
So while adding references of Foo to a Set or a Map (as a key) duplicates will be identified based on their address locations. Now if I override hashCode and equals based on attr2, duplicates will be identified based on the value of attr2. That's how duplicate filtration works in Java - Look for any user defined mechanism, if present use that otherwise use the default mechanism.
If we try to add references of Foo to a sorted collection like TreeSet, TreeMap it will throw ClassCastException saying that there is no comparison mechanism. So we can make it either Comparable or Comparator type and can define a comparison mechanism.
So my question is while finding duplicates, if the user hasn't defined any mechanism Java will look for default mechanism, but while sorting or comparing it insists user to define a mechanism. Why won't it go for a default mechanism, for example comparing references based on their hashcode? Is it because any OOPs concept or any concept in Java may be violated if they go for a default comparison?
TL;DR Give me a sensible way of defining a total ordering between opaque objects.
It is sensible to say, lacking any other information, that objects are the same only if they are physically the same. This is what the default equals and hashCode do.
It is not sensible, and indeed makes no sense, to say that one object is "bigger" than another because a digest of its memory location is bigger.
Even more dammingly for your proposed mechanism, in modern Java the old adage that a hashCode is a memory location is actually incorrect. An Object is assigned a random number at construction and that is used as the hashCode.
So you are really proposing:
"Lacking any information about the objects to be ordered, we will order them completely at random"
I cannot think of any situation where this default behaviour is:
expected
useful

Find an a specific instance, what is the best approach

So imagine I have two instances of a class:
public class MyClass {
public void sayHello() {
System.out.println("Hello");
}
}
a = new MyClass();
b = new MyClass();
Now I add those to another object, such as:
public class OtherClass {
private ArrayList<MyClass> myClsList = new ArrayList<>();
public void add(MyClass obj) {
myClsList.add(obj);
}
public void remove(MyClass obj) {
// ????
}
}
c = new OtherClass();
c.add(a);
c.add(b);
Now I want to remove one specific instance e.g
c.remove(a);
Could I just iterate over them and test for equality, I mean this should theoretically work, since the two instances have distinct "internal pointers"?
I guess using a HashMap based approach would be more efficient, but what can I use as an key there (suppose I can't add unique instance ids or something).
EDIT: There is some confusion as to what exactly I'd like to know.
The key here is that I'd like to know if there is any way of removing that specific instance from c's ArrayList or whatever Aggregator Object I might use, just by providing the respective object reference.
I imagine this could be done by just keeping the ArrayList and testing for equality (although I'm not a 100% sure) but it would be cleaner if it was possible without iterating through the whole list.
I'd just like to know if anything of the like is possible in Java. (I know how to workaround it by using additional information but the clue is to just have the respective object reference for filtering/ retrieving purposes.
You can use a.toString(), according to the Java doc,
The toString method for class Object returns a string consisting of
the name of the class of which the object is an instance, the at-sign
character `#', and the unsigned hexadecimal representation of the hash
code of the object.
This should give you an unique identifier for your class instance, hence you can use this as a hash key without storing / creating any extra identifiers.
NB: Be careful with this practice, don't rely on the the value returned by `Object.toString(), as being related to the actual object addres, see detailed explanation here.
While your question is one that many beginners have (including myself), I believe that your concern is not justified in this case. The features you are asking for are already built into the Java language at the specification level.
First of all, let's look at Object.equals(). On the one hand, the Language Specification states that
The method equals defines a notion of object equality, which is based on value, not reference, comparison.
However, the documentation for Object.equals() clearly states that
The equals method for class Object implements the most discriminating possible equivalence relation on objects; that is, for any non-null reference values x and y, this method returns true if and only if x and y refer to the same object (x == y has the value true).
This means that you can safely redirect OtherClass.remove to ArrayList.remove(). Whatever Object.equals is comparing works exactly like a unique ID. In fact, in many (but not all) implementations, it compares the memory addresses to the objects, which are a form of unique ID.
Quite understandably, you do not wish to use linear iteration every time. As it happens, the machinery of Object is perfectly suited for use with something like a HashSet, which, by the way is the solution I recommend you use in this case.
If you are not dealing with some huge data set, we do not need to discuss the optimization of Object.hashCode(). You just need to know that it will implement whatever contract is necessary to work correctly with Object.equals to make HashSet.remove work correctly.
The spec itself only states that
The method hashCode is very useful, together with the method equals, in hashtables such as java.util.Hashmap.
This does not really say much, so we turn to the API reference. The two relevant point are:
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.
Simply put, the hashCode of equal objects must be the same, but an equal hashCode does not necessarily mean equal objects. Object implements this contract, so you can use it with a HashSet, which is backed by a HashMap.
The one piece of information that is missing to make this a formal argument in favor of not doing any additional work, is why I keep citing the API reference as if it was the language specification. As it happens:
As noted above, this specification often refers to classes of the Java SE platform API. In particular, some classes have a special relationship with the Java programming language. Examples include classes such as Object, Class, ClassLoader, String, Thread, and the classes and interfaces in package java.lang.reflect, among others. This specification constrains the behavior of such classes and interfaces, but does not provide a complete specification for them. The reader is referred to the Java SE platform API documentation.
[emphasis mine], but you get the idea. The Java SE API reference is the language spec as far as the behavior of the methods of Object is concerned.
As an aside, you will probably want to stay away from something like TreeSet, because that will require you to add a bunch of machinery to your implementation. As a minimum, MyClass instances will have to be orderable, either by implementing Comparable, or by assigning a custom Comparator to the Set.
TL;DR
The language specification states that you have at least the following two options available to you with no additional effort on your part:
Make myClsList an ArrayList and use the appropriate add()/remove() methods as you see fit.
Make myClsList a HashSet and use the appropriate add()/remove() methods.
I recommend the second option. In fact, instead of containment, you may consider extending HashSet so you don't have to bother implementing your own add/remove methods.
Final Note
All this works as long as MyClass overrides neither Object.equals nor Object.hashCode. The moment you do that, you put the burden of satisfying contractual requirements entirely on yourself.

Why override equals instead of using another method name

This seems like a silly question but why do we override equals method instead of creating a new method with new name and compare using it?
If I didn't override equals that means both == and equals check whether both references are pointed to same memory location?
This seems like a silly question but why do we override equals method instead of creating a new method with new name and compare using it?
Because all standard collections (ArrayList, LinkedList, HashSet, HashMap, ...) use equals when deciding if two objects are equal.
If you invent a new method these collections wouldn't know about it and not work as intended.
The following is very important to understand: If a collection such as ArrayList calls Object.equals this call will, in runtime, resolve to the overridden method. So even though you invent classes that the collections are not aware of, they can still invoke methods, such as equals, on those classes.
If I didn't override equals that means both == and equals check whether both references are pointed to same memory location?
Yes. The implementation of Object.equals just performs a == check.
You override equals if you are using classes that rely on equals, such as HashMap, HashSet, ArrayList etc...
For example, if you store elements of your class in a HashSet, you must override hashCode and equals if you want the uniqueness of elements to be determined not by simple reference equality.
Yes, if you don't override equals, the default equals implementation (as implemented in the Object class) is the same as ==.
In addition to the main reason, already given in other answers, consider program readability.
If you override equals and hashCode anyone reading your code knows what the methods are for. Doing so tells the reader the criteria for value equality between instances of your class. Someone reading your code that uses equals will immediately know you are checking for value equality.
If you use some other name, it will only confuse readers and cost them extra time reading your JavaDocs to find out what your method is for.
Because equals() is a method of the Object class, which is the superclass of all classes, and due to which it is inherently present in every class you write. Hence every collection class or other standard classes use equals() for object comparsion. If you want your custom class objects to be supported for equality by other classes, you have to override equals() only (since other classes know that for object comparion call equals()). If you are only using your own classes, you might create a new method and make sure everything uses your custom method for comparison.
The equals and hashcode method are special methods, widely used across the java's utility classes specially collection framework, and the wrpper classes e.g. String, Integer have overridden this method, So e.g. if you are placing any Object of your choice which has correct equals and hashcode implementation inside the HashSet, to maintain the property of uniqueness the hashcode will compare with all the existing object in hashset, and if it finds any of the hashcode matching then it looks into the equals method to double check if both are really equal and if that equality check also is pass then incoming object is rejected, but if the hashcode equality check is not passed then hashset will not go for the equals method and straight way place that object into the hashset. So we need to make sure the implementation of equals and hashcode is logically proper.
A class like HashMap<T,U> needs to have some means of identifying which item in the collection, if any, should be considered equivalent to a given item. There are two general means via which this can be accomplished:
Requiring that anything to be stored in a collection must include virtual methods to perform such comparison (and preferably provide a quick means (e.g. hashCode()) of assigning partial equivalence classes).
Require that code which creates the collection must supply an object which can accept references to other objects and perform equivalence-related operations upon them.
It would have been possible to omit equals and hashCode() from Object, and have types like HashMap only be usable with key types that implement an equatable interface that includes such members; code which wishes to use collections of references keyed by identity would have to use IdentityHashMap instead. Such a design would not have been unreasonable, but the present design makes it possible for a general-purpose collection-of-collections type which uses HashMap to be usable with things that are compared by value as well as by identity, rather than having to define a separate types for collection-of-HashMap and collection-of-IdentityHashMap.
An alternative design might have been to have a GeneralHashMap type whose constructor requires specifying a comparison function, and have IdentityHashMap and HashMap both derive from that; the latter would constrain its type to equatable and have its identity functions chain to those of the objects contained therein. There would probably have been nothing particularly wrong with that design, but that's not how things were done.
In any case, there needs to be some standard means by which collections can identify items that should be considered equivalent; using virtual equals(Object) and getHashCode() is a way of doing that.
Question 1
There are Two things.
equals() is Located inside Object class
Collection framework using equals() and hashcode() methods when comparing objects
Question 2
Yes for comparing two Object. but when You comparing two String Objects using equals() its only checking the value.

Why there is no Hashable interface in Java

Object in Java has hashCode method, however, it is being used only in associative containers like HashSet or HashMap. Why was it designed like that? Hashable interface having hashCode method looks as much more elegant solution.
The major argument seems to me that there is a well-defined default hashCode that can be calculated for any Java object, along with an equally well-defined equals. There is simply no good reason to withhold this function from all objects, and of course there are plenty reasons not to withhold it. So it's a no-brainer in my book.
This question is claimed as a duplicate from another which asks why there's no interface which behaves like Comparator (as distinct from Comparable) but for hashing. .NET includes such an interface, called IEqualityComparer, and it would seem like Java could as well. As it is, if someone wants to e.g. have a Java collection which e.g. maps strings to other objects in case-insensitive fashion (perhaps the most common use of IEqualityComparer) one must wrap the strings in objects whose hashCode and equals methods act on a case-insensitive basis.
I suspect the big issue is that while an "equalityComparer" interface could be convenient, in many cases efficiently testing an equivalence relation would require caching information. For example, while a case-insensitive string-hashing function could make an uppercase-only copy of the passed-in string and call hashCode on that, it would be difficult to avoid having every request for the hashcode of a particular string repeat the conversion to uppercase and the hashing of that uppercase value. By contrast, a "case-insensitive string" object could include fields for an uppercase-only copy of the string, which would then only have to be generated once for the instance.
An EqualityComparer could achieve reasonable performance if it included something like a WeakHashMap<string,string> to convert raw strings to uppercase-only strings, but such a design would either require different threads to use different EqualityComparer instances despite the lack of externally visible state, or else require performance-robbing locking and synchronization code even in single-threaded scenarios.
Incidentally, a second issue that arises with comparator-style interfaces is that a collection type which uses an externally-supplied comparator (whether it compares for rank or equality) is that the comparator itself becomes part of the state of the class which uses it. If hash tables use different EqualityComparer instances, there may be no way to know that they can safely be considered equivalent, even if the two comparators would behave identically in all circumstances.

TreeSet/TreeMap equivalent for HashSet/HashMap (custom hasher)

TreeSet has a constructor that takes a comparator, meaning even if the objects you store aren't Comparable objects by themselves, you can provide a custom comparator.
Is there an analogous implementation of a nonordered set? (e.g. an alternative to HashSet<T> that takes a "hasher" object that calculates equals() and hashCode() for objects T that may be different from the objects' own implementations?)
C++ std::hash_set gives you this, just wondering if there's something for Java.
Edit: #Max brings up a good technical point about equals() -- fair enough; and it's true for TreeMap and HashMap keys via Map.containsKey(). But are there other well-known data structures out there that allow organization by custom hashers?
No, having a "hasher" object is not supported by the Collections specifications. You can certainly implement your own collection that supports this but another way to do this is to consider the Hasher to be a wrapping object that you store in your HashSet instead.
Set<HasherWrapper<Foo>> set = new HashSet<HasherWrapper<Foo>>();
set.add(new HasherWrapper(foo));
...
The wrapper class would then look something like:
private class HasherWrapper<T> {
T wrappedObject;
public HasherWrapper(T wrappedObject) {
this.wrappedObject = wrappedObject;
}
#Override
public int hashCode() {
// special hash code calculations go here
}
#Override
public boolean equals(Object obj) {
// special equals code calculations go here
}
}
There is no such implementation in the standard library, but it doesn't prevent you from rolling your own. This is something i've often wanted to have myself.
See http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4771660 for the reason:
We wanted to avoid the complexity. We seriously entertained this notion
at the time the collections framework was designed, but rejected it. The
power-to-weight ration seemed to low. We felt that equals was what you
wanted 95% of the time; ==, 4%; and something else 1%. Writing sensible
contracts for bulk operations when is very tricky when equality predicates
differ.
No, there is not and there can not be by specification. Moreover, you misunderstood the way TreeSet uses it's Comparator.
From TreeSet Javadoc:
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. (See Comparable or Comparator
for a precise definition of consistent with equals.) This is so
because the Set interface is defined in terms of the equals operation,
but a TreeSet instance performs all element comparisons using its
compareTo (or compare) method, so two elements that are deemed equal
by this method are, from the standpoint of the set, equal. The
behavior of a set is well-defined even if its ordering is inconsistent
with equals; it just fails to obey the general contract of the Set
interface.
From Comparable javadoc:
The natural ordering for a class C is said to be consistent with
equals if and only if e1.compareTo(e2) == 0 has the same boolean value
as e1.equals(e2) for every e1 and e2 of class C. Note that null is not
an instance of any class, and e.compareTo(null) should throw a
NullPointerException even though e.equals(null) returns false.
From Collection javadoc:
boolean contains(Object o)
Returns true if this collection contains
the specified element. More formally, returns true if and only if this
collection contains at least one element e such that (o==null ?
e==null : o.equals(e)).
Therefore, by specification there can not be any kind of class that implements Collection<E> interface and fully depend on some external Comparator-style object to insert objects. All Collections should use equals method of an Object class to verify if the object is already inserted.
There is definitely nothing like that, hashcode() and equals() are defining attributes of an object and should not be changed. They define what makes an object equal to each other and this shouldn't be different from one set to another. The only way to do what you are talking about is to subclass the object and write a new hashcode() and equals() and this would only really make sense to do if the subclass had a defining variable that should be added in addition to the super class' hashcode() and equals(). I know this may not be what you are aiming for but I hope this helps. If you explain your reasoning more for wanting this then it might help to find a better solution if there exists one.

Categories

Resources