1. 问题的源由

在J2EE项目开发中,会涉及很多领域模型对象,例如,

VO (View Object) 视图对象,也叫展示对象,用于前端页面渲染所需要的数据

DTO (Data Transfer Object) 数据传输对象,一般用于Service和Manager向外传输数据

PO (Persistent Object) 持久化对象,一般和数据库表结构会形成一一映射关系,通过DAO层向上传输数据源对象

更多领域分层模型,及其所在的应用分层,可见阿里巴巴Java开发手册中的工程规约定义。

这些VO/DTO/PO都是POJO对象,每个属性都有getter/setter方法的定义,其数据转换链条大概为,

数据库DB 》PO 》DTO 》VO

可以看到,在项目中不同的应用分层DB/DAO/Service/Manager/Web,经常会需要把一个数据对象转换为其它类型的数据对象,转换的同时复制部分对象属性,有时部分属性需要从多个数据对象获取进行组装。这些数据的转换有一个共同的需求,那就是从一个源对象变为目标对象

源对象 Source s 》目标对象 Target t

接下来我们就需要讨论这个转换过程。

2. 单个对象的转换

1)简单的Java对象转换,使用BeanUtils.copyProperties

使用Spring都知道Spring Beans中提供了一个BeanUtils,可以用于对象的属性复制,其使用方法为,

Target t = new Target();

BeanUtils.copyProperties(s, t);

BeanUtils.copyProperties(s, t, 'password');

其中,第二个copyProperties方法中第三个参数是告知复制过程中忽略复制'password'这个属性。

2)单个对象的转换,指定目标类

BeanUtils.copyProperties不提供目标对象的创建,因此每次都需要new出一个目标对象。为了能够省去这一步,可以使用下面的方法。

实现代码

public static T convert(S s, Class clazz) {

T t = null;

try {

t = clazz.newInstance();

BeanUtils.copyProperties(s, t);

} catch (Exception e) {

e.printStackTrace();

}

return t;

}

上述方法将对象的生成封装起来返回。

使用样例

Target t = convert(s, Target.class);

使用中直接指定目标类,代码更加简洁易懂。

3. List/Set集合之间的转换

在很多时候,我们不仅处理一个对象的转换,而是面对集合的对象,这时需要能够对集合中的对象进行批量处理转换。

1)List集合转换,将列表中每一个实体转换为目标类

实现代码

public static List convert(Iterable iterable, Class clazz) {

return StreamSupport.stream(iterable.spliterator(), false)

.map(s -> convert(s, clazz))

.collect(Collectors.toList());

}

使用样例

List targets = convert(srcCollection, Target.class);

2)List集合转换,提供一个mapper转换方法,将列表中每一个实体对象转换为目标类

实现代码

public static List convert(Iterable iterable,

Function super S, ? extends T> mapper) {

return StreamSupport.stream(iterable.spliterator(), false)

.map(mapper)

.collect(Collectors.toList());

}

使用样例

List targets = convert(srcCollection, mapper);

其中mapper的定义为Function super S, ? extends T>,即该函数需要一个源对象作为输入参数,返回一个目标对象

3)List集合转换,转换为指定的List/Set容器类,集合中的实体对象不变

实现代码

public static > U collect(Iterable iterable, CollectionFactory factory) {

U collection = factory.createCollection();

iterable.forEach(collection::add);

return collection;

}

其中CollectionFactory为一个集合类工厂。

使用样例

Iterable iterable = IntStream.range(0, 5).boxed().collect(Collectors.toList());

ArrayList arrayList = collect(iterable, ArrayList::new);

HashSet hashSet = collect(iterable, HashSet::new);

LinkedList linkedList = collect(iterable, LinkedList::new);

上述将一个Collection集合转换为ArrayList/HashSet/LinkedList,集合中的整型变量对象保持不变。

4)List集合转换,转换为指定的List/Set容器类,并将集合中的实体对象转换为目标类

实现代码

public static , T> U collect(Iterable iterable, Class clazz, CollectionFactory factory) {

Iterable list = convert(iterable, clazz);

U collection = factory.createCollection();

list.forEach(collection::add);

return collection;

}

使用样例

Iterable users = UserDao.SingleInstance.findAll();

ArrayList arrayList = collect(users, UserVO.class, ArrayList::new);

HashSet hashSet = collect(users, UserVO.class, HashSet::new);

LinkedList linkedList = collect(users, UserVO.class, LinkedList::new);

上述将一个Collection集合转换为ArrayList/HashSet/LinkedList,同时集合中的UserEntity变量对象转换为了UserVO。

4. List/Set/Map之间的转换

Java中Map的处理效率比List/Set高效不少,有些时候需要在List/Set/Map之间进行转换。

1)List集合转换,转换为Map集合,提供一个keyGenerator生成key,实体对象保存为value

实现代码

public static Map map(Iterable iterable, Function super S, String> keyGenerator) {

return StreamSupport.stream(iterable.spliterator(), false)

.collect(Collectors.toMap(keyGenerator, Function.identity()));

}

使用样例

List users = UserDao.SingleInstance.findAll();

Map result = map(users, UserEntity::getName);

上述样例中由UserEntity::getName来生成对象的键值,其必须为保证唯一。

2)Map集合转换,转换为List集合,实体对象保持不变

实现代码

public static > U collect(Map map, CollectionFactory factory) {

U collection = factory.createCollection();

map.values().iterator().forEachRemaining(collection::add);

return collection;

}

使用样例

Map users;

List list1 = collect(users, ArrayList::new);

Set set = collect(users, HashSet::new);

List list2 = collect(users, LinkedList::new);

Collection collection = users.values();

上述将一个用户的Map集合转换为List集合,可以看到一个简单获得List集合的方法就是使用map.values()方法。

3)Map集合转换,转换为指定的Map容器类

实现代码

public static > U map(Map srcMap, Class clazz, MapFactory factory) {

U u = factory.createMap();

srcMap.entrySet()

.forEach(ksEntry -> u.put(ksEntry.getKey(),

convert(ksEntry.getValue(), clazz)));

return u;

}

使用样例

Map users;

Map m1 = map(users, UserVO.class, HashMap::new);

Map m2 = map(users, UserVO.class, LinkedHashMap::new);

Hashtable m3 = map(users, UserVO.class, Hashtable::new);

上述将一个用户的Map集合转换为List集合,可以看到一个简单获得List集合的方法就是使用map.values()方法。

5. 源代码

演示代码仓库地址:https://gitee.com/pphh/blog,可以通过如下git clone命令获取仓库代码,

git clone git@gitee.com:pphh/blog.git

本篇博文的程序代码样例在文件路径171201_java_object_copy\171201_java_object_copy_convert中,请使用JDK8及后续版本编译并运行项目代码。

6. 参考资料

POJO阿里巴巴Java开发手册(2017版本):应用分层和领域模型PO/DTO/VO

java steam reduce_Java Stream相关推荐

  1. java steam reduce_Java 8新特性:Stream API与Date API

    除此之外,Java8 API中包含了很多内建的函数式接口,这些接口都增加了@FunctionalInterface注解以便能用在lambda表达式上.它还提供了很多全新的函数式接口来让工作更加方便,其 ...

  2. Java函数式编程教程(五):Java Steam API

    译:GentlemanTsao,2020-7-7 文章目录 Java Steam 定义 Steam 处理 获取Steam 终结操作和中间操作 中间操作 filter() map() flatMap() ...

  3. Java Steam详解

    Steam概述 Stream是 Java 8新增加的类,用来补充集合类. Stream代表数据流,流中的数据元素的数量可能是有限的,也可能是无限的. Java Stream提供了提供了串行和并行两种类 ...

  4. 使用java Steam流进行逻辑分页(内存分页)

    物理分页与逻辑分页 物理分页 物理分页依赖的是某一物理实体,这个物理实体就是数据库,比如MySQL数据库提供了limit关键字,程序员只需要编写带有limit关键字的SQL语句,数据库返回的就是分页结 ...

  5. Java基础(十一) Stream I/O and Files

    Java基础(十一) Stream I/O and Files 1. 流的概念 程序的主要任务是操纵数据.在Java中,把一组有序的数据序列称为流. 依据操作的方向,能够把流分为输入流和输出流两种.程 ...

  6. 造成java.io.IOException: Stream Closed异常的代码

    造成java.io.IOException: Stream Closed异常的代码 参考文章: (1)造成java.io.IOException: Stream Closed异常的代码 (2)http ...

  7. 【java笔记】Stream流(2):获取流的两种方法

    获取一个流通常有两种方式: ●所以的Collection集合都可以通过stream默认方法获取流 ●Stream接口的静态方法of可以获取数组对应的流 根据Collection集合获取Stream流 ...

  8. 【java笔记】Stream流(1)你知道什么叫Stream流吗?

    JDK8 中的 Stream 是对集合(Collection)对象功能的增强,它借助于lambda表达式,更优雅的表达风格,极大的提高编程效率和程序可读性.它针对于对集合对象进行各种非常便利.高效的聚 ...

  9. java day24【Stream流、方法引用】

    第一章 Stream流 说到Stream便容易想到I/O Stream,而实际上,谁规定"流"就一定是"IO流"呢?在Java 8中,得益于Lambda所带来的 ...

最新文章

  1. gensim的word2vec如何得出词向量(python)
  2. 墨迹天气: 更重视商业数据的公共价值
  3. python迭代器和生成器_python中迭代器和生成器。
  4. Python3 websocket server与client
  5. python 打包 .app 运行 控制台窗口_Python打包工具PyInstaller的安装与pycharm配置支持PyInstaller详细方法...
  6. 动态添加的路由 直接访问_VUE 动态路由(二)
  7. access开发精要(9)-排序
  8. java正则表达式判断_Java正则表达式判断
  9. html5语异性元素,异性的5句性暗示
  10. c语言编程三问三答,c语言程序编程
  11. APPCAN学习笔记003---原生开发与HTML5技术
  12. 谈MicroMessageTest的开始创建
  13. ubuntu下用postfix搭建邮件服务器
  14. js 里不能使用${pageContext.request.contextPath}解决方案
  15. fpgrowth算法实战 mlib_sparkmllib关联规则算法(FPGrowth,Apriori)
  16. Windows无法安装到磁盘磁盘具有MBR分区表的解决
  17. 微信企业号开发—发送消息
  18. RFC 822 中文版 MIME解析基础(4)(第5-6也)
  19. 命令行运行matlab的方法
  20. 2019计算机小高考成绩,小高考没过怎么办 2021小高考难度如何

热门文章

  1. 华为内部LINUX学习资料(PDF格式)
  2. 訾博 2021年8月16日 周一 雨 芹菜火腿刀削面
  3. 应用在智能手表中的加密设置
  4. Android 原型设计模式
  5. Word在写文章、写论文时出现吞字的情况-解决办法
  6. js 字符截取 substr substring slice 兼容对比
  7. 书论11 王羲之《笔势论十二章》
  8. 推荐6个高颜值 Linux 桌面主题
  9. 采访大学计算机专业老师,放飞梦想   活出精彩——计算机系陈琳琳老师访谈录...
  10. CAD设置点的样式(网页版)