I just found the post Mr. Gosling - why did you make URL equals suck?!? on programming.reddit.com and just had to share

Okay, I’m totally hacked! java.net.URL class officially sucks! The equals method on this shining example of the JDK API mess actually does a blocking DNS lookup on the host string to resolve to an IP address and then compares the IP addresses rather than the host string. What freakin’ sense does that make?

Simple example:

URL url1 = new URL("http://foo.example.com");
URL url2 = new URL("http://example.com");

Let’s say these map to these IP addresses:

http://foo.example.com => 245.10.10.1
http://example.com => 245.10.10.1

Here’s the scary part:

url1.equals(url2) => true!

That's definitely the best example of code that deserves to be on The Daily WTF I've seen from a standard library. Just thinking about all the code I have in RSS Bandit that tests URLs for equality, it boggles my mind to think a standard library could have such a craptacular implementation of the equals() method.

Anyone have similar examples from other standard libraries (C++, .NET, JDK, Ruby, etc)? I need some bad code to cheer me up after a day that's already had too many meetings. :)


 

Wednesday, January 31, 2007 10:21:27 PM (GMT Standard Time, UTC+00:00)
Funny you should ask.

.NET 1.1 had a bug in System.Uri that was nearly as maddening -- it broke the Equals / GetHashCode contract. This led to one of the nastiest bugs I ever had to track down.

As an example, these two Uri instances are considered Equal() but return different hash code values:

new Uri("http://microsoft.com/home?query=dot%20net");
new Uri("http://microsoft.com/home?query=dot+net");

This was fixed in 2.0, but the scar is still there :)
Wednesday, January 31, 2007 11:25:00 PM (GMT Standard Time, UTC+00:00)
@Derek,

> new Uri("http://microsoft.com/home?query=dot%20net");
> new Uri("http://microsoft.com/home?query=dot+net");

Not a nit-picking point, but shouldn't %20 (the space character) be %2B (the plus character)?

The reason I mention this is that (assuming that the above was merely a percent encoding oversight), while I can recognize both the obvious bug and the frustration caused by it, the above is an example of an actual bug where the URI wasn't properly unescaped before making the comparison. It seems obvious that the example in Dare's post was specifically designed as a feature (looking up an IP and using this as the comparison is not something that happens unintentionally.)

None-the-less, I recognize your point. Just not sure that it qualifies as a "WTF" which, to me anyway, represents something that someone has done intentionally that doesn't seem to make any logical sense, warranting an official WTF label, questioning the thinking behind the result.
Thursday, February 1, 2007 3:26:14 AM (GMT Standard Time, UTC+00:00)
While its maddening to see URL class's behavior, its not technically *wrong*. Or its actually behaving exactly as advertised. http://java.sun.com/j2se/1.5.0/docs/api/java/net/URL.html#equals(java.lang.Object)

Of course, that does not make it better...
Thursday, February 1, 2007 3:44:47 AM (GMT Standard Time, UTC+00:00)
I noticed this code in Socket.BeginRead() recently:

catch (Exception exception)
{
if (((exception is ThreadAbortException) || (exception is StackOverflowException)) || (exception is OutOfMemoryException))
{
throw;
}

...
}
SteveG
Thursday, February 1, 2007 1:10:08 PM (GMT Standard Time, UTC+00:00)
string str = "i love strings";
str.Replace("love", "hate"); // returns a new string instead of replacing!

wtf do I care if its immutable.
Ranji
Thursday, February 1, 2007 5:52:28 PM (GMT Standard Time, UTC+00:00)
Java's BigDecimal has a terrible equals implementation as well:

BigDecimal bd1 = new BigDecimal("1.1");
BigDecimal bd2 = new BigDecimal("1.10");
System.out.println(bd1.equals(bd2)); // returns false, because of precision differences; 1.1 != 1.10 ?
System.out.println(bd1.compareTo(bd2)); // returns 0, indicating they are equal after all!

Thursday, February 1, 2007 6:26:16 PM (GMT Standard Time, UTC+00:00)
@Ranji

It's actually pretty important - the things you could do if strings weren't immutable are pretty crazy, e.g.

string x = String.Empty;
x.Insert(0, "*");

Ok, I used Insert instead of Replace, but the principal holds true - no-one would want to see every subsequent instance of String.Empty to return "*".
Mike Goatly
Friday, February 2, 2007 10:40:58 AM (GMT Standard Time, UTC+00:00)
Luke: I don't think that's a bug with the BigDecimal. The whole point of the BigDecimal class is to provide means to calculate with really big values where precision is an important feature of the number. So in the context of that class, equality is different from pure numerical equality.
Saturday, February 3, 2007 12:13:12 AM (GMT Standard Time, UTC+00:00)
Martin: Sun's own documentation says that it is strongly recommended that the implementation of compareTo and equals should provide the same results for two objects being compared. They do note that it is not required, but then they go on to say all the issues that come up if you don't follow that rule. And they do point out that BigDecimal is notably an issue here. It's almost as if it to say, "Yea, we probably should have rethought that, but we'll act like we really had a good idea for doing that. If others want to follow our footsteps, be our guest."
James Arendt
Saturday, February 3, 2007 4:34:56 PM (GMT Standard Time, UTC+00:00)
When you really dig into it, you'll find that almost all the standard libraries are quite awful. Over in Xbox land, we've been able to make most of the standard CRT math functions 10-1000 times faster (still using standard C) just by implementing them better -- paying more attention to branches and memory usage and so on. A favorite example of mine is how rand()/Math.Random() is both slower and less random than many other random number generators out there.

And then there are all the things a language will invisibly do for you, like static constructors and array bounds checks. I remember a few years ago reading about how some people were porting Java to run on mobile processors, and they found that the first 100K-500K instructons were spent initializing static data such as character tables for internationalization, none of which they actually needed (and even on a standard desktop install most of that data goes unused).

You're like "Wow, that's stupid" until you realize that it's just that no one else had looked at it (or looked at it that way) before. Clearly if the engineers who made it had realized what was going on, they would have fixed it. Whoever implemented java.net.URL.equals() probably had some goofy reason why it works this way (probably to support some specific product), without realizing the broader implications.

The lesson I take from this is that writing "standard" libraries appropriate for every use is very hard, maybe even impossible. There are lots of conflicting use-cases. Even if you decide to focus just on making the library lean and mean, then you still get conflicts optimizing for code size vs. workspace vs. runtime vs. development time (including maintainability of the codebase). Also, an implementation that might run fine on one kind of hardware might fail miserably on other hardware or under emulation. It all falls out of the principle that small changes in assumptions and requirements can require disproprtionately large changes to code implementation and design.
Comments are closed.