一、摘要
在實際開發過程中,經常碰到需要進行對象與map之間互轉的問題,其實對於對象、Map 之間進行互轉有很多種方式,下面我們一起來梳理一下:
- 利用 JSON 工具包,將對象轉成字符串,之后再轉成 Map,這種需要轉換2次,相對來說效率比較底;
- 利用 Java 反射,獲取 Bean 類的屬性和值,再轉換到 Map 對應的鍵值對中,相對來說這種方法效率高些,在實現上比較麻煩;
- 利用 Java 的內省(Introspector) 實現,獲取 Bean 類的屬性和值,Map與對象互轉,效率比較高;
- 利用 apache 中的
BeanUtils
工具包進行操作,底層實現類似方法三; - 利用
net.sf.cglib.beans.BeanMap
類中的方法,這種方式效率也非常高;
二、常用方法
也不多說了,直接show code
,為了更接近實際場景,我們新建兩個實體類User
、Role
,假設一個用戶有多個角色,如下:
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