힘들게 만든 OE계정에서,, 

나이 구하기
+ 연령대 구하기

select date_of_birth,to_char(date_of_birth,'YYYY/MM/DD') as birth, 
        trunc((sysdate - date_of_birth) / 365) as age,
        case
            when trunc((sysdate - date_of_birth) / 365) >= 70 and trunc((sysdate - date_of_birth) / 365) < 80 then '70대'
            when trunc((sysdate - date_of_birth) / 365) >= 60 and trunc((sysdate - date_of_birth) / 365) < 70 then '60대'
            when trunc((sysdate - date_of_birth) / 365) >= 50 and trunc((sysdate - date_of_birth) / 365) < 60 then '50대'
            when trunc((sysdate - date_of_birth) / 365) >= 40 and trunc((sysdate - date_of_birth) / 365) < 50 then '40대'
            when trunc((sysdate - date_of_birth) / 365) >= 30 and trunc((sysdate - date_of_birth) / 365) < 40 then '30대'
            when trunc((sysdate - date_of_birth) / 365) >= 20 and trunc((sysdate - date_of_birth) / 365) < 30 then '20대'
            when trunc((sysdate - date_of_birth) / 365) >= 10 and trunc((sysdate - date_of_birth) / 365) < 20 then '10대'
            else '기타'
        end as Generation
        
from customers;




---------------------------------------------------------------------------------------------------

위 구문을 식으로 바꿔보았다 (아래)


select date_of_birth,to_char(date_of_birth,'YYYY/MM/DD') as birth, 
        trunc((sysdate - date_of_birth) / 365) as age,
        case
            when sysdate-date_of_birth >= 0 then floor(floor((sysdate-date_of_birth)/365)/10)*10 || '대' 
            else '기타'
        end as Generation
        
from customers;

조건이 반복되면 간략하게 만들어서 사용할 생각을 하자

다시 scott 계정으로 돌아왔다

 

 

단일행 함수 → input과 output의 수가 같음.

다중행 함수 → input이 여러개여도 output은 하나

 

-- 단일행 합수 : round(), floor()..

-- 다중행 함수 : sum(), avg(), max(), min(), count()..

-- 회사직원들의 급여 합, 평균
-- distinct 중복제거
select sum(sal), sum(distinct sal), avg(distinct sal)
from emp;

select sal
from emp;

-- 전체 사원 14명, 성과금받는 직원 4명, 성과금의 합 2200, 급여가 같은 사원 중복제거
-- 다중행함수는 null 값을 무시한다
select count(*), count(comm), sum(comm), count(distinct sal)
from emp;

select max(sal), min(sal)
from emp;

-- 일반컬럼과 단일행 그룹 함수는 함께 사용할 수 없다.
-- 각 컬럼에 조회된 결과의 개수가 같아야 한다.
select ename, max(sal)
from emp;

-- 일반컬럼과 단일행 그룹 함수는 함께 사용할 수 없다.
select max(sal)
from emp;

-- 추후 해결하는 방법을 배울 예정

 

 

group by절

-- group by 기준 컬럼
-- 테이블에 데이터를 논리적으로 묶어서 처리하는 역할

select *
from emp
order by deptno;

-- 부서 별 급여의 합계, 평균
-- group by로 묶이면 그룹의 갯수대로 값이 나온다
-- group by에 사용되는 컬럼은 그룹함수와 함께 사용될 수 있다
select deptno, sum(sal), avg(sal)
from emp
group by deptno; 

select *
from emp
order by deptno;

select deptno, job, sum(sal)
from emp
group by deptno, job
order by deptno;

-- having 조건절
-- where 그룹함수를 조건식으로 사용 불가, group by절 보다 먼저 실행된다
select deptno, sum(sal)
from emp
group by deptno
having sum(sal) > 9000;

-- where절과 group by절을 함께 사용한 예제
select deptno, job, avg(sal)
from emp
where sal <= 3000
group by deptno, job
-- having avg(sal) >= 2000; -- 그룹함수를 사용하여 조건식을 구성한다.
having deptno = 10;

 

문제풀기

-- 문제 풀기

-- 1번
select deptno, trunc(avg(sal)) as AVG_SAL, max(sal) as MAX_SAL, min(sal) as MIN_SAL, count(*) as CNT
from emp
group by deptno;

-- 2번
select job, count(*)
from emp
group by job
having count(*) >= 3;

-- 3번
select to_char(hiredate,'YYYY') as HIRE_YEAR, deptno, count(*) as CNT
from emp
group by to_char(hiredate,'YYYY'), deptno
order by HIRE_YEAR;

 

group by 절 사용 방법

select
from
where 
group by
having
order by -- 가장 마지막에 작성

 

 

 

join

조인 
- 두개 이상의 테이블을 사용하여 데이터를 조회하는 것
- 두개의 테이블의 공통컬럼을 사용하여 데이터를 조회
- 하나의 쿼리문으로 데이터 통합 조회
- from절에 두개 이상의 테이블을 사용한다

--종류
-- cross join : 조건없이 양쪽 테이블을 모두 조회
-- equi join : 공통 컬럼에 값을 비교해서 같으면 조회(등가)
-- non equi join : 컬럼의 값을 범위 비교한다
-- self join : 하나의 테이블을 두개의 테이블처럼 사용하여 조회(등가)
-- outer join : 누락된 데이터를 조회(등가)
select *
from emp;

select *
from dept;

-- cross join 
select *
from emp, dept;

-- equi join
select ename, sal, emp.deptno, dname, loc
from emp, dept
where emp.deptno = dept.deptno; -- 조인 조건
-- 공통으로 가지고 있는 컬럼이 아니라면 생략이 가능하다
-- select emp.ename, emp.sal, emp.deptno, dept.dname, dept.loc

select ename, sal, e.deptno, dname, loc
from emp e, dept d -- 별칭사용 (원래 테이블명 사용불가)
where e.deptno = d.deptno -- 조인 조건
and ename = 'SMITH'; -- 일반 조건

-- non equi join
select *
from emp;

select *
from salgrade;

select ename, sal, grade
from emp e, salgrade s 
-- where e.sal >= s.losal and e.sal <= s.hisal; -- 범위 비교
where e.sal between s.losal and s.hisal;

-- slef join
select *
from emp;

select e.ename || '의 매니저는 ' || m.ename || ' 입니다'
from emp e, emp m -- emp(mgr) = emp(empno)
where e.mgr = m.empno; -- 조인 조건
-- null값 누락

-- outer join
-- 누락되는 null 값도 가지고 온다
-- 조인 조건식의 한쪽 컬럼에 (+)표시를 작성한다 -> 해당 데이터가 없는 쪽에 붙일 것. 
select e.ename || '의 매니저는 ' || m.ename || ' 입니다'
from emp e, emp m -- emp(mgr) = emp(empno)
where e.mgr = m.empno(+); -- 조인 조건

select  ename, sal, d.deptno, dname
from emp e,dept d
where e.deptno(+) = d.deptno;


-- 사원이름, 급여, 부서번호, 부서명, 급여등급

select ename, sal, e.deptno, dname, grade
from emp e, dept d, salgrade s 
where e.deptno = d.deptno
and e.sal between s.losal and s.hisal;

 

표준조인방식

-- 표준 조인 방식 (ANSI-JOIN)
-- 위의 조인 방식은 오라클에서 쓸 수있는 조인 
-- 이 표준 조인 방식은 어디에서나 사용 가능
-- 호환성을 위해 표준을 쓰는 것을 권고한다

-- 종류
-- cross join
-- inner join(equi join, non equi join, self join)
-- outer join

select *
from emp cross join dept;

-- 조건문 on
select ename, sal, e.deptno, dname, loc
from emp e inner join dept d -- inner 생략 가능
on e.deptno = d.deptno;

-- 일반조건 추가 where
select ename, sal, e.deptno, dname, loc
from emp e inner join dept d -- inner 생략 가능
on e.deptno = d.deptno -- 조인조건
where ename = 'SCOTT'; -- 일반조건

select ename, sal, e.deptno, dname, loc
from emp e inner join dept d 
-- on e.deptno = d.deptno
using(deptno); -- 조인 조건 (컬럼명이 동일할 경우)


select ename, sal, grade
from emp e inner join salgrade s
on e.sal between s.losal and s.hisal;

select e.ename || '의 매니저는 ' || m.ename || ' 입니다'
from emp e inner join emp m
on e.mgr = m.empno;

-- left, right, full을 outer 앞에 붙여줘야한다
select e.ename || '의 매니저는 ' || m.ename || ' 입니다'
from emp e left outer join emp m 
on e.mgr = m.empno;


-- 테이블 먼저 나열하고 쓰면 안된다
select ename, sal, e.deptno, dname, grade
from emp e inner join dept d
on e.deptno = d.deptno
inner join salgrade s
on e.sal between s.losal and s.hisal;

 

 

 

서브쿼리문

-- 서브 쿼리문
-- select 문 안에 중첩되는 select 구문
-- 단일행 서브쿼리 : 결과가 하나 (비교연산자)
-- 다중행 서브쿼리 : 결과가 여러 개 (비교연산자 사용 불가), in, any, all

-- select (select)
-- from (select)
-- where (select)

--------------------단일행 서브쿼리--------------------

-- SCOTT 보다 급여를 많이 받는 사람을 알고 싶을 때
select sal
from emp
where ename = 'JONES';

select *
from emp
where sal > (
    select sal
    from emp
    where ename = 'SCOTT'
);

-- 근무지가 뉴욕인 사람들
select *
from emp
where deptno = (
    select deptno
    from dept
    where loc = 'NEW YORK'
);

-- 급여를 가장 많이 받는 사원의 급여와 사원이름
select max(sal)
from emp;


select ename,sal 
from emp
where sal = (
    select max(sal)
    from emp
);

--------------------단일행 서브쿼리--------------------


--------------------다중행 서브쿼리--------------------

-- 급여를 3000 이상 받는 사람들
select ename, sal, deptno
from emp
where deptno in ( -- or 조건과 동일
    select distinct deptno
    from emp
    where sal >= 3000
);

-- 30번 부서에서 근무하는 사람들의 급여 조회, 그 급여보다 많은 급여를 받는 사람 조회
select ename, sal
from emp
where sal > all ( 
    select sal
    from emp
    where deptno = 30
);

-- any : 값 중에 가장 작은 값보다 큰 값을 가진 사원 조회 
-- all : 값 중에 가장 큰 값보다 큰 값을 가진 사원 조회

--------------------다중행 서브쿼리--------------------

 

 

✔️ 테이블 만들기

DDL (data definition language)
-- creat : 객체 생성
-- alter : 객체 수정
-- drop : 객체 삭제
-- truncate : 테이블을 초기화
create table 테이블명 (
		컬럼명1 타입, 
		컬럼명2 타입,
		컬럼명3 타입,
);


-- 컬럼이름을 만든 후 타입에 사용
-- 숫자 : number(4), number(7,2)
-- 문자 : char(10) : 고정형, varchar2(10) : 가변형
-- 문자를 다룰 때는 가변형을 주로 사용한다
-- 날짜 : date, timestamp

데이터를 조회하는 연산자

 

집합연산자 (합집합, 교집합, 차집합)

  • 두개의 select 구문은 반드시 컬럼의 개수와 타입이 같아야 한다
  • 컬럼명은 달라도 상관 없다
  • 실제로는 물리적으로 다른 데이터들을 가지고 집합연산을 한다.

 

-- 합집합 (union)
select empno, ename, sal, deptno
from emp
where deptno = 10
union
select empno, ename, sal, deptno
from emp
where deptno = 20;

union → 중복 제거

union all → 중복 허용

 

-- 차집합 (minus)
select empno, ename, sal, deptno
from emp
minus
select empno, ename, sal, deptno
from emp
where deptno = 10;
-- 교집합
select empno, ename, sal, deptno
from emp
intersect
select empno, ename, sal, deptno
from emp
where deptno = 10

 

 

문제풀이) 내가 푼 ver.

-- 문제 1번
-- 이름이 s 로 끝나는 사람
select *
from emp
where ename like '%S';

-- 문제 2번
-- 30번 부서에서 근무하는 사람
select empno, ename, job, sal, deptno
from emp
where deptno = 30;

-- 문제 3번
-- 급여가 2000 초과인 사원

-- 집합연산자 사용 X
select empno, ename, sal, deptno
from emp
where sal > 2000 and (deptno = 20 or deptno = 30);

-- 집합연산자 사용
select empno, ename, sal, deptno
from emp
where sal > 2000
intersect
select empno, ename, sal, deptno
from emp
where deptno = 20 or deptno = 30;

-- 문제 4번
-- 급여 2000 이상 3000 이하 이외의 범위
select empno, ename, job, mgr, hiredate, sal, comm, deptno
from emp
minus
select empno, ename, job, mgr, hiredate, sal, comm, deptno
from emp
where sal >= 2000 and sal <= 3000;

-- 문제 5번
-- 이름에 E 포함, 급여 1000 ~ 2000 사이
select empno, ename, sal, deptno
from emp
where ename like '%E%'
and not (sal >= 1000 and sal <= 2000);

-- 문제 6번
-- 추가수당 X , 'MANAGER','CLERK' 직책 구분, 이름의 두번째 글자가 L이 아닌 사원
select empno, ename, job, mgr, hiredate, sal, comm, deptno
from emp
where comm is null
intersect
select empno, ename, job, mgr, hiredate, sal, comm, deptno
from emp
where (job = 'MANAGER' or job = 'CLERK')
and ename not like '_L%';

 

강사님 ver.

-- 문제 3번
-- 집합연산자 사용 X
select empno, ename, sal, deptno
from emp
where deptno in(20, 30)
and sal > 2000;


-- 문제 5번
-- 이름에 E 포함, 부서 30, 급여 1000 ~ 2000 사이
select empno, ename, sal, deptno
from emp
where ename like '%E%'
and deptno = 30
and sal not between 2000 and 3000;

-- 문제 6번
-- 추가수당 X , 상급자가 없고, 'MANAGER','CLERK' 직책, 이름의 두번째 글자가 L이 아닌 사원
select *
from emp
where comm is null
and mgr is not null
and job in ('MANAGER','CLERK')
and ename not like '_L%';

문제에서 놓친 조건이 있었다.. 문제를 잘 읽자!

그리고 in을 잘 활용해야겠다.

 

 

 

함수

  • 문자함수
  • 숫자함수
  • 날짜함수

 

🍀  문자함수

-- 문자함수
-- upper 대문자 lower 소문자 initcap 첫글자 대문자 뒤는 소문자
select upper(ename), lower(ename), initcap(ename)
from emp;

select *
from emp
where ename = upper('scott');

select *
from emp
where lower(ename) = 'scott';

-- length 길이
select ename, length(ename)
from emp
where length(ename) >= 5;

-- 문자열에서 일부 추출
-- 오라클DB에서 인덱스는 1번부터 시작

-- substr (문자열, 시작위치, 개수)
-- substr (문자열, 시작위치)

select job, substr(job, 1, 2), substr(job, 5)
from emp;

-- 원하는 문자를 넣어 확인 dual
-- 문자를 변경하고 싶을 때
select '010-123-1234' as phone,
replace('010-123-1234', '-', '/'),
replace('010-123-1234', '-') -- '-'가 빠진다
from dual;

-- 문자연결
select ename || ' is a ' || job
from emp;

select concat(empno, ename)
from emp;

select concat(ename, concat(' is a ', job))
from emp;

-- instr(문자열, 찾고자하는 문자)
-- instr(문자열, 찾고자하는 문자, 문자를 찾고자하는 시작위치)
-- instr(문자열, 찾고자하는 문자, 문자를 찾고자하는 시작위치, 찾으려는 문자가 몇번째 있는 문자인지(위치))

select instr('HELLO, ORACLE','L'),
instr('HELLO, ORACLE','L', 5),
instr('HELLO, ORACLE','L', 2, 2)
from dual;

 

-- 문제

-- 1번. 입사일자 82년도 입사한 사람 조회

select *
from emp
where hiredate between '19820101' and '19821231';

-- substr() 이용

select *
from emp
where substr(hiredate, 1, 2) = '82';

-- 2번. 이름에 세번째 문자가 R인 사원 조회

select *
from emp
where ename like '__R%';

-- substr() 이용 -> 문자반환
select *
from emp
where substr(ename, 3, 1) = 'R';

-- instr() 이용 -> 위치반환
select *
from emp
where instr(ename,'R', 3, 1) = 3;

 

🍀  숫자함수

-- 숫자함수
-- 소수점(실수) 다루는 함수가 대부분
-- round() : 반올림
-- trunc() : 실수를 버림
-- ceil() : 강제 올림
-- floor() : 강제 내림
-- mod() : 나머지 구하기


-- round()
select 
round(1234.5678),
round(1234.5678,1), 
round(1234.5678,2)
from dual;

-- trunc()
select
trunc(1234.5678),
trunc(1234.5678, 1),
trunc(1234.5678, 2)
from dual;

-- ceil(), floor()
select
ceil(3.14),
floor(3.14)
from dual;

-- mod() 나머지
select
mod(15,6),
mod(11,2)
from dual;

-- 사원 중에 사번이 홀수인 사원
select *
from emp
where mod (empno,2) = 1;

 

🍀  날짜함수

-- 날짜함수
-- 연산이 가능

-- 현재 날짜 정보
select sysdate
from dual;

select sysdate - 1 as "어제",sysdate, sysdate + 1 as "내일"
from dual;

-- 날짜 데이터끼리 연산 가능
select trunc(sysdate - hiredate) as "근무일수", trunc((sysdate - hiredate)/365) as "근무년수"
from emp;

-- 달을 기준으로 연산
select sysdate, add_months(sysdate, 3)
from dual;

-- 문제
-- 근속 년수가 41년 이하인 사원 조회
select *
from emp
where trunc((sysdate - hiredate)/365) <= 41;

-- add_months()
select *
from emp
where add_months(hiredate, 12 * 43) <= sysdate;

 


✨ 형변환

-- 형변환
-- to_number(), to_char(), to_date()
--          숫자 ->  문자  -> 날짜
--              <-      <-

-- 날짜를 문자로
select sysdate, to_char(sysdate, 'YYYY/MM/DD DY HH:MI:SS')
from dual;

-- 숫자를 문자로
-- 0, 9
-- L 로컬(지역) 돈 단위로 처리
select sal, to_char(sal,'L9,999')
from emp;

-- 문자를 숫자로
select '1300' - '1000' -- 자동형변환
from dual;

select to_number('1,300', '9,999') - to_number('1,000', '9,999')
from dual;

-- 문자를 날짜로
select to_date('2024-01-01','YYYY-MM_DD')
from dual;

select *
from emp
where hiredate >= '1981/01/01';  -- to_date('1981/01/01','YYYY/MM/DD');ㅍ

 

-- 기타함수
-- nvl()

-- 조건문 형식 함수
-- decode(값, 비교값1, 결과1, 비교값2, 결과2, 비교값3, 결과3, 기타결과)
-- 비교값1, 결과1 -> 한쌍
-- 비교했을때 원하는 값이 없으면 기타결과가 출력
-- -> switch case 문과 유사하다
-- case 구문 -> 다중 if 문과 유사

select deptno,
decode(deptno,
10, 'ACCOUNTING',
20, 'RESEARCH',
30, 'SALES',
40, 'OFERATIONS',
'ETC'
)
from emp;

-- JOB이 manage인 사원의 급여를 10% 인상한 결과를 출력
-- JOB이 salesman 사원 급여를 5% 인상한 결과 출력
-- JOB이 analyst 사원은 급여를 동결한 결과 출력
-- 기타 JOB인 사원들은 3% 급여 인상

select job, sal,
decode(job,
'MANAGER', sal * 1.1,
'SALESMAN', sal * 1.05,
'ANALYST', sal,
sal * 1.03
)
from emp;


select job, sal,
case job 
when 'MANAGER' then sal * 1.1
when 'SALESMAN' then sal * 1.05
when 'ANALYST' then sal
else sal * 1.03 
end as upsal
from emp;

-- 범위비교
select job, sal,
case 
when sal >= 3000 and sal <= 5000 then sal * 1.1
when sal >= 2000 and sal < 3000 then sal * 1.05
when sal >= 1000 and sal < 2000 then sal
else sal * 1.03 
end as upsal
from emp;

 

 

맥에서 계정 풀어서 만들어야함.

oe계정 만들기

select date_of_birth, to_char(date_of_birth, 'YYYY-MM-DD') as birth
from customers;

oe.sql → 오라클에서 지원해주는 교육용 DB

------> 어떻게 해도 안돼서 그냥 노가다로 하나씩 집어넣었다;  자세한 내용은 아래 참조.

 

맥북 M1 오라클 21c 실습을 위한 새로운 계정 생성하기

맥으로 오라클 쓰기 정말 힘들다!일단 오라클 자체는 여러 블로그를 뒤적거리며 어찌저찌 깔았다.그리고 그보다 심한 난관은 바로.. 수업을 위해 새로운 계정을 생성하는 것이었다..!!!! scott계

100dumpling.tistory.com

 

오라클 scott 계정 등록까지 완료

 

 

데이터 유형(타입)

  • 숫자 : number,number(크기), number(7,2)
  • 날짜 : date
  • 문자 : varchar2(크기)

 

오라클에서는 문자 1개가 1byte

한글은 2byte

대소문자를 가리지 않는다!

 

  • CRUD =
  • C(insert)R(select)U(update)D(delete)
  • DQL : select (조회) DML : insert(삽입), update (수정), delete(삭제)

 

 

데이터를 조회하는 방식

select 컬럼명1, 컬럼명2, 컬럼명3, ...
from 테이블명

 

예)

select deptno, dname, loc
from dept;

select empno, ename, job, mgr, hiredate, sal, comm, deptno
from emp;

-- * : 모든 컬럼을 조회할 때
select * 
from emp;

select empno, ename, job
from emp;

 

 

select문을 사용하는 이유 : 새로운 형태의 데이터를 만들어내기위해.

→ 연산자를 이용함.

 

사원의 연봉을 알아보기 예제)

-- 연산자
-- 산술연산자 : +, -, /, *
-- null 값은 연산이 불가하다, 연산을 하면 모든 결과가 null이 되어버림.
-- 연산이 불가한 null 데이터를 연산이 가능하도록 임시로 변경할 수 있다. 
-- nvl() : null 값을 원하는 값으로 변경해준다. 
-- 기존 컬럼명 별칭 부여 : as (생략가능)

-- 연봉을 알고 싶을 때
-- 연봉 sal * 12 + comm
select ename "사원명" , sal, sal * 12, comm, sal * 12 + nvl(comm,0) as "연봉"
from emp;

nvl(comm,0) → comm 에서 나오는 null 값을 임시적으로 0으로 변경해서 계산해라

 

결과 :

 

 

|| 와 distinct

-- || 를 이용한 합치기 
select ename || ' is a ' || job
from emp;

-- 중복제거
select distinct job
from emp;

 

조건절

-- 조건절
-- where 조건식 (컬럼명 = 값)
-- 비교연산자 ( =, !=, <, >, <=, >= )
-- 숫자 
-- 문자 : 홑따옴표 사용, 대소문자 구분
-- 날짜 : 홑따옴표, 날짜형식(년, 일, 월)

-- 숫자
select *
from emp
where sal >= 3000;

select *
from emp
where deptno = 10;

-- 문자
select *
from emp
where ename = 'SCOTT'; -- 대소문자 구분

select *
from emp
where job = 'SALESMAN';

-- 날짜 (구분자는 생략가능)
select *
from emp
where hiredate < '19820101';
-- where hiredate < '1982/01/01';

SQL 문자를 쓸 때는 ‘ ‘ ( “는 쓰지않는다)

 

논리연산자

-- 논리연산자
-- and, or, not 

select *
from emp
where deptno = 10
and job = 'MANAGER';

select *
from emp
where deptno = 10
or job = 'MANAGER';

select *
from emp
where not deptno = 10;

select *
from emp
where deptno <> 10; -- != , <> 같지 않다는 뜻의 연산자

select *
from emp
where not (sal >= 2000 and sal <= 3000);

select *
from emp
-- where sal >= 2000 and sal <= 3000;
where not (sal < 2000 or sal > 3000);


-- and 와 or 를 간단하게 쓸 수 있는 연산자

-- between 값1 and 값2
select *
from emp
where sal between 2000 and 3000;

select *
from emp
where sal not between 2000 and 3000;

-- in()
select *
from emp
where comm = 300 or comm = 500 or comm = 1400; 

select *
from emp
where comm in(300, 500, 1400);

select *
from emp
where comm not in(300, 500, 1400);

 

like 연산자와 와일드카드

-- like 연산자와 와일드카드
-- _ : 하나의 문자가 반드시 있어야 한다.
-- % : %가 오는 자리에는 값이 없어도 되고 하나 이상 여러 글자가 있어도 된다.
-- 값의 일부를 가지고 조회하는 방식
-- like 만 쓰면 의미가 없어 _, % 와 같이 쓴다.

select *
from emp
where ename like 'F%';

select *
from emp
where ename like '%R';

select *반
from emp
where ename like '%A%';

select *
from emp
where ename like '_L%';

-- like의 부정
select *
from emp
where ename not like '_L%';

 

null 데이터 조건절에서 조회

-- null 데이터 조건절에서 조회
-- is null
-- is not null
-- nvl(null, 바꾸고 싶은 값) : 값의 타입이 일치해야 한다
select *
from emp
where comm is null;

select sal, sal * 12 + nvl(comm,0)
from emp
where comm is not null;

 

정렬(오름, 내림)

-- 정렬(오름, 내림)
-- order by 컬럼명 asc 오름 / desc 내림
-- asc은 생략가능 (기본값) 

select *
from emp
order by sal asc;

select *
from emp
order by sal desc;

select *
from emp
order by hiredate desc;

select *
from emp
order by sal desc, ename asc; -- sal desc 기준으로 ename asc 정렬


-- 부서로 정렬하고 이름으로 정렬
select *
from emp
order by deptno;

select *
from emp
order by ename;

 

 


오늘 UI구현시험은 말아먹었다. 왜 피그마도 못하는 걸까..

진짜 열심히 만들었는데 프로토타입이 실행이 안된다..

암튼 그냥 한숨쉬고 있는데 단위평가 점수가 28점이라 보니까 아직 체점 중이어서 그렇다고, ㅋㅋ 근데 그와중에 인터넷에 치면 다 나오는 서술형을 하나 틀렸다는 걸 알게 되었다.

옆 짝꿍이랑 답 맞춰보니 테스크 플로우를 써야하는데 유저 플로우라고 써놓았다.

이 참에 둘다 뭔지 모르고 인터넷 보고 쓴거니.. 정리하기로

 

테스크 플로우와 유저 플로우

더보기
  • 유저 플로우: 사용자가 특정 목표를 달성하기 위해 시스템이나 서비스와 상호 작용하는 과정전체적으로 나타냅니다. 사용자의 의도, 감정, 환경 등을 고려하여 사실적인 경로뿐만 아니라 가능성 있는 경로까지 포함합니다.

 

  • 테스크 플로우: 사용자가 특정 작업을 완료하기 위해 수행하는 단계들을 구체적으로 보여줍니다. 각 단계에서 사용자가 수행하는 행동사용하는 기능에 초점을 맞춥니다.

지난 시간 서버를 이용해 채팅하는 프로그래밍을 만들어봤다.

오늘은 이를 응용해 멀티채팅을 만들어 볼 것이다.

 

서버 코드 :

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;

public class TcpIpMultichatServer {
	// 사용자의 이름과 클라이언트와 연결된 소켁 정보 관리 HashMap 사용

	HashMap clients;

	TcpIpMultichatServer() {
		clients = new HashMap();
		// HashMap에 동기화를 걸어 스레드가 동시에 접근하지 못하도록 한다
		// 기존 HashMap을 동기화처리가 안되어 있음. 필요할 때 직접 동기화 처리를 하도록
		Collections.synchronizedMap(clients); // 컬렉션의 동기화

	}

	// 서브소켓 생성, 클라이언트와 연결
	public void start() {

		ServerSocket serverSocket = null;
		Socket socket = null;

		try {

			serverSocket = new ServerSocket(7777);
			System.out.println("서버가 시작되었습니다.");

			// 서버는 죽으면 안된다
			// 무한하게 클라이언트의 요청을 받기
			while (true) {
				socket = serverSocket.accept();
				System.out.println("[" + socket.getInetAddress() + ":" + socket.getPort() + "]" + "에서 접속했습니다.");

				// 현재 접속된 클라이언트의 정보를 넘긴다
				ServerReceiver thread = new ServerReceiver(socket);
				thread.start();
			}

			// 클라이언트의 정보가 들어오면 HashMap에 저장해야 한다

		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public static void main(String[] args) {
		// 소켓을 여러 개 만들 수 있다면 멀티 가능
		// 콜렉션(ArrayList)을 이용해 소켓정보를 저장

		// command + option + up / down 한줄 복사
		// command + F11 샐행

		new TcpIpMultichatServer().start();

	}

	class ServerReceiver extends Thread {

		Socket socket;
		DataInputStream in;
		DataOutputStream out;

		public ServerReceiver(Socket socket) {
			this.socket = socket;

			try {
				in = new DataInputStream(socket.getInputStream());
				out = new DataOutputStream(socket.getOutputStream());
			} catch (IOException e) {

			}
		}

		void sendToAll(String msg) { // 현재 접속한 모든 클라이언트에게 메세지 전달
			Iterator it = clients.keySet().iterator();

			while (it.hasNext()) {
				try {
					DataOutputStream out = (DataOutputStream) clients.get(it.next());
					out.writeUTF(msg);
				} catch (Exception e) {

				}
			}
		}

		// 데이터를 받아서 연결된 클라이언트에게 보내는 작업
		// 클라이언트의 이름을 받아 그 사람이 접속했다는 것을 이름으로 알려주기
		@Override
		public void run() {
			String name = "";

			try {

				name = in.readUTF();
				sendToAll("#" + name + "님이 들어오셨습니다.");

				clients.put(name, out);
				System.out.println("현재 서버접속자 수는 " + clients.size() + "입니다.");

				while (in != null) {
					sendToAll(in.readUTF());
				}

			} catch (IOException e) {

				// 대화하다 클라이언트가 나갈 때
				// 클라이언트 삭제
			} finally {
				sendToAll("#" + name + "님이 나가셨습니다.");
				clients.remove(name);
				System.out.println("[" + socket.getInetAddress() + ":" + socket.getPort() + "]" + "에서 접속을 종료하였습니다.");
				System.out.println("현재 서버접속자 수는 " + clients.size() + "입니다.");
			}
		}

	}

}

 

클라이언트 코드 :

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Scanner;

public class TcpIpMultichatClient {

	public static void main(String[] args) {
		// 접속할 서버에 대한 정보 세팅

		// 서버의 정보를 문자열로 처리
		String serverIP = "192.168.105.80";

		Scanner sc = new Scanner(System.in);
		System.out.println("대화명 입력 >>");
		String name = sc.next();

		try {
			Socket socket = new Socket(serverIP, 7777);
			System.out.println("서버에 연결되었습니다");

			// 메세지 전송
			Thread sender = new Thread(new ClientSender(socket, name));

			// 메세지 수신
			Thread receiver = new Thread(new clientReceiver(socket));

			sender.start();
			receiver.start();

		} catch (UnknownHostException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}

	} // main end

	static class ClientSender extends Thread {

		Socket socket;
		DataOutputStream out;
		String name;

		public ClientSender(Socket socket, String name) {
			this.socket = socket;
			this.name = name;

			try {
				out = new DataOutputStream(socket.getOutputStream());
			} catch (Exception e) {

			}

		}

		// 문자메세지 입력
		@Override
		public void run() {
			Scanner sc = new Scanner(System.in);

			try {
				if (out != null) {
					out.writeUTF(name);
				}

				while (out != null) {
					out.writeUTF("[" + name + "]" + sc.nextLine());
				}

			} catch (Exception e) {

			}
		}

	}

	// 문자메세지 받기
	static class clientReceiver extends Thread {

		Socket socket;
		DataInputStream in;
		String name;

		public clientReceiver(Socket socket) {
			this.socket = socket;

			try {
				in = new DataInputStream(socket.getInputStream());
			} catch (Exception e) {

			}

		}

		@Override
		public void run() {
			while (in != null) {

				try {
					System.out.println(in.readUTF());
				} catch (IOException e) {
					e.printStackTrace();
				}

			}
		}

	}

}

 

 

오라클 DB

scott 계정까지 생성완료.

 

table = 객체 (어떤 정보를 표현하고자 하는 대상) → 속성과 기능

프로그래밍에서 객체라는 말이 나오면 최우선적으로 속성과 기능을 떠올릴 것.

데이터베이스에서는 속성을 떠올릴 것.

이 속성을 표현한 것이 컬럼, 여러개의 컬럼이 모여 한 객체의 정보를 나타낸다

 

컬럼 가로 한 줄을 ‘레코드’라고 한다 = 튜플이라고도 함. 

컬럼명1      
       
       

 

테이블이 불러와지지 않는 맥 동료들도 많았는데 난 다행히 불러와졌다.

주말 내 삽질해서 얻어낸 노하우를 다른 맥 동료들에게도 알려 무사히 수업에 따라감..

// 문자 기반 스트림

Reader, Writer -> 인터페이스

 

// Reader

// 데이터를 읽어온다

read(), read(char[] c), read(char[] c, int off, int len)

 

// Writer

writer(int c), writer(char[] c), writer(char[] c, int off, int len)

writer(String srt), writer(String str, int off, int len)

 

// 문자 기반 보조 스트림

BufferedReader, BufferWriter

 

FileReader, Filewriter 로 문자열 읽어오기

import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;

public class FileReaderTest1 {

	public static void main(String[] args) {
		// FileReader, Filewriter

		try {
			FileInputStream fis = new FileInputStream("test.txt");
			FileReader fr = new FileReader("test.txt");

			int data = 0;
			while ((data = fis.read()) != -1) {
				System.out.print((char) data);
			}

			System.out.println();
			fis.close();

			while ((data = fr.read()) != -1) {
				System.out.print((char) data);
			}

			fr.close();

		} catch (IOException e) {
			e.printStackTrace();
		}

	}

}

 

파일 내용 :

Hello, 안녕하세요?

 

출력 :

Hello, 안녕하세요? // 바이트 기반으로 읽어오면 한글 깨짐이 생긴다
Hello, 안녕하세요? // 문자 기반으로 읽어오면 한글 깨짐이 생기지 않는다

 

 

문자 기반 보조 스트림으로 파일 읽어오기

BufferedReader, BufferWriter

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class BufferedReaderTest {

	public static void main(String[] args) {

		try {
			FileReader fr = new FileReader("src/BufferedReaderTest.java");
			BufferedReader br = new BufferedReader(fr); // 보조스트림(메인스트림)

			String line = "";

			// 보조스트림을 사용해 문자열을 라인단위로 읽어올 수 있다.
			// 더 이상 읽을 값이 없으면 null을 반환한다.
			for (int i = 1; (line = br.readLine()) != null; i++) {
				System.out.println(line);
			}

			br.close();

		} catch (IOException e) {
			e.printStackTrace();
		}

	}

}

 

출력 :

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class BufferedReaderTest {

	public static void main(String[] args) {

		try {
			FileReader fr = new FileReader("src/BufferedReaderTest.java");
			BufferedReader br = new BufferedReader(fr); // 보조스트림(메인스트림)

			String line = "";

			// 보조스트림을 사용해 문자열을 라인단위로 읽어올 수 있다.
			// 더 이상 읽을 값이 없으면 null을 반환한다.
			for (int i = 1; (line = br.readLine()) != null; i++) {
				System.out.println(line);
			}

			br.close();

		} catch (IOException e) {
			e.printStackTrace();
		}

	}

}

 

 

BufferedReader의 line으로 파일 복사하기

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

public class BufferedReaderTest {

	public static void main(String[] args) {

		try {
			FileReader fr = new FileReader("src/BufferedReaderTest.java"); // BufferedReaderTest.bak
			BufferedReader br = new BufferedReader(fr); // 보조스트림(메인스트림)
			FileWriter fw = new FileWriter("BufferedReaderTest.bak");

			String line = "";
			
			// 보조스트림을 사용해 문자열을 라인단위로 읽어올 수 있다.
			// 더 이상 읽을 값이 없으면 null을 반환한다.
			
			while ((line = br.readLine()) != null) {
				if ( !line.endsWith("\n")) {
					line += "\n";
				}
				System.out.println(line);
				fw.write(line);
			}
			
			br.close();
			fw.close();

		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}

	}

}

 

결과 :

 

				if ( !line.endsWith("\n")) {
					line += "\n";
				}

위 구문을 써야하는 이유 :

BufferedReader.readLine() 메서드는 **줄 끝 문자(\\n 또는 \\r\\n)**를 만날 때까지 문자열을 읽어 하나의 문자열로 반환

때문에 저 구문 없이 실행하면 ‘BufferedReaderTest.bak’ 파일에는 다 한줄로 코드가 복사된다.

 

 

line 과 data 로 파일을 읽어오는 방법 비교

출저 : 구글 gemini

 

 

✨ 파일클래스

파일과 관련된 정보, 작업을 처리

출저 : 자바 API

맥에서 파일경로 찾기 : command + i

 

 

파일다루기

import java.io.File;
import java.io.IOException;

public class FileTest1 {

	public static void main(String[] args) {
		// File 
		// 자바에서 제공하는 라이브러리
		// 파일 또는 디렉토리 정보를 다루는 기능
		// 생성자에 정의하는파일 정보가 없어도 오류가 발생하지 않는다.
		// 파일이 존재하지 않아도 결과가 나온다. 
		
		// File f = new File("/Users/yunakang/eclipse-workspace/IOStreamProject0530/src/FileTest1.java");
		// File f = new File("/Users/yunakang/eclipse-workspace/IOStreamProject0530/src","/FileTest1.java");
		File f = new File("/Users/yunakang/eclipse-workspace/IOStreamProject0530/src","/ABC.txt");
		
		// 파일 새로 만들기
		try {
			f.createNewFile();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		// 파일 이름 알기
		String fileName = f.getName();
		
		System.out.println(fileName); // FileTest1.java
		
		// 파일의 경로 찾기
		System.out.println(f.getPath()); // /Users/yunakang/eclipse-workspace/IOStreamProject0530/src/FileTest1.java
		
		// 파일의 절대경로
		System.out.println(f.getAbsolutePath());
		
		// 파일 이름을 바꾸며 똑같은 내용을 가진 파일을 새로 생성 (복사의 개념)
		File newFile = new File("/Users/yunakang/eclipse-workspace/IOStreamProject0530/src","/rename.txt");
		
		f.renameTo(newFile);
		
		// 필요없는 기존 파일 지우기 
		f.delete();
		
		System.out.println(newFile.getName()); 

	}

}

 

결과 :

파일이 새로 생겼다. (자바파일로 잘못만들어서 txt파일로 고쳐줬다.)

그 후 renameTo()를 이용해 파일의 이름을 바꿔 새 파일을 만들고 기존의 파일은 삭제하는 작업까지 해보았다.

 

 

파일의 목록 읽어오기

import java.io.File;

public class FileTest2 {

	public static void main(String[] args) {
		// 파일의 목록을 읽어오는 프로그래밍
		// 목록을 읽어오기 위해서 배열로 가지고 와서 저장
		
		File f = new File("/Users/yunakang/eclipse-workspace/IOStreamProject0530");
		
		// 파일이 없을 때 체크
		if (!f.exists() || !f.isDirectory()) {
			System.out.println("파일 정보가 존재하지 않습니다");
			// 프로그램 강제 종료
			System.exit(0);
		}
		
		File[] files = f.listFiles();
		
		for (int i = 0; i < files.length; i++) {
			String fileName = files[i].getName();
			// 파일인지 디렉토리인지 구분, 파일이면 괄호 안에 이름
			System.out.println(files[i].isDirectory() ? "[" + fileName + "]" : fileName) ;
		}

	}

}

 

결과 :

FileCopy.bak
score.data
.DS_Store
[bin]
.classpath
BufferedReaderTest.bak
[.settings]
.project
test.txt
123.txt
sample.data
[src]

파일들의 목록이 잘 나온다.

 

 

 

✨ 네트워크

네트워크 : 물리적으로 떨어진 컴퓨터 간의 인터넷 망을 이용해서 연결

클라이언트와 서버 간 요청과 응답을 반복하는 구조

예를 들어 카톡의 경우, 우리가 카톡을 보내면 바로 상대에게 가는 것이 아니라 서버측에 전송한 후 서버가 전송해주는 구조. 서버는 중간관리자

회사에서도 개발팀에 서버측과 클라이언트측이 있음. 연차가 낮으면 클라이언트측, 연차가 높으면 서버측.

서버가 어렵다. 중요한 기술이 많다!

→ 향후 모델은 다름!

앞으로는 내 컴퓨터가 클라이언트이자 서버

예 ) 토랜트

 

 

컴퓨터의 주소 : IP

서버와 클라이언트 간 통신을 위해 주소가 필요함.

고정IP : 사용자 계정(아이디)에 IP주소 지정, 독점

유동IP : 서버에 여러 개의 IP주소 존재, 서버에 접속하는 순간 임의의 IP주소 할당

우리가 쓰는 것은 로컬IP

 

통신규약 (TCP)/ IP

데이터에 따라 보내는 방식이 다름.

데이터를 보낼 때 IP를 사용.

→ 데이터를 보낼 때는 무조건 스트림이 필요하다.

 

스트림 : 데이터를 보내는 통로

스트림을 주고 받을 때는 in 과 out, 두 개의 스트림이 필요하다.

스크림의 in/out은 상대적.

 

소켓 Socket 으로 컴퓨터를 연결해주면 스트림으로 컴퓨터와 통신

소켓도 서버와 클라이언트 하나씩 존재

서버는 두 개의 소켓과 하나의 포트로 구성

  • ServerSocket → 감시용
  • Socket → 통신용
  • port (아파트의 동과 호)

→ 포트번호는 고유하다

 

클라이언트의 소켓은 한 가지

  • Socket

서버소켓이 하는 일 : 항상 돌아가며 감지하기

 

서버와 클라이언트 만들어보기

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
import java.net.UnknownHostException;

public class TcpIpClient {

	public static void main(String[] args) {
		// 서버의 IP주소와 포트번호를 알아야 한다

		String serverIp = "192.168.105.80";

		System.out.println("서버에 연결 중 입니다. 서버IP :" + serverIp);

		Socket socket;
		try {

			socket = new Socket(serverIp, 7777);
			InputStream in = socket.getInputStream();
			DataInputStream dis = new DataInputStream(in);

			System.out.println("서버로부터 받은 메세지 : " + dis.readUTF());
			System.out.println("연결종료");

			dis.close();
			socket.close();

		} catch (UnknownHostException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();

		}
	}

}

클라이언트는 결과가 출력되고 실행이 안되지만 서버는 계속 돌아가고 있다.

서버를 꺼줘야 오류가 생기지 않는다.

 

 

 

🍀 문자채팅이 가능한 서버와 클라이언트 만들기

항상 서버 먼저 실행 후 클라이언트 실행

하나의 파일 안에 하나의 클래스에만 퍼블릭을 붙일 수 있다

 

서버 :

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;

public class TcpIpSever2 {

	public static void main(String[] args) {

		ServerSocket serverSocket = null;
		Socket socket = null;

		try {
			serverSocket = new ServerSocket(7777);
			System.out.println("서버가 준비되었습니다.");

			socket = serverSocket.accept();

			// 데이터 전송
			Sender sender = new Sender(socket);

			// 데이터 수신
			Receiver receiver = new Receiver(socket);
			
			// 독립적인 스레드로 각각 동작한다
			sender.start();
			receiver.start();

		} catch (IOException e) {
			e.printStackTrace();
		}

	}

}

// 데이터 전송
class Sender extends Thread {

	// 소켓 정보를 초기화하는 생성자
	Socket socket;
	DataOutputStream out;
	String name;

	public Sender(Socket socket) {

		this.socket = socket;

		// DataOutputStream 초기화
		// getOutputStream()을 쓸 때는 예외처리
		try {
			out = new DataOutputStream(socket.getOutputStream());
			// 사용자의 IP 주소와 포트번호 받아오기
			name = "[" + socket.getInetAddress() + " : " + socket.getPort() + "]";
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	@Override
	public void run() {
		// 입력된 값을 처리해서 보내는 부분
		Scanner sc = new Scanner(System.in);

		// 데이터를 받을 상대가 있는지 없는지 체크
		while (out != null) {
			try {
				System.out.print(">>");
				out.writeUTF(name + sc.nextLine());
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

}

//데이터 수신
class Receiver extends Thread {

	Socket socket;
	DataInputStream in;

	public Receiver(Socket socket) {
		this.socket = socket;

		try {
			in = new DataInputStream(socket.getInputStream());
		} catch (IOException e) {
			e.printStackTrace();
		}

	}

	@Override
	public void run() {
		while (in != null) {
			try {
				System.out.println(in.readUTF());
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

}

 

클라이언트 :

import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;

public class TcpIpClient2 {

	public static void main(String[] args) {
		String serverIp = "192.168.105.80";

		System.out.println("서버에 연결 중 입니다. 서버IP :" + serverIp);
		
		Socket socket;
		
		try {
			
			socket = new Socket(serverIp, 7777);
			
			Sender sender = new Sender(socket);
			Receiver receiver = new Receiver(socket);
			
			sender.start();
			receiver.start();
			
		} catch (UnknownHostException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}

	}

}

서버에 연결되었다.

혼자 대화를 해보았다.

 

 

다른 사람과 채팅해보기

클라이언트가 대화하려는 사람의 아이피를 받아 클라이언트에 넣어주기

그 다음 서버 실행하면 클라이언트 측 실행

와이파이는 와이파이끼리, 같은 와이파이를 써야한다

성공~!

 

 

 

 

다음 주, 맥 오라클 깔아야함 11g Enterprise

docker pull paliari/oracle-xe-11g

docker run --name oracle -d -p 1521:1521 paliari/oracle-xe-11g

지난 시간 스트림에 대해 배웠다.

 

스트림 : 데이터를 처리하기 위한 연산 방식의 표준화

중간연산자 : map(), filter(), distinct(). forEach()

최종연산자 : sum(), average(), max(), min()

정렬 : .sorted()

 

람다와 스트림은 자바를 하는 동안 아주 중요하다.

 

IO 입출력과 스트림

 

다른 공간에 있는 데이터를 서로 주고 받는다.

어제 배웠던 스트림과는 다른 의미의 스트림

여기서 스트림이란, 데이터를 운반하는데 사용되는 연결 통로

// Input(입력) / Output (출력)
// 컴퓨터 내부 또는 외부의 장치간 데이터 교환

// Stream : 데이터가 전송되는 통로
// 통로의 In, Out은 상대적 개념

// 바이트 기반 스트림 (사진, 동영상, 음성)
// 문자 기반 스트림
// 보조 스트림 (효율적으로 데이터를 주고 받기 위해 제공)
// 보조 스트림은 바이트 기반 보조 스트림과 문자 기반 보조 스트림이 있다.
// FIFO 구조

 

🍀 바이트 기반 스트림
// InputStream, OutputStream -> 인터페이스
// 상속받은 표준화된 기능들 오버라이딩

// InputStream
read(), read(byte[], b), read(byte[] b, int off, int len) 세 가지 형태로 데이터를 읽어올 수 있다.

// OutputStream
write(int b), write(byte[] b), write(byte[] b, int off, int len)
세 가지 형태로 데이터를 받아올 수 있다.

// 바이트 기반 보조 스트림
BufferedInputStream, BufferedOutputStream DataInputStream, DataOutputStream

 

🍀 문자 기반 스트림
// Reader, Writer -> 인터페이스

// Reader
// 데이터를 읽어온다
read(), read(char[] c), read(char[] c, int off, int len)

// Writer
writer(int c), writer(char[] c), writer(char[] c, int off, int len)
writer(String srt), writer(String str, int off, int len)


🍀 문자 기반 보조 스트림
BufferedReader, BufferWriter

 

 

배열을 이용해 입출력

toByteArray() : 스트림된 내용을 byte 배열로 반환 해 준다.

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.util.Arrays;

public class IOTest1 {

	public static void main(String[] args) {
		byte[] inSrc = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
		byte[] outSrc = null;

		ByteArrayInputStream input = null;
		ByteArrayOutputStream output = null;

		input = new ByteArrayInputStream(inSrc); // 어느 대상으로부터 Input을 할지 대상을 정해야 한다
		output = new ByteArrayOutputStream();

		// input 에서 데이터를 받아 저장하는 곳
		// 읽어오는 데이터는 1byte, int의 기본단위는 4byte
		// 데이터의 크기와는 상관없이 읽어온다
		int data = 0;

		// -1 이 아닐 때까지 데이터를 읽어온다
		while ((data = input.read()) != -1) {
			output.write(data);
		}

		outSrc = output.toByteArray(); // 스트림된 내용을 byte 배열로 반환 해 준다.

		System.out.println("input source : " + Arrays.toString(inSrc)); // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
		System.out.println("output source : " + Arrays.toString(outSrc)); // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

	}

}

한번에 1byte 만 불러오기 때문에 성능이 떨어진다.

 

한번에 많은 양의 데이터를 보낼 수 있도록 처리할 수 있다.

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.util.Arrays;

public class IOTest2 {

	public static void main(String[] args) {
		byte[] inSrc = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
		byte[] outSrc = null;
		byte[] temp = new byte[10];

		ByteArrayInputStream input = null;
		ByteArrayOutputStream output = null;

		input = new ByteArrayInputStream(inSrc); // 어느 대상으로부터 Input을 할지 대상을 정해야 한다
		output = new ByteArrayOutputStream();

		// 읽어온 데이터를 배열 temp에 담는다
		input.read(temp, 0, temp.length);
		output.write(temp, 5, 5); // temp[5]부터 5개의 데이터를 write 한다.

		outSrc = output.toByteArray(); // 스트림된 내용을 byte 배열로 반환 해 준다.

		System.out.println("input source : " + Arrays.toString(inSrc)); // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
		System.out.println("temp : " + Arrays.toString(temp)); // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
		System.out.println("output source : " + Arrays.toString(outSrc)); // [5, 6, 7, 8, 9]
	}

}

10byte를 읽어오고, output에 출력할 때는 temp[5] 부터 5byte을 출력했다.

= 필요한만큼 input 하고, 필요한만큼 output 한다

 

read()가 값을 읽어오는 방식

available()은 블락킹(blocking)없이 읽어올 수 잇는 바이트의 수를 반환한다.

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Arrays;

import javax.print.event.PrintEvent;

public class IOTest3 {

	public static void main(String[] args) {
		byte[] inSrc = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
		byte[] outSrc = null;
		byte[] temp = new byte[4];

		ByteArrayInputStream input = null;
		ByteArrayOutputStream output = null;

		input = new ByteArrayInputStream(inSrc);
		output = new ByteArrayOutputStream();
		
		System.out.println("input source : " + Arrays.toString(inSrc));
		
		// IOException 타입으로 예외처리
		// 4바이트씩 순서대로 읽어오는 구조가 아니기 때문에 available()을 이용해 다음 4바이트의 시작 위치를 찾아준다.
		try {
			while(input.available() > 0) {
				input.read(temp); // 읽어온 값의 갯수 ( 4 -> 4 -> 2 )
				output.write(temp);
				
				outSrc = output.toByteArray();
				printArrays(temp, outSrc);
				
			}
			
		}catch (IOException e) {}
	}
	
	static void printArrays(byte[] temp, byte[] outSrc) {
		System.out.println("temp : " + Arrays.toString(temp));
		System.out.println("Output Source : " + Arrays.toString(outSrc));
	}

}

 

출력 :

input source : [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
temp : [0, 1, 2, 3]
Output Source : [0, 1, 2, 3]
temp : [4, 5, 6, 7]
Output Source : [0, 1, 2, 3, 4, 5, 6, 7]
temp : [8, 9, 6, 7]
Output Source : [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 6, 7]

Output Source : [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 6, 7] 에 보면 6, 7이 함께 읽힌다.

이를 해결하기 위해 코드를 수정해보았다.

 

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Arrays;

public class IOTest4 {

	public static void main(String[] args) {
		byte[] inSrc = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
		byte[] outSrc = null;
		byte[] temp = new byte[4];

		ByteArrayInputStream input = null;
		ByteArrayOutputStream output = null;

		input = new ByteArrayInputStream(inSrc);
		output = new ByteArrayOutputStream();

		try {
			while (input.available() > 0) {
				int len = input.read(temp); // 출력을 조절
				output.write(temp, 0, len); // 원하는 위치만큼 write
			}

		} catch (IOException e) {
		}

		outSrc = output.toByteArray();

		System.out.println("Input Source : " + Arrays.toString(inSrc));
		System.out.println("temp : " + Arrays.toString(temp)); // [8, 9, 6, 7]
		System.out.println("Output Source : " + Arrays.toString(outSrc)); // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

	}
}

 

출력 :

Input Source : [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
temp : [8, 9, 6, 7]
Output Source : [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

read() 메소드 자체는 바뀌지 않았지만 Output 의 값은 9까지 들어갔다.

 

 

파일기반의 IO 스트림

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

public class FileViewer {

	public static void main(String[] args) {

		// FileInputStream , FileOutputStream

		// 무조건 예외처리를 해야한다.
		try {
			FileInputStream fis = new FileInputStream("src/FileViewer.java");

			int data = 0;

			while ((data = fis.read()) != -1) {
				char c = (char) data;
				System.out.print(c);
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}

	}

}

 

결과 :

	public static void main(String[] args) {

		// FileInputStream , FileOutputStream

		// 무조건 예외처리를 해야한다.
		try {
			FileInputStream fis = new FileInputStream("src/FileViewer.java");

			int data = 0;

			while ((data = fis.read()) != -1) {
				char c = (char) data;
				System.out.print(c);
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}

	}

}

한글 주석이 특수문자 처리되어 결과가 나온다.

복붙이 바로 이런 스트림을 이용한 것..!

 

 

파일 자체를 복사해 새로 생성하는 프로그램

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

public class FileCopy {

	public static void main(String[] args) {
		
		try {
			FileInputStream fis = new FileInputStream("src/FileCopy.java");
			FileOutputStream fos = new FileOutputStream("FileCopy.bak");
			
			int data = 0;
			
			while((data = fis.read()) != -1) {
				fos.write(data);
			}
			
			// 스트림을 사용할 때는 항상 닫아줘야 한다.
			// 서버부하를 막기 위함.
			fis.close();

			
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}

	}

}

실행 후 새로고침을 하면 복사한 파일이 생긴다.

 

 

 

보조스트림 연결해보기

main 스트림을 도와주는 역할을 한다.

보조스트림 자체로 구현 X

 

BufferedInputStream, BufferedOutputStream 보조스트림

import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

public class BufferedOutputTest {

	public static void main(String[] args) {
		try {

			FileOutputStream fos = new FileOutputStream("123.txt");

			BufferedOutputStream bos = new BufferedOutputStream(fos, 5); // 생성자에 메인스트림을 연결해야 한다.

			for (int i = '1'; i <= '9'; i++) {
				bos.write(i);
			}

		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}

	}

}

 

출력 : (파일에 출력됨)

12345

5바이트씩 가지고 와야 하는데 나머지는 5바이트를 채우지 못해 가지고 오지 않았다.

→ 버퍼에 데이터가 다 차지 않더라도 flush()를 이용해 데이터를 밀어줄 수 있다.

 

수정된 코드 :

import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

public class BufferedOutputTest {

	public static void main(String[] args) {
		try {

			FileOutputStream fos = new FileOutputStream("123.txt");

			BufferedOutputStream bos = new BufferedOutputStream(fos, 5); // 생성자에 메인스트림을 연결해야 한다.

			for (int i = '1'; i <= '9'; i++) {
				bos.write(i);
			}
			
			bos.flush();
			// 닫아주는 순서는 역순
			bos.close();
			fos.close();

		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}

	}

}

 

출력 : (파일에 출력됨)

123456789

→ 보조스트림을 닫아주는 것 만으로도 동작한다.

  		// bos.flush();
			// 닫아주는 순서는 역순
			bos.close();
			// fos.close();

 

 

 

DataInputStream, DataOutputStream 보조스트림

8가지 기본 자료형의 단위로 읽고, 쓰기 가능

Output에서 write한 순서대로 read 해야한다

import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

public class DataInputStreamTest {

	public static void main(String[] args) {
		
		// DataInputStrem, DataOutputStream
		// 8가지 기본 자료형의 단위로 읽고, 쓰기 가능
		// write한 순서대로 read 해야한다
		
		try {
			
			FileInputStream fis = new FileInputStream("sample.data");
			DataInputStream dis = new DataInputStream(fis);
			
			System.out.println(dis.readInt());
			System.out.println(dis.readFloat());
			System.out.println(dis.readBoolean());
			
			dis.close();
			
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}

	}

}

 

결과 :

10
20.0
true

 

import java.io.DataOutputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

public class DataOutputStreamTest {

	public static void main(String[] args) {
		
		// DataInputStrem, DataOutputStream
		// 8가지 기본 자료형의 단위로 읽고, 쓰기 가능
	
        try {
			
			FileOutputStream fos = new FileOutputStream("sample.data");
			DataOutputStream dos = new DataOutputStream(fos);
			
			dos.writeInt(10);
			dos.writeFloat(20.0f);
			dos.writeBoolean(true);
			
			dos.close();
			
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		

	}

}

 

 

파일을 읽어와 연산 처리하기

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

public class DataInputStreamTest2 {

	public static void main(String[] args) {
		
		//int[] score = { 100, 90, 95, 85, 50 };
		int max = Integer.MIN_VALUE;
        int min = Integer.MAX_VALUE;
        int sum = 0;
        int count = 0;
		// 최대값
		// 최소값
		// 합계
		// 평균
		
		try {
			
			FileInputStream fis = new FileInputStream("score.data");
			DataInputStream dis = new DataInputStream(fis);
			
			while(dis.available() > 0) {
				int score = dis.readInt();
                max = Math.max(max, score);
                min = Math.min(min, score);
                sum += score;
                count++;
			}
			
		}catch (FileNotFoundException e) {
			e.printStackTrace();
        } catch (IOException e) {
        	e.printStackTrace();
        }
	
	 double avg = (double) sum / count;


     System.out.println("Max: " + max);
     System.out.println("Min: " + min);
     System.out.println("Sum: " + sum);
     System.out.println("Avg: " + avg);
 }
}

 

결과 :

Max: 100
Min: 50
Sum: 420
Avg: 84.0

 

import java.io.DataOutputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;


public class DataOutputStreamTest2 {

	public static void main(String[] args) {
		
		int[] score = { 100, 90, 95, 85, 50 };
		
		 try {
				
				FileOutputStream fos = new FileOutputStream("score.data");
				DataOutputStream dos = new DataOutputStream(fos);
				
				for(int i : score) {
					dos.writeInt(i);
				}
				dos.close();
				
			} catch (FileNotFoundException e) {
				e.printStackTrace();
			} catch (IOException e) {
				e.printStackTrace();
			}

	}

 

 

파일을 읽어와 연산 처리하기

  • 배열에 스트림연산을 적용해 풀어보기
import java.io.DataInputStream;
import java.io.EOFException;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;

public class DataInputStreamTest3 {

	public static void main(String[] args) {
		
		FileInputStream fis = null;
		DataInputStream dis = null;
		ArrayList<Integer> list = new ArrayList<Integer>();
		
		try {
			
			fis = new FileInputStream("score.data");
			dis = new DataInputStream(fis);
			
			while(true) {
				list.add(dis.readInt());
			}
			
			
		} catch (EOFException e) {
			System.out.println("파일 읽기 완료");
        } catch (FileNotFoundException e) {
        	e.printStackTrace();
	    } catch (IOException e) {
        	e.printStackTrace();
        }
		
		System.out.println(list);
		
		// 배열의 데이터를 스트림을 이용해 int형의 일차원 배열 만들기
		int[] arr = list.stream().mapToInt(i -> i).toArray();
		
		int max = Arrays.stream(arr).max().getAsInt();
		int min = Arrays.stream(arr).min().getAsInt();
		int sum = Arrays.stream(arr).sum();
		double avg = Arrays.stream(arr).average().getAsDouble();
		
		   System.out.println("Max: " + max);
	     System.out.println("Min: " + min);
	     System.out.println("Sum: " + sum);
	     System.out.println("Avg: " + avg);
	
 }
}

결과 :

파일 읽기 완료
[100, 90, 95, 85, 50]
Max: 100
Min: 50
Sum: 420
Avg: 84.0

 

 

 

문자 기반 스트림

Reader, Writer -> 인터페이스

 

 

내일 이어서..

내일까지 자바를 배움.

람다식

 

Map 함수형인터페이스로 값 출력하기

// Map
		Map<String, String> map = new HashMap<String, String>();
		map.put("1", "1");
		map.put("2", "2");
		map.put("3", "3");
		map.put("4", "4");

		// iterator 방식
		Iterator<Map.Entry<String, String>> entries = map.entrySet().iterator();

		while (entries.hasNext()) {
			Map.Entry<String, String> entry = entries.next();
			System.out.println("key : " + entry.getKey() + ", value :" + entry.getValue());
		}

		System.out.println();

		// 향상된 for문
		for (Map.Entry<String, String> entry : map.entrySet()) {
			System.out.println("key : " + entry.getKey() + ", value :" + entry.getValue());
		}
		
		System.out.println();
		
		// 함수형 인터페이스
		map.forEach((k,v) -> System.out.println(k + "," + v));

이어서 람다식을 사용해본다.

 

java.util.function 패키지 예제

import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;

public class LambdaTest4 {

	public static void main(String[] args) {
		Supplier<Integer> s = () -> (int) (Math.random() * 100) + 1;
		Consumer<Integer> c = i -> System.out.print(i + ", ");
		Predicate<Integer> p = i -> i % 2 == 0;
		Function<Integer, Integer> f = i -> i / 10 * 10; // i의 일의 자리를 없앤다.

		List<Integer> list = new ArrayList<>();
		makeRandomList(s, list);
		System.out.println(list);
		printEvenNum(p, c, list);
		List<Integer> newList = dosomthing(f, list); 
		System.out.println(newList);

	}

	// 메소드의 매개변수를 제한할 떄 제네릭 타입 사용하기
	// list에 나온 수들의 1의 자리를 없앤 값 newList에 넣기
	static <T> List<T> dosomthing(Function<T, T> f, List<T> list) {
		// ArrayList는 사이즈를 지정하지 않아도 기본 크기가 10
		// 크기를 지정해두면 속도가 좋아진다
		List<T> newList = new ArrayList<T>(list.size());

		for (T i : list) {
			newList.add(f.apply(i));
		}

		return newList;
	}

	// list에서 짝수만 출력하기
	static <T> void printEvenNum(Predicate<T> p, Consumer<T> c, List<T> list) {
		System.out.print("[");
		
		for (T i : list) {
			if (p.test(i)) {
				c.accept(i);
			}
		}
		System.out.println("]");
	}

	// 100 이하의 랜덤 수 10개를 list에 넣기
	static <T> void makeRandomList(Supplier<T> s, List<T> list) {
		for (int i = 0; i < 10; i++) {
			list.add(s.get());
		}
	}
}

 

람다식의 메소드 참조 방식 (매개변수 생략, ::)

import java.util.function.BiFunction;
import java.util.function.Function;

public class LambdaTest5 {

	public static void main(String[] args) {
		// 람다식의 메소드 참조 방식 (매개변수 생략, ::)
		// 람다식이 하나의 메소드만 호출하는 경우에는 메소드 참조라는 방식으로 호출할 수 있다.

		// 문자열을 정수로 바꿔준다
		// Function<String, Integer> f = s -> Integer.parseInt(s);
		Function<String, Integer> f = Integer::parseInt; // 메소드 참조
		int i = f.apply("100");
		System.out.println(i + 100);

		// BiFunction<String, String, Boolean> f2 = (s1, s2) -> s1.equals(s2); 
		// 문자열이 같은지 비교
		BiFunction<String, String, Boolean> f2 = String::equals; // 메소드 참조
		boolean b = f2.apply("hello", "hello"); // 문자열이 같다
		System.out.println(b); // 문자열이 같으므로 true

	}

}

 

 

System.out.println() 의 메소드 참조 방식 

 // BiFunction<String, String, Boolean> f2 = (s1, s2) -> s1.equals(s2); // 문자열이 같은지 비교
		BiFunction<String, String, Boolean> f2 = String::equals; // 메소드 참조
		boolean b = f2.apply("hello", "hello"); // 문자열이 같다
		System.out.println(b); // 문자열이 같으므로 true
		
		ArrayList<Integer>list = new ArrayList<Integer>();
		list.add(1);
		list.add(2);
		list.add(3);
		list.add(4);
		list.add(5);
		
		//list.forEach(i -> System.out.println(i));
		list.forEach(System.out :: println);

많이 쓴다!!

위 코드들은 베이스 코드. 응용에 앞서 베이스코드를 혼자 짤 수 있어야 한다.

대표하는 베이스 코드는 머리 속에 넣어둬야 한다. → 그냥 외워

 

Stream

: 데이터를 추상화(표준화) 하여 사용하기 편하게 연산하는 방식을 통일한다

 

배열을 스트림화

package Stream0529;

import java.util.Arrays;
import java.util.stream.IntStream;
import java.util.stream.Stream;

public class StreamTest {
	
	// Stream
	// 객체형식으로 되어있는 데이터를 다루는 변수 구조 : 배열, 컬렉션
	// 데이터를 추상화(표준화) 하여 사용하기 편하게 연산하는 방식을 통일한다
	// 중간연산, 최종연산
	
	// 배열을 스트림화 하는 3가지 방식
	
	Stream<String> stream1 = Stream.of("a", "b", "c"); // 가변인자
	Stream<String> stream2 = Stream.of(new String[] {"a", "b", "c"}); // 직접입력
	Stream<String> stream3 = Arrays.stream(new String[] {"a", "b", "c"}); // Arrays 사용
	
	// 기본형에 IntStream 으로 스트림화 하기
//	IntStream IntStream.of(int ...value);
//	IntStream intStream.of(int[]);
//	IntStream Arrays.stream(int[]);
			

}

 

연산자 : map(), filter(), distinct(). forEach() 사용

package Stream0529;

import java.io.File;
import java.util.stream.Stream;

public class StreamTest2 {

	public static void main(String[] args) {
		File[] fileArr = { new File("Ex1.java"), new File("Ex1.bak"), new File("Ex2.java"), new File("Ex1"),
				new File("Ex1.txt") };

//		File f1 = fileArr[0];
//		System.out.println(f1.getName()); // Ex1.java

		Stream<File> fileStream = Stream.of(fileArr);

		Stream<String> filenameStream = fileStream.map(File::getName);
		filenameStream.forEach(System.out::println);

		// 스트림을 다시 생성해서 초기상태로 만들어야한다.
		fileStream = Stream.of(fileArr);

		// 연산자 : map(), filter(), distinct(). forEach() 사용
		fileStream.map(File::getName) // Stream<File> -> Stream<String>
				.filter(s -> s.indexOf('.') != -1) // 확장자가 없는 것은 제외
				.map(s -> s.substring(s.indexOf('.') + 1)) // 확장자만 유출
				.map(String::toUpperCase) // 모두 대문자로 변환
				.distinct() // 중복제거
				.forEach(System.out::print); // JAVABAKTXT

		System.out.println();

	}

}

 

기본형을 스트림화

package Stream0529;

import java.util.stream.IntStream;

public class StreamTest3 {
	public static void main(String[] args) {

		IntStream intStream = IntStream.of(1, 2, 3, 4, 5);
		intStream.forEach(System.out::println);

		System.out.println("============");
		IntStream.range(1, 5).forEach(System.out::println); // 마지막은 포함하지 않는다

		System.out.println("============");
		IntStream.rangeClosed(1, 5).forEach(System.out::println); // 마지막까지 포함된다

		System.out.println("============");
		IntStream.rangeClosed(1, 5).map(n -> n * n).filter(n -> n % 2 == 0).forEach(System.out::println);

		// sum(), average(), max(), min()

		System.out.println("============");

		// 1부터 5까지의 합
		int sum = IntStream.rangeClosed(1, 5).sum();
		System.out.println(sum);

		// 1부터 5까지의 합의 평균 구하기
		// getAsDouble() 실수형으로 값을 바꿔준다
		double avg = IntStream.rangeClosed(1, 5).average().getAsDouble();
		System.out.println(avg);

		// 최대값 구하기
		int max = IntStream.rangeClosed(1, 5).max().getAsInt();
		System.out.println(max);

		// 최소값 구하기
		int min = IntStream.rangeClosed(1, 5).min().getAsInt();
		System.out.println(min);

		// anyMatch()
		// 스트림의 요소 중 어느 하나라도 주어진 술어를 만족하면 'true'를 반환
		boolean any = IntStream.rangeClosed(1, 5).anyMatch(n -> n == 7);
		System.out.println(any);

	}
}

 

 

스트림 정렬

기본정렬 방식에 대한 정의가 클래스 안에 되어있어야 한다!

String 안에 기본 정렬(오름차순)이 있었던 것처럼!

 

정렬에 대해 복습하며 제네릭으로 바꿔보기)

더보기

제네릭

 

제네릭 적용 전

package come.list;

import lombok.AllArgsConstructor;
import lombok.ToString;

@AllArgsConstructor
@ToString
public class Student implements Comparable {
	String name;
	int ban;
	int no;
	int kor, eng, math;

	int getTotal() {
		return kor + eng + math;
	}

	float getAverage() {
		return (int) ((getTotal() / 3f) * 10 + 0.5) / 10f;
	}

	// 학생의 이름에 따라 정렬
	@Override
	public int compareTo(Object o) { // Object o -> Student

		if (o instanceof Student) {
			Student other = (Student) o; // 값으로 가지고 와서 사용할 수 있다.

			// return this.no - other.no; // 오름차순
			// return other.no - this.no; // 내림차순
			return this.name.compareTo(other.name); // 오름차순
			// return this.name.compareTo(other.name) * -1; // 내림차순
		}
		return -1;
	}
}

 

 

제네릭 적용 후

public class Student implements Comparable<Student> {
	String name;
	int ban;
	int no;
	int kor, eng, math;

	int getTotal() {
		return kor + eng + math;
	}

	float getAverage() {
		return (int) ((getTotal() / 3f) * 10 + 0.5) / 10f;
	}

	// 학생의 이름에 따라 정렬
	@Override
	public int compareTo(Object o) { // Object o -> Student

			return this.name.compareTo(s.name); // 오름차순
			
		}
}

 

 

 

스트림으로 정렬해보기

package Stream0529;

public class Student implements Comparable<Student>{
	
	String name;
	int ban;
	int totalScore;

	public Student(String name, int ban, int totalScore) {
		this.name = name;
		this.ban = ban;
		this.totalScore = totalScore;
	}

	@Override
	public String toString() {
		return "Student [name=" + name + ", ban=" + ban + ", totalScore=" + totalScore + "]";
	}
	
	String getName() {
		return name;
	}
	
	int getBan() {
		return ban;
	}
	
	int getTotalScore() {
		return totalScore;
	}
	
	@Override
	public int compareTo(Student s) {
		// TODO Auto-generated method stub
		return s.totalScore - this.totalScore; // 총점으로 내림차순
	}

}
package Stream0529;

import java.util.Comparator;
import java.util.stream.Stream;

public class StreamTest4 {

	public static void main(String[] args) {
		// 가변형 방식의 스트림
		
		Stream<Student> studentStream = Stream.of(
				new Student("남파이", 3, 142),
				new Student("강자바", 3, 298),
				new Student("최타임", 2, 90),
				new Student("코틀린", 5, 273),
				new Student("크립트", 7, 194),
				new Student("이루비", 2, 210),
				new Student("안쿼리", 1, 198)
				);
		
		//studentStream.sorted(Comparator.comparing((Student s) -> s.getBan()))
		studentStream.sorted(Comparator.comparing(Student::getBan))
		.forEach(System.out::println);

	}

}

 

결과 :

Student [name=안쿼리, ban=1, totalScore=198]
Student [name=최타임, ban=2, totalScore=90]
Student [name=이루비, ban=2, totalScore=210]
Student [name=남파이, ban=3, totalScore=142]
Student [name=강자바, ban=3, totalScore=298]
Student [name=코틀린, ban=5, totalScore=273]
Student [name=크립트, ban=7, totalScore=194]

반을 기준으로 오름차순 되어 나온다.

✨ 람다식

 

익명 객체를 람다식으로 대체 가능한 이유 →

람다식도 실제로는 익명 객체이고 익명 객체의 메서드와 람다식의 매개변수 타입과 개수, 그리고 반환값이 일치하기 때문.

 

람다식은 객체이기 때문에 참조변수가 필요, 그래서 필요한 게 함수형 인터페이스

함수형 인터페이스 타입을 통해 참조변수로 람다식을 참조할 수 있다.

단, 함수형 인터페이스의 메서드와 람다식의 매개변수 개수와 반환타입이 일치해야한다.

람다식을 다루기 위한 인터페이스 : 함수형 인터페이스 (functional interface)

@FunctionalInterface

interface MyFunction { // 함수형 인처페이스 myFunction을 정의
	public abstract int max(int a, int b);
}

@FunctionalInterface 를 붙이지 않아도 오류가 나진 않지만 붙이면 올바르게 코드를 짜고 있는지 확인해준다.

 

함수형 인터페이스는 오직 하나의 추상 메서드만 정의되어야 한다.

그래야 람다식과 인터페이스가 1 : 1로 연결될 수 있기 때문.

 

함수와 메서드의 차이

→ 근본적으로 동일, 함수는 일반적 용어, 메서드는 객체지향개념 용어

→ 함수는 클래스에 독립적, 메서드는 클래스에 종속적

 

java.util.function 패키지

→ 자주 사용되는 다양한 함수형 인터페이스를 제공

함수형 인터페이스 메서드 설명
java.lang.Runnable void run() 매개변수도 없고, 반환값도 없음.
Supplier<T> T get() 매개변수는 없고, 반환값만 있음.
Consumer<T> void accept (T t) Supplier와 반대로 매개변수만 있고, 반환값이 없음
Function<T, R> R apply (T t) 일반적인 함수, 하나의 매개변수를 받아서 결과를 반환
Predicate<T> boolean test (T t) 조건식을 표현하는데 사용됨. 매개변수는 하나, 반환타입은 boolean

 

매개변수가 2개인 함수형 인터페이스

함수형 인터페이스 메서드 설명
BiConsumer<T, U> void accept (T t, U u) 두 개의 매개변수만 있고, 반환값이 없음
BiPredicate<T,U> boolean test(T t, U u) 조건식을 표현하는데 사용됨. 매개변수는 둘, 반환값은 boolean
BiFunction<T, U, R> R apply (T t, U u) 두 개의 매개변수를 받아서 하나의 결과를 반환

(Bi 는 두개라는 뜻. )

3개의 매개변수가 필요하면 만들면 된다.

 

UnaryOperator와 BinaryOperator

함수형 인터페이스 메서드 설명
UnaryOperator T apply (T t) Function의 자손, Function과 달리 매개변수와 결과 타입이 같다.
BinaryOperator T apply (T t, T t) BiFunction의 자손, BiFunction과 달리 매개변수와 결과 타입이 같다.

매개변수 타입과 반환타입이 모두 일치,

UnaryOperator의 조상은 Function, BinaryOperator의 조상은 BiFunction

 

 

+ Recent posts