리스트
리스트(List)는 배열과 비슷하지만 편리한 기능이 더 많은 자료형이다.
둘의 큰 차이점은 배열은 크기가 정해져 있지만.
리스트는 변한다는 데 있다.
예를 들어 야구선수의 투구 수는 3개가 될 수도 있고 100개가 될 수도 있다. 이런 경우 리스트를 사용해야한다.
- ArrayList
리스트 자료형에서 가장 일반적으로 사용하는 ArrayList를 알아보자.
리스트 자료형에는 ArrayList, Vector, LinkedLsit등이 있다.
1) add
add 메서드를 사용하면 요솟값을 추가할 수 있다.
야구 선수가 공을 3번 던졌는데 각각 시속 138, 129, 142(km/h)였다면 다음과 같이 코드를 작성할 수 있다.
import java.util.ArrayList;
public class Sample {
public static void main(String[] args) {
ArrayList pitches = new ArrayList();
pitches.add("138");
pitches.add("129");
pitches.add("142");
}
}
ArrayList를 사용하려면 import java.util.ArrayList와 같은 import문을 작성해야함.
만약 첫 번째 위치에 133라는 투구 속도를 추가하고 싶다면 다음과 같이 삽입할 위치를 파라미터로 넘겨주자.
pitches.add(0, "133"); // 첫번째 위치에 133 삽입
두 번째 위치에 133 삽입은?
pitches.add(1, "133");
자바 J2SE 5.0버전부터 객체를 포함하는 자료형은 어떤 객체를 포함하는지
ArrayList<String> pitches = new ArrayList<>(); 처럼 명확하게 표현할 것을 권고하고 있다.
제네릭스에서 더 설명
2) get
ArrayList의 get 메서드를 사용하면 특정 인덱스의 값을 추출할 수 있다.
앞의 예제에서 야구선수의 두 번째 투구 속도를 출력하고 싶다면 다음과 같이 코드를 작성해야함.
import java.util.ArrayList;
public class Sample {
public static void main(String[] args) {
ArrayList pitches = new ArrayList();
pitches.add("138");
pitches.add("129");
pitches.add("142");
System.out.println(pitches.get(1));
}
}
3) size
siz 메서드는 ArrayList의 요소 개수를 리턴한다.
System.out.println(pitches.size());
4) contains
리스트 안에 해당 항목이 있는지 판별해 그 결과를 boolean으로 리턴함
System.out.println(pitches.contains("142"));
5) remove
2가지 방식이 있다.
remove(객체)
remove(인덱스)
remove(객체)를 사용하면 리스트에서 객체에 해당하는 항목을 삭제한 뒤,
그 결과로 true 또는 false를 리턴한다.
(... 생략 ...)
System.out.println(pitches.remove("129")); // 129를 리스트에서 삭제하고, true를 리턴한다.
두 번째 방식인 remove(인덱스)를 사용하면 인덱스에 해당한 항목을 삭제한 뒤, 그 항목을 리턴한다.
(... 생략 ...)
System.out.println(pitches.remove(0)); // pitches의 첫 번째 항목이 138이므로, 138을 삭제한 뒤 138을 리턴한다.
- 제네릭스
자료형을 안전하게 사용할 수 있도록 만들어 주는 기능이다.
제네릭스를 사용하면 자료형을 강제로 바꿀 때 생길 수 있는 캐스팅 오류를 줄일 수 있다.
일반적인 방식 : ArrayList<String> pitches = new ArrayList<String>();
선호되는 방식 : ArrayList<String> pitches = new ArrayList<>();
뒷 부분의 자료형은 명확하므로 굳이 적지 않아도 된다.
제네릭스 도입 전까지는 다음과 같이 사용함
ArrayList pitches = new ArrayList();
제네릭스 도입 전후의 차이는 ArrayList 자료형 다음에 <String> 이 있는가에 있다.
제네릭스를 표현한 <String> 은 ‘ArrayList에 담을 수 있는 자료형은 String뿐이다’라는 뜻이다.
제네릭스를 이용하면 자료형을 좀 더 명확하게 체크할 수 있다.
제네릭스를 사용하지 않은 예시
ArrayList pitches = new ArrayList();
pitches.add("138");
pitches.add("129");
String one = (String) pitches.get(0);
String two = (String) pitches.get(1);
제네릭스를 사용하지 않으면 ArrayList에 추가하는 객체는 Object 자료형으로 인식됨
Object 자료형은 모든 객체가 상속하고 있는 가장 기본적인 자료형이다.
따라서 ArrayList 객체인 pitches에 값을 넣을 때는 문제가 없지만 값을 가져올 때는 매번 Object 자료형에서 String 자료형으로 형 변환(cating)을 해야한다.
String one = (String) pitches.get(0); // Object 자료형을 String 자료형으로 형 변환
이 때 주의해야 할 점은 pitches에는 String 외의 다른 객체도 넣을 수 있어서 형 변환 오류가 발생할 수 있다는 것이다.
앞에 코드에 제네릭스를 적용해보자
ArrayList<String> pitches = new ArrayList<>();
pitches.add("138");
pitches.add("129");
String one = pitches.get(0); // 형 변환이 필요없다.
String two = pitches.get(1); // 형 변환이 필요없다.
제네릭스로 자료형을 선언하면 그 이후로는 자료형을 형 변환하는 과정이 필요없다.
pitches에는 반드시 String 자료형만 추가되어야 한다는 것을 컴파일러가 이미 알기 때문이다.
이처럼 제네릭스를 이용하면 형 변한과 관련된 불필요한 코딩을 줄일 수 있고, 잘못된 형 변환 때문에 발생하는 런타임 오류를 방지할 수 있다.
- 다양한 방법으로 ArrayList 만들기
ArrayList의 add 메서드를 사용하면 ArrayList 객체에 요소를 추가할 수 있다.
import java.util.ArrayList;
public class Sample {
public static void main(String[] args) {
ArrayList<String> pitches = new ArrayList<>(); // 제네릭스를 사용한 표현
pitches.add("138");
pitches.add("129");
pitches.add("142");
System.out.println(pitches); // [138, 129, 142] 출력
}
}
하지만 이미 문자열 배열이 있으면 ArrayList를 좀 더 편하게 생성할 수 있다.
import java.util.ArrayList;
import java.util.Arrays;
public class Sample {
public static void main(String[] args) {
String[] data = {"138", "129", "142"}; // 이미 투구수 데이터 배열이 있다.
ArrayList<String> pitches = new ArrayList<>(Arrays.asList(data));
System.out.println(pitches); // [138, 129, 142] 출력
}
}
이처럼 java.util.Array 클래스의 asList 메서드를 사용하면 이미 존재하는 문자열 배열로 ArrayList를 만들 수 있다.
또는 String 배열 대신 String 자료형을 여러 개 전달하여 생성할 수도 있다.
import java.util.ArrayList;
import java.util.Arrays;
public class Sample {
public static void main(String[] args) {
ArrayList<String> pitches = new ArrayList<>(Arrays.asList("138", "129", "142"));
System.out.println(pitches);
}
}
- String.join
ArrayList의 각 요소를 콤마(,)로 구분해서 1개의 문자열로 만들 수 있을까?
import java.util.ArrayList;
import java.util.Arrays;
public class Sample {
public static void main(String[] args) {
ArrayList<String> pitches = new ArrayList<>(Arrays.asList("138", "129", "142"));
String result = String.join(",", pitches);
System.out.println(result); // 138,129,142 출력
}
}
String.join 을 사용하면 더 간단하게 처리할 수 있다.
String.join(”구분자”, 리스트 객체) 를 사용해 리스트의 각 요소에 구분자를 넣어 하나의 문자열로 만들 수 있다.
String.join은 문자열 배열에도 사용할 수 있다.
public class Sample {
public static void main(String[] args) {
String[] pitches = new String[]{"138", "129", "142");
String result = String.join(",", pitches);
System.out.println(result); // 138,129,142 출력
}
}
- 리스트 정렬하기
ArrayList를 순서대로 정렬해보자.
리스트의 sort 메서드 이용하기
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
public class Sample {
public static void main(String[] args) {
ArrayList<String> pitches = new ArrayList<>(Arrays.asList("138", "129", "142"));
pitches.sort(Comparator.naturalOrder()); //오름차순 정렬
System.out.println(pitches); // [129, 138, 142] 출력
}
}
오름차순 정렬 : Comparator.naturalOrder()
내림차순 정렬 : Comparator.reverseOrder()
sort 메서드로 정렬하고 pitches를 출력하면 [129, 138, 142]처럼 오름차순으로 정렬되어 출력되는 것을 확인할 수 있다.
맵
사람을 ‘이름 = 홍길동’, 생일= 몇 월 며칠’ 등으로 구분할 수 있듯이 맵(Map)은 대응 관계를 쉽게 표현할 수 있게 해주는 자료형
다른 언어에서도 있는 자료형임, associative array 또는 hash라고도 불림
맵은 키(key)와 값(value)을 한 쌍으로 갖는 자료형이다.
맵은 리스트나 배열처럼 순차적으로 요솟값을 구하지 않고 키(key)를 이용해 값(value)을 얻는다.
맵 자료형에는 HashMap, LinkedHashMap, TreeMap 등이 있음
- HashMap
맵 자료형 중 가장 기본인 HashMap에 대해 알아보자.
1) put
put메서드는 key와 value를 추가할 수 있다.
key와 value가 String인 HashMap을 만들고 항목을 입력해보자
import java.util.HashMap;
public class Sample {
public static void main(String[] args) {
HashMap<String, String> map = new HashMap<>();
map.put("people", "사람");
map.put("baseball", "야구");
}
}
HashMap 역시 제네릭스를 이용한다. HashMap의 제네릭스는 key, value 모두 String 자료형이다. 따라서 key, value에 String이외의 자료형은 사용할 수 없다.
2) get
get 메서드는 key에 해당하는 value를 얻을 때 사용한다.
import java.util.HashMap;
public class Sample {
public static void main(String[] args) {
HashMap<String, String> map = new HashMap<>();
map.put("people", "사람");
map.put("baseball", "야구");
System.out.println(map.get("people")); // "사람 출력"
}
}
key인 people에 대응하는 value인 사람이 출력됨
- getOrDefault
맵의 key에 해당하는 value가 없을 때 get메서드를 이용하면 null이 리턴된다.
System.out.println(map.get("java")); // null 출력
null 대신 기본값(default)을 얻고 싶으면 getOrDefault 메서드를 이용하면 된다.
System.out.println(map.getOrDefault("java", "자바"); //자바 출력
3) containKey
containKey 메서드는 맵에 해당하는 key가 있는지를 참(true) 또는 거짓(false)으로 리턴한다.
HashMap<String, String> map = new HashMap<>();
map.put("people", "사람");
map.put("baseball", "야구");
System.out.println(map.containKey("people")); //true 출력
4) remove
맵의 항목을 삭제하는 메서드, key항목을 삭제한 후 value 값을 리턴한다.
HashMap<String, String> map = new HashMap<>();
map.put("people", "사람");
map.put("baseball", "야구");
System.out.println(map.remove("people")); // 사람 출력
5) size
size 메서드는 맵 요소의 개수를 리턴한다.
HashMap<String,String> map = new HashMap<>();
map.put("people", "사람");
map.put("baseball", "야구");
System.out.println(map.remove("people");
System.out.println(size()); // 1
6) keySet
keySet은 맵의 모든 key를 모아서 리턴한다.
import java.util.HashMap;
public class Sample{
public static void main(String[] args){
HashMap<String, String> map = new HashMap<>();
map.put("people", "사람");
map.put("baseball", "야구");
System.out.println(map.keySet()); // [baseball, people]
}
}
✅ 집합 자료형은 리스트 자료형으로 바꾸어 사용할 수 있음 - ArrayList이용
ArrayList<String> list = new ArrayList<>(map.keySet());
- LinkedHashMap과 TreeMap
맵의 가장 큰 특징은 순서에 의존하지 않고 key로 value를 가져오는 것이다.
그런데 가끔 Map에 입력된 순서대로 데이터를 가져오거나 입력한 key에 의해 정렬(sort)하도록 저장하고 싶을 수 있다.
이럴 때는 LinkedHashMap과 TreeMap을 이용하면 된다.
LinkedHashMap : 입력된 순서대로 데이터를 저장한다. TreeMap : 입력된 Key의 오름차순으로 데이터를 저장한다.
리스트는 순서가 중요하지만 맵은 순서에 의존하지 않는다. 따라서 프로그래밍의 목적에 따라 개발자가 잘 선택해서 사용하자
집합(Set)
집합(Set)자료형은 집합과 관련된 것을 쉽게 처리하기 위해서 만든 것으로
HashSet, TreeSet, LinkedHashSet 등이 있다.
이중 가장 많이 사용하는 HashSet에 대해 알아보기
import java.util.Arrays;
import java.util.Hashset;
public class Sample{
public static void main(String[] args) {
HashSet<String> set = new HashSet<>(Array.asList("H", "e", "l", "l", "o"));
System.out.println(set); // [e, H, l, o] 출력
}
}
- 집합 자료형의 2가지 특징
분명 문자열 H, e, l, l, o라는 문자열 배열로 HashSet 자료형을 만들었는데 출력된 자료형에는 l 문자가 빠져있고 순서도 뒤죽박죽이다.
그 이유는 2가지 특징 때문인데.
- 중복을 허용하지 않는다.
- 순서가 없다(unordered)
리스트나 배열은 순서가 있기 때문에 인덱싱을 통해 자료형의 값을 얻을 수 있지만
집합 자료형은 순서가 없기 때문에 인덱싱으로 값을 얻을 수 없다.
이는 마치 맵 자료형과 비슷하다. 맵 자료형 역시 순서가 없는 자료형이라 인덱싱을 지원하지 않는다.
집합 자료형은 중복을 허용하지 않기 때문에 자료형의 중복을 제거하기 위한 필터역할로 종종 사용한다.
- 교, 합, 차집합 구하기
먼저 집합 자료형 2개 만들기
import java.util.Arrays;
import java.util.HashSet;
public class Sample {
public static void main(String[] args) {
HashSet<Integer> s1 = new HashSet<>(Arrays.asList(1,2,3,4,5,6));
HashSet<Integer> s2 = new HashSet<>(Arrays.asList(4,5,6,7,8,9));
}
}
s1은 1부터 6까지 s2는 4부터 9까지
1) 교집합 구하기
retainAll 메서드를 이용하면 교집합을 쉽게 구할 수 있다.
s1의 데이터를 유지하기 위해 s1으로 intersection이라는 HashSet 객체를 복사해 생성했다.
만약 intersection 대신 s1에 retainAll 메서드를 사용하면 s1내용이 바뀔 것이다
import java.util.Arrays;
import java.util.HashSet;
public class Sample {
public static void main(String[] args) {
HashSet<Integer> s1 = new HashSet<>(Arrays.asList(1, 2, 3, 4, 5, 6));
HashSet<Integer> s2 = new HashSet<>(Arrays.asList(4, 5, 6, 7, 8, 9));
HashSet<Integer> intersection = new HashSet<>(s1);
intersection.retainAll(s2); // 교집합
System.out.println(intersection); // 4 5 6 출력
}
}
2) 합집합 구하기
addAll 메서드 이용하기
import java.util.Arrays;
import java.util.HashSet;
public class Sample {
public static void main(String[] args) {
HashSet<Integer> s1 = new HashSet<>(Arrays.asList(1, 2, 3, 4, 5, 6));
HashSet<Integer> s2 = new HashSet<>(Arrays.asList(4, 5, 6, 7, 8, 9));
HashSet<Integer> union = new Hashset<>(s1);
union.addAll(s2); // 합집합 수행
System.out.println(union); //[1, 2, 3, 4, 5, 6, 7, 8, 9]
}
}
3) 차집합 구하기
차집합은 removeAll 메서드를 사용
import java.util.Arrays;
import java.util.HashSet;
public class Sample {
public static void main(String[] args) {
HashSet<Integer> s1 = new HashSet<>(Arrays.asList(1, 2, 3, 4, 5, 6));
HashSet<Integer> s2 = new HashSet<>(Arrays.asList(4, 5, 6, 7, 8, 9));
HashSet<Integer> sub = new HashSet<>(s1);
sub.removeAll(s2); //차집합 수행
System.out.println(sub); // [1, 2, 3] 출력
}
}
- 좀 더 알아보기 add, addAll, remove
4) add
add 메서드는 집합 자료형에 값을 추가할 때 사용한다.
import java.util.HashSet;
public class Sample {
public static void main(String[] args) {
HashSet<String> set = new HashSet<>();
set.add("jump");
set.add("to");
set.add("java");
System.out.println(set); // [java, to, jump 출력
}
}
5) addAll
한꺼번에 여러개를 추가하기
import java.util.Arrays;
import java.util.HashSet;
public class Sample {
public static void main(String[] args) {
HashSet<String> set = new HashSet<>();
set.add("jump");
set.addAll(Arrays.asList("to", "java"));
System.out.println(set);
}
}
6) remove
remove 메서드는 특정 값을 제거할 때 사용
import java.util.Arrays;
import java.util.HashSet;
public class Sample {
public static void main(String[] args) {
HashMap<String> set = new HashSet<>(Arrays.asList("Jump", "To", "Java"));
set.remove("to");
System.out.println(set);
}
}
상수 집합(enum 사용하기)
enum 자료형은 서로 연관 있는 여러 개의 상수 집합을 정의할 때 사용한다.
예를 들어 어느 카페에서 판매하는 커피의 종류가 다음과 같다.
아메리카노
아이스 아메리카노
카페라떼
3가지 커피를 판매한다면 enum으로 상수 집합을 만들 수 있다.
enum CoffeeType{
AMERICANO,
ICE_AMERICANO,
CAFE_LATTE
};
이렇게 정의한 상수 집합은 다음과 같이 사용할 수 있다.
public class Sample {
enum CoffeeType{
AMERICANO,
ICE_AMERICANO,
CAFE_LATTE
};
public static void main(String[] args) {
System.out.println(CoffeeType.AMERICANO);
System.out.println(CoffeeType.ICE_AMERICANO);
System.out.println(CoffeeType.CAFE_LATTE);
}
}
또는 반복문도 사용가능.
public class Sample {
enum CoffeeType }
AMERICANO,
ICE_AMERICANO,
CAFE_LATTE
};
public static void main(String[] args){
for(CoffeeType type : CoffeeType.values()) {
System.out.println(type); // 순서대로 출력
}
}
}
- enum이 필요한 이유
enum 사용 장점 알아보기.
/*
countSellCoffee는 판매된 커피의 갯수를 리턴하는 메서드
@param type 커피의 종류
(1: 아메리카노, 2: 아이스 아메리카노, 3: 카페라떼)
*/
int countSellCoffee(int type){
...
}
countSellCoffee 메서드는 종류별로 판매된 커피의 개수를 리턴함.
아메리카노의 판매 개수를 알려려면 다음과 같이 숫자 1을 넘겨야함.
이 방식은 숫자 1이 아메리카노라고 기억하고 사용해야해서 불편함
int americano = countSellCoffee(1);
그리고 다음과 같이 사용할 때도 문제가 발생한다.
int result = countSellCoffe(99);
// 99라는 커피 종류는 존재 하지 않음
물론 1,2,3이라는 인수의 값과 의미를 명확하게 알고 있다면 오류는 발생하지 않지만
오류의 가능성은 남음
앞에서 사용한 메서드를 바꿔보기.
enum CoffeeType {
AMERICANO,
ICE_AMERICANO,
CAFE_LATTE
};
/**
* countSellCoffee는 판매된 커피의 갯수를 리턴하는 메서드이다.
* @param type 커피의 종류 (CoffeType)
*/
int countSellCoffee(CoffeType type) {
... 생략 ...
}
메서드의 매개변수를 이렇게 바꾸면 메서드를 호출할 때 숫자 대신 CoffeeType 인수로 사용해야함
int americano = countSellCoffee(CoffeType.AMERIVANO);
// 아메리카노의 판매갯수
1을 사용했을 때보다 코드가 명확해진다. 그리고 countSellCoffee 메서드에는 CoffeeType에 정의된 상수만 전달할 수 있기 때문에 위에서 보았던 99처럼 엉뚱한 숫잣값을 입력해 생기는 오류가 발생하지 않는다.
정리하면 enum은 다음과 같은 장점이 있음
- 매직넘버를 사용할 때보다 코드가 명확함
- 잘못된 값을 사용해 생길 수 있는 오류를 막을 수 있음
형 변환과 fianl
- 형 변환
문자열이 다음과 같이 있을 때 int로 바꿔보자
String to int
public class Sample {
public static void main(String[] args) {
String num = "123";
int n = Integer.parseInt(num);
System.out.println(n); // 123 출력
}
}
int to String
세 가지 방법이 있음.
public class Sample{
public static void main(String[] args) {
int n = 123;
String num = "" + n;
System.out.println(num); // 123 출력
String num1 = String.valueOf(n);
String num2 = Integer.toString(n);
System.out.println(num1); // 123 출력
System.out.println(num2); // 123 출력
}
}
소수점이 포함되어 있는 숫자 형태의 문자열은 아래와 같은 방법으로 형변환한다.
float는 Float.parseFloat
public class Sample {
public static void main(String[] args) {
String num = "123.456";
double d = Double.parseDouble(num);
System.out.println(d);
}
}
자주 사용하지 않지만 정수와 실수 사이의 형변환도 가능함.
public class Sample {
public static void main(String[] args) {
int n1 = 123;
double d1 = n1; // integer to double은 캐스팅이 필요없음.
double d2 = 123.456;
int n2 = (int) d2; // 실수를 정수로 바꿀때에는 반드시 정수형으로 캐스팅해 주어야 한다.
System.out.println(n2); // 소숫점이 생략된 123 출력
}
}
실수를 정수로 변환하면 소수점이 제거됨.
int n2 = (int) d2; 에서 d2앞의 int 는 d2의 자료형을 강제로 int형으로 바꾼다는 의미
이를 casting이라고 함.
실수 형태의 문자열을 정수로 바꿀 때는 NumberFormatException이 발생하므로 주의해야 한다.
public class Sample {
public static void main(String[] args) {
String num = "123.456";
int n = Integer.parseInt(num); // 실수 형태의 문자열을 정수로 변환할 경우 NumberFormatException이 발생한다.
}
}
실수 형태의 문자열을 숫자형으로 바꾸려면 Double.parseDouble() 을 이용해 실수로 바꾼 후 사용하자
final
final은 자료형에 값을 단 한 번만 설정할 수 있게 강제하는 키워드
값을 한번 설정하면 그 값을 다시 설정할 수 없음
public class Sample {
public static void main(String[] args) {
final int n = 123; // final로 설정하면 값을 바꿀 수 없음
n = 456; // 컴파일 오류 발생
}
}
리스트의 경우도 final로 선언하면 재할당 불가.
import java.util.ArrayList;
import java.util.Arrays;
public class Sample {
public static void main(String[] args) {
final ArrayList<String> a = new ArrayList<>(Arrays.asList("a", "b");
a = new ArrayList<>(Arrays.asList("c", "d")); // 컴파일 에러 발생
}
}
- Unmodifiable List
리스트의 경우 final로 선언할 때 리스트에 값을 더하거나(add) 뺄(remove)수 있다.
다만 재할당만 불가능할 뿐이다.
만약 값을 더하거나 빼는 것도 할 수 없게 하고 싶다면 List.of()를 작성하여
수정할 수 없는 리스트(Unmodifiable List)로 만들면 된다.
참고자료 :
위키독스 점프 투 자바 https://wikidocs.net/book/31
'Java' 카테고리의 다른 글
[점프 투 자바] 5장 객체지향 프로그래밍1 (1) | 2024.01.01 |
---|---|
[점프 투 자바] 4장 제어문 이해하기 (0) | 2024.01.01 |
[점프 투 자바] 3장 자료형 (String 내장 메서드 등) 배열까지. (0) | 2024.01.01 |
[점프 투 자바] 2장 자바 시작하기 (0) | 2024.01.01 |
[점프 투 자바] 1장 자바란 무엇인가? (2) | 2024.01.01 |