스트림은 곧 '데이터의 흐름'입니다.
컬렉션의 저장요소를 하나씩 참조해서 람다식으로 처리할 수 있도록 해주는 반복자 입니다
스트림을 활용해서 필터링, 데이터 변경, 다른타입이나 자료구조로 변환 등을 할 수 있습니다
/ 스트림의 특징
스트림은 데이터 소스를 변경하지 않는다
스트림은 작업을 내부적으로 반복 처리합니다
슽트림은 컬렉션의 요소를 모두 읽고 나면 닫혀서 재사용이 불가능합니다. 그러므로 필요할 경우 재생성을 해야 합니다
/ 1 스트림 생성
스트림을 이용하기 위해 먼저 스트림을 생성
Stream<T> Collection.stream()
을 이용하여 해당하는 컬렉션을 기반으로 하는 스트림을 생성할 수 있다
/ 2 중간연산
중간단계로써 데이터의 형변환 혹은 필터링, 정렬 등 스트림에 대한 가공을 해줍니다.
map(변환) / sorted(정렬) / skip(스트림 자르기) / limit(스트림 자르기) 등이 있습니다
/ 3 최종연산
스트림의 요소를 소모해서 결과를 반환하는 단계입니다. 최종연산 이후에는 스트림이 닫히게 되고 더 이상 사용할 수 없습니다.
최종 연산의 결과값은 단일 값일수도 있으며 배열 혹은 컬렉션일 수도 있습니다
collect()를 이용해서 다른 콜렉션으로 바꾸는것, reduce를 이용해서 incremental caculation하는것도 가장 많이 쓰이는 패턴입니다.
예제 1) 기본구조
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
public class Main {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("서울");
list.add("속초");
list.add("부산");
list.add("서울");
System.out.println(list);
List<String>result = list.stream() // 스트림 생성
.limit(2) // 중간연산
.collect(Collectors.toList()); // 최종연산
System.out.println(result);
System.out.println("list -> transformation -> set");
Set<String> set = list.stream()
.filter("서울" ::equals)
.collect(Collectors.toSet());
set.forEach(System.out::println);
}
}
예제2) Array를 Stream으로 변환
import java.util.Arrays;
import java.util.stream.Stream;
public class Main {
public static void main(String[] args) {
String[] arr = {"엑셀보다 쉬운 SQL", "웹개발 종합반",
"알고보면 알기쉬운 알고리즘", "웹개발의 봄,Spring"};
Stream<String> stringStream = Arrays.stream(arr);
stringStream.forEach(System.out::println);
}
}
예제3) map연산 활용
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
import org.apache.commons.lang3.tuple.Pair;
class Sale {
String fruitName;
int price;
float discount;
public Sale(String fruitName, int price, float discount) {
this.fruitName = fruitName;
this.price = price;
this.discount = discount;
}
}
public class Main {
public static void main(String[] args) {
List<Sale> saleList = Arrays.asList(
new Sale("Apple", 5000, 0.05f),
new Sale("Grape", 3000, 0.1f),
new Sale("Orange", 4000, 0.2f),
new Sale("Tangerine", 2000, 0)
);
Stream<Sale> saleStream = saleList.stream();
saleStream.map(sale -> Pair.of(sale.fruitName, sale.price * (1 - sale.discount)))
## .forEach(pair -> System.out.println(pair.getLeft() + " 실 구매가: " + pair.getRight() + "원 "));
}
}
오브젝트는 다양한 속성들의 집합
속성또한 속성으로 이루어진 객체이다
사물을 설명하는것은 그사물의 속성을 설명하는것 -> 객체
현실세계를 모방하는것이 아니라 창조하는 것이다
현실세계의 존재 -> 소프트웨어의 존재
수동적(현실세계) -> 능독적 변함(소프트웨어 세계)
소프트웨어의 세계에서는 객체가 더많은 일을 스스로 할수 있어야함
실세계에서는 존재하지도 않는 추상적인것들 또한 스스로 결정할 수 있는 존재로 탄생
예) 커피 - 실세계에서는 스스로 커피양이 줄지 않는다
소프트웨어 세계
즉, 객채지향은 현실세계를 모방한 것이 아니라 현실세계를 참조하여 새로운 세상을 탄생시키는 것
객체는 현실에 대한 은유이다
메세지: 객체들이 협력하며, 목표를 달성해나가는 과정
협력 : 객체들 간의 관계
역할과 책임,협력(+메세지)
각 객체들은 역할과 책임이 주어진다
각 객체들은 서로 협력을 통하여 움직인다
협력은 요청과 응답
예) customer -> (커피주문) -> casher
customer -> (커피받음) -> casher
역할과 책임을 가지는 객체들이, 서로 협력을 통해서 시스템을 구성한다
객체의 품질이 협력의 품질을 결정한다
각 객체가 요청(메세지)에 대하여, 성실히 이행하는가에 달려있다
객체는 다른 객체와 협력하기 위해 메세지를 전송하고,
메세지를 전달 받은 객체는 메세지를 처리하는데 적합한 메서드를 자율적으로 선택하여 처리한다
클래스는 객체들의 협력 관계를 코드로 옮기는 도구일 뿐이다
객체의 역할, 책임, 협력에 집중
객체지향은 객체를 지향하는 것이다 (클래스를 지향하는 것이 아님)
메모리에 적재되어 실행되고 있는 프로그램의 인스턴스
운영체제로부터 시스템자원을 할당받은 직업의 단위
프로세스는 독립된 메모리를 할당 받는다
프로세스가 메모리를 관리하기 위해 이 공간들을 어떤 구조로 관리하는데,
이를 프로세스 주소공간 이라고 부른다
프로세스 주소 공간은 Code,Data,Stack,Heap으로 구성된다
Code: 코드 자체를 구성하는 메모리 영역(프로그램 명령)
Data : 전역변수, 정적변수 등
Stack : 지역변수 , 함수 매개변수, 리턴값(임시 메모리 영역)
Heap : 동적 할당시 사용(new(),malloc()등)
기본적으로 프로세스마다 최소 1개의 스레드 (메인 스레드) 를 갖는다
프로세스 내에서 프로세스의 자원을 이용하는 여러 실행 흐름의 단위
스레드는 프로세스 내의 Code,Data,Stack,Heap영역은 다른 스레드와 공유하고 Stack영역을 따로 할당받는다
프로세스와 해당 프로세스 내의 다른 스레드와 자원과 공간을 공유하면서 사용
하나의 프로그램을 여러개의 프로세스로 구성하여 각 프로세스가 하나의 작업을 처리하도록 하는 것
장점 :
여러개의 자식 프로세스 중에 하나가 문제가 발생하면
그 자식 프로세스만 죽는 것 이상으로 다른 영향이 확산되지 않는다
(프로세스는 구분 되어있고 스레드는 서로 공유(연결)? 되어잇다)
단점 :
문맥교환(Context Switching)에서의 오버헤드(리소스가 많이든다?)
캐쉬메모리 초기화등 무거운 작업이 진행되고 많은 시간이 소모되는 등의 오버헤드가 발생
프로세스간 통신 기법(IPC, Inter Process Comunication)
프로세스는 각 독립된 메모리 영역을 할당받았기 때문에
하나의 프로그램에 속하는 프로세스들 사이의 변수를 공유할 수 없다
따라서 IPC라는 방법을 사용해야하며, 이는 어렵고 복잡한 통신방법이다 (비용 크다)
cpu 는 한번에 하나의 프로세스만 처리할 수 있기 때문에 여러 프로세스를 처리해야 하는 상황에서는 돌아가면서
여러 프로세스 작업을 처리하는데 이과정을 문맥교환이라 한다
동작중인 프로세스가 대기를 하면서 해당 프로세스의상태(context)를 프로세스 제어블록(pcb)에 보관하고,
대기하고 있던 다음 순서의 프로세스가 동작하면서
이전에 보관햇던 프로세스의 상태를 복구하는 작업을 말한다
특정 프로세스에 대한 중요한 정보를 저장하고 잇는 커널 내의 자료구조이다
프로세스는 cpu를 할당받아 작업을 처리하다가 프로세스 전환이 발생하면
진행하던 작업을 저장하고 cpu를 반환해야 한다
이때 작업의 진행상황을 모두 pcb에 저장한다
그리고 다시 cpu를 할당받게되면 pcb에 저장되었던 내용을 불러와 종료되었던
시점부터 다시 작업을 수행한다
하나의 프로그램을 여러개의 스레드로 구성하고 각 스레드가 하나의 작업을 처리하도록 하는 것
장점
프로세스에 비해 메모리 공간과 시스템 자원소모가 줄어들게 된다
스레드간 통신시 Data Heap메모리 영역을 이용해 데이터를 주고받으므로 통신방법이 간단하다
문맥교환시 pcb및 캐시 메모리를 비울필요가 없기 때문에 비용이 적고 더 빠르다
단점
서로다른 스레드가 data heap영역등을 공유하기 때문에 어떤 스레드가 다른 스레드 에서 사용중인 변수나
자료구조에 접근항 엉뚱한 값을 읽어오거나 수정할 수 있다 즉, 자원공유 동기화 문제가 발생한다
하나의 스레드에 문제가 생가면 전체 프로세스가 영향을 받는다
멀티스레드 환경에서 여러 스레드가 동시에 사용되어도 안전하다는것을 말한다
즉, 여러스레드가 프로세스의 공유자원(하나의 객체 및 변수)에 접근할때, 공유 자원의 무결성을 보장하는
(= 자원동기화가 잘 되지 않는 이슈 없이 의도한대로 동작하는) 것을 말한다
블로킹 / 논블로킹 / 동기 / 비동기 ( blocking, non-blocking, syncronous, asyncronous )
제어권:
제어권은 자신(함수)의 코드를 실행할 권리 같은 것이다. 제어권을 가진 함수는 자신의 코드를 끝까지실행한 후 자신을 호출한 함수에 돌려준다
동시성 :
함수a와함수b가 동시에 진행되는것(처럼 보이는 것)
그러기 위해서는 특정 함수 a가 다른함수 b의 결과값에 의존하는지가 중요하다
블로킹
함수 a가 함수b를 호출한 뒤, 함수b의 리턴값이 올때까지 기다린 후 진행되는 것
즉, 제어권을 넘겨주는 것
논 블로킹
함수 a가 함수b를 호출한 뒤, 함수 b의 작업완료 여부와 상관없이 진행되는 것
즉, 제어권을 넘겨주지 않는것
동기
함수 a와 함수 b를 호출할 때, 함수 a가 함수b의 리턴값을 계속 확인하면서 신경 쓰는것
비동기
함수 a와 함수 b를 호출할 때, 함수 a가 함수b의 작업완료 여부는 신경 쓰지 않는 것
( 동기 비동기 뭔가 명확하지 않음, 추상적이다?)
성능을 뽑아먹기위해 비동기 사용
언어마다 비동기를 어떻게 처리하는지 다르다 공부해보자!
Java의 비동기
특징
기본적으로 멀티 스레드 방식이다 (스레드로 빼서 비동기로 처리?)
JVM(Java Virtual Machine)에 의해 스레드 스케줄링이 이뤄진다.
활용예시
request요청 처리 , db 접근 등
JavaScript의 비동기
특징
JavaScript는 싱글스레드 논블로킹 모델의 환경에서 실행됩니다
호출 스택: 동기코드를 담당. 함수들이 실행되는 데이터공간.
백그라운드: 비동기 작업을 실행하는 공간.
태스크 큐: 비동기 작업이 끝나서 이후 실행돼야 할 콜백 함수들이 줄을 서서 대기하고 있는 공간.
바로 실행되지 않고 호출 스택이 비워지면 이벤트 루프에 의해 호출 스택으로 옮겨져서 실행된다.
이벤트 루프: 호출 스택이 비어 있는게 확인되면 이벤트 루프는
태스크 큐에서 함수를 하나씩 꺼내(들어온 순서로 꺼냄) 호출 스택으로 옮긴다.