2014-02-08 5 views
17

나는 반복적으로재귀 스트림

자바 (8) 모든 파일과 디렉토리 만 재귀없이 반환하는 listFiles 방법을 제공하는 자바 (8)를 사용하여 내 컴퓨터의 모든 파일을 나열합니다. 어떻게 그것을 (돌연변이 컬렉션을 사용하지 않고) 파일의 전체 재귀 목록을 얻으려면 사용할 수 있습니까? (이유는 확실하지)

참고

static Function<Path, Stream<Path>> listFiles = p -> { 
    if (p.toFile().isDirectory()) { 
     try { return Files.list(p); } 
     catch (Exception e) { return Stream.empty(); } 
    } else { 
     return Stream.of(p); 
    } 
}; 

public static void main(String[] args) throws IOException { 
    Path root = Paths.get("C:/temp/"); 
    Files.list(root).flatMap(listFiles).forEach(System.out::println); 
} 

그리고 return Files.list(p).flatMap(listFiles);를 사용하여 컴파일되지 않습니다 :

나는 아래의 코드를 시도했지만 그것은 단지 깊은 한 수준을 간다 내가 관심이 아니다 FileVisitors 또는 외부 라이브러리와 관련된 솔루션

+1

'Files.walkFileTree'? 아니면 정말 재귀 적 스트림을 사용하고 싶습니까? :-) –

+1

@StuartMarks 예 재귀 스트림을 사용하고 싶습니다! 그리고 walkFileTree는 아주 길어서 "one-liner"를 찾으려고했습니다. – assylias

+1

와우, 나는'Files.walk'을 의미했습니다. Path를 취해 Stream 를 돌려줍니다. –

답변

17

파일 시스템을 반복적으로 따라 가면서 Path 스트림을 생성하는 새로운 API는 Files.walk입니다.

당신이 정말로 재귀 스트림을 생성 할 경우

는 (반드시 파일 트리를 도보로하지,하지만 난 예를 들어 있음을 계속 사용할 것) 방법 참조를 사용하여 재귀를 달성하기 위해, 그것은 좀 더 간단 할 수 있습니다

class RecursiveStream { 
    static Stream<Path> listFiles(Path path) { 
     if (Files.isDirectory(path)) { 
      try { return Files.list(path).flatMap(RecursiveStream::listFiles); } 
      catch (Exception e) { return Stream.empty(); } 
     } else { 
      return Stream.of(path); 
     } 
    } 

    public static void main(String[] args) { 
     listFiles(Paths.get(".")).forEach(System.out::println); 
    } 
} 

메소드 참조는 기능적 인터페이스와 동일한 "모양"(인수 및 반환 유형)을 가진 명명 된 메소드를 해당 기능 인터페이스에 적용하는 데 매우 유용합니다. 또한 인스턴스 또는 정적 변수에 람다를 저장하고 자체적으로 재귀 적으로 호출하여 잠재적 인 초기화 원형도를 방지합니다.

3

메서드 참조를 통해 해당 함수 정의 내의 함수를 참조하는 것은 명백하게 불가능하지만 람다와 함께 작동합니다.

따라서 함수에서 return Files.list(p).flatMap(listFiles);은 컴파일되지 않지만 return Files.list(p).flatMap(q -> listFiles.apply(q));은 컴파일됩니다.

는 재귀 적으로 주어진 폴더의 모든 파일을 인쇄 :

static final Function<Path, Stream<Path>> listFiles = p -> { 
    if (p.toFile().isDirectory()) { 
     try { return Files.list(p).flatMap(q -> listFiles.apply(q)); } 
     catch (Exception e) { return Stream.empty(); } 
    } else { 
     return Stream.of(p); 
    } 
}; 

public static void main(String[] args) throws IOException { 
    Path root = Paths.get("C:/temp/"); 
    Files.list(root).flatMap(listFiles).forEach(System.out::println); 
} 

하지만 지적이 불필요 :

Files.walk(root).forEach(System.out::println); 

않습니다 같은 일 ...

+0

'정의되기 전에 필드를 참조 할 수 없다.' – nobeh

+0

b128에서 컴파일한다 – assylias

+0

그냥주의해야한다. 이것은'listFiles '이 예에서와 같이 정적 필드이므로 다른 경우에는 작동하지 않습니다. 그럼에도 불구하고 최신 JDK에서는 필드를 처음 'null'로 초기화하고 돌아 서서 람다를 사용하여 다시 초기화하지 않으면 컴파일 할 수 없습니다. 물론,이 모든 것은 매우 냄새가 난다. – jkschneider