원지의 개발
article thumbnail
728x90

Comparable VS Comparator

  • 둘 다 인터페이스(interface) 이므로 추상클래스를 반드시 구현해야 함
  • 새로운 클래스 객체를 만들어 비교하려고 하면 비교할 기준이 필요
  • comparable은 Arrays.sort(); 사용할 때 자동으로 오름차순 정렬 되고, compareTo 메서드 구현하면 자기 자신을 기준으 객체의 기본 정렬 순서를 정의
  • comparator는 내가 특정 객체들을 기준으로 정렬하고 싶을 때 compare 메서드를 오버라이드하여 기준을 만들어(커) 객체들을 비교하는데 사용
- Comparable:
해당 객체의 기본 정렬 순서를 변경하고 싶을 때 사용
객체 클래스 내에 Comparable 인터페이스를 구현하고 compareTo() 메서드를 재정의.
그 후, 해당 객체들의 배열이나 컬렉션을 Arrays.sort() 또는 Collections.sort() 메서드로 정렬할 때 자동으로 사용

- Comparator:
기존의 정렬 순서와 다른 정렬 기준을 적용하고자 할 때 사용
별도의 Comparator 클래스를 작성하거나 익명 클래스 또는 람다식을 활용하여 Comparator 객체를 생성
Arrays.sort() 또는 Collections.sort() 메서드의 두 번째 인자로 전달하여 사용

Comparable <T> { }

  • Java.lang 패키지 (import 필요X)

compareTo(T o) 메서드

  • 자기 자신과 매개변수 객체 비교
  • compareTo 메소드를 반드시 구현해야 함
  • 조건문을 통해 대소비교를 한 후, 자기자신을 기준으로 대소관계를 파악
    나 > 상대방 [양수, 1 ]
    나 = 상대방 [ 0 ]
    나 < 상대방 [음수, -1 ]
  • Overflow, Underflow가 발생할 여지가 있는지를 반드시 확인 후 사용해야 함
jdk 1.8 documents 확인
java.lang - String Class -  Comparable <String>
java.util - Date Calss - Comparable <Date>
Class 안에 들어가 있어서 비교 할 때 compareTo 메서드 사용
    @Override
    public int compareTo(Student o) {
    	//this.age가 더 크면 양수 리턴
        return this.age - o.age;
    }
	@Override
	public int compareTo(PersonVO o) { // 내림차순
		// 문자열 비교 방법 설정
		return o.name.compareTo(this.name);
	}
    
    //오름차순으로 하고 싶으면 순서를 바꿔서 return name.compareTo(o.name); 으로 변경

가격 기준으로 정렬

class Fruit implements Comparable<Fruit> {
    private String name;
    private int price;

    public Fruit(String name, int price) {
        this.name = name;
        this.price = price;
    }

    public String getName() {
        return name;
    }

    public int getPrice() {
        return price;
    }

    @Override
    public int compareTo(Fruit other) {
        return this.price - other.price; // 가격을 기준으로 정렬
    }
}
Fruit apple = new Fruit("Apple", 1000);
Fruit orange = new Fruit("Orange", 800);
Fruit banana = new Fruit("Banana", 1200);

List<Fruit> fruits = new ArrayList<>();
fruits.add(apple);
fruits.add(orange);
fruits.add(banana);

Collections.sort(fruits);

for (Fruit fruit : fruits) {
    System.out.println(fruit.getName());
}

이름 기준으로 정렬

package collectionTest;

import java.lang.reflect.Array;
import java.util.*;

class Student implements Comparable<Student>{
	String name;
	int ban;
	int no;
	int kor, eng, math;
	
	public Student(String name, int ban, int no, int kor, int eng, int math) {
		super();
		this.name = name;
		this.ban = ban;
		this.no = no;
		this.kor = kor;
		this.eng = eng;
		this.math = math;
	}
	
	int getTotal() {
		return kor + eng + math;
	}
	
	float getAverage() {
		return (int)((getTotal() / 3f)*10 + 0.5)/10f;
	}

	@Override
	public int compareTo(Student o) { //오름차순
		return name.compareTo(o.name);
	}

	@Override
	public String toString() {
		return "Student [name=" + name + ", ban=" + ban + ", no=" + no + ", kor=" + kor + ", eng=" + eng + ", math="
				+ math + "]";
	}
} //end Student


public class Ex11_3 {
	public static void main(String[] args) {
		ArrayList<Student> list = new ArrayList<>();
		list.add(new Student("홍길동", 1, 1, 100, 100, 100));
		list.add(new Student("남궁성", 1, 2, 90, 70, 80));
		list.add(new Student("김자바", 1, 3, 80, 80, 90));
		list.add(new Student("이자바", 1, 4, 70, 90, 70));
		list.add(new Student("안자바", 1, 5, 60, 100, 80));
		
		Collections.sort(list);
		Iterator it = list.iterator();
		
		while(it.hasNext()) {
			System.out.println(it.next());
		}
	}
}
---------------------------------------------------------------
Student [name=김자바, ban=1, no=3, kor=80, eng=80, math=90]
Student [name=남궁성, ban=1, no=2, kor=90, eng=70, math=80]
Student [name=안자바, ban=1, no=5, kor=60, eng=100, math=80]
Student [name=이자바, ban=1, no=4, kor=70, eng=90, math=70]
Student [name=홍길동, ban=1, no=1, kor=100, eng=100, math=100]
  • 기본적으로 sort는 Comparable 인터페이스를 구현하는데 여기에는 compareTo 메서드가 정의
    학생의 이름으로 정렬될 것을 요구했으므로 오버라이딩하여 이름 기준으로 맞춰줘야 함

Comparator<T> { }

  • Java.util 패키지
  • Comperator 기능만 두고 싶은 경우 Anonymous Class 활용

compare(T o1, T o2) 메서드

  • 두개의 매개변수 비교
    @Override
    public int compare(Student o1, Studetn o2) {
    	//o1.age가 더 크면 양수 리턴
        return o1.age - o2.age;
    }
//문자열 정렬
int result = Character.compare(c1, c2);

//사전순으로
c1 → c1 => -1
c1 = c2 => 0
c2 → c1 => 1

문자열의 길이 기준으로 정렬

class LengthComparator implements Comparator<String> {
    @Override
    public int compare(String str1, String str2) {
        return str1.length() - str2.length(); // 길이를 기준으로 정렬
    }
}
List<String> strings = new ArrayList<>();
strings.add("Apple");
strings.add("Banana");
strings.add("Orange");

LengthComparator comparator = new LengthComparator();
Collections.sort(strings, comparator);

for (String str : strings) {
    System.out.println(str);
}
더보기

반 > 번호 순으로 오름차순 정렬

package collectionTest;

import java.lang.reflect.Array;
import java.util.*;

class Student2 implements Comparable<Student>{
	String name;
	int ban;
	int no;
	int kor, eng, math;
	
	public Student2(String name, int ban, int no, int kor, int eng, int math) {
		super();
		this.name = name;
		this.ban = ban;
		this.no = no;
		this.kor = kor;
		this.eng = eng;
		this.math = math;
	}
	
	int getTotal() {
		return kor + eng + math;
	}
	
	float getAverage() {
		return (int)((getTotal() / 3f)*10 + 0.5)/10f;
	}

	@Override
	public int compareTo(Student o) { //오름차순
		return name.compareTo(o.name);
	}

	@Override
	public String toString() {
		return name + ", " + ban + ", " + no + ", " + kor + ", " + eng + ", " + math;
	}
} //end Student

class BanNoAscending implements Comparator<Student2> {

	@Override
	public int compare(Student2 o1, Student2 o2) {
		if(o1.ban == o2.ban) { //o1.ban - o2.ban == 0
			return o1.no - o2.no;
		}
		return o1.ban - o2.ban;
	}
}

public class Ex11_4 {
	public static void main(String[] args) {
		ArrayList<Student2> list = new ArrayList<>();
		list.add(new Student2("홍길동", 1, 3, 100, 100, 100));
		list.add(new Student2("남궁성", 1, 1, 90, 70, 80));
		list.add(new Student2("김자바", 1, 2, 80, 80, 90));
		list.add(new Student2("이자바", 2, 1, 70, 90, 70));
		list.add(new Student2("안자바", 2, 2, 60, 100, 80));
		
		Collections.sort(list, new BanNoAscending());
		Iterator it = list.iterator();
		
		while(it.hasNext()) {
			System.out.println(it.next());
		}
	}
}
-------------------------------------------------------------
남궁성, 1, 1, 90, 70, 80
김자바, 1, 2, 80, 80, 90
홍길동, 1, 3, 100, 100, 100
이자바, 2, 1, 70, 90, 70
안자바, 2, 2, 60, 100, 80

1. Anonymous Class 활용

  • implements하면 그 안의 메서드 다 override 해야하는데 여기서는 특정 부분만 비교해서 정렬하고 싶어서 따로 클래스를 만들지 않고, 익명클래스 사용
package _api.util.stream.nestedclass;

import java.util.Arrays;
import java.util.Comparator;

public class AnonymousMain3 {

	public static void main(String[] args) {
		
		//배열의 정렬
		String[] arr = {"SM", "JYP", "스타쉽", "YG", "나무엑터스"};
		
		//배열의 내림차순 정렬
		//Arrays.sort(배열, 비교를 위한 Comparator<T> 인터페이스를 구현한 클래스의 객체);
		//를 호출해야 함
		
		Arrays.sort(arr, new Comparator<String>() {
			@Override
			public int compare(String o1, String o2) {
				//이 내부에는 공부를 해야 쓸 수 있음
				//지금은 여기까지 틀을 만들어내는 게 중료
				return o2.compareTo(o1); //내림차순, java.lang.String의 compareTo 메서드
			}
		});
		
		//배열의 요소를 빠르게 확인
		System.out.println(Arrays.toString(arr));
		
		//하나씩 확인
		for(String app : arr) {
			System.out.println(app);
		}
		
	}
}
  • 따로 클래스를 만들어 주지 않고, new Comparator<String>() 으로 생성
import java.util.Arrays;
import java.util.Comparator;
 
public class Test {
	
	public static void main(String[] args) {
		
		MyInteger[] arr = new MyInteger[10];
		
		// 객체 배열 초기화 (랜덤 값으로) 
		for(int i = 0; i < 10; i++) {
			arr[i] = new MyInteger((int)(Math.random() * 100));
		}
 
		// 정렬 이전
		System.out.print("정렬 전 : ");
		for(int i = 0; i < 10; i++) {
			System.out.print(arr[i].value + " ");
		}
		System.out.println();
---------------------------------------------------------------------------------		
		Arrays.sort(arr, comp);
        // MyInteger에 대한 Comparator을 구현한 익명객체를 넘겨줌
---------------------------------------------------------------------------------        
		// 정렬 이후
		System.out.print("정렬 후 : ");
		for(int i = 0; i < 10; i++) {
			System.out.print(arr[i].value + " ");
		}
		System.out.println();
	}
 
---------------------------------------------------------------------------------	
	static Comparator<MyInteger> comp = new Comparator<MyInteger>() {
		
		@Override
		public int compare(MyInteger o1, MyInteger o2) {
			return o1.value - o2.value;
		}
	};
}
--------------------------------------------------------------------------------- 
class MyInteger {
	int value;
	
	public MyInteger(int value) {
		this.value = value;
	}
	
}
  • 위와 같은데 그냥 변수에 담아줌
//compare 메서드를 구현한 클래스 작성 - 내림차순
	class IntegerDecComparator implements Comparator<Integer> {
		public int compare(Integer n1, Integer n2) {
			return (n1 > n2) ? 1 : (n1 < n2) ? -1 : 0;
		}
	}

//IntegerDecComparator 인스턴스 생성
// 정수의 내림차순으로 순서매기기를 수행하는 comparator
	final IntegerDecComparator INT_DEC_COMP = new IntegerDecComparator();
	BinTree<Integer, Data> tree = new BinTree<Integer, Data>(INT_DEC_COMP);
  • Comparator가 외부 클래스(BinTree)에서 사용되지만 다른 클래스에서는 사용되지 않는 경우
  • 이런 경우 익명 내부 클래스를 사용하여 간편하게 Comparator를 구현하고, BinTree 클래스 내에서만 사용하는 것이 적합
  • Tree의 quiz3 코드
궁금증? 위의 코드와 아래의 코드의 순서(인스턴스를 생성의 순)가 다른 이유
- 두 코드에서 사용되는 Comparator의 목적과 형태가 다르기 때문임.
- 위의 코드에서는 Comparator를 구현하고 객체를 생성하여 다른 클래스에 전달하는 방식을 사용
- 아래의  코드에서는 Comparator를 구현한 클래스를 정적 내부 클래스로 선언하고 있으며, 해당 클래스의 인스턴스를 정적 상수로 선언하여 사용

2023.06.25 - [Study/Data Structure&Algorithm] - [자료구조&알고리즘] 트리, 이진 트리, 이진 검색 트리

 

2. 클래스 생성

  • Comparator를 구현하는 별도의 클래스를 정의하여 사용하는 방식
  • 정적 내부 클래스로 Comparator를 구현
  • Comparator가 다른 클래스 내에서만 사용되고, 해당 클래스에서만 관리되는 경우에 주로 사용
	//오름차순으로 정렬하기 위한 comparator
		public static final Comparator<PhyscData> HEIGHT_ORDER = new HeightOrderComparator();
		//Comparator 인터페이스를 구현한 HeightOrderComparator 인스턴스 생성
        
		private static class HeightOrderComparator implements Comparator<PhyscData> {
			public int compare(PhyscData d1, PhyscData d2) {
				return (d1.height > d2.height) ? 1 :
					   (d1.height < d2.height) ? -1 : 0;
			}
		}
	}
	
	public static void main(String[] args) {
		Scanner scan = new Scanner(System.in);
		PhyscData[] x = { //키의 오름차순으로 정렬되어 있음
				new PhyscData("이나령", 162, 0.3),
				new PhyscData("유지훈", 168, 0.4),
				new PhyscData("김한결", 169, 0.8),
				new PhyscData("홍준기", 171, 1.5),
				new PhyscData("전서현", 173, 0.7),
				new PhyscData("이호연", 174, 1.2),
				new PhyscData("이수민", 175, 2.0),
		};
		System.out.print("몇 cm인 사람은 찾고 있나요?");
		int height = scan.nextInt();
		int result = Arrays.binarySearch(
							x, //배열 x에서
							new PhyscData("", height, 0.0), //키가 height인 요소를
							PhyscData.HEIGHT_ORDER //HEIGHT_ORDER에 의해 검색
                        	/////////////// 여기서 사용▲ ///////////////
					 );

3. 익명 클래스 vs 클래스 사용

익명 클래스 클래스
코드 길이를 줄일 수 있음 Comparator를 재사용
특정한 Comparator를 한 곳에만 사용하고자 할 경우 Comparator의 로직이 복잡할 경우

4. 스트림의 중간연산

스트림에서 Comparator의 comparing()으로 추가적인 정렬 기준(기본 정렬기준은 Comparable)을 제공

Comparator<T> 메서드

더보기
package chapter14;

import java.util.Comparator;
import java.util.stream.Stream;

public class StreamComparator {

	public static void main(String[] args) {
		Stream<Student> studentStream = Stream.of(
				new Student("이자바", 3, 300),
				new Student("김자바", 1, 200),
				new Student("안자바", 2, 100),
				new Student("박자바", 2, 150),
				new Student("소자바", 1, 200),
				new Student("나자바", 3, 290),
				new Student("감자바", 3, 180)
				);
		
		//studentStream.sorted(Comparator.comparing(Student::getBan) //1.반별 정렬
		studentStream.sorted(Comparator.comparing((Student s) -> s.getBan()) //1.반별 정렬
					 .thenComparing(Comparator.naturalOrder())) //2. 기본 정렬(총점, 내림차순)
					 .forEach(System.out::println);
	}
}

class Student implements Comparable<Student> { //기본 구현 Comparable
	String name;
	int ban;
	int totalScore;
	
	Student(String name, int ban, int totalScore) {
		this.name = name;
		this.ban = ban;
		this.totalScore = totalScore;
	}
	
	public String toString() {
		return String.format("[%s, %d, %d]", name, ban, totalScore);
	}
	
	String getName() {return name;}
	int getBan() {return ban;}
	int gettotalScore() {return totalScore;}
	
	//총점 내림차순을 기본 정렬로 함
	public int compareTo(Student s) {
		return s.totalScore - this.totalScore;
	}
} //end class
// 4.7 모든 거래 내역중에서 거래 금액의 최댓값과 최솟값을 구하라.
    //단, 최댓값은 reduce를 이용하고 최솟값은 stream의 min()을 이용하라.
    public Integer[] quiz7() {
    	Integer[] arr = new Integer[2];
    	arr[0] = transactions.stream()
    			           .map(t -> t.getValue())
    			           .reduce(Integer::max).orElse(-1);
    	arr[1] = transactions.stream()
    						 .min(Comparator.comparingInt(Transaction::getValue))
    						 .orElseThrow(RuntimeException::new).getValue();
    	return arr;
    }
  • Java Stream API에서 min 메서드를 사용할 때, 해당 메서드는 Comparator를 기반으로 최솟값을 찾습니다. Transaction::getValue를 Comparator.comparingInt에 전달함으로써 getValue 메서드를 사용하여 객체의 비교를 수행합니다. 이렇게 하는 이유는 min 메서드가 정확한 순서를 결정하기 위해서입니다.
  • 만약 map 메서드로 먼저 getValue를 호출하고 그 후에 min 메서드를 사용한다면, min은 Transaction 객체 간의 비교를 수행할 수 없게 됩니다. 왜냐하면 getValue로 얻은 값만을 가지고 비교하는 것이 아니라, Transaction 객체 자체를 비교해야 하기 때문입니다.
  • 종합적으로, Comparator.comparingInt(Transaction::getValue)은 min 메서드가 Transaction 객체를 비교할 때 사용할 비교자(Comparator)를 제공합니다. 이를 통해 최솟값을 정확하게 찾을 수 있습니다.

출처: https://github.com/MangKyu/stream-quiz, 챗 gpt

 


2022.10.25 - [프로그래밍 언어/Java] - [Java] API_ java.util (arrays - search, date, calendar, random)

 

[Java] API_ java.util (arrays - search, date, calendar, random)

JAVA API : application programming interface 미리 만들어진 기능인 라이브러리 API안에 많은 패키지들이 존재 (lang, util, IO 등) 메서드 모형 읽는 법 접근제어자 + (매개변수) + ;(결과) 반환 유형 - 클래스가

j-won950101.tistory.com

 

2023.06.07 - [Study/Data Structure&Algorithm] - [자료구조&알고리즘] 리스트 (선형, 연결(포인터, 커서), 원형, 이중 연결)

2022.10.28 - [프로그래밍 언어/Java] - [Java] Nested Class, Lambda, Stream API(ArrayList)

 

참조 블로그:

https://st-lab.tistory.com/243

 

https://poew.tistory.com/439


문제

https://school.programmers.co.kr/learn/courses/30/lessons/12915

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

 

728x90
profile

원지의 개발

@원지다

250x250