Есть ли правильный способ закрыть ресурсы, открытые в Java-потоке API (для каждого элемента)? - PullRequest
4 голосов
/ 18 апреля 2019

Есть ли правильный способ открыть ресурс для каждого элемента в коллекции, чем использовать stream api, сделать какие-то map (), filter (), peek () и т. Д. С использованием ресурса и чем закрыть ресурс?

У меня есть что-то вроде этого:

List<String> names =  getAllNames();
names.stream().map(n -> getElementFromName(n))
              .filter(e -> e.someCondition())
              .peek(e -> e.doSomething())
              .filter(e -> e.otherCondition())
              .peek(e -> e.doSomethingElse())
              .filter(e -> e.lastCondition())
              .forEach(e -> e.doTheLastThing());

Это должно работать нормально, за исключением того, что я открываю ресурс (например, соединение БД) в методе getElementFromName. Затем я работаю с этим ресурсом в других методах экземпляра, таких как someCondition или doSomething. И я понятия не имею, как правильно его закрыть.

Я знаю, что я использую только промежуточные операции в потоке. Это означает, что все методы оцениваются в операции forEach, и производительность должна быть в порядке, потому что итерация выполняется только один раз.

Но я не могу понять, как закрыть ресурсы, открытые для каждого элемента в методе getElementFromName.

Я могу сохранить список всех элементов, созданных getElementFromName, и закрыть ресурсы позже. Но я бы просто терял пространство, сохраняя все ресурсы живыми. Также будет вторая итерация по списку элементов. Что делает избегание потока API предпочтительным в этом случае. Так есть ли способ как-то автоматически закрыть ресурс, когда я закончу с использованием элемента?

Также я знаю, что это легко сделать с помощью foreach lopp, мне было просто интересно, если это можно сделать с помощью stream api.

Ответы [ 2 ]

3 голосов
/ 18 апреля 2019

Для этого можно использовать flatMap:

.flatMap(n -> {
    YourResource r = getElementFromName(n);
    return Stream.of(r).onClose(r::close);
})

Предполагается, что ресурс, инкапсулированный в возвращаемом элементе, имеет метод close().Если нет, код становится более сложным, но картина должна быть ясной.Вместо того, чтобы просто возвращать один объект, вы возвращаете одноэлементный поток, действие которого onClose выполняет необходимую очистку.Если он может генерировать проверенные исключения, вам также потребуется добавить обработчик для этого, предпочтительно заключив исключение в непроверенное исключение.

Это зависит от гарантии, предоставленной для flatMap:

Каждый сопоставленный поток имеет значение closed после помещения его содержимого в этот поток.

Но, как правило, этот код, особенночрезмерное использование peek выглядит крайне подозрительно.

0 голосов
/ 18 апреля 2019

Вы можете создать второй список для хранения элементов по мере их появления.К сожалению, это единственный известный мне способ закрыть все элементы с помощью потоков.

В этом примере я предполагаю, что getElementFromName возвращает объект типа Element:

List<Element> elements = new ArrayList<>(); // Or whatever kind of list you want
List<String> names =  getAllNames();
names.stream().map(n -> getElementFromName(n))
              .peek(e -> elements.add(e)) // Add the elements to the list
              .filter(e -> e.someCondition())
              .peek(e -> e.doSomething())
              .filter(e -> e.otherCondition())
              .peek(e -> e.doSomethingElse())
              .filter(e -> e.lastCondition())
              .forEach(e -> e.doTheLastThing());

elements.forEach(e -> e.close()); // Close all the elements
...