Является ли Javas Collectors.toSet() гарантированным разрешить null?
Интерфейс Set
не делает promises о том, позволяют ли реализации использовать элементы null
. Каждая реализация должна объявить об этом в своей документации.
Collectors.toSet()
promises, чтобы вернуть реализацию Set
, но явно не дает никаких гарантий относительно типа, изменчивости, сериализуемости, или безопасность потока Set
возвращена ". Нулевая безопасность не упоминается.
Текущая реализация Collectors.toSet()
в OpenJDK всегда использует HashSet
, которая допускает нулевые элементы, но это может измениться в будущем, а другие реализации могут по-другому.
Если реализация Set
запрещает элементы null
, она бросает NullPointerException
в разное время, в частности во время попытки add(null)
. Казалось бы, если Collectors.toSet()
решил использовать реалистичную реализацию Set
с нулевым нетерпимостью, вызов stream.collect(Collectors.toSet())
на Stream stream
бросить. Спецификация collect
не содержит никаких исключений, а также не спецификация любого из методов Collector
. Это может означать, что вызов collect
разрешает null в пределах stream
, но, с другой стороны, неясно, действительно ли это означает много, поскольку NullPointerException
является неконтролируемым исключением и не обязательно должен быть указан.
Является ли это более четким в любом другом месте? В частности, является ли следующий код гарантированным не бросать? Гарантируется ли возврат true
?
import java.util.stream.*;
class Test {
public static boolean setContainsNull() {
return Stream.of("A", "list", "of", null, "strings")
.collect(Collectors.toSet())
.contains(null);
}
}
Если нет, то я предполагаю, что мы всегда должны гарантировать, что поток не содержит нулей перед использованием Collectors.toSet()
или будет готов к обработке NullPointerException
. (Достаточно ли этого исключения?) Альтернативно, когда это неприемлемо или сложно, мы можем запросить конкретную реализацию набора с использованием кода типа Collectors.toCollection(HashSet::new)
.
Изменить: существует существующий вопрос, который кажется поверхностным схожим, и этот вопрос закрылся как предполагаемый дубликат этого. Однако связанный вопрос вообще не затрагивает Collectors.toSet()
. Более того, ответы на этот вопрос составляют основные предположения моего вопроса. Этот вопрос спрашивает: допустимы ли пустые значения в потоках? Да. Но что происходит, когда (полностью разрешенный) поток, содержащий нули, собирается через стандартный сборщик?
Ответы
Ответ 1
Существует разница между преднамеренно неопределенным поведением, таким как "тип, изменчивость, сериализуемость или безопасность потоков", и недоопределенным поведением, таким как null
поддержка.
Всякий раз, когда поведение недостаточно определено, фактическое поведение эталонной реализации имеет тенденцию становиться фактом, который не может быть изменен позже, даже если он противодействует первоначальному намерению, из-за ограничений совместимости, или, по крайней мере, его нельзя изменить без веской причины.
Обратите внимание, что хотя зарезервированное право на возвращение действительно неизменяемого или сериализуемого Set
non- не использовалось, просто потому, что такого типа не было в релизе Java 8, применение null
поведения non- было возможно даже без существования адекватного хэша Тип карты, как и в groupingBy
запрещает null
ключи, хотя и не указан.
Следует также отметить, что в то время как groupingBy
коллектор намеренно отвергает null
ключи в коде реализации, toMap
является хорошим примером того, как фактическое поведение становится частью договора. В Java 8 toMap
допускает null
ключи, но отклоняет null
значения просто потому, что вызывает Map.merge
который имеет такое поведение. Кажется, это не было намеченным поведением в первую очередь. Теперь в Java 9 сборщик toMap
без функции Map.merge
больше не использует Map.merge
(JDK-8040892, см. Также этот ответ), но намеренно отклоняет null
значения в коде сборщика, чтобы быть поведенчески совместимыми с предыдущей версией. Просто потому, что никогда не говорилось, что null
поведение намеренно не определено.
Итак, Collectors.toSet()
(и аналогично Collectors.toList()
) теперь допускают null
значения для двух основных версий Java, и нет никакой спецификации, говорящей, что вы не должны принимать это как должное, так что вы можете быть совершенно уверены, что это не изменится в будущее.