2013-05-20 9 views
8

Ich versuche, einen Satz von Arrays von Ints zu schaffen, das, was ist, wenn ich versuche zu tun:Hinzufügen von Arrays mit gleichen Werten ergeben doppelte Elemente Hashset

HashSet<int[]> s = new HashSet<int[]>(); 
int a1[] = {1,2,3}; 
int a2[] = {1,2,3}; 
s.add(a1); 
s.add(a2) 
System.out.println(s.size()); 

Dann s zwei Objekte hat, aber es sollte nur einen geben. Hinweis: Es spielt keine Rolle, ob HashSet < Integer []> ist. Es funktioniert einfach nicht.

Jetzt Wenn ich versuche, dies mit einer Arraylist < Integer zu tun>, so etwas wie:

HashSet<ArrayList<Integer>> s = new HashSet<ArrayList<Integer>>(); 
ArrayList<Integer> a1 = new ArrayList<Integer>(); 
ArrayList<Integer> a2 = new ArrayList<Integer>(); 
a1.add(1); 
a1.add(2); 
a1.add(3); 

a2.add(1); 
a2.add(2); 
a2.add(3); 

s.add(a1); 
s.add(a2) 
System.out.println(s.size()); 

Dann hat s ein Objekt.

I obwohl ein Weg, um den Fehler in dem ersten Code zu vermeiden, und wurde in einem Hashset Hashcodes jeden Arrays wie folgt zu speichern:

int a1[] = {0,10083,10084,1,0,1,10083,0,0,0,0}; 
int a2[] = {1 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,1 ,0,2112}; 
HashSet<Integer> s= new HashSet<Integer>();//hashcodes of each array 
s.add(Arrays.hashCode(a1)); 
s.add(Arrays.hashCode(a2)); 
System.out.println(Arrays.hashCode(a1)); 
System.out.println(Arrays.hashCode(a2)); 
System.out.println(s.size()); 

Es arbeitet für den ersten Fall (1,2,3), aber in Fällen, in denen Kollisionen nicht funktionieren, müsste ich Kollisionen verwalten. Also denke ich, dass ich ein HashSet selbst implementiere.

Mit HashSet < ArrayList < Integer >> es funktioniert perfekt. Ich nehme an, dass Java Kollisionen in diesem Fall verwaltet.

Meine Frage ist, warum Java erlaubt keine HashSet < int [] zu verwalten> oder HashSet < Integer []> wenn Hashcodes erzeugt sind die gleichen wie in Arraylist < Integer> und Hashcodes von Arrays können einfach durch den Aufruf berechnet werden Arrays.hashCode (...).

Und schließlich, wenn ich ein HashSet < int []> (oder HashSet < Integer []>) machen möchte, müsste ich es selbst implementieren? Oder gibt es einen besseren Weg?

Danke.

UPDATE: Ok, endlich denke ich, ich bin zu einer vollständigen Antwort gekommen. Wie @ZiyaoWei und @ user1676075 kommentiert, funktioniert es nicht, weil equals false zurückgibt und hashcodes differents sind. Aber, warum Java diese Methoden nicht überschreibt (mit Arrays.equals(), Arrays.hashCode()), also kann man etwas wie HashSet tun < int []>? Die Antwort ist, weil ein Array ein veränderbares Objekt ist und Hashcode nicht von veränderbaren Werten abhängig sein kann (jedes Element des Arrays ist ein veränderbarer Wert), gemäß dem allgemeinen Vertrag des Hashcodes. Mutable objects and hashCode

Hier schöne Erklärungen der Verwendung wandelbar Felder in hashCode http://blog.mgm-tp.com/2012/03/hashset-java-puzzler/ und wandelbar Schlüssel in Hashmaps Are mutable hashmap keys a dangerous practice?

Meine Antwort ist, wenn Sie ein HashSet < int [] verwenden möchten> Sie eine Klasse erstellen müssen, dass eine hat Wenn Sie möchten, dass Hashcode und equals von Werten abhängen, überschreiben Sie die Methoden equals() und hashCode() mit Arrays.equals() und Arrays.hashCode(). Wenn Sie nicht gegen den Vertrag verstoßen wollen, machen Sie das Array endgültig.

Danke allen!

+0

Siehe Ziyao Antwort unten. Überprüfen Sie kurz, ob a.hashCode() mit b.hashCode() übereinstimmt. Ich wette, sie sind nicht gleich. –

+0

möglich duplicate von [wie man eine Reihe von Array in Java machen?] (Http://stackoverflow.com/questions/9841934/how-to-make-a-set-of-array-in-java) – Raedwald

Antwort

8

Es hat nichts mit Kollision am Ende des Tages zu tun:

a1.equals(a2) == false 

Da sie nicht gleich sind, ein Set wird sie als verschiedene behandeln.

Hinweis Array in Java überschreibt nicht die equals Methode von Object.

Und da add in Set ist definiert als

Formal fügt das angegebene Element e an diesen Satz, wenn der Satz kein Element e2, so dass (e == null e2 enthält == null: e .equals (e2))

ist, scheint unmöglich zu sein, richtig ein Set zu implementieren, die Ihren Anforderungen gerecht werden könnte (vgl Elemente mit Arrays.equals) ohne einige Verträge zu verletzen.

+0

Ich fühle mich Das ist ein Fehler in Java. 'Array' sollte aus meiner Sicht' Equals' überschreiben. Ich kann in Effective Java keine Referenz finden, die erklärt, warum dies nicht der Fall ist. –

+0

@PhilipWhitehouse Einverstanden, und ich kann nichts finden, das das Thema entweder erklärt. –

+0

Hallo, danke. Ich denke, dass es mit Kollisionen zusammenhängt. Wenn Sie ein HashSet verwenden möchten, müssen Sie HashCode und equals Methoden überschreiben. Denn wenn es den gleichen Hashcode hat, dann muss es die equals-Methode verwenden, um zu sehen, ob sie gleich sind oder nicht (wie ich weiß, korrigiere mich, wenn ich falsch liege, bitte!). Ich denke, du hast Recht, weil java die equals-Methode für Arrays nicht überschreibt. :) – voodoo14

1

Der Grund für die Funktionsweise von HashSet ist, dass das HashSet den .equals() - Vergleich verwendet, um zu entscheiden, ob Sie das gleiche Objekt zweimal einfügen. Im Fall von Liste werden zwei Listen desselben Basistyps (z. B. ArrayList) mit dem gleichen Inhalt in der gleichen Reihenfolge als gleich verglichen. Sie sagen dem HashSet also, das selbe Objekt zweimal einzufügen. Es dauert nur einmal eine Instanz.

Wenn Sie versuchen, die gleiche Operation mit einem Array zu tun. Sehen Sie diesen Beitrag: equals vs Arrays.equals in Java für weitere Details über Array-Vergleiche in Java. Wenn Sie zwei Arrays einfügen, testet der Standardwert .equals(), ob es sich um dasselbe Objekt handelt, was nicht der Fall ist. So scheitert es.

Verwandte Themen