En ce qui concerne la traversée de tableaux, pratiquement tous les développeurs ont écrit à ce sujet. Il n'y a pas grand chose à dire sur la traversée elle-même, mais lorsque nous avons une logique métier complexe pendant le processus de traversée, nous constaterons que le niveau du code est différent. va progressivement s'approfondir.
Par exemple, dans un cas simple, trouvez les nombres pairs dans un tableau à deux dimensions et enregistrez-les dans une liste.
Parcourez le tableau à deux dimensions et jugez si chaque élément est un nombre pair. peut être facilement écrit, tel que :
public void getEven() { int[][] cells = new int[][]{{1, 2, 3, 4}, {11, 12, 13, 14}, {21, 22, 23, 24}}; List<Integer> ans = new ArrayList<>(); for (int i = 0; i < cells.length; i ++) { for (int j = 0; j < cells[0].length; j++) { if ((cells[i][j] & 1) == 0) { ans.add(cells[i][j]); } } } System.out.println(ans); }
Il n'y a rien de mal avec l'implémentation ci-dessus, mais la profondeur de ce code peut facilement atteindre trois niveaux s'il y a d'autres conditions de jugement dans ce qui précède, alors le niveau de code peut ; peut facilement être augmenté ; le tableau à deux dimensions est très bien, s'il s'agit d'un tableau à trois dimensions, un parcours sera de trois niveaux ; avec un peu de logique, quatre ou cinq niveaux ne seraient-ils pas une question de minutes ? , quel sera le problème lorsque les niveaux de code augmenteront ?
Tant que le code peut s'exécuter, quel est le problème ? !1. Les méthodes fonctionnelles réduisent les niveaux de code
Pour résoudre ce problème, la clé est de saisir les points clés. Quel est le point clé du parcours ? Obtenez les coordonnées de chaque élément ! Alors que pouvons-nous faire ?
Définissez une méthode de fonction, l'entrée correspond aux coordonnées de la fonction et exécutez notre logique de parcours dans ce corps de fonctionSur la base de l'idée ci-dessus, je pense que nous pouvons facilement écrire une méthode générale de parcours de tableau bidimensionnel
public static void scan(int maxX, int maxY, BiConsumer<Integer, Integer> consumer) { for (int i = 0; i < maxX; i++) { for (int j = 0; j < maxY; j++) { consumer.accept(i, j); } } }
Principalement l'implémentation ci-dessus, la méthode fonction utilise directement le BiConsumer fourni par JDK par défaut. Les deux paramètres sont des tableaux int comme indiqué dans le tableau ci-dessous ; il n'y a pas de valeur de retour
Alors, comment utiliser ce qui précède ?
C'est la même chose que l'exemple ci-dessus. Après l'avoir modifié, cela ressemble à :public void getEven() {
int[][] cells = new int[][]{{1, 2, 3, 4}, {11, 12, 13, 14}, {21, 22, 23, 24}};
List<Integer> ans = new ArrayList<>();
scan(cells.length, cells[0].length, (i, j) -> {
if ((cells[i][j] & 1) == 0) {
ans.add(cells[i][j]);
}
});
System.out.println(ans);
}
Cependant, lorsque le tableau devient tridimensionnel ou quadridimensionnel , lorsqu'il n'y a pas de dimension, le niveau d'écriture de ce changement ne changera pas
2 Prise en charge du retour pendant le parcours
Par exemple, si nous parcourons un tableau à deux dimensions et que nous voulons déterminer s'il contient des nombres pairs, comment pouvons-nous l'arrondir ?
Réfléchissez attentivement à notre méthode d'analyse, en espérant prendre en charge le retour. Le principal problème est qu'une fois la méthode fonction exécutée, comment savoir si je dois continuer la boucle ou revenir directement
Il est facile de penser à ajouter l'exécution ? logique Une valeur de retour supplémentaire est utilisée pour marquer s'il faut interrompre la boucle et revenir directement
Basé sur cette idée, nous pouvons implémenter une version de démonstration simple
Définir une méthode fonction qui accepte l'indice de la boucle + valeur de retour
@FunctionalInterface public interface ScanProcess<T> { ImmutablePair<Boolean, T> accept(int i, int j); }
Méthode de boucle générale Elle peut être modifiée en conséquence :
public static <T> T scanReturn(int x, int y, ScanProcess<T> func) {
for (int i = 0; i < x; i++) {
for (int j = 0; j < y; j++) {
ImmutablePair<Boolean, T> ans = func.accept(i, j);
if (ans != null && ans.left) {
return ans.right;
}
}
}
return null;
}
@Test
public void getEven() {
int[][] cells = new int[][]{{1, 2, 3, 4}, {11, 12, 13, 14}, {21, 22, 23, 24}};
List<Integer> ans = new ArrayList<>();
scanReturn(cells.length, cells[0].length, (i, j) -> {
if ((cells[i][j] & 1) == 0) {
return ImmutablePair.of(true, i + "_" + j);
}
return ImmutablePair.of(false, null);
});
System.out.println(ans);
}
Maintenant que la valeur de retour a été prise en compte, qu'en est-il du passage des paramètres ? Est-il possible d'utiliser un paramètre défini pour déterminer s'il faut interrompre et renvoyer le résultat ?
Sur la base de cette idée, nous pouvons d'abord définir une classe d'empaquetage de paramètres :public static class Ans<T> {
private T ans;
private boolean tag = false;
public Ans<T> setAns(T ans) {
tag = true;
this.ans = ans;
return this;
}
public T getAns() {
return ans;
}
}
public interface ScanFunc<T> {
void accept(int i, int j, Ans<T> ans)
}
Les modifications de méthode et les exemples correspondants sont les suivants :
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!public static <T> T scanReturn(int x, int y, ScanFunc<T> func) {
Ans<T> ans = new Ans<>();
for (int i = 0; i < x; i++) {
for (int j = 0; j < y; j++) {
func.accept(i, j, ans);
if (ans.tag) {
return ans.ans;
}
}
}
return null;
}
public void getEven() {
int[][] cells = new int[][]{{1, 2, 3, 4}, {11, 12, 13, 14}, {21, 22, 23, 24}};
String ans = scanReturn(cells.length, cells[0].length, (i, j, a) -> {
if ((cells[i][j] & 1) == 0) {
a.setAns(i + "_" + j);
}
});
System.out.println(ans);
}