flatMap을 사용하여 하위 디렉터리 나열
우리는 지정된 디렉터리에 있는 파일을 나열하는 방법을 이전에 살펴보았습니다. 지정된 디렉터리(깊이는 1)의 직접 하위 디렉터리를 순회하는 방법을 살펴보고 먼저 간단한 버전을 구현한 다음 보다 편리한 flatMap() 메서드를 사용하여 이를 구현합니다.
먼저 전통적인 for 루프를 사용하여 지정된 디렉터리를 탐색합니다. 하위 디렉터리에 파일이 있으면 목록에 추가하고, 그렇지 않으면 하위 디렉터리를 목록에 추가합니다. 마지막으로 모든 파일의 총 개수를 출력합니다. 코드는 아래와 같습니다. 이것은 하드 모드용입니다.
다음과 같이 코드 코드를 복사합니다.
공개 정적 무효 listTheHardWay() {
List<File> 파일 = new ArrayList<>();
File[] filesInCurrentDir = new File(".").listFiles();
for(파일 파일 : filesInCurrentDir) {
File[] filesInSubDir = file.listFiles();
if(filesInSubDir != null) {
files.addAll(Arrays.asList(filesInSubDir));
} 또 다른 {
파일.추가(파일);
}
}
System.out.println("개수: " + files.size())
}
먼저 현재 디렉터리에서 파일 목록을 가져온 다음 이를 탐색합니다. 각 파일에 하위 파일이 있으면 목록에 추가합니다. 여기에는 문제가 없지만 가변성, 기본 유형 편집증, 명령성, 코드 장황함 등 몇 가지 일반적인 문제가 있습니다. flatMap()이라는 작은 메서드가 이러한 문제를 해결할 수 있습니다.
이름에서 알 수 있듯이 이 방법은 매핑 후에 평면화됩니다. map()과 마찬가지로 컬렉션의 요소를 매핑합니다. 그러나 map() 메서드와 달리 map() 메서드의 람다 식은 요소만 반환하며 여기서 반환되는 것은 Stream 개체입니다. 따라서 이 방법은 여러 스트림을 평면화하고 내부의 각 요소를 평면화된 스트림에 매핑합니다.
flatMap()을 사용하여 다양한 작업을 수행할 수 있지만 당면한 문제는 그 가치를 보여줍니다. 각 하위 디렉터리에는 파일 목록이나 스트림이 있으며 현재 디렉터리 아래의 모든 하위 디렉터리에 있는 파일 목록을 가져오려고 합니다.
일부 디렉터리는 비어 있거나 하위 요소가 없을 수 있습니다. 이 경우 빈 디렉터리나 파일을 스트림 개체로 래핑합니다. 파일을 무시하려는 경우 JDK의 flatMap() 메서드는 빈 파일을 매우 잘 처리할 수 있으며 null 참조를 빈 컬렉션으로 스트림에 병합합니다. flatMap() 메소드의 사용법을 살펴보겠습니다.
다음과 같이 코드 코드를 복사합니다.
공개 정적 무효 betterWay() {
목록<파일> 파일 =
Stream.of(새 파일(".").listFiles())
.FlatMap(파일 -> file.listFiles() == null ?
Stream.of(파일) : Stream.of(file.listFiles()))
.collect(toList());
System.out.println("개수: " + files.size());
}
먼저 현재 디렉터리의 하위 파일 스트림을 얻은 다음 flatMap() 메서드를 호출합니다. 그런 다음 지정된 파일의 하위 파일 스트림을 반환하는 람다 식을 이 메서드에 전달합니다. flatMap() 메서드는 현재 디렉터리의 모든 하위 디렉터리에 있는 파일 모음을 반환합니다. Collectors에서는 Collect() 메서드와 toList()( 메서드를 사용하여 목록으로 수집합니다.
flatMap()에 전달하는 람다 표현식은 파일의 하위 파일을 반환합니다. 그렇지 않은 경우 파일의 스트림이 반환됩니다. flatMap() 메서드는 이 스트림을 스트림 컬렉션으로 우아하게 매핑한 다음 컬렉션을 평면화하고 마지막으로 단일 스트림으로 병합합니다.
flatMap() 메서드는 많은 개발 작업을 줄여줍니다. 이는 종종 튜플이라고 불리는 두 개의 연속 작업을 하나의 우아한 작업으로 결합합니다.
우리는 flatMap() 메서드를 사용하여 바로 하위 디렉터리에 있는 모든 파일을 나열하는 방법을 이미 알고 있습니다. 파일 수정 작업을 모니터링해 보겠습니다.
파일 수정 모니터링
우리는 파일과 디렉터리를 찾는 방법을 이미 알고 있지만 파일이 생성, 수정 또는 삭제될 때 프롬프트 메시지를 받고 싶다면 이 방법도 매우 간단합니다. 이러한 메커니즘은 구성 파일 및 시스템 리소스와 같은 특수 파일의 변경 사항을 모니터링하는 데 매우 유용합니다. 파일 수정을 모니터링하는 데 사용할 수 있는 Java 7, WatchService에 도입된 이 도구를 살펴보겠습니다. 아래에서 볼 수 있는 많은 기능은 JDK 7에서 가져온 것이지만 여기서 가장 큰 개선점은 내부 반복자가 가져온 편리함입니다.
먼저 현재 디렉터리에서 파일 수정을 모니터링하는 예를 작성해 보겠습니다. JDK의 Path 클래스는 관찰자 서비스의 팩토리인 파일 시스템의 인스턴스에 해당합니다. 다음과 같이 이 서비스에 대한 알림 이벤트를 등록할 수 있습니다.
다음과 같이 코드 코드를 복사합니다.
최종 경로 경로 = Paths.get(".");
최종 WatchService watchService =
경로.getFileSystem()
.newWatchService();
path.register(watchService, StandardWatchEventKinds.ENTRY_MODIFY);
System.out.println("다음 1분 이내에 변경된 파일을 보고합니다...");
현재 디렉터리의 수정 사항을 관찰하기 위해 WatchService를 등록했습니다. 이 WatchService를 폴링하여 디렉터리에 있는 파일의 수정 작업을 얻을 수 있으며 WatchKey를 통해 이러한 변경 사항을 우리에게 반환합니다. 키가 있으면 모든 이벤트를 반복하여 파일 업데이트에 대한 세부 정보를 얻을 수 있습니다. 여러 파일이 동시에 수정될 수 있으므로 폴링 작업이 여러 이벤트를 반환할 수 있습니다. 폴링 및 순회 코드를 살펴보겠습니다.
다음과 같이 코드 코드를 복사합니다.
최종 WatchKey watchKey = watchService.poll(1, TimeUnit.MINUTES);
if(watchKey != null) {
watchKey.pollEvents()
.개울()
.forEach(이벤트 ->
System.out.println(event.context()));
}
여기에서 볼 수 있듯이 Java 7과 Java 8의 기능이 동시에 나타납니다. pollEvents()에서 반환된 컬렉션을 Java 8 스트림으로 변환한 다음 내부 반복자를 사용하여 각 파일에 대한 자세한 업데이트 정보를 인쇄합니다.
이 코드를 실행한 다음 현재 디렉터리에 있는 Sample.txt 파일을 수정하여 프로그램이 이 업데이트를 감지할 수 있는지 확인해 보겠습니다.
다음과 같이 코드 코드를 복사합니다.
다음 1분 이내에 변경된 파일을 보고합니다...
샘플.txt
이 파일을 수정하면 프로그램은 파일이 수정되었다는 메시지를 표시합니다. 이 기능을 사용하여 다양한 파일에 대한 업데이트를 모니터링한 다음 해당 작업을 수행할 수 있습니다. 물론 파일 생성이나 삭제 작업만 등록할 수도 있습니다.
요약
람다 식과 메서드 참조를 사용하면 문자열 및 파일 조작, 사용자 지정 비교기 생성과 같은 일반적인 작업이 더 쉽고 간결해집니다. 익명의 내부 클래스는 우아해지며, 해가 뜬 후 아침 안개처럼 변동성이 사라집니다. 이 새로운 스타일로 코딩하는 또 다른 이점은 JDK의 새로운 기능을 사용하여 대규모 디렉토리를 효율적으로 탐색할 수 있다는 것입니다.
이제 람다 식을 만들고 메서드에 전달하는 방법을 알았습니다. 다음 장에서는 기능적 인터페이스와 람다 표현을 사용하여 소프트웨어를 설계하는 방법을 소개합니다.