Java技巧函數(shù)方法實(shí)現(xiàn)二維數(shù)組遍歷
前言
對于數(shù)組遍歷,基本上每個(gè)開發(fā)者都寫過,遍歷本身沒什么好說的,但是當(dāng)我們在遍歷的過程中,有一些復(fù)雜的業(yè)務(wù)邏輯時(shí),將會(huì)發(fā)現(xiàn)代碼的層級(jí)會(huì)逐漸加深
如一個(gè)簡單的case,將一個(gè)二維數(shù)組中的偶數(shù)找出來,保存到一個(gè)列表中
二維數(shù)組遍歷,每個(gè)元素判斷下是否為偶數(shù),很容易就可以寫出來,如:
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); }
上面這個(gè)實(shí)現(xiàn)沒啥問題,但是這個(gè)代碼的深度很容易就有三層了;當(dāng)上面這個(gè)if中如果再有其他的判定條件,那么這個(gè)代碼層級(jí)很容易增加了;二維數(shù)組還好,如果是三維數(shù)組,一個(gè)遍歷就是三層;再加點(diǎn)邏輯,四層、五層不也是分分鐘的事情么
那么問題來了,代碼層級(jí)變多之后會(huì)有什么問題呢?
只要代碼能跑,又能有什么問題呢?!
1. 函數(shù)方法消減代碼層級(jí)
由于多維數(shù)組的遍歷層級(jí)天然就很深,那么有辦法進(jìn)行消減么?
要解決這個(gè)問題,關(guān)鍵是要抓住重點(diǎn),遍歷的重點(diǎn)是什么?獲取每個(gè)元素的坐標(biāo)!那么我們可以怎么辦?
定義一個(gè)函數(shù)方法,輸入的就是函數(shù)坐標(biāo),在這個(gè)函數(shù)體中執(zhí)行我們的遍歷邏輯即可
基于上面這個(gè)思路,相信我們可以很容易寫一個(gè)二維的數(shù)組遍歷通用方法
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); } } }
主要上面的實(shí)現(xiàn),函數(shù)方法直接使用了JDK默認(rèn)提供的BiConsumer,兩個(gè)傳參,都是int 數(shù)組下表;無返回值
那么上面這個(gè)怎么用呢?
同樣是上面的例子,改一下之后,如:
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); }
相比于前面的,貌似也就少了一層而已,好像也沒什么了不起的
但是,當(dāng)數(shù)組變?yōu)槿S、四維、無維時(shí),這個(gè)改動(dòng)的寫法層級(jí)都不會(huì)變哦
2. 遍歷中return支持
前面的實(shí)現(xiàn)對于正常的遍歷沒啥問題;但是當(dāng)我們在遍歷過程中,遇到某個(gè)條件直接返回,能支持么?
如一個(gè)遍歷二維數(shù)組,我們希望判斷其中是否有偶數(shù),那么可以怎么整?
仔細(xì)琢磨一下我們的scan方法,希望可以支持return,主要的問題點(diǎn)就是這個(gè)函數(shù)方法執(zhí)行之后,我該怎么知道是繼續(xù)循環(huán)還是直接return呢?
很容易想到的就是執(zhí)行邏輯中,添加一個(gè)額外的返回值,用于標(biāo)記是否中斷循環(huán)直接返回
基于此思路,我們可以實(shí)現(xiàn)一個(gè)簡單的demo版本
定義一個(gè)函數(shù)方法,接受循環(huán)的下標(biāo) + 返回值
@FunctionalInterface public interface ScanProcess<T> { ImmutablePair<Boolean, T> accept(int i, int j); }
循環(huán)通用方法就可以相應(yīng)的改成:
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; }
基于上面這種思路,我們的實(shí)際使用姿勢如下:
@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); }
上面這個(gè)實(shí)現(xiàn)可滿足我們的需求,唯一有個(gè)別扭的地方就是返回,總有點(diǎn)不太優(yōu)雅;那么除了這種方式之外,還有其他的方式么?
既然考慮了返回值,那么再考慮一下傳參呢?通過一個(gè)定義的參數(shù)來裝在是否中斷以及返回結(jié)果,是否可行呢?
基于這個(gè)思路,我們可以先定義一個(gè)參數(shù)包裝類:
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) }
我們希望通過Ans這個(gè)類來記錄循環(huán)結(jié)果,其中tag=true,則表示不用繼續(xù)循環(huán)了,直接返回ans結(jié)果吧
與之對應(yīng)的方法改造及實(shí)例如下:
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); }
這樣看起來就比前面的要好一點(diǎn)了
實(shí)際跑一下,看下輸出是否和我們預(yù)期的一致;
到此這篇關(guān)于Java技巧函數(shù)方法實(shí)現(xiàn)二維數(shù)組遍歷的文章就介紹到這了,更多相關(guān)Java數(shù)組遍歷內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
springboot攔截器過濾token,并返回結(jié)果及異常處理操作
這篇文章主要介紹了springboot攔截器過濾token,并返回結(jié)果及異常處理操作,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-09-09Java合并兩個(gè)List后并去掉重復(fù)項(xiàng)的兩種做法
工作中很多時(shí)候需要用到合并兩個(gè)List并去除其中的重復(fù)內(nèi)容,這是一個(gè)很簡單的操作,實(shí)現(xiàn)的方法也多種多樣,這篇文章主要給大家介紹了關(guān)于Java合并兩個(gè)List后并去掉重復(fù)項(xiàng)的兩種做法,需要的朋友可以參考下2023-10-10springboot開發(fā)flowable定時(shí)任務(wù)問題
這篇文章主要介紹了springboot開發(fā)flowable定時(shí)任務(wù)問題,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-11-11IDEA上運(yùn)行Flink任務(wù)的實(shí)戰(zhàn)教程
這篇文章主要介紹了IDEA上運(yùn)行Flink任務(wù)的實(shí)戰(zhàn)教程,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-10-10Spring Boot 2和Redis例子實(shí)現(xiàn)過程解析
這篇文章主要介紹了Spring Boot2發(fā)布與調(diào)用REST服務(wù)過程解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-11-11