스프링 자바 인 액션을 공부하는 과정에서 테스트 코드를 작성해 검증해야할 로직을 작성하게 되었다.
*** 문제
그 과정에서 스트림안에 람다식을 넣어 각 element를 순회하여 이전의 리스트와 값을 비교하는 로직이었는데, index++코드가 컴파일 오류가 나 실행할 수 없는 문제가 발생하였다.
int index = 0;
dishNames.parallelStream().forEach(dishname -> {
Assertions.assertThat(menu.get(index++).getName()).isEqualTo(dishname); //dishNames를 순회하면서 이름을
});
컴파일 오류가 발생한 코드이다.
*** 문제 인식 및 해결방법 모색
처음에는 스트림과 관련된 오류인 줄 알았지만, 알고보니 람다식 안에 서는 final 변수만 사용가능 하기 때문에 발생한 오류였다.
***해결방법
람다식 안에서 index++ 와 같은 역할을 하는 변수를 사용하기 위해서는 2가지 방법이 있었다.
1. Atomic Integer 타입을 쓰는것.
2. array를 선언해 array안의 값을 계속 변경해주면서 사용하는 방법.
나는 두가지 방법을 다 시도해보았다.
첫 번째 방법인 Atomic Integer를 사용한 코드이다.
@DisplayName("For-each를 사용하는 외부 반복")
@Test
public void useForeach() {
List<String> dishNames = new ArrayList<>(); // 요리의 이름들을 담을 리스트
for (Dish dish : menu) {
dishNames.add(dish.getName()); // 요리의 이름들을 dishNames 리스트에 담음.
}
AtomicInteger index = new AtomicInteger();
dishNames.stream().forEach(
dishname -> Assertions.assertThat(menu.get(index.getAndIncrement()).getName()).isEqualTo(dishname) //dishNames를 순회하면서 이름을 출력
);
}
Atomic Integer는 멀티쓰레드 환경에서 Thread Safe 한 Wrapper 타입이라고 한다.
참고 : 자바 동시성 문제를 해결하는 3가지 방법.
- volatile : Thread1에서 write하고, Thread2에서 read 하는 경우에만 동시성이 보장됩니다. 2개의 쓰레드에서 모두 write 하면 문제 발생.
- synchronized :안전하게 동시성을 보장 가능. 비용 높음.
- Atomic : CAS (Compare And Swap)를 이용한 동시성을 보장. 여러 쓰레드에서 데이터를 write해도 Thread safe.
CAS(Compare And Swap) : thread 별 캐시값과 메인 메모리의 값이 같은 경우에 값을 변경. 말그대로 비교해서 같으면 변경 아니면 변경하지 않음.
두 번째 방법인 배열을 선언해 그 안의 데이터를 Increment 한 코드이다.
@DisplayName("Iterator 객체를 사용하는 외부 반복")
@Test
public void useIterator() {
List<String> dishNames = new ArrayList<>(); //For-each를 사용하는 외부 반복과 동일
Iterator<Dish> iterator = menu.iterator(); //menu로 부터 iterator를 가져옴.
while (iterator.hasNext()) {
Dish dish = iterator.next();
dishNames.add(dish.getName()); //iterator를 이용해 dish의 이름을 names리스트에 담음.
}
dishNames.stream().forEach(System.out::println); //dish의 이름을 출력 (For-each를 통한 외부반복과 결과가 같음.)
// AtomicInteger index = new AtomicInteger();
final int[] index = {0};
dishNames.stream().forEach(dishname -> {
Assertions.assertThat(menu.get(index[0]++).getName()).isEqualTo(dishname);
System.out.println(dishname);//dishNames를 순회하면서 이름을 출력
});
}
항상 람다 내부에선 final변수만 사용해야 한다고 알고 있어서 이렇게 값이 변할 수 있는 변수들을 사용하는 방법에 대해서 생각했는데, 테스트 코드 검증 시 스트림을 사용하면서 의도치 않게 이런 컴파일 오류 발생으로 인해 새로운 방법을 또 하나 알게 되었다.
'프로그래밍 > JAVA 내용정리' 카테고리의 다른 글
[MordernJavaInAction] 6장 스트림으로 데이터 수집(Stream Collector) 1편. (0) | 2022.09.25 |
---|---|
[ModernJavaInAction] 5장 스트림의 활용 (1) | 2022.09.12 |
[ModernJavaInAction] 4장 스트림(Stream) (0) | 2022.08.22 |
컬렉션 프레임 워크 (Set, HashSet) (0) | 2020.10.11 |
컬렉션 프레임 워크 (List, ArrayList, Vector, LinkedList) (0) | 2020.10.11 |