Javas "Object" -Klasse ist eine sehr wichtige Klasse in der Oberklasse aller Klassen, aber ist es nicht selten, dass sie selbst "neu" ist? In diesem Artikel zeige ich Ihnen zwei Codebeispiele, bei denen "new Object ()" sinnvoll ist.
Das folgende Codebeispiel ist im Kommentar "Implementierung einer Kombination von Mathematik in Java - Qiita" beschrieben. Diese Methode listet "k" -Kombinationen auf und gibt sie aus den "Daten" des String-Arrays zurück.
static List<String[]> combination(String[] data, int k) {
List<String[]> result = new ArrayList<String[]>();
combination(data, 0, new String[k], 0, result);
return result;
}
static void combination(String[] data, int di, String[] comb, int ci, List<String[]> result) {
if (ci == comb.length) {
result.add(comb.clone());
return;
}
for ( ; di <= data.length - (comb.length - ci); di++) {
comb[ci] = data[di];
combination(data, di + 1, comb, ci + 1, result);
}
}
Es besteht aus zwei Methoden, die direkt von außen aufgerufen wird, ist die Kombination mit zwei Argumenten, und die Kombination mit fünf Argumenten wird intern verwendet. Die Kombination mit 5 Argumenten ruft sich selbst zurück, aber von den 5 Argumenten geben die 3 Argumente ("Daten", "Kamm", "Ergebnis") genau den gleichen Wert wie das Argument an.
Wenn Java verschachtelte Methoden definieren kann, sollten Sie bei einem Rückrufaufruf nicht dieselben Argumente schreiben müssen. Leider können Sie Methoden nicht in Java verschachteln.
Schreiben wir es also mit new Object ()
um.
static List<String[]> combination(String[] data, int k) {
int length = data.length;
List<String[]> result = new ArrayList<String[]>();
String[] comb = new String[k];
new Object() {
void combination(int di, int ci) {
if (ci == k) {
result.add(comb.clone());
return;
}
for (; di <= length - (k - ci); di++) {
comb[ci] = data[di];
combination(di + 1, ci + 1);
}
}
}.combination(0, 0);
return result;
}
Die 5-Argument-Kombination wird in das neue Object () {} geschrieben und die festen 3 Argumente (Daten, Kamm, Ergebnis) werden gelöscht.
Das new Object () {}
in diesem Code definiert eine anonyme innere Klasse und erstellt gleichzeitig eine Instanz dieser Klasse. Die .combination (0, 0) nach dem neuen Object () {} ruft die in der inneren Klasse definierte Kombination (int di, int ci) auf.
Die festen 3 Variablen ("Daten", "Kamm", "Ergebnis") können innerhalb der inneren Klasse referenziert werden, sodass sie nicht für jeden Aufruf als Argumente übergeben werden müssen.
Ich erstelle eine nutzlose Instanz mit "new Object ()", aber der Overhead ist gering, da ich nur eine erstelle. Der Stapelverbrauch wird reduziert, da der Rückrufaufruf weniger Argumente enthält.
Angenommen, Sie haben eine Klasse wie diese:
public class Person {
private final String name;
private final String age;
public Person(String name, String age) {
this.name = name;
this.age = age;
}
public String getName() {
return this.name;
}
public String getAge() {
return this.age;
}
}
Angenommen, Sie haben die folgende Liste von "Personen".
List<Person> list = List.of(
new Person("Yamada", "18"),
new Person("Ichikawa", "72"),
new Person("Sato", "39"),
new Person("Tanaka", "9"));
Sortieren Sie dies nach "Alter" in aufsteigender Reihenfolge. Da age
eine Zeichenfolge ist, muss sie so geschrieben werden, um sie als Ganzzahl zu sortieren.
List<Person> result = list.stream()
.sorted(Comparator.comparingInt(p -> Integer.parseInt(p.getAge())))
.collect(Collectors.toList());
Dies funktioniert gut, ist aber nicht sehr effizient, da es bei jedem Vergleich von Werten zweimal "Integer.parseInt (p.getAge ())" ausführt. Selbst mit dem schnellen Sortieralgorithmus wird Integer.parseInt
$ 2n \ log n $ mal ausgeführt.
Map.Entry
Das Koppeln einer Instanz von "Person" mit einer ganzzahligen Version von "Alter" löst dieses Problem. In solchen Fällen wird die Klasse "Map.Entry" häufig als temporärer Speicherort verwendet.
List<Person> result = list.stream()
.map(p -> Map.entry(Integer.parseInt(p.getAge()), p))
.sorted(Comparator.comparingInt(Entry::getKey))
.map(Entry::getValue)
.collect(Collectors.toList());
Die Ganzzahl age
muss nur einmal berechnet werden, es ist jedoch nicht intuitiv klar, wo Map.Entry
verwendet wird.
Records Java 14 fügt eine Funktion namens Records hinzu, mit der sich unveränderliche Klassen für die Datenspeicherung leicht definieren lassen. Wenn Sie dies verwenden, wird es so sein.
record PersonWithAge(Person p, int age) {
PersonWithAge(Person p) {
this(p, Integer.parseInt(p.getAge()));
}
}
List<Person> result = list.stream()
.map(PersonWithAge::new)
.sorted(Comparator.comparingInt(PersonWithAge::age))
.map(PersonWithAge::p)
.collect(Collectors.toList());
Es ist leichter zu verstehen, aber es fühlt sich auch etwas übertrieben an, weil es eine Klasse für die temporäre Speicherung definiert.
new Object() Dies passiert, wenn Sie "new Object ()" verwenden.
List<Person> result = list.stream()
.map(p -> new Object() {
int intAge = Integer.parseInt(p.getAge());
Person person = p;
})
.sorted(Comparator.comparingInt(obj -> obj.intAge))
.map(obj -> obj.person)
.collect(Collectors.toList());
new Object () {...}
ist ein temporärer Speicherort, der nur in diesem Ausdruck gültig ist. Obwohl es sich um eine unbenannte Klasse handelt, wird sie in diesem Ausdruck als vollwertige Klasse mit den Feldern "intAge" und "person" erkannt.
Wie war das? Sie können sehen, dass die Object
-Klasse auch verschiedene Verwendungszwecke hat. Ich kann es nicht empfehlen, weil es sich schwierig anfühlt, aber ich benutze es, seit ich wusste, dass ich es so schreiben kann.
Recommended Posts