Post

Java 12버전은 뭐가 달라졌나

개요

자바12는 이전 버전에 비해 크게 바뀐 것은 없다.

✏️ 1. New String and Files methods

String.indent()

이 함수는 메소드안에 있는 인자만큼 문자열을 들여쓰기 해준다.

여러줄에 걸쳐서 들여쓰기를 해야하는 경우 이 함수는 이를 감지하여 적용 시켜준다.

1
2
3
4
5
6
7
8
9
@Test
@DisplayName("String.indent() test")
void indentTest(){
    String s = "hello my name is kms";
    System.out.println(s);
    System.out.println(s.indent(4));
    s = "hello \n my \n name \n is \n kms";
    System.out.println(s.indent(4));
}

image

String.transform()

이 함수는 인자로 들어온 함수를 문자열에 적용한다.

1
2
3
4
5
6
7
8
9
@Test
@DisplayName("String.transform() test")
void transformTest(){
    String name="kms".transform(String::toUpperCase);
    Integer i = "0727".transform(Integer::valueOf);

    System.out.println(name);
    System.out.println(i);
}

image

근데 위의 소스는

1
2
3
4
5
6
7
8
9
@Test
@DisplayName("String.transform() test")
void transformTest(){
    String name="kms".toUpperCase();
    Integer i = Integer.valueOf("0727");

    System.out.println(name);
    System.out.println(i);
}

로도 쓸 수 있는데 transform()함수가 무슨 장점이 있는걸까?

transform()함수에 들어온 인자값은 런타임에 동적으로 작동한다. 이러한 점때문에 프로그램의 유연성을 높일 수 있고, 다양한 용도로 사용할 수 있게 한다.

반면에 밑의 소스 경우는 문자열 변환이 컴파일 시에 고정이 된다는 것이다.

Files.mismatch()

이름에서도 알 수 있다싶이 두 파일을 비교하여 파일의 내용이 같으면 -1을 리턴하고, 다르면 다른 부분의 위치를 반환한다. 그리고 만약 비교 도중에 한 파일이 먼저 EOF에 도달해버리면 해당 파일 내용의 길이를 리턴한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Test
@DisplayName("String.mismatch() test")
void fileMismatchTest() throws IOException {
    //hihihihihi?
    Path path1 = Path.of("test1.txt");
    Path path2 = Path.of("test1.txt");
    // hihihihihi?
    Path path3 = Path.of("test_success.txt");
    // hihihihihi
    Path path4 = Path.of("test_fail.txt");
    // hihihihihi?hihihihihi?
    Path path5 = Path.of("test_long.txt");

    //파일명, 파일 내용 같을 때
    System.out.println(Files.mismatch(path1,path2));
    //파일 내용만 같을 때
    System.out.println(Files.mismatch(path1,path3));
    //내용이 다를 때
    System.out.println(Files.mismatch(path1,path4));
    //한 파일이 먼저 끝나는 경우
    System.out.println(Files.mismatch(path1,path5));
}

image

✏️ 2. The Teeing Collector

어떤 경우에는 하나의 스트림을 두 개의 스트림으로 쪼개고 싶고, 어떤 경우에는 두 개의 스트림을 하나의 스트림으로 합치고 싶을 때가 있다.

밑의 예제는 랜덤한 정수를 가지는 1개의 스트림의 요소중 최댓값과 최솟값을 빼려는 예제다.

1
2
3
4
5
6
7
8
9
10
@Test
@DisplayName("The Teeing Collector test")
void TeeingCollectorTest(){
    Stream<Integer> numbers = new Random().ints(100).boxed();

    int min = numbers.collect(Collectors.minBy(Integer::compareTo)).orElseThrow();
    int max = numbers.collect(Collectors.maxBy(Integer::compareTo)).orElseThrow();
    long range = (long) max - min;
    System.out.println(range);
}

하지만 실행해보면

image

이러한 예외가 발생한다.

왜냐하면 스트림은 일회성이기 때문에 재사용할 수 없기 때문이다. 한 번 최종처리까지 간 스트림은 닫히게 되고 이는 재사용 할 수 없다.

그러면 어떻게 해결해야할까?

가장 먼저 생각이 드는 방법은 스트림이 닫히기 전에 한 번에 처리하는 것이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
int[] result = numbers.collect(
        () -> new int[] {Integer.MAX_VALUE, Integer.MIN_VALUE},
        (minMax,i) -> {
            if (i < minMax[0]) minMax[0] = i;
            if (i > minMax[1]) minMax[1] = i;
        },
        (minMax1, minMax2) -> {
            if (minMax2[0] < minMax1[0]) minMax1[0] = minMax2[0];
            if (minMax2[1] > minMax1[1]) minMax1[1] = minMax2[1];
        }
);
long range = (long) result[1] - result[0];
System.out.println(range);

가독성도 떨어지고 복잡해보인다.

여기서 Java12에서 추가된 Teeing Collector를 사용하면 두 개의 Collector를 조합해서 하나의 Collector로 만들어버릴 수 있다.

1
2
3
4
5
6
7
8
long range = numbers.collect(
        Collectors.teeing(
                Collectors.minBy(Integer::compareTo),
                Collectors.maxBy(Integer::compareTo),
                (min,max) -> (long) max.orElseThrow() - min.orElseThrow()
        )
);
System.out.println(range);

image

가독성은 좋아지고 복잡성은 떨어졌다.

이러한 방식이 가능한 이유는 하나의 스트림을 두 개의 다운 스트림(DownStream)으로 쪼개 연산을 수행하고 마지막에 합치는 과정을 거치기 때문이다.

image

tee는 관이나 파이프를 다른 관이나 파이프로 연결하는 것을 의미한다. 그래서 TeeingCollector는 두 개의 Collector를 하나의 Collector로 만들거나 하나의 Collector를 두 개의 Collector로 이어버린다.이러한 유래에서 오지 않을까 한다.

✏️ 3. Support for Compact Number Formatting

Java12에서는 Compact number formatting이라는 것을 제공하는데, 이건 사람들이 읽기 쉬운 표현으로 숫자를 변환해준다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Test
void compactNumberFormattingTest(){
    NumberFormat nfShort = NumberFormat.getCompactNumberInstance(Locale.US,NumberFormat.Style.SHORT);
    NumberFormat nfLong = NumberFormat.getCompactNumberInstance(Locale.US,NumberFormat.Style.LONG);

    System.out.println("        1,000 short -> " + nfShort.format(1_000));
    System.out.println("      456,789 short -> " + nfShort.format(456_789));
    System.out.println("    2,000,000 short -> " + nfShort.format(2_000_000));
    System.out.println("3,456,789,000 short -> " + nfShort.format(3_456_789_000L));
    System.out.println("34,456,789,000 short -> " + nfShort.format(34_456_789_000L));
    System.out.println();
    System.out.println("        1,000 long -> " + nfLong.format(1_000));
    System.out.println("      456,789 long -> " + nfLong.format(456_789));
    System.out.println("    2,000,000 long -> " + nfLong.format(2_000_000));
    System.out.println("3,456,789,000 long -> " + nfLong.format(3_456_789_000L));
}

image

참고로 Locale.KOREA로 바꾸면

1
2
3
4
5
        1,000 short -> 1천
      456,789 short -> 46만
    2,000,000 short -> 200만
3,456,789,000 short -> 35억
34,456,789,000 short -> 345억

이런 결과가 나타난다.

✏️ 4. Performance Improvements

This post is licensed under CC BY 4.0 by the author.