javalistarraylist

Find all indexes of a value in a List


I'm trying to search an ArrayList for a user input. I've managed to create a search that prints the index of the first occurrence from within the list.

I'm having trouble trying to get the rest of the indexes that the item are stored.

Here is the code I have got so far to print the first index of search:

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

I understand that a loop needs to be added. But I am having trouble trying to formulate it.

Example

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

Say search = "Bob". My expected result would be {0,2}. Instead, I am only able to get the index of the first occurrence (0).

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

[...]

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

How can I get all indexes that match the search string?


Solution

  • Explanation

    The method List#indexOf only returns the index of the first found matching element. From its documentation:

    Returns the index of the first occurrence of the specified element in this list, or -1 if this list does not contain the element. [...]

    But you want all, therefore you also need to iterate all elements.

    Also note that calling List#contains is not necessary since List#indexOf also answers this question, it returns -1 if not found. In fact in an ArrayList both calls are very expensive (they iterate from left to right until found) so you shouldn't use unnecessary statements if they are such expensive.


    Solution

    Instead just iterate all elements and collect the ones that match:

    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);
    

    Or you may use some of the very convenient methods of the Stream API. Stream#filter (documentation) for example:

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