본문 바로가기

기술 서적/자바의 정석

[자바의 정석] 11-2.컬렉션 프레임워크 : iterator / Arrays / Comparator / Set

1.5 Iterator

더보기

- 컬렉션에 저장된 요소 접근에 사용

- iterator() : iterator 반환 메소드

- 컬렉션에 정의=> 자손인 List/Set에도 호출 가능

- List : 순서가 저장됨

- Set : 순서가 저장x 

- Map은 직접호출 불가 =>  EntrySet이나 keySet로 collection 얻어온 후,  호출 가능

 

>>메서드

ex)

package ch11;
import java.util.*;

public class IteratorEx1 {
    public static void main(String[] args) {
        ArrayList list = new ArrayList();
        list.add("1");
        list.add("2");
        list.add("3");
        list.add("4");
        list.add("5");

        Iterator it= list.iterator();
        while(it.hasNext()){
            Object obj= it.next();
            System.out.println(obj);
        }
        
    }
}

>>실행결과
1
2
3
4
5

 

>> ListIterator

: 양방향으로 이동이 가능

: 이동하기 전에 hasNext() / hasPrevious() 를 통해 읽어올 객체가 있는지 확인해야 함

 

ex)

package ch11;
import java.util.*;

public class IteratorEx1 {
    public static void main(String[] args) {
        ArrayList list = new ArrayList();
        list.add("1");
        list.add("2");
        list.add("3");
        list.add("4");
        list.add("5");

        ListIterator lit= list.listIterator();
        while(lit.hasNext()){
            Object obj= lit.next();
            System.out.print(obj+" ");
        }
        System.out.println();

        while(lit.hasPrevious()){
            Object obj = lit.previous();
            System.out.print(obj+" ");
        }
    }
}

>>실행결과
1 2 3 4 5
5 4 3 2 1

 

 

>> remove는 단독으로 쓰일 수 없고 next()와 함께 쓰여야 함

- 즉, 읽어온 것을 삭제함

- next() 호출없이 remove()를 호출하면 IllegalStateException이 발생됨

public class IteratorEx2 {
    public static void main(String[] args) {
        ArrayList original= new ArrayList(10);
        ArrayList copy1 = new ArrayList(10);
        ArrayList copy2= new ArrayList(10);

        for (int i = 0; i < 10; i++) {
            original.add(i+"");
        }
        Iterator it= original.iterator();

        while(it.hasNext()){
            copy1.add(it.next());
        }
        System.out.println("=Original에서 copy1로의 복사=");
        System.out.println("origianl : " + original);
        System.out.println("copy1 : "+ copy1);
        System.out.println();
        it= original.iterator();

        while(it.hasNext()){
            copy2.add(it.next());
            it.remove(); //읽은 값을 삭제
        }
        System.out.println("=Original에서 copy1로의 복사=");
        System.out.println("origianl : " + original);
        System.out.println("copy2 : "+ copy2);
    }
}

>>실행결과
=Original에서 copy1로의 복사=
origianl : [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
copy1 : [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

=Original에서 copy1로의 복사=
origianl : []
copy2 : [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

1.6 Arrays

더보기

- 배열을 다루는데 유용한 메서드

 

1. 배열의 복사 : copyOf(), copyOfRange()

 

2. 배열 채우기 : fill() / setAll()

- fill(배열, 값) : 배열을 모두 값으로 채움

- setAll(배열, 람다식/함수형 인터페이스) : 배열에 모두 메서드 호출

 

3. 배열의 정렬과 검색 : sort() / binarySearch()

-binearySearch() : 이진탐색은 반드시 정렬된 상태에서 해야 올바른 결과를 얻음

 

4. 배열의 비교와 출력 :  equals() / toString()

 

>> 다차원 배열의 비교 => deepEquals()/ deepToString()

=> 재귀적으로 equals와 toString을 호출

- 다차원 배열에서 equals를 하면 각 주소값이 호출되며 false가 나옴

- deepEquals를 해야 재귀적으로 equals가 호출되며 값을 온전히 비교

 

5. 리스트로 변환 : asList()

=> but, asList가 반환한 리스트의 경우, 크기를 변경할 수가 없음(추가/삭제가 불가함)

=> ArrayList로 변경해주어야 가능

 

ex)

package ch11;

import java.util.*;

public class ArrayEx {
    public static void main(String[] args) {
        int [] arr= {0,1,2,3,4};
        int [][] arr2D ={{11,12,13}, {21,22,23}};

        System.out.println("arr="+ Arrays.toString(arr));
        System.out.println("arr2D="+ Arrays.deepToString(arr2D));
        
        //arr=[0, 1, 2, 3, 4]
        //arr2D=[[11, 12, 13], [21, 22, 23]]
        
        int [] arr2= Arrays.copyOf(arr, arr.length);
        int [] arr3= Arrays.copyOf(arr, 3);
        int [] arr4 = Arrays.copyOf(arr, 7);
        int [] arr5 = Arrays.copyOfRange(arr, 2,4);
        int [] arr6 = Arrays.copyOfRange(arr, 0,7);

        System.out.println("arr2= "+ Arrays.toString(arr2));
        System.out.println("arr3= "+ Arrays.toString(arr3));
        System.out.println("arr4= "+ Arrays.toString(arr4));
        System.out.println("arr5= "+ Arrays.toString(arr5));
        System.out.println("arr6= "+ Arrays.toString(arr6));
        
        //arr2= [0, 1, 2, 3, 4]
        //arr3= [0, 1, 2]
        //arr4= [0, 1, 2, 3, 4, 0, 0]
        //arr5= [2, 3]
        //arr6= [0, 1, 2, 3, 4, 0, 0]
        
        int [] arr7= new int[5];
        Arrays.fill(arr7, 9);
        System.out.println("arr7=" + Arrays.toString(arr7));

        Arrays.setAll(arr7, i-> (int) (Math.random()*6)+1);
        System.out.println("arr7= "+Arrays.toString(arr7));

        //arr7=[9, 9, 9, 9, 9]
        //arr7= [3, 1, 1, 5, 4]
        
        for(int i: arr7){
            char [] graph = new char[i];
            Arrays.fill(graph, '*');
            System.out.println(new String(graph)+i);
        }
        
        //***3
        //*1
        //*1
        //*****5
        //****4
        
        String [][] str2D= new String [][] {{"aaa", "bbb"}, {"AAA", "BBB"}};
        String [][] str2D2 = new String [][] {{"aaa", "bbb"}, {"AAA", "BBB"}};

        System.out.println(Arrays.equals(str2D, str2D2)); //false
        System.out.println(Arrays.deepEquals(str2D, str2D2)); //true

        char [] chArr= {'A','D','C', 'B', 'E'};
        System.out.println("chArr = "+ Arrays.toString(chArr));
        System.out.println("index of B ="+ Arrays.binarySearch(chArr, 'B'));
        System.out.println("=After sorting =");
        Arrays.sort(chArr);
        System.out.println("chArr="+Arrays.toString(chArr));
        System.out.println("index of B="+ Arrays.binarySearch(chArr, 'B'));


        //chArr = [A, D, C, B, E]
        //index of B =-2 => 정렬을 안해서 이상한 값으로 나옴
        //=After sorting =
        //chArr=[A, B, C, D, E]
        //index of B=1
    }
}

1.7 Comparator와 Comparable

더보기

- 객체 정렬하는데 필요한 메서드를 정의한 인터페이스

- compare/ compareTo는 두 객체의 비교결과를 반환 => 작으면 0  오른쪽이 크면 - / 작으면 +

 

ex)

public class comparatorEx {
    public static void main(String[] args) {
        String [] strArr ={"cat", "Dog", "lion", "tiger"};
        Arrays.sort(strArr);
        System.out.println("strArr="+ Arrays.toString(strArr));

        Arrays.sort(strArr, String.CASE_INSENSITIVE_ORDER);//대소문자 구분x
        System.out.println("strArr="+ Arrays.toString(strArr));

        Arrays.sort(strArr, new Descending());
        System.out.println("strArr="+ Arrays.toString(strArr));
    }
}

class Descending implements Comparator{
    public int compare(Object o1, Object o2){
        if( o1 instanceof Comparable && o2 instanceof Comparable){
            Comparable c1= (Comparable)o1;
            Comparable c2= (Comparable)o2;
            return c1.compareTo(c2)*-1;
        }
        return -1;
    }
}

>>실행결과
strArr=[Dog, cat, lion, tiger]
strArr=[cat, Dog, lion, tiger]
strArr=[tiger, lion, cat, Dog]

 

-1) sort(strArr) => 대소문자 구분하며 정렬

-2) sort(strArr, String.CASE_INSENSTIVE_ORDER) => 대소문자 구분하지 않고 정렬 

-3) sort(strArr, new Descending()) => 기본 정렬의 역순(-1을 곱함)


1.8 SET : HashSet / TreeSet

더보기

--HashSet

: 순서x / 중복x

: 순서를 유지하려면 => LinkedHashSet을 사용해야 함

: boolean add(Object o) => 중복된 요소 추가시 false반환

 

 

>>생성자

HashSet() HashSet 객체를 생성한다.
HashSet(Collection c) 주어진 컬렉션을 포함하는 HashSet 객체를 생성한다.
HashSet(int initialCapacity) 주어진 값을 초기용량으로 하는 HashSet 객체를 생성한다.
HashSet(int initialCapacity, float loadFactor) 초기용량과 load factor를 지정하는 생성자

>>메서드

메서드 설명
boolean add(Object o)
boolean addAll(Collection c)
- 객체를 추가 / 성공하면 true
- 주어진 컬렉션에 저장된 모든 개게를 추가(합집합)
boolean remove(Object o)
boolean removeAll(Collection c)
- 객체를 삭제 / 성공하면 true
- Collection에 있는 요소들을 set에서 모두 삭제 / 성공하면 true
void clear() Set을 비우기
Object clone() Set을 복제해서 반환(얕은 복사)
boolean contains(Object o)
boolean containsAll(Collection c)
- 객체를 포함하는지 확인
- 컬렉션의 요소들을 모두 포함하는지 확인
boolean isEmpty() Set이 비었는지
Iterator iterator() Iterator 반환
boolean retainAll(Collection c) 교집합만을 남기
int size() 저장된 객체의 개수를 반환
Object [] toArray()
Object [] toArray(Object [] a)
-저장된 객체들을 객체배열의 형태로 만듦
- 반환 객체배열을 a에 담음

 

 

>>문제 : equals가 인스턴스에 대해 주소값으로 판단됨

- equals와 hashCode 오버라이딩을 통해 중복요소 판단에 대한 부분을 정의 가능

 

ex)

package ch11;
import java.util.*;
public class HashSetEx1 {
    public static void main(String[] args) {
        HashSet set = new HashSet();
        set.add("abc");
        set.add("abc");
        set.add(new Person("David", 10));
        set.add(new Person("David", 10));
        System.out.println(set);
    }
}

class Person{
    String name;
    int age;

    Person(String name, int age){
        this.name= name;
        this.age=age;
    }

    public String toString(){
        return name+":"+age;
    }
}

>>실행결과
[David:10, David:10, abc]

=> 두 객체의 주소값이 다르기에 다른 요소로 판단

=> 그럼 두 객체를 같은 것으로 인식하게 하기 위해서는 어떻게 해야하나? 

== hashCode와 equals오버라이딩

 

ex2)

package ch11;
import java.util.*;
public class HashSetEx1 {
    public static void main(String[] args) {
        HashSet set = new HashSet();
        set.add("abc");
        set.add("abc");
        set.add(new Person("David", 10));
        set.add(new Person("David", 10));
        System.out.println(set);
    }
}

class Person{
    String name;
    int age;

    Person(String name, int age){
        this.name= name;
        this.age=age;
    }

    public String toString(){
        return name+":"+age;
    }

    public boolean equals(Object obj){
        if(obj instanceof Person){
            Person p= (Person)obj;
            return name.equals(p.name) && age==p.age;
        }
        return false;
    }

    public int hashCode(){
        return Objects.hash(name, age);
    }
}

>>실행결과
[David:10, abc]

 

>> hashCode() 오버라이딩 시 유의사항

1) 동일 객체에 대해 hashCode를 여러번 호출해도 동일한 값을 반환

2) equals로 비교해서 true를 얻은 두 객체의 hashCode값은 일치해야 함

    => 같다고 판단한 것은 hash도 같아야 함

3) equals로 비교한 결과가 false인 두 객체의 hashCode()값은 같을 수 있음

  => hash는 더 넓은 범위를 작은 범위에 대응시키기 때문

 

ex3)

package ch11;
import java.util.*;
public class HashSetEx2 {
    public static void main(String[] args) {
        HashSet setA = new HashSet();
        HashSet setB = new HashSet();

        HashSet setHab = new HashSet();
        HashSet setKyo = new HashSet();
        HashSet setCha = new HashSet();

        setA.add("1");setA.add("2");setA.add("3");setA.add("4");setA.add("5");
        setB.add("4");setB.add("5");setB.add("6");setB.add("7");setB.add("8");

        Iterator it = setB.iterator();
        //교집합
        while(it.hasNext()){
            Object tmp= it.next();
            if(setA.contains(tmp)){
                setKyo.add(tmp);
            }
            setHab.add(tmp);
        }

        it= setA.iterator();
        //차집합
        while(it.hasNext()){
            Object tmp= it.next();
            if(!setB.contains(tmp)){
                setCha.add(tmp);
            }
            setHab.add(tmp);
        }

        System.out.println("교집합="+setKyo);
        System.out.println("합집합="+setHab);
        System.out.println("차집합="+setCha);
    }
}

>>실행결과
교집합=[4, 5]
합집합=[1, 2, 3, 4, 5, 6, 7, 8]
차집합=[1, 2, 3]

더보기

>>TreeSet

: 이진 검색트리로 이루어짐

: 정렬, 검색, 범위검색에 높은 성능을 보임

: 중복된 데이터 저장을 허용x + 정렬된 위치에 저장

: 부모보다 작은 값을 왼쪽에 큰 값은 오른쪽에

: HashSet보다 데이터 추가, 삭제에 시간이 더 걸림

 

>>데이터 저장과정

ex) 7 - 4 - 9 - 1 - 5 과정

 -> 컴퓨터는 알아서 값을 비교하지 못함

-> TreeSet에 저장되는 객체가 Comparable을 구현 or Comparator를 TreeSet에게 제공

 

>> 생성자와 메서드

 

>>범위검색 예제

import java.util.*;
public class TreeSetEx1 {
    public static void main(String[] args) {
        TreeSet set = new TreeSet();
        String from ="b";
        String to="d";

        set.add("abc"); set.add("alien"); set.add("bat");
        set.add("car"); set.add("Car"); set.add("disc");
        set.add("dance"); set.add("dZZZZ"); set.add("dzzzzz");
        set.add("elephant"); set.add("elevator"); set.add("fan");
        set.add("flower");

        System.out.println(set);
        System.out.println("range search : from " + from + " to "+ to);
        System.out.println("result1 : "+ set.subSet(from, to));
        System.out.println("result2 : "+ set.subSet(from, to+"zzz"));
    }
}

>>실행결과
[Car, abc, alien, bat, car, dZZZZ, dance, disc, dzzzzz, elephant, elevator, fan, flower]
range search : from b to d
result1 : [bat, car]
result2 : [bat, car, dZZZZ, dance, disc]

 

 

ex2) headSet / tailSet 

public class TreeSetEx2 {
    public static void main(String[] args) {
        TreeSet set= new TreeSet();
        int [] score={80,95,50,35,45,65,10,100};

        for (int i = 0; i <score.length ; i++) {
            set.add(score[i]);
        }
        System.out.println("50보다 작은 값 : "+ set.headSet(50));
        System.out.println("50보다 큰 값 : "+ set.tailSet(50));
    }
}

>>실행결과
50보다 작은 값 : [10, 35, 45]
50보다 큰 값 : [50, 65, 80, 95, 100]

 => 오토박싱되므로 new Integer(50)을 안해주어도 됨