List<String> friends = new ArrayList<>();
friends.add("Raphael");
friends.add("Olivia");
friends.add("Thibaut");
세 문자를 리스트로 저장하려면 위와 같이 많은 코드가 필요하다.
List<String> friends = Arrays.asList("Raphael", "Olivia", "Thibaut");
Arrays.asList()
메서드를 사용하면 간단하게 줄일 수 있지만 추가, 삭제 등의 연산이 불가능하다.
그렇다면 집합은 어떻게 만들까?
Set<String> friends
= new HashSet<>(Arrays.asList("Raphael", "Olivia", "Thibaut"));
컬렉션을 인수로 받는 HashSet의 생성자를 사용할 수 있다.
Set<String> friends
= Stream.of("Raphael", "Olivia","Thibaut")
.collect(Collectors.toSet());
또는 스트림 API를 사용할 수 있다.
하지만 위의 두 방법은 내부적으로 불필요한 객체 할당이 필요하다.
List<String> friends = List.of("Raphael", "Olivia", "Thibaut");
자바9부터 추가된 List.of()
팩토리 메서드를 사용할 수 있다. 불변 리스트를 만든다.
왜 List.of(E… elements)
로 다중 요소를 받을 수 있게 하지 않고, 위와 같이 오버로딩 했을까?
Set<String> friends = Set.of("Raphael", "Olivia", "Thibaut");
집합의 경우도 팩토리 메서드가 있다. 중복된 요소가 있으면 IllegalArgumentException
이 발생한다.
Map<String, Integer> ageOfFriends
= Map.of("Raphael", 30, "Olivia", 25, "Thibaut", 26);
Map.of()
를 사용해서 Key와 Value를 번갈아 제공하는 방법이 있다.
List.of()
와 마찬가지로 불변 객체가 만들어진다. 수정이 가능할까??
Map<String, Integer> ageOfFriends
= Map.ofEntries(entry("Raphael", 30),
entry("Olivia", 25),
entry("Thibaut", 26));
Map.Entry<K, V> 객체를 사용하는 Map.ofEntries()
로 위와 같이 각 엔트리를 넘길 수 있다. 가변 인수가 만들어지게 된다. 수정이 가능할까??
* `removeIf(Predicate filter)`: Predicate를 만족하는 모든 요소 제거
* `replaceAll(UnaryOperator operator)`: UnaryOperator로 요소를 변경
* `sort(Comparator c)`: 리스트를 정렬한다.
위의 메서드들은 컬렉션 상태를 변경한다. 왜 필요할까??
for(Apple apple: apples){
if(apple.getColor().equals(Color.RED)){
apples.remove(apple);
}
}
사과 리스트를 순환하면서 빨간색 사과를 제거하는 코드이다. 무엇이 문제일까?
위 코드는 ConcurrentModificationException
이 발생한다.
for-each 문은 컬렉션의
iterator()
을 사용한다.
Iterator 객체와 List 컬렉션의 상태가 동기화되지 않는다.
이를 해결하려면Iterator.remove()
를 호출하면 된다.for(Iterator<Apple> iterator = apples.iterator(); iterator.hasNext(); ){ Apple apple = iterator.next(); if(apple.getColor().equals(Color.RED)){ iterator.remove(); } }
위와 같이 동기화 문제를 해결할 수 있다.
apples.removeIf(apple -> apple.getColor().equals(Color.RED));
이러한 상황에서
removeIf()
를 사용하면 간단해지고 버그가 발생할 일도 줄어든다.
만약 제거하지 않고 리스트의 각 요소를 새로운 요소로 바꿔야 한다면??
apples.replaceAll(apple -> apple.setColor(Color.GREEN));
이런 식으로 모든 사과의 색을 초록색으로 변경할 수 있다.
for(Map.Entry<String, Integer> entry: ageOfFriends.entrySet()){
String friend = entry.getKey();
int age = entry.getValue();
System.out.println(friend + " 의 나이: " + age);
}
맵의 모든 이름과 나이 쌍을 출력한다.
ageOfFriends.forEach((friend, age)
-> System.out.println(friend + " 의 나이: " + age));
자바 8부터 Map의 인터페이스는 forEach(BiConsumer consumer)
를 지원하므로 간단하게 사용할 수 있다.
* Entry.comparingByValue()
* Entry.comparingByKey()
apples.entrySet().stream()
.sorted(Entry.comparingByValue())
.forEach(System.out::println);
위의 두 Comparator 팩토리를 사용해서 쉽게 정렬할 수 있다.
Map<String, Integer> friends = new HashMap<>();
int result = friends.getOrDefault("연어", 5);
맵에서 해당하는 키가 없을 경우 null 체킹을 getOrDefault()
를 사용해서 쉽게 할 수 있다.
* computeIfAbsent: 제공된 키가 없거나 저장된 값이 null이라면, 키를 사용해서 새 값을 계산하고 맵에 추가
* computeIfPresent: 위와 반대의 경우라면, 새 값을 계산하고 맵에 추가
* compute: 제공된 키로 새 값을 계산하고 맵에 추가
맵의 키가 존재하는지 여부에 따라 계산 후 결과를 저장해야 할 때 사용
String key = "연어";
int value = 180;
if(friends.containsKey(key) && value==friends.get(key)){
friends.remove(key);
}
Key와 Value가 모두 같은 지 확인하고 해당하는 Entry가 존재한다면 삭제하는 코드
friends.remove(key, value);
자바8부터 위와 같이 사용할 수 있다.
* replaceAll: BiFunction을 적용해서 각 항목의 값을 교체
* replace: 키가 존재하면 값을 교체, 키가 특정 값일 때만 값을 교체하는 오버로드 버전도 존재
이전에 살펴본 List의 replaceAll()
과 비슷하다.
Map<String, String> family = Map.ofEntries(
entry("Teo", "Star Wars"),
entry("Christina", "James Bond"));
Map<String, String> everyone = Map.ofEntries(
entry("Raphael", "Star Wars"));
everyone.putAll(family);
Map끼리 합칠 때에는 putAll()
을 사용해서 위와 같이 합칠 수 있다. 이때 중복된 키가 없어야 한다.
Map<String, String> family = Map.ofEntries(
entry("Teo", "Star Wars"),
entry("Christina", "James Bond"));
Map<String, String> everyone = Map.ofEntries(
entry("Teo", "Star Wars"),
entry("Christina", "James Bond"));
family.forEach((k, v) ->
everyone.merge(k, v, String::concat));
중복된 키가 있다면, merge()
를 사용해서 병합 후 저장한다.
합치려는 대상의 key가 없거나 value가 null이면 주어진 v 값으로 저장한다.
HashMap<String, Integer> map = new HashMap<>();
map.merge("hi", 1, Integer::sum); //1
map.merge("hi", 1, Integer::sum); //2
map.merge("hi", 1, Integer::sum); //3
내부 자료구조의 특정 부분만 락 -> 동시 추가 및 갱신 허용
* forEach: 각 entry에 주어직 액션을 실행
* reduce: 모든 entry를 reducing
* search: null이 아닌 값을 반환할 때까지 searching
위의 모든 메서드는 병렬 작업을 수행한다.
이때 parallelismThreshold 의 값이 한 스레드가 처리할 최대 entry 수 이다.
* 맵의 entry 갯수가 int 범위를 넘어갈 때는 `mappingCount()`
* 기존의 `keySet()`은 Map -> HashSet 으로 변경한다. ConcurrentHashSet으로 바꾸고 싶다면 `newKeySet()`을 사용한다.