Стек с использованием API потоковой передачи Java 8

У меня есть метод, который генерирует объект каждый раз, когда я его выполняю, и мне нужно отменить порядок, с которым я их получаю. Поэтому я думал, что естественным способом сделать это будет Stack, поскольку это LIFO.

Однако Java Stack, похоже, не очень хорошо работает с новым потоковым API Java 8.

Если я это сделаю:

   Stack<String> stack = new Stack<String>();
   stack.push("A");
   stack.push("B");
   stack.push("C");

   List<String> list = stack.stream().collect(Collectors.toList());
   System.out.println("Collected: " + list);

Выход, который я получаю:

Collected: [A, B, C]

Почему он не выводит их в ожидаемый порядок LIFO для потока? Это правильный способ вымыть все элементы из стека в список справа (LIFO)?

Ответы

Ответ 1

Как уже упоминалось в комментариях, мы хорошо протестировали Deque интерфейс, который должен быть предпочтительным.

Но я вам объясню причину, по которой Stack не следует использовать.

Сначала, Java Doc. из стека говорит сам:

Более полный и последовательный набор операций стека LIFO предоставляемые интерфейсом Deque и его реализациями, которые должны используется для этого класса. Например:

Deque stack = new ArrayDeque();

См. JavaDoc.

Так в чем проблема с классом Stack.

Как уже упоминал Мартин Фаулер в своей книге   Рефакторинг: улучшение дизайна существующего кода по методу рефакторинга Заменить наследование с делегированием, Стек не должен наследоваться от вектора.

Один из классических примеров ненадлежащего наследования - это стек подкласса вектора. Java 1.1 делает это в своих утилитах (непослушные мальчики!) [6, с. 288]

Вместо этого они должны были использовать делегирование, как на рисунке ниже, который также из книги.

См. также здесь: Заменить наследование делегацией

Replace Inheritance with Delegation

Итак, но почему это проблема:

Поскольку в стеке всего 5 методов:

  • поп
  • толчок
  • IsEmpty
  • поиск
  • размер

    size() и isEmpty() наследуются от класса Vector, а другие методы из Vector не используются. Но через наследование другие методы передаются классу Stack, что не имеет смысла.

И Фаулер говорит об этой проблеме:

Вы можете жить с ситуацией и использовать соглашение, чтобы сказать, что хотя это подкласс, он использует только часть суперкласса функция. Но это приводит к коду, который говорит одно, когда ваш намерение - это нечто другое - путаницу, которую вы должны удалить.

Это вредит Принцип разделения сети

который гласит:

КЛИЕНТЫ НЕ ДОЛЖНЫ БЫТЬ ЗАВИСИМО ОТ ОТНОШЕНИЯ К ИНТЕРФЕЙСАМ, КОТОРЫЕ ОНИ НЕ ИСПОЛЬЗУЙТЕ.


Вы можете проверить исходный код Vector и Stack class и вы увидите, что класс Stack наследует метод spliterator и VectorSpliterator innerClass из класса Vector.

Этот метод используется интерфейсом Collection для impl. версия потокового метода по умолчанию:

default Stream<E> More ...stream() {
  return StreamSupport.stream(spliterator(), false);
}

Поэтому избегайте просто использования классов Vector и Stack.

[6] Рефакторинг: улучшение дизайна существующего кода Fowler, Martin     год 1997