List를 정렬하는 여러가지 방법들을 정리해본다.
Collections.sort 에서부터 stream.sorted까지 살펴본다.
글의 흐름은 Head First Java를 기초로 하고 있다.
/* 배열은 정렬 불가 */
String[] sArr = {"z", "b", "k"};
Collections.sort(sArr); // error
/* List로 변경해서 정렬 */
String[] sArr = {"z", "b", "k"};
List<String> sList = Arrays.asList(sArr);
System.out.println("b4" + sList); // z,b,k
Collections.sort(sList);
System.out.println("a4" + sList); // b,k,z - 원본 수정
원본 배열을 정렬하기 때문에, 원본 배열에 변경이 발생
/* Comparable 구현 전 */
class NotComparable {
private String name;
private int age;
NotComparable(String nm, int age) {
this.name = nm;
this.age = age;
}
String getName() {
return name;
}
Integer getAge() {
return age;
}
}
NotComparable nc3 = new NotComparable("3", 10);
NotComparable nc2 = new NotComparable("2", 17);
NotComparable nc1 = new NotComparable("1", 12);
List<NotComparable> ncList = List.of(nc3, nc2, nc1);
System.out.println(ncList);
Collections.sort(ncList); // error - comparable 미구현
Comparable< T > 인터페이스
Collections.sort를 사용하기 위해 구현되어야하는 인터페이스
String class는 이미 Comparable을 구현하고 있기 때문에 sort를 사용가능
SAM interface a.k.a Functional interfaces
SAM은 single abstract method를 의미한다.
한개의 추상메소드 구현을 요구하는 interface는 람다로 치환될 수 있다.
/* Comparable 구현 후 */
class NotComparable implements Comparable<NotComparable> {
private String name;
private int age;
NotComparable(String nm, int age) {
this.name = nm;
this.age = age;
}
String getName() {
return name;
}
Integer getAge() {
return age;
}
@Override
public int compareTo(NotComparable o) {
System.out.println("is executed?");
return name.compareTo(o.name);
}
}
NotComparable nc3 = new NotComparable("3", 10);
NotComparable nc2 = new NotComparable("2", 17);
NotComparable nc1 = new NotComparable("1", 12);
List<NotComparable> ncList = List.of(nc3, nc2, nc1);
System.out.println(ncList);
Collections.sort(ncList); // error - 불변객체를 변경하려함
// Collections.sort는 원본을 변경하는데,
// List.of로 생성된 List는 불변List
List<NotComparable> ncList2 = new ArrayList<>();
ncList2.add(nc3);
ncList2.add(nc2);
ncList2.add(nc1);
System.out.println(ncList2); // nc3, nc2, nc1
Collections.sort(ncList2);
System.out.println(ncList2); // nc1, nc2, nc3
Comparable을 구현한 형태는 compareTo라는 함수를 Override하게 되는데, 이는 정렬하려는 기준이 여러개가 되었을 때 대응할 수 없다.
/* 이름 순 정렬기능이 구현되어있는데, 나이로 정렬하고 싶다면? */
class Student implement Comparable<Student> {
private String name;
private int age;
@Override
public int compareTo(Student o) {
// Override를 사용하기 때문에 1가지 기능만 구현 가능
// 혹은 flag를 사용하는 좋지 않은 코드가 나올수도..
return name.compareTo(o.name)
}
}
class NotComparable implements Comparable<NotComparable> {
private String name;
private int age;
NotComparable(String nm, int age) {
this.name = nm;
this.age = age;
}
String getName() {
return name;
}
Integer getAge() {
return age;
}
@Override
public int compareTo(NotComparable o) {
System.out.println("is executed?");
return name.compareTo(o.name);
}
}
class AgeComparator implements Comparator<NotComparable> {
@Override
public int compare(NotComparable o1, NotComparable o2) {
return o1.getAge().compareTo(o2.getAge());
}
}
/* 실행 */
NotComparable nc3 = new NotComparable("3", 10);
NotComparable nc2 = new NotComparable("2", 17);
NotComparable nc1 = new NotComparable("1", 12);
List<NotComparable> ncList2 = new ArrayList<>();
ncList2.add(nc3);
ncList2.add(nc2);
ncList2.add(nc1);
AgeComparator ac = new AgeComparator(); // 원하는 로직을 생성
System.out.println(ncList2);
Collections.sort(ncList2, ac); // 외부에서 주입
// 기존에 실행되던 compareTo는 무시됨
System.out.println(ncList2);
class NotComparable {
private String name;
private int age;
NotComparable(String nm, int age) {
this.name = nm;
this.age = age;
}
String getName() {
return name;
}
Integer getAge() {
return age;
}
}
NotComparable nc3 = new NotComparable("3", 10);
NotComparable nc2 = new NotComparable("2", 17);
NotComparable nc1 = new NotComparable("1", 12);
List<NotComparable> ncList2 = new ArrayList<>();
ncList2.add(nc3);
ncList2.add(nc2);
ncList2.add(nc1);
System.out.println(ncList2); // nc3, nc2, nc1
ncList2.sort(); // compile error 발생
ncList2.sort(null); // ClassCastException 발생
System.out.println(ncList2);
NotComparable nc3 = new NotComparable("3", 10);
NotComparable nc2 = new NotComparable("2", 17);
NotComparable nc1 = new NotComparable("1", 12);
List<NotComparable> ncList2 = new ArrayList<>();
ncList2.add(nc3);
ncList2.add(nc2);
ncList2.add(nc1);
AgeComparator ac = new AgeComparator();
System.out.println(ncList2); // nc3, nc2, nc1
ncList2.sort(ac);
System.out.println(ncList2); // nc3, nc1, nc2
// 여전히 원본은 변경된다
/* Runtime Exception case */
class NotComparable {
private String name;
private int age;
NotComparable(String nm, int age) {
this.name = nm;
this.age = age;
}
String getName() {
return name;
}
Integer getAge() {
return age;
}
}
NotComparable nc3 = new NotComparable("3", 10);
NotComparable nc2 = new NotComparable("2", 17);
NotComparable nc1 = new NotComparable("1", 12);
List<NotComparable> ncList2 = new ArrayList<>();
ncList2.add(nc3);
ncList2.add(nc2);
ncList2.add(nc1);
System.out.println(ncList2);
Stream<NotComparable> stream = ncList2.stream().sorted();
System.out.println(ncList2);
List<NotComparable> l = stream.collect(Collectors.toList()); // exception 발생
System.out.println("기존" + ncList2);
System.out.println("뉴" + list);
Collections.sort와 비슷하게, Comparator를 사용하지 않는 경우는 필수로 Comparable을 구현해야하고, Comparator를 사용한다면 구현여부와 상관없이 Comparable 관련 메소드는 실행되지 않는다.
NotComparable nc3 = new NotComparable("3", 10);
NotComparable nc2 = new NotComparable("2", 17);
NotComparable nc1 = new NotComparable("1", 12);
List<NotComparable> ncList2 = new ArrayList<>();
ncList2.add(nc3);
ncList2.add(nc2);
ncList2.add(nc1);
AgeComparator ac = new AgeComparator();
System.out.println(ncList2);
Stream<NotComparable> stream = ncList2.stream().sorted(ac);
System.out.println(ncList2);
List<NotComparable> l = stream.collect(Collectors.toList());
System.out.println("기존" + ncList2); // 유지
System.out.println("뉴" + list); // 정렬됨