优秀的编程知识分享平台

网站首页 > 技术文章 正文

JAVA轮询遍历两个数组进行比较(遍历数组 java)

nanyue 2024-09-20 21:56:13 技术文章 3 ℃

现在有一个需求,我会从两个不同的服务器获取到两个数组A和B,两个数组都是相同的对象格式,其中包含一个相同的主键ID。我们要将数组A和B进行比较,假设A是主数组,B是新数组。然后执行操作:

1、A的一个ID在B里不存在,执行delete()方法。

2、B数组中的一个ID在A数组里不存在,执行add()方法。

3、B数组中的一个ID在A数组中存在,但是内容不同,则执行update()方法。

首先,我想到使用HashSet集合存储数据,然后轮询遍历,根据之前项目的需求量,设置A为8000条数据,B为8010条数据,通过循环赋值到一个Set中。A数组起名为oldList,B数组起名为newList然后对性能进行计时计算,代码如下


  1. public class Test1 {
  2. public static void main(String[] args) {
  3. Set<Client> oldList = new HashSet<Client>();
  4. Set<Client> newList = new HashSet<Client>();
  5. int updateNum = 0;
  6. int addNum = 0;
  7. for (int i = 0; i < 8000; i++) {
  8. oldList.add(new Client(i+"", "客户"+i, "地址"+i, "CID"+i));
  9. }
  10. for (int i = 0; i < 8010; i++) {
  11. if(i%179==0) {
  12. newList.add(new Client("", "客户信息"+i, "地址信息"+i, "CID"+i));
  13. }else {
  14. newList.add(new Client(i+"", "客户"+i, "地址"+i, "CID"+i));
  15. }
  16. }
  17. long start = new Date().getTime();
  18. long initTime = new Date().getTime();
  19. System.out.println("初始化时间:"+(initTime-start));
  20. for(Client newC:newList) {
  21. boolean newhas = false;
  22. for(Client oldC:oldList) {
  23. if(newC.getClientId().equals(oldC.getClientId())) {
  24. newhas = true;
  25. if(oldC.getAddress().equals(newC.getAddress()) && oldC.getName().equals(newC.getName())) {
  26. break;
  27. }else {
  28. updateNum++;
  29. update();
  30. }
  31. }
  32. }
  33. if(!newhas) {
  34. //oldList.add(new Client((oldList.size()+1)+"", newC.getName(), newC.getAddress(), newC.getClientId()));
  35. addNum++;
  36. add();
  37. }
  38. }
  39. long end = new Date().getTime();
  40. System.out.println("总共用时:"+(end-start)+"ms,执行用时:"+(end-initTime)+"ms,新增了:"+addNum+"条,修改了:"+updateNum+"条");
  41. }
  42. public static void update() {
  43. }
  44. public static void add() {
  45. }
  46. }

第一次执行:

初始化时间:0

总共用时:884ms,执行用时:884ms,新增了:10条,修改了:45条

第二次执行:

初始化时间:0

总共用时:905ms,执行用时:905ms,新增了:10条,修改了:45条

第三次执行:

初始化时间:0

总共用时:871ms,执行用时:871ms,新增了:10条,修改了:45条

其中for循环往Set中存数据的过程不在计时的时间内。可以看到,8000条数据需要将近1秒的时间遍历出来,从现阶段可能还算够用,但是长久考虑,数据量越来越大(往后可能百万级),这个肯定不行。我们试一下2W条数据:

初始化时间:0

总共用时:11082ms,执行用时:11082ms,新增了:0条,修改了:112条

实在是长的不行。然后我就分析到底是哪里导致性能这么差,我想是因为多次无意义的循环导致的吧。然后我突然想起了Map集合可以判断key是否存在,那我把主键ID存入key中,value做为这个对象,来使用呢?然后完成如下代码:


  1. public class Test3 {
  2. public static void main(String[] args) {
  3. Set<Client> oldList = new HashSet<Client>();
  4. Set<Client> newList = new HashSet<Client>();
  5. int updateNum = 0;
  6. int addNum = 0;
  7. int deleteNum = 0;
  8. for (int i = 0; i < 8000; i++) {
  9. Client client = new Client(i+"", "客户"+i, "地址"+i, "CID"+i);
  10. oldList.add(client);
  11. }
  12. for (int i = 0; i < 8010; i++) {
  13. if(i%5657==0) {
  14. Client client = new Client("", "客户信息"+i, "地址信息"+i, "CID"+i);
  15. newList.add(client);
  16. }else if(i%9973==0){
  17. continue;
  18. }else {
  19. Client client = new Client(i+"", "客户"+i, "地址"+i, "CID"+i);
  20. newList.add(client);
  21. }
  22. }
  23. long start = new Date().getTime();
  24. Map<String,Client> newMap = new HashMap<String,Client>();
  25. Map<String,Client> oldMap = new HashMap<String,Client>();
  26. for (Client n : newList) {
  27. newMap.put(n.getClientId(), n);
  28. }
  29. for (Client n : oldList) {
  30. oldMap.put(n.getClientId(), n);
  31. }
  32. long initTime = new Date().getTime();
  33. System.out.println("初始化时间:"+(initTime-start)+"ms");
  34. Set<String> newKey = newMap.keySet();
  35. Set<String> oldKey = oldMap.keySet();
  36. for (String key : newKey) {
  37. if(oldMap.containsKey(key)) {
  38. Client o = oldMap.get(key);
  39. Client n = newMap.get(key);
  40. if(!(o.getAddress().equals(n.getAddress()) && o.getName().equals(n.getName()))) {
  41. updateNum++;
  42. update();
  43. }
  44. }else {
  45. addNum++;
  46. add();
  47. }
  48. }
  49. long addUpdateTime = new Date().getTime();
  50. System.out.println("新增和修改时间:"+(addUpdateTime-initTime)+"ms");
  51. for (String key : oldKey) {
  52. if(!newMap.containsKey(key)) {
  53. deleteNum++;
  54. delete();
  55. }
  56. }
  57. long end = new Date().getTime();
  58. System.out.println("删除时间:"+(end-addUpdateTime)+"ms");
  59. System.out.println("总共用时:"+(end-start)+"ms,新增了:"+addNum+"条,修改了:"+updateNum+"条,删除了:"+deleteNum+"条");
  60. }
  61. public static void add() {
  62. }
  63. public static void update() {
  64. }
  65. public static void delete() {
  66. }
  67. }

这套代码不仅实现了新增修改,还实现了删除功能,那么效率如何呢?

第一次运行:(初始化时间为把Set存入Map的时间,没有删除是因为设置的需要删除的素数较大,8000条中并不存在)

初始化时间:10ms

新增和修改时间:9ms

删除时间:3ms

总共用时:22ms,新增了:10条,修改了:2条,删除了:0条

第二次运行:

初始化时间:18ms

新增和修改时间:9ms

删除时间:3ms

总共用时:30ms,新增了:10条,修改了:2条,删除了:0条

第三次运行:

初始化时间:9ms

新增和修改时间:9ms

删除时间:3ms

总共用时:21ms,新增了:10条,修改了:2条,删除了:0条

可以看出来,这套代码的性能非常高,支撑8000条数据毫无压力,那么再多了呢?

1W条数据:

初始化时间:10ms

新增和修改时间:9ms

删除时间:4ms

总共用时:23ms,新增了:10条,修改了:2条,删除了:1条

5W条数据:

初始化时间:31ms

新增和修改时间:32ms

删除时间:15ms

总共用时:78ms,新增了:10条,修改了:9条,删除了:5条

30W条数据:

初始化时间:162ms

新增和修改时间:161ms

删除时间:78ms

总共用时:401ms,新增了:10条,修改了:54条,删除了:30条

50W条数据:

初始化时间:315ms

新增和修改时间:258ms

删除时间:128ms

总共用时:701ms,新增了:10条,修改了:89条,删除了:50条

一直到50W条数据,速度都比第一种方法的8000条数据要快,其中初始化就占用的将近一半的时间。最后我试一下百万条数据的速度如何:

初始化时间:611ms

新增和修改时间:548ms

删除时间:296ms

总共用时:1455ms,新增了:10条,修改了:177条,删除了:100条

百万条数据,大约只需要一秒半的时间就可以解决。感觉速度是真的快到不行!虽然我相信这肯定不是最优写法,但是感觉目前的业务需求量来讲,已经完全足够了。

那么在什么情况下,用第一种方法呢?我测试了一下,100条数据以内,你想用哪个用哪个,速度都是差不多的了。但是100条以上的,后者效率都要比前者快。

最后所以下遇到的神奇的情况,就是在数据量为25W~27.3W的区间里,效率会明显降低,全都需要1秒钟以上的响应时间,但是一到27.4W又恢复正常了。研究白天,百度很久,也没有发现问题所在。不过1秒钟的速度也还是可以接受了,所以暂时就先不考虑优化问题了。

这是我第一次对算法进行简单的优化,感觉是一件很有意思的事情。不知道这种结构是否能优化到百万级数据也在一秒以内查询出来?如果有大神看到这篇文章,帮忙分享一下呀,谢谢!

Tags:

最近发表
标签列表