본문 바로가기

Java/문법

17 -3 장 Set<E> 컬렉션 인터페이스 (1)

1. Set<E> 컬렉션의 특징 

 

- 인덱스 정보를 포함하지 않은 동일한 타입의 묶음

- 집합의 개념과 같은 컬렉션

- Set<E>는 데이터를 구분할 수 있는 유일한 방법이 데이터 그 자체

- 동일한 데이터의 중복 저장을 허용하지 않음

- 데이터 중복 허용의 기준은 특정 데이터를 꺼낼 수 있느냐에 따라 달려있음

 - List<E>

  -> 중복된 데이터는 서로 다른 인덱스에 저장돼 있기 때문에 정확히 구분 가능

 -Set<E>

 -> 데이터가 중복됐을 때 aSet.get("다")가 가리키는 정확한 데이터를 알 수 없음

 

2. Set<E>의 주요 메서드

 

-contains(Object o)

 -> 해당 Set<E> 매개변수로 넘어온 뎅이터가 객체 내의 포함돼 있는지를 불리언값으로 리턴

 

-iterator()

 ->Iterator<E>객체 리턴

 ->Set<E> 객체에서 데이터를 1개씩 꺼내는 기능 포함

 ->Set<E>은 인덱스가 없어 for문으로 데이터 못꺼냄

 

-Set<E>도 인터페이스 이므로 자바 컬렉션 프레임워크에서 제공하는 Set<E> 인터페이스를 상속한 구현 클래스 이용

 (HashSet<E>, LinkedHashSet<E>, TreeSet<E>)

구분 메서드명 기능
데이터 추가 add(E element) 매개변수로 입력된 원소를 리스트에 추가
addAll(Collectiton<? Extends E> c) 매개변수로 입력된 컬렉션 전체를 추가
데이터 삭제 remove(Object o) 원소 중 매개변수 입력과 동일한 객체 삭제
clear() 전체 원소 삭제
데이터 정보 추출 isEmpty() Set<E> 객체가 비어 있는지 여부를 리턴
contains(Object o) 매개변수로 입력된 원소가 있는지 여부를 리턴
size() 리스트 객체 내의 포함된 원소의 개수
iterator() Set<E> 객체 내의 데이터를 연속해 꺼내는 iterator 객체 리턴
Set<E> 객체 배열 변환 toArray() 리스트를 Object 배열로 변환
toArray(T[] t) 입력매개변수로 전달한 타입의 배열로 변환

 

1) 데이터 추가하기 - add()

 

- 중복된 데이터는 추가 x

 - HashSet<E>은 모든 데이터를 하나의 주머니에 넣어 관리하므로 입력순서와 다르게 출력될 수도 있다.

Set<String> hSet1 = new HashSet<String>();
//1.add(E element)
hSet1.add("가");
hSet1.add("나");
hSet1.add("가");
System.out.println(hSet1.toString()); //[가,나]

//2.addAll(Collection<? extends E> c)
Set<String> hSet2 = new HashSet<String>();
hSet2.add("나");
hSet2.add("다");
hSet2.addAll(hSet1);
System.out.println(hSet2.toString()); //[가,다,나] //입력 순서와 다름

 

2) 데이터 삭제하기 -remove(), clear()

 

 - Set<E>객체에는 인덱스 번호가 없으므로 데이터를 삭제하려면 remove() 메서드의 매개변수로 실제 삭제할 원솟값을 넣어야 함

//3.remove(Object o) hset2=[가,다,나]
hSet2.remove("나");
System.out.println(hset2.toString()); //[가,다]

//4.clear()
hset2.clear();
System.out.println(hset2.toString()); //[]

 

3) 데이터 정보 추출하기 -isEmpty(), contains(), iterator()

 

-isEmtpy()

 -> 데이터가 비어 있는지의 여부 

 

-contians(Object o)

 ->HashSet<E> 객체 안에 해당 원소가 있는지를 true/false로 리턴

 

-size()

 -> 저장된 데이터의 개수를 정수형으로 리턴

 

- Iterator()

 -> Set<E> 객체 내부의 데이터를 1개씩 꺼내 처리하고자 할때 사용

 

 -> iterator() 메서드 호출시 

  - 제네릭 클래스 타입인 Iterator<E>의 객체 생성

  (Iterator<String>iterator =hSet3.iterator();)

 

  - hasNext()메서드는 다음으로 가리킬 원소의 존재 여부를 불리언으로 리턴 

    hasNext() = false이면 마지막 데이터까지 읽은것

    hasNext() = true 이면 아직 읽을 데이터가 남아 있는것

 

  - next() 메서드는 다음 원소 위치로 가서 읽은 값 리턴

    cf> 최초 iterator<E>객체 생성시 객체가 가리키고 있는 위치는 첫 원소 바로 이전의 위칫값

          첫 번째 원솟값을 읽으려면 iterator.next() 실행

 

while(iterator.hasNext()) {
System.out.println(iterator.next() +" "); } -> "아직 읽을 데이터가 남아있으면 다음 원소로 위치로 가서 읽은값을 리턴해라"

 

//5.isEmpty() hset2=[]
System.out.println(hset2.isEmpty()); //true

//6.contains(Object o)
Set<String>hSet3 = new HashSet<String>();
hSet3.add("가");
hSet3.add("다");
hSet3.add("나");
System.out.println(hSet3.contains("나"); //true
System.out.println(hSet3.contains("라"); //false

//7.size()
System.out.println(hset3.size()); //3

//8.iterator()
Iterator<String>iterator =hSet3.iterator();
while(iterator.hasNext()) {
	System.out.println(iterator.next() +" "); //가 나 다
}
System.out.println();

 

-for-each 구문으로 데이터 정보 추출

//8.for-each
for(String s :hSet3) {
	System.out.println(s+""); //가 다 나
}
System.out.println();

 

4) 배열로 변환하기 -toArray(), toArray(T[] t)

//9.toArray() hSet3 =[가 다 나]
Object() objArray = hSet3.toArray();
System.out.println(Arrays.toString(objArray)); //[가 다 나]

//10-1 toArray(T[] t) 배열 개수가 더 작을때
String[] strArray1 = hSet3.toArray(new String[0]);
System.out.println(Arrays.toString(strArray1)); //[가 다 나]

//10-2 toArray(T[] t) 배열 개수가 더 클때
String[] strArray2 = hSet3.toArray(new String[5]);
System.out.println(Arrays.toString(strArray2)); //[가 다 나 null null]

 

5) HashSet<E>의 중복 확인 메커니즘

 

-A a1 = new A(3) 과 A a2 = new A(3)

 -> 3의 데이터를 넣으면 내부적으로는 new Integer(3)과 같이 객체로 변환돼 저장

 -> 생성자에 동일한 값을 넘겨 객체를 생성했으므로 두 객체는 완벽히 똑같이 생김

 -> But, 클래스 A에서 equals() , hashCode() 오버라이딩을 하지 않았으면 HashSet<E> 관점에선는 다른 객체

 

-String s1 = new String("안녕")과 String s2 =new String("안녕")

 -> HashSet<E>의 관점에서 동일 객체 

 

객체의 해시코드(HashCode)

 - 객체가 저장된 번지와 같은 값

 - 객체가 저장된 번지를 기준으로 생성된 정수형 고윳값

 

 - hasCode() 메서드 

  -> object 클래스 정의 메서드 

  -> 객체의 해시코드값 리턴

 

 - 오버라이딩하지 않았을 때 객체의 hashcode(), toString()를 이용한 해시코드값 출력

class A extends Object{}
public class Test{
	public static void main(String[] args) {
    	A a = new A();
        System.out.printf("%x", a.hashCode()); //7852e922
        System.out.println(a.toString()); //패키지명.클래스명.@해시코드
    }
}

-Objects.hash() 메서드를 이용한 해시코드 생성

 -> 매개변수로 들어오는 값과 순서까지 고려해 고유한 해시코드 생성

System.out.println(Objects.hash(1,2,3)); //30817
System.out.println(Objects.hash(2,1,3)); //31747
System.out.println(Objects.hash("안녕")); //1611052
System.out.println(Objects.hash("방가")); //1537302

 

등가연산자(==)

 - 스택 메모리값을 동등 비교 

 - 기본 자료형(int..)의 등가연산 -> 실제값 비교

 - 참조 자료형(String..)의 등가연산 -> 위칫값 비교 

 

equals() 메서드 

 - Obejct 클래스의 메서드 

 - 등가연산과 완벽하게 동일 

 - 자신의 객체와 매개변수로 넘어온 객체의 등가연산을 수행한 결과를 리턴

public boolean equals(Object obj) {
	return (this ==obj);
}

 

-equals() 메서드를 오버라이딩하지 않았을 때 등가 연산과 equlas() 연산 결과 비교 

 ->등가 연산과 equals() 메서드의 결과는 동일한 값

class A{
	int data;
    public A(int data) {
    	this.data =data;
     }
}

public class Test {
	public static void main(String[] args) {
    	A a1 = new A(3);
        A a2 = new A(3);
        System.out.println(a1 == a2); //false
        System.out.println(a1.equals(a2)); //false
    }
}

 

HashSet<E>에서의 중복 확인 메커니즘

 

1단계 hashCode()값이 동일한지 확인

2단계 equals() 결과가 true인지 확인 

 

- 해시코드가 동일하고 equals()메서드가 true이면 두 객체는 동일한 객체로 인지(중복된 값으로 인식)

 

- 두 메서드 모두 오버라이딩 하지 않았을 때

 -> 2개의 객체를 동일한 생성자로 생성했기 떄문에 객체의 모양은 동일하지만, 각각 다른 위치에 저장

 -> HashSet<E>의 관점에서는 완벽히 다른 객체 

class A {
	int data;
    public A(int data) {
    	this.data=data;
    }
}

public class Test{
	public static void main(String[] args) {
    	//equals():오버라이딩 x + hashCode(): 오버라이딩 x
        Set<E> 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()); // 2018699554, 1311053135
        
        hashSet1.add(a1);
        hashSet1.add(a2);
        System.out.println(hashSet1.size()); //2(다른객체)
       }
 }

 

- equals() 메서드만 오버라이딩 했을때

 -> 오버라이딩한 equals() 메서드는 두 객체의 필드값 비교하므로 true 리턴

 -> 해시코드값은 두 객체가 다르므로 서로 다른값 가짐

 ->  HashSet<E>의 관점에서는 완벽히 다른 객체 

 

class B{
	int data;
    public B(int data) {
    	this.data =data;
    }
    //override
    public boolean equals(Obeject obj) {
    	if(obj instance of B){
        	if(this.data == ((B)obj).data)
            	return true;
         }
        return false;
    }
}

public class Test{
	public static void main(String[] args) {
    	//equals() :오버라이딩o +hascode() : 오버라이딩 x
        Set<B>hashSet2 = new HashSet<>();
        B b1 = new B(3);
        B b2 = new B(3);
        System.out.println(b1 == b2); //false
        Sysetm.out.println(b1.equals(b2)); //true
        System.out.println(b1.hashCode() +" "+b2.hashCode()); //118352462, 1550089733
        hashSet2.add(b1);
        hashSet2.add(b2);
        System.out.println(hashSet2.size()); //2(다른 객체)
     }
}

 

-equals(), hashCode() 메서드 모두 오버라이딩했을 떄

 -> equals()는 필드값이 동일할 때 true 리턴하도록 오버라이딩

 -> hashCode()는 Objects 클래스의 정적 메서드인 hash() 메서드를 사용해 필드값 기준으로 해시코드 생성

 -> return (new Integer(data)).hashCode() => return data로 간단히 쓰기 가능

 

class C{
	int data;
    public C(int data) {
    	this.data =data;
     }
     //override
     public boolean equals(Object obj) {
     	if(obj instanceof C) {
        	if(this.data == ((c)obj.data)
            	return ture;
        }
        return false;
     }
     
     //override
     public int hashCode() {
     	return Objects.hashcode(data); //data
     }
}

public class Test {	
	public staic void main(String[] args) {
   		//equals() :오버라이딩 o, hashCode(): 오버라이딩 O
        Set<C>hashSet3 = new HashSet<>();
        C c1 = new C(3);
        C c2 = new C(3);
        System.out.println(c1 == c2); //false
        System.out.pritnln(c1.equals(c2)); //true
        System.out.println(c1.hashCode()+ " " +c2.hashCode()); //34 34
        hashSet3.add(c1);
        hashSet3.add(c2);
        System.out.println(hashSet3.size()); //1(같은객체)
    }
}