二分法搜索List<T>泛型集合

快来打我* 2023-10-17 23:06 83阅读 0赞

List集合在项目开发中是最常用的一个集合,在项目中经常需要从集合中查找某一个对象,如果直接用for循环小数据量时没什么性能问题,但是数据量非常大时,用for循环就会显得特别慢。本文将详细讲解如何利用二分法从List集合中查询泛型对象。

需求:传入一个List集合,从中搜索指定排序属性值对应的对象。

思路:未明确指定集合中的对象,所以我们需要使用泛型;未指明对象排序属性类型,所以我们需要考虑排序属性类型,便于比较;未指明传入的集合是升序排序还是降序排序,所以我们需要将传入的集合排序。

请看一下代码(代码中附讲解):

  1. package test;
  2. import java.lang.reflect.Field;
  3. import java.util.ArrayList;
  4. import java.util.Collections;
  5. import java.util.List;
  6. //构建一个搜索工具类,指定泛型对象必须实现Comparable接口
  7. public class RecursionDichotomy<T extends Comparable<T>> {
  8. //比较属性名
  9. private String searchKey;
  10. //传入的集合
  11. private List<T> data = new ArrayList<T>();
  12. //构造方法
  13. public RecursionDichotomy(List<T> data, String searchId) {
  14. this.data = data;
  15. this.searchKey = searchId;
  16. //排序,升序
  17. Collections.sort(this.data);
  18. }
  19. //排序属性get,set方法
  20. public String getSearchKey() throws Exception {
  21. if (null == searchKey || "".equals(searchKey)) {
  22. throw new Exception("searchKey is null !");
  23. }
  24. return searchKey;
  25. }
  26. public void setSearchKey(String searchKey) {
  27. this.searchKey = searchKey;
  28. }
  29. //对外调用接口排序属性为字节型
  30. public T search(Byte id) throws Exception {
  31. return search(data, id);
  32. }
  33. //对外调用接口排序属性为短整型
  34. public T search(Short id) throws Exception {
  35. return search(data, id);
  36. }
  37. //对外调用接口排序属性为整型
  38. public T search(Integer id) throws Exception {
  39. return search(data, id);
  40. }
  41. //对外调用接口排序属性为长整型
  42. public T search(Long id) throws Exception {
  43. return search(data, id);
  44. }
  45. //对外调用接口排序属性为单精度浮点型
  46. public T search(Float id) throws Exception {
  47. return search(data, id);
  48. }
  49. //对外调用接口排序属性为双精度浮点型
  50. public T search(Double id) throws Exception {
  51. return search(data, id);
  52. }
  53. //对外调用接口排序属性为字符串
  54. public T search(String id) throws Exception {
  55. return search(data, id);
  56. }
  57. //定义一个内部私有公共方法,通过递归实现二分法查找指定排序属性值得对象
  58. private T search(List<T> searchData, Object id) throws Exception {
  59. //获取集合中间值
  60. int midle = searchData.size() / 2;
  61. //获取传入属性值得类型
  62. String type = id.getClass().getTypeName();
  63. type=type.substring(type.lastIndexOf(".")+1);
  64. //获得集合中间对象
  65. T mt = searchData.get(midle);
  66. //通过反射获取T排序属性对应的值
  67. Field field = mt.getClass().getDeclaredField(this.getSearchKey());
  68. //设置属性可以被访问(主要防止对象中定义的属性为私有(private)属性)
  69. field.setAccessible(true);
  70. //比较传入的排序属性值与当前传入集合中间对象的排序属性值,本文这里只考虑基本数据类型(布尔类型除外)和String类型属性。
  71. int rin = 2;
  72. switch (type) {
  73. case "Byte":
  74. Byte by = field.getByte(mt);
  75. rin = by.compareTo((Byte) id);
  76. case "Short":
  77. Short sh = field.getShort(mt);
  78. rin = sh.compareTo((Short) id);
  79. break;
  80. case "Integer":
  81. Integer in = field.getInt(mt);
  82. rin = in.compareTo((Integer) id);
  83. break;
  84. case "Long":
  85. Long lo = field.getLong(mt);
  86. rin = lo.compareTo((Long) id);
  87. break;
  88. case "Float":
  89. Float fl = field.getFloat(mt);
  90. rin = fl.compareTo((Float) id);
  91. break;
  92. case "Double":
  93. Double dou = field.getDouble(mt);
  94. rin = dou.compareTo((Double) id);
  95. break;
  96. case "String":
  97. String st = (String) field.get(mt);
  98. rin = st.compareTo((String) id);
  99. break;
  100. default:
  101. rin=2;
  102. break;
  103. }
  104. //传入的属性值小于集合中间对象排序值,取集合左边子集合传入当前方法
  105. if (rin == 1) {
  106. return this.search(searchData.subList(0, midle), id);
  107. //传入的属性值大于集合中间对象排序值,取集合右边子集合传入当前方法
  108. } else if (rin == -1) {
  109. return this.search(searchData.subList(midle, searchData.size()), id);
  110. //传入的属性值等于当前中间值时,返回该对象。
  111. } else if (rin == 0) {
  112. return mt;
  113. }
  114. return null;
  115. }
  116. }

以上代码是对我们最开始需求的实现,下面我们定义一个对象,测试我们写的方法。

定义一个用户对象:

  1. package test;
  2. //用户对象,需实现Comparable,因为我们封装的方法指明了集合中的对象需要实现Comparable接口
  3. public class UserInfo implements Comparable<UserInfo> {
  4. private int id;
  5. private int age;
  6. private String name;
  7. public UserInfo() {
  8. super();
  9. }
  10. public UserInfo(int id, int age, String name) {
  11. super();
  12. this.id = id;
  13. this.age = age;
  14. this.name = name;
  15. }
  16. public int getId() {
  17. return id;
  18. }
  19. public void setId(int id) {
  20. this.id = id;
  21. }
  22. public int getAge() {
  23. return age;
  24. }
  25. public void setAge(int age) {
  26. this.age = age;
  27. }
  28. public String getName() {
  29. return name;
  30. }
  31. public void setName(String name) {
  32. this.name = name;
  33. }
  34. @Override
  35. public int hashCode() {
  36. final int prime = 31;
  37. int result = 1;
  38. result = prime * result + age;
  39. result = prime * result + id;
  40. result = prime * result + ((name == null) ? 0 : name.hashCode());
  41. return result;
  42. }
  43. @Override
  44. public boolean equals(Object obj) {
  45. if (this == obj)
  46. return true;
  47. if (obj == null)
  48. return false;
  49. if (getClass() != obj.getClass())
  50. return false;
  51. UserInfo other = (UserInfo) obj;
  52. if (age != other.age)
  53. return false;
  54. if (id != other.id)
  55. return false;
  56. if (name == null) {
  57. if (other.name != null)
  58. return false;
  59. } else if (!name.equals(other.name))
  60. return false;
  61. return true;
  62. }
  63. @Override
  64. public String toString() {
  65. return "UserInfo [id=" + id + ", age=" + age + ", name=" + name + "]";
  66. }
  67. //重写compareTo方法实现用于排序,排序属性为用户ID
  68. @Override
  69. public int compareTo(UserInfo o) {
  70. if (this.id > o.getId()) {
  71. return 1;
  72. } else if (this.id < o.getId()) {
  73. return -1;
  74. } else {
  75. return 0;
  76. }
  77. }
  78. }

用户对象定义好了,下面我们来测试下我们写的方法:

  1. public static void main(String[] args) {
  2. List<UserInfo> data = new ArrayList<UserInfo>();
  3. UserInfo u1=new UserInfo();
  4. u1.setId(1);
  5. UserInfo u2=new UserInfo();
  6. u2.setId(2);
  7. UserInfo u3=new UserInfo();
  8. u3.setId(3);
  9. UserInfo u4=new UserInfo();
  10. u4.setId(4);
  11. UserInfo u5=new UserInfo();
  12. u5.setId(5);
  13. UserInfo u6=new UserInfo();
  14. u6.setId(6);
  15. UserInfo u7=new UserInfo();
  16. u7.setId(7);
  17. UserInfo u8=new UserInfo();
  18. u8.setId(8);
  19. data.add(u8);
  20. data.add(u7);
  21. data.add(u5);
  22. data.add(u6);
  23. data.add(u4);
  24. data.add(u1);
  25. data.add(u2);
  26. data.add(u3);
  27. RecursionDichotomy<UserInfo> rd = new RecursionDichotomy<UserInfo>(data, "id");
  28. UserInfo userInfo = null;
  29. try {
  30. userInfo = rd.search(7);
  31. System.out.println("搜索到的结果:" + userInfo.toString());
  32. userInfo = rd.search(4);
  33. System.out.println("搜索到的结果:" + userInfo.toString());
  34. userInfo = rd.search(1);
  35. System.out.println("搜索到的结果:" + userInfo.toString());
  36. } catch (Exception e) {
  37. e.printStackTrace();
  38. }
  39. }

测试输出结果:

70

以上方法你学会了吗?快动手试试吧。

发表评论

表情:
评论列表 (有 0 条评论,83人围观)

还没有评论,来说两句吧...

相关阅读

    相关 Java集合

    认识泛型 (1)泛型是JDK1.5的新特性,泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数,使代码可以应用于多种类型。 (2)Java语言引入泛型的好

    相关 二分法搜索List集合

    List集合在项目开发中是最常用的一个集合,在项目中经常需要从集合中查找某一个对象,如果直接用for循环小数据量时没什么性能问题,但是数据量非常大时,用for循环就会显得特别慢