Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

indexOf() Strange Java.util.List behaviour with duplicate Strings

Tags:

java

list

I've just come across some odd behaviour I wouldn't expect from an ArrayList<String> in Java. This is coming, for sure, from my poor understanding of references in Java.

Let me show you this piece of code:

List<String> myList = new ArrayList<>();

myList.add("One");
myList.add("Two");
myList.add("Two");
myList.add("Three");

for (String s : myList){
  System.out.println(myList.indexOf(s));
}

This piece of code provides the following output:

0  
1  
1  
3

How come? I've added on purpose two Strings containing the same characters ("Two"), but the object itself shouldn't be the same. What am I misunderstanding here? I was expecting this other output:

0
1
2
3
like image 298
JorgeGRC Avatar asked Aug 18 '15 13:08

JorgeGRC


People also ask

How does indexOf () work in Java?

The indexOf() method returns the position of the first occurrence of specified character(s) in a string. Tip: Use the lastIndexOf method to return the position of the last occurrence of specified character(s) in a string.

Can you have duplicates in a list Java?

All you need to know is that Set doesn't allow duplicates in Java. Which means if you have added an element into Set and trying to insert duplicate element again, it will not be allowed. In Java, you can use the HashSet class to solve this problem.


3 Answers

ArrayList.indexOf() doesn't use reference equality to find the object. It uses the equals() method. Notice what the documentation says (emphasis mine):

returns the lowest index i such that (o==null ? get(i)==null : o.equals(get(i))), or -1 if there is no such index.

Thus, it will match on the first string that is logically equal.

EDIT:

Andremoniy's comment is absolutely right. In the case of strings literals, because they are interned, they will also happen to have the same reference. So your 2 strings "Two" are actually the same reference in this case.

System.out.println("Two" == "Two"); // will return true because they are the same reference.
like image 139
sstan Avatar answered Nov 14 '22 22:11

sstan


It's simply because indexOf returns the first occurrence of the item in the list that is equal to the given string. See 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. More formally, returns the lowest index i such that (o==null ? get(i)==null : o.equals(get(i))), or -1 if there is no such index.

like image 24
Maroun Avatar answered Nov 14 '22 21:11

Maroun


You'll have to note two points:

  1. most probably you are using the same String-instance, since the constant "Two" gets interned, that is all occurences of this literal will refer to the same instance.
  2. List.indexOf() doesn't compare items by == (that is object-identity) but using equals() - that is some class-defined way to compare two objects for equality (which makes perfect sense as otherwise you wouldn't be able to find something in the list unless you already have a reference to it). So even two different String-objects (e.g. created by new String("Two")) would still produce the same output.

For completeness the quote from the javadoc of indexOf(as already mentioned in the other answers:

returns the lowest index i such that (o==null ? get(i)==null : o.equals(get(i))), or -1 if there is no such index.

like image 25
piet.t Avatar answered Nov 14 '22 22:11

piet.t