【问题标题】:Better way to find index of item from ArrayList<CustomObject>从 ArrayList<CustomObject> 中查找项目索引的更好方法
【发布时间】:2012-10-31 17:45:53
【问题描述】:

首先,如果我错了,请纠正我。我想在不使用 For 循环的情况下从 ArrayList&lt;CustomType&gt; 中找到项目的索引(即字符串值)。

POJO:

id;
name;

代码:

ArrayList<POJO> list = new ArrayList<POJO>;

//Lots of data added to these list...

现在我想从数组列表中找到特定名称的 id,而不使用下面的 for 循环。

String id = null;
// TODO Auto-generated method stub
for (int i = 0; i < list.size(); i++) {
    if("ABCD".equalsIgnoreCase(list.get(i).getName())) {
        id = list.get(i).getId();
        break;
    }
}

理想情况下,我不想实现 For 循环,因为在某些情况下,我在 List 中有 500 多个数据,并且使用 For 循环查找索引并不是一个好方法。

【问题讨论】:

  • 也许您应该使用Map&lt;String, POJO&gt; 代替String 的名称?
  • 500 个元素并不多,这个循环很可能对代码的性能没有实际影响(即使它效率低下)。
  • @JoachimSauer,但我也必须考虑未来。可能会增加到数千甚至更多。这就是问题所在。 BYW 感谢您的热情回复。

标签: java search arraylist indexof


【解决方案1】:

您可以使用list.indexOf(),但为了使其工作,您需要覆盖您的POJOequalshasCode

默认情况下,如果两个对象具有相同的引用,则它们将被视为相等。您可以覆盖 equals 以适应您的情况:

public boolean equals(Object o) {
  if (!(o instanceof POJO)) {
    return false;
  }
  POJO other = (POJO) o;
  return name.equalsIgnoreCase(other.getName());
}

覆盖等于建议你覆盖hashCode。例如:

public int hashCode() {
  return name.hashCode();
}

【讨论】:

  • 这并没有解决问题中提到的低效率问题,它“只是”另一种方式来做问题中的代码所做的事情。另外:根据 POJO 的大小/使用,实例化一个只是为了搜索同名的 on 是一个坏主意。另外:大多数时候,在认为id 的 POJO 上实现 equals() 是个坏主意。
  • 有没有使用java.util.Comparator 的选项?
  • @Martin 您可以使用比较器进行排序,然后进行二分搜索。
  • @Dan 有趣的想法。但是我仍然需要重载equals ()——如果您需要搜索不同的谓词,这将无济于事。可惜我不能使用似乎有这个功能的 Java 8。
【解决方案2】:

以这种方式查找元素会给您带来 BIG-O (n) 的复杂性。我认为,如果您使用 Map 会得到更好的结果。

HashMap 会是更好的选择。 - 复杂度为 O(1)。

【讨论】:

  • 好的。是的,我可以为此使用 HashMap 。感谢您的建议@Quoi。
  • 如果我只有一个列表,则需要再次 BIG-O (n) 将列表转换为地图。
  • @yogeshprajapati - 转换是 BIG-O(n),但无论何时查询,它都是 O(1)。如果需要经常查询,最好转成地图。
【解决方案3】:

如果您需要搜索字符串值,您应该使用HashMap 而不是ArrayList

【讨论】:

    【解决方案4】:

    你可以使用List.indexOf() - 但你必须确保你也覆盖POJO.equals() - (并且作为约定的一部分 - 也是hashCode()

    请注意 - 结果将是 O(n) - 另一种方法是使用 排序数组 (POJO[]) 并使用 Arrays.binarySearch()Set/Map .

    如果您使用数组和binarySearch() - 您必须确保POJO 也实现Comparable&lt;POJO&gt;


    请注意,对于静态数据(您的列表不会经常/根本不会更改) - 尽管数组和 binarySearch() 在大 O 符号方面“更糟”然后 HashSet 在实践中 - 它通常是快得多,尤其是对于相对较短的列表。
    就大 O 表示法而言,基于散列的解决方案提供了 O(1) 平均案例访问。

    【讨论】:

    • binarySearch 还需要对List 进行排序。
    • @JoachimSauer 是的,当然 - 我认为这很明显,我会添加一个明确的指示。
    • 是否有使用java.util.Comparator 或类似名称的选项?因此可以搜索不同的谓词。
    【解决方案5】:

    感谢大家的友好和快速响应。但特别感谢Joachim Sauer。您绝对正确,500 个元素并不多,这个循环可能对您的代码性能没有真正的影响(即使它效率低下)。 即使我尝试了 5000 个元素并且仍然对性能没有负面影响。

    再次感谢大家,感谢您的评论Joachim Sauer

    【讨论】:

      【解决方案6】:

      为了回答这个问题,我使用 JMH 启动了一个基准测试。

      毫无疑问,经典循环是做到这一点的有效方法。

      我使用了包含 17576 个元素的 DATA_ARRAY,搜索到的元素位于索引 7733 处。

      使用经典循环 - 0,030 ± 0,001 毫秒/操作:

      int i = 0;
      for (String str : DATA_ARRAYLIST) {
          if (str.equals("lll")) break;
          i++;
      }
      

      使用 indexOf 并覆盖 equals 方法:0,030 ± 0,002 ms/op

      MY_DATA_ARRAYLIST.indexOf("lll");
      

      使用数据范围:0,082 ± 0,003 ms/op

      OptionalInt integer = IntStream.range(0, DATA_ARRAYLIST.size())
                  .filter(i -> DATA_ARRAYLIST.get(i).equals("lll"))
                  .findFirst();
      

      在找到流后使用 indexOf:0,074 ± 0,008 ms/op

      String result = DATA_ARRAYLIST.stream().filter(e -> e.equals("lll")).findFirst().get();
      DATA_ARRAYLIST.indexOf(result);
      

      在找到并行流后使用 indexOf:0,087 ± 0,023 ms/op

      String result = DATA_ARRAYLIST.parallelStream().filter(e -> e.equals("lll")).findFirst().get();
      DATA_ARRAYLIST.indexOf(result);
      

      但是如果搜索到的元素在最后一个索引中:

      • 经典循环:0,066 ± 0,002 毫秒/操作
      • 并行流:0,121 ± 0,023 ms/op
      • 流:0,161 ± 0,024 毫秒/操作

      如果你有 456 976 个元素:

      • 经典循环:2,172 ± 0,297 毫秒/操作
      • 并行流:3,145 ± 0,380 毫秒/操作
      • 流:6,081 ± 0,097 毫秒/操作

      如您所见,击败 Loop 真的很难!

      【讨论】:

        猜你喜欢
        • 2012-01-16
        • 1970-01-01
        • 1970-01-01
        • 2019-08-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-06-09
        • 1970-01-01
        相关资源
        最近更新 更多