티스토리 뷰
728x90
반응형
컬렉션 팩토리
- 자바 9에서는 작은 컬렉션 객체를 쉽게 만들 수 있는 팩토리 메서드를 제공하고 있습니다.
💡 리스트 팩토리
- Arrays.asList(), List.of()는 고정 크기의 리스트를 만들기 때문에 요소를 추가 및 삭제하려 하면 UnsupportedOperationException이 발생합니다.
- Arrays.asList, List.of는 컬렉션이 의도치 않게 변하는 것을 막을 수 있습니다. 하지만 객체의 요소가 변하는 것은 막을 수 없습니다.
// 기존 방식
List<String> stringList = new ArrayList<>();
stringList.add("A");
// 새로운 방식 Arrays.asList
List<String> stringList1 = Arrays.asList("A", "B");
stringList1.add("C"); // UnsupportedOperationException 발생
// 새로운 방식 List.of
List<String> stringList2 = List.of("A", "B");
stringList2.add("C"); // UnsupportedOperationException
// 새로운 방식 Stream.of
List<String> stringList3 = Stream.of("A", "B").collect(Collectors.toList());
stringList3.add("C"); // 가능
💡 집합 팩토리
- Set.of 팩토리 메서드를 사용해서 변경할 수 없는 집합을 만들 수 있습니다.
- Set은 중복을 허용하지 않기 때문에 중복된 요소가 있다면 예외가 발생합니다.
Set<String> set = Set.of("A", "B", "B"); // java.lang.IllegalArgumentException: duplicate element: B
💡 맵 팩토리
- Map.of 및 Map.ofEntries를 사용하여 맵을 만들 수 있습니다.
// Map.of
Map<String, Integer> map = Map.of("홍길동", 30, "이순신", 35);
// Map.ofEntries
Map<String, Integer> map = Map.ofEntries(Map.entry("홍길동", 30), Map.entry("이순신", 35));
리스트와 집합 처리
- removeIf - Predicate를 만족하는 요소를 제거합니다. List, Set을 구현하거나, 그 구현을 상속받은 모든 클래스에서 사용할 수 있습니다.
- replaceAll - 리스트에서 이용할 수 있는 기능으로 UnaryOperator 함수를 이용해 요소를 바꿉니다.
- sort : List 인터페이스에서 제공하는 기능으로 리스트를 정렬합니다.
💡 removeIf 메서드 사용 방법
- 아래의 예제는 ConcurrentModificationException이 발생합니다. 그 이유는 for-each문을 순회하면서 Type이 MEAT 인것을 삭제할려고 하는데 menuList 객체가 dish 객체를 참조하고 있기 때문에 발생하고 있습니다.
List<Dish> menuList = new ArrayList<>(List.of(
new Dish("스테이크", false, 800, Type.MEAT),
new Dish("도넛", true, 530, Type.OTHER),
new Dish("회", false, 450, Type.FISH),
new Dish("회덮밥", false, 300, Type.FISH)
));
// ConcurrentModificationException 발생
for (Dish dish : menuList) {
if (dish.getType() == Type.MEAT) {
menuList.remove(dish);
}
}
// for-each와 같은 문제 발생 menuList가 참조하고 있기 때문에 문제 발생
Iterator<Dish> iterator = menuList.iterator();
while (iterator.hasNext()) {
Dish dish = iterator.next();
if (dish.getType() == Type.FISH) {
menuList.remove(dish);
}
}
// 해결 방법1 - iterator를 사용하여 iterator 자기 자신을 삭제하면 됩니다.
// 하지만 코드가 지저분해 집니다.
Iterator<Dish> iterator = menuList.iterator();
while (iterator.hasNext()) {
Dish dish = iterator.next();
if (dish.getType() == Type.MEAT) {
iterator.remove();
}
}
// 해결 방법2 - removeIf를 사용하여 Predicate에 만족한다면 삭제됩니다.
// 코드 또한 깔끔해집니다.
menuList.removeIf(dish -> dish.getType() == Type.MEAT);
💡 replaceAll 메서드 사용 방법
- replaceAll 메서드 또한 편리하게 각 요소에 새로운 요소로 바꿀 수 있습니다.
List<String> text = new ArrayList<>(List.of("a12", "C14", "b13"));
text.replaceAll(t -> Character.toUpperCase(t.charAt(0)) + t.substring(1));
Map 처리
💡 forEach 메서드
- 기존 for-each 메서드에서는 깔끔하지 못하게 순회를 했어야했는데 새로워진 for-each에서는 깔끔하게 순회를 할 수 있습니다.
Map<String, Integer> friends = Map.ofEntries(
Map.entry("주몽", 60),
Map.entry("유관순", 50),
Map.entry("이순신", 40),
Map.entry("홍길동", 30));
// 기존 Map을 순회하는 코드
for (Map.Entry<String, Integer> entry : friends.entrySet()) {
String key = entry.getKey();
Integer value = entry.getValue();
System.out.println(key + ", " + value);
}
// 새로워진 for-each 메서드
friends.forEach((key, value) -> System.out.println(key + ", " + value));
💡 정렬 메서드
- Entry.comparingByValue - 값에 의한 정렬
- Entry.comparingByKey - 키에 의한 정렬
Map<String, Integer> friends = Map.ofEntries(
Map.entry("주몽", 60),
Map.entry("유관순", 50),
Map.entry("이순신", 40),
Map.entry("홍길동", 30));
// sorted stream에 comparingByValue를 사용하여 정렬
// 나이를 내림차순으로 정렬
friends.entrySet()
.stream()
.sorted(Map.Entry.comparingByValue((s1, s2) -> Integer.compare(s2, s1)))
.forEach(System.out::println);
💡 getOrDefault
- 해당 메서드는 키가 존재하더라도 값이 null인 경우 getOrDefault가 null을 반환할 수 있으므로 NPE가 발생합니다.
Map<String, Integer> friends = Map.ofEntries(
Map.entry("주몽", 60),
Map.entry("유관순", 50),
Map.entry("이순신", 40),
Map.entry("홍길동", null)); // 홍길동의 값은 null
friends.getOrDefault("홍길동", 20); // NPE 발생
💡 계산 패턴
- computeIfAbsent - 제공된 키에 해당하는 값이 없으면(값이 없거나 null), 키를 이용해 새 값을 계산하고 Map에 추가합니다.
- computeIfPredent - 제공된 키가 존재하면 새 값을 계산하고 Map에 추가합니다.
- compute - 제공된 키로 새 값을 계산하고 Map에 저장합니다.
- 아래 예제는 음식 종류별로 묶은 Map을 만든 후 Map에서 DRINK 종류의 음식을 꺼내지만 음식이 없다면 새로운 음식을 추가하는 예제입니다.
List<Dish> menuList = new ArrayList<>(List.of(
new Dish("스테이크", false, 800, Type.MEAT),
new Dish("도넛", true, 530, Type.OTHER),
new Dish("회", false, 450, Type.FISH),
new Dish("회덮밥", false, 300, Type.FISH)
));
Map<Type, List<Dish>> 음식종류 = menuList.stream().collect(Collectors.groupingBy(Dish::getType));
List<Dish> dishList = 음식종류.get(Type.DRINK);
// computeIfAbsent 사용하지 않고
if (dishList == null) { // DRINK 종류의 음식종류가 없다면
dishList = new ArrayList<>(); // DRINK를 담음 배열 생성
dishList.add(new Dish("water", false, 0, Type.DRINK)); // DRINK 종류의 객체 생성 후 배열에 담기
음식종류.put(Type.DRINK, dishList);
}
// computeIfAbsent 사용 후
음식종류.computeIfAbsent(Type.DRINK,
dishList2 -> new ArrayList<>())
.add(new Dish("water", false, 0, Type.DRINK));
💡 삭제 패턴
- 자바 8에서는 기존에 있는 remove 메서드를 오버로딩하여, 키가 특정값과 연관되어 있는 경우 삭제하도록 하고 있습니다.
💡 합침(merge)
- 아래 예제는 Dish 객체의 회덮밥이 겹쳐 정상적인 putAll이 되지 않아 merge를 구현하는 예제입니다.
List<Dish> menuList1 = new ArrayList<>(List.of(
new Dish("스테이크", false, 800, Type.MEAT),
new Dish("도넛", true, 530, Type.OTHER),
new Dish("회덮밥", false, 300, Type.FISH)
));
List<Dish> menuList2 = new ArrayList<>(List.of(
new Dish("음료", false, 0, Type.DRINK),
new Dish("회덮밥", false, 300, Type.FISH)
));
Map<Type, List<Dish>> 음식종류 = menuList1.stream().collect(Collectors.groupingBy(Dish::getType));
Map<Type, List<Dish>> 물종류 = menuList2.stream().collect(Collectors.groupingBy(Dish::getType));
// 중복되는 회덮밥이 있는 경우 제대로 merge가 되지 않는 문제 발생
음식종류.putAll(물종류);
{
OTHER=[Dish(name=도넛, vegetarian=true, calorie=530, type=OTHER)],
DRINK=[Dish(name=음료, vegetarian=false, calorie=0, type=DRINK)],
MEAT=[Dish(name=스테이크, vegetarian=false, calorie=800, type=MEAT)],
FISH=[Dish(name=회덮밥, vegetarian=false, calorie=300, type=FISH)] // 회덮밥이 하나밖에 없음
}
// 문제를 해결하는 방법
Map<Type, List<Dish>> 합친음식종류 = new HashMap<>(음식종류);
물종류.forEach((key, value) -> {
합친음식종류.merge(key, value, (o1, o2) -> {
o1.addAll(o2);
return o1;
});
});
{
OTHER=[Dish(name=도넛, vegetarian=true, calorie=530, type=OTHER)],
MEAT=[Dish(name=스테이크, vegetarian=false, calorie=800, type=MEAT)],
DRINK=[Dish(name=음료, vegetarian=false, calorie=0, type=DRINK)],
FISH=[Dish(name=회덮밥, vegetarian=false, calorie=300, type=FISH), Dish(name=회덮밥, vegetarian=false, calorie=300, type=FISH)]
}
728x90
반응형
'스터디 > 모던 자바 인 액션' 카테고리의 다른 글
모던 자바 인 액션 - 11장 null 대신 Optional 클래스 (0) | 2022.09.15 |
---|---|
모던 자바 인 액션 - 9장 리펙터링, 테스팅, 디버깅 (0) | 2022.09.11 |
모던 자바 인 액션 - 6장 스트림으로 데이터 수집 (0) | 2022.09.10 |
모던 자바 인 액션 - 4장 스트림 소개 (0) | 2022.09.04 |
모던 자바 인 액션 - 3장 람다 표현식 (0) | 2022.09.03 |
공지사항
최근에 올라온 글
최근에 달린 댓글
- Total
- Today
- Yesterday
TAG
- 서비스 기반 아키텍처
- transactional outbox pattern spring boot
- pipe and filter architecture
- spring boot redis 대기열 구현
- redis sorted set
- @ControllerAdvice
- 공간 기반 아키텍처
- 람다 표현식
- redis 대기열 구현
- spring boot excel download oom
- spring boot 엑셀 다운로드
- JDK Dynamic Proxy와 CGLIB의 차이
- spring boot redisson destributed lock
- transactional outbox pattern
- 트랜잭셔널 아웃박스 패턴 스프링 부트 예제
- spring boot poi excel download
- spring boot redisson 분산락 구현
- polling publisher spring boot
- pipeline architecture
- spring boot redisson sorted set
- space based architecture
- 트랜잭셔널 아웃박스 패턴 스프링부트
- 레이어드 아키텍처란
- redis sorted set으로 대기열 구현
- java ThreadLocal
- 자바 백엔드 개발자 추천 도서
- microkernel architecture
- spring boot excel download paging
- java userThread와 DaemonThread
- service based architecture
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
글 보관함