在一个列表中查找某个值的所有索引

3

我正在尝试搜索一个ArrayList来查找用户输入。我已经成功创建了一个搜索功能,可以打印出列表中第一次出现的索引。

我正在尝试获取该项存储的其余索引,但遇到了困难。

以下是我目前用于打印search第一个索引的代码:

if (names.contains(search)) {
    System.out.println("name found!");
    System.out.println(names.indexOf(search));
}

我知道需要添加一个循环。但是我在尝试组织它时遇到了麻烦。

示例

ArrayList<String> names = new ArrayList<String>();
names.add("Bob");
names.add("Jerry");
names.add("Bob"); 
names.add("Mick");

假设search = "Bob"。 我期望得到的结果是{0,2},但实际上我只能获取到第一次出现的索引(0)。

assert allIndexesOf(names, "Bob").equals(List.of(0, 2));

[...]

private List<Integer> allIndexesOf(List<?> list, Object o) {
  // How can this be implemented?
}

我该如何获取所有与搜索字符串匹配的索引?

1
暂时忘记编程。想象一下你有10个抽屉,编号从0到9,并且想要找到所有包含蓝色纸张的抽屉,并将它们的编号写在一张纸上。你会怎么做? - JB Nizet
1
对于列表中的所有项目,如果当前索引处的项目是我要查找的内容,则将该索引添加到另一个列表中,否则不执行任何操作并继续。 - ollie
要求关于作业帮助的问题必须包括您已经完成的解决问题的工作摘要,以及您在解决问题时遇到的困难的描述([help], [ask])。- 您已经做到了,很好! - Zabuzard
1
投票重新开放。 “重复”的问题是关于字符串中的字符,而不是列表中的元素。虽然它们具有类似的API,但它们并不相同。此外,还有其他解决方案(例如流)可能比indexOf更适用于列表。 - M. Justin
3个回答

2

解释

List#indexOf 方法只返回第一个匹配元素的索引。从它的文档中可以看到:

返回此列表中指定元素的第一次出现的索引,如果此列表不包含元素,则返回-1。[...]

但是你需要全部匹配元素的索引,因此你还需要遍历所有元素

另外请注意,调用 List#contains 不是必要的,因为 List#indexOf 也可以回答这个问题,如果没有找到则返回 -1。事实上,在 ArrayList 中,这两个调用都非常昂贵(它们从左到右迭代直到找到),因此如果它们非常昂贵,就不应该使用不必要的语句。


解决方案

相反,只需遍历所有元素并收集匹配的元素即可:

ArrayList<String> author = ...
String needle = ...

// Collect matches
List<Integer> matchingIndices = new ArrayList<>();
for (int i = 0; i < author.size(); i++) {
    String element = author.get(i);

    if (needle.equals(element)) {
        matchingIndices.add(i);
    }
}

// Print matches
matchingIndices.forEach(System.out::println);

或者你可以使用一些非常方便的 Stream API 方法。例如,Stream#filter文档):

List<Integer> matchingIndices = IntStream.range(0, author.size())
    .filter(i -> needle.equals(author.get(i))) // Only keep those indices
    .collect(Collectors.toList());

从项目第一个索引开始而不是从第0个位置开始,这样不是更好吗? - Delrius Euphoria
@CoolCloud 如果你已经预先知道那个索引,那当然可以...但是如果你建议使用像 indexOf 这样的东西,你并没有真正获得任何好处。因为该方法也只是简单地迭代。 - Zabuzard

1
你可以遍历整个列表并保存所有与搜索项匹配的索引。Java 8的流提供了一种相当优雅的方法:
int[] indexes =
    IntStream.range(0, names.size())
             .filter(i -> names.get(i).equals(search))
             .toArray();

但是他失去了索引。我认为他想收集索引,而不是元素(因为它们相等)。 - Zabuzard
@Zabuza 你应该再读一遍我的解决方案——它确实收集了索引。 - Mureinik
在目前的形式下,我认为这是一个不太好的问题。正如JB Nizet所说,还有一个更大的问题需要解决。 - S.R.I
啊,确实,我以为你已经做了一个“map”,因此失去了索引的追踪。 - Zabuzard
@S.R.I 我认为这是一个好问题。实际上,我今天在一个明显不是作业问题中遇到了这个问题。 - M. Justin

0
for (int i = 0;i<author.size();i++){
  if(author.get(i).equals(searchTerm)) {
    System.out.println("Author found!");
    System.out.println(i);
  }
}

避免使用 == 比较 String。结果可能不是您所期望的。请使用 String#equals 进行替换,这样就可以了。 - Zabuzard
更多关于此内容的信息,请参见SO:如何在Java中比较字符串? - Zabuzard
1
谢谢你的提示。我已经编辑了我的帖子。 - Silvan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接