zoukankan      html  css  js  c++  java
  • 对象Bean与Map互转问题

    一、摘要

    在实际开发过程中,经常碰到需要进行对象与map之间互转的问题,其实对于对象、Map 之间进行互转有很多种方式,下面我们一起来梳理一下:

    • 利用 JSON 工具包,将对象转成字符串,之后再转成 Map,这种需要转换2次,相对来说效率比较底;
    • 利用 Java 反射,获取 Bean 类的属性和值,再转换到 Map 对应的键值对中,相对来说这种方法效率高些,在实现上比较麻烦;
    • 利用 Java 的内省(Introspector) 实现,获取 Bean 类的属性和值,Map与对象互转,效率比较高;
    • 利用 apache 中的 BeanUtils工具包进行操作,底层实现类似方法三;
    • 利用net.sf.cglib.beans.BeanMap类中的方法,这种方式效率也非常高;

    二、常用方法

    也不多说了,直接show code,为了更接近实际场景,我们新建两个实体类UserRole,假设一个用户有多个角色,如下:

    publicclass User {
    
        private String userId;
    
        private String userName;
    
        private List<Role> roleList;
    
        //... 省略 setter 和 getter
    
        public User() {}
    
        public User(String userId, String userName) {
            this.userId = userId;
            this.userName = userName;
        }
    }
    publicclass Role {
    
        private String userId;
    
        private String roleName;
    
        //... 省略 setter 和 getter
    
        public Role(String userId, String roleName) {
            this.userId = userId;
            this.roleName = roleName;
        }
    }

    2.1、通过 JSON 进行转换

    在这里,我们利用阿里巴巴的fastjson包进行转换,通过maven引入 jar,如下:

    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>1.2.49</version>
    </dependency>

    方法如下:

    publicclass BeanMapUtilByJson {
    
        /**
         * 对象转Map
         * @param object
         * @return
         */
        public static Map beanToMap(Object object){
            return JSONObject.parseObject(JSON.toJSONString(object),Map.class);
        }
    
        /**
         * map转对象
         * @param map
         * @param beanClass
         * @param <T>
         * @return
         */
        publicstatic <T> T mapToBean(Map map, Class<T> beanClass){
            return JSONObject.parseObject(JSON.toJSONString(map),beanClass);
        }
    }

    2.2、利用反射进行转换

    这种操作是利用 java 原生提供的反射特性来实现互转,方法如下:

    publicclass BeanMapUtilByReflect {
    
        /**
         * 对象转Map
         * @param object
         * @return
         * @throws IllegalAccessException
         */
        public static Map beanToMap(Object object) throws IllegalAccessException {
            Map<String, Object> map = new HashMap<String, Object>();
            Field[] fields = object.getClass().getDeclaredFields();
            for (Field field : fields) {
                field.setAccessible(true);
                map.put(field.getName(), field.get(object));
            }
            return map;
        }
    
        /**
         * map转对象
         * @param map
         * @param beanClass
         * @param <T>
         * @return
         * @throws Exception
         */
        publicstatic <T> T mapToBean(Map map, Class<T> beanClass) throws Exception {
            T object = beanClass.newInstance();
            Field[] fields = object.getClass().getDeclaredFields();
            for (Field field : fields) {
                int mod = field.getModifiers();
                if (Modifier.isStatic(mod) || Modifier.isFinal(mod)) {
                    continue;
                }
                field.setAccessible(true);
                if (map.containsKey(field.getName())) {
                    field.set(object, map.get(field.getName()));
                }
            }
            return object;
        }
    }

    2.3、利用内省机制进行转换

    内省(Introspector)是 Java 语言对 JavaBean 类属性、事件的一种缺省处理方法,也是基于 java 原生实现,操作如下:

    publicclass BeanMapUtilByIntros {
    
        /**
         * 对象转Map
         * @param object
         * @return
         */
        public static Map beanToMap(Object object) throws Exception {
            Map<String, Object> map = new HashMap<String, Object>();
            BeanInfo beanInfo = Introspector.getBeanInfo(object.getClass());
            PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
            for (PropertyDescriptor property : propertyDescriptors) {
                String key = property.getName();
                if (key.compareToIgnoreCase("class") == 0) {
                    continue;
                }
                Method getter = property.getReadMethod();
                Object value = getter!=null ? getter.invoke(object) : null;
                map.put(key, value);
            }
            return map;
        }
    
        /**
         * map转对象
         * @param map
         * @param beanClass
         * @param <T>
         * @return
         * @throws Exception
         */
        publicstatic <T> T mapToBean(Map map, Class<T> beanClass) throws Exception {
            T object = beanClass.newInstance();
            BeanInfo beanInfo = Introspector.getBeanInfo(object.getClass());
            PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
            for (PropertyDescriptor property : propertyDescriptors) {
                Method setter = property.getWriteMethod();
                if (setter != null) {
                    setter.invoke(object, map.get(property.getName()));
                }
            }
            return object;
        }
    }

    2.4、利用 apache 中的 BeanUtils 进行转换

    在使用这个方法前,需要手动引入 apache 中的 beanutils 包,方法如下:

    <dependency>
        <groupId>commons-beanutils</groupId>
        <artifactId>commons-beanutils</artifactId>
        <version>1.9.3</version>
    </dependency>

    想必大家都不会陌生,操作如下:

    publicclass BeanMapUtilByApache {
    
        /**
         * 对象转Map
         * @param object
         * @return
         */
        public static Map beanToMap(Object object){
            returnnew org.apache.commons.beanutils.BeanMap(object);
        }
    
        /**
         * map转对象
         * @param map
         * @param beanClass
         * @param <T>
         * @return
         */
        publicstatic <T> T mapToBean(Map map, Class<T> beanClass) throws Exception {
            T object = beanClass.newInstance();
            org.apache.commons.beanutils.BeanUtils.populate(object, map);
            return object;
        }
    }

    2.5、利用 cglib 中的 BeanMap 进行转换

    在使用这个方法前,需要手动引入cglib 包,方法如下:

    <dependency>
        <groupId>cglib</groupId>
        <artifactId>cglib</artifactId>
        <version>3.3.0</version>
    </dependency>

    Spring 内置了 cglib,在项目中经常看到有同学用这个方法,操作如下:

    publicclass BeanMapUtilByCglib {
    
        /**
         * 对象转Map
         * @param object
         * @return
         */
        public static Map beanToMap(Object object){
            Map<String, Object> map = new HashMap<String, Object>();
            if (object != null) {
                BeanMap beanMap = BeanMap.create(object);
                for (Object key : beanMap.keySet()) {
                    map.put(key+"", beanMap.get(key));
                }
            }
            return map;
        }
    
        /**
         * map转对象
         * @param map
         * @param beanClass
         * @param <T>
         * @return
         * @throws Exception
         */
        publicstatic <T> T mapToBean(Map map, Class<T> beanClass) throws Exception {
            T bean = beanClass.newInstance();
            BeanMap beanMap = BeanMap.create(bean);
            beanMap.putAll(map);
            return bean;
        }
    }

    2.6、测试

    上面介绍完了操作,最后我们来测试一下,新建一个测试客户端TestClient,分别测试上面5个工具类

    publicclass BeanMapClient {
    
        /**
         * 测试
         * @param args
         */
        public static void main(String[] args) {
            //为了更贴近实际场景,构造嵌套实体对象
            User user = new User("1","张三");
            List<Role> roleList = new ArrayList<Role>();
            roleList.add(new Role("1","技术经理"));
            roleList.add(new Role("1","信息主任"));
            user.setRoleList(roleList);
            //bean转map
            Map userMap = BeanMapUtilByJson.beanToMap(user);
            System.out.println("转换后的map:" + JSON.toJSONString(userMap));
            //修改源对象中信息
            user.getRoleList().get(0).setRoleName("项目经理");
            user.getRoleList().add(new Role("1","项目经理"));
            //map转bean
            User newUser = BeanMapUtilByJson.mapToBean(userMap,User.class);
            System.out.println("转换后的bean:" + JSON.toJSONString(newUser));
        }
    }

    输出结果如下:

    对上面5中输出结果进行分析,可以得出如下结论:

    • 将对象转换成map,基本都一致,将map转成bean的时候,有些小区别;
    • 方法一,通过json转换后的bean对象,即使源对象发生改变,转换后的对象不会发生变化,这是因为json在底层转换bean时,都创建了新的对象;
    • 方法二、三、四、五,当源对象有嵌套对象,修改嵌套对象,也就是Role内容,转换后的对象也会随之发生改变;

    从结果可以看出,方法一、二、三、四、五,都可以进行对象与 map 的互转,那他们到底有啥区别呢?

    下面我们从性能角度来观察一下,使用for循环测试 map 与对象的互转,结果如下:

    可能每个机器的性能不一样,这个是我的电脑上测试的结果,从数据上可以看出:

    • 性能最好的是反射方法,其次就是内省方法,这两个方法都没有引用第三方jar包,所以相对可能要快点;
    • 使用第三方引用中,cglib 效率还可以,其次 apache的beanUtils工具包,最后就是Json包;

    三、总结

    如果对性能要求很高,可以采用 java 原生编程实现对象与 map 的互转,如果已经引用了 apache 或者 cglib 的jar,可以利用它提供的方法进行转换,优先推荐使用 cglib,因为 Spring 已经继承了 cglib,在项目中可以直接使用!

    原文链接:https://mp.weixin.qq.com/s/6dq6aKfV1GKLOvD-RGmcpw

  • 相关阅读:
    CodeForces 681D Gifts by the List (树上DFS)
    UVa 12342 Tax Calculator (水题,纳税)
    CodeForces 681C Heap Operations (模拟题,优先队列)
    CodeForces 682C Alyona and the Tree (树上DFS)
    CodeForces 682B Alyona and Mex (题意水题)
    CodeForces 682A Alyona and Numbers (水题,数学)
    Virtualizing memory type
    页面跳转
    PHP Misc. 函数
    PHP 5 Math 函数
  • 原文地址:https://www.cnblogs.com/yrjns/p/12373591.html
Copyright © 2011-2022 走看看