В 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 . |