오늘은 Java 제너릭과 List, Wrapper 클래스에 대한 강의를 듣고, 프로그래머스 알고리즘 문제를 풀었다.
Generic 제네릭
Java는 타입언어로 변수 선언, 메소드에 파라미터 작성 시 타입을 선언해주어야 한다.
거기에 조금의 유연함을 주는 것이 제네릭이다.
예를들어 메소드 오버로딩 시, 똑같은 로직을 수행해도 파라미터 타입이 다르면 메소드를 여러번 구현해야 한다.
이럴 때 제네릭을 사용하면 중복되는 코드를 줄여줄 수 있다.
Generic <T> 형식으로 사용하고, 여기서 T는 타입변수다.
타입변수명으로는 T, U, V, E 등을 자주 사용한다.
제네릭은 클래스 또는 메소드에 사용할 수 있다.
주의할 점은, 제네릭 배열을 생성할 수 없고 객체의 static멤버에게는 사용할 수 없다.
public class Generic<T> { // 제네릭 클래스 : 원시타입
public void set(T t) { ... } // 제네릭 메소드
}
[제네릭 문법]
다수의 타입변수 사용 가능하다
다형성은 그대로 적용된다
와일드 카드를 통해 제네릭의 제한을 구체적으로 정할 수 있다.
<T extends ...>, <... super T>
List
추상적 자료구조로 순서가 있고, 중복을 허용하지 않는다.
List는 인터페이스이고, ArrayList나 LinkedList는 클래스(구현체)다.
+ 인터페이스 : 추상적인 타입의 역할을 하고 실제로 구현하는 구현체들의 행동 양식을 정해준다.
리스트는 순서를 가지고 저장되어 있기 때문에 인덱스를 알면 되어서 검색에 유리하고,
수정/삭제시에는 인덱스가 밀리거나 당겨지는 작업이 일어나기 때문에 불리하다.
가장 앞, 가장 뒤에서 데이터를 넣거나 삭제하는 경우 순서도 유지하고 수정/삭제도 보완된 게 스텍과 큐이다.
상황에 맞는 적절한 자료구조 선택하는 방법
인터페이스는 코드를 보고 어떠한 일을 해주는지 알 수 있다.
클래스는 어떠한 일을 어떻게 해주는지 알 수 있다.
그래서, 지금 데이터를 다루는데 필요한 기능을 어떠한 것들이 해주는지는 인터페이스에서 찾고,
어떠한 방식으로 해줘야 유리할지는 클래스를 보고 판단한다.
제일 좋은 방법은, 자주 코드를 접하며 어떤 상황에서 어떤 자료구조를 주로 사용하는지 보고, 주로 사용하는 클래스들은 실제 명세를 봐서 어떤 특징이 있는지 알아두는 게 중요하다.
wrapper 객체
성능상의 이유로 값만 다루는 경우에는 원시타입 그대로 기본형으로 쓰고, 필요할 때 객체화 해서 사용한다.
boxing 박싱 : 기본값을 객체로
unboxing 언박싱 : 객체를 기본값으로
Integer num = new Integer(3); // 박싱
int n = num.intValue(); // 언박싱
Character ch = 'a'; // 오토박싱
char c = ch; // 오토박싱
같은 숫자는 싫어
문제
배열 arr가 주어집니다. 배열 arr의 각 원소는 숫자 0부터 9까지로 이루어져 있습니다. 이때, 배열 arr에서 연속적으로 나타나는 숫자는 하나만 남기고 전부 제거하려고 합니다. 단, 제거된 후 남은 수들을 반환할 때는 배열 arr의 원소들의 순서를 유지해야 합니다. 예를 들면,
- arr = [1, 1, 3, 3, 0, 1, 1] 이면 [1, 3, 0, 1] 을 return 합니다.
- arr = [4, 4, 4, 3, 3] 이면 [4, 3] 을 return 합니다.
배열 arr에서 연속적으로 나타나는 숫자는 제거하고 남은 수들을 return 하는 solution 함수를 완성해 주세요.
처음에 contains를 사용해 list에 arr과 같은 값이 없다면 값을 추가하도록 했는데, 중복되는 값이 모두 없이 출력되었다.
equals를 사용해 같은 값이 없으면 리스트에 값을 추가하도록 했는데 이번에는 모든 값이 다 추가되었다.
곰곰히 생각해보니 equals를 쓰더라도 중복된 값을 넣지 않겠다는 건 똑같은 전제같아 문제를 다시 읽고 새로 접근해보았다.
[1차 시도]
// 1차 시도 - fail
import java.util.*;
public class Solution {
public int[] solution(int []arr) {
int[] answer = {};
ArrayList<Integer> list = new ArrayList<Integer>();
// list와 arr[i]를 비교해 같은 값이 없으면 리스트에 넣도록 했는데, 모든 수가 나온다.
for(int i = 0; i < arr.length; i++){
if(list.equals(arr[i]) == false) {
list.add(arr[i]);
}
// contains 메서드를 사용해 list에 arr[i]과 같은 값이 없다면 값을 추가하도록 함
// [1,1,3,3,0,1,1] -> [1,3,0]으로 출력되어 [1,3,0,1]과 다르다.
// 문제를 제대로 읽지 않아 발생한 실수!!
// if(list.contains(arr[i]) == false) {
// list.add(arr[i]);
// }
}
answer = new int[list.size()];
for(int i = 0; i < list.size(); i++){
answer[i] = list.get(i);
}
return answer;
}
}
반복되는 숫자가 있으면 하나만 넣고, 다음으로 넘어가는 solution 함수니까
반복이 끝나고 다음 값이 나오는 순간에 반복되던 숫자를 list에 넘겨주면 되고,
마지막 값은 변하는 순간이 나오지 않으니 list에 없는 상황이니까 그대로 list에 넣어주면 되는 문제였다.
아래와 같이 해결!
[2차 시도]
// 2차 시도 - pass
import java.util.*;
public class Solution {
public int[] solution(int []arr) {
int[] answer = {};
ArrayList<Integer> list = new ArrayList<Integer>();
for(int i = 0; i < arr.length - 1; i++){
// 값이 변하는 순간이 나오면 앞순서 숫자를 list에 넣는다.
if(arr[i] != arr[i+1]) {
list.add(arr[i]);
}
// 마지막 값은 변하는 순간이 나오지 않으니까 list에 없다.
// 그래서 마지막 값은 그냥 list에 넣어주면 된다.
if(i == arr.length - 2) {
list.add(arr[arr.length - 1]);
}
}
// list 사이즈만큼 answer배열을 할당하고, 값을 옮겨담는다.
answer = new int[list.size()];
for(int i = 0; i < list.size(); i++) {
answer[i] = list.get(i);
}
return answer;
}
}
두 개 뽑아서 더하기
문제
정수 배열 numbers가 주어집니다. numbers에서 서로 다른 인덱스에 있는 두 개의 수를 뽑아 더해서 만들 수 있는 모든 수를 배열에 오름차순으로 담아 return 하도록 solution 함수를 완성해주세요.
두 수를 찾아 더한 값이 list에 있는지 비교하고 없으면 추가한 다음, list를 오름차순으로 정렬하고 answer에 옮겨 담는 로직을 생각해 풀었고 해결했다.
[소스 코드]
import java.util.*;
class Solution {
public int[] solution(int[] numbers) {
int[] answer = {};
ArrayList<Integer> sumNum = new ArrayList<Integer>();
int sum;
// 두 수를 뽑아 더한 값을 list에 옮겨 저장한다.
// 이미 있는 값이라면 저장하지 않는다.
// list를 오름차순으로 정렬하고 answer에 옮긴다.
for(int i = 0; i < numbers.length; i++) {
for(int j = 0; j < i; j++) {
sum = numbers[i] + numbers[j];
// 두 수의 합이 이미 리스트에 있으면 넣지 않고, 없으면 넣는다
if(sumNum.contains(sum) == false) {
sumNum.add(sum);
}
}
}
// Integer list 오름차순 정렬
Collections.sort(sumNum);
// answer배열 크기 설정
answer = new int[sumNum.size()];
// answer 배열에 sumNum리스트에서 값 가져와 넣기
for(int i = 0; i < sumNum.size(); i++) {
answer[i] = sumNum.get(i);
}
return answer;
}
}
처음에 sumNum.sort();로 정렬을 시도했으나 에러가 났다.
String이 아닌 Integer 형이라 그런건가 싶어 검색을 해보았다.
+ Integer List를 오름차순으로 정렬하는 방법
Java.util 라이브러리 안에는 Collections라는 하위 라이브러리가 존재한다.
Collections가 제공하는 기능 중 정렬이 있다.
Collections.sort(list명); // 오름차순 (Ascending)
Collections.sort(list명, Collections.reversOrder()); // 내림차순 (Descending)
+ 추가로 알아보니 sort 메소드는 parameter로 받는 Comparator를 통해 리스트 안의 데이터를 비교해 오름차순으로 정렬한다고 한다.
만약 리스트의 데이터 오브젝트가 Comparable을 implements한 오브젝트라면 null값을 입력해 오름차순으로 정렬할 수 있다고 한다.
sumNum.sort(null); // 이렇게 해줘도 에러 없이 실행되었다.
'TIL' 카테고리의 다른 글
슬기의 TIL - 2023.04.13 (0) | 2023.04.13 |
---|---|
슬기의 TIL - 2023.04.12 (0) | 2023.04.12 |
슬기의 TIL - 2023.04.10 (0) | 2023.04.11 |
슬기의 TIL - 2023.04.08 (0) | 2023.04.08 |
[항해99 14기] Java 문법 종합반 1주차 과제 (0) | 2023.04.03 |