Java 动态判断数组维数并取值

news2024/9/20 18:26:02

一、背景

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

问题描述

一个对象是一个未知的数组类型,可能是 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 中反射相关的类,多了解一些知名的三方工具类,很多功能实现起来就会容易一些。
要了解常见的设计模式,很多问题优先考虑是否可以使用某种设计模式或者某种设计模式的变种来解决问题。

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


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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/31132.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

springboot 接入 logback.xml 彻底搞出一个超级完整加注释的版本-可在生产环境直接使用

目录 介绍 开搞 先logback.xml相关的 pom.xml application.yml 配置 启动配置 类中编写 引入 Slf4j logback.xml 重点介绍 logback项目名称 最大保存时间 365天 lOGGER PATTERN 根据个人喜好选择匹配 控制台输出 滚动文件 过滤器 可以选择自己要的日志级别 不选…

【机器学习数据集制作】视频转图片(代码注释,思路推导)

目录数据集效果资源下载实现思路代码实战总结『机器学习』分享机器学习0基础的数据集制作过程。 欢迎关注 『机器学习』 系列&#xff0c;持续更新中 欢迎关注 『机器学习』 系列&#xff0c;持续更新中 数据集效果 资源下载 拿来即用&#xff0c;所见即所得。 项目仓库&#…

​LabVIEW从另一个VI或通过VI服务器访问正在运行的可执行文件

​LabVIEW从另一个VI或通过VI服务器访问正在运行的可执行文件 有没有办法从另一个VI或可执行文件访问正在运行的LabVIEW可执行文件。例如&#xff0c;从显示控件获取值&#xff0c;为控件设置值&#xff0c;以及初始化运行LabVIEW可执行文件VI的前面板。 在正在运行的可执行文…

IOS逆向初探

前言 这些文章用于记录学习路上的点点滴滴&#xff0c;也希望能给到刚入门的小伙伴们一点帮助。爱而所向&#xff0c;不负所心。 环境 iphone 6 MacOS Monterey 12.3.1 一、IOS开发语言 Objective-C Objective-C是iOS操作系统运用的软件开发语言。Objective-C的流行完全是因…

免费下载word简历模板的网站

我这里分享了6个免费简历网站&#xff0c;分享给各位。 1.OfficePlus 微软官方出品的 office 免费模板网站https://www.officeplus.cn/ 2&#xff0e;简历设计网 2000Word模板免费下载&#xff0c;每个用于每天可下载10篇免费模板。https://www.jianlisheji.com/ 3.办公资源…

mysql 一对多查询 合并为一行数据

用户包含多个角色 执行&#xff1a; SELECT ur.user_id,u.name user_name,u.mail,ur.role_id,r.name role_name FROM tb_user_role ur LEFT JOIN tb_user u ON u.idur.user_id LEFT JOIN tb_role r ON r.idur.role_id WHERE u.is_delete0 ORDER BY …

华为大数据HCIA题目1

1. HDFS 不适用于以下哪些场景&#xff1f;[多选题] A.流式数据访问 B.大量小文件存储 C.大文件存储与访问 D.随机写入 &#xff08;BD&#xff09; 2. ZKFC 进程部署在 hdfs 中的以下那个节点上&#xff1f;[多选题] A.active namenode B.standby namenode C.datanod…

重塑运维系统,跨越烟囱式建设的陷阱

企业运维系统建设经过多年演变&#xff0c;从以商业软件为主&#xff0c;到开源软件的百花齐放&#xff0c;极大的降低了成本&#xff0c;但是在建设过程中&#xff0c;却非常容易落入到烟囱式建设的陷阱&#xff0c;因此如何跨越它&#xff0c;成为了众多企业面临的难题。 今…

Deepin Linux系统怎安装打印机? 兄弟1618w打印机驱动安装图文教程

Deepin系统作为国产的一款电脑操作系统&#xff0c;拥有极为非常美观的UI界面。很多不熟悉该操作系统的朋友都不知道该怎么安装打印机驱动&#xff0c;今天我们就以兄弟1618w打印机为例&#xff0c;分享驱动下载&#xff0c;安装&#xff0c;调试的过程。 电脑环境和打印机型号…

Design Compiler工具学习笔记(5)

目录 引言 知识储备 代码风格 DFT 实际操作 引言 本篇继续学习 DC的基本使用。本篇主要学习 DC 工作机理和工作过程 以及简单介绍 DFT。 前文链接&#xff1a; Design Compiler工具学习笔记&#xff08;1&#xff09; Design Compiler工具学习笔记&#xff08;2&#…

动态规划算法学习四:最大上升子序列问题(LIS:Longest Increasing Subsequence)

文章目录前言一、问题描述二、DP步骤1、最优子结构a、限界上升子序列b、最优子结构性质2、状态表示和递推方程3、计算最优值4、算法实现三、优化&#xff1a;非DP /二分法1、新问题2、算法实现前言 一、问题描述 二、DP步骤 1、最优子结构 给定序列&#x1d446;[&#x1d4…

“300万”只是新起点,比亚迪将开启下一个 “黄金周期”

比亚迪再次创造全球新能源汽车市场新标杆。 11月16日&#xff0c;比亚迪第300万辆新能源汽车正式下线。成为首个达成这一里程碑的中国品牌。 正如比亚迪股份有限公司董事长兼总裁王传福说&#xff0c;从“第1辆新能源汽车到第100万辆新能源汽车”用时13年、从“100万辆到200万…

MCE | 磁珠 Protocol,如何快速捕获您心仪的蛋白~

磁珠的优势 ◎ 蛋白荷载量高 ◎ 特异性强、非特异性结合性低 ◎ 样品损失小 ◎ 操作方便 如何操作 ■ 磁珠预处理 将磁珠充分混悬&#xff0c;取 25-50 μL 磁珠&#xff0c;置于 1.5 mL EP 管中&#xff0c;加入 400 μL 结合/洗涤缓冲液&#xff0c;充分混悬&#xff0c;置…

Word处理控件Aspose.Words功能演示:使用 Java 将文本转换为 PDF

TXT格式的文本文档包含行形式的纯文本。TXT 文件是存储没有任何格式的纯文本的最简单和最简单的方法。我们可以在任何文本编辑器或文字处理应用程序中轻松创建、打开和编辑 TXT 文件。在某些情况下&#xff0c;我们可能需要将文本转换为只读格式&#xff0c;例如PDF。在本文中&…

flutter 怎么消除按钮事件的点击溅射背景

flutter 怎么消除按钮事件的点击溅射背景前言一、设置 ThemeData二、Theme 设置三、单独设置总结前言 在flutter 中&#xff0c;大部分事件组件都有一个溅射背影&#xff0c;但是假如某天需求让我们取消点击溅射效果&#xff0c;我们该怎么办呢&#xff1f;本篇文章将记录怎么…

画法几何及机械制图复习题及答案

机 械 制 图复习题及参考答案 一、填空题 1&#xff0e;比例的种类有 、 、 。 2&#xff0e;图样中的可见轮廓线用 绘制&#xff1b;图样中尺寸线和尺寸界线用 绘制。 3&#xff0e;正投影的基本性质包括 、 、 。 4&#xff0e;三视图的投影关系表现为&#xff1a;主、俯…

【ML特征工程】第 1 章 :机器学习管道

&#x1f50e;大家好&#xff0c;我是Sonhhxg_柒&#xff0c;希望你看完之后&#xff0c;能对你有所帮助&#xff0c;不足请指正&#xff01;共同学习交流&#x1f50e; &#x1f4dd;个人主页&#xff0d;Sonhhxg_柒的博客_CSDN博客 &#x1f4c3; &#x1f381;欢迎各位→点赞…

数字逻辑·时序线路分析【常见的时序线路】

这一篇和之前那一篇讲的是时序线路 之前学过的是组合线路 寄存器 有3个D触发器控制 C1 − C3 用来寄存二进制代码。 下面的与或非门用来接收要寄存的二进制代码。 上面的与非门用来发送寄存的二进制代码。 输入信号&#xff1a; RD&#xff1a;清除信号。 WAC&#xff1a;直送…

spring-cloud-dubbo基本使用

创建模块 api模块使用mave quick-start 构建: &#xff0c;provider模块使用 下面方式创建&#xff1a; 点击下一步&#xff0c;会看到一些基于阿里的cloud的依赖&#xff1a; 上面这个是基于阿里云的&#xff0c;下面的Spring Cloud Alibaba是开源的组件依赖&#xff1a; …

torch.as_tensor()、torch.Tensor() 、 torch.tensor() 、transforms.ToTensor()的区别

1&#xff09;torch.as_tensor(data, dtypeNone,deviceNone)->Tensor : 为data生成tensor&#xff0c;保留 autograd 历史记录并尽量避免复制&#xff08;dtype和devices相同&#xff0c;尽量浅拷贝&#xff09;。 如果data已经是tensor&#xff0c;且dtype和device与参数相…