💡 이 장의 내용
- 컬렉션 팩토리 사용하기
- 리스트 및 집합과 사용할 새로운 관용 패턴 배우기
- 맵과 사용할 새로운 관용 패턴 배우기
8.1 컬렉션 팩토리
자바 9에서는 작은 컬렉션 객체를 쉽게 만들 수 있는 몇 가지 방법을 제공한다.
8.1.1) 리스트 팩토리
List.of 팩토리 메서드
- List.of("Raphael", "Olivia", "Thibaut"); 간단하게 리스트를 만들 수 있다.
- 불변의 리스트가 만들어져서 요소를 추가하거나 삭제할 수 없다.
- null 요소를 허용하지 않는다 -> 의도치 않은 버그를 방지하고 간결한 내부 구현 달성.
8.1.2) 집합 팩토리
Set.of 팩토리 메서드
- Set.of("Raphael", "Olivia", "Thibaut");
- 중복된 요소를 허용하지 않는다.
8.1.3) 맵 팩토리
Map.of 팩토리 메서드
- 열 개 이하의 키와 값 쌍을 가진 작은 맵을 만들 때 유용하다.
Map.ofEntries 팩토리 메서드
- 열 개 이상의 맵에서는 Map.Entry<K, V> 객체를 인수로 받으며 가변 인수로 구현된 Map.ofEntries 팩토리 메서드를 이용하는 것이 좋다.
- 키와 값을 감쌀 추가 객체 할당을 필요로 한다.
Map <String, Integer> ageOfFriends = Map.ofEntries(entry("Raphael", 30),
entry("Olivia", 25),
entry("Thibaut", 26));
지금까지 자바 9의 새로운 팩토리 메서드를 이용해 컬렉션을 쉽게 만드는 방법을 살펴봤다.
보통 컬렉션을 만들었으면 이를 처리해야 한다. 8.2 절에서는 리스트와 집합을 쉽게 처리할 수 있는 일반 패턴을 구현하는 새로운 개선사항을 배운다.
8.2 리스트와 집합 처리
자바 8에서 List, Set 인터페이스에 다음과 같은 메서드를 추가했다.
- removeIf : 프레디케이트를 만족하는 요소를 제거한다. List나 Set을 구현하거나 그 구현을 상속받은 모든 클래스에서 이용할 수 있다.
- replaceAll : 리스트에서 이용할 수 있는 기능으로 UnaryOperator 함수를 이용해 요소를 바꾼다.
- sort : List 인터페이스에서 제공하는 기능으로 리스트를 정렬한다.
이들 메서드는 호출한 컬렉션 자체를 바꾼다. 에러를 유발할 수 있는 이러한 기능을 왜 추가했을까?
8.2.1) removeIf 메서드
이 부분은 이전에 어리둥절 하며 지나갔던 부분을 다루고 있어서 지금 너무 기쁘기 때문에.. 자세히 적어보겠다
다음은 숫자로 시작되는 참조 코드를 가진 트랜잭션을 삭제하는 코드다.
for (Transaction transaction : transactions ) {
if (Character.isDigit(transaction.getReferenceCode().charAt(0))) {
transactions.remove(transaction);
}
}
안타깝게도 위 코드는 ConcurrentModificationException을 일으킨다. 왜 그럴까?
내부적으로 for-each 문은 Iterator를 사용하기 때문이다. 위 코드는 다음과 같이 해석된다.
for (Iterator<Transaction> iterator = transactions.iterator(); iterator.hanNext(); ) {
Transcaction transaction = iterator.next();
if (Character.isDigit(transaction.getReferenceCode().charAt(0))) {
transactons.remove(transaction);
}
}
- Iterator 객체, next(), hasNext()를 이용해 소스를 질의한다.
- Collection 객체 자체, remove()를 호출해 요소를 삭제한다.
결과적으로 반복자의 상태는 컬렉션의 상태와 서로 동기화되지 않는다. Iterator 객체를 명시적으로 사용하고 그 객체의 remove() 메서드를 호출함으로 이 문제를 해결할 수 있다.
for (Iterator<Transaction> iterator = transactions.iterator(); iterator.hasNext(); ) {
Transaction transaction = iterator.next();
if (Character.isDigit(transaction.getReferenceCode().charAt(0))) {
iterator.remove();
}
}
이 코드 패턴은 자바 8의 removeIf 메서드로 바꿀 수 있다. 그러면 코드가 단순해질 뿐 아니라 버그도 예방할 수 있다.
transactions.removeIf(transaction ->
Character.isDigit(transaction.getReferenceCode().charAt(0)));
하지만 때로는 요소를 제거하는게 아니라 바꿔야 하는 상황도 있다. 이런 상황에서는 replaceAll을 사용한다.
8.2.2) replaceAll 메서드
List 인터페이스의 replaceAll 메서드를 이용해 리스트의 각 요소를 새로운 요소로 바꿀 수 있다. 살짝 귀찮지만... 신기한 내용이니까 적어보겠다.
referenceCodes.stream()
.map(code -> Character.toUpperCase(code.charAt(0)) + code.substring(1))
.collect(Collectors.toList())
.forEach(System.out::println);
하지만 이 코드는 새 문자열 컬렉션을 만든다. 우리가 원하는 것은 기존 컬렉션을 바꾸는 것이라면 이렇게 할 수 있다.
for (ListIterator<String> iterator = referenceCodes.listIterator(); iterator.hasNext(); ) {
String code = iterator.next();
iterator.set(Character.toUpperCase(code.charAt(0)) + code.substring(1));
}
그리고 이러한 코드를 자바 8부터는 이렇게 구현할 수 있다.
referenceCodes.replaceAll(code -> Character.toUpperCase(code.charAt(0)) + code.substring(1));
8.3 절에서는 Map 인터페이스에 추가된 새 기능을 설명한다. 😃
'Book > 모던 자바 인 액션' 카테고리의 다른 글
[모던 자바 인 액션] Chapter 09. 리팩터링, 테스팅, 디버깅 (1) (4) | 2024.11.11 |
---|---|
[모던 자바 인 액션] Chapter 08. 컬렉션 API 개선 (2) (1) | 2024.11.09 |
[모던 자바 인 액션] Chapter 07. 병렬 데이터 처리와 성능 (2) (0) | 2024.11.01 |
[모던 자바 인 액션] Chapter 07. 병렬 데이터 처리와 성능 (1) (0) | 2024.10.29 |
[모던 자바 인 액션] Chapter 06. 스트림으로 데이터 수집 (2) (1) | 2024.10.25 |