This is the intuitive explanation.
A List can be compared to a Set using ==, because there could be classes whose instances are both lists and sets.
A String cannot be a compared to a StringBuilder using == because an object that is both a String and a StringBuilder is impossible.
However if you wanted to be able to compare String and StringBuilder using == you could do this:
String str = "test";
CharSequence chars = new StringBuilder(); 
if (str == chars) {
    // ...
}
Obviously, the above test will evaluate to false.  But it will compile.
This is legal because the type system says that chars could be a String.  Note that CharSequence is a supertype of String and StringBuffer.  (In this case, the supertype is an interface, but it doesn't need to be an interface.)
(Notes for other people encountering this question and wondering what the code is "trying to do" ... and the right way to do it.
- It makes no sense to compare a - HashSetand an- ArrayListusing- ==.  The- ==operator tests object identity, and no- HashSetand- ArrayListcan ever be the same object.
 
- It makes no sense to use - equals(Object)either.  The javadocs state that a list can only be "equal" to another list.  Besides, comparing an explicitly ordered collection with an collection where the ordering is unstable is ... nonsensical.
 
- The same applies to - Stringversus- StringBuffer.  They can never be- ==and they can never be equal according to- equals(Object).  If you wanted to compare a string and a string buffer character-wise, the simple answer would be- string.equals(stringBuffer.toString()).
 
In short.  The code makes almost no sense ... except as an illustration of the OP's real question about how the typing works.)