Java编程经验---比较两个List对象差异
- 问题引入
- 解决问题
- 简化模型
- 一般的办法
- 速度更快的方法
- Lambda表达式解决办法
- 结语
问题引入
如何比较两个List对象的差异,这个问题来源于我最近正在开发的新系统中的一个细节。大致情况就是,从数据库中的一个视图A向另一个数据库的一张B表进行数据迁移。A的数据会随时更新,为了保证表B也可以及时获取数据,需要采用定时任务,不断同步数据。
每N分钟
视图A
表B
视图A中的数据在导入表B时,可能有数据已经在表B中,重复的导入浪费性能且可能发生潜在错误。那么就需要分析数据的差异后进行导入。先设一个前提,视图A与表B的结构相似,Primary Key为表中的某一单字段。
视图A示例
UserId | UserName | Status | … |
1 | Mike | OK | … |
2 | Trump | NO | … |
3 | LiLi | OK | … |
表B示例
UserId | UserName | Gender | … |
1 | Mike | Men | … |
2 | Trump | Women | … |
5 | Chou | Men | … |
*这里的 UserId 时 Primery Key
解决问题
简化模型
我们可以先将问题简化为两个基本类型的List,举个最简单的例子就是两个List<int>。
一般的办法
如何对比两个List的差异,最简单粗暴的当然就是使用两个for循环。
public static void main(String[] args) {
int i = 0 ;
//对比ListA和ListB的差异
List<Integer> ListA = new LinkedList();
List<Integer> ListB = new LinkedList();
//获取相同的元素放入ListC
List<Integer> ListC = new LinkedList();
while( i<10000){
ListA.add(i);
i++;
}
i = 5;
while( i<10005){
ListB.add(i);
i++;
}
//程序开始时间
long st = System.nanoTime();
//取得相同的元素
for(int aIndex: ListA)
{
for(int bIndex: ListB)
{
if(aIndex == bIndex)
{
ListC.add(aIndex);
}
}
}
//分辨结束
System.out.println("total times "+(System.nanoTime()-st));
ListA.removeAll(ListC);
ListB.removeAll(ListC);
ListA.addAll(ListB);
System.out.println(ListA.toString()); //打印差异元素
}
结果
total times 362768500
[0, 1, 2, 3, 4, 10000, 10001, 10002, 10003, 10004]
此时的算法复杂度为
速度更快的方法
空间换时间
public class Test {
public static void main(String[] args) {
int i = 0 ;
//对比ListA和ListB的差异
List<Integer> ListA = new LinkedList();
List<Integer> ListB = new LinkedList();
//获取相同的元素放入ListC
List<Integer> ListC = new LinkedList();
while( i<10000){
ListA.add(i);
i++;
}
i = 5;
while( i<10005){
ListB.add(i);
i++;
}
Map<Integer,Integer> listMap = new HashMap();
//程序开始时间
long st = System.nanoTime();
//取得相同的元素
Iterator<Integer> iteratorA = ListA.iterator();
while(iteratorA.hasNext())
{
Integer tempInteger = iteratorA.next();
listMap.put(tempInteger.intValue(),tempInteger);
}
Iterator<Integer> iteratorB = ListB.iterator();
while(iteratorB.hasNext())
{
Integer tempInteger = iteratorB.next();
if(listMap.containsKey(tempInteger))
{
ListC.add(tempInteger);
}
}
//分辨结束
System.out.println("total times "+(System.nanoTime()-st));
ListA.removeAll(ListC);
ListB.removeAll(ListC);
ListA.addAll(ListB);
System.out.println(ListA.toString()); //打印差异元素
}
}
结果
total times 2537200
[0, 1, 2, 3, 4, 10000, 10001, 10002, 10003, 10004]
做了一点改善,将原来的For循环用迭代来完成遍历,速度快了20%
此时的算法复杂度为
很明显引入HashMap之后,算法复杂度大幅下降。这是由于在HashMap中查找元素的算法复杂度可以达到恐怖的O(1)。
特别说明
Java 8 之后,HashMap引入了红黑树存储,使得存储效率进一步提升,触发条件就是List长度大于8。
Lambda表达式解决办法
public static void main(String[] args) {
int i = 0 ;
//对比ListA和ListB的差异
List<Integer> ListA = new ArrayList();
List<Integer> ListB = new ArrayList();
//获取相同的元素放入ListC
List<Integer> ListC = new ArrayList();
while( i<10000){
ListA.add(i);
i++;
}
i = 5;
while( i<10005){
ListB.add(i);
i++;
}
//程序开始时间
long st = System.nanoTime();
//取得相同的元素
//采用Lambda表达式实现
ListC = ListA.stream().filter(p -> ListB.contains((Integer)p)).collect(Collectors.toList());
//分辨结束
System.out.println("total times "+(System.nanoTime()-st));
ListA.removeAll(ListC);
ListB.removeAll(ListC);
ListA.addAll(ListB);
System.out.println(ListA.toString()); //打印差异元素
}
先放结果吧
total times 41008300
[0, 1, 2, 3, 4, 10000, 10001, 10002, 10003, 10004]
总的来说还是要比第一种方法要快5,6倍,但是和使用HashMap还是有一定的差距。但是很明显语句非常的简洁。所以适当使用Lambda表达式可以使代码更加优雅。但是处理大量的数据时,效率不佳,不推荐。
结语
点滴积累,才能扎实成长。
时间有限,如有问题,及时更正。