안녕하세요, 여행벌입니다.

오늘은 자바의 Wrapper 클래스에 대해서 알아보겠습니다.


Wrapper 클래스

 자바에는 다음과 같은 기본 자료형들이 있습니다. boolean, char, byte, short, int, long, float, double

 이런 기본 자료형들을 인스턴스로 표현해야하는 상황을 위해 Wrapper 클래스가 존재합니다.

 

 Wrapper 클래스는 이름 그대로 "기본자료형의 값을 감싸는 클래스"로 오로지 기본 자료형을 인스턴스로 표현하기 위해 존재합니다.

 모든 기본 자료형마다 Wrapper 클래스가 다음과 같이 존재하고, 기본 자료형을 Wrapper 클래스로 감싸는 행위를 Boxing, Wrapper 클래스에서 기본 자료형 값을 꺼내오는 행위를 UnBoxing 이라고 합니다.

1
2
3
        Integer int1 = new Integer(7); // 박싱
        Double double1 = new Double(4.5); // 박싱
        Boolean bool1 = new Boolean(false); // 박싱
cs

 위와 같이 인스턴스를 생성할 수 있습니다.

오토 박싱(Auto Boxing) & 오토 언박싱(Auto UnBoxing)

 자바 5부터 Wrapper 클래스와 기본 자료형 간의 박싱, 언박싱을 자동으로 지원해주기 시작했습니다.

 따라서, 다음과 같은 코드도 구현이 가능합니다.

1
2
3
4
5
6
7
8
public class test{
    public static void main(String args[]) {
        Integer a = 5// 오토박싱 Wrapper클래스를 생성해서 기본 자료형 5를 저장
        int b = a; // 오토 언박싱 Wrapper 클래스에서 기본 자료형 값 5를 꺼내옴.
        Double c = 5.0// 오토박싱
        double d = c; // 오토언박싱
    }
}
cs

Wrapper 클래스 사용예시

 Wrapper 클래스는 기본 자료형의 값을 인스턴스로 만들어주기 위해 등장했다고 했습니다. 그럼, 어떤 경우에 Wrapper 클래스를 활용할 수 있을까요??

 

 Object 를 입력으로 받는 메소드가 있다고 합시다. 이 메소드에 기본 자료형의 값을 매개변수로 전달 할 수 없습니다. 하지만, Wrapper 클래스를 활용해 인스턴스를 메소드의 매개변수로 전달하면 메소드를 기본 자료형에 대해서도 이용할 수 있습니다.

1
2
3
4
5
6
7
8
public class test{
    static void exMethod(Object obj) {
        System.out.println(obj);
    }
    public static void main(String args[]) {
        test.exMethod(new Integer(5));
    }
}
cs

 자바 5에서부터 오토박싱과 오토언박싱을 진행하므로 다음과 같이 인자를 전달해도 됩니다.

1
2
3
4
5
6
7
8
public class test{
    static void exMethod(Object obj) {
        System.out.println(obj);
    }
    public static void main(String args[]) {
        test.exMethod(5);
    }
}
cs

다음 포스팅에서는 Wrapper 클래스의 상위 클래스인 Number 클래스에 대해서 다뤄보겠습니다.

 

 

안녕하세요, 여행벌입니다.

오늘은 자바 Object 클래스의 clone 메소드에 대해서 정리해보겠습니다.


clone 메소드

 Object 클래스에는 인스턴스의 복사를 위한 clone 메소드가 정의되어 있습니다.

1
protected Object clone() throws CloneNotSupportedException
cs

 이 메소드가 호출되면, 호출된 메소드가 속한 인스턴스의 복사본이 생성되고, 이렇게 만들어진 복사본의 참조 값이 반환됩니다.

 단, Cloneable 인터페이스를 구현한 인스턴스를 대상으로만 위의 메소드를 호출할 수 있고, Cloneable 인터페이스를 구현하지 않은 클래스의 인스턴스를 대상으로 호출하면 CloneNotSupportedException 예외가 발생합니다.

 Cloneable 인터페이스는 인스턴스를 복사해도 된다는 '마커 인터페이스'로 정의해야 할 메소드가 따로 존재하지 않는 인터페이스입니다.

 

 저번 포스팅과 마찬가지로 좌표를 담는 클래스를 선언하고, clone 메소드를 이용해보겠습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
package Hello;
 
class Point implements Cloneable{
    private int xPos;
    private int yPos;
    public Point(int x, int y) {
        xPos = x;
        yPos = y;
    }
    @Override
    protected Object clone() throws CloneNotSupportedException{
        return super.clone();
    }
    @Override
    public boolean equals(Object obj) {
        if(((Point)obj).xPos == xPos && ((Point)obj).yPos == yPos) return true;
        return false;
    }
    
}
 
public class test{
    public static void main(String args[]) {
        Point p1 = new Point(57);
        try {
            Point p2 = (Point)p1.clone();
            if(p1 == p2) System.out.println("참조 대상이 같습니다");
            else System.out.println("참조 대상이 다릅니다");
            
            if(p1.equals(p2)) System.out.println("내용이 같습니다");
            else System.out.println("내용이 다릅니다");
        }
        catch(CloneNotSupportedException e) {
            e.printStackTrace();
        }
    }
}
cs

 clone 메소드를 이용하기 위해, Cloneable 인터페이스를 implements 한 것을 볼 수 있습니다.

 clone 메소드는 새로운 인스턴스를 생성하고 참조 값을 반환해주므로, "==" 연산 결과는 서로 다르지만, equals 결과는 같다는 것을 예상할 수 있습니다.

 

1
2
3
[Output]
참조 대상이 다릅니다
내용이 같습니다
cs

깊은 복사, 얕은 복사

 clone 메소드를 이용하면 대상 인스턴스의 내용을 복사해서 새로운 인스턴스를 만들 수 있습니다.

 그럼 다음과 같은 상황에서 clone 메소드는 어떻게 작용할까요?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
class Point implements Cloneable{
    private int xPos;
    private int yPos;
    public Point(int x, int y) {
        xPos = x;
        yPos = y;
    }
    public void showPoint() {
        System.out.println("좌표 : " + xPos + ' ' + yPos);
    }
    public void change(int x, int y) {
        xPos = x;
        yPos = y;
    }
    @Override
    protected Object clone() throws CloneNotSupportedException{
        return super.clone();
    }
    @Override
    public boolean equals(Object obj) {
        if(((Point)obj).xPos == xPos && ((Point)obj).yPos == yPos) return true;
        return false;
    }
    
}
 
class Line implements Cloneable{
    private Point p1;
    private Point p2;
    public Line(Point p1, Point p2) {
        this.p1 = p1;
        this.p2 = p2;
    }
    public void showPosition() {
        System.out.println("Line안에 있는 Point들의 좌표");
        System.out.println("p1의 좌표 ");
        p1.showPoint();
        System.out.println("p2의 좌표 ");
        p2.showPoint();
    }
    public void change(int x1, int y1, int x2, int y2) {
        p1.change(x1, y1);
        p2.change(x2, y2);
    }
    @Override
    protected Object clone() throws CloneNotSupportedException{
        return super.clone();
    }
    @Override
    public boolean equals(Object obj) {
        if(p1.equals(((Line)obj).p1) && p2.equals(((Line)obj).p2)) return true;
        return false;
    }
}
cs

 Point 인스턴스를 2개 가지고 있는, Line 클래스를 정의했습니다. equals 메소드는 Point 의 내용들이 모두 같으면 true, 다르면 false 를 반환하도록 Point 클래스의 equals 메소드를 이용했습니다. showPosition, change 메소드는 인스턴스 변수의 값을 출력해주고, 값을 바꿔주는 메소드입니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
public class test{
    public static void main(String args[]) {
        Line line1 = new Line(new Point(57), new Point(34));
        Line line2;
        try {
            line2 = (Line)line1.clone();
            line1.change(1234);
            line1.showPosition();
            line2.showPosition();
        }
        catch(CloneNotSupportedException e) {}
    }
}
cs

 line1 인스턴스를 생성하고, line2는 line1 인스턴스를 clone 했습니다. 그 후, line1의 인스턴스 변수들의 값을 변경한 후, line1과 line2의 인스턴스 변수 값들을 출력해보았습니다.

1
2
3
4
5
6
7
8
9
10
11
12
[Output]
Line안에 있는 Point들의 좌표
p1의 좌표 
좌표 : 1 2
p2의 좌표 
좌표 : 3 4
 
Line안에 있는 Point들의 좌표
p1의 좌표 
좌표 : 1 2
p2의 좌표 
좌표 : 3 4
cs

 line1과 line2의 인스턴스 변수 값들이 동시에 변경된 것을 볼 수 있습니다.

 우리가 원한 clone은 line 클래스 안에 있는 Point 인스턴스 변수 p1, p2 도 새롭게 인스턴스를 생성해서 위의 그림과 같이 각각 다른 Point 인스턴스를 참조하는 "깊은 복사" 를 원했습니다.

 하지만, 결과를 보면 아래 그림과 같이 "얕은 복사"가 된 것을 확인 할 수 있습니다.

 깊은 복사를 하기 위해서는 다음과 같이 clone 메소드를 수정해야 됩니다.

	@Override
	protected Object clone() throws CloneNotSupportedException{
		Line copy = (Line)super.clone(); // Line을 먼저 copy
		copy.p1 = (Point)p1.clone();
		copy.p2 = (Point)p2.clone();
		return copy;
	}

 Line 클래스의 인스턴스 변수인 p1, p2도 각각 다시 clone 메소드를 이용해 복사한다면, 우리가 원하는 깊은 복사를 할 수 있습니다. 결과는 다음과 같습니다.

1
2
3
4
5
6
7
8
9
10
11
12
[Output]
Line안에 있는 Point들의 좌표
p1의 좌표 
좌표 : 1 2
p2의 좌표 
좌표 : 3 4
 
Line안에 있는 Point들의 좌표
p1의 좌표 
좌표 : 5 7
p2의 좌표 
좌표 : 3 4
cs

 깊은 복사가 잘 이루어져서, line1과 line2가 각각 완전히 다른 인스턴스들을 참조하고 있는 것을 확인할 수 있습니다.

배열의 clone 메소드 호출

 배열도 인스턴스이므로, clone 메소드의 호출이 가능하도록 오버라이딩 되어있습니다. 하지만, 배열은 깊은 복사가 진행되도록 오버라이딩 되어 있지는 않습니다. 따라서 배열이 지니는 참조 값의 복사만 이루어질 뿐 해당 참조 값의 인스턴스까지는 복사되지 않습니다.

 즉, 배열은 얕은 복사만 가능합니다.


 

안녕하세요, 여행벌입니다.

오늘은 자바 최상위 클래스 Object 클래스의 equals 메소드에 대해서 알아보겠습니다.


equals 메소드

 Object 클래스에는 다음과 같이 equals 메소드가 정의되어있습니다.

public boolean equals(Object obj)

 메소드 이름 그대로 같은지 다른지 판단해서 boolean 값을 return 해주는 메소드로, 오버라이딩을 통해 원하는 내용 비교를 할 수 있도록 최상위 클래스인 Object 클래스에 정의되어 있습니다.

 

 우리가 비교를 생각하면 "==" 연산이 가장 먼저 떠오릅니다. 하지만, "==" 연산은 참조대상이 같은지, 다른지 판단해주는 연산으로 우리가 원하는 내용 비교와는 조금 다릅니다.

 같은 문자열을 담고 있는 2개의 String 인스턴스를 생성해 보도록 하겠습니다.

public class test{
	public static void main(String args[]) {
		String name1 = new String("travelbeeee");
		String name2 = new String("travelbeeee");
		if(name1 == name2) System.out.println("둘이 같습니다");
		else System.out.println("둘이 다릅니다");
	}
}

 name1, name2 인스턴스에는 분명히 같은 문자열이 저장되어있지만, 각각 인스턴스를 생성했기 때문에 서로 다른 인스턴스를 참조하고 있습니다. 따라서, "==" 연산의 결과는 둘이 다르다고 판단합니다.

[Output]
둘이 다릅니다

 그럼 내용 비교를 하고 싶으면 어떻게 해야 될까요? 그럴 때 equals 메소드를 오버라이딩해서 사용하면 됩니다. String 클래스에서는 이미 equals 메소드를 오버라이딩해서 지원해주고 있기 때문에, 다음과 같이 사용하면 됩니다.

public class test{
	public static void main(String args[]) {
		String name1 = new String("travelbeeee");
		String name2 = new String("travelbeeee");
		if(name1.equals(name2)) System.out.println("둘이 내용이 같습니다");
		else System.out.println("둘이 내용이 다릅니다");
	}
}
[Output]
둘이 내용이 같습니다

 x좌표와 y좌표를 담고 있는 클래스를 만든 후, equals 메소드를 좌표가 같은지 판단해주도록 오버라이딩 해보겠습니다.

class Point{
	private int xPos;
	private int yPos;
	public Point(int x, int y) {
		xPos = x;
		yPos = y;
	}
	@Override
	public boolean equals(Object obj) {
		if(((Point)obj).xPos == xPos && ((Point)obj).yPos == yPos) return true;
		return false;
	}
}

public class test{
	public static void main(String args[]) {
		Point p1 = new Point(5, 7);
		Point p2 = new Point(5, 7);
		if(p1 == p2)
			System.out.println("참조대상이 같다");
		else
			System.out.println("참조대상이 다르다");
		
		if(p1.equals(p2))
			System.out.println("내용이 같다");
		else
			System.out.println("내용이 다르다");
	}
}
[Output]
참조대상이 다르다
내용이 같다

 "==" 연산 결과는 서로 다른 인스턴스를 참조하고 있다고 알려주고 있지만, equals 메소드를 둘의 좌표를 비교하도록 오버라이딩 했기 때문에 내용이 같다고 비교해주고 있는 것을 볼 수 있습니다.

 

 이렇듯 두 인스턴스의 내용 비교 결과인 true, false 의 반환 조건은 해당 클래스를 정의하는 프로그래머가 결정해야 합니다. 기본적으로 Object 클래스의 equals 메소드는 "==" 연산자와 마찬가지로 참조변수의 참조 값을 비교하도록 정의되어 있습니다. 하지만, "==" 연산이 참조변수의 참조 값을 비교하는 기능을 지원해주므로 String 클래스 등과 같이 자바에서 제공하는 표준 클래스의 경우 equals 메소드가 내용 비교를 하도록 이미 오버라이딩 되어 있는 경우가 많습니다.


 

문제 : https://www.acmicpc.net/problem/15591


Graph BFS

 a 동영상과 b 동영상의 relevant 값은 a에서 b로 가는 경로 중 간선의 값이 가장 작은 값이 됩니다. 우리는 k와 v가 주어질 때마다, v번 동영상에서 다른 모든 동영상까지 relevant 값이 k 이상인 동영상들을 count 해줘야합니다. 따라서, a 동영상에서 시작해서 BFS 탐색을 진행하면서 다른 모든 동영상과의 relevant 값을 구하면 되는데, a에서 어떤 동영상까지 탐색하는데 relevant 값이 주어진 k보다 작아진다면, 그 뒤의 탐색들도 다 k보다 relevant 값이 작아지므로 탐색을 마저 진행하지 않는다.

#include<iostream>
#include<vector>
#include<algorithm>
#include<queue>

using namespace std;

int N, Q, p, q, r, k, v;
vector<pair<int, int>> map[5001];

int BFS(int k, int v) {
	int ans = 0;
	vector<bool> visited(5001, false);
	queue<int> q;
	q.push(v);
	visited[v] = 1;
	while (!q.empty()) {
		int cur = q.front();
		q.pop();
		for (int i = 0; i < map[cur].size(); i++) {
			int next = map[cur][i].first;
			int relev = map[cur][i].second;
			if (!visited[next] && k <= relev) {
				visited[next] = 1;
				q.push(next);
				ans++;
			}
		}
	}
	return ans;
}

int main(void) {
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);

	cin >> N >> Q;
	for (int i = 0; i < N - 1; i++) {
		cin >> p >> q >> r;
		map[p].push_back({ q, r });
		map[q].push_back({ p, r });
	}
	for (int i = 0; i < Q; i++) {
		cin >> k >> v;
		cout << BFS(k, v) << '\n';
	}

	return 0;
}

 

 

'Problem Solving > BOJ' 카테고리의 다른 글

[BOJ] 15681 : 트리와 쿼리  (0) 2020.07.01
[BOJ] 18235 : 지금 만나러 갑니다  (0) 2020.06.25
[BOJ] 10021 : Watering the Fields  (0) 2020.06.22
[BOJ] 2352 : 반도체 설계  (0) 2020.06.15
[BOJ] 1162 : Revamping Trails  (0) 2020.06.09

문제 : https://www.acmicpc.net/problem/10021


MST Graph Prim

 N개의 노드가 존재하고, a 노드에서 b 노드로 가는 간선이 존재하려면, 거리가 C 이상이여야 됩니다. 우리는 모든 노드들이 서로 연결되어있는 상태를 최소 비용으로 만들고 싶으므로, MST를 만들면 됩니다.

 MST 알고리즘은 대표적으로 Prim, Kruskal 2개가 있는데 이 문제는 간선이 굉장히 많이 존재할 수 있는 밀집 그래프 문제이므로 Kruskal로 MST를 구하면 시간초과를 직면하게 되고, Prim 알고리즘을 사용해야된다.

 

1) 모든 노드를 순회하며 거리를 계산하고, 거리가 C 이상이면 graph 에 간선을 만들어준다.

( 간선은 양방향 )

2) Prim 알고리즘을 통해 MST를 구한다.

#include<iostream>
#include<queue>
#include<cmath>
using namespace std;

const int INF = 1000000001;

int N, C;
int graph[2000][2000];
pair<int, int> point[2000];
int dist[2000];
bool selected[2000];

int getDist(int i, int j) {
	return (int)pow(point[i].first - point[j].first, 2) + (int)pow(point[i].second - point[j].second, 2);
}

int get_min_vertex() {
	int v;
	for (int i = 0; i < N; i++) {
		if (!selected[i]) {
			v = i;
			break;
		}
	}
	for (int i = 0; i < N; i++)
		if (!selected[i] && (dist[i] < dist[v])) v = i;
	return v;
}

int prim(int s) {
	for (int i = 0; i < N; i++)
		dist[i] = INF;
	dist[s] = 0;
	for (int i = 0; i < N; i++) {
		int u = get_min_vertex();
		selected[u] = 1;
		if (dist[u] == INF) return -1;
		for (int j = 0; j < N; j++)
			if (graph[u][j] != INF && !selected[j] && graph[u][j] < dist[j])
				dist[j] = graph[u][j];
	}
    int ans = 0;
    for(int i = 0; i < N; i++) ans += dist[i];
    return ans;
}
int main(void) {
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);

	// i <--> j 의 모든 edge를 구하고, MST를 구하면 된다.
	cin >> N >> C;
	for (int i = 0; i < N; i++)
		cin >> point[i].first >> point[i].second;

	// 프림으로 해보자.
	for (int i = 0; i < N; i++)
		for (int j = i + 1; j < N; j++) {
			int dist = getDist(i, j);
			if (C <= dist) graph[i][j] = graph[j][i] = dist;
			else graph[i][j] = graph[j][i] = INF;
		}
    cout << prim(0) << '\n';
    
	return 0;
	
}

 

'Problem Solving > BOJ' 카테고리의 다른 글

[BOJ] 18235 : 지금 만나러 갑니다  (0) 2020.06.25
[BOJ]15591 : MooTube(Silver)  (0) 2020.06.22
[BOJ] 2352 : 반도체 설계  (0) 2020.06.15
[BOJ] 1162 : Revamping Trails  (0) 2020.06.09
[BOJ] 1086 : 박성원  (0) 2020.06.02

+ Recent posts