1. 자바 제네릭(Generic)
1.1. 제네릭이란?
제네릭(Generic)은 컴파일 시 타입을 지정하여 타입 안정성을 보장하고 코드 재사용성을 높이는 기능입니다. 클래스, 인터페이스, 메서드에서 사용할 수 있으며, 타입을 명시하지 않아도 다양한 데이터 타입을 처리할 수 있도록 해줍니다.
✅ 타입 안정성 보장: 컴파일 시 타입을 체크하여 오류를 방지할 수 있음
✅ 코드 중복 방지: 동일한 코드에서 여러 타입을 지원할 수 있음
1.2. 제네릭을 사용하지 않는 경우의 문제점
class Apple {}
class Pencil {}
class ManageApple {
private Apple apple = new Apple();
public Apple get() { return this.apple; }
public void set(Apple apple) { this.apple = apple; }
}
class ManagePencil {
private Pencil pencil = new Pencil();
public Pencil get() { return this.pencil; }
public void set(Pencil pencil) { this.pencil = pencil; }
}
새로운 상품이 추가될 때마다 새로운 관리 클래스를 만들어야 하므로 유지보수성이 떨어짐.
1.3. 제네릭을 이용한 해결 방법
class Manage<T> {
private T item;
public T get() { return this.item; }
public void set(T item) { this.item = item; }
}
✅ T를 이용하여 다양한 데이터 타입을 저장할 수 있도록 설계
✅ 코드 중복 제거 및 재사용 가능
public class Test {
public static void main(String[] args) {
Manage<Apple> appleManager = new Manage<>();
appleManager.set(new Apple());
Apple apple = appleManager.get();
Manage<Pencil> pencilManager = new Manage<>();
pencilManager.set(new Pencil());
Pencil pencil = pencilManager.get();
}
}
Manage<T> 클래스를 통해 Apple, Pencil 등 다양한 객체를 저장하고 관리할 수 있음.
1.4. 제네릭 메서드 활용
제네릭을 메서드에 적용하면 다양한 타입의 데이터를 유연하게 처리할 수 있습니다.
public class GenericMethod {
public static <T> void printArray(T[] array) {
for (T item : array) {
System.out.print(item + " ");
}
System.out.println();
}
public static void main(String[] args) {
Integer[] intArray = {1, 2, 3};
String[] strArray = {"A", "B", "C"};
printArray(intArray); // 1 2 3
printArray(strArray); // A B C
}
}
T 타입을 사용하여 다양한 배열을 처리할 수 있음.
1.5. 와일드카드(Wildcard) 활용
제네릭에서 ?를 사용하면 다양한 타입을 유연하게 처리할 수 있습니다.
import java.util.List;
public class WildcardExample {
public static void printList(List<?> list) {
for (Object obj : list) {
System.out.println(obj);
}
}
}
?를 사용하면 특정 타입이 아닌 모든 리스트를 처리 가능함
2. 컬렉션(Collection)
2.1. 컬렉션이란?
컬렉션은 데이터를 저장하고 관리하는 자료구조로, 자바에서는 Collection 인터페이스를 통해 다양한 컬렉션 클래스를 제공합니다.
✅ List: 순서가 있는 데이터 저장 (ArrayList, LinkedList 등)
✅ Set: 중복을 허용하지 않는 데이터 저장 (HashSet, TreeSet 등)
✅ Map: 키-값 쌍으로 데이터 저장 (HashMap, TreeMap 등)
2.2. List 컬렉션
ArrayList vs. LinkedList
특징 | ArrayList | LinkedList |
데이터 저장 방식 | 동적 배열 기반 | 연결 리스트 기반 |
검색 속도 | 빠름 (O(1)) | 느림 (O(n)) |
삽입/삭제 속도 | 느림 (O(n)) | 빠름 (O(1) - 중간 삽입 시) |
List<String> list = new ArrayList<>();
list.add("사과");
list.add("바나나");
System.out.println(list); // [사과, 바나나]
ArrayList는 배열 기반이라 검색이 빠르고, LinkedList는 삽입/삭제가 빠름.
2.3. Set 컬렉션
HashSet vs. TreeSet
특징 | HashSet | TreeSet |
중복 허용 | X | X |
정렬 여부 | X | O (정렬됨) |
Set<String> set = new HashSet<>();
set.add("사과");
set.add("바나나");
System.out.println(set); // [사과, 바나나]
HashSet은 중복을 허용하지 않으며, TreeSet은 정렬된 상태로 유지됨.
2.4. Map 컬렉션 상세 설명
HashMap vs. TreeMap
특징 | HashMap | TreeMap |
정렬 여부 | X | O (키 기준 정렬) |
성능 | 빠름 | 느림 |
Map<Integer, String> map = new HashMap<>();
map.put(1, "사과");
map.put(2, "바나나");
System.out.println(map.get(1)); // 사과
HashMap은 키-값 쌍을 저장하며, TreeMap은 키 기준으로 정렬된 데이터를 유지함.
3. 함수형 프로그래밍
3.1. 함수형 프로그래밍이란?
함수형 프로그래밍은 데이터와 상태 변경을 최소화하고 순수 함수(Pure Function)를 기반으로 프로그래밍하는 방식입니다.
✅ 불변성 (immutability): 데이터를 변경하지 않고 새로운 값을 반환
✅ 순수 함수 (pure function): 동일한 입력값에 대해 항상 같은 출력값을 반환
@FunctionalInterface
interface Calculator {
int calculate(int x, int y);
}
public class LambdaExample {
public static void main(String[] args) {
Calculator add = (x, y) -> x + y;
System.out.println(add.calculate(5, 3)); // 8
}
}
함수형 인터페이스를 활용한 간결한 연산 구현 가능.
4. 람다식(Lambda Expression)
4.1. 람다식이란?
람다식은 자바에서 함수형 프로그래밍을 지원하는 문법으로, 간결한 코드 블록을 작성할 수 있습니다.
✅ 익명 함수(anonymous function)로 사용할 수 있음
✅ 기존 익명 클래스보다 가독성이 뛰어남
@FunctionalInterface
interface Printer {
void print(String message);
}
public class LambdaExample {
public static void main(String[] args) {
Printer printer = message -> System.out.println(message);
printer.print("Hello, Lambda!");
}
}
Printer 인터페이스를 람다식으로 구현하여 코드 간결화
4.2. 다양한 람다식 표현
// 매개변수와 반환값이 없는 경우
() -> System.out.println("Hello World");
// 매개변수는 있고, 반환값이 없는 경우
x -> System.out.println(x);
// 매개변수와 반환값이 있는 경우
(x, y) -> x + y;
람다식을 활용하면 코드의 가독성과 유지보수성이 향상됨.
5. 스트림(stream)
5.1 스트림이란?
스트림(Stream)은 자바 8에서 추가된 기능으로, 컬렉션(배열 포함)의 저장 요소를 하나씩 참조하면서 람다식을 이용하여 처리할 수 있도록 해주는 반복자입니다.
✅ 컬렉션을 함수형으로 처리할 수 있음
✅ 내부 반복자를 사용하여 코드 가독성을 높임
✅ 원본 데이터를 변경하지 않으며, 불변성을 유지
✅ 병렬 처리를 활용하여 성능을 최적화 가능
5.2 스트림 생성 방법
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
public class StreamExample {
public static void main(String[] args) {
List<String> list = Arrays.asList("Apple", "Banana", "Cherry");
// 컬렉션을 스트림으로 변환
Stream<String> stream = list.stream();
// 스트림 요소 출력
stream.forEach(System.out::println);
}
}
stream() 메서드를 사용하여 리스트를 스트림으로 변환하고, forEach()를 통해 출력할 수 있습니다.
5.3 스트림의 주요 연산
스트림은 **중간 연산(Intermediate Operation)**과 **최종 연산(Terminal Operation)**으로 나뉩니다.
5.3.1 중간 연산 (Intermediate Operations)
✅ filter(): 특정 조건을 만족하는 요소만 걸러내는 연산
List<String> list = Arrays.asList("Apple", "Banana", "Cherry", "Blueberry");
list.stream()
.filter(s -> s.startsWith("B"))
.forEach(System.out::println); // Banana, Blueberry
✅ map(): 요소를 다른 값으로 변환하는 연산
List<String> list = Arrays.asList("Apple", "Banana", "Cherry");
list.stream()
.map(String::toUpperCase)
.forEach(System.out::println); // APPLE, BANANA, CHERRY
✅ sorted(): 요소를 정렬하는 연산
List<Integer> numbers = Arrays.asList(3, 1, 4, 1, 5, 9);
numbers.stream()
.sorted()
.forEach(System.out::println); // 1, 1, 3, 4, 5, 9
✅ distinct(): 중복 요소 제거
List<String> list = Arrays.asList("Apple", "Banana", "Apple", "Cherry");
list.stream()
.distinct()
.forEach(System.out::println); // Apple, Banana, Cherry
5.3.2 최종 연산 (Terminal Operations)
✅ forEach(): 모든 요소를 반복하면서 처리
List<String> list = Arrays.asList("Apple", "Banana", "Cherry");
list.stream()
.forEach(System.out::println);
✅ count(): 요소의 개수를 반환
long count = list.stream().count();
System.out.println("개수: " + count);
✅ reduce(): 요소를 하나의 결과로 축소
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
int sum = numbers.stream().reduce(0, Integer::sum);
System.out.println("합계: " + sum); // 15
5.4 병렬 스트림(Parallel Stream)
✅ 순차 처리와 병렬 처리 비교
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
public class ParallelStreamExample {
public static void main(String[] args) {
List<String> list = Arrays.asList("Apple", "Banana", "Cherry", "Blueberry");
// 순차 처리
list.stream().forEach(System.out::println);
System.out.println("----");
// 병렬 처리
list.parallelStream().forEach(System.out::println);
}
}
parallelStream()을 사용하면 멀티코어를 활용하여 병렬로 데이터를 처리할 수 있습니다.
6. 마무리
이번 강의를 통해 자바 제네릭, 컬렉션, 함수형 프로그래밍, 람다식의 개념과 활용법을 학습하였습니다.
이 글은 LG CNS AM Inspire Camp 1기 진행 중 Obsidian에 정리해뒀던 글을 블로그에 옮긴 글입니다.
'Experience > LG CNS AM Inspire Camp 1기' 카테고리의 다른 글
[LG CNS AM Inspire Camp] 14. 스프링 프레임워크와 스프링빈, AOP (0) | 2025.02.10 |
---|---|
[LG CNS AM Inspire Camp] 13. 자바 실습 - 끝말잇기 게임 (with CLI) (0) | 2025.02.10 |
[LG CNS AM Inspire Camp] 11. 자바(Java) 상속과 다형성, 인터페이스, 예외 처리 (1) | 2025.02.10 |
[LG CNS AM Inspire Camp] 10. 자바(Java) 객체지향 프로그래밍 (배열, String, 클래스, 메서드, static) (0) | 2025.02.10 |
[LG CNS AM Inspire Camp] 9. React 컴포넌트와 상태 관리 (0) | 2025.02.10 |