As for array traversal, basically every developer has written about it. There is nothing much to say about the traversal itself, but when we have some complex business logic during the traversal process, we will find that The level of the code will gradually deepen
For example, in a simple case, find the even numbers in a two-dimensional array and save them in a list
Traverse the two-dimensional array and judge each element Whether it is an even number can be easily written, such as:
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); }
There is no problem with the above implementation, but the depth of this code can easily be three levels; when the above if If there are other judgment conditions, then the code level can be easily increased; two-dimensional arrays are fine, but if it is a three-dimensional array, one traversal will be three levels; with a little logic, four or five levels can be achieved in minutes, right?
Then the question is, what problems will there be when the code level increases?
As long as the code can run, what's the problem? !
Because the traversal level of multi-dimensional arrays is naturally very deep, is there any way to reduce it?
To solve this problem, the key is to grasp the key points. What is the key point of traversal? Get the coordinates of each element! So what can we do?
Define a function method, the input is the function coordinates, and execute our traversal logic in this function body
Based on the above idea, I believe we can easily It is easy to write a two-dimensional array traversal general method
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); } } }
The main implementation is above. The function method directly uses the BiConsumer provided by JDK by default. The two parameters passed are both int arrays as shown in the table below; no return value
So how to use the above?
The same is the above example. After changing it, it looks like:
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); }
Compared with the previous one, it seems that there is only one less layer, and it seems to be nothing special.
However, when the array becomes three-dimensional, four-dimensional, or non-dimensional, the writing level of this change will not change
Previous There is no problem with the implementation of normal traversal; but when we encounter a certain condition during the traversal process and return directly, can it be supported?
For example, if we are traversing a two-dimensional array and we want to determine whether there are even numbers in it, how can we round it?
Think about our scan method carefully, hoping to support return. The main problem is that after the function method is executed, how do I know whether to continue looping or return directly?
It’s easy What comes to mind is to add an additional return value to the execution logic to mark whether to interrupt the loop and return directly
Based on this idea, we can implement a simple demo version
Define a function method , accepting the subscript return value of the loop
@FunctionalInterface public interface ScanProcess<T> { ImmutablePair<Boolean, T> accept(int i, int j); }
The general loop method can be changed accordingly:
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; }
Based on the above idea, our actual The usage posture is as follows:
@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); }
The above implementation can meet our needs. The only awkward thing is the return, which is always a bit inelegant; besides this method, there are other The way?
Now that the return value has been considered, what about passing parameters? Is it feasible to use a defined parameter to determine whether to interrupt and return the result?
Based on this idea, we can first define a parameter packaging class:
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) }
We hope to record the loop results through the Ans class, where tag=true means There is no need to continue looping, just return the ans result directly
The corresponding method transformation and examples are as follows:
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); }
This will look better than the previous one
Actually run it and see if the output is consistent with our expectations;
The above is the detailed content of How to use functions to traverse a two-dimensional array in Java?. For more information, please follow other related articles on the PHP Chinese website!