Collection 팩토리 메소드
가변 리스트 만들기
List<String> friends1 = new ArrayList<>();
friends1.add("Raphael");
friends1.add("Olivia");
friends1.add("Thibaut");
고정 크기의 리스트 만들기
List<String> friends2 = Arrays.asList("Raphael", "Olivia"); // fixed size
friends2.set(0, "Richard");
// friends.add("Thibaut"); // throws UnsupportedOperationException
한 발짝 더 나아가 불변 리스트 만들기 (Java 9 부터 지원)
List<String> friends3 = List.of("Raphael", "Olivia", "Thibaut"); // immutable list
// friends3.add("Chih-Chun"); // UnsupportedOperationException
// friends3.set(0, "Park"); // UnsupportedOperationException
List 인터페이스를 보면 List.of가 많이 구현되어있는데, 아래처럼 E의 인수가 없을때 부터 여러개가 들어오는 것에 따라서 오버로딩 되어서 구현되어있다, 0개~10개까지 구현이 되어있다. 가변인수로 받게 되면 내부적으로 추가 배열을 할당해서 리스트로 감싸서 비용이 발생하기 때문에 10개까지는 조금 더 성능을 내기 위해서 이와 같이 되어있다고 한다.
static <E> List<E> of()
static <E> List<E> of(E e1)
static <E> List<E> of(E e1, E e2)
....
static <E> List<E> of(E... elements)
참고로 Set.of(), Map.of()도 0~10개가 동일하게 구현되어 되어있다.
불변 집합 만들기
Set<String> friendsSet = Set.of("Raphael", "Olivia", "Thibaut");
// 중복 요소로 인해 throws IllegalArgumentException
// Set<String> friendsSet2 = Set.of("Raphael", "Olivia", "Olivia");
불변 맵 만들기
import static java.util.Map.entry;
Map<String, Integer> ageOfFriends = Map.of("Raphael", 30, "Olivia", 25, "Thibaut", 26);
System.out.println(ageOfFriends);
Map<String, Integer> ageOfFriends2 = Map.ofEntries(
entry("Raphael", 30),
entry("Olivia", 25),
entry("Thibaut", 26)
);
removeIf
리스트에서 필터링을 할 때 filter()를 쓸 수도 있지만, removeIf()를 쓸 수도 있다. 일반 for문으로 짰을 때는 다음과 같은데, ConcurrentModificationException이 일어날 수 있다.
List<Transaction> transactions = new ArrayList<>();
transactions.add(new Transaction(new Trader("A", "a"), 100, 1000));
transactions.add(new Transaction(new Trader("B", "b"), 200, 2000));
for (Transaction transaction: transactions) {
if (Character.isDigit(transaction.getReferenceCode().charAt(0))) {
transactions.remove(transaction); // ConcurrentModificationException
}
}
왜냐면 iterator가 돌면서 next()를 해야되는데 지워버렸을 수 있으니까
그래서 iterator.hasNext()를 체킹하고 넘어가야 에러가 나지 않지만 코드가 장황해진다.
Iterator<Transaction> iterator = transactions.iterator();
while(iterator.hasNext()) {
Transaction transaction = iterator.next();
if (Character.isDigit(transaction.getReferenceCode().charAt(0))) {
iterator.remove();
}
}
removeIf()를 쓴다면
transactions.removeIf(transaction -> Character.isDigit(transaction.getReferenceCode().charAt(0)));
한 줄로 정리가 된다.
replaceAll
문자열 리스트의 각 앞자리만 대문자로 바꿔야 한다면
List<String> referenceCodes = Arrays.asList("a7sj13d", "bidj3fd", "psk39kcd");
referenceCodes.stream()
.map(code -> Character.toUpperCase(code.charAt(0)) + code.substring(1))
.collect(Collectors.toList())
.forEach(System.out::println);
이렇게 하면 요구사항에 만족할 수 있지만, 새로운 문자열 컬렉션을 만들게 된다.
기존의 컬렉션을 활용해서 바로 바로 바꾸고 싶다면
referenceCodes.replaceAll(code -> Character.toUpperCase(code.charAt(0)) + code.substring(1));
System.out.println(referenceCodes);
맵 처리
forEach
Java 8 이전에는 map을 iterating 하려면 entrySet을 활용해야 했다.
for (Map.Entry<String, Integer> entry: ageOfFriends.entrySet()) {
String friend = entry.getKey();
Integer age = entry.getValue();
System.out.println(friend + " is " + age + " years old");
}
Java 8 부터는 BiConsumer를 인수로 받는 forEach를 쓸 수 있게 되었다.
ageOfFriends.forEach((friend, age) -> System.out.println(friend + " is " + age + " years old"));
정렬
Entry.comparingByValue
Entry.comparingByKey
말그대로 key 순서대로 혹은 value 순서대로 정렬할 수 있도록 하는 유틸리티이다.
Map<String, String> favoriteMoviews = Map.ofEntries(
entry("Cristina", "Matrix"),
entry("Raphael", "Star Wars"),
entry("Olivia", "James Bond")
);
favoriteMoviews.entrySet()
.stream()
.sorted(Map.Entry.comparingByKey())
.forEachOrdered(System.out::println);
/* 실행 결과
Cristina=Matrix
Olivia=James Bond
Raphael=Star Wars
*/
getOrDefault
nvl 같은거라고 보면 된다. null일 경우 디폴트 값을 리턴. 첫 번째 인수가 key, 두 번째 인수가 defaultValue
System.out.println(favoriteMoviews.getOrDefault("Olivia", "Matrix")); // James Bond
System.out.println(favoriteMoviews.getOrDefault("Thibaut", "Matrix")); // Matrix
remove
이전에는 map이 key를 갖고 있는지 없는지 체크하면서 value를 꺼내는 작업을 했다면, Java 8부터는 remove가 key가 없을 경우에는 아무 연산을 하지 않고, key가 있다면 삭제를 하도록 개선되었다.
favoriteMovies.remove(key, value);
merge
Cristina를 합치게 되면 Matrix로 덮어씌워진다.
Map<String, String> family = Map.ofEntries(entry("Teo", "Star Wars"), entry("Cristina", "James Bond"));
Map<String, String> friends = Map.ofEntries(entry("Raphael", "Star Wars"), entry("Cristina", "Matrix"));
Map<String, String> everyone = new HashMap<>(family);
everyone.putAll(friends);
System.out.println(everyone);
// {Cristina=Matrix, Raphael=Star Wars, Teo=Star Wars}
충돌이 나는 부분을 유연하게 대처하려면 merge를 쓰면 된다. 코드는 다소 어렵다.
friends.forEach((k, v) -> everyone.merge(k, v, (movie1, movie2) -> movie1 + " & " + movie2));
System.out.println(everyone);
// {Raphael=Star Wars, Cristina=James Bond & Matrix, Teo=Star Wars}
실제로 이러한 코드를 언제 짜볼까 싶지만, 컬렉션 API들은 일단 알고봐야 실무에서 다시 꺼내 볼 수 있는 것 같다.
책에서 나오는 ConcurrentHashMap 예제는 컴파일이 안 되었고, 내용도 다소 빈약해서 나중에 따로 다뤄봐야겠다.
'Programming > Java' 카테고리의 다른 글
Modern Java In Action 정리 - 12 새로운 날짜와 시간 API (0) | 2020.07.04 |
---|---|
Modern Java In Action 정리 - 11 null 대신 Optional 클래스 (0) | 2020.06.27 |
Modern Java In Action 정리 - 10장 람다를 이용한 도메인 전용 언어 (0) | 2020.06.22 |
Modern Java In Action 정리 - 9장 리팩터링, 테스팅, 디버깅 (0) | 2020.06.13 |
[Java] Interface vs Abstract class (0) | 2019.05.07 |