It would be simpler to start from the end of the list and decrement the counter. After removing the double at i, we can break without checking the whole string, because further doubles will be detected when i reaches j.
for(int i=animals.size()-1; i>0; i--) {
for(int j=i-1; j>=0; j--) {
if(animals.get(i).equals(animals.get(j))) {
animals.remove(i);
break;
}
}
}
Moving backwards avoids the problem as you move forward the indexes have changed because you removed earlier elements (and you failed to adjust the index to take that into account).
Another problem with your logic you were using remove(object) rather than remove(index), which causes the first matching object to be removed. However, based on expected output, you want to preserve the order of the first matching objects. So instead you should have removed the last matching object, via index.
If you want to move forward rather than backwards, but you don't wish to make adjustments to the index after a removal, it is possible to make use of iterator's remove method:
for(int i=0; i<animals.size()-1; i++) {
ListIterator<?> iter = animals.listIterator(i+1);
while(iter.hasNext()) {
if(animals.get(i).equals(iter.next())) {
iter.remove();
}
}
}
Unfortunately the outer loop here cannot use an iterator because that would result in a ConcurrentModificationException.
Finally, you could also use a subList to solve it with a single explicit loop:
for(int i=0; i<animals.size()-1; i++) {
animals.subList(i+1, animals.size()).removeIf(animals.get(i)::equals);
}
Listwhile iterating it.