В Java8 есть действительно хорошая новая функция, которая позволяет вам получить поток строк из файла в один слой.
|
1
|
List lines = Files.lines(path).collect(Collectors.toList()); |
Вы можете манипулировать потоком так же, как и любым другим потоком, например, вы можете захотеть отфильтровать () или map () или limit () или skip () и т. Д. Я начал использовать это во всем коде, пока не столкнулся с этим исключением ,
|
01
02
03
04
05
06
07
08
09
10
11
12
|
Caused by: java.nio.file.FileSystemException: /tmp/date.txt: Too many open files in system at sun.nio.fs.UnixException.translateToIOException(UnixException.java:91) at sun.nio.fs.UnixException.rethrowAsIOException(UnixException.java:102) at sun.nio.fs.UnixException.rethrowAsIOException(UnixException.java:107) at sun.nio.fs.UnixFileSystemProvider.newByteChannel(UnixFileSystemProvider.java:214) at java.nio.file.Files.newByteChannel(Files.java:361) at java.nio.file.Files.newByteChannel(Files.java:407) at java.nio.file.spi.FileSystemProvider.newInputStream(FileSystemProvider.java:384) at java.nio.file.Files.newInputStream(Files.java:152) at java.nio.file.Files.newBufferedReader(Files.java:2784) at java.nio.file.Files.lines(Files.java:3744) at java.nio.file.Files.lines(Files.java:3785) |
По какой-то причине у меня было слишком много открытых файлов! Странно, разве Files.lines () не закрывает файл?
Посмотрите код ниже ( run3() ), где я создал, воспроизвел проблему:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
|
package utility;import java.io.BufferedReader;import java.io.FileReader;import java.io.FileWriter;import java.io.IOException;import java.nio.file.Files;import java.nio.file.Path;import java.nio.file.Paths;import java.util.Date;import java.util.stream.Stream;public class Test2 { public static void main(String[] args) throws IOException{ int times = 100_000; Path path = Paths.get("/tmp", "date.txt"); Test2 t2 = new Test2(); t2.setDate(path); for (int i = 0; i < times; i++) { t2.run1(path); } for (int i = 0; i < times; i++) { t2.run2(path); } for (int i = 0; i < times; i++) { t2.run3(path); //throws exception too many files open } System.out.println("finished"); } public String run1(Path path){ try(BufferedReader br = new BufferedReader(new FileReader(path.toFile()))){ return br.readLine(); } catch (IOException e) { throw new AssertionError(e); } } public String run2(Path path){ try(Stream<String> stream = Files.lines(path)) { return stream.findFirst().get(); } catch (IOException e) { throw new AssertionError(e); } } public String run3(Path path) throws IOException{ return Files.lines(path).findFirst().get(); } public void setDate(Path path) { try (FileWriter writer = new FileWriter(path.toFile())){ writer.write(new Date().toString()); writer.flush(); } catch (IOException e) { throw new AssertionError(e); } }} |
Мой код выглядел примерно так, как run3() которая run3() исключение. Я доказал это, выполнив команду Unix lsof (выводит список открытых файлов) и заметил множество примеров открытия date.txt. Чтобы убедиться, что проблема действительно в Files.lines() я убедился, что код run1() с run1() с использованием BufferedReader , что он и сделал. Прочитав исходный код для Files я понял, что поток должен быть создан в автоматическом закрытии . Когда я реализовал это в run2() код снова run2() нормально.
На мой взгляд, я не думаю, что это не особенно интуитивно понятно. Это действительно портит один лайнер, когда вы должны использовать автоматическое закрытие. Я предполагаю, что коду нужен сигнал о том, когда закрывать файл, но было бы неплохо, если бы он был скрыт от нас. По крайней мере, это должно быть выделено в JavaDoc, что не является 🙂
| Ссылка: | Подводный камень Java 8 — Остерегайтесь Files.lines () от нашего партнера по JCG Даниэля Шая в блоге Rational Java . |