背景
最近在开发一个数据对比的worker,内容是对比MySQL中的数据是否和ElasticSearch中的数据一致,其中就涉及到List类型的数据对比,有个比较头痛的点,就是要求不考虑集合内元素的顺序。
我们都知道,List是一个有序的集合,简单重写equals、hashCode方法,是会存在元素顺序的判断的,故我们需要写一个专门的方法来进行对比。
方案
方案很简单,对比的时候分4个步骤:
- 重写List里边对象的equals、hashCode方法
- 非空判断——null
- 元素个数判断——size
- 元素全部包含判断——containAll
关键就是后面3步吧,前面属于常规操作,下面我以一个例子来说明
(1)含有List类型的对象
package com.example.springbootcomparer.domain;
import lombok.Data;
import java.util.Date;
import java.util.List;
/**
* 书
*
* @author hongcunlin
*/
@Data
public class Book {
/**
* 主键
*/
private Long id;
/**
* 书类型
*/
private Integer type;
/**
* 书名
*/
private String name;
/**
* 创建时间
*/
private Date createTime;
/**
* 更新时间
*/
private Date updateTime;
/**
* 书扩展字段
*/
private List<BookExt> bookExtList;
}
如上面的bookExtList字段,该字段为List类型
(2)重写List里边对象的equals、hashCode方法
package com.example.springbootcomparer.domain;
import lombok.Data;
import java.util.Date;
import java.util.Objects;
/**
* 书扩展
*
* @author hongcunlin
*/
@Data
public class BookExt {
/**
* 主键
*/
private Long id;
/**
* 创建类型
*/
private Integer type;
/**
* 内容
*/
private String value;
/**
* 创建时间
*/
private Date createTime;
/**
* 更新时间
*/
private Date updateTime;
/**
* 1.1.重写equals
*
* @param o 对象
* @return 是否相等
*/
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
BookExt ext = (BookExt) o;
return Objects.equals(id, ext.id) && Objects.equals(type, ext.type) && Objects.equals(value, ext.value) && Objects.equals(createTime, ext.createTime) && Objects.equals(updateTime, ext.updateTime);
}
/**
* 1.2.重写hashCode
*
* @return hash值
*/
@Override
public int hashCode() {
return Objects.hash(id, type, value, createTime, updateTime);
}
}
如我们重写了作为List类型的元素BookExt的equals、hashCode两个方法,而编写这两个方法,可以通过IDEA的Generate自动生成。
(3)编写对比服务
package com.example.springbootcomparer.service.impl;
import com.example.springbootcomparer.domain.Book;
import com.example.springbootcomparer.domain.BookExt;
import com.example.springbootcomparer.service.BookCompareService;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Objects;
/**
* @author hongcunlin
*/
@Service
public class BookCompareServiceImpl implements BookCompareService {
@Override
public boolean compare(Book source, Book target) {
if (source == null) {
return target == null;
} else {
if (target == null) {
return false;
}
return Objects.equals(source.getId(), target.getId()) &&
Objects.equals(source.getType(), target.getType()) &&
Objects.equals(source.getName(), target.getName()) &&
Objects.equals(source.getCreateTime(), target.getCreateTime()) &&
Objects.equals(source.getUpdateTime(), target.getUpdateTime()) &&
compareList(source.getBookExtList(), target.getBookExtList());
}
}
/**
* List类型不考虑顺序,比较是否一样
*
* @param source 源
* @param target 目标
* @return 是否一样
*/
private boolean compareList(List<BookExt> source, List<BookExt> target) {
// 2.非空判断
if (source == null) {
return target == null;
} else {
if (target == null) {
return false;
}
// 2.元素个数判断
if (source.size() != target.size()) {
return false;
}
// 3.元素全部包含判断
return source.containsAll(target);
}
}
}
可以看到,首先是进行基础字段的对比,使用Objects类提供的静态方法equals。接着是核心的List类型字段的对比,再compareList中,按照我们说的步骤来。
测试
我们可以编写一个测试方法,来验证下我们的,想法,结果是符合要求的
package com.example.springbootcomparer;
import com.example.springbootcomparer.domain.Book;
import com.example.springbootcomparer.domain.BookExt;
import com.example.springbootcomparer.service.BookCompareService;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import javax.annotation.Resource;
import java.util.*;
@SpringBootTest
class SpringBootComparerApplicationTests {
@Resource
private BookCompareService compareService;
@Test
void contextLoads() {
// true
System.out.println(compareService.compare(buildBookA(), buildBookB()));
}
private Book buildBookA() {
Book book = new Book();
book.setId(1L);
book.setType(1);
book.setName("hello");
book.setCreateTime(new Date());
book.setUpdateTime(new Date());
List<BookExt> bookExt = new ArrayList<>();
// 顺序不一致
bookExt.add(buildExtA());
bookExt.add(buildExtB());
book.setBookExtList(bookExt);
return book;
}
private Book buildBookB() {
Book book = new Book();
book.setId(1L);
book.setType(1);
book.setName("hello");
book.setCreateTime(new Date());
book.setUpdateTime(new Date());
List<BookExt> bookExt = new ArrayList<>();
// 顺序不一致
bookExt.add(buildExtB());
bookExt.add(buildExtA());
// bookExtList.add(buildExtB());
book.setBookExtList(bookExt);
return book;
}
private BookExt buildExtA() {
BookExt ext = new BookExt();
ext.setId(1L);
ext.setValue("hello");
return ext;
}
private BookExt buildExtB() {
BookExt ext = new BookExt();
ext.setId(2L);
ext.setValue("hi");
return ext;
}
}
可以看到,两个对象的比较,我们只是让它们的List字段里边的元素顺序不一致而已,其他的都一致,判断是否相等的结果是true,符合我们预期的结果。
最后
本代码已经上传到github上了,有需要的同学可以自行下载查阅
https://github.com/larger5/SpringBootComparer