Set<E> 컬렉션 인터페이스

jaegeunsong97·2023년 1월 21일
0
post-thumbnail

2023_1_21_TIL

Set< E > 컬렉션의 특징

  • 집합의 개념
  • 인덱스 정보 X -> 데이터를 중복해 저장하면 중복된 데이터 중 특정 데이터를 지칭해 꺼낼 방법 없음
  • Set< E >의 데이터를 구분하는 방법 '데이터'그 자체
  • 동일한 데이터의 중복 저장 허용 X -> '같음' 과 '다름' 비교 필요
  • 인터페이스이기 때문에 객체 생성 불가
    • 자식 클래스 이용
    • HashSet, LinkedHashSet, TreeSet

Set< E >의 주요 메소드

  • iterator() -> Iterator< E > 객체 리턴
    • Set< E > 객체에서 데이터를 1개씩 꺼내는 기능 포함
    • List< E >도 있지만 굳이 쓸 필요가 있을까?
    • for문으로 데이터 못 꺼냄
    • 하지만 for-each문으로는 가능

HashSet< E > 구현 클래스

  • HashSet< E > capacity -> 기본생성자로 생성 시 16기본
    • 데이터의 개수 증가시 동적으로 증가
  • 데이터 추가하기 - add()
    • 입출력 순서가 달라짐(주머니라고 생각)
  • 데이터 삭제하기 - remove(), clear()
  • 데이터 정보 추출하기 - isEmpty(), contains(), size(), iterator()
    • iterator() -> Iterator< E > 객체 리턴
      • 여기서 제네릭 타입은 HashSet< E >가 저장하고 있는 원소의 타입
      • boolean hasNext() -> 다음으로 가리킬 원소의 존재 여부를 불리언으로 리턴
      • E next() -> 다음 원소 위치로 가서 읽은 값을 리턴
      • 주의점 -> Iterator< E >객체 생성되면 객체가 가리ㅣ고 있는 위치는 첫 우너소 위치가 아닌 첫 원소 바로 이전의 위칫값 -> 첫 번째 원소를 읽으려면 iterator.next() 실행
  • 배열로 변환하기 - toArray(), toArray(T[ ] t)
    • HashSet< E >내의 모든 원소를 리턴
    • List< E >와 다른점은 입출력 순서가 다를 수 있음
    • Object[] toArray()
    • toArray(T[ ] t)
      • HashSet< E >데이터 개수 << 배열 크기 -> 데이터 개수만큼 확장
      • HashSet< E >데이터 개수 >> 배열 크기 -> 데이터 앞에서부터 채운 후 남은 것 공백으로 리턴
public class HashSetMethod {
    public static void main(String[] args) {
        Set<String> hSet1 = new HashSet<>();

        hSet1.add("가");
        hSet1.add("나");
        hSet1.add("가");
        System.out.println(hSet1.toString());

        Set<String> hSet2 = new HashSet<>();
        hSet2.add("나");
        hSet2.add("다");
        hSet2.addAll(hSet1);
        System.out.println(hSet2);

        hSet2.remove("나");
        System.out.println(hSet2);

        hSet2.clear();
        System.out.println(hSet2.toString());

        System.out.println(hSet2.isEmpty());

        Set<String> hSet3 = new HashSet<>();
        hSet3.add("가");
        hSet3.add("나");
        hSet3.add("다");
        System.out.println(hSet3.contains("나"));
        System.out.println(hSet3.contains("다"));

        System.out.println(hSet3.size());

        Iterator<String> iterator = hSet3.iterator();
        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }

        Object[] objArray = hSet3.toArray();
        System.out.println(Arrays.toString(objArray));

        String[] strArray = hSet3.toArray(new String[0]);// [가 나 다]
        System.out.println(Arrays.toString(strArray));

        String[] strArray2 = hSet3.toArray(new String[5]);// [가 나 다 null null]
        System.out.println(Arrays.toString(strArray2));
    }
}
  • HashSet< E >의 중복 확인 메커니즘
    • 모든 컬렉션은 객체만 저장가능 -> 3의 데이터를 넣는다? -> 내부적으로는 new Integer(3)과 같이 객체로 변환돼 저장
    • 해시코드 -> 객체가 저장된 번지와 연관된 값(실제 주소 아님)
      • 객체가 저장된 번지를 기준으로 생성된 정수형 고유값
    • == -> 스택 메모리값을 동등 비교
      • 기본자료형 -> 실제값을 비교
      • 참조자료형 -> 주소 비교
      • Object의 equals()는 메소드 내부에서는 == 과 같은 작업을 함
        • 따라서 equals()오버라이딩 무조건!
    • hashCode() 중복확인 메커니즘
        1. 두 객체의 해시코드가 동일한지 비교
        1. equals()를 통해 두 객체 비교
      • hashCode()는 Objects클래스의 static hash()를 사용해 field값을 기준으로 해시코드를 생성 -> field값이 같으면 같은 해시코드값 리턴
      • 앞에 Hash가 붙으면 모드 이런 중복 메커니즘을 가짐 -> HashMap<K, V> HashTree<K, V>
class A {
    int data;
    public A(int data) {
        this.data = data;
    }
}
class B {
    int data;
    public B(int data) {
        this.data = data;
    }
    @Override
    public boolean equals(Object obj) {
        if (obj instanceof B) {
            if (this.data == ((B) obj).data) {
                return true;
            }
        }
        return false;
    }
}
class C {
    int data;
    public C(int data) {
        this.data = data;
    }
    @Override
    public boolean equals(Object obj) {
        if (obj instanceof C) {
            this.data = ((C) obj).data;
            return true;
        }
        return false;
    }
    @Override
    public int hashCode() {
        return Objects.hash(data);
    }
}

public class HashSetMachanism {
    public static void main(String[] args) {
        Set<A> hashSet1 = new HashSet<>();
        A a1 = new A(3);
        A a2 = new A(3);
        System.out.println(a1 == a2);// false
        System.out.println(a1.equals(a2));// false
        System.out.println(a1.hashCode() + ", " + a2.hashCode());// different
        hashSet1.add(a1);
        hashSet1.add(a2);
        System.out.println(hashSet1.size());
        System.out.println();

        Set<B> hashSet2 = new HashSet<>();
        B b1 = new B(3);
        B b2 = new B(3);
        System.out.println(b1 == b2);// false
        System.out.println(b1.equals(b2));// true
        System.out.println(b1.hashCode() + ", " + b2.hashCode());// different
        hashSet2.add(b1);
        hashSet2.add(b2);
        System.out.println(hashSet2.size());
        System.out.println();

        Set<C> hashSet3 = new HashSet<>();
        C c1 = new C(3);
        C c2 = new C(3);
        System.out.println(c1 == c2);// false
        System.out.println(c1.equals(c2));// true
        System.out.println(c1.hashCode() + ", " + c2.hashCode());// same
        hashSet3.add(c1);
        hashSet3.add(c2);
        System.out.println(hashSet3.size());
    }
}

LinkedHashSet< E > 구현 클래스

  • 입출력 순서 동일(Linked 니까!) -> 데이터 연결 가능해서
  • 입출력 순서의 정보는 가지고 있음, but List< E >처럼 중간에 데이터를 추가하거나 특정 순서에 저장된 값을 가져오는 것은 불가능
public class LinkedHashSetMethod {
    public static void main(String[] args) {
        Set<String> linkedSet1 = new LinkedHashSet<>();

        linkedSet1.add("가");
        linkedSet1.add("나");
        linkedSet1.add("다");
        System.out.println(linkedSet1);

        Set<String> linkedSet2 = new LinkedHashSet<>();
        linkedSet2.add("나");
        linkedSet2.add("다");
        linkedSet2.addAll(linkedSet1);
        System.out.println(linkedSet2);

        linkedSet2.remove("나");
        System.out.println(linkedSet2);

        linkedSet2.clear();
        System.out.println(linkedSet2);

        System.out.println(linkedSet2.isEmpty());

        Set<String> linkedSet3 = new LinkedHashSet<>();
        linkedSet3.add("가");
        linkedSet3.add("나");
        linkedSet3.add("다");
        System.out.println(linkedSet3.contains("나"));
        System.out.println(linkedSet3.contains("라"));

        System.out.println(linkedSet3.size());

        Iterator<String> iterator = linkedSet3.iterator();
        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }

        Object[] objArray = linkedSet3.toArray();
        System.out.println(Arrays.toString(objArray));

        String[] strArray1 = linkedSet3.toArray(new String[0]);
        System.out.println(Arrays.toString(strArray1));

        String[] strArray2 = linkedSet3.toArray(new String[5]);
        System.out.println(Arrays.toString(strArray2));
    }
}

TreeSet< E > 구현 클래스

  • 입출력의 순서와 상관없이 객체의 '크기'순으로 비교
    • 오름차순으로 정렬함
    • HashSet< E >는 두 객체가 같은지, 다른지를 비교
  • NavogableSet, SortedSet -> 정렬과 탐색 기능이 추가
    • Set< String > treeSet = new TreeSet<>();
      • Set< E >메소드만 사용 가능
    • TreeSet< String > treeSet = new TreeSet<>();
      • Set< E >메소드와 정렬, 탐색 기능 사용 가능
  • TreeSet< E >의 주요 메소드

    • 매개변수에 boolean이 있다? SortedSet< E >리턴
    • 매개변수에 boolean이 없다? NavigableSet< E >리턴
    • 데이터 정렬 descendingSet()
      • 현재 정렬 기준을 반대로
public class TreeMethod_1 {
    public static void main(String[] args) {
        TreeSet<Integer> treeSet = new TreeSet<>();
        for (int i = 50; i > 0; i-=2) {
            treeSet.add(i);
        }
        System.out.println(treeSet.toString());

        System.out.println(treeSet.first());
        System.out.println(treeSet.last());

        System.out.println(treeSet.lower(26));
        System.out.println(treeSet.higher(26));

        System.out.println(treeSet.floor(25));
        System.out.println(treeSet.floor(26));

        System.out.println(treeSet.ceiling(25));
        System.out.println(treeSet.ceiling(26));
        System.out.println();

        int treeSetSize = treeSet.size();
        System.out.println(treeSetSize);
        for (int i = 0; i < treeSetSize; i++) {
            System.out.println(treeSet.pollFirst() + " ");
        }
        System.out.println();
        System.out.println(treeSet.size());

        for (int i = 50; i > 0; i-=2) {
            treeSet.add(i);
        }
        treeSetSize = treeSet.size();
        for (int i = 0; i < treeSetSize; i++) {
            System.out.println(treeSet.pollLast() + " ");
        }
        System.out.println();
        System.out.println(treeSet.size());

        for (int i = 50; i > 0; i-=2) {
            treeSet.add(i);
        }

        SortedSet<Integer> sSet = treeSet.headSet(20);
        System.out.println(sSet);

        NavigableSet<Integer> nSet = treeSet.headSet(20, true);
        System.out.println(nSet);
        nSet = treeSet.headSet(20, false);
        System.out.println(nSet);

        sSet = treeSet.tailSet(20);
        System.out.println(sSet);

        nSet = treeSet.tailSet(20, true);
        System.out.println(nSet);

        nSet = treeSet.tailSet(20, false);
        System.out.println(nSet);

        sSet = treeSet.subSet(10, 20);
        System.out.println(sSet);

        nSet = treeSet.subSet(10, true, 20, false);
        System.out.println(nSet);
        nSet = treeSet.subSet(10, false, 20, true);
        System.out.println(nSet);

        System.out.println(treeSet);
        NavigableSet<Integer> descendingSet = treeSet.descendingSet();
        System.out.println(descendingSet);
        descendingSet = descendingSet.descendingSet();
        System.out.println(descendingSet);
    }
}
  • TreeSet< E >에서 데이터 크기 비교
    • treeSet< E >에 저장되는 모든 객체 -> 크기 비교 기준이 필요
      • 해당 기준은 어떤 것이여도 상관 없음
      1. java.lang.Comparable< T >인터페이스 구현하기
      • int compareTo(T t) -> '자신의 객체'가 매개변수 t보다 작으면 음수, 같으면 0, 크면 양수 리턴(일반적으로 -1, 0, 1)
      1. TreeSet< E >생성하면서 생성자 매개변수로 Comparator< T >객체 제공
      • Comparator< T >인터페이스임 -> 내부 compare(T t1, T t2)로 크기 비교의 기준을 가지게 됨 -> 이 방법은 기존의 클래스 수정 ㄴㄴ
      • t1 < t2 -> 양수
      • t1 == t2 -> 0
      • t1 > t2 -> 음수
class MyClass {
    int data1;
    int data2;
    public MyClass(int data1, int data2) {
        this.data1 = data1;
        this.data2 = data2;
    }
}
class MyComparableClass implements Comparable<MyComparableClass> {
    int data1;
    int data2;
    public MyComparableClass(int data1, int data2) {
        this.data1 = data1;
        this.data2 = data2;
    }
    @Override
    public int compareTo(MyComparableClass o) {
        if (this.data1 < o.data1) return -1;
        else if (this.data1 == o.data1) return 0;
        else return 1;
    }
}

public class TreeSetMethod_2 {
    public static void main(String[] args) {
        TreeSet<Integer> treeSet1 = new TreeSet<>();
        Integer intValue1 = new Integer(20);
        Integer intValue2 = new Integer(10);
        treeSet1.add(intValue1);
        treeSet1.add(intValue2);
        System.out.println(treeSet1);

        TreeSet<String> treeSet2 = new TreeSet<>();
        String str1 = "가나";
        String str2 = "다라";
        treeSet2.add(str1);
        treeSet2.add(str2);
        System.out.println(treeSet2);

        /*
        TreeSet<MyClass> treeSet3 = new TreeSet<>();
        MyClass myClass1 = new MyClass(2, 5);
        MyClass myClass2 = new MyClass(3, 3);
        treeSet3.add(myClass1);
        treeSet3.add(myClass2);
        System.out.println(treeSet3);
        */

        //
        TreeSet<MyComparableClass> treeSet4 = new TreeSet<>();
        MyComparableClass myComparableClass1 = new MyComparableClass(2, 5);
        MyComparableClass myComparableClass2 = new MyComparableClass(3, 3);
        treeSet4.add(myComparableClass1);
        treeSet4.add(myComparableClass2);
        for (MyComparableClass mcc: treeSet4) {
            System.out.println(mcc.data1);
        }

        //
        TreeSet<MyClass> treeSet5 = new TreeSet<>(new Comparator<MyClass>() {
           @Override
           public int compare(MyClass o1, MyClass o2) {
               if (o1.data1 < o2.data1) return -1;
               else if (o1.data1 == o2.data1) return 0;
               else return 1;
           }
        });
        MyClass myClass1 = new MyClass(2, 5);
        MyClass myClass2 = new MyClass(3, 3);
        treeSet5.add(myClass1);
        treeSet5.add(myClass2);
        for (MyClass mcc: treeSet5) {
            System.out.println(mcc.data1);
        }
    }
}

참조

https://incheol-jung.gitbook.io/docs/q-and-a/java/collection
https://www.programiz.com/java-programming/treeset

profile
현재 블로그 : https://jasonsong97.tistory.com/

0개의 댓글