Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | |
7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 | 29 | 30 |
Tags
- synchronized
- Google OAuth
- middleware
- Spring
- OAuth 2.0
- lombok
- 일급 컬렉션
- builder
- factory
- nestjs
- spring security
- 일급 객체
- Volatile
- java
- Dependency Injection
Archives
- Today
- Total
HJW's IT Blog
[JAVA] Collections Framework 1 (Generic, ArrayList) 본문
Generic
- Type 을 포괄적으로 일단 정의하겠다
- 클래스나 메서드에서 타입을 미리 정하지 않고 외부에서 사용할 때 타입을 정하도록 하는 문법적 장치
- 왜 사용하는가 → 어떠한 클래스를 만들었는데, 거의 똑같은 클래스를 또 만들어야 한다면 중복되는 부분이 많고 비효율적이다 → 중복을 최소화 하자!
class Data{
int obj;
Data(int obj){
this.obj = obj;
}
int getObj(){ return obj; }
void printInfo(Integer obj){
System.out.println(obj.getClass().getName());
}
}
class Data2{
String obj;
Data2(String obj){
this.obj = obj;
}
String getObj(){ return obj; }
void printInfo(String obj){
System.out.println(obj.getClass().getName());
}
}
- 위와 같은 두개의 클래스가 있다고 가정해 보자
- 거의 같은 클래스이다 → 불필요한 중복이 들어가 있다
- 이럴때 제네릭을 사용한다
class Data{
Object obj;
Data(Object obj){
this.obj = obj;
}
Object getObj(){ return obj; }
void printInfo(Object obj){
System.out.println(obj.getClass().getName());
}
}
Integer foo = Integer.valueOf(100);
Data d = new Data(foo);
System.out.println(d.getObj());
d.printInfo(d);
Integer a = d.getObj();
- 위와 같이 Integer a 에 d의 값을 할당하려고 하면 오류가 발생한다 → 형변환이 일어났기 때문
- Integer a = (Integer) d.getObj(); → 컴파일 단계에서 오류 x
- 하지만 타입 안정성이 떨어지며 명시적인 타입 캐스팅이 필요하다
- 그렇기에 다음 방법을 사용하여 컴파일 단계에서 오류를 잡아낼 수 있다
class Data<T>{
T obj;
Data(T obj){
this.obj = obj;
}
T getObj(){ return obj; }
void printInfo(){
System.out.println(obj.getClass().getName());
}
}
매개변수를 여러개 받는 제네릭 클래스
class Data<T1, T2>{
private T1 foo;
private T2 bar;
public void set(T1 t1, T2 t2){
foo = t1;
bar = t2;
}
public void printInfo(){
System.out.println(foo.getClass().getName() + " " + bar.getClass().getName());
}
public void printInfo2(){
System.out.println(foo + " : " + bar);
}
}
제네릭 클래스의 매개변수 타입 제한
- 예를 들어 Number (Int, Double, …) 만으로 제한하고 싶다면 다음과 같이 선언하면 된다
class Data<T extends Number>{
private T foo;
Data(T t){
foo = t;
}
public T getFoo(){
return foo;
}
public String toString(){
return foo.getClass().getName();
}
}
- 위와 같이 선언을 한 뒤, 객체를 String 으로 생성하게 되면 오류 발생
- Data<String> d = new Data<String>();
제네릭과 배열리스트
public class Main {
public static void main(String[] args) {
AnimalList<Birds> animals = new AnimalList<>();
animals.set(new Eagle());
animals.set(new Owl());
animals.set(new Dog());
}
}
abstract class Birds { abstract void cry(); }
class Eagle extends Birds{
void cry(){
System.out.println("Eagle");
}
}
class Owl extends Birds{
void cry(){
System.out.println("Owl");
}
}
class Dog{
void cry(){
System.out.println("Dog");
}
}
class AnimalList<T>{
ArrayList<T> alist = new ArrayList<>();
void set(T obj){
alist.add(obj);
}
T get(int idx){
return alist.get(idx);
}
int getSize(){
return alist.size();
}
}
- 위 예시는 제네릭을 활용한 클래스 생성 예시이다
- 추상 클래스 Birds 를 상속받는 클래스 Eagle, Owl 이 있고, 상속받지 않는 클래스 Dog 가 있다
- AnimalList 클래스는 지정된 타입의 객체들만 저장할 수 있는 리스트를 구현한다
- 그렇기 때문에 Bird를 상속받지 않은 Dog 를 추가하려 하였을 때, 컴파일 에러가 발생한다
제너릭 메서드
- 메서드에 대해서 제너릭 문법 적용
- 메서드의 선언부에 타입 파라미터를 선언하여 메서드의 매개변수와 리턴 타입을 지정 타입으로
- 메서드를 호출할 때 매개변수 타입과 리턴 타입을 결정하겠다는 의미
class DataCheck {
public static <T> T showData(T data){
if(data instanceof Integer){
System.out.println("INT");
}else if(data instanceof String){
System.out.println("STRING");
}
return data;
}
}
제너릭 메서드 제약 걸기
public static <T extends CharSequence> void showFirstChar(T param){
System.out.println(param.charAt(0));
}
ArrayList
- 배열과 비슷하지만, 더 좋아진, 업그레이드 된 배열
- List 가 내부적으로 배열을 이용한다
- 배열과 다르게, 길이가 가변적 이다
- null 값이 허용되며, 요소가 추가된 순서를 유지한다
- List 는 인터페이스 이기 때문에 객체를 생성할 수 없다 → 이를 상속 구현한 구현체(ArrayList) 를 통해 객체를 생성하고 사용한다
주요 메서드
- add() : 요소 추가
- remove() : 요소 삭제
- size() : 길이
- set() : 수정
- get() : 출력
- Collections.sort( 컬렉션 명) : 정렬 (collections.reverseOrder() 사용시 역방향)
- clear() : 모든 요소 삭제
- contains(Object o) : 특정 요소가 있는지 확인
- isEmpty() : 리스트가 비어있는지
- indexOf(Object o) : 특정 요소의 첫 인덱스 반환
- lastIndexOf(Object o) : 특정 요소가 여러개 있을때 마지막 인덱스 반환
- Collections.freqency(컬렉션명, 요소명) : 특정 요소가 포함된 개수
- Collections.frequency(list, 100)
- list1.retainAll(list2) : 교집합만 남겨두고 삭제
사용자 정의 객체 정렬
- Comparable 인터페이스나 Comparator 인터페이스, 람다 표현식을 사용해야 한다
- Comparable 의 경우, 자연 순서를 정의한다,
- 객체 자체에서 정렬 기준을 제공해야 하며, compareTo 메서드를 구현해야 한다
class Person implements Comparable<Person> {
String name;
int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// compareTo 메서드 구현 (나이 순으로 정렬)
@Override
public int compareTo(Person other) {
return this.age - other.age; // 나이가 적을수록 앞에 오도록
}
}
public class Main {
public static void main(String[] args) {
ArrayList<Person> people = new ArrayList<>();
people.add(new Person("Kevin", 25));
people.add(new Person("Alice", 22));
people.add(new Person("Bob", 30));
Collections.sort(people); // Comparable에 정의된 정렬 기준에 따라 정렬
}
}
- Comparator 의 경우, 외부에서 정렬기준을 정의하고, compare 메서드를 통해 두 객체를 비교한다
class Person {
String name;
int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
class NameComparator implements Comparator<Person> {
@Override
public int compare(Person p1, Person p2) {
return p1.name.compareTo(p2.name); // 이름 순으로 정렬
}
}
public class Main {
public static void main(String[] args) {
ArrayList<Person> people = new ArrayList<>();
people.add(new Person("Kevin", 25));
people.add(new Person("Alice", 22));
people.add(new Person("Bob", 30));
Collections.sort(people, new NameComparator()); // NameComparator를 사용하여 이름순으로 정렬
}
}
- 람다 표현식
- Collections.sort(people, (p1, p2) -> p2.age - p1.age); 혹은,
Comparator<String> cl = (s1, s2) -> s1.length() - s2.length();
Arrays.sort(people, cl);
'JAVA' 카테고리의 다른 글
Spring 없이 의존성 관리와 팩토리 패턴 구현하기 (0) | 2025.01.24 |
---|---|
JVM 은 어떻게 동작하는가 (1) | 2025.01.24 |
JAVA Volatile 키워드와 멀티쓰레드 (0) | 2025.01.13 |
[JAVA] Collections Framework (Linked List, Stack, Queue, Set) (1) | 2024.10.13 |
[JAVA] 객체와 클래스 (1) | 2024.10.13 |