○ 직접 구현하는 Iterable, Interator
자바는 자료구조 구현과 관계없이 모든 자료 구조를 동일한 방법으로 순회할 수 있는 Iterable,Iterator 인터페이스를 제공한다.
public interface Iterable<T> {
Iterator<T> iterator(); //iterator 반복자 반환
}
public interface Iterator<E> {
boolean hasNext(); //다음요소 없으면 false
E next(); //다음요소 반환
}
먼저 Iterator 구현체를 만든다.
public class MyArrayIterator implements Iterator<Integer> {
private int currentIndex = -1;
private int[] targetArr;
public MyArrayIterator(int[] targetArr) {
this.targetArr = targetArr;
}
@Override
public boolean hasNext() {
return currentIndex < targetArr.length - 1;
}
@Override
public Integer next() {
return targetArr[++currentIndex];
}
}
Iterator는 단독사용이 불가하다.
Iterable 인터페이스를 구현하면 Iterator() 메서드를 구현해야 한다.
Iterator를 통해 순회 대상이 되는 자료구조를 만든다.
public class MyArray implements Iterable<Integer>{
private int[] numbers;
public MyArray(int[] numbers) {
this.numbers = numbers;
}
@Override
public Iterator<Integer> iterator() {
return new MyArrayIterator(numbers); //사용할 Iterator 반환
}
}
public class MyArrayMain {
public static void main(String[] args) {
MyArray myArray = new MyArray(new int[]{1,2,3,4});
Iterator<Integer> iterator = myArray.iterator();
System.out.println("iterator");
while (iterator.hasNext()){
Integer value = iterator.next();
System.out.println("value = " + value);
}
}
}
○ 순회2 - 향상된 for문
자바는 Iterable 인터페이스를 구현한 객체에 대해서 향상된 for문을 사용할 수 있게 해준다.
for (int value : myArray) {
System.out.println("value = " + value);
}
위의 코드는 컴파일 시점에 아래와 같이 변경된다.
//자바는 컴파일 시점 코드 변경
while (iterator.hasNext()) {
Integer value = iterator.next();
System.out.println("value = " + value);
}
○ 순회3 - 자바가 제공하는 Iterable, Iterator
자바가 제공하는 컬렉션 프레임워크의 모든 자료 구조는 Iterable 과 Iterator 를 사용해서 편리하고 일관된 방법으로 순회할 수 있다. 물론 Iterable 을 구현하기 때문에 향상된 for문도 사용할 수 있다.
public class JavaIterableMain {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
Set<Integer> set = new HashSet<>();
set.add(1);
set.add(2);
set.add(3);
printAll(list.iterator());
printAll(set.iterator());
}
private static void printAll(Iterator<Integer> iterator) {
System.out.println("iterator = " + iterator.getClass());
while(iterator.hasNext()) {
System.out.println(iterator.next());
}
}
private static void foreach(Iterable<Integer> iterable) {
System.out.println("iterable = " + iterable.getClass());
for(Integer i:iterable){
System.out.println("i = " + i);
}
}
}
○ 정렬1 - Comparable, Comparator
자바는 비교자(Comparator)를 사용해 비교 기준을 직접 제공할 수 있다.
//첫번째 인수가 더 작으면 음수, 두 값이 같으면 0, 첫번재 인수가 더 크면 양수
public interface Comparator<T> {
int compare(T o1, T o2);
}
import java.util.Arrays;
import java.util.Comparator;
public class SortMain2 {
public static void main(String[] args) {
Integer[] array = {3,2,1};
System.out.println(Arrays.toString(array));
System.out.println("Comparator 비교");
Arrays.sort(array, new AscComparator());
Arrays.sort(array, new DescComparator());
System.out.println("DescComparator:" + Arrays.toString(array));
Arrays.sort(array, new AscComparator().reversed()); //DescComparator 와 같다.
System.out.println("AscComparator.reversed:" + Arrays.toString(array));
}
static class AscComparator implements Comparator<Integer> {
@Override
public int compare(Integer o1, Integer o2) {
System.out.println("o1 = " + o1 + " o2 = " + o2);
return (o1 < o2) ? -1 : ((o1 == o2) ? 0 : 1);
}
}
static class DescComparator implements Comparator<Integer> {
@Override
public int compare(Integer o1, Integer o2) {
System.out.println("o1 = " + o1 + " o2 = " + o2);
return (o1 < o2) ? -1 : ((o1 == o2) ? 0 : 1) * -1;
}
}
}
○ 정렬2 - Comparable, Comparator
직접만든 객체를 정렬하려면 어떻게 할까?
기본정렬을 할때에는 객체가 스스로 가지고 있는 Comparable 인터페이스를 사용해서 비교한다.
Comparable 을 통해 구현한 순서를 자연 순서(Natural Ordering)라 한다.
//현재 객체가 인수로 주어진 객체보다 더 작으면 음수
//두 객체 크기가 같으면 0
//현재 객체가 인수로 주어진 객체보다 더 크면 양수
public interface Comparable<T> {
public int compareTo(T o);
}
public class MyUser implements Comparable<MyUser>{
private String id;
private int age;
public MyUser(String id,int age){
this.id = id;
this.age = age;
}
public String getId() {
return id;
}
public int getAge() {
return age;
}
@Override
public int compareTo(MyUser o) {
return this.age < o.age ? -1 : (this.age == o.age ? 0 : 1);
}
@Override
public String toString() {
return "MyUser{" +
"id='" + id + '\'' +
", age=" + age +
'}';
}
}
public class SortMain3 {
public static void main(String[] args) {
MyUser myUser1 = new MyUser("a", 30);
MyUser myUser2 = new MyUser("b", 20);
MyUser myUser3 = new MyUser("c", 10);
MyUser[] array = {myUser1,myUser2,myUser3};
System.out.println("기본 데이터");
System.out.println(Arrays.toString(array));
System.out.println("Comparable 기본 정렬");
Arrays.sort(array);
System.out.println(Arrays.toString(array)); //오름차순
}
}
객체가 가지고있는 Comparable 기본정렬이 아닌 다른 정렬을 사용하려면 Arrays.sort 인수로 비교자 Comparator를 만들어서 넘겨주면 된다.이렇게 비교자를 따로 전달하면 객체가 가지고있는 것을 무시하고 별도 전달 비교자를 사용해 전달한다.
public class IdComparator implements Comparator<MyUser> {
@Override
public int compare(MyUser o1, MyUser o2) {
return o1.getId().compareTo(o2.getId());
}
}
//SortMain3 추가
System.out.println("IdComparator 정렬");
Arrays.sort(array, new IdComparator());
System.out.println(Arrays.toString(array));
System.out.println("IdComparator().reversed() 정렬");
Arrays.sort(array, new IdComparator().reversed());
System.out.println(Arrays.toString(array));
○ 정렬3 - Comparable, Comparator
정렬은 배열 뿐만 아니라 List 같은 자료 구조에도 사용할 수 있다. 보통은 객체 스스로 정렬 메서드를 가지고 있는 list.sort() 사용을 더 권장한다.
public class SortMain4 {
public static void main(String[] args) {
MyUser myUser1 = new MyUser("a", 30);
MyUser myUser2 = new MyUser("b", 20);
MyUser myUser3 = new MyUser("c", 10);
List<MyUser> list = new LinkedList<>();
list.add(myUser1);
list.add(myUser2);
list.add(myUser3);
System.out.println("기본 데이터");
System.out.println(list);
System.out.println("Comparable 기본 정렬");
list.sort(null);
//Collections.sort(list);
System.out.println(list);
System.out.println("IdComparator 정렬");
list.sort(new IdComparator());
//Collections.sort(list, new IdComparator());
System.out.println(list);
}
}
참고로,이진 탐색 트리 구조는 데이터를 보관할때, 데이터를 정렬하면서 보관한다.따라서 정렬 기준을 제공하는 것이 필수다.
즉 TreeSet , TreeMap 은 Comparable 또는 Comparator 가 필수이다.
public class SortMain5 {
public static void main(String[] args) {
MyUser myUser1 = new MyUser("a", 30);
MyUser myUser2 = new MyUser("b", 20);
MyUser myUser3 = new MyUser("c", 10);
TreeSet<MyUser> treeSet1 = new TreeSet<>();
treeSet1.add(myUser1);
treeSet1.add(myUser2);
treeSet1.add(myUser3);
System.out.println("Comparable 기본 정렬");
System.out.println(treeSet1);
TreeSet<MyUser> treeSet2 = new TreeSet<>(new IdComparator());
treeSet2.add(myUser1);
treeSet2.add(myUser2);
treeSet2.add(myUser3);
System.out.println("IdComparator 정렬");
System.out.println(treeSet2);
}
}
○ 컬렉션 유틸
🟢 collections 정렬 관련 메서드
- max : 정렬 기준으로 최대 값을 찾아서 반환한다.
- min : 정렬 기준으로 최소 값을 찾아서 반환한다.
- shuffle : 컬렉션을 랜덤하게 섞는다.
- sort : 정렬 기준으로 컬렉션을 정렬한다.
- reverse : 정렬 기준의 반대로 컬렉션을 정렬한다.
🟢 불변 컬렉션
- List.of(...)를 사용하면 불변 컬렉션이 생성된다.
- 불변 컬렉션 변경 메서드를 호출하면 UnsupportedOperationException 예외가 발생한다.
- 가변 리스트를 불변 리스트로 전환하려면 Collections.unmodifiableList() 를 사용하면 된다.
public class ImmutableMain {
public static void main(String[] args) {
//불변 리스트 생성
List<Integer> list = List.of(1,2,3);
//가변 리스트
ArrayList<Integer> mutableList = new ArrayList<>(list);
mutableList.add(4);
System.out.println("mutableList = " + mutableList);
System.out.println("mutableList class = " + mutableList.getClass());
//불변 리스트
List<Integer> unmodifiableList = Collections.unmodifiableList(mutableList);
System.out.println("unmodifiableList class = " +
unmodifiableList.getClass());
//예외 발생 java.lang.UnsupportedOperationException
// unmodifiableList.add(5);
}
}
mutableList = [1, 2, 3, 4]
mutableList class = class java.util.ArrayList
unmodifiableList class = class
java.util.Collections$UnmodifiableRandomAccessList
🟢 빈 불변 리스트 생성
- Collections.emptyList() : 자바5부터 제공되는 기능
- List.of() : 자바9부터 제공되는 최신 기능
🟢 Arrays.asList()
Arrays.asList() 로 생성된 리스트는 고정된 크기를 가지지만, 요소들은 변경할 수 있다. (고정도 가변도 아닌 애매한 리스트이다...)
List<Integer> list = Arrays.asList(1, 2, 3); //자바 1.2부터 존재함
List<Integer> list = List.of(1, 2, 3); //자바 9부터 (권장)
🟢 멀티스레드 동기화
Collections.synchronizedList 를 사용하면 일반 리스트를 멀티스레드 상황에서 동기화 문제가 발생하지 않는 안전한 리스트로 만들 수 있다.동기화 작업으로 인해 일반 리스트보다 성능은 더 느리다.
public class SyncMain {
public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
//list class = class java.util.ArrayList
System.out.println("list class = " + list.getClass());
List<Integer> synchronizedList = Collections.synchronizedList(list);
//synchronizedList class = class
//java.util.Collections$SynchronizedRandomAccessList
System.out.println("synchronizedList class = " + synchronizedList.getClass());
}
}
○ 컬렉션 프레임워크 전체 정리
🟢 collection 인터페이스 주요 메서드
- add(E e) : 컬렉션에 요소를 추가한다.
- remove(Object o) : 주어진 객체를 컬렉션에서 제거한다.
- size() : 컬렉션에 포함된 요소의 수를 반환한다.
- isEmpty() : 컬렉션이 비어 있는지 확인한다.
- contains(Object o) : 컬렉션이 특정 요소를 포함하고 있는지 확인한다.
- iterator() : 컬렉션의 요소에 접근하기 위한 반복자를 반환한다.
- clear() : 컬렉션의 모든 요소를 제거한다.
🟢 자바 컬렉션 프레임워크 핵심 인터페이스
- Collection: 단일 루트 인터페이스로, 모든 컬렉션 클래스가 이 인터페이스를 상속받는다.
- List: 순서가 있는 컬렉션을 나타내며, 중복 요소를 허용한다. 인덱스를 통해 요소에 접근할 수 있다.
- Set: 중복 요소를 허용하지 않는 컬렉션을 나타낸다. 특정 위치가 없기 때문에 인덱스를 통해 요소에 접근할 수 없
다. - Queue: 요소가 처리되기 전에 보관되는 컬렉션
- Map: 키와 값 쌍으로 요소를 저장하는 객체이다. Map 은 Collection 인터페이스를 상속받지 않는다.
🟢 실무 선택 가이드
- List 의 경우 대부분 ArrayList 를 사용한다.
- Set 의 경우 대부분 HashSet 을 사용한다.
- Map 의 경우 대부분 HashMap 을 사용한다.
- Queue 의 경우 대부분 ArrayDeque 를 사용한다.
'Java' 카테고리의 다른 글
[Java의 정석] chapter13 쓰레드 thread 알아보기 (0) | 2024.07.18 |
---|---|
[Java] 김영한의 실전 자바 - 고급 1편, 멀티스레드와 동시성 섹션1 프로세스와 스레드 소개 (0) | 2024.07.16 |
[Java] 김영한의 실전 자바 - 중급편2 섹션1 제네릭 - Generic1,Generic2 (0) | 2024.06.07 |
[Java] 김영한의 실전 자바 - 중급편 섹션10 예외처리2 - 실습 (0) | 2024.05.23 |
[Java] 김영한의 실전 자바 - 중급편 섹션9 예외처리1 - 이론 (0) | 2024.05.22 |