内容简介:版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zhangxin09/article/details/86670936
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zhangxin09/article/details/86670936
Map 是非常常见的一个数据结构,至于多常见则不再赘说了。框架无论大小,都会多少提供 Map 的相关 工具 方法,或进行封装。笔者在没用使用 Java 8 之前,也封装过,用了一段时间,如今 Java 8 问世几年,是时候对库改造一番了。
我们知道,String [] 有 join 的方法,把多个 String 转换为字符串,各个元素用 & 联结(或自定义字符),同样我们把该方法延伸到 Map 身上,于是有 join 的方法,
/** * Map 转换为 String * * @param map Map 结构,Key 必须为 String 类型 * @param div 分隔符 * @param fn 对 Value 的处理函数,返回类型 T * @return Map 序列化字符串 */ public static <T> String join(Map<String, T> map, String div, Function<T, String> fn) { String[] pairs = new String[map.size()]; int i = 0; for (String key : map.keySet()) pairs[i++] = key + "=" + fn.apply(map.get(key)); return String.join(div, pairs); }
测试:
Map<String, Object> map = new HashMap<String, Object>() { private static final long serialVersionUID = 1L; { put("foo", null); put("bar", 500); put("zx", "hi"); } }; @Test public void testJoin() { assertEquals("bar=500&foo=null&zx=hi", join(as(map, v -> v.toString()))); }
反之,将字符串转换为 Map,则有 toMap 方法,分别有以下两种情形:
/** * String[] 转换为 Map * * @param pairs 结对的字符串数组,包含 = 字符分隔 key 和 value * @param fn 对 Value 的处理函数,返回类型 Object * @return Map 对象 */ public static Map<String, Object> toMap(String[] pairs, Function<String, Object> fn) { if (CommonUtil.isNull(pairs)) return null; Map<String, Object> map = new HashMap<>(); for (String pair : pairs) { if (!pair.contains("=")) throw new IllegalArgumentException("没有 = 不能转化为 map"); String[] column = pair.split("="); if (column.length >= 2) map.put(column[0], fn == null ? column[1] : fn.apply(column[1])); else map.put(column[0], "");// 没有 等号后面的,那设为空字符串 } return map; } /** * String[] 转换为 Map,key 与 value 分别一个数组 * * @param columns 结对的键数组 * @param values 结对的值数组 * @param fn 对 Value 的处理函数,返回类型 Object * @return Map 对象 */ public static Map<String, Object> toMap(String[] columns, String[] values, Function<String, Object> fn) { if (CommonUtil.isNull(columns)) return null; if (columns.length != values.length) throw new UnsupportedOperationException("两个数组 size 不一样"); Map<String, Object> map = new HashMap<>(); int i = 0; for (String column : columns) map.put(column, fn.apply(values[i++])); return map; }
测试:
@Test public void testToMap() { assertEquals(1, MapTool.toMap(new String[] { "a", "b", "d" }, new String[] { "1", "c", "2" }, MappingValue::toJavaValue).get("a")); assertEquals(1, MapTool.toMap(new String[] { "a=1", "b=2", "d=c" }, MappingValue::toJavaValue).get("a")); assertEquals("你好", MapTool.toMap(new String[] { "a=%e4%bd%a0%e5%a5%bd", "b=2", "d=c" }, Encode::urlDecode).get("a")); }
值得一提的是,MappingValue::toJavaValue 能把字符串还原为 Java 里面的真实值,如 “true”–true,“123”–123,“null”–null,源码如下,
/** * 把字符串还原为 Java 里面的真实值,如 "true"--true,"123"--123,"null"--null * * @param value 字符串的值 * @return Java 里面的值 */ public static Object toJavaValue(String value) { if (value == null) return null; value = value.trim(); if ("".equals(value)) return ""; if ("null".equals(value)) return null; if ("true".equalsIgnoreCase(value)) return true; if ("false".equalsIgnoreCase(value)) return false; // try 比较耗资源,先检查一下 if (value.charAt(0) == '-' || (value.charAt(0) >= '0' && value.charAt(0) <= '9')) try { int int_value = Integer.parseInt(value); if ((int_value + "").equals(value)) // 判断为整形 return int_value; } catch (NumberFormatException e) {// 不能转换为数字 } return value; }
代码比较简单,主要是结合了 Java 8 特性发挥,理解函数可以作为变量“传来传去”就好了。
万能 Map 泛型转换器
为了转换泛型,如 Map<String, Object>
与 Map<String, String>
之间的互转,提供了该方法——说“万能”的口气好像比较大,但扒开源码呢,还是没啥技术含量的,顶多使用了 Function<K, T> fn
函数接口。源码如下,
/** * 万能 Map 转换器,为了泛型的转换而设的一个方法,怎么转换在 fn 中处理 * * @param map 原始 Map,key 必须为 String 类型 * @param fn 转换函数 * @return */ public static <T, K> Map<String, T> as(Map<String, K> map, Function<K, T> fn) { Map<String, T> _map = new HashMap<>(); for (String key : map.keySet()) { K value = map.get(key); _map.put(key.toString(), value == null ? null : fn.apply(value)); } return _map; }
需要注意的是 key 必须为 String 类型。如果不限制,应该也是可以,代码就要复杂一点,当前先不考虑复杂的情况。
测试:
@Test public void testToMap() { assertEquals(1, MapTool.toMap(new String[] { "a", "b", "d" }, new String[] { "1", "c", "2" }, MappingValue::toJavaValue).get("a")); assertEquals(1, MapTool.toMap(new String[] { "a=1", "b=2", "d=c" }, MappingValue::toJavaValue).get("a")); assertEquals("你好", MapTool.toMap(new String[] { "a=%e4%bd%a0%e5%a5%bd", "b=2", "d=c" }, Encode::urlDecode).get("a")); } @Test public void testAsString() { assertEquals("500", as(map, v -> v.toString()).get("bar")); assertEquals("[1, c, 2]", as(new HashMap<String, String[]>() { private static final long serialVersionUID = 1L; { put("foo", new String[] { "a", "b" }); put("bar", new String[] { "1", "c", "2" }); } }, v -> Arrays.toString(v)).get("bar")); }
Map 与 Bean 的转换
Java Bean 又称 POJO,可以没有任何集成,所以根类是 Object。JDK 自带 Bean “内省”,那样就无须经过反射了。我们先把遍历 bean 各个字段的逻辑抽出来,
@FunctionalInterface public static interface EachFieldArg { public void item(String key, Object value, PropertyDescriptor property); } /** * 遍历一个 Java Bean * * @param bean Java Bean * @param fn 执行的任务,参数有 key, value, property */ public static void eachField(Object bean, EachFieldArg fn) { try { BeanInfo beanInfo = Introspector.getBeanInfo(bean.getClass()); for (PropertyDescriptor property : beanInfo.getPropertyDescriptors()) { String key = property.getName(); // 得到 property 对应的 getter 方法 Method getter = property.getReadMethod(); Object value = getter.invoke(bean); fn.item(key, value, property); } } catch (IntrospectionException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { LOGGER.warning(e); } }
遍历本身足够简单,唯一亮点是自定义函数接口的使用:@FunctionalInterface。当 JDK 自带的 Supply、Function、Consumer 参数不能满足需求时,自定义函数接口就发挥作用了,例如 public void item(String key, Object value, PropertyDescriptor property); 我们一下子安排了三个参数。
接下来的事情就好办,无法获取值,设置值,交换数据,还有一些细节问题处理就是了。
/** * Map 转为 Bean * * @param map 原始数据 * @param clz 实体 bean 的类 * @param isTransform 是否尝试转换值 * @return 实体 bean 对象 */ public static <T> T map2Bean(Map<String, ?> map, Class<T> clz, boolean isTransform) { T bean = ReflectUtil.newInstance(clz); eachField(bean, (key, v, property) -> { try { if (map.containsKey(key)) { Object value = map.get(key); // null 是不会传入 bean 的 if (value != null) { Class<?> t = property.getPropertyType(); // Bean 值的类型,这是期望传入的类型,也就 setter 参数的类型 if (isTransform && value != null && t != value.getClass()) { // 类型相同,直接传入;类型不相同,开始转换 value = MappingValue.objectCast(value, t); } property.getWriteMethod().invoke(bean, value); } } // 子对象 for (String mKey : map.keySet()) { if (mKey.contains(key + '_')) { Method getter = property.getReadMethod(), setter = property.getWriteMethod();// 得到对应的 setter 方法 Object subBean = getter.invoke(bean); String subBeanKey = mKey.replaceAll(key + '_', ""); if (subBean != null) {// 已有子 bean if (map.get(mKey) != null) // null 值不用处理 ReflectUtil.setProperty(subBean, subBeanKey, map.get(mKey)); } else { // map2bean Map<String, Object> subMap = new HashMap<>(); subMap.put(subBeanKey, map.get(mKey)); subBean = map2Bean(subMap, setter.getParameterTypes()[0], isTransform); setter.invoke(bean, subBean); // 保存新建的 bean } } } } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { LOGGER.warning(e); } }); return bean; } /** * map 转实体 * * @param map 原始数据 * @param clz 实体 bean 的类 * @return 实体 bean 对象 */ public static <T> T map2Bean(Map<String, ?> map, Class<T> clz) { return map2Bean(map, clz, false); } /** * Bean 转为 Map * * @param bean 实体 bean 对象 * @return Map 对象 */ public static <T> Map<String, Object> bean2Map(T bean) { Map<String, Object> map = new HashMap<>(); eachField(bean, (k, v, property) -> { if (!k.equals("class")) // 过滤 class 属性 map.put(k, v); }); return map; }
测试:
public static Map<String, Object> userWithoutChild = new HashMap<String, Object>() { private static final long serialVersionUID = 1L; { put("id", 1L); put("name", "Jack"); put("age", 30); put("birthday", new Date()); } }; public static class MapMock { static boolean s = true; public static Map<String, Object> user = new HashMap<String, Object>() { private static final long serialVersionUID = 1L; { put("id", 1L); put("name", "Jack"); put("sex", s); put("age", 30); put("birthday", new Date()); put("children", "Tom,Peter"); put("luckyNumbers", "2, 8, 6"); } }; } @Test public void testMap2Bean() { TestCaseUserBean user = MapTool.map2Bean(userWithoutChild, TestCaseUserBean.class);// 直接转 assertNotNull(user); assertEquals(user.getName(), "Jack"); user = MapTool.map2Bean(MapMock.user, TestCaseUserBean.class, true); assertNotNull(user); assertEquals("Tom", user.getChildren()[0]); assertEquals(8, user.getLuckyNumbers()[1]); assertEquals(true, user.isSex()); } @Test public void testBean2Map() { TestCaseUserBean user = MapTool.map2Bean(MapMock.user, TestCaseUserBean.class, true); Map<String, Object> map = MapTool.bean2Map(user); assertNotNull(map); assertEquals("Jack", map.get("name")); }
XML 与 Bean 互转
XML 部分的代码 copy 第三方代码,没什么好说了。我一向主张使用自带的库,使用直接使用了 Java W3C Dom 解析,小型 XML 文件解析足够了。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Web Development Recipes
Brian P. Hogan、Chris Warren、Mike Weber、Chris Johnson、Aaron Godin / Pragmatic Bookshelf / 2012-1-22 / USD 35.00
You'll see a full spectrum of cutting-edge web development techniques, from UI and eye candy recipes to solutions for data analysis, testing, and web hosting. Make buttons and content stand out with s......一起来看看 《Web Development Recipes》 这本书的介绍吧!