Win a copy of High Performance Python for Data Analytics this week in the Python forum!
  • Post Reply Bookmark Topic Watch Topic
  • New Topic
programming forums Java Mobile Certification Databases Caching Books Engineering Micro Controllers OS Languages Paradigms IDEs Build Tools Frameworks Application Servers Open Source This Site Careers Other all forums
this forum made possible by our volunteer staff, including ...
Marshals:
  • Campbell Ritchie
  • Paul Clapham
  • Ron McLeod
  • Bear Bibeault
  • Liutauras Vilda
Sheriffs:
  • Jeanne Boyarsky
  • Tim Cooke
  • Junilu Lacar
Saloon Keepers:
  • Tim Moores
  • Tim Holloway
  • Stephan van Hulst
  • Jj Roberts
  • Carey Brown
Bartenders:
  • salvin francis
  • Frits Walraven
  • Piet Souris

Different implementations of hashCode() and equals()

 
Ranch Foreman
Posts: 175
8
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
This reply came about from an intersection of seeing this thread concurrently with blowing an interview problem that relied on it being totally safe and valid to have a HashSet< HashSet<Type> > -- which I first presumed was good, then worried about, then decided it is all good due to the Overrides that HashSet makes in this area.

I read Odersky et al's paper on Object Equality in Java.
Whether the hashCode() equals() contract is in scope for the 819 or not, I know I need to know it for decent programming and for interviews, so I care about the first two points.

So sure, 1., 2.  "Override .equals(Object o) and .hashCode() in a consistent manner or you will be very sorry if you try to use your class as a key in a HashMap or an element in a HashSet."
I'd already totally gotten that part down, or so I'd thought.

But that paper makes a big deal of a third thing I think I was violating.

Because it was inherited from Object() and represents a notion of "Object equality", in my mind it was essential to include in both .equals() and .hashCode() overrides everything about an instance of its class.  That is, if I would be persisting it or cloning it or serializing it, it had better be in there, as it contributed to the notion of equality.  Reading the third and fourth parts of the article led me to think I was barking up the wrong dog.

In the comments to that paper (which went all over the Universe and discussed every aspect of the topic one could imagine) a common theme was "Stupid HashMap and HashSet don't let us pass in an Equalatator object, so we are sad."

So where I was thinking that .equals() needed to represent the notion my class had of two instances being "exactly equal", it seems that it is more like the notion of identity equality with respect to this class.  Not Object's dumb-dumb idea of whether they both point to the same heap instance, which really matters only to the JVM, not my program, but not necessarily my idea of "The two objects being identical in content including all mutable fields if any" either.

My prior interpretation of the latter had me feeling "Only immutable objects may ever safely be placed in HashSet' or as keys in HashMaps!  Even then, we need to be very careful because anything that would 'change' an immutable object requires a removal and re-add in that Set or Map which the code making the change hopefully knows about."

What I think I got from that paper was that the Object .equals() and .hashCode() need to work correctly to identify my class as a key or as a set member.  There is no requirement from this point of view to include every instance member of my class that we might want to persist or serialize. but only enough to be a valid primary key to represent it.

This made me go back to the original question in my mind when I was first contemplating the .hashCode() .equals() contract:
"Where else am I contractually bound to making use of .equals() or .hashCode() apart from the Hash-based data structures?"

.equals() is used pretty much everywhere the various Collection and Collections methods you would expect would make use of it -- right?

So we are constrained to have that single definition of .equals(Object o) be used in all those places to be identical to that utilized for purposes of hashSet or hashMap key identity.
'
It feels like we might want to have a totallyEquals() or reallyEquals() that checks values of all state variables in cases we care about that and want to post-process the results of a .contains() .containsAll()  etc. which match on those parts of our Object which are necessary to identify our object as a primary key in a hash structure, but might have some different state values representing state...

But wait, .retainAll(), removeAll(), replaceAll() methods would just blindly follow equals() without paying any mind to our user-defined method representing ":total equality".

I conclude that my notions of Object equality, with respect to all places it is used in the Collections Framework and possibly other places, may be deficient and not well-thought-out enough.

My whole notions of OOAD and what data members can go into a single class are also called into question by these musings.

None of this even gets to the problems that the paper in question, Winston's "Road to Equality" etc. address in terms of possible equality of classes and their subclasses which add new state information.  I start finding similar doubts before I ever create a subclass, just because I have to utilize one singly defined .equals( Object ) everywhere across the Collections Framework rather than passing in an Equalitator that behaves like a (custom) Comparator for the purposes of one data structure or operation.

I felt like I had a decent grip on both the .hashCode() .equals() contract and how to use the Java Collections Framework with classes I create before, now I am really not so sure.
 
Marshal
Posts: 71752
312
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Jesse Silverman wrote:. . . HashSet< HashSet<Type> > . . .

Of course it isn't:-
  • 1: It should be called Set<Set<Foo>>.
  • 2: You are putting references to mutable objects into a hash based collection. If the hash code changes, you won't find your elements again.
  • Whether the hashCode() equals() contract is in scope for the 819 or not, I know I need to know it for decent programming . . .

    This isn't an exam forum. Who cares whether it is in the exam syllabus; it is in scope for this forum.

    . . . it was essential to include in both .equals() and .hashCode() overrides everything about an instance of its class. . . .

    You may wish to make two Cars equal if they have the same paintwork identification and maker. You might wish to ignore speed and direction. The important thing is that the alphabet of variables used to determine equality and to calculate a hash code be the same. (I think you may be able to get away with missing something out of the hash code, but not vice versa.)

    an Equalatator object, . . .

    What's one of them when it's at home?

    . . . notion of identity equality with respect to this class.  Not Object's dumb-dumb idea of whether they both point to the same heap instance . . .

    The method in Object doesn't know whether its subclasses will have any fields or what they will be, so it can only use identity to determine equality: obj == this. What's “dumb‑dumb” about that? The idea is that subclass writers will know which fields they want to compare for equality, and they can follow the general contract. Easy. (Actually it isn't at all easy.)

    My prior interpretation of the latter had me feeling "Only immutable objects may ever safely be placed in HashSet' or as keys in HashMaps!

    Yes, that agrees with what I said earlier. This is what it says in the documentation for Set:-

    The API for Set wrote:Note: Great care must be exercised if mutable objects are used as set elements.  

    JS wrote:Even then, we need to be very careful because anything that would 'change' an immutable object requires a removal and re-add in that Set or Map which the code making the change hopefully knows about." . . .

    That is a contradiction in terms.

    "Where else am I contractually bound to making use of .equals() or .hashCode() apart from the Hash-based data structures?" . . . we are constrained to have that single definition of .equals(Object o) be used in all those places to be identical to that utilized for purposes of hashSet or hashMap key identity.

    All the Collection framework classes are written on the assumption that equals() and hashCode() are implemented correctly. Otherwise you would have to write your own collections classes to accommodate different implementations.

    It feels like we might want to have a totallyEquals() or reallyEquals() that checks values of all state variables in cases we care . . . But wait, .retainAll(), removeAll(), replaceAll() methods would just blindly follow equals() . . .

    The collections framework classes have no way of “knowing” whether its elements have different methods for testing equality. They can only be sure about equals() and hashCode(). Unless you want to write your own specifications and implementations.

    . . . Winston's "Road to Equality" . . . .

    Guilty until proven innocent, or innocent until proven guilty? Winston follows one approach: equal until proven different, but the standard Java® approach assumes different until proven equal. Yes, you can write your own language under different assumptions, but that is the way Java® does it.
     
    Jesse Silverman
    Ranch Foreman
    Posts: 175
    8
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    So there is one single unified notion of two objects of a given class being content equal.
    It is precisely the @Override for .equals( Object o ) for that class, and it is used Universally across the Java Collections Framework in each and every place the notion comes up.

    There might be some place in your code that you want to use a different notion of equality, you could even pass them in to your own code using a BiPredicate for instance -- but all of the examples I mentioned are hard-coded to use the baked-into-the-class notion of .equals(), like sort() or creation of a TreeSet or TreeMap if Java didn't allow the option of passing a custom Comparator.

    You can have one single definition of a natural sort order (or not, if you don't implement Comparable), and pass any number of custom Comparator objects into places doing sort-based stuff, but there is no such extension of this notion to do hash-based things with different notions of equality than the @Override your class does for .equals( Object )

    This is all super-basic, I just hadn't really grokked the implications of it before.
     
    Campbell Ritchie
    Marshal
    Posts: 71752
    312
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Correct

    Remember the Collections classes were mostly written before BiPredicate.
     
    Jesse Silverman
    Ranch Foreman
    Posts: 175
    8
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    I am well aware that the Collections Framework pre-dated not only all the nifty Java 8, 9 and 10 and 11 and 12 and 13 and 14 and 15 additions, but painfully aware of the fact that it pre-dated Java 5 and its generics.  I definitely see lots of questions about what happens when you mix type-safe and pre-generic code even on the most recent material.

    I start doing a double take seeing all the List-specific stuff in Collections utility class, and then remember, oh yeah, people couldn't wait for years and years for Java 8 and its default methods in interfaces, so we have them there rather than in List interface where it feels like they would naturally belong.

    Anyway, at the moment it at least seems reasonable that some (all?) of the Collections-based stuff could potentially be retro-fitted with overloads that would let you say "what exactly do we mean by equals for this operation or data structure being assembled??"

    So far they have not, so if you are using any of the classic Collection/Collections utility methods, or those now in List, etc. you need to realize that the decision you make for equals() thinking about HashSet and keys in HashMap will affect every one of those.

    I still think my idea that "You can only safely put immutable objects into hash-based data structures" was wrong, at least trivially.

    Oderskey et al, point 3 "Don't include mutable state in your .hashCode() and .equals() overrides", if followed, means you are free to happily mutate anything left out at will it seems.

    However, if it is included in the implementation you do for Comparable, if any, then you would still break TreeSet, TreeMap keys and friends.
    If not, the rule becomes "if you change any mutable state in an object stored somewhere in any of the JCF structures, you will break hash-based ones iff your change would affect hashCode()/equals(), and you will break any ones based on custom Comparator objects, iff it would change the value of compare() for any two objects stored somewhere using a Comparator considering that field."

    Maybe that is just way too complicated to remember or enforce, and the safest thing is to only store totally immutable objects everywhere.
    That is the approach I'd already been thinking of as required before reading that paper.

    When I referred to "changing an immutable object" which sounds like a contradiction in terms, I just meant like calling ++ on an Integer that is also stored in a Set of all such Integer objects or something more complex but equivalent to that.  One would need to remove the original element and replace it in the structure with the new value in such a case.

    I think that my HashSet< HashSet<String> > case would have been valid the way I coded it (after I blew the interview) but I will post that separately to avoid hijacking this thread.

    My decision to append to this thread so far was based on the idea that anyone who was hazy about the .hashCode() .equals() Contract probably hadn't thought out the full global implications of their .equals() decisions either.
     
    Campbell Ritchie
    Marshal
    Posts: 71752
    312
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    Jesse Silverman wrote:. . . the Collections Framework pre-dated . . . Java 5 and its generics. . . .

    I might be mistaken, but I suspect they ran out of time or money when Java1.0.2 was issued nearly twenty‑four years ago. Generics was well known back then. That must have produced lots of complaints about type‑unsafety of collections, and they started considering generics pretty quickly after the Collections Framework. Unfortunately, they couldn't work out how to combine generics with reification and backward compatibility, so they had to use erasure. The existence of legacy code means it isn't possible to ban non‑generic use of objects. There were promises that raw types in a generic context would be banned in Java6, but I have never seen that prediction fulfilled

    . . . people couldn't wait for . . . Java 8 and its default methods . . .

    People didn't know there were default methods to wait for. OO programming was all the rage in the 1990s, and nobody thought that a simpler way of threading (=parallel Streams) or functional features (e.g. λs) would become popular. Not until fifteen years later, when Java® looked old‑fashioned and needed dragging kicking and screaming in to the 21st century. Oracle devoted a great deal of effort to that, and it took much longer than they originally anticipated.

    . . . it at least seems reasonable that some (all?) of the Collections-based stuff could potentially be retro-fitted with overloads that would let you say "what exactly do we mean by equals for this operation or data structure being assembled??"

    Yes. That is quite possible. I don't think it is going to happen, though. It would mean adding methods, overloaded or with new method names, to the current interfaces, and that would entail more default methods. If you want that sort of collection, you will have to write your own, or maybe there is a different language that supports that concept.

    . . . the decision you make for equals() thinking . . .

    You mean the decision Gosling made for equals() thinking, surely.

    I still think my idea that "You can only safely put immutable objects into hash-based data structures" was wrong, at least trivially.

    I always thought that point was correct.

    Oderskey et al, point 3 "Don't include mutable state in your .hashCode() and .equals() overrides", if followed, means you are free to happily mutate anything left out at will it seems.

    Whenever you write a class, you decide which fields you are going to include in and exclude from equals() and hashCode(). Look at my Car example where I excluded the speed and direction fields. What if you have have a blue Car and a second Car object returning true from equals() compared to that Car? What if you respray one of them in that nice metallic paint where two colours shade into each other? Now the two will no longer be equal. Or if you change the registration to JS 1. Since OO (=object‑oriented) programming has no way to make fields in multiple instances distinct from one another, you are now going to get false from equals(). If you go with the traditional interpretation of what OO means, you will have mutable fields and there is no guarantee that two objects will remain equal to each other (or vice versa) ever. If you are going to avoid mutable fields in equals() and hashCode(), then why override those methods on mutable types in the first place? If Odersky Spoon and Venners had written in 2019 rather than 2009, they might have introduced a more recent concept in OO, and told their readers to make every type immutable, in which case any change entails creating a new object.
    Back to what you said earlier. It is hazardous to use mutable types as “K”s in a map, or in other hash collections, or in trees sorted by some mutable feature, because any change will make it just about impossible to find the element/pair again.
    What's more, some hash collections may record the hash code from the time of insertion. They check equality of hash codes before using equals() because it is very fast to test h0 == h1. If there is any difference in the two numbers, the collection can assume that equals() will return false and ignore that Entry. In which case, any change to the hash code will absolutely guarantee you won't find what you are looking for.

    . . . "if you change any mutable state . . . only store totally immutable objects everywhere. . . .

    I think I have just spent twenty minutes explaining why I am going to agree with you

    When I referred to "changing an immutable object" . . . calling ++ on an Integer that is also stored in a Set . . .

    I tried it. Wouldn't compile.

    I think that my HashSet< HashSet<String> > case would have been valid . . .

    “Program to the interface.” the concept may be right, but the name entails an implementation. Better to write Set. I can't think of any Sets that don't use some sort of comparison between elements because they need to verify that their elements are distinct.

    . . . hadn't thought out the full global implications . . .

    But you are thinking out those implications
     
    Ranch Foreman
    Posts: 2348
    12
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    People didn't know there were default methods to wait for.



    What does this mean?
     
    Jesse Silverman
    Ranch Foreman
    Posts: 175
    8
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Hi Campbell:

    Thanks for your thoughtful replies.
    I am coming from a world similar to the one that Gosling and company were trying to emerge from 25 years ago.
    Namely, that where the libraries only provided the most minimal, low-level support and we were writing our own custom code for near everything.

    Despite its limitations, and when you mentioned "or use a different language", well, both Goldman Sachs and Google wrote famous "better replacements" for JCF in pure Java, and some people use those and nearly ignore the Collections Framework, if I read various blogs and websites correctly -- despite those limitations, the Framework offers you lots of great stuff people like me were used to having to write (and maintain!) from scratch.

    Generally, things seem to work pretty much as you would like or expect, this question of "But what do we mean by equals?" being a notable deficiency in my understanding of how to leverage it to store aggregates of our custom classes we create.

    It is more conceptual or philosophical than "learn all the popular JCF interfaces, classes and methods and then you are a JCF Master ready to use it to solve all the problems of Computer Science, or at least all interview questions [I am currently seeking employment again]."

    It seems like the best thing I can do for now wanting to move forward, now that I realize this is something that sounds trivial but requires much thought, is probably to heed the advice of only storing immutable objects in not just HashSet, but possibly any of the Collection objects, or to suffer the consequences.

    I can still see at least individual situations that when dealing with mutable objects I would want to think of part as a "key to identity" with possibly a monotonically increasing version id that would change whenever the mutable part of the object changed.  Like an AccountId or StudentId and a current balance or current GPA.

    I recognize that this "new problem" comes from something good, a relatively powerful and easy to use Collections Framework that obviates writing and maintaining much of the code I used to take for granted.  It is less clear how to approach this open-ended issue from the point of view of getting a job and or getting certified, but that is life.

    One more thing I remembered besides the .hashCode() .equals() contract is that you can get into a world of "WTF?" if objects of your class are Comparable and the notion of equality differs between your .equals() and when you get a 0 from .compareTo() which is why I feel this often goes beyond just use of custom objects in hashCode()-based data structures, right?

    Something still feels a bit unresolved even if one is dedicated to "only placing immutable objects in data structures".
    If one of them that would have changed if it were mutable, but instead got replaced with a new one because it was not, is in any data structure anywhere, it just seems like "Yeah, we need to remove the old one and insert the new one because our change-which-isn't-a-change invalidated the data structure(s) we had the old one in."

    My example before was too darn hasty, I meant to show something like this:



    Maybe Integer isn't the best example, tho immutable, because it is essentially a wrapped, cache primitive, rather than an instance of a custom class most people would have made mutable in 1996 but have made immutable now.

    Actually, I am not sure what wouldn't compile as I also got this:
    jshell> ++I;
    $7 ==> 16

    jshell> System.out.println("Does I == J? " + I + ", " + J);
    Does I == J? 16, 10

    I fully understand the distinction between a reference to an immutable object on the heap being changed to a (possibly)new, different object on the heap, rather than changing the existing object on the heap--which clearly confuses many beginning Java programmers.

    I also understand how to make my custom class immutable, which is valuable and useful.

    There seem to be design and coding implications of working with immutable objects that hadn't required attention during many years of working on production enterprise code that I am still coming to grips with.
     
    Jesse Silverman
    Ranch Foreman
    Posts: 175
    8
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    Monica Shiralkar wrote:

    People didn't know there were default methods to wait for.



    What does this mean?



    Hi Monica:

    I meant that because prior to Java 8 giving Sun/Oracle (and everyone else!) the ability to place default methods and static methods in interfaces, you could only place them in classes.
    If you needed a method that seemed logically to belong to an interface, you needed to either push that method down to some (possibly abstract) class that implemented it, or to make a helper/Utility class to contain it.

    It seems weird now to see all the List-specific methods all over the Collections utility class for example -- we could just place them as static or default methods in the List interface where people would expect them, right?

    But they were needed, and had to be put somewhere, for years before that was a possibility.  So they live in Collections which sounds like a utility class for things that are common to all Collection classes, but instead has a bunch of things in it that are only applicable to *some* Collection objects.
     
    Jesse Silverman
    Ranch Foreman
    Posts: 175
    8
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Well Campbell.

    You've caused me to think a lot more about this, which is probably a big part of why you volunteer here in the first place.
    (Causing visitors in general to think, I'm not solipsistic).

    I can make all sorts of excuses about how I spent so much time reading older OOAD/OOPS stuff from back when immutability was thought of as a concern for only multi-threaded parts of Applications if at all, but after all that reading and typing, and also thinking about Relational Database Design advice, which is also often not heeded by the masses...

    "Why did you need to put that mutable state of Account Balance into your aggregate of Account Objects, dude?
    Unless some other data truly seems to be forever static and appropriately immutable, don't put it in there.
    Like the balance, which changes all the time.  Just look that up when/as needed by having a BalanceByAcccountId hash that you maintain and consult."

    "Ummm, yeah, but then sorting the accounts by decreasing balance or flagging all the ones below a certain balance becomes trivially more work."

    There is some parallel between properly normalized and composed tables in the database, and properly composed sets of data members for objects...a lot of people have gotten paid a lot of money in the past for designs that ignored these things, and I worked on and maintained them, but I don't want to continue the tradition...

    I think I have obtained enough of a higher awareness to get back to the nuts-and-bolts learning of all the API's (Interfaces, Abstract and Concrete Classes and static and instance methods) of the Java Collections Framework.

    Two things I am thinking...
    1. When you have a part that seems to identify an object, and some other mutable data that is associated with it, maybe that is screaming in your ear to break the mutable part out into a key, value hash, with the identity part the key and the mutable part being a value and NOT to embed the mutable value right into your object, even if you've seen that done a million times in the past.

    2. If you do this, it becomes relatively easy to fall back to the common Household Safety Advice of only ever storing immutable values in long-lived data structures (except values in HashMaps with nice safe immutable keys).

    This kind of puts together advice coming from different people and places, including those horrified by all sorts of unsafely or illogically de-normalized tables in countless databases.

    Back to the Grind.
     
    Jesse Silverman
    Ranch Foreman
    Posts: 175
    8
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    I feel like there is some other place you can break things if your classes implementation of Comparable( Obj ) ever disagrees with your classes @Override of .equals( Obj , but I just reviewed one.

    Any TreeSet containing members of your class that has that disagreement may be broken in weird ways, any TreeMap using elements of your class as keys will also possibly behave very strangely.

    Somehow the .equals() .hashCode() contract was elevated to a named thing that was  (until recently) an absolute requirement for Certification.  About half of the books or other information sources seem to warn about this other potential disagreement for classes implementing Comparable, or placed into a TreeSet / keys of a TreeMap using a Comparator that disagrees with their natural .equals()

    Most importantly, the official Java API documentation reminds you of this, so despite some information sources boosting themselves as "One-Stop-Shopping for Java Learning" I guess it is still a case of RTFM:
    https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/util/Comparator.html

    Caution should be exercised when using a comparator capable of imposing an ordering inconsistent with equals to order a sorted set (or sorted map). Suppose a sorted set (or sorted map) with an explicit comparator c is used with elements (or keys) drawn from a set S. If the ordering imposed by c on S is inconsistent with equals, the sorted set (or sorted map) will behave "strangely." In particular the sorted set (or sorted map) will violate the general contract for set (or map), which is defined in terms of equals.

    For example, suppose one adds two elements a and b such that (a.equals(b) && c.compare(a, b) != 0) to an empty TreeSet with comparator c. The second add operation will return true (and the size of the tree set will increase) because a and b are not equivalent from the tree set's perspective, even though this is contrary to the specification of the Set.add method.



    I hope this isn't topic hijacking, it seemed reasonable to think people who forgot the why's of the .hashCode() .equals() contract would possibly forget to always ensure their Comparator agreed with the .equals() of their class, tho sadly there is no cool name for this potential hazard.
     
    Monica Shiralkar
    Ranch Foreman
    Posts: 2348
    12
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    Jesse Silverman wrote:

    Hi Monica:

    I meant that because prior to Java 8 giving Sun/Oracle (and everyone else!) the ability to place default methods and static methods in interfaces, you could only place them in classes.
    If you needed a method that seemed logically to belong to an interface, you needed to either push that method down to some (possibly abstract) class that implemented it, or to make a helper/Utility class to contain it.

    It seems weird now to see all the List-specific methods all over the Collections utility class for example -- we could just place them as static or default methods in the List interface where people would expect them, right?

    But they were needed, and had to be put somewhere, for years before that was a possibility.  So they live in Collections which sounds like a utility class for things that are common to all Collection classes, but instead has a bunch of things in it that are only applicable to *some* Collection objects.



    Yes, thanks. Also, there was a good discussion on Default methods in the thread.    https://coderanch.com/t/729699/java/difference-abstract-classes-Java-Interfaces
     
    Jesse Silverman
    Ranch Foreman
    Posts: 175
    8
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Towards Campbell's earlier remarks on the continued co-existence of Generic and Pre-Generic code...history and practical aspects.

    I can attest that even now, new developers are still consulting tutorial materials that seem to think that it is "Nifty, Cool and Awesome" that you can stick unrelated types into Collections from the Java Collections Framework, and show you how to do so.

    Egregious but Real Example: Writing a Custom Comparator that will allow unrelated types to get shoved in to a TreeSet, StringBuilder and String....

    While I think it shows a deep knowledge of how Comparator works that someone can do that at all, I don't even consider the ability to do it at all to be a legacy feature, more like a bug that Oracle can't/won't fix due to reverse compatibility.  It is really hard to drive a car while hanging outside the door reaching in too, but a terrible idea.

    I am not talking about storing <? extends Base> using generics, but completely unrelated types, like String and StringBuilder, without even warning about mutability!

    If I was presenting such material, I would be prefixing, suffixing and infixing it with "Don't ever do this in real life!"

    Jeanne and Scott occasionally show such kinds of things (not this example) in their book, mentioning they are only presenting it because you will see it on the exam.
    They make beautifully clear that you should "Please never do this in real code that someone needs to maintain or use" in every case.

    Now, I have seen mock exam stuff recently, including from them, that does test on the understanding of what happens when one mixes generic and pre-generic code.
    I'd been hoping I'd never see it again, but I do.

    It seems to be acknowledging either deficiencies in the type erasure system or just that some dinosaurs who have been coding in Java since forever still regard Generics as "the new way" and the pre-generic, non-type-safe, anything goes in, better-watch-out-when-you-get-something-out ways as "Classic".

    I will stop now because I am not sure that this all belongs in this thread.  It is related and I think a possibly a serious problem that needs to be addressed by the Community, but straying off-topic.

     
    Campbell Ritchie
    Marshal
    Posts: 71752
    312
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    Jesse Silverman wrote:. . . a bug that Oracle can't/won't fix due to reverse compatibility. . . .

    Not Oracle, but Sun. That was a long time before Oracle. Not won't but can't. They tried hard to fix it. I still think the real problem was not impementing generics from the word “go”.
     
    Jesse Silverman
    Ranch Foreman
    Posts: 175
    8
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    I feel like some of the more computer-sciency types are saying "You seem to love type-safety as much as I do, why aren't you just switching to Scala?"

    If I was starting from scratch I might even do it.

    My concern is that we are perpetuating the problem by not teaching Pre-Generic coding styles as obsolete legacy approaches not to be used in new code.

    I see some educators following exactly that approach, which is where my inclinations lie.

    You need to know how to @Override Object's .equals() and .hashCode() methods or your Comparable won't work with the Java Collections Framework.

    But that isn't true for Comparator<T>, right?
     
    Campbell Ritchie
    Marshal
    Posts: 71752
    312
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Was is this thread where you propounded an Equalitator? I have been thinking about how you would implement it. I can't imagine an Equalitator<E> working. It would be full of instanceof tests for every object whose equality you would want to text, so I abandoned that idea PDQ.
    Maye you would want it to be a nested class. Maybe a private inner class if you want easily to access fields. You could easily implement a different version of equals() and the outer class would implement an interface with a equalitator() method. You would want a mixin interface called Equable or Equator, to go with some of the other daft interface names which have appeared in the last few years. I can imagine it might be awkward to implement collections. You would have to ensure that a collection does or doesn't use the Equalitator throughout, rather like a TreeSet being given a Comparator in its constructor. I think I have convinced myself it is possible, but will take a lot to convince me it is at all desirable.
     
    Jesse Silverman
    Ranch Foreman
    Posts: 175
    8
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Equalitator<E> was too generic for what I thought I saw others proposing.

    You are correct (if I read them right) that they meant an Equalitator<Employee> or an Equalitator<Dog>, similar to a (Custom) Comparator<T>.

    Maybe a little beyond that, they were probably thinking of an Equalitator<? extends Employee> perhaps?

    It is more important that I truly understand proper implementation and use of Comparator<T> in its most modern and best-practice forms.

    My take-home from that whole experience is that your choice of @Override for .equals(Object o) when defining a new class affects a whole lot, and had best be made pretty carefully.

    Not just to be in synch with .hashCode() and any custom Comparator<YourClass>, (of course!) but being key to the operation of all the .equals() dependent methods thru-out the Framework for your class.
     
    Monica Shiralkar
    Ranch Foreman
    Posts: 2348
    12
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    The discussion in this thread has gone to another level now and things are going from over my head.
     
    Campbell Ritchie
    Marshal
    Posts: 71752
    312
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    Jesse Silverman wrote:. . . an Equalitator<Employee> or an Equalitator<Dog>

    That is simply an Equalizer<E> with a very long formal type parameter. It “knows” nothing about the Employee class.

    . . . an Equalitator<? extends Employee> perhaps? . . .

    Yes.

    your choice of @Override for .equals(Object o) when defining a new class affects a whole lot, and had best be made pretty carefully. . . .

    Don't understand. All @Override does is confirm that you are overriding the method. It doesn't affect the logic or implementation or anything. There is no need to be very careful using @Override; if you put it in the wrong place you will suffer a compiler error and no harm will ensue.
     
    Jesse Silverman
    Ranch Foreman
    Posts: 175
    8
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    I was using metonymy or something there, by "your choice of @Override" I meant just precisely what code you overrride Object.equals( Object o) with.
    I am only highlighting the annotation itself a lot because it is my favorite one.
    It seems like it would catch many sloppy errors I made in the past and a lot of people seem to not use it because it is expensive or fattening or they tax it in some states.
    I really don't know why the people who avoid it avoid it, those are just guesses.

    I now feel like I've carjacked Monica's nice thread.  My point was made that there is more that people should learn in the first six months of Java besides .hashCode()/.equals() contract, but fairly closely related to the relationship of one's custom class to the JCF containers. I have indicated some factors I believe are ongoing contributors to this regrettable situation.

    I learned a lot, including practices to AVOID, and I hope Monica got something further out of it before we got lost in the weeds.

    Thanks, everyone, I will think of you every time I @Override .equals() and .hashCode() in each of my classes.
    jesse
     
    Campbell Ritchie
    Marshal
    Posts: 71752
    312
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    Jesse Silverman wrote:. . . I now feel like I've carjacked Monica's nice thread. . . .

    Don't worry; I can split this thread whenever I have the time and energy to decide which posts to split. I agree; your part of the discussion merits a thread of its own.
     
    Monica Shiralkar
    Ranch Foreman
    Posts: 2348
    12
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    Jesse Silverman wrote:

    I now feel like I've carjacked Monica's nice thread.  



    No, you are welcome and your discussion was good. Went over my head though.
     
    Marshal
    Posts: 26308
    80
    Eclipse IDE Firefox Browser MySQL Database
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    Jesse Silverman wrote:a lot of people seem to not use it because it is expensive or fattening or they tax it in some states.
    I really don't know why the people who avoid it avoid it, those are just guesses.



    You missed "laziness" in your list of reasons. That's my usual reason for not using it. If Eclipse is generating the method for me then it will put in @Override where necessary, and sometimes I will go through and copy-paste it into places where it belongs, but mostly, meh.

    And even though laziness is supposed to be a positive attribute of programmers, there's a lot of places where it's not all that helpful.
     
    Campbell Ritchie
    Marshal
    Posts: 71752
    312
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Eclipse adds @Override when it detects overriding. A bit like holes in bombers. During World War II there was a proposal to add armour to bombers in the areas with the most bullet‑holes. Then somebody realised there was survivor bias, and the places where there weren't bullet‑holes needed the armour. It is the methods where Eclipse doesn't add that annotation that really need it.
     
    Paul Clapham
    Marshal
    Posts: 26308
    80
    Eclipse IDE Firefox Browser MySQL Database
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    Campbell Ritchie wrote:Eclipse adds @Override when it detects overriding.



    If Eclipse is generating the method, then yes. But if I open up a class and decide to add a toString() method, it won't put the @Override annotation in for me.
     
    Campbell Ritchie
    Marshal
    Posts: 71752
    312
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    No, but doesn't it give you a prompt to add @Override?
     
    Paul Clapham
    Marshal
    Posts: 26308
    80
    Eclipse IDE Firefox Browser MySQL Database
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Nope.

    Edit: Although that might be one of the many options in Eclipse which I haven't searched out.
     
    Campbell Ritchie
    Marshal
    Posts: 71752
    312
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    For having a question mentioned in the December 2020 CodeRanch Journal, congratulations: this question earns you a cow
     
    Jesse Silverman
    Ranch Foreman
    Posts: 175
    8
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    I deeply appreciate the cow, more than one would expect from a weird-type long-term vegan such as myself!

    My brother's wife's family are Malagasy, from Madagascar.

    The biggest insult in their language is "Your family does not have any cows", they are extreme fighting words, violence normally ensues.

    That was always funny to me as an already vegetarian growing up, Americans were perceived by the Malagasy as very wealthy.
    They would never think of trying that insult on one of us, which would have actually been true even of high-status Vegetarians who invest their wealth in other ways, maybe electric cars or solar panels.

    Now my household has five cows, in addition to any from the weird off-brand Farm game my wife likes to play on her tablet.

    Anyway, I don't remember if it was my error or happened on the mitosis to a separate thread, but the hsahCode() sticks out whenever I see this as something from a foreign language, like the Code of Hammurabi.  I don't know if it will impact Search functionality, but is it too late to repair it to "hashCode() and equals()"?

    "hashCode() and equals()" has a nice ring to it, it reminds me a lot of the over-used "Buddy Cop" trope, but that is appropriate seeing how closely they work as a team in all of our Hash-based data structures.  Serving elements efficiently and protecting the integrity of our Sets and Maps in terms of Uniqueness.
     
    Campbell Ritchie
    Marshal
    Posts: 71752
    312
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    We'll keep your spellling error for evver so as to cow you into submission

    Actually, there are enough instances of hash code and hashCode() spelt correctly for the search program to find easily.
     
    Jesse Silverman
    Ranch Foreman
    Posts: 175
    8
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Non-programmers never understand why I am so deeply averse to typos.  In the most extreme cases I have spent a week of ten hour days in someone else's code chasing a bug only to eventually submit a one-character fix at the end.  The manager didn't understand why it took so long to fix one character, when twenty million of them turned out to be just fine...

    Fair Enough.  There was a wonderful, but very old post by Kathy Sierra Herself that I didn't find until deep into this adventure where the Original Poster decided to call it hasCode in the thread name, which is what made me think of it.
    Probably a coincidence or maybe older posts are weighted less heavily?  It summed up much of this many moons ago.
     
    Campbell Ritchie
    Marshal
    Posts: 71752
    312
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Oh, all right then. [Somebody else has corrected the spelling.]
     
    Jesse Silverman
    Ranch Foreman
    Posts: 175
    8
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    Campbell Ritchie wrote:

    Jesse Silverman wrote:. . . an Equalitator<Employee> or an Equalitator<Dog>

    That is simply an Equalizer<E> with a very long formal type parameter. It “knows” nothing about the Employee class.

    . . . an Equalitator<? extends Employee> perhaps? . . .

    Yes..



    I am reviewing the generics section at the end of Chapter 3 of Jeanne & Scott's Sybex 816 book and think I have better words to say what I thought they had been proposing:

    The interface would be generic, something like:


    They might have been thinking of something more elegant, and might have also had full notions of how they wanted to handle the possibility of sub-types that add extra data, which part made my head whirl, but I was thinking of the implementing class to then look like:


    I might still be confused even now, but as I am reviewing generic interfaces and thinking about implementations of Comparable<T> I thought I'd make another stab at what I muffed describing earlier.

    Of course, one would need to ensure one used the same definition of two things being equal thru-out all usages of the data structure from creation on down, much like binarySearch() needs to use the same Comparator to work, but all the methods of say, NavigableSet interface know they are working with the Comparator the TreeSet was born with.

    This doesn't mean it would be worth it -- I had just swum back to shore and collapsed after reading dozens of posts from multi-lingual developers who had spent twenty times as long as I had on the meaning of Object equality in theory and practice.
     
    Campbell Ritchie
    Marshal
    Posts: 71752
    312
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    The way you described that interface, it isn't a mixin. Create straight implementing classes/λs, just as you would for Comparator<T>.
     
    Jesse Silverman
    Ranch Foreman
    Posts: 175
    8
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    I  was reviewing my C# notes because I may be taking a job with a lot of C# and just noticed the existence in them of:
    IEquatable<T> interface defining Equals(T t)

    This (very old) reference on Stack Overflow suggests that when Project Valhalla finally gets here (checks sky for pigs) we might make good use of it depending on exactly what allowing value types looks like (I can't even remember if it is just for primitives or allows custom value types like C#):
    https://stackoverflow.com/questions/2476793/when-to-use-iequatablet-and-why#:~:text=The%20IEquatable%20%28T%29%20interface%20is%20used%20by%20generic,such%20methods%20as%20Contains%2C%20IndexOf%2C%20LastIndexOf%2C%20and%20Remove.

    Just posting this here because I will forget about it otherwise.
    Or forget about this thread, if I am doing enough C# and Python.
     
    Saloon Keeper
    Posts: 12628
    273
    • Likes 1
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    It's just another testament to the notion that any modern programming language should be designed with generics from the ground up.

    You simply can not bolt generics on in a later version without severely limiting this feature or without resulting in an API that's full of warts.

    It's questionable whether there should even be an Object class to derive from. The only properties that all objects share is that they have a type and an identity. You could get these properties using accessors on an Object type that all types inherit from, or you could get these properties using keywords or operators that are defined on all types, such as the typeof operator in C#.

    At any rate, collection frameworks must avoid Object, and must try to constrain their type parameters as much as possible:
     
    Jesse Silverman
    Ranch Foreman
    Posts: 175
    8
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    I see traits and properties -- that means....Scala?
     
    Stephan van Hulst
    Saloon Keeper
    Posts: 12628
    273
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    No, just a custom language that I banged out yesterday to write that post.
     
    He's dead Jim. Grab his tricorder. I'll get his wallet and this tiny ad:
    Building a Better World in your Backyard by Paul Wheaton and Shawn Klassen-Koop
    https://coderanch.com/wiki/718759/books/Building-World-Backyard-Paul-Wheaton
    reply
      Bookmark Topic Watch Topic
    • New Topic