Java 动态判断数组维数并取值
创始人
2024-02-11 03:58:04

一、背景

技术交流群里有同学提了一个看似基础但挺有意思的问题。

问题描述

一个对象是一个未知的数组类型,可能是 short 二维数组,可能是 int 的三维数组等。

诉求

  • 想要遍历修改(获取)它的值
  • 不想写太多 if else (该同学的最初方案是通过 instance of 枚举出所有类型,通过 if else 来写代码)

在这里插入图片描述
群里 程序员 DMZ 给出了很专业的建议,使用策略模式或者采用递归的方式取值。

我的解法也与之类似,本文给出相对具体的参考代码(因为虽然很多同学也能考虑到递归,但递归时如何取值并不太会;如果用策略模式该怎么写也不太会)。

二、推荐方案

2.1 采用递归

这里主要演示传入一维或者 N 维数组,可以获取到每个元素,实际开发中如何处理取出的元素,可以根据示例修改变通即可。

2.1.1 只需要取每个元素


import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.List;public class ArrayDemo2 {public static void main(String[] args) {// 测试三维数组System.out.println(" ------- 测试 3 维数组 ------- ");int[][][] data = new int[][][]{{{1},{2}},{{3,4}}};Object obj = data;List  result= test(obj);System.out.println(" ------- 测试 2 维数组 ------- ");// 测试二维数组int[][] data2 = new int[][]{{1,2},{3,4,5}};result= test(data2);System.out.println(" ------- 测试 1 维数组 ------- ");// 测试一维数组int[] data3 = new int[]{1,2,3};result= test(data3);}// 开发中并不需要使用方感知到默认的 turn 为1 ,因此再次封装,如果不需要 turn 就不需要再次封账 private static List  test(Object array){return test(array, 1);}/*** 伪代码,result 的逻辑根据业务需要来写,这里就不处理了* turn 是为了记录维数,默认1 维,递归一次 +1,如果不需要知道当前是几维数组 turn 可以去掉*/private static List  test(Object array, int turn){// 伪代码模拟结果List result = new ArrayList<>();if(array.getClass().isArray()){for(int i = 0; i< Array.getLength(array); ++i) {Object obj = Array.get(array, i);if(obj.getClass().isArray()){test(obj,turn+1);}else{System.out.println("值:"+obj+",几维数组:"+turn);// result.add(// 计算结果放到  result里);}}}return result;}
}

打印结果

 ------- 测试 3 维数组 ------- 
值:1,几维数组:3
值:2,几维数组:3
值:3,几维数组:3
值:4,几维数组:3------- 测试 2 维数组 ------- 
值:1,几维数组:2
值:2,几维数组:2
值:3,几维数组:2
值:4,几维数组:2
值:5,几维数组:2------- 测试 1 维数组 ------- 
值:1,几维数组:1
值:2,几维数组:1
值:3,几维数组:1

可以看到,符合预期。

2.1.2 需要感知维数,并转换为对应维数的“数组”

如果返回值需要“复原”数组维数,可以考虑使用 List 变通实现(理论上等价),相对简单。

如果非要返回值是数组类型
(1)可以采用类似的思路对方法进行改造,可以考虑分两步,第一步根据Object 通过反射(Array.getLengthArray.get) 获取维数和每一维的 length, 第二步采用类似的方式转换并放入数据,即可。
(2)定义入参对象将 List 和 turn 作为属性,最后根据这个入参,构造 List 转数组的工具还原为数组类型。

import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.List;public class ArrayDemo2 {public static void main(String[] args) {List result = new ArrayList();// 测试三维数组System.out.println(" ------- 测试 3 维数组 ------- ");int[][][] data = new int[][][]{{{1},{2}},{{3,4}}};Object obj = data;test(obj,result);System.out.println(result);System.out.println(" ------- 测试 2 维数组 ------- ");// 测试二维数组List result2 = new ArrayList();int[][] data2 = new int[][]{{1,2},{3,4,5}};test(data2,result2);System.out.println(result2);System.out.println(" ------- 测试 1 维数组 ------- ");// 测试二维数组List result3 = new ArrayList();int[] data3 = new int[]{1,2,3};test(data3,result3);System.out.println(result3);}private static void  test(Object array, List result){test(array, 1, result);}/*** 伪代码,result 的逻辑根据业务需要来写,这里就不处理了* turn 是为了记录维数,默认1 维,递归一次 +1*/private static void test(Object array, Integer turn,List result){if(array.getClass().isArray()){for(int i = 0; i< Array.getLength(array); ++i) {Object obj = Array.get(array, i);if(obj.getClass().isArray()){List innerResult = new ArrayList<>();result.add(innerResult);test(obj,turn+1, innerResult);}else{System.out.println("值:"+obj+",几维数组:"+turn);// result.add(// 计算结果放到  result里);result.add("模拟"+obj+"转换结果");}}}}
}

打印结果:

 ------- 测试 3 维数组 ------- 
值:1,几维数组:3
值:2,几维数组:3
值:3,几维数组:3
值:4,几维数组:3
[[[模拟1转换结果], [模拟2转换结果]], [[模拟3转换结果, 模拟4转换结果]]]------- 测试 2 维数组 ------- 
值:1,几维数组:2
值:2,几维数组:2
值:3,几维数组:2
值:4,几维数组:2
值:5,几维数组:2
[[模拟1转换结果, 模拟2转换结果], [模拟3转换结果, 模拟4转换结果, 模拟5转换结果]]------- 测试 1 维数组 ------- 
值:1,几维数组:1
值:2,几维数组:1
值:3,几维数组:1
[模拟1转换结果, 模拟2转换结果, 模拟3转换结果]

2.2 使用策略模式

这个问题不推荐使用策略模式,但为了演示为了更通用,提供了策略模式的解决示例。
假设我们遇到类似的需求,不会写递归或者无法写递归,或者数组的类型非常少,我们可以使用策略模式或者责任链模式来破解 If else 的问题。

定义策略接口:

public interface ArrayStrategy {/*** 当前策略是否可以处理该对象*/Class type();/*** 执行处理并返回结果*/Object  handle(Object object);
}

int[] 类型的处理策略(其他类型的自行编写):

/*** int [] 处理策略*/
public class IntArrayStrategy implements  ArrayStrategy{public Class type(){return int[].class;}public  Object handle(Object object) {int[] array = (int[])object;// 处理逻辑for (int j : array) {System.out.println("int 数组,元素:" + j);}// 这里是伪代码,返回空数组return new float[array.length];}
}

示例代码:


public class ArrayDemo {private static Map arrayStrategies = new HashMap<>(16);static {// int 一维数组ArrayStrategy strategy = new IntArrayStrategy();arrayStrategies.put(strategy.type(),strategy);// short 一维数组strategy = new ShortArrayStrategy();arrayStrategies.put(strategy.type(),strategy);//  int 二维数组等,}public static void main(String[] args) {int[] data1 = new int[]{1, 2};// 模拟传入 object 类型Object obj = data1;ArrayStrategy strategy = arrayStrategies.get( obj.getClass());// 需要判断 strategy 是否为空Object result = strategy.handle(obj);System.out.println(result);}
}

构造 Map 映射这里,也可以定义 List 将支持策略放进去, for 循环构造 Map; 也可以将策略定义为 Spring 的 Bean ,通过后置处理器构造类型到 Bean 的映射 Map
参考《巧用 Spring 自动注入实现策略模式升级版》。

运行的结果:

int 数组,元素:1
int 数组,元素:2
[F@3f99bd52

这样就可以将不同类型的特有处理逻辑内聚到对应的策略中,如果需要支持新的数组类型(如要支持 double[][] ),加一个新的策略即可。

三、总结

日常开发中,遇到觉得“不太对劲” 、“不太优雅” 的地方(其实只要不符合高内聚、弱耦合的场景都有问题),要主动思考如何解决,可以和其他同学交流下,努力写出更简洁和优雅的代码。
日常开发中,多了解 JDK 中反射相关的类,多了解一些知名的三方工具类,很多功能实现起来就会容易一些。
要了解常见的设计模式,很多问题优先考虑是否可以使用某种设计模式或者某种设计模式的变种来解决问题。

总之,写代码是良心活,工作中写代码是项目时间和代码质量之间权衡的结果。
对代码没太大追求的同学有一万种理由不去写出更好的代码。想写出好代码的同学会在项目工期紧张的情况下,尽量写出更简洁、优雅的、健壮、拓展性更强代码。


创作不易,如果本文对你有帮助,欢迎点赞、收藏加关注,你的支持和鼓励,是我创作的最大动力。
在这里插入图片描述

相关内容

热门资讯

埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...
脚上的穴位图 脚面经络图对应的... 人体穴位作用图解大全更清晰直观的标注了各个人体穴位的作用,包括头部穴位图、胸部穴位图、背部穴位图、胳...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...
猫咪吃了塑料袋怎么办 猫咪误食... 你知道吗?塑料袋放久了会长猫哦!要说猫咪对塑料袋的喜爱程度完完全全可以媲美纸箱家里只要一有塑料袋的响...
demo什么意思 demo版本... 618快到了,各位的小金库大概也在准备开闸放水了吧。没有小金库的,也该向老婆撒娇卖萌服个软了,一切只...
世界上最漂亮的人 世界上最漂亮... 此前在某网上,选出了全球265万颜值姣好的女性。从这些数量庞大的女性群体中,人们投票选出了心目中最美...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...
脚上的穴位图 脚面经络图对应的... 人体穴位作用图解大全更清晰直观的标注了各个人体穴位的作用,包括头部穴位图、胸部穴位图、背部穴位图、胳...