래퍼 클래스, Arrays 클래스

gustjtmd·2022년 2월 2일
0

Java

목록 보기
16/40

래퍼 클래스 (Wrapper 클래스)

래퍼 클래스의 이름이 의미하듯이 "감싸는 클래스"이다. 그리고 래퍼 클래스가 감싸는 대상은 정수, 실수, 문자와 같은 기본 자료형의 값이다.

기본 자료형의 값을 감싸는 래퍼 클래스

int형 정수나 double형 실수와 같은 기본 자료형의 값들도 인스턴스로 표현해야 하는 경우가 있다.

예를 들어 다음 메소드의 인자로 정수 3과 실수 7.15를 전달해야 하는 상황이라고 가정해보자

public static void showData(Object obj){
	System.out.println(obj);	//toString 메소드 호출하여 반환되는 문자열 출력
}

위 메소드는 인스턴스를 인자로 요구한다. 이렇듯 인스턴스의 참조 값을 요구하는 자리에 기본 자료형
의 값을 놓아야 하는 경우가 종종 있는데 이럴때 필요한 것이 '래퍼 클래스'

"래퍼 클래스는 기본 자료형의 값을 감싸는 클래스이다."

코드로 확인해보자

public class UseWrapperClass {
    public static void showData(Object obj){
        System.out.println(obj);
    }
    public static void main(String[] args) {
        Integer iInst = new Integer(3); //정수 3을 감싸는 래퍼 인스턴스 생성
        showData(iInst);
        showData(new Double(7.15)); //7.15를 감싸는 래퍼 인스턴스 생성 및 전달
    }
}


3
7.15

------------------------------------------------------------------------

래퍼 클래스도 toString 메소드를 오버라이딩 하고 있다.
따라서 println 메소드의 인자로 다음과 같이 전달될 수 있으며 이때 인스턴스가 지닌 값이 출력

Integer iw = new Integer(123);
System.out.println(iw);	//123출력

래퍼클래스의 정의

기본 자료형을 대상으로 래퍼 클래스가 정의되어 있다(오른 편에 위치한 것은 생성자)

Boolean		public BOolean(boolean value)
Character	public Character(char value)
Byte		public Byte(byte value)
Short		public Short(short value)
Integer		public Integer(int value)
Long		public Long(long value)
Float		public Float(float value), public Float(double value)
DOuble		public Double(double value)

래퍼 클래스의 두가지 기능

래퍼 클래스의 중요한 기능 두 가지는 다음과 같다.
하나는 값을 인스턴스로 감싸는 것이고 다른 하나는 인스턴스에서 값을 꺼내는 것

값을 인스턴스에 감싸는 행위를 가리켜 '박싱'이라 하고 반대로 저장된 값을 꺼내는 행위를
'언박싱'이라 한다. 
박싱은 인스턴스 생성을 통해서 이뤄지지만 언박싱은 래퍼 클래스에 정의된 메소드의 호출을
통해서 이뤄진다.

코드로 확인해보자

public class BoxingUnboxing {
    public static void main(String[] args) {
        Integer iObj = new Integer(10); //박싱
        Double dObj = new Double(3.14); //박싱
        System.out.println(iObj);
        System.out.println(dObj);
        System.out.println();

        int num1 = iObj.intValue(); //언박싱
        double num2 = dObj.doubleValue();   //언박싱
        System.out.println(num1);
        System.out.println(num2);
        System.out.println();

        //래퍼 인스턴스 값의 증가 방법
        iObj = new Integer(iObj.intValue() + 10);
        dObj = new Double(dObj.doubleValue() + 1.2);
        System.out.println(iObj);
        System.out.println(dObj);
    }
}

10
3.14

10
3.14

20
4.34

----------------------------------------------------------------------

래퍼 인스턴스들은 담고 있는 값을 수정하지 못한다.
따라서 값의 수정이 필요하면 위 예제의 다음 문장들과 같이 새로운 래퍼 인스턴스 생성해야 한다.

		iObj = new Integer(iObj.intValue() + 10);
        dObj = new Double(dObj.doubleValue() + 1.2);

오토 박싱 & 오토 언 박싱

자바 5부터 박싱과 언박싱이 필요한 상황에서 이를 자동으로 처리하기 시작했다 그리고 이를 가리켜
각각 오토 박싱, 오토 언박싱이라 한다. 

코드로 확인해보자

public class AutoBoxingUnboxing {
    public static void main(String[] args) {
        Integer iObj = 10;  //오토 박싱 진행
        Double dObj = 3.14; //오토 박싱 진행
        System.out.println(iObj);
        System.out.println(dObj);
        System.out.println();

        int num1 = iObj;    //오토 언박싱 진행
        double num2 = dObj; //오토 언박싱 진행
        System.out.println(num1);
        System.out.println(num2);
    }
}

10
3.14

10
3.14

--------------------------------------------------------------------------

위 코드에서 다음 두 문장에서는 대입 연산자가 오른편에 각각 Integer 인스턴스와
Double 인스턴스가 와야 하는데 이를 대신해서 정수와 실수가 위치해 있다.

Integer iObj = 10;
Double dObj = 3.14

이러한 상황에서 다음과 같이 정수와 실수를 기반으로 Integer 인스턴스와 Double 인스턴스가
생성된다 즉 오토박싱이 이뤄진다

Integer iObj = 10;		-> Integeer iObj = new Integer(10);
Double dObj = 3.14;		-> Double dObj = new Double(3.14);

반대로 대입 연산자의 오른편에 정수와 실수가 와야 하는데 이를 대신해 Integer Double
인스턴스가 위치해있다.

int num1 = iObj;
double num2 = dObj;

이러한 상황에서 다음과 같이 오토 언박싱이 진행된다

int num1 = iObj;		-> int num1 = iObj.intValue();
double num2 = dObj;		-> double num2 = dObj.doubleValue();
오토 박싱과 오토 언박싱은 다양한 상황과 문장에서 진행된다

public class AutoBoxingUnboxing2 {
    public static void main(String[] args) {
        Integer num = 10;
        num++;  //오토 박싱, 오토 언박싱 동시 진행
        System.out.println(num);

        num += 3;   //오토 박싱, 오토 언박싱 동시 진행
        System.out.println(num);

        int r = num + 5;    //오토 언박싱 진행
        Integer rObj = num - 5; //오토 언박싱 진행
        System.out.println(r);
        System.out.println(rObj);
        
    }
}

11
14
19
9

--------------------------------------------------------------------------

다음 두 문장에서는 다음과 같이 오토 박싱과 오토 언박싱이 동시에 진행이 된다.

num++;		-> new Integer(num.intValue() +1);
num += 3;	-> new Integer(num.intValue() + 3);

이렇듯 오토박싱과 오토 언박싱 덕분에 Integer형 참조변수 num을 int형 변수 num처럼
사용할수 있게 되었다.

Number 클래스와 래퍼 클래스의 static 메소드

앞서 소개한 모든 래퍼 클래스는 다음 클래스를 상속한다

java.lang.Number

그리고 이 클래스에는 다음의 추상 메소드들이 존재한다(Number도 추상클래스)

public abstract int intValue()
public abstract long longValue()
public abstract double doubleValue()

따라서 이를 상속하는 Integer Double과 같은 클래스들은 위의 메소드 모두를 구현하고 있다
때문에 어떠한 래퍼 인스턴스를 대상으로도 인스턴스에 저장된 값을 다양한 형태로 변환 가능하다

코드로 확인해보자

public class NumberMethod {
    public static void main(String[] args) {
        Integer num1 = new Integer(29);
        System.out.println(num1.intValue());    //int형 값으로 반환
        System.out.println(num1.doubleValue()); //double형 값으로 반환
        System.out.println();
        
        Double num2 = new Double(3.14);
        System.out.println(num2.intValue());    //int형 값으로 반환
        System.out.println(num2.doubleValue()); //double형 값으로 반환
    }
}

29
29.0

3
3.14

-----------------------------------------------------------------------

출력 결과를 보면 Double형 인스턴스에 저장된 값을 int형으로 반환할 경우 소수점 이하의 값이
삭제 되는 것을 알 수 있다.
래퍼 클래스에는 static으로 선언된 다양한 메소드들이 존재한다 

코드로 확인해보자

public class WrapperClassMethod {
    public static void main(String[] args) {
        //클래스 메소드를 통한 인스턴스 생성 방법 두 가지
        Integer n1 = Integer.valueOf(5);  //숫자 기반 Integer 인스턴스 생성
        Integer n2 = Integer.valueOf("1024");   //문자열 기반 Integer 인스턴스 생성

        // 대소 비교와 합을 계산하는 클래스 메소드
        System.out.println("큰수 : " + Integer.max(n1, n2));
        System.out.println("작은 수 :" + Integer.min(n1, n2));
        System.out.println("합 : " + Integer.sum(n1, n2));
        System.out.println();

        //정수에 대한 2진 8진 16진수 표현 결과를 반환하는 클래스 메소드
        System.out.println("12의 2진 표현 : " + Integer.toBinaryString(12));
        System.out.println("12의 8진 표현 : " + Integer.toOctalString(12));
        System.out.println("12의 16진 표현 : " + Integer.toHexString(12));
        
    }
}

큰수 : 1024
작은 수 :5: 1029

122진 표현 : 1100
128진 표현 : 14
1216진 표현 : c

Arrays 클래스

Arrays 클래스는 배열 조작에 도움을 주는 메소드들로 채워져 있다 따라서 이 클래스에 정의된 메소드들을 사용하면 배열의 복사, 비교, 정렬 및 탐색과 관련된 코드를 쉽게 작성할 수 있다.

배열의 복사

배열 복사에 사용되는 Arrays 클래스의 메소드는 다음과 같은데 모든 기본 자료형 배열에 대해
이 메소드가 오버로딩 되어 있으나 아래에서는 int형 배열에 대해 정의된 메소드만 보였다

public static int[] copyOf(int[] original, int newLength)
	-> original에 전달된 배열을 첫번째 요소부터 newLength의 길이만큼 복사
    
위 메소드는 배열을 복사하여 '새로운 배열을 생성'하는 메소드이다. 첫번째 인자로 전달된 배열을
두 번째 인자로 전달된 길이만큼 복사하되 '새로운 배열을 생성'하여 복사한다
그리고 이렇게 생성된 배열의 참조 값을 반환한다

코드로 확인해보자


public class CopyOfArrays {
    public static void main(String[] args) {
        double[] arOrg = {1.1,2.2,3.3,4.4,5.5};

        //배열 전체를 복사
        double[] arCpy1 = Arrays.copyOf(arOrg, arOrg.length);

        //세번째 요소까지만 복사
        double[] arCpy2 = Arrays.copyOf(arOrg, 3);

        for(double d : arCpy1)
            System.out.print(d + "\t");
        System.out.println();

        for (double d : arCpy2)
            System.out.printf(d + "\t");
        System.out.println();
    }
}


1.1	2.2	3.3	4.4	5.5	

1.1	2.2	3.3	
배열의 일부만 복사하려면 Arrays 클래스의 다음 메소드를 사용하면 된다

public static int[] copyOfRange(int[] original, int from. int to)
	-> original에 전달된 배열을 인덱스 from 부터 to 이전 요소까지 복사
    
배열을 새로 생성하지 않고 존재하는 배열에 복사를 하려는 경우 다음 메소드를 호출하면 된다
(java lang.System 클래스이다)

public static void arraycopy(Object src, int srcPos, Object dest, 
						int destPos, int length)
  -> 배열 src의 srcPos에서 배열 dest의 destPos로 length 길이만큼 복사
  
코드로 확인해보자

public class CopyOfSystem {
    public static void main(String[] args) {
        double[] org = {1.1, 2.2, 3.3, 4.4, 5.5};
        double[] cpy = new double[3];

        //배열 org의 인덱스 1에서 배열 cpy 인덱스 0으로 부터 세개의 요소 복사
        System.arraycopy(org,1,cpy,0,3);

        for (double d : cpy) {
            System.out.printf(d + "\t");
        }
        System.out.println();
    }
}

2.2	3.3	4.4	

배열의 비교

배열의 내용 비교에 사용되는 Arrays 클래스의 메소드는 다음과 같다 
기본적으로 모든 기본 자료형의 배열에 대해 이 메소드가 오버로딩 되어 있으나 아래에서는
int형 배열에 대해 정의된 메소드만 보였다.

public static boolean equals(int[] a, int[] a2)
	-> 매개변수 a와 a2의 전달된 배열의 내용을 비교하여 true 또는 false
    
이 메소드는 두 배열에 저장된 데이터의 수, 순서, 그리고 내용이 같을떄 true 반환
코드로 확인해보자

public class ArrayEquals {
    public static void main(String[] args) {
        int[] ar1 = {1, 2, 3, 4, 5};
        int[] ar2 = Arrays.copyOf(ar1, ar1.length);
        System.out.println(Arrays.equals(ar1, ar2));
    }
}

true
이 메소드에는 다음과 같이 Object형 배열에 대해서도 오버로딩 되어있다

public static boolean equals(Object[] a, Object[] a2)

이는 인스턴스의 참조 값을 저장하고 있는 두 배열에 대해서 비교를 진행한다 그렇다면 어떤 상황에서
true를 반환할까? 이 메소드는 참조 값이 아닌 참조하는 인스턴스의 내용을 ㅣㅂ교한다
그리고 이떄 Object 클래스에 정의된 equals 메소드가 사용된다 

코드로 확인해보자

class INum {
    private int num;
    public INum(int num) {
        this.num = num;
    }
}
public class ArrayObjEquals {
    public static void main(String[] args) {
        INum[] ar1 = new INum[3];
        INum[] ar2 = new INum[3];
        ar1[0] = new INum(1); ar2[0] = new INum(1);
        ar1[1] = new INum(2); ar2[1] = new INum(2);
        ar1[2] = new INum(3); ar2[2] = new INum(3);
        System.out.println(Arrays.equals(ar1,ar2));
    }
}


false


--------------------------------------------------------------------------

위 코드에서 두 배열에 동일한 값으로 초기화된 서로 다른 인스턴스를 각각 저장하였다
그러나 배열의 비교 결과는 false이다 Object 클래스에 정의되어 있는 equals 메소드가
다음과 같이 정의되어 있기 떄문이다

public boolean equals(Object obj){
	if(this == obj)	//두 인스턴스가 동일 인스턴스이면
    	return true;
    else
    	return false;
}	//이렇듯 Object 클래스에 정의된 equals 메소드는 참조 값 비교를 한다.

따라서 참조 값 비교가 아닌 내용 비교가 목적이라면 다음과 같이 equals 메소드를 목적에 맞게
오버라이딩 해야 한다

@Override
    public boolean equals(Object obj){
        INum r = (INum) obj;
        if(num == r.num)
            return true;
        else
            return false;
    }
    
true

배열의 정렬

배열의 정렬에 사용되는 Arrays 클래스의 메소드는 다음과 같다 기본적으로 모든 기본 자료형의
배열에 대해 이 메소드가 오버로딩 되어 있으나 아래에서는 int형 배열에 대해 정의된 메소드만 보였다

public static void sort(int[] a)
	-> 매개변수 a로 전달된 배열을 오름차순으로 정렬
    
이 메소드는 배열에 저장된 데이터를 오름차순으로 정렬한다
코드로 확인해보자

public class ArraySort {
    public static void main(String[] args) {
        int[] ar1 = {1,5,3,2,4};
        double[] ar2 = {3.3, 2.2, 5.5, 1.1, 4.4};
        Arrays.sort(ar1);
        Arrays.sort(ar2);

        for (int n : ar1) {
            System.out.printf(n + "\t");
        }
        System.out.println();
        for (double d : ar2) {
            System.out.printf(d + "\t");
        }
        System.out.println();
    }
}

1	2	3	4	5	
1.1	2.2	3.3	4.4	5.5	
그리고 이 메소드는 다음과 같이 Object 배열에 대해서도 오버로딩 되어있다

public static void sort(Object[] a)

이는 인스턴스 참조 값을 저장하고 있는 배열에 대한 정렬을 진행한다. 그러면 순서상 인스턴스의
앞서고 뒤섬은 어디서 판단을 할까? 이는 다음 인터페이스의 구현을 기바능로 프로그래머가 
클래스 별로 결정하게 되어 있다.

interface Comparable
	-> int comparaTo(Object o) 메소드 구현을 통해 인스턴스의 순서 판단 기준을 결정
    
위 인터페이스에 존재하는 추상 메소드 comparaTo의 구현 방법은 다음과 같다

* 인자로 전달된 o가 작다면 양의 정수 반환
* 인자로 전달된 o가 크가면 음의 정수 반환
* 인자로 전달된 o가 같다면 0을 반환

코드로 확인해보자

class Person implements Comparable{
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public int compareTo(Object o){
        Person p = (Person) o;
        if(this.age > p.age)
            return 1;   //인자로 전달된 o가 작다면 양의 정수 반환
        else if(this.age < p.age)
            return -1;   //인자로 전달된 o가 크다면 음의 정수 반환
        else
            return 0;   //인자로 전달된 o와 같다면 0을 반환
    }
    @Override
    public String toString(){
        return name + ": " + age;
    }
}
public class ArrayObjSort {
    public static void main(String[] args) {
        Person[] ar = new Person[3];
        ar[0] = new Person("Lee", 29);
        ar[1] = new Person("Goo", 15);
        ar[2] = new Person("Soo", 37);

        Arrays.sort(ar);
        for (Person p : ar) {
            System.out.println(p);
        }
    }
}

Goo: 15
Lee: 29
Soo: 37


----------------------------------------------------------------------------

@Override
    public int compareTo(Object o){
        Person p = (Person) o;
        if(this.age > p.age)
            return this.age - p.age;
        else if (this.age < p.age)
            return p.age - this.age;
        else
            return 0;
    }

위 코드 대신 아래 코드로 간단하게 대신 정의 할 수도 있다.

배열의 탐색

배열의 탐색에 사용되는 Arrays 클래스의 메소드는 다음과 같다 이 메소드 역시 Arrays 클래스의
다른 메소드들과 마찬가지로 모든 기본 자료형의 배열에 대해 오버로딩 되어 있으나
아래에서는 int 배열에 대해 정의된 메소드만 보였다

public static int binarySearch(int[] a, int key)
	-> 배열 a에서 key를 찾아서 있으면 key의 인덱스 값, 없으면 0보다 작은 수 반환
    
코드로 확인해보자
public class ArraySearch {
    public static void main(String[] args) {
        int[] ar = {33, 55, 11, 44, 22};
        Arrays.sort(ar);    //탐색 이전에 정렬이 선해오디어야 한다.
        for (int n : ar) {
            System.out.printf(n + "\t");
        }
        System.out.println();

        int idx = Arrays.binarySearch(ar, 33);  //배열 ar에서 33을 찾아라.
        System.out.println("Index of 33: " + idx);       
    }
}

11	22	33	44	55	
Index of 33: 2

-------------------------------------------------------------------------------

탐색을 하기전에는 반드시 sort 메소드를 통해 정렬을 진행해줘야 한다.
이 메소드는 다음과 같이 Object형 배열에 대해서도 오버로딩 되어 있다.
물론 이 메소드의 호출을 위해서는 배열이 정렬된 상태이어야 한다.

public static int binarySearch(Object[] a, Object key)

이 메소드가 key와 동일한 인스턴스를 찾았다고 판단하는 기준은 무엇일까?
Comparable 인터페이스의 comparaTo 메소드가 그 기준이다.
comparaTo 메소드의 호출을 통해서 0이 반환되면 key에 해당하는 인스턴스를 찾았다고 판단한다.

코드로 확인해보자

class Person implements Comparable{
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public int compareTo(Object o){
        Person p = (Person) o;
        return this.age - p.age;
    }
    @Override
    public String toString(){
        return name + ": " + age;
    }
}
public class ArrayObjSort {
    public static void main(String[] args) {
        Person[] ar = new Person[3];
        ar[0] = new Person("Lee", 29);
        ar[1] = new Person("Goo", 15);
        ar[2] = new Person("Soo", 37);

        Arrays.sort(ar);    //탐색에 앞서 정렬을 진행
        int idx = Arrays.binarySearch(ar, new Person("Who are you?", 37));
        System.out.println(ar[idx]);
    }
}

Soo: 37


--------------------------------------------------------------------------------

다음 문장을 통해 탐색을 진행하고 있다.

int idx = Arrays.binarySearch(ar, new Person("Who are you?", 37));

문장의 내용만 놓고 보면 이름이 "Who are you" 나이가 37Person 인스턴스를 찾는 것으로
보이나 이름은 "Soo" 나이가 37이다. 이러한 결과는 다음 메소드 정의에서 찾을수 있다.

@Override
    public int compareTo(Object o){
        Person p = (Person) o;
        return this.age - p.age;	//나이가 같으면 0 반환
    }

정리하면 binarySearch 메소드를 통해 인스턴스를 찾고자 하는 경우 탐색으 ㅣ대상이 되는
인스턴스들의 클래스는 Comparable 인터페이스를 구현한 상태이어야 한다.
이는 comparaTo 메소드의 구현 내용을 토대로 탐색이 진행되기 때문이다.
profile
반갑습니다

0개의 댓글