Simple Data Structure (1) - 공통 메서드
사실 우리가 흔히 사용하는 ArrayList 이외에 자료구조들은 정말 많습니다.
그러나 이들은 ArrayList만큼 자주 사용되지 않습니다. 단편적으로 데이터를 중간에 삽입이나 삭제하는 경우 ArrayList보다 LinkedList가 훨씬 빠릅니다.
그러나 제가 겪은 실무 환경에서는 적어도 이러한 상황 자체가 잘 나타나지 않습니다.
자료구조를 제대로 알고 활용한다면 금상첨화지만 그럴만한 시간이 없다면 대표 자료구조 하나라도 제대로 아는 것이 중요하다 생각합니다.
그런 면에서 간단하면서도 꼭 필요한 자료구조들을 소개하는 포스팅을 준비해봤습니다.
Object Class의 공통 메서드
모든 클래스는 Object 클래스를 상속받습니다.
따라서 Object 클래스가 가지고 있는 메서드들 또한 다른 클래스들도 사용할 수 있습니다.
Object obj1 = new Object();
Object obj2 = new Object();
//해시코드 반환
System.out.println(obj1.hashCode()); //92150540
//객체의 타입과 해시코드(HexString) 반환
System.out.println(obj1.toString()); //java.lang.Object@57e1b0c
System.out.println(obj1); //java.lang.Object@57e1b0c
//메모리 주소 비교
System.out.println(obj1 == obj2); //false
System.out.println(obj1.equals(obj2)); //false
특히 출력문에 객체를 넣어도 자동으로 toString
메서드가 호출된다는 점 주의하셔야 합니다.
또한 hashCode 메서드는 기본적으로 메모리주소를 비교합니다.
equals 메서드는 내부적으로 ==을 사용하여 비교하며 ==는 해시코드를 비교합니다.
해당 메서드들을 상속받는 클래스들은 해당 기능을 그대로 사용하기도 하고, 오버라이딩하여 사용하기도 합니다.
Array의 경우 이들을 오버라이딩하지 않습니다.
int[] arr1 = {};
int[] arr2 = {};
System.out.println(arr1.hashCode()); //92150540
System.out.println(arr2.hashCode()); //1110623531
System.out.println(arr1); //[I@57e1b0c
System.out.println(arr2); //[I@4232c52b
System.out.println(arr1.equals(arr2)); //false
System.out.println(arr1.equals(arr2)); //false
하지만 Collection Framework에서는 다음과 같이 오버라이딩합니다.
List<Integer> list1 = new ArrayList<>();
List<Integer> list2 = new ArrayList<>();
System.out.println(list1.hashCode()); //1
System.out.println(list2.hashCode()); //1
System.out.println(list1); //[]
System.out.println(list2); //[]
//메모리 주소 비교 vs 해시코드값 비교
System.out.println(list1 == list2); //false
System.out.println(list1.equals(list2)); //true
결과들이 왜 다를까요? 이유는 오버라이딩 되었기 때문입니다.
Collection Framework에서는 hashCode 메서드가 오버라이딩 되어 원소값에 따라 결과가 반환되게끔 오버라이딩 됩니다.
equals 메서드 또한 오버라이딩하며 hashcode값을 비교하게 만듭니다. 이에 따라 결과가 전혀 다르게 나타납니다.
Collection Framework의 공통 메서드
Map은 Collection Framework에 속하지만 Collection Interface를 상속받지 않습니다.
따라서 컬렉션의 의미에 따라 컬렉션일수도 있고 아닐수도 있다. 따라서 Collection의 메서드들과 이름이 같은 Map의 메서드들이 존재한다. 이들에 대해 알아보자.
List<Integer> list = new ArrayList<>(Arrays.asList(1,2,3));
Map<String, Integer> map = new HashMap<>();
map.put("one", 1);
//크기 반환
System.out.println(list.size()); //3
System.out.println(map.size()); //1
//원소 전체 삭제
list.clear(); //return void
map.clear();
System.out.println(list); //[]
System.out.println(map); //{}
//비어있는지 확인
System.out.println(list.isEmpty()); //true
System.out.println(list.isEmpty()); //true
Arrays와 Collections의 정적 메서드
배열에는 Arrays Class는 배열을 편리하게 다루기 위한 정적 메서드를 제공합니다.
Integer[] arr = {3, 2, 1};
//출력
System.out.println(Arrays.toString(arr)); //[3, 2, 1]
//정렬
Arrays.sort(arr);
System.out.println(Arrays.toString(arr)); //[1, 2, 3]
//Array -> List
List<Integer> dataList = new ArrayList<>(Arrays.asList(1,2,3));
System.out.println(dataList); //[1, 2, 3]
위에서 언급했다싶히 배열은 toString
메서드를 오버라이딩하지 않습니다.
따라서 배열을 출력하려면 반복문을 사용해야했으나 Arrays.toString
메서드를 사용하면 원소들을 모두 출력할 수 있습니다.
또한 정렬은 기본적으로 원소 내의 Comparable 인터페이스의 compareTo
메서드를 기준으로 정렬합니다.
숫자나 문자열의 경우 내부적으로 이미 해당 메서드가 구현되어 있기 때문에 자연순서대로 정렬됩니다.
마지막으로 Arrays.asList
메서드는 단순히 형변환하는 것이 아닌 배열 객체 내부에 존재하는 ArrayList 내부 객체를 반환합니다. 따라서 객체에 대한 읽기작업만 가능합니다.
내부 객체가 아닌 새로운 ArrayList를 반환하고 싶다면 내부 객체를 생성자로 새로운 ArrayList를 만들면 됩니다.
다음은 Collections의 정적 메서드입니다. 마찬가지로 컬렉션을 편리하기 다루기 위한 정적 메서드를 제공합니다.
List<Integer> list = new ArrayList(Arrays.asList(3,2,1));
//최대값과 최소값 구하기
System.out.println(Collections.max(list)); //3
System.out.println(Collections.min(list)); //1
//정렬
Collections.sort(list);
System.out.println(list); //[1, 2, 3]
//섞기
Collections.shuffle(list);
System.out.println(list); //[2, 3, 1]
//뒤집기
Collections.reverse(list);
System.out.println(list); //[1, 3, 2]
마찬가지로 정렬 조건은 원소 내부에 구현되어 있는 Comparable 인터페이스의 compareTo
메서드를 기준으로 정렬합니다.