经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » C# » 查看文章
使用LINQ、Lambda 表达式 、委托快速比较两个集合,找出需要新增、修改、删除的对象
来源:cnblogs  作者:行者小Q  时间:2018/10/15 9:30:42  对本文有异议

本文需要对C#里的LINQ、Lambda 表达式 、委托有一定了解。

在工作中,经常遇到需要对比两个集合的场景,如:

  1. 页面集合数据修改,需要保存到数据库
  2. 全量同步上游数据到本系统数据库

在这些场景中,需要识别出需要新增、更新、删除的数据,由于每次应用是,需要比较的对象类型不一致,因此写了个相对通用的方法。这个过程中,需要理解的有以下2个核心概念:

  1. 唯一标识比较: 如果两个对象的唯一标识相等,则认为这两个对象在业务上代表同一个东西(次要属性是否相等暂不考虑)。
  2. 实体比较:表示两个对象在业务是不是相等(唯一标识相等、次要属性相等)。

代码示例如下:

  1. void Main()
  2. {
  3. // 对比源集合
  4. var source = GenerateStudent(1, 10000, 1000);
  5. // 目标集合
  6. var target = GenerateStudent(5000, 10000, 1000);
  7. // 唯一标识比较
  8. Func<Student, Student, bool> keyCompartor = (s, t) => s.Id == t.Id;
  9. // 实体相等比较
  10. Func<Student, Student, bool> entityCompartor = (s, t) => s.Id == t.Id && s.Name.Equals(t.Name) && s.Age == t.Age;
  11. // 新增前准备
  12. Func<Student, Student> insertAction = (s) =>
  13. {
  14. return new Student
  15. {
  16. Id = s.Id,
  17. Name = s.Name,
  18. Age = s.Age,
  19. Operation = "Insert"
  20. };
  21. };
  22. // 更新前准备
  23. Func<Student, Student, Student> updateAction = (s, t) =>
  24. {
  25. t.Name = s.Name;
  26. t.Age = s.Age;
  27. t.Operation = "Update";
  28. return t;
  29. };
  30. // 删除前准备
  31. Func<Student, Student> deleteAction = (t) =>
  32. {
  33. t.Operation = "Delete";
  34. return t;
  35. };
  36. // 去掉相等对象
  37. RemoveDuplicate(source, target, entityCompartor, (s1, s2) => s1.Id == s2.Id, keyCompartor);
  38. // 需要新增的集合
  39. var insertingStudents = GetInsertingEntities(source, target, keyCompartor, insertAction);
  40. // 需要更新的集合
  41. var updatingStudents = GetUpdatingEntities(source, target, keyCompartor, entityCompartor, updateAction);
  42. // 需要删除的集合
  43. var deletingStudents = GetDeletingEntities(source, target, keyCompartor, deleteAction);
  44. // 后续业务
  45. // InsertStudents(insertingStudents);
  46. // UpdateStudents(updatingStudents);
  47. // DeleteStudents(deletingStudents);
  48. }
  49. // 集合去重
  50. private void RemoveDuplicate<S, T>(List<S> source, List<T> target, Func<S, T, bool> entityCompartor,
  51. Func<S, S, bool> sourceKeyCompartor, Func<S, T, bool> keyComportor)
  52. {
  53. var sameEntities = source.Where(s => target.Exists(t => entityCompartor(s, t))).ToList();
  54. source.RemoveAll(s => sameEntities.Exists(s2 => sourceKeyCompartor(s, s2)));
  55. target.RemoveAll(t => sameEntities.Exists(s => keyComportor(s, t)));
  56. }
  57. // 获取需要新增的对象集合
  58. private List<T> GetInsertingEntities<S, T>(List<S> source, List<T> target, Func<S, T, bool> keyComportor,
  59. Func<S, T> insertAction)
  60. {
  61. var result = new List<T>();
  62. foreach (var s in source)
  63. {
  64. var t = target.FirstOrDefault(x => keyComportor(s, x));
  65. if (t == null)
  66. {
  67. // 目标集合中不存在,则新增
  68. result.Add(insertAction(s));
  69. }
  70. }
  71. return result;
  72. }
  73. // 获取需要更新的对象集合
  74. private List<T> GetUpdatingEntities<S, T>(List<S> source, List<T> target, Func<S, T, bool> keyComportor,
  75. Func<S, T, bool> entityCompartor, Func<S, T, T> updateAction)
  76. {
  77. var result = new List<T>();
  78. foreach (var s in source)
  79. {
  80. var t = target.FirstOrDefault(x => keyComportor(s, x));
  81. if (t != null && !entityCompartor(s, t))
  82. {
  83. // 目标集合中存在,但是次要属性不相等,则更新
  84. result.Add(updateAction(s, t));
  85. }
  86. }
  87. return result;
  88. }
  89. // 获取需要删除的对象集合
  90. private List<T> GetDeletingEntities<S, T>(List<S> source, List<T> target,
  91. Func<S, T, bool> keyComportor, Func<T, T> deleteAction)
  92. {
  93. var result = new List<T>();
  94. foreach (var t in target)
  95. {
  96. var s = source.FirstOrDefault(x => keyComportor(x, t));
  97. if (s == null)
  98. {
  99. // 源集合中存在,目标集合中需要删除
  100. result.Add(deleteAction(t));
  101. }
  102. }
  103. return result;
  104. }
  105. // 随机生成测试集合
  106. private List<Student> GenerateStudent(int minId, int maxId, int maxNumber)
  107. {
  108. var r = new Random();
  109. var students = new List<Student>();
  110. for (int i = 0; i < maxNumber; i++)
  111. {
  112. students.Add(new Student
  113. {
  114. Id = r.Next(minId, maxId),
  115. Name = $"name: {r.Next(1, 10)}",
  116. Age = r.Next(6, 10)
  117. });
  118. }
  119. return students.GroupBy(s => s.Id).Select(s => s.First()).ToList();
  120. }
  121. public class Student
  122. {
  123. public int Id { get; set; }
  124. public string Name { get; set; }
  125. public int Age { get; set; }
  126. public string Operation { get; set; }
  127. }

例子中源集合与目标集合使用了相同的对象Student,但实际使用中,两者的类型可以不一样,只要最终返回目标集合的类型就可以了。

上面是我对集合比较的一点心得,只满足了小数据量的业务情景,并没有在大数据量的情况下做过调优。在这里也算是抛砖引玉,大家要是有更好的办法,还希望不吝赐教。

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号