이전 게시글 JAVA 기초
https://hoozy.tistory.com/entry/JAVA-%EA%B8%B0%EC%B4%88
[JAVA] 기초
이전 게시글 네트워크 2 https://hoozy.tistory.com/entry/%EB%B0%B1%EC%97%94%EB%93%9C-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-2 수많은 언어가 있지만, 저는 자바를 하기 때문에 자바에 대해서 공부한 후 나중에 나머지 언어
hoozy.tistory.com
카테고리 : 자바(심화)
컬렉션
1. List 인터페이스
- 정렬된 모든 객체 컬렉션을 저장할 수 있는 목록 데이터 전용.
- ArrayList
- 동작 배열을 제공한다.
- 표준 배열보다 느릴 수 있지만 배열에서 많은 움직임이 필요한 프로그램에서 유용
- 컬렉션에서 개체를 추가, 삭제하면 ArrayList의 크기가 자동으로 조정됩니다.
ArrayList<Integer> list = new ArrayList<>(); // 선언
for (int i = 0; i < 5; i++) {
list.add(i); // list에 데이터 추가
}
list.remove(3); // list에서 index가 3인 값 제거 -> 3 제거
- LinkedList
- 요소가 연속된 위치에 저장되지 않고 모든 요소가 데이터 부분과 주소 부분이 있는 별도의 객체에 저장.
- 포인터와 주소를 사용해서 데이터 가져옴.
- 각 요소를 노드 라고 부름
LinkedList<Integer> list = new LinkedList<>();
- ArrayList와 유사
- Vector
- 동적 배열을 제공하고, 표준 배열보다 느리지만 많은 움직임이 필요한 프로그램에서 유용
- ArrayList와 유사하지만, Vector는 동기화가 되고, ArrayList는 동기화가 되지 않음.
Vector<Integer> vec = new Vector<>();
- Stack
- 스택 클래스 모델 및 스택 데이터 구조를 구현할 때 주로 사용.
- LIFO(후입선출)을 기본 원칙으로 가짐.
Stack<String> stack = new Stack<>();
Stack.push("Hello");
Iterator<String> itr = stack.iterator(); // Stack Iterator 선언
while(itr.hasNext()) {
System.out.print(itr.next() + " "); // 출력
}
System.out.println(); // 엔터 입력
stack.pop(); // 후입 선출
itr = stack.iterator(); // 재정의
while(itr.hasNext()) {
System.out.print(itr.next() + " "); // 결과 출력
}
2. Queue 인터페이스
- 기본적으로 FIFO(선입선출)을 사용한다.
- 순서가 중요한 업무에서 주로 사용.
- EX) 선착순 티켓 판매
- PriorityQueue
- 우선 순위에 따라 객체를 처리해야 할 때 사용됩니다.
- 선입선출을 기본으로 하지만 우선 순위에 따라 먼저 처리해야할 것이 있다면 우선 순위 힙을 기반으로 처리.
PriorityQueue<Integer> pQueue = new PriorityQueue<>();
pQueue.add(10);
pQueue.add(20);
pQueue.add(15);
System.out.println(pQueue.peek()); // 첫 번쨰 데이터인 10 출력
System.out.println(pQueue.poll()); // 오름차순하여 데이터 출력 -> 출력한 데이터는 제거 된다 -> 10 15 20 된 후 10 출력 후 제거
System.out.println(pQueue.peek()); // 두 번쨰 데이터인 15 출력
3. Deque 인터페이스
- 큐 데이터 구조의 변형.
- 양방형 큐 라고도 불리며, 양쪽 끝에서 요소를 추가하고 제거할 수 있는 구조.
- ArrayDeque
- 크기가 조정되는 배열이고 양쪽 끝에서 요소를 추가하고 제거하는 구조
ArrayDeque<Integer> de_que = new ArrayDeque<>();
de_que.add(10);
de_que.add(20);
de_que.add(30);
System.out.println(de_que); // 출력
de_que.clear(); // 초기화
de_que.addFirst(234); // 첫 번째에 데이터 입력
de_que.addFirst(345);
de_que.addLast(123); // 마지막에 데이터 입력
4. Set 인터페이스
- 중복 값을 저장할 수 없는 정렬되지 않은 데이터 모음
- 중복을 방지하고 고유한 데이터만 저장해야하는 경우 사용.
- HashSet
- 입력되는 데이터는 동일한 순서로 삽입되는 것을 보장하지 않음
- NULL 요소 삽입 허용
HashSet<String> set = new HashSet<>();
set.add("Hello");
set.add("Hello"); // 위와 중복되므로 한개만 들어감
- LinkedHashSet
- HashSet과 유사하지만 차이점은 데이터를 저장하는 순서
LinkedHashSet<String> set = new LinkedHashSet<>();
// 똑같이 중복은 허용안하지만, 순서를 유지함
- TreeSet
- Tree를 사용하여 저장
- 데이터의 순서는 오름차순대로 유지가 됨
TreeSet<String> set = new TreeSet<>();
// 순서가 유지되지 않고, 자동으로 오름차순으로 저장
5. Map 인터페이스
- 데이터를 키-값 으로 매핑을 지원하는 데이터 구조
- 동일한 키가 여러 개 있을 수 없어 중복 키는 지원하지 않음
- 키를 기반으로 프로그래밍 하는 경우 유용
- HashMap
- 자바의 Map 인터페이스의 기본적인 방법
- 데이터를 키-값 형태로 저장
- 데이터에 접근하려면 키를 알고 있어야한다.
- Hashing이라는 기술을 사용.
- Hashing : 인덱싱 및 검색 작업이 더 빨라지도록 키에 산술적인 연산을 적용하여 항목이 저장되어 있는 테이블의 주소를 계산하여 접근하는 방법
HashMap<Integer, String> map = new HashMap<>();
map.put(1,"Hello");
map.put(2,"Bye");
System.out.println(map.get(2)); // Bye 출력 -> .get(key) : key 값에 해당하는 value 값 가져옴
map.entrySet(); // map의 전체 키-값 쌍을 가져옴
직렬화 / 역직렬화
- 직렬화
- 자바 시스템 내부에서 사용되는 Object 또는 Data를 외부의 자바 시스템에서도 사용할 수 있도록 byte 형태로 데이터를 변환하는 기술
- JVM의 메모리에 할당(힙 또는 스택 영역)되어 있는 객체 데이터를 byte 형태로 변환하는 기술
- 역직렬화
- byte로 변환된 Data를 원래대로 Object나 Data로 변환하는 기술
- 직렬화된 byte 형태의 데이터를 객체로 변환해서 JVM으로 할당시키는 형태
가비지 컬렉션
- JVM의 HEAP 영역에서 동적으로 할당했던 메모리 영역 중 필요 없게된 메모리 영역을 주기적으로 삭제하는 프로세스.
제네릭
public class 클래스명<T> {} // 사용법
public interface 클래스명<T> {}
- 타입을 파라미터로 가지는 클래스와 인터페이스를 의미.
- 타입 파라미터들은 바운드 될 수 있다.
- 바운드 : 메소드가 받을 수 있는 타입을 제한할 수 있는 것.
- ? 타입은 모든 클래스나 인터페이스 타입 가능
- 사용법 예시 -> map 인터페이스도 2개의 제네릭 타입을 가진다.
public class GenericTest<A. B> {
private A first;
private B second;
public void setFirst(A first) {
this.first = first;
}
public void setSecond(B second) {
this.second = second;
}
public static void main(String[] args) {
GenericTest<String, Integer> genericTest = new GenericTest<>();
genericTest.setFirst("안녕하세요.");
genericTest.setSecond(123);
}
}
String / StringBuffer / StringBuilder
String : 불변의 속성을 가진다.
- 캐싱 기능에 의한 메모리 절약과 속도 향상
- Java 에서 String 객체들은 Heap의 String Pool 이라는 공간에 저장되는데, 참조하려는 문자열이 String Pool에 존재하는 경우 새로 생성하지 않는 Pool에 있는 객체를 사용하기 때문에 특정 문자열 값을 재사용하는 빈도가 높을수록 상당한 성능 향상을 기대할 수 있다.
- thread-safe
- String 객체는 불변이기 때문에 여러 쓰레드에서 동시에 특정 String 객체를 참조하더라도 안전하다.
- 보안기능
- 중요한 데이터를 문자열로 다루는 경우 강제로 해당 참조에 대한 문자열 값을 바꾸는 것이 불가능하기 때문에 보안이 유리하다.
StringBuffer
- 동기화를 지원하여 멀티 쓰레드 환경에서 주로 사용한다.
StringBuilder
- 동기화를 지원하지 않아 싱글 쓰레드 환경에서 주로 사용합니다.
StringBuffer sb = new StringBuffer();
StringBuilder sb = new StringBuilder();
// 둘 다 아래 형태로 사용
sb.append("asd");
메모리 관리
- 메소드 영역
- 전역변수와 static변수를 저장하며, method영역은 프로그램의 시작부터 종료까지 메모리에 남아있다.
- 스택 영역
- 지역변수와 매개변수 데이터 값이 저장되는 공간이며, 메소드가 호출될 때 메모리에 할당되고 종료되면 메모리가 해제된다.
- LIFO 구조를 갖고 변수에 새로운 데이터가 할당되면 이전 데이터가 지워진다.
- 힙 영역
- new 키워드로 생성되는 객체(인스턴스), 배열 등이 Heap 영역에 저장되며, 가비지 컬렉션에 의해 메모리가 관리되어 진다.
클래스 멤버 변수 초기화 순서
- static 변수 선언부 : 클래스가 로드 될 때 변수가 제일 먼저 초기화 된다.
- 필드 변수 선언부 : 객체가 생성될 때 생성자 block 보다 앞서 초기화 된다.
- 생성자 block : 객체가 생성될 때 JVM이 내부적으로 locking(thread-safe 영역)
static
- static 키워드를 사용한 변수나 메소드는 클래스가 메모리에 올라갈 때 자동으로 생성되며 클래스 로딩이 끝나면 바로 사용 가능.
- 인스턴스(객체 new로 생성) 생성 없이 바로 사용 가능.
- 모든 객체가 메모리를 공유한다는 특징이 있고, GC 관리 영역 밖에 있기 때문에 프로그램이 종료될 때까지 메모리에 값이 유지된 채로 존재하게 됩니다.
- 프로그램 내에서 공통으로 사용되는 데이터들을 관리할 때 이용.
Error / Exception 차이
- Error : 실행 중 일어날 수 있는 치명적 오류이며, 컴파일 시점에 체크할 수 없고, 오류가 발생하면 프로그램은 비정상 종료.
- Exception : Error 보다 경미한 오류이며, try-catch 나 throws를 이용해 프로그램의 비정상 오류를 막을 수 있다.
CheckedException / UnCheckedException
- CheckedException : 실행하기 전에 예측 가능한 예외이며, 반드시 예외 처리해야함.
- EX) IOException, ClassNotFoundException 등
- UnCheckedException : 실행하고 난 후에 알 수 있는 예외이며, 예외처리를 안해도 됨.
- EX) NullPointerException, ArrayIndexOutOfBoundException 등
- RuntimeException : UnCheckedException을상속한 클래스이고, RuntimeException이 아닌 것은 CheckedException을 상속한 클래스이다.
Optional API
- NPE (NullPointerException) 를 피하려면 null 여부 검사를 필연적으로 하게 되는데 만약 null 검사를 해야하는 변수가 많은 경우 코드가 복잡해지고 번거롭다.
- Java8부터
Optional<T>
을 제공하여 null로 인한 예외가 발생하지 않도록 도와주고, Optional 클래스의 메소드를 통해 null을 컨트롤 할 수 있다.
스트림
- Java 8 부터 지원되기 시작한 기능이다. 컬렉션에 저장되어 있는 엘리먼트들을 하나씩 순회하면서 처리할 수 있는 코드 패턴이다.
- 람다식과 함께 사용되어 컬렉션에 들어있는 데이터에 대한 처리를 매우 간결한 표현으로 작성할 수 있다. 병렬처리 또한 쉽다.
스트림 생성법
- 자바의 스트림을 사용하려면 우선 스트림 객체를 생성해야한다.
- 컬렉션
- 자바 코드에서 자주 사용하는 컬렉션 객체들은 stream() 메소드를 지원한다. 컬렉션 객체에서 stream() 메소드를 호출하면 스트림 객체를 만들 수 있다.
List<String> list = Arrays.asList("a", "b", "c");
Stream<String> stream = list.stream();
- 배열
- 배열은 정적 메소드를 이용하면 된다. Arrays
String[] array = new String[]{"a", "b", "c"};
Stream<String> stream2 = Arrays.stream(array, 1, 3); // 인덱스 1 ~ 2 (3 전까지) 만 포함
- 빌더
- 배열이나 컬렉션을 통해서 생성하는게 아닌 직접 값을 입력해서 스트림 객체를 생성하는 법.
String<String> stream = Stream<String>builder()
.add("Apple")
.add("Banana")
.add("Melon")
.build();
- Generator
- 데이터를 생성하는 람다식을 이용해서 스트림을 생성할 수 있다.
Stream<String> stream = Stream.generate(() -> "Hello").limit(5);
- Hello 라는 데이터를 5개만 찍어내도록 제한을 둠. 만약 limit를 안두면 무한대로 생성.
- Iterator
- iterate() 메소드를 이용해서 수열 형태의 데이터를 생성할 수도 있다.
// (100, 110, 120, 130, 140)
Stream<String> stream = Stream.iterate(100, n -> n + 10).limit(5);
- Empty
- 빈 스트림을 사용할 수 있다. stream 객체를 참조하는 변수가 null이라면 NPE이 발생할 수도 있다.
Stream<String> stream = Stream.empty();
- Stream.empty()를 사용하면 된다.
- 기본 타입
IntStream intStream = IntStream.range(1, 10); // 1 ~ 9
LongStream longStream = LngStream.range(1, 10000); // 1 ~ 9999
// 제네릭을 이용한 클래스로 사용하려면 박싱을 해서 사용해야한다.
Stream<Integer> stream = IntStream.range(1, 10).boxed();
// double 형 랜덤 숫자 3개 생성
DoubleStream stream = new Random().double(3);
- 문자열
//(72, 101, 108, 108, 111, 44, 87, 111, 114, 108, 100) -> ASCII 코드 값을 스트림형태로 뽑아주는 것
IntStream stream = "Hello,World".chars();
// 특정 구분자(아래에선 ,)를 이용해서 문자열을 스플릿 한 다음 각각을 스트림으로 뽑아낼 수 있다.
Stream<String> stream = Pattern.compile(",").splitAsStream("Apple,Banana,Melon");
- 파일
// 'test.txt' 파일의 데이터를 라인 단위로 읽어서 뽑아주는 스트림 객체이며, 데이터는 UTF-8로 디코딩해서 읽어들인다.
Stream<String> stream = Files.lines(Paths.get("test.txt"), Charset.forName("UTF-8"));
- 스트림연결
Stream<String> stream1 = Stream.of("Apple", "Banana", "Melon");
Stream<String> stream2 = Stream.of("Kim", "Lee", "Park");
Stream<String> stream3 = Stream.concat(stream1, stream2);
// "Apple", "Banana", "Melon", "Kim", "Lee", "Park"
스트림 데이터 가공
- 데이터를 가공해주는 메소드들은 가공된 결과를 생성해주는 스트림 객체를 리턴한다.
- Filter
- 스트림에서 뽑아져 나오는 데이터에서 특정 데이터들만 골라내는 역할
Stream<Integer> stream = IntStream.range(1, 10).boxed();
stream.filter(v -> ((v % 2) == 0)) // 짝수만 뽑는 람다식
.forEach(System.out::println);
// 2, 4, 6, 8
- Map
- 스트림에서 뽑아져 나오는 데이터에 변경을 가해준다.
Stream<Integer> stream = IntStream.range(1, 10).boxed();
stream.filter(v -> ((v % 2) == 0)) // 짝수만 뽑고
.map(v -> v * 10) // 뽑힌 값들에 10을 곱함
.forEach(System.out::println);
// 20, 40, 60, 80
- flatMap
- 메소드의 인자로 받는 람다는 리턴 타입이 Stream이다.
- 새로운 스트림을 생성해서 리턴하는 람다를 인자로 받는다.
- 중첩된 스트림 구조를 한단계 적은 단일 컬렉션에 대한 스트림으로 만들어주는 역할을 한다.
- 이런 작업을 '플랫트닝'이라고 한다.
List<List<String>> list = Arrays.asLists(Arrays.asList("A", "B", "C"),
Arrays.asList("a", "b", "c"));
List<String> flatList = list.stream()
.flatMap(Collection::stream)
.collect(Collectors.toList());
// ["A", "B", "C", "a", "b", "c"]
- Sorted
- 스트림 데이터들을 오름차순 정렬
Stream<T> sorted();
- Peek
- 스트림 내 엘리먼트들을 대상으로 map() 메소드처럼 연산을 수행한다.
- 새로운 스트림을 생성하지는 않고 그냥 인자로 받은 람다를 적용하기만 한다.
int sum = IntStream.range(1, 10)
.peek(System.out::println) // 중간에 로깅 같은 것을 하고자 할 때 사용한다.
.sum();
JAVA 심화 1 끝.
다음 게시글 JAVA 심화 2
https://hoozy.tistory.com/entry/JAVA-%EC%8B%AC%ED%99%94-2
[JAVA] 심화 2
이전 게시글 JAVA 심화 1 https://hoozy.tistory.com/entry/JAVA-%EC%8B%AC%ED%99%94-1 카테고리 : 자바(심화) 파일 1. java.io.File File 클래스의 createNewFile() 메소드를 이용하여, 새로운 파일 및 디렉토리를 생성할 수
hoozy.tistory.com
참고 자료
https://crazykim2.tistory.com/557
https://go-coding.tistory.com/101
https://dev-coco.tistory.com/28
https://dev-coco.tistory.com/153#%F-%-F%--%A-%--JVM%EC%-D%--%--%EC%--%AD%ED%--%A-%EC%--%--%--%EB%-C%--%ED%--%B-%--%EC%--%A-%EB%AA%--%ED%--%B-%EC%A-%BC%EC%--%B-%EC%-A%---
'CS > 자바' 카테고리의 다른 글
[JAVA] 프레임워크 (0) | 2023.03.10 |
---|---|
[JAVA] 심화 2 (0) | 2023.03.10 |
[JAVA] 기초 (0) | 2023.03.07 |
댓글