IT 이모저모

자바 9 개선점 - 1

exien 2018. 3. 30. 14:56

직관적 인 Collection 작성 및 Stream 처리 개선

 지금까지보다 Collection과 Stream 처리를 손쉽게 작성할되었습니다.

직관적 인 Collection 작성

 List와 Set 또는 Map 같은 컬렉션의 개체가 목록 1과 같이 간단하게 작성할 수있게되었습니다.

목록 1 Collection의 작성 방법 예 (src / main / com / coltware / part6 / jep269 / CollectionUpdates.java 발췌)
// (1) 추가 항목을 순서대로 설명 final Set < Object > of = Set . of ( "a" , "b" , "c" ); final List < String > list = List . of ( "l1 " , "l2 " , "l3 " );
  
  

// (2) Map 형식은 Key, Value, Key, Value처럼 계속 설명 할 수 final Map < String , String > map1 = Map . of ( "k1" , "v1" , "k2" , "v2" );
  

// (3) 이와 같이 설명 할 수 있습니다. final Map < String , String > map2 = Map . ofEntries ( Map . entry ( "k1" , "v1" ) Map . entry ( "k2" , "v2" ));
  

 (1)과 같이 매우 직관적 인 설명을 할 수있게되어 있습니다. 또한 Map 형식의 경우 (2)와 같이 Key와 Value를 순서에 연결할 수 있습니다. 또한 다소 성가신 일입니다 만,이 기술 방법 이외에도 (3)과 같이 설명 할 수 있습니다.

 Listing 1에서 컬렉션 만들 때 final을 붙여 선언하고 있습니다 만,이 선언의 유무에 관계없이 만든 컬렉션의 변경은 할 수 없습니다.

Stream 처리 개선

 Stream 처리에서도 유용한 방법이 몇 가지 추가되었습니다.

iterate 메소드

 먼저 iterate 메소드 입니다. 이 iterate 메소드는 끝없는 스트림뿐만 아니라 for 문처럼 종료 조건부로 설명 할 수 있습니다.

 목록 2는 Java 9 및 이전에 1에서 9까지의 숫자를 표시 할 때의 예입니다.

목록 2 종료 조건을 지정할 수있게되었다 iterate 메소드 (src / main / com / coltware / part6 / jep269 / CollectionUpdates.java 발췌)
// Java 9에서 설명 예 Stream . iterate ( 1 , i -> i < 10 , i -> i + 1 ) forEach ( System . out : println ); // Java 9 이전의 기술 예 Stream . iterate ( 1 , i -> i + 1 ). limit ( 9 ) forEach ( System . out : println );
  

 

ofNullable 메소드

 ofNullable 메소드 가 새롭게 추가되었습니다. 이 메소드를 사용하면 null 이외의 데이터를 다룰 때, 목록 3에 대한 설명이 가능합니다.

목록 3 null 이외의 값 목록을 표시하는 방법 (src / main / com / coltware / part6 / jep269 / CollectionUpdates.java 발췌)
// (1) Java 9에서 설명 예 Stream . of ( null , 7 , 2 , 4 , 3 , 5 , null , 6 ). flatMap ( i -> Stream . ofNullable ( i )) forEach ( System . out :: println ); // (2) Java 9 이전의 기술 예 Stream . of ( null , 7 , 2 , 4 , 3
 

, 5 , null , 6 ). flatMap ( i -> { if ( i == null ) { return Stream . empty (); } else { return Stream . of ( i ); } }) forEach ( System . out : : println ); 
   
      
  
  
      
  

takeWhile / dropWhile 메소드

 계속되어서는, takeWhile 메소드 와 dropWhile 메소드 입니다.

 takeWhile 조건에 일치하는 동안 만 처리를 실시하고 조건에 맞지 않게되면, 그 이후는 처리를 행하지 않습니다. dropWhile는 반대로, 조건에 일치하는 동안에 만 처리를하지 않고, 조건에 일치하지 않는 데이터가 나타나고 이후 처리를하는 경우에 사용할 수 있습니다.

 예를 들어, 1에서 9까지의 숫자가 연속 같은 경우를 상정 한 케이스에서 takeWhile과 dropWhile의 행동을 보여주는 예가 목록 4입니다. 이 예제에서는 이미지하기 쉽도록 데이터를 소수로하고 있습니다 만, 끝없는 스트림 등 모든 데이터를 처리하지 않고 끝나므로 효율적으로 데이터를 처리 할 수 ​​있습니다.

목록 4 1에서 4 이외의 값이 나타난 시점에서 그 이전 데이터 스트림 처리를하는 (src / main / com / coltware / part6 / jep269 / CollectionUpdates.java 발췌)
// (1) 1,2,3,4 출력되는 Stream . of ( 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 1 , 2 , 3 ). takeWhile ( i -> { if ( i > 0 && i < 5 ) { return true ; } else { return false ; } }).
 
      
     
  
  
     
  
forEach ( System . out : println );

// (2) 5,6,7,8,9,1,2,3가 출력되는 Stream . of ( 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 1 , 2 , 3 ) dropWhile ( i -> { if ( i > 0 && i < 5 ) { return true ; } else { return false ; } }).
 
      
     
  
  
     
  
forEach ( System . out : println );

 (1)의 takeWhile는 처음 1-4의 데이터를 출력합니다. 두 번째로 나타나는 1에서 3까지의 값은 그 전에 조건에 일치하지 않는 데이터 인 5가 나타난 시점에서 처리의 대상이되지 않기 때문에 출력되지 않습니다.

 또한 (2)는 동일한 데이터 조건을 dropWhile로 대체했을 때의 처리입니다. 이 경우 조건에 일치하지 않는 1에서 4까지의 데이터가 아닌 5가 나타난 시점에서 처리 대상이 두 번째로 나타나는 1에서 3도 처리의 대상이됩니다.

개선 된 Process API

 Java에서는 기본 프로세스를 처리 Process 클래스와 ProcessBuilder 클래스가 존재합니다. 하지만, 이것만으로는 실현 될 수없는 경우도 있습니다.

 예를 들어, 자신이 데몬 화하는 프로그램을 제작하고, 시작할 때 스스로 PID 파일을 작성할 수 있습니다. 그러나 자신의 프로세스 ID는 쉽게 얻을 수 없기 때문에, JMX 등을 이용하는 등 약간의 궁리가 필요했습니다. Java 9는 프로세스에 대한 정보를 얻기 위해 ProcessHandle 인터페이스 가 추가 된 목록 5와 같이 프로세스에 대한 정보를 얻을 수 있습니다.

목록 5 프로세스 ID를 확인 (src / main / com / coltware / part6 / jep102 / ProcessApi.java 발췌)
// (1) 자신의 프로세스를 가져 ProcessHandle handle = ProcessHandle . current (); long pid = handle . pid ();
 


// (2) 부모 프로세스를 가져 ProcessHandle parent = handle . parent (). get ();


// (3) 프로세스 ID에서 얻을 ProcessHandle handle2 = ProcessHandle . of ( pid ). get ();
 

// (4) 프로세스에 대한 정보를 얻을 ProcessHandle . Info info = handle2 . info (); String command = info . command (). get (); String user   = info . user (). get ();


 자신의 프로세스를 얻으려면 (1)과 같이 ProcessHandle의 current 메소드로 취득합니다. 부모 프로세스를 얻으려면 (2)와 같이 parent 메소드가 준비되어 있습니다. 이외에도 프로세스 종료의 지시 나 프로세스가 살아 있는지 등의 확인도 가능합니다. 또한 미리 프로세스 ID를 알고있는 경우, (3)과 프로세스 ID를 지정하여 정보를 얻을 수 있습니다.

 또한, (4)와 같이 프로세스에 대한 정보를 얻기 위해 ProcessHandle.Info 인터페이스도 추가되어 있고, 다음의 정보를 얻을 수 있습니다.

  • 프로세스를 시작할 때 인수 (arguments 메소드)
  • 프로세스를 실행 한 명령어 (command 메소드)
  • 프로세스를 시작할 때 명령 줄 (commandLine 메소드)
  • 프로세스의 시작 시간 (startInstant 메소드)
  • 프로세스의 총 cpu 시간 (totalCpuDuration 메소드)
  • 프로세스를 실행시킨 사용자 (user 메소드)

 또한 ProcessBuilder 클래스에서는 지금까지 start 메소드를 사용하여 외부 프로그램을 실행했습니다. 이번 startPipeline 메소드가 추가되어 파이프 (|)를 이용한 프로세스의 처리를 쉽게 수행 할 수있게되었습니다.

 Java에 대한 프로세스가 현재 몇개 있는지를 조사 처리에 파이프를 사용하여 실행했을 때의 예를 나열 6입니다.

목록 6 프로세스 ID를 확인 (src / main / com / coltware / part6 / jep102 / ProcessApi.java 발췌)
// (1) 프로세스 목록에서 결과에서 "java"라는 문자를 포함한 행 수를 얻을 // $ ps -ef | grep java | wc -l


// (2) ProcessBuilder의 startPipeline 메소드를 사용하는 경우

// (3) "ps -ef"를 실행하는 프로세스를 시작하는 처리 ProcessBuilder ps = new ProcessBuilder () command ( "ps" , "-ef" ); // (4) "grep java"를 실행 프로세스를 시작하는 처리 ProcessBuilder grep = new ProcessBuilder () command ( "grep" , "java" ); // (5) "wc -l"을 실행하는 프로세스를 시작하는 처리 ProcessBuilder wc = new ProcessBuilder (). command ( "wc" , "-l" ); try {
  

  

  
 

  // (6) 각각의 프로세스를 파이프로 연결하여 실행하는 List < Process > processes = ProcessBuilder . startPipeline ( List . of ( ps , grep , wc )); // (7) 마지막 과정 (wc)를 취득 하고 프로세스의 종료를 기다릴 Process p = processes . get ( processes . size () - 1 ); int wait = p . waitFor (); // (8) wc 프로세스의 출력을 표시하는 BufferedReader reader
   
  
    
  
  
  = new BufferedReader ( new InputStreamReader ( p . getInputStream ())); String line ; while (( line = reader . readLine ()) ! = null ) { System . out . println ( line ); } } catch ( Exception ex ) { }   
    
        
        
    
  
  

 (1)은 Unix 계열의 OS에서 파이프를 사용하여 명령을 실행했을 때의 예입니다. 이 처리와 같이 파이프를 사용하여 Java에서 실행 한 것이 (2) 이후의 처리됩니다.

 (3) (4) (5)에서는 각 프로세스를 시작하기위한 준비를합니다. 그리고 (6)과 같이 startPipeline 메소드를 사용하여 그들을 시작합니다. (7)는 마지막 프로세스의 종료를 기다리고 (8)는 그 결과를 표준 출력에 표시하고 있습니다.

 Java 코드로하면 작성한 코드는 늘어납니다 만, 명령 줄에서 실행할 때와 비슷한 흐름으로 처리를 작성할 수 있습니다.