컬렉션 API 개선
업데이트:
컬렉션 팩토리
- 리스트 팩토리(자바 9)
- List.of 팩토리메소드를 이용해서 간단하게 리스트를 만들수 있다.
List<String> friends = List.of("Raphael","Olivia", "Thibaut"); // java.lang.UnsupportedOperationException 발생 friends.add("Joode")
- 집합 팩토리(자바 9)
- List.of와 비슷한 방법으로 바꿀 수 없는 집합을 만들수 있다.
// 중복 요소가 잇는 경우 IllegalArgumentException 발생 Set<String> friends = Set.of("Raphael","Olivia", "Thibaut"); // java.lang.UnsupportedOperationException 발생
- 맵 팩토리(자바 9)
- Map.of
// 10개 이하일 경우 유용함 Map<String, Integer> ageOfFriends = Map.of("Raphael", 30,"Olivia", 20, "Thibaut", 40);
- Map.ofEntries
// 10개 이상일때 유용함 Map<String, Integer> ageOfFriends = Map.ofEntries( entry("Raphael", 30), entry("Olivia", 20), entry("Thibaut", 40));
리스트와 집합 처리
- removeIf
프레디케이트를 만족하는 요소를 제거한다. List나 Set을 구현하거나 그 구현을 상속받은 모든 클래스에서 이용할 수 있다.
- 숫자로 시작되는 참조 코드를 가진 트랜잭션을 삭제 하는 코드
- 1번 코드는 반복자의 상태는 컬렉션의 상태와 서로 동기되지 않아 문제가 발생한다.
- 2번 코드는 removeIf 메서드가 삭제할 요소를 가리키는 프레디케이트를 인수로 받아 삭제가 가능하다.
//1 for(Iterator<Transaction> iterator = transaction.iterator(); iterator.hasNext();) Transaction transaction = iterator.next(0; if(Character.isDigit(transaction.getReferenceCOde().chatAt(0))){ transactions.remove(transaction); } } //2 transcation.removeIf(transaction -> Character.isDigit(transcation.getReferenceCode().chatAt(0)));
- replaceAll
리스트에서 이용할 수 있는 기능으로 UnaryOperator 함수를이용해 요소를 바꾼다.
- 컬렉션 객체르르 Iterator 객체와 혼용하면 반복과 컬렉션 변경이 동시에 이루어지면서 쉽게 문제를 일으킨다.
- 자바 8의 기능을 이용하면 다음과 같이 간단하게 구현할 수 있다.
referenceCodes.replaceAll(code -> Character.toUppercase(code.chartAt(0)) + code.substring(1));
맵처리
자바 8에서는 Map 인터페이스에 몇가지 디폴트 메서드가 추가 됐다.
- forEach
- Map.Entry<K,V>의 반복자를 이용해 맵의 항목집합을 반복할 수 있었는데 자바8에서는 Map 인터페이스는 BiConsumer를 인수로 받는 forEach메서드를 지원하므로 조금 더 간단히 구현 가능하다.
ageOfFiend.forEach((friend, age) -> System.out.println(friend + " is" + age + " years old"));
- 정렬 메서드
- Entry.comparingByValue, Entry.comparingByKey 유틸리티를 이용하면 값 또는 키를기준으로 정렬할 수 있다.
HashMap 성능 자바 8에서는 HashMap의 내부 구조를바꿔 성능을 개선했다. 기존의 맵의 항목은 키로 생성한 해시코드에 접근할수 있는 버켓에 저장했다. 많은 키가 같은 해시코드를 반환하는 상황이 되면 O(n)의 시간이 걸리는 LinkedList로 버킷을 반환해야 하므로 성능이 저하된다. 최근에는 버킷이 너무 커질경우 O(log(n))의 시간이 소요되는 정렬된 트리를 이용해서 동적으로 치환해 충돌이 일어나는 요소 반환 성능을 개선했다. 하지만 키가 String, Number 클래스 같은 Comparable의 형태여야만 정렬된 트리가 지원된다.
- getOrDefault 메서드
- 키가 존재하지 않으면 널포인트 익셉션으로 요청 결과가 널인지 확인해야 한다. 이 문제를 해결하는 방법이 해당메소드를 사용하는 것이다.
// 값이 존재하지 않는 경우 뒤에입력한 Matrix를 출력한다. favoriteMovies.getOrDefault("Olivia", "Matrix")
- 계산 패턴
- computerIfAbsent : 제공된 키에 해당한느 값이 없으면(없거나 Null),키를 이용해 새 값을 계산하고 밉에 추가한다.
// line이 맵에 없는경우 실행한다. lines.forEach(line -> dataToHash.computeIfAbsent(line, this::clculateDigest));
- computeIfPresent : 제공된 키가 존재하면 새 값을 계산하고 맵에 추가한다.
- compute : 제공된 키로 새 값을 계산하고 맵에 저장한다.
- 삭제 패턴
- favouriteMovies : 첫번째 인수리 넘긴 key의 value값이 두 번째 인수로 넘긴 value와 같으면 삭제한다
favouriteMovies.remove(key, value)
- 교체 패턴
- replaceAll : 맵의 모든 값의 형식을 바꿀 수 있다.
- favouriteMovies : 첫번째 인수리 넘긴 key의 value값이 두 번째 인수로 넘긴 value와 같으면 삭제한다
// 모든 value들이 대문자로 변경된다. favouriteMovies.replaceAll((key, value)-> value.toUpperCase())
- replaceAll : 맵의 모든 값의 형식을 바꿀 수 있다.
- 합침
- marge : 두 개의 맵을 합친다.
// 중복된 키가 있으면 두 값을 연결 favouriteMovies.marge((k,v) -> newMovie.marge(k,v, (movie1, movie2)- > movie1 + " & " + movvie2));
ConcurrentHashMap
동시성 친화적이며 최신 기술을 반영한 HashMap 버전이다. 내부 자료구조의 특정 부문만잠궈 동시 추가, 갱신 작업을 허용해서 동기화된 Hashtable 버전에 비해 읽기 쓰기 연산 성능이 월등하다.
- 리듀스와 검색
- 스트림과 비슷한 종류의 세가지 새로운 연산을 지원한다.
- forEach : 각(키, 값) 쌍에 주어진 액션을 실행
- reduce : 모든(키, 값) 쌍을 제공된 리듀스 함수를 이용해 결과로 합침
- search : 널이 아닌 값을반환할 때까지 각(키, 값) 쌍에 함수 적용
- 키에 함수받기, 값, Map.Entry, (키, 값) 인수를 이용한 네가지 연상 형태를 지원한다.
- 키, 값으로 연산 : forEach, reduce,search
- 키로 연산 : forEachKey, reduceKeys, searchKeys
- 값으로 연산 : forEachValue, reduceValues, searchValues
- Map.Entry 연산 : forEachEntry, reduceEntries, searchEntries
- 이들 연산에 제공한 함수는 계산이 진행되는 동안 바뀔수 있는 객체, 순서 등에 의존하지 않아야 한다.
- 병령성 기준값을 지정해야 한다. 맵의 크기가 주어진 기준값보다 작으면 순차적으로 연산을 실행한다. 기준값을 1로 지정하면 공통스레드 풀을 이용해 병렬성을 극대화 한다.
// 기준값 ConcurrentHashMap<String, Long> map = ConcurrentHashMap<>(); long paralle = 1; Optional<Integer> maxValue = Optional.ofNullable(map.reduceValues(paralle, Long::max))l;
- 스트림과 비슷한 종류의 세가지 새로운 연산을 지원한다.
- 계수
- ConcurrentHashMap 클래스는 맵의 매핑 개수를 반환하는 mappingCount 메서드를 제공한다. 기존의 size 메서드 대신 새 코드에서는 int를 반환하는 mappingCount 메서드를 사용하는 것이좋다.
- 집합뷰
- ConcurrentHashMap을집합 뷰로 반환하는 KetSet이라는 새 메서드를 제공한다.
- 맵을 바꾸면 집합도 바뀌고 반대로 집합을 바꾸면 맵도 영향 받는다.
댓글남기기