Comparable: 비교할 수 있는
Comparator: 비교자
package generic;
import java.awt.Button;
import java.util.ArrayList;
import java.util.Comparator;
public class Test {
public static void main(String[] args) {
int []arr1= {1,2,3}; // homogeneous collection
String []arr2= {"hello","hi","bye"};// homogeneous collection
Object []arr3= new Object[3];//heterogeneous collection
arr3[0]="hello";
arr3[1]=new Test();
arr3[2]=1;
ArrayList<Integer> list=new ArrayList<>();//homogeneous collection
list.add(1);
list.add(new Integer(20));
list.add(3);
System.out.println(list);
list.sort(( o1, o2) -> o2-o1 );//내림차순
System.out.println(list);
ArrayList<String> strList=new ArrayList<>();
strList.add("전은수");
strList.add("홍길동");
strList.add("이영애");
System.out.println(strList);
strList.sort((o1,o2)->o1.compareTo(o2));//오름차순
System.out.println(strList);
}
//
}
if가 있으면 else가 있어야하는게 sercure 코드의 원칙!
package generic;
import java.util.ArrayList;
import java.util.Collections;
public class Test2 {
public static void main(String[] args) {
ArrayList<Member> list=new ArrayList<>();
list.add(new Member("전은수",38));
list.add(new Member("홍길동",20));
list.add(new Member("이영애",45));
list.add(new Member("전은수",20));
System.out.println(list);
//나이 오름차순 정렬
list.sort((o1,o2)->o1.getAge()-o2.getAge());
System.out.println(list);
//이름 오름차순 정렬
list.sort((o1,o2)->o1.getName().compareTo(o2.getName()));
System.out.println(list);
Collections.sort(list);
System.out.println(list);
}
}
class Member implements Comparable<Member>{
private String name;
private int age;
public Member(String name, int age) {
//super();
setName(name);
setAge(age);
}
public Member() {
super();
// TODO Auto-generated constructor stub
}
public String getName() {
return name;
}
public void setName(String name) {
if(name!=null) {
this.name = name;
}else {
System.out.println("null은 불가");
}
}
public int getAge() {
return age;
}
public void setAge(int age) {
if(age>0 && age <=120) {
this.age = age;
}else {
System.out.println("나이는 0보다 크고 120보다 작아야 합니다");
}
}
@Override
public String toString() {
return "Member [name=" + name + ", age=" + age + "]";
}
@Override
public int compareTo(Member o) {
// 나이가 어린 직원부터 이름 오름차순
if(this.age != o.getAge()) {
return this.age-o.getAge();
}
return this.name.compareTo(o.getName());
}
}
위의 코드에서는
@Override
public int compareTo(Member o) {
// 나이가 어린 직원부터 이름 오름차순
if(this.age != o.getAge()) {
return this.age-o.getAge();
}
return this.name.compareTo(o.getName());
}
compareTo메서드의 역할
Comparable<T> 인터페이스를 구현하면 객체를 자연 정렬할 수 있다.
compareTo(Member o) 메서드를 구현하여 기본 정렬을 한것이다.
위의 코드에서는
1. 나이를 기준으로 오름차순 기준 정렬
2. 나이가 같으면 이름을 기준으로 오름차순 정렬했다.
this.age - o.getAge()
두 객체의 age차이를 반환한다.
- this.age > o.getAge() → 양수 (this가 더 크므로 뒤로 감)
- this.age < o.getAge() → 음수 (this가 더 작으므로 앞으로 감)
age가 같으면 naem을 사전순으로 정렬
잘못된 compare 방지
package generic;
import java.util.ArrayList;
import java.util.Collections;
public class Test2 {
public static void main(String[] args) {
ArrayList<Member> list=new ArrayList<>();
list.add(new Member("전은수",-2147483647));
list.add(new Member("홍길동",20));
list.add(new Member("이영애",45));
//list.add(new Member("전은수",20));
//System.out.println(list);
//나이 오름차순 정렬
// list.sort((o1,o2)->o1.getAge()-o2.getAge());
// System.out.println(list);
//이름 오름차순 정렬
// list.sort((o1,o2)->o1.getName().compareTo(o2.getName()));
// System.out.println(list);
Collections.sort(list);
System.out.println(list);
}
}
class Member implements Comparable<Member>{
private String name;
private int age;
public Member(String name, int age) {
//super();
setName(name);
setAge(age);
}
public Member() {
super();
// TODO Auto-generated constructor stub
}
public String getName() {
return name;
}
public void setName(String name) {
if(name!=null) {
this.name = name;
}else {
System.out.println("null은 불가");
}
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age=age;
// if(age>0 && age <=120) {
// this.age = age;
// }else {
// System.out.println("나이는 0보다 크고 120보다 작아야 합니다");
// }
}
@Override
public String toString() {
return "Member [name=" + name + ", age=" + age + "]";
}
@Override
public int compareTo(Member o) {
// 나이가 어린 직원부터 이름 오름차순
if(this.age > o.getAge()) {
return 1;
}else if (this.age < o.getAge()) {
return -1;
}else {
return this.name.compareTo(o.getName());
}
}
}
위의 코드에서
@Override
public int compareTo(Member o) {
// 나이가 어린 직원부터 이름 오름차순
// 밑의 코드 밑줄
if(this.age > o.getAge()) {
return 1;
// 밑의 코드 밑줄
}else if (this.age < o.getAge()) {
return -1;
// 밑의 코드 밑줄
}else {
return this.name.compareTo(o.getName());
}
Generic 심화
1)성능향상을 위한 제네릭
- 타입 체크 x
- 타입 캐스팅 x
package generic;
import java.util.ArrayList;
import java.util.List;
public class Test3 {
public static void main(String[] args) {
List<A> list=new ArrayList<>();
// list.add("java");
// list.add(new String("java"));
// list.add(new StringBuilder("java"));
// list.add(1);
list.add(new A());
list.add(new B());
}
}
class A{}
class B extends A{}
class Biz{
public static void service(List<A> list) {
for(A o:list) {
//중요한 일...
// if(타입 체킹) {
// 타입 캐스팅
// }
}
}
}
제네릭을 쓰면 타입 안정성이 보장된다.
List<A>에는 A타입만 들어갈 수 있다.
List<A> list = new ArrayList<>();
list.add(new A());
list.add(new B()); // B는 A를 상속받았으니까 가능!
만일 list.add("java"), list.add(1)같은걸 추가하면? 컴파일 에러!
왜냐면 list<A>에는 A의 자식들만 넣을 수 있다.
2. extends 와일드 카드 사용이 필요한 경우
package generic;
import java.util.ArrayList;
import java.util.List;
public class Test3 {
public static void main(String[] args) {
List<A> list=new ArrayList<>();
// list.add("java");
// list.add(new String("java"));
// list.add(new StringBuilder("java"));
// list.add(1);
list.add(new A());
list.add(new B());
List<B> list2=new ArrayList<>();
list2.add(new B());
Biz.service(list2);//컴파일 에러 발생
}
}
class A{}
class B extends A{}
class Biz{
public static void service(List<A> list) {
for(A o:list) {
//중요한 일...
// if(타입 체킹) {
// 타입 캐스팅
// }
}
}
}
문제 발생하는 코드
List<B> list2 = new ArrayList<>();
list2.add(new B());
Biz.service(list2); // ❌ 컴파일 에러 발생!!
왜? Biz.service(List<A> list) 메서드는 A 타입의 리스트만 받을 수 있다.
그런데 List<B>는 List<A>의 서브타입이 아니다.
그렇면 어떻게 해결? 💡(유레카!)
class Biz {
public static void service(List<? extends A> list) {
// A를 상속받은 모든 타입(B 포함) 사용 가능!
}
}
?extends A 사용, 그러면 List<B>받을 수 있다.
3. 해결 코드
package generic;
import java.util.ArrayList;
import java.util.List;
public class Test3 {
public static void main(String[] args) {
List<A> list=new ArrayList<>();
// list.add("java");
// list.add(new String("java"));
// list.add(new StringBuilder("java"));
// list.add(1);
list.add(new A());
list.add(new B());
List<B> list2=new ArrayList<>();
list2.add(new B());
Biz.service(list2);
}
}
class A{}
class B extends A{}
class Biz{
public static void service(List<? extends A> list) {
for(A o:list) {
//중요한 일...
// if(타입 체킹) {
// 타입 캐스팅
// }
}
}
}
4. super와일드 카드( 사용이 필요한 경우)
package generic;
import java.util.ArrayList;
import java.util.List;
public class Test3 {
public static void main(String[] args) {
List<A> list=new ArrayList<>();
// list.add("java");
// list.add(new String("java"));
// list.add(new StringBuilder("java"));
// list.add(1);
list.add(new A());
list.add(new B());
List<B> list2=new ArrayList<>();
list2.add(new B());
Biz.service(list);//컴파일 에러
}
}
class A{}
class B extends A{}
class Biz{
public static void service(List<B> list) {
for(A o:list) {
//중요한 일...
// if(타입 체킹) {
// 타입 캐스팅
// }
}
}
}
반대로, B 타입을 포함한 상위 타입을 다 받고 싶다면?
class Biz {
public static void service(List<? super B> list) {
// B의 부모 타입(A 포함) 리스트를 받을 수 있음!
}
}
이렇게 하면 List<A>도 받을 수 있다. (A가 B의 부모니까)
5. 해결 코드
package generic;
import java.util.ArrayList;
import java.util.List;
public class Test3 {
public static void main(String[] args) {
List<A> list=new ArrayList<>();
// list.add("java");
// list.add(new String("java"));
// list.add(new StringBuilder("java"));
// list.add(1);
list.add(new A());
list.add(new B());
List<B> list2=new ArrayList<>();
list2.add(new B());
Biz.service(list);
}
}
class A{}
class B extends A{}
class Biz{
public static void service(List<? super B> list) {
for(A o:list) {//B의 super가 꼭 A만이라는 보장이 안됨
//중요한 일...
// if(타입 체킹) {
// 타입 캐스팅
// }
}
}
}
6. method generic
제네릭을 메서드에서 직접 정의할 수 있다.
package generic;
import java.util.ArrayList;
import java.util.List;
public class Test3 {
public static void main(String[] args) {
List<A> list=new ArrayList<>();
// list.add("java");
// list.add(new String("java"));
// list.add(new StringBuilder("java"));
// list.add(1);
list.add(new A());
list.add(new B());
List<B> list2=new ArrayList<>();
list2.add(new B());
Biz.service(list);
Biz.service(1);
}
}
class A{}
class B extends A{}
class Biz{
public static <T> void service(T list) {
for(A o:list) {
//중요한 일...
// if(타입 체킹) {
// 타입 캐스팅
// }
}
}
}
class Biz {
public static <T> void service(T list) {
for (A o : list) { // ❌ 컴파일 에러!
// 중요한 일...
}
}
}
문제 발생!!!!!!
- <T>는 어떤 타입이든 받을 수 있음 (즉, List<A>도, int도 가능)
- 그런데 for(A o : list)에서 T가 컬렉션인지 아닌지 모름!
- 만약 Biz.service(1);을 호출하면? 1은 List<T>가 아니라서 for문에서 컴파일 에러 발생!
이걸 고치려면?
- T를 리스트로 제한해야 함 → <T> void service(List<T> list)
7. 정제된 메서드 -> 올바른 코드
package generic;
import java.util.ArrayList;
import java.util.List;
public class Test3 {
public static void main(String[] args) {
List<A> list=new ArrayList<>();
// list.add("java");
// list.add(new String("java"));
// list.add(new StringBuilder("java"));
// list.add(1);
list.add(new A());
list.add(new B());
List<B> list2=new ArrayList<>();
list2.add(new B());
Biz.service(list);
}
}
class A{}
class B extends A{}
class Biz{
public static <T> void service(List<T> list) {
for(T o:list) {
//중요한 일...
// if(타입 체킹) {
// 타입 캐스팅
// }
}
}
}
class Biz {
public static <T> void service(List<T> list) {
for (T o : list) {
// 중요한 일...
}
}
}
이제 어떻게 개선됐냐면?
✅ T가 반드시 List 타입이어야 함 → Biz.service(1); 같은 호출이 불가능!
✅ List<A>, List<B> 다 받을 수 있음!
✅ for (T o : lis
t)에서 안전하게 리스트의 요소를 순회 가능!
⛄결론
- ? extends A → A를 상속받은 애들만!
- ? super B → B의 부모 타입들만!
- <T> → 메서드가 알아서 타입을 정하게!
제네릭 쓰면 안전하고, 유연하고, 깔끔한 코드가 완된다.
🔐 if가 있으면 else가 있어야하는게 sercure 코드의 원칙!
Collection은 list와 set으로 나눌 수 있다.
컬렉션 API란?
여러 개의 데이터를 효율적으로 저장하고 관리할 수 있도록 제공하는 기능(라이브러리)
컬렉션 API의 대표적인 자료구조
1. List(리스트)
- 순서가 있는데이터의 모음(ArrayList, LinkedList)
- 중복된 값 허용
- ["사과", "바나나", "체리", "바나나"]
2. Set(집합)
- 순서가 없고, 중복 허용 x ( HashSet, TreeSet)
- 중복된 값 허용
- {"사과", "바나나", "체리"} (중복된 "바나나"가 자동 제거됨)
3. Map(맵, 딕셔너리)
- Key-Value 형태로 데이터를 저장한느 구조(HashMap, TreeMap)
- 키(key)는 중복될 수 없지만, 값(value)는 중복될 수 있다.
{
"이름": "홍길동",
"나이": 25,
"직업": "개발자"
}
Collections
- 정적 메서드로 구성된 클래스, 컬렉션을 조작하거나 반환하는 기능을 제공
Test
package collections;
import java.util.ArrayList;
import java.util.HashSet;
public class Test {
public static void main(String[] args) {
ArrayList<String> list=new ArrayList<>();
list.add("Apple");
list.add("Banana");
list.add("Orange");
list.add("Apple");
System.out.println(list.size());
System.out.println(list);
HashSet<Object> set=new HashSet<>();
String s1=new String("Apple");
set.add(s1);
set.add("Banana");
set.add("Orange");
String s2=new String("Apple");
set.add(s2);
set.add(new Object());
set.add(new Object());
Member m1=new Member("전은수",38);
Member m2=new Member("전은수",38);
set.add(m1);
set.add(m2);
System.out.println(set.size());
System.out.println(set);
System.out.println(m1.hashCode());
System.out.println(m2.hashCode());
}
}
무엇을 한 코드인가? ArrayList & HashSet 기본 테스트
- ArrayList<String> -> 중복 허용하는 리스트
- HashSet<Object> -> 중복을 허용하지 않는 집합
📍 포인트
ArrayList는 같은 값이 들어가도 개수 그래로 유지
HashSet은 중복 제거 기능있어서 같은 값 들어가면 하나만 저장
package collections;
public class Member implements Comparable<Member>{
private String name;
private int age;
public Member(String name, int age) {
//super();
setName(name);
setAge(age);
}
public Member() {
super();
// TODO Auto-generated constructor stub
}
public String getName() {
return name;
}
public void setName(String name) {
if(name!=null) {
this.name = name;
}else {
System.out.println("null은 불가");
}
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age=age;
// if(age>0 && age <=120) {
// this.age = age;
// }else {
// System.out.println("나이는 0보다 크고 120보다 작아야 합니다");
// }
}
@Override
public String toString() {
return "Member [name=" + name + ", age=" + age + "]";
}
@Override
public int compareTo(Member o) {
// 나이가 어린 직원부터 이름 오름차순
if(this.age > o.getAge()) {
return 1;
}else if (this.age < o.getAge()) {
return -1;
}else {
return this.name.compareTo(o.getName());
}
}
@Override
public int hashCode() {
// TODO Auto-generated method stub
return name.hashCode()+age;
}
@Override
public boolean equals(Object obj) {
// TODO Auto-generated method stub
return this.hashCode()==obj.hashCode();
}
}
무엇을 한 코드인가? 정렬 및 중복 비교용 객체
- name과 age 필드를 갖는 회원 클래스
- Comparable<Member>을 구현해서 정렬 가능
- hashCode()와 equals()를 오버라이딩해서 HashSet, HashMap에서 중복 체크
📌 정렬 기준:
- 나이(age) 오름차순 정렬
- 나이가 같으면 이름(name) 오름차순 정렬
ArrayList와 sort
package collections;
import java.util.ArrayList;
import java.util.Collections;
public class ArrayListTest {
public static void main(String[] args) {
ArrayList<Member> list=new ArrayList<>();
Member m1=new Member("전은수",38);
Member m2=new Member("홍길동",20);
Member m3=new Member("이영애",45);
list.add(m1);
list.add(m2);
list.add(m3);
System.out.println(list.size());
System.out.println(list);
Collections.sort(list);
System.out.println(list);
}
}
- ArrayList는 순서 유지 + 중복 허용
- Collections.sort(list)를 이용해 정렬 가능
📌 정렬 결과:
- compareTo() 로직에 따라 나이 → 이름 순으로 정렬됨
LinkedList와 sort
package collections;
import java.util.Collections;
import java.util.LinkedList;
public class LinkedListTest {
public static void main(String[] args) {
LinkedList<Member> list = new LinkedList<>();
Member m1=new Member("전은수",38);
Member m2=new Member("홍길동",20);
Member m3=new Member("이영애",45);
list.add(m1);
list.add(m2);
list.add(m3);
System.out.println(list.size());
System.out.println(list);
Collections.sort(list);
System.out.println(list);
}
}
- LinkedList는 데이터 삽입/삭제에 최적화된 리스트
- ArrayList와 똑같이 Collections.sort(list) 가능
📌 정렬 결과:
- ArrayList와 동일하게 나이 → 이름순 정렬
HashSet와 sort
package collections;
import java.util.*;
public class HashSetTest {
public static void main(String[] args) {
HashSet<Member> set = new HashSet<>();
Member m1 = new Member("전은수", 38);
Member m2 = new Member("홍길동", 20);
Member m3 = new Member("이영애", 45);
Member m4 = new Member("전은수", 38);
set.add(m1);
set.add(m2);
set.add(m3);
set.add(m4);
System.out.println(set.size());
System.out.println(set);
List<Member> list=new ArrayList<>(set);
Collections.sort(list);
System.out.println(list);
}
}
hash함수의 특징
data값이 같으면 결과값이 같다.
해쉬 값이 다르다? 데이터 값이 다르다.
위의 코드를 기반으로 특징
- 중복 제거 기능이 있어서 같은 Member가 들어가면 하나만 저장됨
- Collections.sort()를 쓰기 위해 List로 변환 후 정렬
정렬 과정
- HashSet<Member> 생성
- List<Member>로 변환
- Collections.sort(list) 실행
주의
Member 클래스의 hashCode()와 equals()를 오버라이딩해야 중복 체크 가능
TreeSet (들어가는 순간 정렬)
package collections;
import java.util.TreeSet;
public class TreeSetTest {
public static void main(String[] args) {
TreeSet<Member> set = new TreeSet<>();
Member m1 = new Member("전은수", 38);
Member m2 = new Member("홍길동", 20);
Member m3 = new Member("이영애", 45);
Member m4 = new Member("전은수", 38);
set.add(m1);
set.add(m2);
set.add(m3);
set.add(m4);
System.out.println(set.size());
System.out.println(set);
}
}
- TreeSet은 들어가는 순간 자동 정렬
- Comparable<Member> 구현 필수 (compareTo() 로직 사용)
📌 정렬 결과:
- compareTo() 로직에 따라 나이 → 이름순 정렬
HashMap
package collections;
import java.util.*;
public class HashMapTest {
public static void main(String[] args) {
Map<String, Member> map = new HashMap<>();
Member m1 = new Member("전은수", 38);
Member m2 = new Member("홍길동", 20);
Member m3 = new Member("이영애", 45);
Member m4 = new Member("전은수", 28);
map.put(m1.getName(), m1);
map.put(m2.getName(), m2);
map.put(m3.getName(), m3);
map.put(m4.getName(), m4);
System.out.println(map.size());
System.out.println(map);
}
}
- HashMap<Key, Value> 구조로 데이터를 저장
- Key는 중복 불가, Value는 중복 가능
- HashSet처럼 hashCode()와 equals()가 중요
실행 과정
- map.put(name, member) → name을 키로 사용해서 Member 저장
- 같은 name이 들어오면 덮어쓰기 (기존 값이 새로운 값으로 교체됨)
- System.out.println(map.size()) → 저장된 키 개수 출력
주의점
- m1과 m4의 name이 같아서 m4가 m1을 덮어씀
💭 다시 공부해보자..!
💭 주말에 자바 총 정리 필슈..
💭 어렵네..🥲🤯😩😬😱😵💫😵🫣🧐🤓
'💡 URECA' 카테고리의 다른 글
[URECA] 프로그래머스 입문 - 20문제 (0) | 2025.02.13 |
---|---|
[URECA] DAY14 | 자바(5) 재귀 (0) | 2025.02.13 |
[URECA] DAY 12 | 자바(4) (1) | 2025.02.11 |
[URECA] DAY11 | 자바(3) (0) | 2025.02.10 |
[URECA] DAY 10 | 자바(2) (1) | 2025.02.07 |