Set<E> 인터페이스를 구현하는 HashSet<E> 클래스
HashSet<E> 클래스 특징
- 저장 순서가 유지되지 않는다.
- 데이터의 중복 저장을 허용하지 않는다.
public class TestMain {
public static void main(String[] args) {
HashSet<Integer> set = new HashSet<>();
set.add(10); set.add(20);
set.add(30); set.add(10);
for (Integer e : set)
System.out.print(e + " ");
System.out.println();
}
}
출력 결과 : 20 10 30
정의되어있는 대부분의 클래스들은 Object 클래스의 hashcode와 equals 메소드가 적절하게 오버라이딩 되어있다.
따라서 중복을 제거하고 출력을 해준다.
그러나 아래와 같이 사용자 정의가 된 클래스는 출력 결과를 보면 중복 된 값을 허용한다.
class Point {
int x,y;
public Point(int _x, int _y) {
x = _x;
y = _y;
}
@Override
public String toString() {
return "x = " + x + " "+"y = " +y;
}
}
public class TestMain {
public static void main(String[] args) {
HashSet<Point> set = new HashSet<>();
set.add(new Point(10,20)); set.add(new Point(10,30));
set.add(new Point(20,20)); set.add(new Point(10,30));
for (Point e : set)
System.out.println(e);
System.out.println();
}
}
--출력 결과--
x = 10 y = 30
x = 10 y = 30
x = 20 y = 20
x = 10 y = 20
따라서, 아래와 같이 toString 메소드와 equals 메소드를 다음과 같이 적절하게 오버라이딩 한다.
class Point {
int x,y;
public Point(int _x, int _y) {
x = _x;
y = _y;
}
@Override
public String toString() {
return "x = " + x + " "+"y = " +y;
}
@Override
public int hashCode() {
return (x+y)%3;
}
@Override
public boolean equals(Object obj) {
return x==((Point)obj).x && y==((Point)obj).y;
}
}
public class TestMain {
public static void main(String[] args) {
HashSet<Point> set = new HashSet<>();
set.add(new Point(10,20)); set.add(new Point(10,30));
set.add(new Point(20,20)); set.add(new Point(10,30));
for (Point e : set)
System.out.println(e);
System.out.println();
}
}
여기서 hashCode() 메소드는 분류를 나누는 작업을 한다. 여기서 정의한 해쉬 알고리즘 (x+y)%3 이 되며 이 결과 값은 세 가지인 0,1,2 로 분류된다. 즉, f(x,y)=(x+y)%3 이 되고 x,y로 어떤 값이 들어오든 0,1,2 세 가지중 하나에 속하게 된다.
이렇게 분류를 마치면 equals() 메소드를 통해 값을 비교하는데 기본적으로 Object 클래스의 equals() 메소드는 참조 변수의 참조 값을 하기 때문에 적절히 값을 비교하게 위와 같이 오버라이딩 하면 된다.
Set<E> 인터페이스를 구현하는 TreeSet<E> 클래스
TreeSet<E> 클래스 특징
- 정렬된 상태를 유지하면서 저장된다.
- 데이터의 중복 저장을 허용하지 않는다.
public class TestMain {
public static void main(String[] args) {
TreeSet<Integer> set = new TreeSet<>();
set.add(4); set.add(3);
set.add(3); set.add(1);
set.add(5);
for (Integer e : set)
System.out.print(e+" ");
System.out.println();
}
}
--출력 결과--
1 3 4 5
정렬은 오름차순을 기준으로 하며 compareTo 메소드를 통해 비교를 한다. compareTo 메소드는 Comparable 인터페이스의 추상 메소드 이므로 Comparable을 구현해 줘야 한다. 구현이 되면 TreeSet 클래스의 add 메소드는 구현된 compareTo 메소드를 통해 정렬을 통해 인스턴스를 저장하게 된다.
class Point implements Comparable<Point>{
int x,y;
public Point(int _x, int _y) {
x = _x;
y = _y;
}
@Override
public String toString() {
return "x = " + x + " "+"y = " +y;
}
@Override
public int compareTo(Point o) {
if(x==o.x&& y==o.y)
return 0;
else if((x==o.x && y>o.y) || x>o.x)
return 1;
else
return -1;
}
}
public class TestMain {
public static void main(String[] args) {
TreeSet<Point> set = new TreeSet<>();
set.add(new Point(10,20)); set.add(new Point(10,30));
set.add(new Point(20,20)); set.add(new Point(10,30));
for (Point e : set)
System.out.println(e);
System.out.println();
}
}
--출력 결과--
x = 10 y = 20
x = 10 y = 30
x = 20 y = 20
오버라이딩한 compareTo 메소드를 보면 인자로 전달된 객체의 값의 비교를 통해 같으면 0 작으면 -1 크면 1을 반환하게 했다. 기본적으로 compareTo는 전달된 인자의 값이 작으면 양의 정수를 반환하게 되어있는데 이는 오름차순을 하겠다는 의미와 같다. 위에서는 x를 기준으로 오름차순 정렬 후 y를 기준으로 오름차순으로 정렬하고 있다.
여기서 내림차순으로 정렬하고 싶다면?
compareTo 메소드를 수정하면 되지만 String 클래스 또는 Wrapper 클래스 같은 경우는 수정 할 수가 없다.
이때 사용하는 인터페이스가 Comparator 인터페이스 이다. Comparator 인터페이스는 compare 메소드를 가지고 있으며 이 인터페이스를 구현한 클래스의 인스턴스는 public TreeSet(Comparator<? super E> comparator) 생성자를 통해 전달할 수 있다.
class Point implements Comparable<Point>{
int x,y;
public Point(int _x, int _y) {
x = _x;
y = _y;
}
@Override
public String toString() {
return "x = " + x + " "+"y = " +y;
}
@Override
public int compareTo(Point o) {
if(x==o.x&& y==o.y)
return 0;
else if((x==o.x && y>o.y) || x>o.x)
return 1;
else
return -1;
}
}
class DescPoint implements Comparator<Point>{
@Override
public int compare(Point o1, Point o2) {
return o2.compareTo(o1); // o1,o2의 위치를 바꾼다.
}
}
public class TestMain {
public static void main(String[] args) {
TreeSet<Point> set = new TreeSet<>(new DescPoint());
set.add(new Point(10,20)); set.add(new Point(10,30));
set.add(new Point(20,20)); set.add(new Point(10,30));
for (Point e : set)
System.out.println(e);
System.out.println();
}
}
--출력 결과--
x = 20 y = 20
x = 10 y = 30
x = 10 y = 20
위 코드를 보면 compareTo 메소드의 수정 없이 Comparator 인터페이스를 구현한 클래스를 정의해 TreeSet의 생성자로 전달하고 있다. 그리고 이렇게 Comparator<? super E> comparator 생성자를 통해 생성된 인스턴스는 compare 메소드를 기준으로 정렬하게 된다.
참고
TreeSet 클래스는 트리라는 자료구조를 기반으로 인스턴스를 저장하기 때문에 hasCode를 구현할 필요가 없다.
'Programming > JAVA' 카테고리의 다른 글
Lambda Expression (0) | 2020.12.10 |
---|---|
Nested Class, Inner Class (0) | 2020.12.08 |
Upper Bounded Wildcards (0) | 2020.11.29 |
댓글