Loading the catalog…
Loading the catalog…
Java 기초 문법과 객체지향을 익혔다면 다음으로 자주 만나는 개념이 제네릭 , 컬렉션 , 람다 , 스트림 이다. 이 기능들은 데이터를 타입 안전하게 보관하고, 반복되는 코드를 줄이며, 컬렉션을 선언적으로 처리하는 데 사용된다. 1. 제네릭은 타입을 매개변수로 받는다 타입마다 비슷한 클래스를 따로 만든다고 생각해보자. class StringBox { String value; } class IntegerBox { Integer value; } 동작은 같은데 타입만 다르다. 제네릭을 사용하면 하나의 클래스로 만들 수 있다. class Box<T> { private T value; public void set(T value) { this.value = value; } public T get() { return value; } } Box<String> textBox = new Box<>(); textBox.set("response"); Box<Integer> countBox = new Box<>(); countBox.set(10); 컴파일러가 타입을 확인하므로 잘못된 값을 넣는 실수를 줄일 수 있다. 2. 제네릭 메소드 메소드 단위로도 타입 매개변수를 사용할 수 있다. public static <T> void printValue(T value) { System.out.println(value); } printValue(100); printValue("hello"); printValue(true); 동작은 같고 타입만 달라지는 경우 코드 중복을 줄일 수 있다. 3. Wrapper 클래스 제네릭 타입에는 기본 자료형을 직접 넣을 수 없다. // List<int>는 사용할 수 없음 대신 기본 자료형에 대응하는 Wrapper 클래스를 사용한다. 기본 자료형 Wrapper int Integer long Long double Double boolean Boolean char Character Java는 기본 자료형과 Wrapper 사이를 자동으로 변환해주는 오토박싱/언박싱 을 지원한다. Integer count = 10; // autoboxing int value = count; // unboxing 단, Wrapper는 객체이므로 null 이 될 수 있다. Integer count = null; // int value = count; // NullPointerException 가능 4. ArrayList : 가장 자주 쓰는 리스트 크기가 고정된 배열과 달리 ArrayList 는 요소를 동적으로 추가하거나 제거할 수 있다. import java.util.ArrayList; import java.util.List; List<String> endpoints = new ArrayList<>(); endpoints.add("/users"); endpoints.add("/orders"); System.out.println(endpoints.get(0)); System.out.println(endpoints.size()); System.out.println(endpoints.contains("/orders")); endpoints.set(0, "/members"); endpoints.remove("/orders"); 가능하면 변수 타입은 구현체보다 인터페이스인 List 로 선언하면 구현 교체가 쉬워진다. List<String> items = new ArrayList<>(); 5. LinkedList : 링크로 연결된 리스트 import java.util.LinkedList; LinkedList<String> queue = new LinkedList<>(); queue.add("job-1"); queue.addFirst("urgent-job"); queue.addLast("job-2"); LinkedList 를 단순히 "삽입과 삭제가 언제나 빠르다"고 외우면 정확하지 않다. 특정 노드 위치를 이미 알고 있다면 연결 변경은 빠르지만, 인덱스로 원하는 위치를 찾는 데는 O(n) 이 걸릴 수 있다. 일반적인 순차 데이터 처리에서는 ArrayList 가 더 자주 선택된다. 큐/덱 용도라면 ArrayDeque 가 더 적합한 경우도 많다. 6. HashSet : 중복 없는 집합 import java.util.HashSet; import java.util.Set; Set<String> roles = new HashSet<>(); roles.add("ADMIN"); roles.add("USER"); roles.add("ADMIN"); System.out.println(roles.size()); // 2 HashSet 은 중복 요소를 허용하지 않고 기본적으로 반복 순서를 보장하지 않는다. System.out.println(roles.contains("ADMIN")); roles.remove("USER"); 중복 제거, 포함 여부 검사에 잘 맞는다. 7. HashMap : Key와 Value로 데이터 저장 import java.util.HashMap; import java.util.Map; Map<String, Integer> requestCount = new HashMap<>(); requestCount.put("/users", 120); requestCount.put("/orders", 85); System.out.println(requestCount.get("/users")); System.out.println(requestCount.containsKey("/orders")); System.out.println(requestCount.size()); HashMap 은 키의 중복을 허용하지 않지만 값은 중복될 수 있다. 같은 키로 다시 put() 하면 기존 값이 교체된다. 또한 일반 HashMap 은 순서를 보장하지 않는다. 8. Iterator 컬렉션 요소를 순차적으로 방문하기 위한 표준 인터페이스다. import java.util.Iterator; import java.util.List; List<String> jobs = new ArrayList<>(); jobs.add("sync"); jobs.add("backup"); Iterator<String> iterator = jobs.iterator(); while (iterator.hasNext()) { String job = iterator.next(); System.out.println(job); } 반복 중 안전하게 현재 요소를 제거해야 할 때도 사용할 수 있다. while (iterator.hasNext()) { String job = iterator.next(); if (job.equals("backup")) { iterator.remove(); } } 9. 익명 클래스 한 번만 사용할 구현체를 별도 클래스 파일 없이 즉석에서 만들 수 있다. Runnable task = new Runnable() { @Override public void run() { System.out.println("작업 실행"); } }; 작은 구현에는 편리하지만 코드가 길어질 수 있다. 함수형 인터페이스라면 람다로 더 간결하게 표현할 수 있다. 10. 람다식 람다는 이름 없는 동작을 값처럼 전달하는 문법이다. (x, y) -> x + y Java의 람다는 아무 메소드나 대신하는 것이 아니라 함수형 인터페이스 의 구현으로 사용된다. @FunctionalInterface interface Calculator { int calculate(int x, int y); } Calculator add = (x, y) -> x + y; Calculator multiply = (x, y) -> x * y; System.out.println(add.calculate(2, 3)); // 5 System.out.println(multiply.calculate(2, 3)); // 6 함수형 인터페이스는 추상 메소드가 하나인 인터페이스다. 11. 스트림으로 컬렉션 처리하기 스트림은 배열이나 컬렉션 데이터를 필터링, 변환, 집계 하는 흐름을 선언적으로 표현한다. List<Integer> responseTimes = List.of(120, 80, 310, 95, 220); responseTimes.stream() .filter(time -> time >= 200) .map(time -> time / 10) .forEach(System.out::println); 흐름을 나누면 다음과 같다. 데이터 소스 ↓ filter: 필요한 데이터만 선택 ↓ map: 다른 값으로 변환 ↓ forEach: 최종 처리 결과를 새 리스트로 받기 List<Integer> slowTimes = responseTimes.stream() .filter(time -> time >= 200) .toList(); 원본 리스트를 직접 변경하지 않고 새로운 결과를 얻을 수 있다. 12. 스트림은 언제 좋은가? 반복문보다 스트림이 항상 좋은 것은 아니다. 단순한 순회나 복잡한 상태 변경이 필요한 코드에서는 반복문이 더 읽기 쉬울 수 있다. 반면 필터 → 변환 → 집계 처럼 데이터 처리 단계가 명확한 경우 스트림이 의도를 잘 드러낸다. long count = responseTimes.stream() .filter(time -> time >= 200) .count(); 코드가 짧다는 이유보다 처리 과정이 더 명확해지는지 를 기준으로 선택하면 된다. 핵심 정리 제네릭은 타입을 매개변수화해 코드 재사용성과 타입 안정성을 높인다. 기본 자료형을 제네릭에서 사용할 때는 Integer , Double 같은 Wrapper 클래스를 쓴다. ArrayList 는 일반적인 동적 리스트로 가장 자주 사용된다. HashSet 은 중복을 제거하고, HashMap 은 Key-Value 구조로 데이터를 저장한다. HashMap 은 키 중복은 허용하지 않지만 값 중복은 가능하다. Iterator 는 컬렉션을 순회하며 반복 중 요소를 제거할 때 유용하다. 람다는 함수형 인터페이스를 간결하게 구현한다. 스트림은 컬렉션의 필터링·변환·집계를 선언적으로 표현한다.
What RADAR observed and classified to build this opportunity. It is what the source published, not a verification that the offer is still active.
Java 제네릭, 컬렉션, 람다와 스트림 핵심 정리. Java 기초 문법과 객체지향을 익혔다면 다음으로 자주 만나는 개념이 제네릭 , 컬렉션 , 람다 , 스트림 이다. 이 기능들은 데이터를 타입 안전하게 보관하고, 반복되는 코드를 줄이며, 컬렉션을 선언적으로 처리하는 데 사용된다. 1. 제네릭은 타입을 매개변수로 받는다 타입마다 비슷한 클래스를 따로 만든다고 생각해보자. class StringBox { String value; } class IntegerBox { Integer value; } 동작은 같은데 타입만 다르다. 제네릭을 사용하면 하나의 클래스로 만들 수 있다. class Box { private T value; public void set(T value) { this.value = value;…
Open source