Gegeben den folgenden Code:
public static void main(String[] args) {
record Foo(int[] ints){}
var ints = new int[]{1, 2};
var foo = new Foo(ints);
System.out.println(foo); // Foo[ints=[I@6433a2]
System.out.println(new Foo(new int[]{1,2}).equals(new Foo(new int[]{1,2}))); // false
System.out.println(new Foo(ints).equals(new Foo(ints))); //true
System.out.println(foo.equals(foo)); // true
}
Es scheint offensichtlich, das Arrays toString
, equals
Methoden verwendet werden (anstelle von statischen Methoden Arrays::equals
, Arrays::deepEquals
oder Array::toString
).
Ich denke, Java 14-Datensätze ( JEP 359 ) funktionieren mit Arrays nicht besonders gut. Die jeweiligen Methoden müssen mit einer IDE generiert werden (die zumindest in IntelliJ standardmäßig "nützliche" Methoden generiert, dh sie verwenden die statischen Methoden in Arrays
).
Oder gibt es eine andere Lösung?
java
arrays
java-14
java-record
user140547
quelle
quelle
List
anstelle eines Arrays?toString()
,equals()
undhashCode()
Verfahren eines Datensatzes implementiert eine invokedynamic Referenz. . Wenn nur das kompilierte Klassenäquivalent näher an dem gewesen wäre, was die Methode heuteArrays.deepToString
in ihrer privaten überladenen Methode tut, hätte es möglicherweise für die primitiven Fälle gelöst.Object
, da dies auch bei benutzerdefinierten Klassen passieren kann. zB falsch gleichinvokedynamic
hat absolut nichts mit der Auswahl der Semantik zu tun. indy ist hier ein reines Implementierungsdetail. Der Compiler hätte Bytecode ausgeben können, um dasselbe zu tun. Dies war nur ein effizienterer und flexiblerer Weg dorthin. Während des Entwurfs von Datensätzen wurde ausführlich diskutiert, ob eine differenziertere Gleichheitssemantik (wie z. B. tiefe Gleichheit für Arrays) verwendet werden soll, aber dies stellte sich als weitaus problematischer heraus, als es angeblich gelöst wurde.Antworten:
Java-Arrays stellen Datensätze vor verschiedene Herausforderungen, die dem Design eine Reihe von Einschränkungen hinzufügen. Arrays sind veränderlich und ihre Gleichheitssemantik (von Object geerbt) beruht auf Identität und nicht auf Inhalten.
Das Grundproblem bei Ihrem Beispiel besteht darin, dass Sie wünschen, dass
equals()
auf Arrays Inhaltsgleichheit und nicht Referenzgleichheit bedeutet. Die (Standard-) Semantik fürequals()
Datensätze basiert auf der Gleichheit der Komponenten. in Ihrem Beispiel sind die beidenFoo
Datensätze verschiedene Arrays enthalten sind unterschiedlich, und der Datensatz korrekt verhält. Das Problem ist nur, dass Sie sich wünschen, der Gleichheitsvergleich wäre anders.Das heißt, Sie können einen Datensatz mit der gewünschten Semantik deklarieren, es erfordert nur mehr Arbeit, und Sie haben möglicherweise das Gefühl, dass dies zu viel Arbeit ist. Hier ist eine Aufzeichnung, die macht, was Sie wollen:
Dies ist eine defensive Kopie auf dem Weg nach innen (im Konstruktor) und auf dem Weg nach draußen (im Accessor) sowie die Anpassung der Gleichheitssemantik, um den Inhalt des Arrays zu verwenden. Dies unterstützt die in der Oberklasse geforderte Invariante,
java.lang.Record
dass "das Zerlegen eines Datensatzes in seine Komponenten und das Rekonstruieren der Komponenten in einen neuen Datensatz einen gleichen Datensatz ergibt".Man könnte sagen: "Aber das ist zu viel Arbeit. Ich wollte Datensätze verwenden, damit ich nicht all das Zeug eingeben muss." Datensätze sind jedoch nicht in erster Linie ein syntaktisches Werkzeug (obwohl sie syntaktisch angenehmer sind), sondern ein semantisches Werkzeug: Datensätze sind nominelle Tupel . Meistens liefert die kompakte Syntax auch die gewünschte Semantik. Wenn Sie jedoch eine andere Semantik wünschen, müssen Sie zusätzliche Arbeit leisten.
quelle
List< Integer >
ProblemumgehungUmgehung: Verwenden Sie eine
List
vonInteger
Objekten (List< Integer >
) statt Array von Primitiven (int[]
).In diesem Beispiel instanziiere ich eine nicht modifizierbare Liste nicht spezifizierter Klassen mithilfe der
List.of
zu Java 9 hinzugefügten Funktion. Sie können sie auchArrayList
für eine modifizierbare Liste verwenden, die von einem Array unterstützt wird.quelle
Problemumgehung: Erstellen Sie eine
IntArray
Klasse und schließen Sie dieint[]
.Nicht perfekt, weil Sie jetzt
foo.getInts()
statt anrufen müssenfoo.ints()
, aber alles andere funktioniert so, wie Sie es möchten.Ausgabe
quelle
record
Feld möglicherweise aus vielen Feldern besteht und nur die Array-Felder so umbrochen werden.List
die die Art von Wrapping bieten, die man mit der hier vorgeschlagenen Lösung suchen könnte. Oder denken Sie, dass dies für solche Anwendungsfälle ein Overhead sein könnte?int[]
, ist aList<Integer>
aus mindestens zwei Gründen nicht dasselbe: 1) Die Liste verwendet viel mehr Speicher und 2) es gibt keine integrierte Konvertierung zwischen ihnen.Integer[]
🡘List<Integer>
ist ziemlich einfach (toArray(...)
undArrays.asList(...)
), aberint[]
🡘 erfordertList<Integer>
mehr Arbeit, und die Konvertierung braucht Zeit, sodass Sie dies nicht immer tun möchten. Wenn Sie eine habenint[]
und diese in einem Datensatz speichern möchten (mit anderen Dingen, andernfalls warum sollten Sie einen Datensatz verwenden) und sie als benötigenint[]
, ist die Konvertierung jedes Mal, wenn Sie sie benötigen, falsch.Arrays.equals
,Arrays.toString
gegenüber derList
Implementierung, die beim Ersetzen derint[]
in der anderen Antwort vorgeschlagenen verwendet wird. (Vorausgesetzt, beide sind sowieso Problemumgehungen.)