Ich denke, dass zweidimensionale Arrays häufig verwendet werden, um Daten mit Zwei-Wege-Indizes zu verwalten.
Beispiel für ein zweidimensionales Array
String[][] names = {
{"Yamauchi", "Wald", "Izumi", "Yoshikawa", "Shibuya"},
{"Matsumoto", "Kono", "Mizuno", "Kawaguchi", "Ushio"},
{"Kato", "Nishimura", "Taketa", "Yoshida", "Ito"},
{"Kawano", "Kato", "Yamada", "Sasaki", "Fujita"},
{"Shibata", "Ozaki", "Omori", "Hügel", "Yaguchi"}
};
System.out.println(names[1][4]); //Ushio
Ich dachte, wenn Sie mit einem Zwei-Wege-Index verwalten, können Sie die Daten auch dann verwalten, wenn es sich um ein eindimensionales Array handelt, wenn Sie einen Zwei-Wege-Index erstellen. Deshalb habe ich beschlossen, die Idee eines zweidimensionalen Arrays mit einem eindimensionalen Array nachzuahmen. [^ Experiment].
Definieren Sie zunächst zwei Variablen, die die Breite und Höhe des zweidimensionalen Arrays angeben. Im Folgenden wird $ width $ als ** Array width ** und $ height $ als ** array height ** bezeichnet.
python
int width = 5;
int height = 5;
Bereiten Sie dann im Voraus eine eindimensionale Version des obigen zweidimensionalen Arrays vor [^ Personenklasse].
python
String[] names = {
"Yamauchi", "Wald", "Izumi", "Yoshikawa", "Shibuya",
"Matsumoto", "Kono", "Mizuno", "Kawaguchi", "Ushio",
"Kato", "Nishimura", "Taketa", "Yoshida", "Ito",
"Kawano", "Kato", "Yamada", "Sasaki", "Fujita",
"Shibata", "Ozaki", "Omori", "Hügel", "Yaguchi"
};
Iterator<String> nameTokens = Arrays.stream(names).iterator();
Person[] people = IntStream.range(0, width * height)
.mapToObj(i -> new Person(nameTokens.next()))
.toArray(Person[]::new);
Hier ist ein Diagramm der Anordnung. Dieses Array ist ein Array mit sowohl ** Arraybreite ** als auch ** Arrayhöhe **.
Wenn der horizontale Index jeder Zeile $ x $ und der vertikale Index jeder Spalte $ y $ ist, dann ist [^ x und y] beispielsweise der Index, der "Ushio" anzeigt, $ x = 4, \ y = 1
Scannen Sie die Elemente mit dem folgenden Code.
Code scannen
for (int i = 0, x = 0, y = 0; i < array.length; i++, x++) {
...
if (x == width - 1) {
x = -1;
y++;
}
}
Scannt alle Elemente des Zielarrays (von Index 0 bis array.length
). Dieses Mal definieren wir ein People-Array. Ersetzen Sie also "array.length" durch "people.length". Die in der for-Anweisung deklarierten Zählervariablen sind:
Variable | Bedeutung |
---|---|
i |
Index des Arrays |
x |
Ein Wert, der den horizontalen Index des Arrays angibt |
y |
Ein Wert, der den vertikalen Index des Arrays angibt |
if (x == width --1) {...}
erhöht y
, wenn es das letzte Element der Zeile nach der Verarbeitung des ...
Teils des Scan-Codes ist, und dann Bedeutet, sich zur Linie von zu bewegen. "x = -1" dient zur Einstellung.
Verwenden Sie dies beispielsweise im `` `Teil des Scan-Codes
python
if (y == 1 && x == 4) System.out.println(people[i]);
Wenn Sie definieren, können Sie "Ushio" ausgeben.
Extraktionsbeispiel
for (int i = 0, x = 0, y = 0; i < array.length; i++, x++) {
if (y == 1 && x == 4) System.out.println(people[i]); // Person [name=Ushio]
if (x == width - 1) {
x = -1;
y++;
}
}
Ebenfalls,
python
if (y == 1 && x == 4) array[i] = new Person("Hoge");
Wenn Sie definieren, können Sie "Ushio" durch "Hoge" ersetzen.
Ersatzbeispiel
for (int i = 0, x = 0, y = 0; i < array.length; i++, x++) {
if (y == 1 && x == 4) array[i] = new Person("Hoge");
if (x == width - 1) {
x = -1;
y++;
}
}
Ich dachte, es wäre mühsam, es jedes Mal manuell zu definieren, also habe ich es modularisiert [^ schmutzig].
python
class Matrix<T, R> {
private T[] array;
public final int width;
public final int height;
public int i;
public int x;
public int y;
private Consumer<Matrix<T, R>> action;
private Function<Matrix<T, R>, R> function;
private Matrix(T[] array, int width, int height) {
this.array = array;
this.width = width;
this.height = height;
}
static <T, R> Matrix<T, R> of(T[] array, int width, int height) {
if (ObjectUtils.isEmpty(array))
throw new IllegalArgumentException("Das als erstes Argument verwendete Array ist leer oder null.");
if (array.length != width * height)
throw new IllegalArgumentException("Die Länge des Arrays als erstes Argument und der Wert von "zweites Argument x drittes Argument" stimmen nicht überein.");
return new Matrix<>(array, width, height);
}
Matrix<T, R> setAction(Consumer<Matrix<T, R>> action) {
if (Objects.nonNull(function)) function = null;
this.action = action;
return this;
}
Matrix<T, R> setAction(Function<Matrix<T, R>, R> function) {
if (Objects.nonNull(action)) action = null;
this.function = function;
return this;
}
@SuppressWarnings("unchecked")
R get(int xIndex, int yIndex) {
if (isIllegalIndex(xIndex, yIndex)) return null;
return setAction(m -> {
if (m.y == yIndex && m.x == xIndex) return (R) array[m.i];
return null;
})
.run();
}
Matrix<T, R> put(T obj, int xIndex, int yIndex) {
if (isIllegalIndex(xIndex, yIndex)) return this;
setAction(m -> {
if (m.y == yIndex && m.x == xIndex) array[m.i] = obj;
})
.run();
return this;
}
T[] array() {
return array;
}
Matrix<T, R> insertShiftLeft(T obj, int xIndex, int yIndex) {
if (isIllegalIndex(xIndex, yIndex)) return this;
setAction(m -> {
if (m.i < width * yIndex + xIndex) array[m.i] = array[m.i + 1];
if (m.y == yIndex && m.x == xIndex) array[m.i] = obj;
})
.run();
return this;
}
Matrix<T, R> insertShiftRight(T obj, int xIndex, int yIndex) {
if (isIllegalIndex(xIndex, yIndex)) return this;
T[] cloneArray = array.clone();
setAction(m -> {
if (width * height - m.i - 1 > width * yIndex + xIndex)
array[width * height - m.i - 1] = cloneArray[width * height - m.i - 2];
if (m.y == yIndex && m.x == xIndex) array[m.i] = obj;
})
.run();
return this;
}
R run() {
if (Objects.isNull(action) && Objects.isNull(function))
throw new IllegalStateException("Definieren Sie die sequentielle Verarbeitung.");
R result = null;
Supplier<R> l_action = null;
if (Objects.nonNull(action))
l_action = () -> {action.accept(this); return null;};
else if (Objects.nonNull(function))
l_action = () -> function.apply(this);
for (int i = 0, x = 0, y = 0; i < array.length; i++, x++) {
this.i = i;
this.x = x;
this.y = y;
result = l_action.get();
if (Objects.nonNull(result)) break;
if (x == width - 1) {
x = -1;
y++;
}
}
return result;
}
private boolean isIllegalIndex(int xIndex, int yIndex) {
if (xIndex < 0 || width - 1 < xIndex) return true;
if (yIndex < 0 || height - 1 < yIndex) return true;
return false;
}
}
Ich habe sowohl keinen Rückgabewert als auch einen Rückgabewert angenommen. Geben Sie den Typ des Arrays im T-Teil von <T, R> von (...) an. Geben Sie für den Teil "R" den Rückgabetyp an, wenn ein Rückgabewert vorhanden ist. Wenn es keinen Rückgabewert gibt, geben Sie "java.lang.Void" an und geben Sie an, dass es keinen Rückgabewert gibt [^ null Void].
Verwendungsbeispiel (kein Rückgabewert)
Matrix.<Person, Void>of(people, width, height)
.setAction(m -> {
if (m.y == 1 && m.x == 4) System.out.println(people[m.i]); // Person [name=Ushio]
})
.run();
Definieren Sie den `` Teil des Scan-Codes mit der setAction-Methode, bevor Sie die run-Methode aufrufen. Es verwendet eine funktionale Schnittstelle und ersetzt "i", "x" und "y" durch "m.i", "m.x" bzw. "m.y".
m muss nicht
m` sein, da es der Argumentname [^ m] des Lambda-Ausdrucks ist.
Wenn es einen Rückgabewert gibt, ist dies etwas kompliziert, es wird jedoch eine Funktionsschnittstelle mit "return" wie folgt verwendet. Es wird definiert und verwendet, um ein Nicht-Null-Objekt zurückzugeben, wenn es einer bestimmten Bedingung entspricht. Wenn die Bedingung nicht erfüllt ist, wird null zurückgegeben [^ null].
Verwendungsbeispiel (mit Rückgabewert)
String personName = Matrix.<Person, String>of(people, width, height)
.setAction(m -> {
if (m.y == 1 && m.x == 4) return people[m.i].toString();
return null;
})
.run();
System.out.println(personName); // Person [name=Ushio]
Diese Operationen werden als get-Methode und put-Methode implementiert.
python
Matrix<Person, Person> m = Matrix.of(people, width, height);
System.out.println(m.get(0, 3)); // Person [name=Kawano]
System.out.println(m.get(3, 2)); // Person [name=Yoshida]
System.out.println(m.get(4, 1)); // Person [name=Ushio]
m.put(new Person("Hoge"), 0, 3);
System.out.println(m.get(0, 3)); // Person [name=Hoge]
Da ein eindimensionales Array verwendet wird, können Sie Elemente einfügen, indem Sie es wie folgt definieren. Da die Länge des Arrays festgelegt ist, werden die Elemente vor "Yamada" einzeln nach links verschoben, und das erste Element (Yamauchi) verschwindet.
Fügen Sie "Hoge" zwischen "Yamada" und "Sasaki" ein und verschieben Sie nach links.
int xIndex = 2;
int yIndex = 3;
Matrix.<Person, Void>of(people, width, height)
.setAction(m -> {
if (m.i < width * yIndex + xIndex) array[m.i] = array[m.i + 1];
if (m.y == yIndex && m.x == xIndex) array[m.i] = new Person("Hoge");
})
.run();
Im Gegenteil, wenn es wie folgt definiert ist, werden die Elemente nach "Yamada" nacheinander nach rechts verschoben und das letzte Element (Yaguchi) verschwindet. Wenn Sie nur mit einem Array arbeiten, ist die Elementverschiebung bei Angabe von $ x = 0 $ aufgrund des Problems der Referenzkopie nicht wie erwartet. Duplizieren Sie das Array also einmal und arbeiten Sie von diesem Array zum ursprünglichen Array. Ich bin.
Fügen Sie "Hoge" zwischen "Kato" und "Yamada" ein und verschieben Sie nach rechts.
int xIndex = 2;
int yIndex = 3;
Person[] cloneArray = people.clone();
Matrix.<Person, Void>of(people, width, height)
.setAction(m -> {
if (width * height - m.i - 1 > width * yIndex + xIndex)
array[width * height - m.i - 1] = cloneArray[width * height - m.i - 2];
if (m.y == yIndex && m.x == xIndex) array[m.i] = new Person("Hoge");
})
.run();
Diese Operationen werden als insertShiftLeft-Methode und insertShiftRight-Methode implementiert.
python
Matrix.<Person, Void>of(people, width, height)
.insertShiftLeft(new Person("Hoge"), 2, 4); // 「大森」と「丘」の間に「Hoge」を挿入し、左にシフトする。
python
Matrix.<Person, Void>of(people, width, height)
.insertShiftRight(new Person("Hoge"), 2, 4); // 「尾崎」と「大森」の間に「Hoge」を挿入し、右にシフトする。
Selbst mit einem eindimensionalen Array konnten wir Daten in einem zweidimensionalen Array verwalten. Wenn Sie die Array-Breite oder Array-Höhe später ändern möchten, können Sie dies tun, indem Sie einfach die Werte von $ width $ und $ height $ ändern.
python
int width = 7;
int height = 2;
String[] names = {
"Yamauchi", "Wald", "Izumi", "Yoshikawa", "Shibuya", "Matsumoto", "Kono",
"Kato", "Nishimura", "Taketa", "Yoshida", "Ito", "Kawano", "Kato"
};
Iterator<String> nameTokens = Arrays.stream(names).iterator();
Person[] people = ... //Kürzung
Matrix<Person, Person> m = Matrix.of(people, width, height);
System.out.println(m.get(3, 1)); // Person [name=Yoshida]
Wenn Sie dies möchten, können Sie alternativ eine Methode in der Matrix-Klasse definieren, die die Nicht-Null-Elemente auf der linken Seite jeder Zeile nacheinander wie folgt extrahiert. Durch Verwendung der Variablen des Scan-Codes ist es möglich, die Elemente auf der rechten Seite, der oberen Seite und der unteren Seite auszugeben.
python
@SuppressWarnings("unchecked")
public T[] getNonNullRightElements() {
T[] elements = (T[]) Array.newInstance(array.getClass().getComponentType(), height);
setAction(m -> {
if (Objects.nonNull(array[m.i])) elements[m.y] = array[m.i];
})
.run();
return ArrayUtils.removeAllOccurrences(elements, null);
}
Auf diese Weise ist es auch möglich, die Zellen zu bestimmen, die eine Trefferbeurteilung haben, wie in der folgenden Abbildung gezeigt.
Ein Kommentar von @sdkei wies auf die Matrix-Klasse hin. Vielen Dank! Basierend auf den Verbesserungsmaßnahmen, die ich erhalten habe, habe ich die Matrix-Klasse wie folgt geändert.
python
public final class Matrix<T, R> {
private final T[] array;
private final int width;
private final int height;
private MatrixRunner runner;
private MatrixSupplier<R> supplier;
private Matrix(T[] array, int width, int height) {
this.array = array;
this.width = width;
this.height = height;
}
public static <T, R> Matrix<T, R> of(T[] array, int width, int height) {
if (ObjectUtils.isEmpty(array))
throw new IllegalArgumentException("Das als erstes Argument verwendete Array ist leer oder null.");
if (array.length != width * height)
throw new IllegalArgumentException("Die Länge des Arrays als erstes Argument und der Wert von "zweites Argument x drittes Argument" stimmen nicht überein.");
return new Matrix<>(array.clone(), width, height); //Defensive Kopie
}
public Matrix<T, R> setAction(MatrixRunner runner) {
if (Objects.nonNull(supplier)) supplier= null;
this.runner = runner;
return this;
}
public Matrix<T, R> setAction(MatrixSupplier<R> supplier) {
if (Objects.nonNull(runner)) runner= null;
this.supplier = supplier;
return this;
}
@SuppressWarnings("unchecked")
public R get(int xIndex, int yIndex) {
if (indexOutOfBounds(xIndex, yIndex)) return null;
return (R) array[width * yIndex + xIndex];
}
public Matrix<T, R> put(T obj, int xIndex, int yIndex) {
if (indexOutOfBounds(xIndex, yIndex)) return this;
array[width * yIndex + xIndex] = obj;
return this;
}
public T[] array() {
return array.clone(); //Defensive Kopie
}
public Matrix<T, R> insertShiftLeft(T obj, int xIndex, int yIndex) {
if (indexOutOfBounds(xIndex, yIndex)) return this;
setAction((i, x, y) -> {
if (i < width * yIndex + xIndex) array[i] = array[i + 1];
if (y == yIndex && x == xIndex) array[i] = obj;
})
.run();
return this;
}
public Matrix<T, R> insertShiftRight(T obj, int xIndex, int yIndex) {
if (indexOutOfBounds(xIndex, yIndex)) return this;
T[] cloneArray = array.clone();
setAction((i, x, y) -> {
if (width * height - i - 1 > width * yIndex + xIndex)
array[width * height - i - 1] = cloneArray[width * height - i - 2];
if (y == yIndex && x == xIndex) array[i] = obj;
})
.run();
return this;
}
public R run() {
if (Objects.isNull(runner) && Objects.isNull(supplier))
throw new IllegalStateException("Definieren Sie die sequentielle Verarbeitung.");
R result = null;
MatrixSupplier<R> l_supplier = null;
if (Objects.nonNull(runner))
l_supplier = (i, x, y) -> { runner.run(i, x, y); return null; };
else if (Objects.nonNull(supplier))
l_supplier = (i, x, y) -> supplier.get(i, x, y);
for (int i = 0, x = 0, y = 0; i < array.length; i++, x++) {
result = l_supplier.get(i, x, y);
if (Objects.nonNull(result)) break;
if (x == width - 1) {
x = -1;
y++;
}
}
return result;
}
@FunctionalInterface
public interface MatrixRunner {
void run(int i, int x, int y);
}
@FunctionalInterface
public interface MatrixSupplier<R> {
R get(int i, int x, int y);
}
private boolean indexOutOfBounds(int xIndex, int yIndex) {
if (xIndex < 0 || width - 1 < xIndex) return true;
if (yIndex < 0 || height - 1 < yIndex) return true;
return false;
}
}
Durch die Erstellung meiner eigenen Funktionsschnittstelle konnte ich die Kapselung verstärken. Um ehrlich zu sein, war ich beeindruckt.
Zusätzlich wurde eine runOnly-Methode hinzugefügt, um die Variabilität der Läufer- und Lieferantenfelder zu nutzen.
python
public Matrix<T, R> runOnly() {
if (Objects.isNull(runner))
throw new IllegalStateException("Definieren Sie die sequentielle Verarbeitung.");
for (int i = 0, x = 0, y = 0; i < array.length; i++, x++) {
runner.run(i, x, y);
if (x == width - 1) {
x = -1;
y++;
}
}
return this;
}
python
@test
public void testRunOnly() {
List<String> people = new ArrayList<>();
matrix.setAction((i, x, y) -> {
if (x == 0 &&
(this.people[i].name.contains("Berg") || this.people[i].name.contains("Fluss"))
people.add(this.people[i].name);
})
.runOnly()
.setAction((i, x, y) -> {
if (x == 3 && (this.people[i].name.contains("Kichi")))
people.add(this.people[i].name);
})
.runOnly();
assertThat(people, contains("Yamauchi", "Kawano", "Yoshikawa", "Yoshida"));
}
Ich denke, dass es nicht sehr praktisch ist, nacheinander zu verarbeiten, aber nachdem Sie einen Prozess mit runOnly ()
ausgeführt haben, werfen Sie das Matrix-Objekt einmal auf eine andere Methode oder ein anderes Objekt, verschiedene Aktionen innerhalb der anderen Methode oder des anderen Objekts Wenn Sie nach dem Festlegen mit der setAction-Methode "runOnly ()" aufrufen und eine andere Verarbeitung durchführen, wird die Implementierung der Verarbeitung meiner Meinung nach etwas umfassender sein.
[^ Experiment]: Es ist nur eine experimentelle Sache, die aus Neugier besteht. Es ist keineswegs ein zweidimensionales Array. [^ Personenklasse]: Die Personenklasse definiert ein Feld, das einen Namen darstellt und die toString-Methode zum Drucken des Namens überschreibt. [^ x und y]: Sowohl $ x als auch \ y $ werden in positiver Richtung um 1 erhöht. [^ Dirty]: Ich habe eine funktionale Schnittstelle eingeführt, die keinen Rückgabewert und einen Rückgabewert annimmt, sodass der Code stark verschmutzt ist. Wenn Sie Vorschläge zur Verbesserung der Lesbarkeit haben, würde ich mich freuen, wenn Sie mich darüber informieren könnten. [^ null Void]: Es handelt sich nur um eine explizite Anweisung, sodass tatsächlich ein null Void-Objekt zurückgegeben wird. [^ m]: Hier wird es als Akronym für Matrix verwendet. [^ null]: Wenn eine Nicht-Null zurückgegeben wird, wenn die Bedingung nicht erfüllt ist, wird das Objekt immer zurückgegeben. [^ Unveränderliche Klasse]: Die Felder Läufer und Lieferanten sind nicht vollständig unveränderlich, da erwartet wird, dass sie veränderlich sind.
Recommended Posts