Держитесь подальше от решений, использующих indexOf
.Хотя они могут позволять писать довольно короткий код, эта операция indexOf
выполняет операцию линейного поиска на основе содержимого, вызывая equals
для элементов списка до тех пор, пока не будет найдено совпадение.
Хотя это может выглядеть кактривиальная вещь, поскольку все подсписки различаются по размеру, за исключением соответствующего элемента, большинство реализаций Java 8 List
не используют размер для сокращения сравнения.
Чтобы проиллюстрировать проблему,
используйте следующий вспомогательный класс
class Counter {
int count;
@Override
public boolean equals(Object obj) {
count++;
return super.equals(obj);
}
@Override
public int hashCode() {
return super.hashCode();
}
@Override
public String toString() {
return "equals invoked "+count+" times";
}
}
и
Counter c = new Counter();
List<List<Counter>> list = Arrays.asList(
new ArrayList<>(Collections.nCopies(10, c)),
new ArrayList<>(Collections.nCopies(15, c)),
new ArrayList<>(Collections.nCopies(7, c)),
new ArrayList<>(Collections.nCopies(10, c))
);
Comparator<List<?>> cmp = Comparator.comparingInt(List::size);
System.out.println("using IntStream.range(0, list.size()).boxed()\r\n" +
" .min(Comparator.comparing(list::get, cmp))");
int minIndex =
IntStream.range(0, list.size()).boxed()
.min(Comparator.comparing(list::get, cmp)).orElse(-1);
System.out.println("result "+minIndex+", "+c);
c.count = 0;
System.out.println("\nusing list.indexOf(Collections.min(list, cmp))");
minIndex = list.indexOf(Collections.min(list, cmp));
System.out.println("result "+minIndex+", "+c);
c.count = 0;
System.out.println("\nusing list.indexOf(list.stream().min(cmp).get())");
minIndex = list.indexOf(list.stream().min(cmp).get());
System.out.println("result "+minIndex+", "+c);
, он напечатает
using IntStream.range(0, list.size()).boxed()
.min(Comparator.comparing(list::get, cmp))
result 2, equals invoked 0 times
using list.indexOf(Collections.min(list, cmp))
result 2, equals invoked 14 times
using list.indexOf(list.stream().min(cmp).get())
result 2, equals invoked 14 times
в Java 8, показывая, что вызов equals
для любого содержащегося элемента - ненужная операция (см. первый вариант, полученный из этого ответа ), но выполненный несколько раз для других вариантов.Теперь представьте, что произойдет, если мы будем использовать большие списки и / или большее количество списков и иметь тип элемента с довольно дорогим тестом на равенство.
Обратите внимание, что для ArrayList
это было решено в JDK 11,но все еще остаются реализации списка, например, возвращаемые Collections.nCopies
или Arrays.asList
, которые не замыкают накоротко, поэтому, как правило, предпочтительнее не выполнять полностью устаревшую контентную операцию линейного поиска.