▼ Why ? What ?
1일 1알고리즘 스터디에서 "데이터 분석"이라는 문제를 풀었는데, 해결 과정에서 제네렉 타입으로 기본형 타입을 무의식적으로 사용하지 않고 있었던 부분을 회고하고, 람다 표현식에서 외부 변수를 사용할 때 주의해야 할 점을 알게 되어 따로 정리해봤다.
[ 해당 문제 ]
[ 1 Day - 1 Algo ] 알고리즘 문제 : "데이터 분석"
▼ 제네릭 타입으로 기본형(Primitive) 배열 타입 사용
배열이 참조형 타입인 이유 ?
- 일관된 메모리 관리
: 참조형 타입(Reference Type)은 힙(Heap) 메모리에 저장되며, 배열도 마찬가지로 힙 메모리에 저장되기 때문에, 기본형 배열도 힙 메모리에 객체로 저장되어 일관된 메모리 관리가 가능해진다. - 다형성(Polymorphism)
: 배열이 객체로 취급되므로, Object 타입의 변수로 다양한 타입의 배열을 참조할 수 있기 때문에, 'JSON 파싱 라이브러리'나 '데이터 변환 라이브러리'처럼 다양한 타입의 데이터를 처리해야 하는 경우 유용하다고 하니 이렇게 사용할 수 있다는 정도만 알아두면 좋을 것 같다.
( 일반적인 경우엔 특정 배열 타입을 명시적으로 사용하는 게 더 명확하고 안전해서, 사실 쓰일 일이 많지는 않을 듯..? )
// 정수 배열 생성
int[] intArray = {1, 2, 3, 4, 5};
// 문자열 배열 생성
String[] stringArray = {"Hello", "World"};
// 다차원 배열 생성
int[][] multiArray = { {1, 2}, {3, 4} };
// Object 타입의 변수로 배열을 참조
Object obj;
obj = intArray; // int 배열 참조
System.out.println("intArray: " + java.util.Arrays.toString((int[]) obj));
obj = stringArray; // String 배열 참조
System.out.println("stringArray: " + java.util.Arrays.toString((String[]) obj));
obj = multiArray; // 2차원 int 배열 참조
System.out.println("multiArray: " + java.util.Arrays.deepToString((int[][]) obj));
참조형 배열과 제네릭
- Java의 제네릭은 실제로 참조형(Reference Type)으로 처리 된다 !
➙ Java에선 배열도 객체이기 때문에, 제네릭 타입으로 기본형 배열 타입은 당연히 사용 가능하다 !
( ex: `int[]` 타입의 배열은 `Object` 클래스의 하위 타입이다. ) - 제네릭에 기본형 배열 타입도 사용 가능하니, 굳이 `List<Integer[]>`처럼 Wrapper 클래스 타입으로 지정해서 `stream`을 이용해 복잡하게 다시 기본형 타입으로 변환해주는 과정(Boxing/Unboxing)을 피할 수 있으면 피하자 !
▼ 람다 표현식과 'effective final' 변수
`Effective final` 변수란 ?
- 실제로는 `final` 키워드가 붙어있지 않더라도, 값이 변경되지 않는 변수이다.
람다(Lambda) 표현식에선 `effective final` 변수를 사용해야 한다 !
- 람다 표현식은 '익명 클래스(Anonymous Class)`와 유사하게 동작하기 때문이다.
➙ 익명 클래스에선 외부 변수에 접근할 때 해당 변수가 `final`로 선언되어 있어야 하는 규칙과 유사하다 ! - 람다 표현식 내에서 변수를 사용하는 경우, 해당 변수는 값이 변경되어선 안된다 !
➙ 람다 표현식이 생성된 시점의 변수를 사용하기 때문에, 컴파일러는 값이 변경될 수 있는 변수는 허용하지 않는다 !
int extIdx = 0, sbIdx = 0;
// 이와 같이 변수의 값이 초기화된 이후로 변수의 값이 변경되는 로직이 있으면
// 람다 표현식에서 해당 변수를 사용할 수 없다.
for (int i = 0; i < criteria.length; ++i) {
if (criteria[i].equals(ext)) extIdx = i;
if (criteria[i].equals(sort_by)) sbIdx = i;
}
// Compile error!
list.sort((a, b) -> a[sbIdx] - b[sbIdx]);
// OK
final int SB_IDX = sbIdx;
list.sort((a, b) -> a[SB_IDX] - b[SB_IDX]);
`final` 혹은 'effective final' 제약 없이 변수 값 사용하는 방법
- 람다 표현식 외부에서 `Comparator`를 정의하면, `final` 또는 ‘effective final’ 제약 없이 변수 값을 사용할 수 있다 !
int extIdx = 0, sbIdx = 0;
// 변수의 값이 변경되는 로직
for (int i = 0; i < criteria.length; ++i) {
if (criteria[i].equals(ext)) extIdx = i;
if (criteria[i].equals(sort_by)) sbIdx = i;
}
// 데이터 리스트 생성
int size = 0;
for (int[] d : data) {
if (d[extIdx] < val_ext) {
list.add(d);
size++;
}
}
int[][] answer = new int[size][4];
// final int SB_IDX = sbIdx;
// Comparator를 람다 표현식 외부에서 정의
Comparator<int[]> comparator = (a, b) -> a[sbIdx] - b[sbIdx];
// list 정렬
list.sort(comparator);
[ 참고 자료 - Comparator ]
[Java] Collection Framework, `Comparable` 인터페이스vs `Comparator` 인터페이스 (cf. 람다함수, 익명객체)
▼ Why ? What ?1일 1알고리즘 스터디에서 "스테이지 별 실패율"을 구해서 "실패율"을 기준으로 "스테이지 번호"를 "내림차순으로 정렬"하는 문제를 풀다가, Collection Framework의 구조, 익명객체와 람다
ukym-tistory.tistory.com
▼ Why ? What ?
1일 1알고리즘 스터디에서 "데이터 분석"이라는 문제를 풀었는데, 해결 과정에서 제네렉 타입으로 기본형 타입을 무의식적으로 사용하지 않고 있었던 부분을 회고하고, 람다 표현식에서 외부 변수를 사용할 때 주의해야 할 점을 알게 되어 따로 정리해봤다.
[ 해당 문제 ]
[ 1 Day - 1 Algo ] 알고리즘 문제 : "데이터 분석"
▼ 제네릭 타입으로 기본형(Primitive) 배열 타입 사용
배열이 참조형 타입인 이유 ?
- 일관된 메모리 관리
: 참조형 타입(Reference Type)은 힙(Heap) 메모리에 저장되며, 배열도 마찬가지로 힙 메모리에 저장되기 때문에, 기본형 배열도 힙 메모리에 객체로 저장되어 일관된 메모리 관리가 가능해진다. - 다형성(Polymorphism)
: 배열이 객체로 취급되므로, Object 타입의 변수로 다양한 타입의 배열을 참조할 수 있기 때문에, 'JSON 파싱 라이브러리'나 '데이터 변환 라이브러리'처럼 다양한 타입의 데이터를 처리해야 하는 경우 유용하다고 하니 이렇게 사용할 수 있다는 정도만 알아두면 좋을 것 같다.
( 일반적인 경우엔 특정 배열 타입을 명시적으로 사용하는 게 더 명확하고 안전해서, 사실 쓰일 일이 많지는 않을 듯..? )
// 정수 배열 생성
int[] intArray = {1, 2, 3, 4, 5};
// 문자열 배열 생성
String[] stringArray = {"Hello", "World"};
// 다차원 배열 생성
int[][] multiArray = { {1, 2}, {3, 4} };
// Object 타입의 변수로 배열을 참조
Object obj;
obj = intArray; // int 배열 참조
System.out.println("intArray: " + java.util.Arrays.toString((int[]) obj));
obj = stringArray; // String 배열 참조
System.out.println("stringArray: " + java.util.Arrays.toString((String[]) obj));
obj = multiArray; // 2차원 int 배열 참조
System.out.println("multiArray: " + java.util.Arrays.deepToString((int[][]) obj));
참조형 배열과 제네릭
- Java의 제네릭은 실제로 참조형(Reference Type)으로 처리 된다 !
➙ Java에선 배열도 객체이기 때문에, 제네릭 타입으로 기본형 배열 타입은 당연히 사용 가능하다 !
( ex: `int[]` 타입의 배열은 `Object` 클래스의 하위 타입이다. ) - 제네릭에 기본형 배열 타입도 사용 가능하니, 굳이 `List<Integer[]>`처럼 Wrapper 클래스 타입으로 지정해서 `stream`을 이용해 복잡하게 다시 기본형 타입으로 변환해주는 과정(Boxing/Unboxing)을 피할 수 있으면 피하자 !
▼ 람다 표현식과 'effective final' 변수
`Effective final` 변수란 ?
- 실제로는 `final` 키워드가 붙어있지 않더라도, 값이 변경되지 않는 변수이다.
람다(Lambda) 표현식에선 `effective final` 변수를 사용해야 한다 !
- 람다 표현식은 '익명 클래스(Anonymous Class)`와 유사하게 동작하기 때문이다.
➙ 익명 클래스에선 외부 변수에 접근할 때 해당 변수가 `final`로 선언되어 있어야 하는 규칙과 유사하다 ! - 람다 표현식 내에서 변수를 사용하는 경우, 해당 변수는 값이 변경되어선 안된다 !
➙ 람다 표현식이 생성된 시점의 변수를 사용하기 때문에, 컴파일러는 값이 변경될 수 있는 변수는 허용하지 않는다 !
int extIdx = 0, sbIdx = 0;
// 이와 같이 변수의 값이 초기화된 이후로 변수의 값이 변경되는 로직이 있으면
// 람다 표현식에서 해당 변수를 사용할 수 없다.
for (int i = 0; i < criteria.length; ++i) {
if (criteria[i].equals(ext)) extIdx = i;
if (criteria[i].equals(sort_by)) sbIdx = i;
}
// Compile error!
list.sort((a, b) -> a[sbIdx] - b[sbIdx]);
// OK
final int SB_IDX = sbIdx;
list.sort((a, b) -> a[SB_IDX] - b[SB_IDX]);
`final` 혹은 'effective final' 제약 없이 변수 값 사용하는 방법
- 람다 표현식 외부에서 `Comparator`를 정의하면, `final` 또는 ‘effective final’ 제약 없이 변수 값을 사용할 수 있다 !
int extIdx = 0, sbIdx = 0;
// 변수의 값이 변경되는 로직
for (int i = 0; i < criteria.length; ++i) {
if (criteria[i].equals(ext)) extIdx = i;
if (criteria[i].equals(sort_by)) sbIdx = i;
}
// 데이터 리스트 생성
int size = 0;
for (int[] d : data) {
if (d[extIdx] < val_ext) {
list.add(d);
size++;
}
}
int[][] answer = new int[size][4];
// final int SB_IDX = sbIdx;
// Comparator를 람다 표현식 외부에서 정의
Comparator<int[]> comparator = (a, b) -> a[sbIdx] - b[sbIdx];
// list 정렬
list.sort(comparator);
[ 참고 자료 - Comparator ]
[Java] Collection Framework, `Comparable` 인터페이스vs `Comparator` 인터페이스 (cf. 람다함수, 익명객체)
▼ Why ? What ?1일 1알고리즘 스터디에서 "스테이지 별 실패율"을 구해서 "실패율"을 기준으로 "스테이지 번호"를 "내림차순으로 정렬"하는 문제를 풀다가, Collection Framework의 구조, 익명객체와 람다
ukym-tistory.tistory.com