Maison  >  Article  >  Java  >  Comment utiliser des fonctions pour parcourir un tableau à deux dimensions en Java ?

Comment utiliser des fonctions pour parcourir un tableau à deux dimensions en Java ?

WBOY
WBOYavant
2023-05-07 19:34:061178parcourir

Avant-propos

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

Étant donné que le niveau de traversée des tableaux multidimensionnels est naturellement très profond, existe-t-il un moyen de le réduire ?

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 fonction

Sur 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);
}
Par rapport au précédent, il semble qu'il n'y ait qu'une couche de moins, et cela ne semble pas être un gros problème

.

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

L'implémentation précédente n'a aucun problème avec le parcours normal ; mais lorsque nous rencontrons une certaine condition pendant le processus de traversée, nous pouvons directement revenir, pouvez-vous la supporter ?

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;
}

Basée sur l'idée ci-dessus, notre posture d'utilisation réelle est la suivante :

@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);
}
La mise en œuvre ci-dessus peut répondre à nos besoins. La seule bizarrerie est le retour, qui est. toujours un peu gênant. Trop élégant alors à part cette méthode, y a-t-il un autre moyen ?

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)
}
Nous espérons enregistrer les résultats de la boucle via la classe Ans, où tag=true signifie qu'il n'est pas nécessaire de continuer la boucle et de revenir directement les résultats ans

Les modifications de méthode et les exemples correspondants sont les suivants :

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);
}
Cela semble meilleur que le précédent

Exécutez-le pour voir si le résultat est conforme à nos attentes ;

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!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer