Я смотрю на реализацию Streams::findLast
из гуавы и, пытаясь понять это, была пара вещей, которые я просто не мог понять.Вот его реализация:
public static <T> java.util.Optional<T> findLast(Stream<T> stream) {
class OptionalState {
boolean set = false;
T value = null;
void set(@Nullable T value) {
set = true;
this.value = value;
}
T get() {
checkState(set);
return value;
}
}
OptionalState state = new OptionalState();
Deque<Spliterator<T>> splits = new ArrayDeque<>();
splits.addLast(stream.spliterator());
while (!splits.isEmpty()) {
Spliterator<T> spliterator = splits.removeLast();
if (spliterator.getExactSizeIfKnown() == 0) {
continue; // drop this split
}
// Many spliterators will have trySplits that are SUBSIZED even if they are not themselves
// SUBSIZED.
if (spliterator.hasCharacteristics(Spliterator.SUBSIZED)) {
// we can drill down to exactly the smallest nonempty spliterator
while (true) {
Spliterator<T> prefix = spliterator.trySplit();
if (prefix == null || prefix.getExactSizeIfKnown() == 0) {
break;
} else if (spliterator.getExactSizeIfKnown() == 0) {
spliterator = prefix;
break;
}
}
// spliterator is known to be nonempty now
spliterator.forEachRemaining(state::set);
return java.util.Optional.of(state.get());
}
Spliterator<T> prefix = spliterator.trySplit();
if (prefix == null || prefix.getExactSizeIfKnown() == 0) {
// we can't split this any further
spliterator.forEachRemaining(state::set);
if (state.set) {
return java.util.Optional.of(state.get());
}
// fall back to the last split
continue;
}
splits.addLast(prefix);
splits.addLast(spliterator);
}
return java.util.Optional.empty();
}
По сути, реализация не так уж и сложна, если честно, но вот некоторые вещи, которые я нахожу немного странными (и я возьму всю вину на себя, если этот вопрос возникнет)закрыт как «основанный на мнении», я понимаю, что это может произойти).
Первое - это создание класса OptionalState
, его можно было бы заменить массивомодного элемента:
T[] state = (T[]) new Object[1];
и используется так же просто, как:
spliterator.forEachRemaining(x -> state[0] = x);
Тогда весь метод можно разбить на 3 части:
1), когдаопределенный Spliterator, как известно, пустой:
if (spliterator.getExactSizeIfKnown() == 0)
В этом случае это просто - просто отбросьте его.
2) тогда, если известно, что Spliterator равен SUBSIZED
.Это сценарий «счастливого пути»;как в этом случае мы можем разделить это, пока мы не дойдем до последнего элемента.По сути, реализация гласит: split до тех пор, пока prefix
не станет равным null
или не станет пустым (в этом случае используется «правильный» сплитератор), или если после разделения известно, что «правый» сплитератор пуст, используйте prefix
один.Это делается с помощью:
// spliterator is known to be nonempty now
spliterator.forEachRemaining(state::set);
return java.util.Optional.of(state.get());
Второй вопрос, который у меня есть, на самом деле об этом комментарии:
// Many spliterators will have trySplits that are SUBSIZED
// even if they are not themselves SUBSIZED.
Это очень интересно, но я не смог найти такойНапример, был бы признателен, если бы кто-то познакомил меня с одним.Фактически, поскольку этот комментарий существует, код в следующей (3-й части метода не может быть выполнен с while(true)
, как во втором), потому что он предполагает, что после trySplit
мы могли бы получитьSpliterator
, то есть SUBSIZED
, даже если наш начальный не был, поэтому он должен перейти к самому началу findLast
.
3) эта часть метода - это когда Spliteratorизвестно, что не равно SUBSIZED
, и в этом случае он не имеет известного размера;таким образом, он зависит от того, как реализован Spliterator из источника, и в этом случае на самом деле findLast
не имеет особого смысла ... например, Spliterator
из HashSet
вернет то, что последняя запись находится в последнем сегменте...