Zagadka HashSet

Spis treści

HashSet jest klasą pochodzącą z Java Collections Framework, do którego należą także inne typy kolekcji, wśród nich inne zbiory, listy i mapy.

HashSet implementuje interfejs Set, posiada więc metody:

add(), clear(), clone(), contains(), isEmpty(), iterator(), remove(), size().

Ich działania można się domyślić na podstawie nazwy.

Pamiętajmy jedynie o tym, że zbiory przyjmują tylko różne obiekty (elementy o takim samym kluczu zajmowały by to samo miejsce w strukturze). Także przykładowo jeśli do zbioru liczb całkowitoliczbowych dodamy trzy razy liczbę 1, to będziemy pamiętali w naszej strukturze tylko jedną taką wartość.

Przechodząc do zagadki, w naszym przypadku utworzymy zbiór obiektów typu URL, które w skrócie pozwalają na komunikację z zasobami WWW.

Kurs Java

Pytanie jest takie jak zwykle, co zostanie wyświetlone po wykonaniu poniższego kodu?

import java.net.MalformedURLException;
 import java.net.URL;
 import java.util.HashSet;
 import java.util.Set;
 
 public class JavaStart {
 
 	public static void main(String[] args) throws MalformedURLException {
 		Set set = new HashSet();
 		set.add(new URL("http://wolnifarmerzy.com.pl"));
 		set.add(new URL("http://zagubionawyspa.com.pl"));
 		set.add(new URL("http://editor.javastart.pl"));
 		set.add(new URL("http://google.pl"));
 
 		System.out.println("Size: "+set.size());
 	}
 }

Odpowiedzi:

A) Size: 1

B) Size: 4

C) Size: 3

D) Inna odpowiedź

Rozwiązanie

Pytanie w tej części JavaTraps dotyczyło tego jaki rozmiar zbioru (HashSet) zostanie wyświetlony na ekranie, gdy dodamy do niego cztery obiekty typu URL. Przypomnijmy kod:

import java.net.MalformedURLException;
 import java.net.URL;
 import java.util.HashSet;
 import java.util.Set;
 
 public class JavaStart {
 
 	public static void main(String[] args) throws MalformedURLException {
 		Set set = new HashSet();
 		set.add(new URL("http://wolnifarmerzy.com.pl"));
 		set.add(new URL("http://zagubionawyspa.com.pl"));
 		set.add(new URL("http://editor.javastart.pl"));
 		set.add(new URL("http://google.pl"));
 
 		System.out.println("Size: "+set.size());
 	}
 }

Na pierwszy rzut oka odpowiedź wydaje się oczywista. Metoda add powoduje dodanie nowego obiektu do kolekcji (wewnętrznie reprezentowanej jako tablica haszowana), po wcześniejszym sprawdzeniu, czy identyczny obiekt już nie istnieje. Warunek ten zostaje sprawdzony przy pomocy metody equals().

Poprawna odpowiedź to: D) inna odpowiedź

Niestety jest to spowodowane kiepskim rozwiązaniem w metodzie equals() klasy URL . Była ona projektowana, gdy internet był tak naprawdę jeszcze słabiej rozwinięty i nikt nie przypuszczał, że będzie taka sytuacja, w której na jednym serwerze o jednym IP będzie hostowanych kilka(naście) serwisów o takim samym adresie IP(wtedy jeszcze tylko v4). Metoda equals() traktuje dwa obiekty URL za równe, jeśli ich adres sieciowy się zgadza. Ponieważ strony wolnifarmerzy.com.pl oraz zagubionawyspa.com.pl posiadają taki sam adres IP serwera, to są przez metodą equals() traktowane jako równe obiekty, więc w wyniku otrzymamy Size: 3.

Nie jest to niestety jedyna prawidłowa odpowiedź. W przypadku, gdy nie będziemy połączeni z internetem, wynikiem będzie Size: 4 - nie zostanie wtedy porównany adres IP.

Niestety jest to spora niespójność, ponieważ wynik działania tej metody powinien być niezależny od platformy na jakiej zostanie wywołana.

Jak sobie z tym radzić?

Jeśli na prawdę nie masz rozsądnego wytłumaczenia do użycia klasy URL , zastąp ją nowszą klasą URI , w której podobne problemy nie występują. Udostępnia ona również metodę toURL(), więc tak naprawdę, nie ma rozsądnego wytłumaczenia do używania klasy URL w nowo pisanych programach.

Dyskusja i komentarze

Masz pytania do tego wpisu? Może chcesz się podzielić spostrzeżeniami? Zapraszamy dyskusji na naszej grupie na Facebooku.