Arrays
클래스 ]
Arrays
클래스는 배열 복사, 항목 정렬, 항목 검색과 같은 배열 조작 기능을 가지고 있다.
다음은 Arrays
클래스가 가지고 있는 메소드들이다. Arrays
클래스의 모든 메소드는 정적(static
)이므로 객체를 생성하지 않고도 바로 사용할 수 있다.
배열 복사를 위해 사용할 수 있는 메소드는
copyOf(원본배열, 복사할 길이)
,copyOfRange(원본배열, 시작인덱스, 끝인덱스)
이다.
copyOf(원본배열, 복사할 길이)
원본 배열의 0번 인덱스에서 복사할 길이만큼 복사한 타겟 배열을 리턴한다.
복사할 길이는 원본 배열의 길이보다 커도 되며, 나머지는 배열 요소의 타입에 맞게 기본값으로 채워진다.
✅ 참고 | 타입별 배열 초기값
다음은 arr1[]
배열의 전체 항목을 복사해서 arr2[]
배열을 생성한다.
char[] arr1 = {'J', 'A', 'V', 'A'};
char[] arr2 = Arrays.copyOf(arr1, arr1.length);
copyOfRange(원본배열, 시작인덱스, 끝인덱스)
원본 배열의 시작 인덱스에서 끝 인덱스까지 복사한 배열을 리턴한다.
시작 인덱스는 포함되지만 끝 인덱스는 포함되지 않는다.
다음은 arr1[]
배열 항목 중 1번, 2번 인덱스 항목을 arr2[]
배열의 0번, 1번 인덱스 항목으로 복사한다.
char[] arr1 = {'J', 'A', 'V', 'A'};
char[] arr2 = Arrays.copyOfRange(arr1, 1,3);
System.arraycopy()
단순히 배열을 복사할 목적이라면 Arrays
클래스를 사용하지 않고 System.arraycopy()
메소드를 이용할 수도 있다.
💡
System.arraycopy()
메소드System.arraycopy(Object src, int srcPos, Object dest, int destPos, int length);
Object src
: 원본 배열int srcPos
: 원본 시작 인덱스Object dest
: 타겟 배열int destPos
: 타겟 시작 인덱스int length
: 복사 개수
예를 들어 arraycopy(arr1, 1, arr2, 3, 2)
는 다음과 같이 복사된다.
Arrays
의equals()
와deeqpEquals()
는 배열 항목을 비교한다.
equals()
는 1차 항목의 값만 비교하고, deepEquals()
는 1차 항목이 서로 다른 배열을 참조할 경우, 중첩된 배열의 항목까지 비교한다.public class EqualsExample {
public static void main(String[] args) {
int[][] original = {{1, 2}, {3, 4}};
//얕은 복사 후 비교
System.out.println("[얕은 복사 후 비교]");
int[][] cloned1 = Arrays.copyOf(original, original.length);
System.out.println("배열 번지 비교: " + original.equals(cloned1));
System.out.println("1차 배열 항목값 비교: " + Arrays.equals(original, cloned1));
System.out.println("중첩 배열 항목값 비교: " + Arrays.deepEquals(original, cloned1));
//깊은 복사 후 비교
System.out.println("\n[깊은 복사 후 비교]");
int[][] cloned2 = Arrays.copyOf(original, original.length);
cloned2[0] = Arrays.copyOf(original[0], original[0].length);
cloned2[1] = Arrays.copyOf(original[1], original[1].length);
System.out.println("배열 번지 비교: " + original.equals(cloned2));
System.out.println("1차 배열 항목값 비교: " + Arrays.equals(original, cloned2));
System.out.println("중첩 배열 항목값 비교: " + Arrays.deepEquals(original, cloned2));
}
}
실행 결과
[얕은 복사 후 비교]
배열 번지 비교: false
1차 배열 항목값 비교: true
중첩 배열 항목값 비교: true
[깊은 복사 후 비교]
배열 번지 비교: false
1차 배열 항목값 비교: false
중첩 배열 항목값 비교: true
Arrays.sort()
메소드의 매개값으로 지정해주면 자동으로 오름차순 정렬이 된다.Comparable
인터페이스를 구현하고 있어야 정렬이 된다.int[] scores = {99, 97, 98};
Arrays.sort(scores);
for(int i = 0; i < scores.length; i++) {
System.out.println("scores[" + i + "] = " + scores[i]);
}
scores[0] = 97
scores[1] = 98
scores[2] = 99
String
배열String[] names = {"김", "이", "박"};
Arrays.sort(names);
for(int i = 0; i < names.length; i++) {
System.out.println("names[" + i + "] = " + names[i]);
}
names[0] = 김
names[1] = 박
names[2] = 이
Member
배열에서 Member
객체들을 name
필드값으로 정렬하고 싶다면
다음과 같이 Member
클래스를 작성하면 된다.
Member
- Comparable
구현 클래스public class Member implements Comparable<Member> {
String name;
Member(String name) {
this.name = name;
}
@Override
public int compareTo(Member o) {
return name.compareTo(o.name);
}
}
Comparable<Member>
는 Member
타입만 비교하기 위해서 제네릭 <>
을 사용하였다.compareTo()
메소드는 비교값을 리턴하도록 오버라이딩하였다.compareTo()
메소드는 오름차순일 때 자신이 매개값보다 낮을 경우 음수, 같은 경우 0, 높을 경우 양수를 리턴한다.name
필드값으로 정렬하고자 할 때 유니코드로 비교해야 해서 String
의 compareTo()
리턴값을 사용하였다.SortExample
- 비교Member m1 = new Member("김");
Member m2 = new Member("이");
Member m3 = new Member("박");
Member[] members = {m1, m2, m3};
Arrays.sort(members);
for(int i = 0; i < members.length; i++) {
System.out.println("members[" + i + "].name = " + members[i].name);
}
members[0].name=김
members[1].name=박
members[2].name=이
배열 항목을 검색하려면
- 먼저
Arrays.sort()
메소드로 항목들을 오름차순으로 정렬한 후,Arrays.binarySearch()
메소드로 항목을 찾아야 한다.
public class SearchExample {
public static void main(String[] args) {
//기본 타입값 검색
int[] scores = {99, 97, 98};
Arrays.sort(scores);
int index = Arrays.binarySearch(scores, 99);
System.out.println("찾은 인덱스: " + index);
//문자열 검색
String[] names = {"김", "이", "박"};
Arrays.sort(names);
index = Arrays.binarySearch(names, "이");
System.out.println("찾은 인덱스: " + index);
//객체 검색
Member m1 = new Member("김");
Member m2 = new Member("이");
Member m3 = new Member("박");
Member[] members = {m1, m2, m3};
Arrays.sort(members);
index = Arrays.binarySearch(members, m2);
System.out.println("찾은 인덱스: " + index);
}
}
실행 결과
찾은 인덱스: 2
찾은 인덱스: 2
찾은 인덱스: 2
Wrapper
(포장) 클래스 ]자바는 기본 타입의 값을 갖는 객체를 생성할 수 있다.
기본 타입의 값을 내부에 두고 포장하기 때문에 이러한 객체를 포장(Wrapper
) 객체라고 부른다.
포장 객체의 특징은 포장하고 있는 기본 타입 값은 외부에서 변경할 수 없다는 특징을 갖는다. 만약 내부의 값을 변경하고 싶다면 새로운 포장 객체를 만들어야 한다.
포장 클래스는 java.lang
패키지에 포함되어 있는데, 다음과 같이 기본 타입에 대응되는 클래스들이 있다.
Boxing
)과 언박싱(Unboxing
)기본 타입의 값을 포장 객체로 만드는 과정을 박싱이라고 하고,
반대로 포장 객체에서 기본 타입의 값을 얻어내는 과정을 언박싱이라고 한다.
간단하게 포장 클래스의 생성자 매개값으로 기본 타입의 값
또는 문자열
을 넘겨주면 된다.
생성자를 이용하지 않고, 다음과 같이 각 포장 클래스마다 가지고 있는 정적 valueOf()
메소드를 사용해도 된다.
Integer obj = Integer.valueOf(1000);
Integer obj = Integer.valueOf("1000");
이렇게 박싱된 포장 객체에서 다시 기본 타입의 값을 얻어 내기 위해서는(언박싱하기 위해서는)
각 포장 클래스마다 가지고 있는 기본타입명+Value()
메소드를 호출하면 된다.
기본 타입 값을 직접 박싱, 언박싱하지 않아도 자동적으로 박싱과 언박싱이 일어나는 경우가 있다.
✅ 자동 박싱은 포장 클래스 타입에 기본값이 대입될 경우에 발생한다.
예를 들어, int
타입의 값을 Integer
클래스 변수에 대입하면 자동 박싱이 일어나 힙 영역에 Integer
객체가 생성된다.
Integer obj = 100; //자동 박싱
✅ 자동 언박싱은 기본 타입에 포장 객체가 대입될 경우에 발생한다.
예를 들어 Integer
객체를 int
타입 변수에 대입하거나, Integer
객체와 int
타입값을 연산하면 Integer
객체로부터 int
타입의 값이 자동 언박싱되어 연산된다.
Integer obj = new Integer(200);
int value1 = obj; //자동 언박싱
int value2 = obj + 100; //자동 언박싱
자동 박싱과 언박싱은 코드를 간결하게 만들어주는 역할을 한다!
포장 클래스의 주요 용도는 기본 타입의 값을 박싱해서 포장 객체로 만드는 것이지만,
문자열을 기본 타입 값으로 변환할 때에도 많이 사용된다.
대부분의 포장 클래스에는 parse + 기본타입
명으로 되어 있는 정적 메소드가 있다. 이 메소드는 문자열을 매개값으로 받아 기본 타입 값으로 변환한다.
포장 객체는 내부의 값 비교를 위해
==
와!=
연산자를 사용할 수 없다.
이 연산자는 내부의 값을 비교하는 것이 아니라 포장 객체의 참조를 비교하기 때문이다.
예를 들어 두 Integer
객체는 300
이라는 동일한 값을 갖지만 ==
연산의 결과는 false
가 나온다.
Integer obj1 = 300;
Integer obj2 = 300;
System.out.println(obj1 == obj2); //false
내부의 값만 비교하려면 언박싱한 값을 얻어서 비교해야 한다.
그러나 자바 언어 명세에 보면 다음과 같은 규칙이 있다. 박싱된 값이 다음 표에 나와 있는 값이라면 ==
와 !=
연산자로 내부의 값을 바로 비교할 수 있다. 그 이외의 경우에는 박싱된 값을 ==
와 !=
연산자로 비교할 수 없다.
포장 객체에 정확히 어떤 값이 저장될지 모르는 상황이라면
==
와!=
연산자는 사용하지 않는 것이 좋다.
직접 내부 값을 언박싱해서 비교하거나, equals()
메소드로 내부 값을 비교하는 것이 좋다. 포장 클래스의 equals()
메소드는 내부의 값을 비교하도록 오버라이딩 되어 있다.