다차원 배열 복사는 어떻게? in Java

조성현의 이미지

다차원 배열 두개가 있습니다.
이것을 서로 복사하려고 하는데요.
그냥 for 문 돌리면 쉽겠지만, 좀더 축약적으로 하는 법을 알고 싶어서입니다.
javaland.co.kr 이나 기타 자바 관련 사이트에서 찾아보았지만 몇개 메소드밖에 찾지 못했습니다.

        private int[][] input = {       {1, 2, 3},
                                        {4, 5, 6},
                                        {7, 8, 0}
                                };
        private int[][] data = new int[input.length][input[0].length];

        System.arraycopy(input, 0, this.data, 0, this.data.length);

위와 같이 System.arraycopy를 쓰면 되지만, 위와 같이 하면, data 배열을 조작한 후 input 배열도 조작되어 버립니다.

조작후 결과 wrote:
input_before:
1 4 3
7 0 6
5 8 2
data:
0 1 3
7 4 6
5 8 2
input_after:
0 1 3
7 4 6
5 8 2

그냥 System.arraycopy(input 에서 input 대신에 어떤 로컬 변수로 주면 쉽게 되지만, 왜 arraycopy에서 동시에 변화가 되는지 모르겠습니다.

이유 아시는 분은 답변 바랍니다.

관련 페이지: [url=http://java.sun.com/j2se/1.4.2/docs/api/java/lang/System.html#arraycopy(java.lang.Object,%20int,%20java.lang.Object,%20int,%20int)]메뉴얼에서의 함수 위치입니다. [/url]

xyhan의 이미지

순간 포인터로 하지라는 엉뚱한 생각이..

다차원 배열 복사해야 하는 이유를 알고 싶네요...

객체 복사는 clone() 이라는 게 있는데..

간단 한 예입미다..
Vector v= new Vector();
v.addElement(new Integer(3));
Vector s=v.clone()

int[][] aa={{0,1,2},{3,4,5}};
int[][] bb=aa.clone();
처럼 쓰면 안되더 군요... :oops:
배열은 클론이 안되네요..
for()문 사용하세요..

============================================================

선한 인간이냐 악한 인간이냐는 그사람의 의지에 달렸다. -에픽테토스-
의지 노력 기다림은 성공의 주춧돌이다. -파스퇴르-

============================================================

조성현의 이미지

혹시 같은 주소로 지정되는 건 아닌지요?

제 질문을 보시면 알겠지만, 저는 따로 관리는 하고 싶어서입니다.

즉 input과 data 배열을 조작하는데 중복이 생겨나지 않게 하는 것이 목적이라는 거죠.

제가 사용한 System.arraycopy는 다시 말씀드리지만, 동시에 조작이 되어버립니다(아마도 포인터로 같은 주소로 되어 있지 않을까 생각해봅니다).

int[][] data = input;

위와 같이 한 것과 같은 결과가 되어버리죠.

clone도 찾는 중에 알게 되었는데, 사용하기가 힘들더라구요.

다차원 배열을 지원하는지도 모르겠고요. 관련 레퍼런스도 봤지만.. 저로써는 사용하기 힘들더군요. Object객체로 되어야하나 생각도 들고요.

urstory의 이미지

2차원 배열을 복사할려면 어떻게 하여야 하나?

      private int[][] input = {       {1, 2, 3}, 
                                        {4, 5, 6}, 
                                        {7, 8, 0} 
                                }; 
        private int[][] data = new int[input.length][input[0].length]; 

        System.arraycopy(input, 0, this.data, 0, this.data.length);

위의 코드를 보면 System이 가지고 있는 arraycopy를 이용하여 복사를 할 수 있지만,
data 2차원 배열의 값을 수정하게 되면, input 2차원 배열의 값도 같이 수정이 됩니다.

자바의 2차원 배열은 사실 존재하지 않습니다.
실제로는 1차원 배열이 1차원 배열을 레퍼런스(가리키는)할뿐이지요.

Quote:

input
+---+ +---+---+---+
| 0 | ----> | 1 | 2 | 3 |
+---+ +---+---+---+
| 1 | ----> | 4 | 5 | 6 |
+---+ +---+---+---+
| 2 | ----> | 7 | 8 | 9 |
+---+ +---+---+---+

위와 같은 input 배열이 복사될 때, 실제 데이터를 가리키는 앞의 1차원 배열의 내용만 복사가
되기때문에 그런현상이 벌어지게 됩니다.

2차원 배열을 복사하기 위하여는 다음과 같이 for문장을
이용할 수 있습니다.

public class  ArrayCopy
{
	public static void main(String[] args) 
	{
		int[][] input = {       {1, 2, 3}, 
                                {4, 5, 6}, 
                                {7, 8, 0} 
                         }; 
        int[][] data = new int[input.length][input[0].length];

		for(int i = 0; i < input.length; i++){
	        System.arraycopy(input[i], 0, data[i], 0, input[i].length);
		}

		// data배열의 값의 변경
		data[0][1] = 10;

		// input 배열의 출력
		for(int i = 0; i < input.length; i++){
			for(int j = 0; j < input[i].length; j++){
				System.out.print(input[i][j]);
				System.out.print(",");
			} // for in
			System.out.println();
		}//for out

		// data배열의 출력
		for(int i = 0; i < data.length; i++){
			for(int j = 0; j < data[i].length; j++){
				System.out.print(data[i][j]);
				System.out.print(",");
			} // for in
			System.out.println();
		}//for out
	}
}

만약 for문장을 사용하고 싶지 않을 경우에는
ObjectOutputStream과 ObjectInputStream을 이용할 수도 있습니다.
아래의 코드는 2차원 배열뿐만이 아니라 다차원 배열도 복사할 수
있다는 장점이 있습니다.

아래의 내용을 이해하기 위하여는 객체 직렬화에 대하여 알고 있어야
합니다.

import java.io.*;
public class  ArrayCopy2
{
	public static void main(String[] args) 
	{
		int[][] input = {       {1, 2, 3}, 
                                {4, 5, 6}, 
                                {7, 8, 0} 
                         }; 
        int[][] data = null;

		try{
			ByteArrayOutputStream bao = new ByteArrayOutputStream();
			ObjectOutputStream oos = new ObjectOutputStream(bao);
			oos.writeObject(input);

			byte[] buffer = bao.toByteArray();

			ByteArrayInputStream bai = new ByteArrayInputStream(buffer);
			ObjectInputStream ois = new ObjectInputStream(bai);
			data = (int[][])ois.readObject();
		}catch(Exception ex){
			System.out.println(ex);
		}

		// data배열의 값의 변경
		data[0][1] = 10;

		// input 배열의 출력
		for(int i = 0; i < input.length; i++){
			for(int j = 0; j < input[i].length; j++){
				System.out.print(input[i][j]);
				System.out.print(",");
			} // for in
			System.out.println();
		}//for out

		// data배열의 출력
		for(int i = 0; i < data.length; i++){
			for(int j = 0; j < data[i].length; j++){
				System.out.print(data[i][j]);
				System.out.print(",");
			} // for in
			System.out.println();
		}//for out
	}
}

그리고 마지막 방법으로는 clone() 메서드를 이용하는 것입니다.
해당 방법을 이용할 경우에는 복제후에 data배열을 변경하면 input배열도 함께 변경됩니다.그러니 사용상에 문제가 있습니다.

public class  ArrayCopy3
{
	public static void main(String[] args) 
	{
		int[][] input = {       {1, 2, 3}, 
                                {4, 5, 6}, 
                                {7, 8, 0} 
                         }; 
        int[][] data = (int[][])input.clone();


		// data배열의 값의 변경
		data[0][1] = 10;

		// input 배열의 출력
		for(int i = 0; i < input.length; i++){
			for(int j = 0; j < input[i].length; j++){
				System.out.print(input[i][j]);
				System.out.print(",");
			} // for in
			System.out.println();
		}//for out

		// data배열의 출력
		for(int i = 0; i < data.length; i++){
			for(int j = 0; j < data[i].length; j++){
				System.out.print(data[i][j]);
				System.out.print(",");
			} // for in
			System.out.println();
		}//for out
	}
}
저의 홈페이지에 중복 포스팅합니다

----------
http://sunny.sarang.net
JAVA,Oracle,MySQL,Linux,PHP

조성현의 이미지

좀 복잡하군요. 저는 그냥 static 함수로 제공할 줄 알았는데, Stream까지 사용해야 하는군요.

더 알기 위해서 질문 드리는 것이었구요. 질문에 간단히 언급했지만, 사실 간단히 로컬 변수로 해버리면 더 쉬울 것 같군요.

        private void readyJobs(int[][] user_input, int[][] user_output) throws java.lang.NullPointerException
        {
                int i, j;

                for (i = 0; i < this.data.length; i++) {
                        for (j = 0; j < this.data[i].length; j++) {
                                this.input[i][j] = user_input[i][j];
                                this.output[i][j] = user_output[i][j];
                                if (user_input[i][j] == 0) {
                                        current_row = i;
                                        current_col = j;
                                }
                                if (user_output[i][j] == 0) {
                                        dest_row = i;
                                        dest_col = j;
                                }
                        }
                }
                System.arraycopy(user_input, 0, this.data, 0, this.data.length);
        }

위와 같이 그냥 로컬변수인 user_input의 위치를 복사하면 그대로 남아 있긴 하군요. (아마도 나중에 덮어씌워질 수도 있을 것 같군요)

하지만 로컬이라도,

                System.arraycopy(user_input, 0, this.data, 0, this.data.length);
                System.arraycopy(user_input, 0, this.input, 0, this.input.length);

로 해버리면 data = input; 과 같은 결과가 나와버립니다. ;;

좀 애매하지만, 어쩔 수 없는 듯 하군요.

아무튼, 좋은 답변 감사드립니다.

urstory의 이미지

clone() 메서드를 이용하고 싶다면 아래와 같이 코딩해야 합니다.
위에서 쓴 예제에서는 clone()을 사용하면 안되었는데요.
아래와 같이 for문장을 이용하면 가능하지요.

public class  ArrayCopy4
{
	public static void main(String[] args) 
	{
		int[][] input = {       {1, 2, 3}, 
                                {4, 5, 6}, 
                                {7, 8, 0} 
                         }; 
        int[][] data = new int[input.length][];

		for(int i = 0; i < input.length; i++){
			data[i] = (int[])input[i].clone();
		}

		// data배열의 값의 변경
		data[0][1] = 10;

		// input 배열의 출력
		for(int i = 0; i < input.length; i++){
			for(int j = 0; j < input[i].length; j++){
				System.out.print(input[i][j]);
				System.out.print(",");
			} // for in
			System.out.println();
		}//for out

		// data배열의 출력
		for(int i = 0; i < data.length; i++){
			for(int j = 0; j < data[i].length; j++){
				System.out.print(data[i][j]);
				System.out.print(",");
			} // for in
			System.out.println();
		}//for out
	}
}

----------
http://sunny.sarang.net
JAVA,Oracle,MySQL,Linux,PHP

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.