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));
}
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);
}
근데 위의 소스는
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));
}
✏️ 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);
}
하지만 실행해보면
이러한 예외가 발생한다.
왜냐하면 스트림은 일회성이기 때문에 재사용할 수 없기 때문이다. 한 번 최종처리까지 간 스트림은 닫히게 되고 이는 재사용 할 수 없다.
그러면 어떻게 해결해야할까?
가장 먼저 생각이 드는 방법은 스트림이 닫히기 전에 한 번에 처리하는 것이다.
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);
가독성은 좋아지고 복잡성은 떨어졌다.
이러한 방식이 가능한 이유는 하나의 스트림을 두 개의 다운 스트림(DownStream)으로 쪼개 연산을 수행하고 마지막에 합치는 과정을 거치기 때문이다.
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));
}
참고로 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억
이런 결과가 나타난다.