一、背景

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

问题描述

一个对象是一个未知的数组类型,可能是 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<Float>  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<Float>  test(Object array){return test(array, 1);}/*** 伪代码,result 的逻辑根据业务需要来写,这里就不处理了* turn 是为了记录维数,默认1 维,递归一次 +1,如果不需要知道当前是几维数组 turn 可以去掉*/private static List<Float>  test(Object array, int turn){// 伪代码模拟结果List<Float> 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<Class, ArrayStrategy > 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<ArrayStrategy> 将支持策略放进去, for 循环构造 Map; 也可以将策略定义为 Spring 的 Bean ,通过后置处理器构造类型到 Bean 的映射 Map
参考《巧用 Spring 自动注入实现策略模式升级版》。

运行的结果:

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

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

三、总结

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

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


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

Java 动态判断数组维数并取值相关推荐

  1. java如何判断回文数

    java如何判断回文数 什么是回文数?数值两边呈对称的数是回文数,比如121,12321,1234321- 但是回文数应该怎么判断呢,以121举例子: 121%(取余)10得到了1, 12%10得到了 ...

  2. 【Numpy】改变数组维数

    文章目录 前言 1. atleast_1d.atleast_2d.atleast_3d 2. broadcast 3. broadcast_to 4. broadcast_arrays 5. expa ...

  3. C语言——判断矩阵维数(sizeof、_msize)

    C语言中可以利用二维数组或者malloc开辟矩阵,本章将分别介绍这两种方法是如何判断矩阵维数的. 判断矩阵维数 sizeof 以二维数组创建矩阵时,可以利用sizeof判断内存的大小 例如: doub ...

  4. 【VBA】数组维数的确定

    在VBA中,数组维数的确定其实更多的应该叫做矩阵的维数确定,因为其实说数组的维数的时候,指的是这个矩阵的维数 例如,对于单元格区域A1:C100,如果定义以下数组: array=range(" ...

  5. Java循环判断数组中是否包含字符串

    关于Java循环判断数组中是否包含字符串的方法: // 循环判断数组中是否包含字符串public static boolean useLoop(String[] arr, String targetV ...

  6. Java如何判断数组是否相等呢?

    转自: Java如何判断数组是否相等呢? 数组: 数组(Array)是有序的元素序列. [1]  若将有限个类型相同的变量的集合命名,那么这个名称为数组名.组成数组的各个变量称为数组的分量,也称为数组 ...

  7. 杨桃的Python进阶讲座16——数组array(六)一维数组和二维数组的索引和取值(配详细图解)

    本人CSDN博客专栏:https://blog.csdn.net/yty_7 Github地址:https://github.com/yot777/ 在进阶讲座8中讲过数组(矩阵)的维度,我们再看看多 ...

  8. C语言各数据类型所占字节数和取值范围

    C语言中各数据类型所占字节数和取值范围 注:每种数据类型的取值范围都是与编译器相关的,以下为数据Visual C++32位环境下的参数,如想清楚了解自己所用编译器对各数据类型所占字节,可以用sizeo ...

  9. JS 判断数组中是否包含某个值

    方式一:array.indexOf(searchvalue, start) 判断数组中是否存在某个值,如果存在,则返回数组元素的下标,否则返回-1 参数 描述 searchvalue 必填.规定需检索 ...

最新文章

  1. android jni 字符串拼接,AndroidJNI
  2. 自己写的哈希表以及解决哈希冲突
  3. 计算机辅助教学( ),27075 计算机辅助教学
  4. 使用adb命令查看Sqlite数据库
  5. java 只显示文本文件_Java设计并实现一个应用程序,能够读取一个文本文件中的内容并显示,同时能够计算出文本中的行数。...
  6. elementUI vue table status的状态列颜色变化和操作列状态显示(停用, 启用)
  7. Andorid之jni里面崩溃然后用errno分析结果解决问题
  8. [USACO19JAN,Platinum]Train Tracking 2
  9. 图片高亮处理编程_GMT语法高亮-智能提示-代码补全插件
  10. POJ 2386 Lake Counting DFS水水
  11. input ios问题 小程序_微信小程序开发常见问题汇总
  12. 当常规的算法都山穷水尽之后,你可以试试python中的SMOTE算法
  13. 前端开发响应式布局和移动端布局有哪些特点和区别?
  14. 进程篇—进程整理(转)
  15. gin框架的学习--golang
  16. 如何利用window下的Dos命令实现将多个txt合并成一个txt
  17. 【Windows 问题系列第 12 篇】Windows 10 如何显示文件名后缀
  18. Oracle中lpad的用法
  19. ISO26262-道路车辆功能安全
  20. win10打开谷歌浏览器chrome,并进入kiosk模式

热门文章

  1. PTA java 6-3 BMI计算
  2. java jpcap使用的注意事项
  3. 学业计算机水平考试试题,信息技术学业水平考试试题
  4. 什么才是用户的真实需求?
  5. 青少年创新大赛python考点_青少年编程热门竞赛专题 | 全国中小学信息技术创新与实践大赛指南!...
  6. IIC总线式驱动开发(mpu6050)
  7. 软件工程师如何做到越老越吃香
  8. 商场三十六计——第9计 “隔岸观火”
  9. 10分钟就可以秒懂计算机体系结构与CPU工作原理
  10. 千亿咖啡市场,占比七成的速溶赛道,被现磨咖啡倒逼升级?