<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>JEREGIM.log</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Wed, 05 Apr 2023 13:05:08 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>JEREGIM.log</title>
            <url>https://velog.velcdn.com/images/jere-gim/profile/082623b2-dd53-4bf3-a066-16a9d8fc7fdf/image.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. JEREGIM.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/jere-gim" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[SQL 조회 2]]></title>
            <link>https://velog.io/@jere-gim/SQL-%EC%A1%B0%ED%9A%8C-2</link>
            <guid>https://velog.io/@jere-gim/SQL-%EC%A1%B0%ED%9A%8C-2</guid>
            <pubDate>Wed, 05 Apr 2023 13:05:08 GMT</pubDate>
            <description><![CDATA[<h1 id="📌subquery">📌subquery</h1>
<ul>
<li><p>subquery란 SELECT, INSERT, UPDATE, DELETE에 포함된 query를 말한다.</p>
</li>
<li><p>outer query : subquery를 포함하는 query를 말한다.</p>
</li>
<li><p>subquery는 ()안에 기술된다.</p>
</li>
</ul>
<p><strong>| id가 14인 임직원보다 생일이 빠른 임직원의 id,이름,생일을 출력하라</strong></p>
<pre><code class="language-sql">mysql&gt; SELECT birth_date FROM employee WHERE id = 14;
+------------+
| birth_date |
+------------+
| 1992-08-04 |
+------------+
1 row in set (0.04 sec)

mysql&gt; SELECT id, name, birth_date FROM employee WHERE birth_date &lt; &#39;1992-08-04&#39;;
+----+--------+------------+
| id | name   | birth_date |
+----+--------+------------+
|  1 | MESSI  | 1987-02-01 |
|  5 | DINGYO | 1990-11-05 |
|  6 | JULIA  | 1986-12-11 |
|  9 | HENRY  | 1982-05-20 |
| 10 | NICOLE | 1991-03-26 |
| 13 | JISUNG | 1989-07-07 |
+----+--------+------------+</code></pre>
<ul>
<li>위의 두 쿼리를 한 문장으로 합치면 다음과 같다.
```sql
mysql&gt; SELECT id, name, birth_date FROM employee
  -&gt; WHERE birth_date &lt; (SELECT birth_date FROM employee WHERE id = 14);</li>
<li>----+--------+------------+
| id | name   | birth_date |</li>
<li>----+--------+------------+
|  1 | MESSI  | 1987-02-01 |
|  5 | DINGYO | 1990-11-05 |
|  6 | JULIA  | 1986-12-11 |
|  9 | HENRY  | 1982-05-20 |
| 10 | NICOLE | 1991-03-26 |
| 13 | JISUNG | 1989-07-07 |</li>
<li>----+--------+------------+<pre><code></code></pre></li>
</ul>
<p><strong>| id가 1인 임직원과 같은 부서, 같은 성별인 임직원들의 id,이름,직군을 출력하라</strong></p>
<pre><code class="language-sql">mysql&gt; SELECT id, name, position FROM employee
    -&gt; WHERE (dept_id, sex) = (
    -&gt; SELECT dept_id, sex
    -&gt; FROM employee
    -&gt; WHERE id = 1
    -&gt; );
+----+-------+-----------+
| id | name  | position  |
+----+-------+-----------+
|  1 | MESSI | DEV_BACK  |
| 14 | SAM   | DEV_INFRA |
+----+-------+-----------+</code></pre>
<h2 id="in">IN</h2>
<ul>
<li><p><code>v IN (v1, v2, v3 ...)</code> : v가 (v1, v2, v3 ...) 중 하나라도 값이 같다면 true를 리턴한다.</p>
</li>
<li><p>(v1, v2, v3 ...) 안에 subquery의 결과가 들어갈 수 있다.</p>
</li>
<li><p><code>v NOT IN (v1, v2, v3 ...)</code> :  v가 (v1, v2, v3 ...) 의 모든 값과 일치하지 않을 때 true를 리턴한다.</p>
</li>
</ul>
<p><strong>id가 5인 임직원과 같은 프로젝트에 참여한 임직원들의 id를 출력하라</strong></p>
<pre><code class="language-sql">mysql&gt; SELECT DISTINCT empl_id FROM works_on
    -&gt; WHERE empl_id &lt;&gt; 5 AND proj_id IN (
    -&gt; SELECT proj_id FROM works_on WHERE empl_id = 5);
+---------+
| empl_id |
+---------+
|       1 |
|       3 |
|      10 |
|      13 |
|      14 |
+---------+</code></pre>
<p><strong>| id가 5인 임직원과 같은 프로젝트에 참여한 임직원들의 id, 이름을 출력하라</strong>
이름은 works_on 테이블만으로는 알 수 없다. employee 테이블에서 위에서 구한 id와 대응하는 이름들을 조회해야한다.</p>
<pre><code class="language-sql">mysql&gt; SELECT id, name
    -&gt; FROM employee WHERE id IN (
    -&gt; SELECT DISTINCT empl_id FROM works_on
    -&gt; WHERE empl_id &lt;&gt; 5 AND proj_id IN (
    -&gt; SELECT proj_id FROM works_on WHERE empl_id = 5));
+----+--------+
| id | name   |
+----+--------+
|  1 | MESSI  |
|  3 | JENNY  |
| 10 | NICOLE |
| 13 | JISUNG |
| 14 | SAM    |
+----+--------+</code></pre>
<p>같은 조건을 반환하지만 subquery를 FROM 절에서도 쓸 수 있다.</p>
<pre><code class="language-sql">mysql&gt; SELECT id, name
    -&gt; FROM employee, (
    -&gt; SELECT DISTINCT empl_id FROM works_on
    -&gt; WHERE empl_id &lt;&gt; 5 AND proj_id IN (
    -&gt; SELECT proj_id FROM works_on WHERE empl_id = 5))
    -&gt; AS E
    -&gt; WHERE id = E.empl_id;
+----+--------+
| id | name   |
+----+--------+
|  1 | MESSI  |
|  3 | JENNY  |
| 10 | NICOLE |
| 13 | JISUNG |
| 14 | SAM    |
+----+--------+</code></pre>
<ul>
<li>subquery를 FROM에 적어서 가상의 테이블로 만들고 가상의 테이블의 별칭을 E라고 정해준다.
그 후 WHERE 절에서 E.empl_id와 employ테이블의 id를 통해 join condition을 만들어준다.</li>
</ul>
<h2 id="exists">EXISTS</h2>
<ul>
<li><p><code>EXISTS</code> : subquery의 결과가 최소 하나의 row라도 있다면 true 반환</p>
</li>
<li><p><code>NOT EXISTS</code> : subquery의 결과가 단 하나의 row도 없다면 true 반환</p>
</li>
</ul>
<p><strong>| id가 7 혹은 12인 임직원이 참여한 프로젝트의 id, 이름을 출력하라</strong></p>
<pre><code class="language-sql">mysql&gt; SELECT P.id, P.name
    -&gt; FROM project P
    -&gt; WHERE EXISTS (
    -&gt; SELECT *
    -&gt; FROM works_on W
    -&gt; WHERE W.proj_id = P.id AND W.empl_id IN (7, 12)
    -&gt; );
+------+------------------+
| id   | name             |
+------+------------------+
| 2003 | 홈페이지 UI 개선   |
+------+------------------+</code></pre>
<ul>
<li>EXISTS와 IN은 서로 바꿔가면서 쓸 수 있다. 위의 쿼리문을 IN으로 바꾸면 다음과 같다.
```sql
mysql&gt; SELECT P.id, P.name
  -&gt; FROM project P
  -&gt; WHERE P.id IN (
  -&gt; SELECT W.proj_id
  -&gt; FROM works_on W
  -&gt; WHERE W.empl_id IN (7, 12)
  -&gt; );</li>
<li>------+------------------+
| id   | name             |</li>
<li>------+------------------+
| 2003 | 홈페이지 UI 개선   |</li>
<li>------+------------------+<pre><code></code></pre></li>
</ul>
<p><strong>| 2000년대생이 없는 부서의 id와 이름을 출력하라</strong>
NOT EXISTS를 사용하면 다음과 같다.</p>
<pre><code class="language-sql">mysql&gt; SELECT D.id, D.name
    -&gt; FROM department D
    -&gt; WHERE NOT EXISTS (
    -&gt; SELECT *
    -&gt; FROM employee E
    -&gt; WHERE E.dept_id = D.id AND E.birth_date &gt;= &#39;2000-01-01&#39;);
+------+-------------+
| id   | name        |
+------+-------------+
| 1003 | development |
| 1001 | headquater  |
| 1002 | hr          |
| 1005 | product     |
+------+-------------+</code></pre>
<ul>
<li>NOT EXISTS 또한 NOT IN으로 바꿔줄 수 있다.
```sql
mysql&gt; SELECT D.id, D.name
  -&gt; FROM department D
  -&gt; WHERE D.id NOT IN (
  -&gt; SELECT E.dept_id
  -&gt; FROM employee E
  -&gt; WHERE E.birth_date &gt;= &#39;2000-01-01&#39;);</li>
<li>------+-------------+
| id   | name        |</li>
<li>------+-------------+
| 1003 | development |
| 1001 | headquater  |
| 1002 | hr          |
| 1005 | product     |</li>
<li>------+-------------+<pre><code></code></pre></li>
</ul>
<h2 id="any">ANY</h2>
<ul>
<li><p><code>v 비교연산자 ANY (subquery)</code> : subquery가 반환하는 결과들 중에서 단 하나라도 v와 비교 연산한 값이 true라면 true를 반환한다.</p>
</li>
<li><p>SOME도 ANY와 같은 역할을 한다.</p>
</li>
</ul>
<p><strong>| 리더보다 높은 연봉을 받는 부서원을 가진 리더의 id,이름,연봉을 출력하라</strong></p>
<pre><code class="language-sql">mysql&gt; SELECT E.id, E.name, E.salary
    -&gt; FROM department D, employee E
    -&gt; WHERE D.leader_id = E.id AND E.salary &lt; ANY (
    -&gt; SELECT salary
    -&gt; FROM employee
    -&gt; WHERE id &lt;&gt; D.leader_id AND dept_id = E.dept_id);
+----+--------+----------+
| id | name   | salary   |
+----+--------+----------+
|  3 | JENNY  | 50000000 |
| 13 | JISUNG | 90000000 |
+----+--------+----------+</code></pre>
<ul>
<li><p>ANY 다음에 오는 subquery의 결과와 E.salary를 비교한다. 비교연산자에 만족하는 결과값이 하나라도 있다면 true를 반환한다.</p>
</li>
<li><p>위의 결과는 JENNY, JISUNG이 이끄는 부서에서 JENNY, JISUNG보다 연봉이 높은 부서원이 있다는 뜻이 된다. 그렇다면 그 부서원의 연봉이 얼마인지도 출력하려면 다음과 같다.
```sql
mysql&gt; SELECT E.id, E.name, E.salary, (
  -&gt; SELECT max(salary)
  -&gt; FROM employee
  -&gt; WHERE dept_id = E.dept_id
  -&gt; ) AS dept_max_salary
  -&gt; FROM department D, employee E
  -&gt; WHERE D.leader_id = E.id AND E.salary &lt; ANY (
  -&gt; SELECT salary
  -&gt; FROM employee
  -&gt; WHERE id &lt;&gt; D.leader_id AND dept_id = E.dept_id);</p>
</li>
<li><p>------+--------+----------+-----------------+
| id   | name   | salary   | dept_max_salary |</p>
</li>
<li><p>------+--------+----------+-----------------+
|    3 | JENNY  | 50000000 |       180000000 |
|   13 | JISUNG | 90000000 |       170000000 |</p>
</li>
<li><p>------+--------+----------+-----------------+
```</p>
</li>
<li><p>select 절에도 subquery를 쓸 수 있다.</p>
</li>
</ul>
<h2 id="all">ALL</h2>
<ul>
<li><code>v 비교연산자 ALL (subquery)</code> : subquery의 결과들과 v를 비교해서 모두 참이라면 true를 반환한다.</li>
</ul>
<p><strong>| id가 13인 임직원과 한번도 같은 프로젝트에 참여하지 못한 임직원들의 id,이름,직군을 출력하라</strong></p>
<pre><code class="language-sql">mysql&gt; SELECT DISTINCT E.id, E.name, E.position
    -&gt; FROM employee E, works_on W
    -&gt; WHERE E.id = W.empl_id AND W.proj_id &lt;&gt; ALL (
    -&gt; SELECT proj_id
    -&gt; FROM works_on
    -&gt; WHERE empl_id = 13);
+----+---------+----------+
| id | name    | position |
+----+---------+----------+
|  1 | MESSI   | DEV_BACK |
|  2 | JANE    | DSGN     |
|  3 | JENNY   | DEV_BACK |
|  6 | JULIA   | CFO      |
|  7 | MINA    | DSGN     |
|  9 | HENRY   | HR       |
| 11 | SUZANNE | PO       |
| 12 | CURRY   | PLN      |
+----+---------+----------+</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[0, 1로 숫자와 문자를 표현하는 방법]]></title>
            <link>https://velog.io/@jere-gim/0-1%EB%A1%9C-%EC%88%AB%EC%9E%90%EC%99%80-%EB%AC%B8%EC%9E%90%EB%A5%BC-%ED%91%9C%ED%98%84%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95</link>
            <guid>https://velog.io/@jere-gim/0-1%EB%A1%9C-%EC%88%AB%EC%9E%90%EC%99%80-%EB%AC%B8%EC%9E%90%EB%A5%BC-%ED%91%9C%ED%98%84%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95</guid>
            <pubDate>Tue, 04 Apr 2023 07:32:09 GMT</pubDate>
            <description><![CDATA[<h1 id="📌0-1로-숫자를-표현하는-방법">📌0, 1로 숫자를 표현하는 방법</h1>
<p>컴퓨터는 0, 1밖에 이해할 수 없다. 그렇다면 <code>3+4</code>를 컴퓨터는 어떻게 이해할까 ? 이에 대해 알아보고자 한다.</p>
<h2 id="정보단위">정보단위</h2>
<h3 id="비트bit">비트(bit)</h3>
<p><strong>: 0과 1을 표현하는 가장 작은 정보 단위</strong>
2비트로 총 4가지의 정보를 표현할 수 있다.</p>
<pre><code>00
01
10
11</code></pre><ul>
<li><p>n비트로 $$2^n$$가지의 정보를 표현할 수 있다.</p>
</li>
<li><p>프로그램은 수많은 비트로 이루어져 있다. 다만, 표현할 때는 비트보다 더 큰 단위를 사용한다.</p>
</li>
</ul>
<table>
<thead>
<tr>
<th align="center"></th>
<th align="center"></th>
</tr>
</thead>
<tbody><tr>
<td align="center">1바이트(1byte)</td>
<td align="center">8비트(8bit)</td>
</tr>
<tr>
<td align="center">1킬로바이트(1kB)</td>
<td align="center">1,000바이트(1,000byte)</td>
</tr>
<tr>
<td align="center">1메가바이트(1MB)</td>
<td align="center">1,000킬로바이트(1,000kB)</td>
</tr>
<tr>
<td align="center">1기가바이트(1GB)</td>
<td align="center">1,000메가바이트(1,000MB)</td>
</tr>
<tr>
<td align="center">1테라바이트(1TB)</td>
<td align="center">1,000기가바이트(1,000GB)</td>
</tr>
</tbody></table>
<ul>
<li>1024개씩 묶은 단위는 kiB, MiB ... 로 따로 있다. 과거에는 1024개씩 묶는 단위와 1000개씩 묶는 단위를 혼용해서 사용했지만 점차 다루는 데이터의 크기가 커지면서 24개씩의 오차가 점차 스노우볼이 굴러서 큰 오차가 되면서 문제를 발생시키게 된다.
따라서 현재에는 두 개의 단위를 혼용해서 사용하지 않는다.</li>
</ul>
<h3 id="워드word">워드(word)</h3>
<p><strong>: CPU가 한번에 처리할 수 있는 정보의 크기 단위</strong></p>
<ul>
<li><p>하프 워드(half word) : 워드의 절반 크기</p>
</li>
<li><p>풀 워드(full word) : 워드 크기</p>
</li>
<li><p>더블 워드(double word) : 워드의 두 배 크기</p>
</li>
</ul>
<h2 id="이진법">이진법</h2>
<p><strong>0과 1로 숫자를 표현하는 방법</strong></p>
<h3 id="이진수-표기법">이진수 표기법</h3>
<p><img src="https://velog.velcdn.com/images/jere-gim/post/de93b9de-e5e9-4343-8273-8d4e749de3d0/image.png" alt=""></p>
<h3 id="0과-1로-음수-표현">0과 1로 음수 표현</h3>
<p><strong>2의 보수법 이용</strong></p>
<ol>
<li>모든 0과 1을 뒤집는다.</li>
<li>1을 더한다.<pre><code>1101
0010
0011 -&gt; 2의 보수</code></pre>1101의 음수를 표현한 0011과 십진수 3을 표현한 0011을 어떻게 구분할까 ?</li>
</ol>
<p>-&gt; 컴퓨터는 cpu 내부의 플래그 레지스터 라는 것으로 둘을 구분한다.</p>
<h2 id="16진법">16진법</h2>
<ul>
<li>이진법으로는 숫자의 길이가 너무 길어지기 때문에 16진법도 많이 사용한다.</li>
</ul>
<table>
<thead>
<tr>
<th align="center">10진수</th>
<th align="center">0</th>
<th align="center">1</th>
<th align="center">2</th>
<th align="center">3</th>
<th align="center">4</th>
<th align="center">5</th>
<th align="center">6</th>
<th align="center">7</th>
<th align="center">8</th>
<th align="center">9</th>
<th align="center">10</th>
<th align="center">11</th>
<th align="center">12</th>
<th align="center">13</th>
<th align="center">14</th>
<th align="center">15</th>
<th align="center">16</th>
<th align="center">17</th>
<th align="center">...</th>
</tr>
</thead>
<tbody><tr>
<td align="center">16진수</td>
<td align="center">0</td>
<td align="center">1</td>
<td align="center">2</td>
<td align="center">3</td>
<td align="center">4</td>
<td align="center">5</td>
<td align="center">6</td>
<td align="center">7</td>
<td align="center">8</td>
<td align="center">9</td>
<td align="center">A</td>
<td align="center">B</td>
<td align="center">C</td>
<td align="center">D</td>
<td align="center">E</td>
<td align="center">F</td>
<td align="center">10</td>
<td align="center">11</td>
<td align="center">...</td>
</tr>
</tbody></table>
<p><img src="https://velog.velcdn.com/images/jere-gim/post/ee94ddc3-e4d2-4606-88e7-eea65c6c969a/image.png" alt=""></p>
<ul>
<li>16진법이 헷갈릴때 사용해보면 좋다.</li>
</ul>
<h3 id="16진수-표기법">16진수 표기법</h3>
<p><img src="https://velog.velcdn.com/images/jere-gim/post/89c72614-5857-4d0e-8aa2-93f3d6e6a41d/image.png" alt=""></p>
<h2 id="이진수---16진수">이진수 &lt;-&gt; 16진수</h2>
<p>두 진수를 변환하는 방법이 십진수로 변환하는 방법보다 훨씬 간단하다.</p>
<p>방법 : 이진수를 4자리씩 끊어서 16진수로 변환</p>
<pre><code>0001101000101011
0001 / 1010 / 0010 / 1011
-&gt; 1 / A / 2 / B
-&gt;1A2B</code></pre><p>반대로 16진수는 1개씩 끊어서 2진수로 변환시키면 된다.</p>
<pre><code>5D
5 -&gt; 0101
D -&gt; 1101
-&gt;01011101</code></pre><hr>
<h1 id="📌0-1로-문자를-표현하는-방법">📌0, 1로 문자를 표현하는 방법</h1>
]]></description>
        </item>
        <item>
            <title><![CDATA[SQL 조회 1]]></title>
            <link>https://velog.io/@jere-gim/SQL-%EC%A1%B0%ED%9A%8C</link>
            <guid>https://velog.io/@jere-gim/SQL-%EC%A1%B0%ED%9A%8C</guid>
            <pubDate>Tue, 04 Apr 2023 05:45:48 GMT</pubDate>
            <description><![CDATA[<h1 id="📌select">📌SELECT</h1>
<pre><code class="language-sql">SELECT attributes
FROM tables
WHERE conditions;</code></pre>
<p><strong>id가 9인 직원의 이름과 직군을 출력하라</strong></p>
<pre><code class="language-sql">mysql&gt; SELECT name, position FROM employee WHERE id = 9;
+-------+----------+
| name  | position |
+-------+----------+
| HENRY | HR       |
+-------+----------+</code></pre>
<ul>
<li><p>조건 <code>id = 9</code> 를 selection condition 이라고 한다.</p>
</li>
<li><p><code>name, position</code>을 projection attributes 라고 한다. 조건에 맞는 tuples의 값들 중에서 projection attributes에 의해 지정된 attribute에 대응하는 값만 출력한다.</p>
</li>
</ul>
<p><strong>project 2002를 리딩하는 임직원의 id와 이름과 직군을 출력하라</strong>
<img src="https://velog.velcdn.com/images/jere-gim/post/f4ab0c4e-5c20-454f-a680-09a70e28d3d1/image.png" alt=""></p>
<ul>
<li><p>2개의 테이블을 참고해서 sql 문을 만들어야 한다.
```sql
mysql&gt; SELECT employee.id, employee.name, employee.position
  -&gt; FROM employee, project
  -&gt; WHERE project.id = 2002 AND project.leader_id = employee.id;</p>
</li>
<li><p>----+--------+----------+
| id | name   | position |</p>
</li>
<li><p>----+--------+----------+
| 13 | JISUNG | PO       |</p>
</li>
<li><p>----+--------+----------+</p>
<pre><code></code></pre></li>
<li><p><code>project.leader_id = employee.id</code>를 join condition이라고 한다.</p>
</li>
<li><p>project.id = 2002 인 튜플과 join condition으로 인해 이에 대응하느 employee 테이블의 튜플도 선택이 된다.</p>
</li>
<li><p>마지막으로 <code>employee.id, employee.name, employee.position</code> 라는 projection attributes를 통해 튜플 중 projection attributes와 대응하는 값만 출력되는 것이다.</p>
</li>
</ul>
<h2 id="주의사항">주의사항</h2>
<p><strong>SELECT로 조회할 때 조건들을 포함해서 조회를 한다면 이 조건들과 관련된 attributes에 index가 걸려있어야 한다. 그렇지 않으면 데이터가 많아질수록 조회 속도가 느려지게 된다.</strong></p>
<hr>
<h1 id="📌as">📌AS</h1>
<p><strong>테이블이름을 별칭으로 바꿀 수 있다.</strong></p>
<pre><code class="language-sql">mysql&gt; SELECT E.id, E.name, E.position
    -&gt; FROM employee AS E, project AS P
    -&gt; WHERE P.id = 2002 AND P.leader_id = E.id;
+----+--------+----------+
| id | name   | position |
+----+--------+----------+
| 13 | JISUNG | PO       |
+----+--------+----------+</code></pre>
<p><strong>출력되는 attribute 이름을 바꿀 수 있다.</strong></p>
<pre><code class="language-sql">mysql&gt; SELECT E.id AS leader_id, E.name AS leader_name, E.position
    -&gt; FROM employee AS E, project AS P
    -&gt; WHERE P.id = 2002 AND P.leader_id = E.id;
+-----------+-------------+----------+
| leader_id | leader_name | position |
+-----------+-------------+----------+
|        13 | JISUNG      | PO       |
+-----------+-------------+----------+</code></pre>
<p><strong>AS는 생략이 가능하다.</strong></p>
<pre><code class="language-sql">mysql&gt; SELECT E.id leader_id, E.name leader_name, E.position
    -&gt; FROM employee E, project P
    -&gt; WHERE P.id = 2002 AND P.leader_id = E.id;
+-----------+-------------+----------+
| leader_id | leader_name | position |
+-----------+-------------+----------+
|        13 | JISUNG      | PO       |
+-----------+-------------+----------+</code></pre>
<hr>
<h1 id="📌distinct">📌DISTINCT</h1>
<p><strong>직군이 디자이너인 임직원들이 참여하고 있는 프로젝트들의 id와 이름을 출력하라</strong>
<img src="https://velog.velcdn.com/images/jere-gim/post/719d9644-8e43-404f-a87e-4b2e9d2f3f07/image.png" alt=""></p>
<ul>
<li><p>3개의 테이블을 참고해야한다.</p>
</li>
<li><p>employee 테이블과 project 테이블을 연결시키기 위해 works_on 테이블이 필요하다.</p>
</li>
</ul>
<pre><code class="language-sql">mysql&gt; SELECT P.id, P.name
    -&gt; FROM employee E, works_on W, project P
    -&gt; WHERE E.position = &#39;DSGN&#39;
    -&gt; AND E.id = W.empl_id AND P.id = W.proj_id;
+------+------------------+
| id   | name             |
+------+------------------+
| 2003 | 홈페이지 UI 개선   |
| 2003 | 홈페이지 UI 개선   |
+------+------------------+</code></pre>
<ul>
<li>SELECT 결과에서 중복되는 tuples을 제외하고 싶을때 DISTINCT를 사용한다.</li>
</ul>
<pre><code class="language-sql">mysql&gt; SELECT DISTINCT P.id, P.name
    -&gt; FROM employee E, works_on W, project P
    -&gt; WHERE E.position = &#39;DSGN&#39;
    -&gt; AND E.id = W.empl_id AND P.id = W.proj_id;
+------+------------------+
| id   | name             |
+------+------------------+
| 2003 | 홈페이지 UI 개선   |
+------+------------------+</code></pre>
<hr>
<h1 id="📌like">📌LIKE</h1>
<table>
<thead>
<tr>
<th align="center">항목</th>
<th align="center">설명</th>
</tr>
</thead>
<tbody><tr>
<td align="center">LIKE</td>
<td align="center">문자열 pattern matching에 사용</td>
</tr>
<tr>
<td align="center"><code>%</code></td>
<td align="center">0개 이상의 임의의 개수를 가지는 문자들을 의미</td>
</tr>
<tr>
<td align="center"><code>_</code></td>
<td align="center">하나의 문자를 의미</td>
</tr>
<tr>
<td align="center"><code>\</code></td>
<td align="center"><code>%</code>, <code>_</code>를 문자 그대로의 의미로 사용하고 싶을때 사용</td>
</tr>
</tbody></table>
<p><strong>이름이 N으로 시작하거나 N으로 끝나는 직원의 이름을 출력하라</strong></p>
<pre><code class="language-sql">mysql&gt; SELECT name
    -&gt; FROM employee
    -&gt; WHERE name LIKE &#39;N%&#39; OR name LIKE &#39;%N&#39;;
+--------+
| name   |
+--------+
| BROWN  |
| NICOLE |
+--------+</code></pre>
<ul>
<li><p><code>name LIKE &#39;N%&#39;</code> : 이름이 N으로 시작</p>
</li>
<li><p><code>name LIKE &#39;%N&#39;</code> : 이름이 N으로 끝남</p>
</li>
</ul>
<p><strong>이름에 NG가 포함되어있는 직원의 이름을 출력하라</strong></p>
<pre><code class="language-sql">mysql&gt; SELECT name
    -&gt; FROM employee
    -&gt; WHERE name LIKE &#39;%NG%&#39;;
+--------+
| name   |
+--------+
| DINGYO |
| JISUNG |
+--------+</code></pre>
<ul>
<li><code>name LIKE &#39;%NG%&#39;</code> : 이름에 NG가 포함되어 있음</li>
</ul>
<p><strong>이름이 J로 시작하고 4글자인 직원의 이름을 출력하라</strong></p>
<pre><code class="language-sql">mysql&gt; SELECT name
    -&gt; FROM employee
    -&gt; WHERE name LIKE &#39;J___&#39;;
+------+
| name |
+------+
| JANE |
+------+</code></pre>
<ul>
<li><code>name LIKE &#39;J___&#39;</code> : 이름이 J로 시작하고 4글자</li>
</ul>
<p><strong><code>%</code>나 <code>_</code>를 문자 그대로의 의미로 사용하고 싶을땐 <code>\%</code>, <code>\_</code> 로 앞에 <code>\</code>를 붙이면 된다.</strong></p>
<pre><code class="language-sql">mysql&gt; SELECT name
    -&gt; FROM project
    -&gt; WHERE name LIKE &#39;\%%&#39;;</code></pre>
<ul>
<li><p><code>name LIKE &#39;\%%&#39;</code> : %로 시작하는 project 이름</p>
</li>
<li><p><code>name LIKE &#39;%\_&#39;</code> : _로 끝나는 project 이름</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[SQL 추가, 수정, 삭제]]></title>
            <link>https://velog.io/@jere-gim/SQL-%EC%B6%94%EA%B0%80-%EC%88%98%EC%A0%95-%EC%82%AD%EC%A0%9C</link>
            <guid>https://velog.io/@jere-gim/SQL-%EC%B6%94%EA%B0%80-%EC%88%98%EC%A0%95-%EC%82%AD%EC%A0%9C</guid>
            <pubDate>Sat, 01 Apr 2023 15:44:23 GMT</pubDate>
            <description><![CDATA[<h1 id="📌insert-into추가">📌INSERT INTO(추가)</h1>
<pre><code class="language-sql">INSERT INTO 테이블이름 VALUES (all values);

INSERT INTO 테이블이름 (attributes list) VALUES (attributes list 순서에 맞는 values);

INSERT INTO 테이블이름 VALUES (...), (...), (...);</code></pre>
<p><img src="https://velog.velcdn.com/images/jere-gim/post/792baaaf-3ecf-4f9c-8d5e-81b7fcd3f7b7/image.png" alt=""></p>
<ul>
<li>맨 마지막 null 부분은 외래키에 해당하는 값이다. 아직 해당 값을 참조하는 테이블에 데이터를 추가하지 않았기 때문에 null을 추가한 것이다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/jere-gim/post/f3cd5fbb-d35c-4a5d-95e6-c76d8566af59/image.png" alt=""></p>
<ul>
<li><p>&#39;employee_chk_2&#39; 라는 제약사항을 위반했다면서 데이터가 추가되지 않았다.</p>
</li>
<li><p>저 제약사항이 뭔지 확인하고 싶을때 <code>SHOW CREATE TABLE 테이블이름;</code> 을 사용하면 된다.</p>
<ul>
<li>salary가 5천만 이상이어야 하는 제약사항을 위반함</li>
</ul>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/jere-gim/post/4013a45c-3f76-4ad8-bb69-3fec2716d451/image.png" alt=""></p>
<ul>
<li>VALUES의 마지막 값은 department 테이블의 pk인 id를 참조한 외래키이다. 그런데 department 테이블에 데이터를 아직 추가하지 않았기 때문에 111이라는 값은 존재하지 않는다. 따라서 데이터가 추가되지 않고 에러가 발생한다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/jere-gim/post/b0379055-057d-4bc3-99d3-4cc94c7c9124/image.png" alt=""></p>
<ul>
<li>값을 넣을 attribute를 적고 이에 해당하는 값만 추가해줄 수 있다. attribute의 순서를 꼭 테이블 만들때 생성했던 순서로 하지 않아도 된다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/jere-gim/post/a9397589-7cdc-45f7-b7cc-99e66542ec30/image.png" alt=""></p>
<ul>
<li><p>salary의 default 값을 5천만으로 정해줬기 때문에 추가할 때 값을 넣어주지 않으면 5천만으로 자동설정된다.</p>
</li>
<li><p>dept_id 또한 따로 추가해주지 않았기 때문에 NULL값으로 자동설정 되었다.</p>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/jere-gim/post/f221b5d5-fe1d-4922-a0b5-9043ff2a97bb/image.png" alt=""></p>
<ul>
<li>이렇게 tuple들을 한꺼번에 추가해줄수도 있다.</li>
</ul>
<p><strong>데이터를 추가해준 테이블들은 다음과 같다</strong></p>
<ol>
<li><p>department 테이블
<img src="https://velog.velcdn.com/images/jere-gim/post/24e83f4b-b286-4118-b1ad-9278ace54720/image.png" alt=""></p>
</li>
<li><p>employee 테이블
<img src="https://velog.velcdn.com/images/jere-gim/post/70095740-eafa-4bd2-8c5e-4511e2e12b6e/image.png" alt=""></p>
</li>
<li><p>project 테이블
<img src="https://velog.velcdn.com/images/jere-gim/post/8939e91b-ac48-44b0-b7f4-6684871ec7fe/image.png" alt=""></p>
</li>
<li><p>works_on 테이블
<img src="https://velog.velcdn.com/images/jere-gim/post/08c13f21-eade-40a8-9623-e8f6250dc781/image.png" alt=""></p>
</li>
</ol>
<hr>
<h1 id="📌update수정">📌UPDATE(수정)</h1>
<pre><code class="language-sql">UPDATE 테이블이름
SET 수정할 값
WHERE 조건</code></pre>
<p><img src="https://velog.velcdn.com/images/jere-gim/post/e7589aff-ffb4-4bac-ac2c-97188dc27601/image.png" alt=""></p>
<ul>
<li>employee 테이블에 값을 추가해줄 당시에는 dept_id 값을 전부 null로 해줬기 때문에 update를 해줘야 한다. </li>
</ul>
<p><img src="https://velog.velcdn.com/images/jere-gim/post/7376f42b-e50d-4bf2-aefb-a7298eb7fc1d/image.png" alt=""></p>
<ul>
<li>정상적으로 dept_id가 수정이 된 것을 확인할 수 있다.</li>
</ul>
<p><strong>개발팀의 연봉을 2배로 늘리시오(개발팀의 dept_id는 1003)</strong>
<img src="https://velog.velcdn.com/images/jere-gim/post/e98ce9a2-715b-44ee-a795-70923f31d2cb/image.png" alt=""></p>
<p><strong>프로젝트 ID가 2003인 프로젝트에 참여한 임직원의 연봉을 2배로 늘리시오</strong>
<img src="https://velog.velcdn.com/images/jere-gim/post/898e3a34-8fe0-4a3c-8525-d2e7f1e9ba78/image.png" alt=""></p>
<hr>
<h1 id="📌delete삭제">📌DELETE(삭제)</h1>
<pre><code class="language-sql">DELETE FROM 테이블이름
WHERE 조건</code></pre>
<p><strong>JOHN이라는 직원이 퇴사하였으니 employee 테이블에서 삭제하시오</strong>
<img src="https://velog.velcdn.com/images/jere-gim/post/9c3f3cbe-d5cc-4eb0-b4f3-2b116bb624a8/image.png" alt=""></p>
<p>id = 8 인 JOHN을 employee 테이블에서 삭제하려고 한다. 근데 JOHN은 프로젝트 id가 2001인 프로젝트에 참여중이다. 그러나 works_on 테이블에서 따로 삭제해줄 필요가 없다.
이유는 works_on 테이블을 생성할 때 
<img src="https://velog.velcdn.com/images/jere-gim/post/6a47a672-f51a-45df-ae8f-1200a4db805a/image.png" alt=""></p>
<p>이러한 제약사항을 설정해줬기 때문이다. <code>on delete CASCADE</code>는 참조한 키가 삭제되었을 때 그 키와 대응하는 empl_id도 삭제하라는 제약사항이다.</p>
<p><strong>DINGYO(id = 5)는 2001, 2002, 2003 프로젝트에 참여중이다. 2001 프로젝트에만 참여하도록 하기 위해 나머지 프로젝트를 삭제하시오</strong>
<img src="https://velog.velcdn.com/images/jere-gim/post/2b1c0e7b-8734-45eb-b430-c3f955e8a9cf/image.png" alt=""></p>
<ul>
<li><code>&lt;&gt;</code> 는 <code>!=</code> 이 표시와 같다. 2001을 제외한 proj_id를 모두 삭제하기 위해서 사용되었다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[컴퓨터 구조의 큰 그림]]></title>
            <link>https://velog.io/@jere-gim/%EC%BB%B4%ED%93%A8%ED%84%B0-%EA%B5%AC%EC%A1%B0%EC%9D%98-%ED%81%B0-%EA%B7%B8%EB%A6%BC</link>
            <guid>https://velog.io/@jere-gim/%EC%BB%B4%ED%93%A8%ED%84%B0-%EA%B5%AC%EC%A1%B0%EC%9D%98-%ED%81%B0-%EA%B7%B8%EB%A6%BC</guid>
            <pubDate>Sat, 01 Apr 2023 08:59:08 GMT</pubDate>
            <description><![CDATA[<h1 id="📌컴퓨터-구조">📌컴퓨터 구조</h1>
<h2 id="컴퓨터가-이해하는-정보">컴퓨터가 이해하는 정보</h2>
<h3 id="데이터">데이터</h3>
<ul>
<li><p>숫자, 문자, 이미지, 동영상과 같으 정적인 정보</p>
</li>
<li><p>컴퓨터와 주고받은 정보 혹은 컴퓨터 내부에 저장된 정보를 데이터라고 부른다.</p>
</li>
<li><p>0과 1로 숫자와 문자를 표현하는 방법</p>
</li>
</ul>
<h3 id="명령어">명령어</h3>
<p><strong>컴퓨터란 명령어를 처리하는 기계</strong></p>
<ul>
<li><p>명령어</p>
<ul>
<li>컴퓨터를 실질적으로 움직이는 정보</li>
<li>데이터는 명령어를 위한 일종의 재료</li>
</ul>
</li>
<li><p>명령어의 생김새와 동작 방식</p>
</li>
</ul>
<p>명령어란 쉽게 말해 이런 것이다.</p>
<ul>
<li>1과 2를 더해라</li>
<li>&quot;안녕하세요&quot;를 출력해라 등</li>
</ul>
<h2 id="컴퓨터의-4가지-핵심-부품">컴퓨터의 4가지 핵심 부품</h2>
<p><img src="https://velog.velcdn.com/images/jere-gim/post/3f73df1f-5325-409f-a1bf-8659d1885cfb/image.png" alt=""></p>
<ul>
<li><p>메인보드 : 4가지 핵심부품을 부착하는 곳</p>
</li>
<li><p>시스템 버스 : 부품들끼리 서로 정보를 주고받는 통로를 버스라고 한다. 그 중에서 사람의 척추와 같이 중요한 버스를 시스템 버스라고 한다.</p>
<h3 id="cpu">CPU</h3>
</li>
<li><p><em>컴퓨터의 두뇌라고 할 수 있으며, 메모리에 저장된 명렁어를 읽어들이고, 해석하고, 실행하는 부품*</em>
CPU는 크게 ALU, 제어장치, 레지스터로 구성되어 있다.</p>
</li>
<li><p>ALU(산술논리연산장치) : 계산기</p>
</li>
<li><p>레지스터 : CPU 내부의 임시 저장 장치</p>
</li>
<li><p>제어장치 : 제어 신호를 발생시키고, 명령어를 해석하는 장치</p>
<ul>
<li>제어 신호란 컴퓨터 부품들을 관리하고 작동시키기 위한 전기 신호이다. 
대표적으로 CPU가 메모리에 저장된 값을 읽고 싶을 때 메모리에 &quot;메모리 읽기&quot;라는 제어 신호를, 메모리에 값을 저장할고 싶을 때 메모리에 &quot;메모리 쓰기&quot;라는 제어 신호를 보낸다.<h3 id="메모리주기억장치-ram">메모리(주기억장치, RAM)</h3>
</li>
</ul>
</li>
<li><p><em>현재 실행되는 프로그램(프로세스)의 명령어와 데이터를 저장하는 부품*</em>
메모리에는 주소라는 개념이 있다. 이 주소를 통해서 내가 원하는 명령어 혹은 데이터가 메모리의 어디에 위치해있는지 알 수 있다.</p>
</li>
<li><p>프로그램이 실행되기 위해서는 메모리에 저장되어 있어야 한다.</p>
</li>
<li><p>메모리는 실행되는 프로그램의 명령어와 데이터를 저장한다.</p>
</li>
<li><p>메모리에 저장된 값의 위치는 주소를 통해 알 수 있다.</p>
<h3 id="보조기억장치">보조기억장치</h3>
</li>
<li><p><em>전원이 꺼져도 보관될 프로그램을 저장하는 부품*</em>
메모리는 전원이 꺼지면 저장된 내용을 다 잃어버리는 휘발성 저장 장치이다. 그래서 등장한 것이 전원이 공급안되도 정보를 저장하는 보조기억장치이다. SSD, HDD 등이 있다.</p>
</li>
</ul>
<p>-&gt; 메모리는 <strong>실행할 정보</strong>를 저장하고 보조기억장치는 <strong>보관할 정보</strong>를 저장한다.</p>
<h3 id="입출력장치">입출력장치</h3>
<p><strong>컴퓨터 외부와 연결되어 컴퓨터 내부와 정보를 교환할 수 있는 부품</strong></p>
<h3 id="메인보드와-시스템-버스">메인보드와 시스템 버스</h3>
<ul>
<li><p>메인보드에 연결된 부품을 버스를 통해 정보를 주고 받는다.</p>
</li>
<li><p>버스는 컴퓨터의 부품끼리 정보를 주고 받는 일종의 통로이다.</p>
</li>
<li><p>다양한 종류의 버스가 있는데 컴퓨터의 핵심 부품을 연결하는 중요한 버스를 <strong>시스템 버스</strong>라고 한다.</p>
</li>
</ul>
<h2 id="시스템-버스">시스템 버스</h2>
<p><strong>시스템 버스 내부 구성</strong>
<img src="https://velog.velcdn.com/images/jere-gim/post/354015cb-cc7f-43de-a183-4084e27489b4/image.png" alt=""></p>
<h3 id="주소-버스">주소 버스</h3>
<p>주소를 주고받는 통로</p>
<h3 id="데이터-버스">데이터 버스</h3>
<p>명령어와 데이터를 주고받는 통로</p>
<h3 id="제어-버스">제어 버스</h3>
<p>제어 신호를 주고받는 통로</p>
<h2 id="메모리-값-읽기">메모리 값 읽기</h2>
<p><img src="https://velog.velcdn.com/images/jere-gim/post/d8f5c640-8d0b-4758-b497-a82ad717ec0f/image.png" alt=""></p>
<h2 id="메모리-값-쓰기">메모리 값 쓰기</h2>
<p><img src="https://velog.velcdn.com/images/jere-gim/post/847164f2-2f74-40b2-93a6-e8ba557beaca/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[SQL 기본 개념, DB 구조 정의]]></title>
            <link>https://velog.io/@jere-gim/SQL-%EA%B8%B0%EB%B3%B8-%EA%B0%9C%EB%85%90-DB-%EA%B5%AC%EC%A1%B0-%EC%A0%95%EC%9D%98</link>
            <guid>https://velog.io/@jere-gim/SQL-%EA%B8%B0%EB%B3%B8-%EA%B0%9C%EB%85%90-DB-%EA%B5%AC%EC%A1%B0-%EC%A0%95%EC%9D%98</guid>
            <pubDate>Fri, 31 Mar 2023 07:56:16 GMT</pubDate>
            <description><![CDATA[<h1 id="📌sql-기본-개념">📌SQL 기본 개념</h1>
<h2 id="sql">SQL</h2>
<ul>
<li><p>Structured Query Language</p>
</li>
<li><p>현업에서 쓰이는 relational DBMS의 표준 언어</p>
</li>
<li><p>종합적인 database 언어 : DDL + DML + VDL</p>
</li>
</ul>
<h2 id="sql-주요-용어">SQL 주요 용어</h2>
<table>
<thead>
<tr>
<th align="center">relational data model</th>
<th align="center">-&gt;</th>
<th align="center">SQL</th>
</tr>
</thead>
<tbody><tr>
<td align="center">relation</td>
<td align="center">-&gt;</td>
<td align="center">table</td>
</tr>
<tr>
<td align="center">attribute</td>
<td align="center">-&gt;</td>
<td align="center">column</td>
</tr>
<tr>
<td align="center">tuple</td>
<td align="center">-&gt;</td>
<td align="center">row</td>
</tr>
<tr>
<td align="center">domain</td>
<td align="center">-&gt;</td>
<td align="center">domain</td>
</tr>
</tbody></table>
<h2 id="sql에서-relation">SQL에서 relation</h2>
<ul>
<li>multiset of tuples. 즉, 중복된 tuple을 허용한다.</li>
</ul>
<h2 id="sql--rdbms">SQL &amp; RDBMS</h2>
<p>SQL은 RDBMS의 표준 언어이지만 실제 구현에 강제가 없기 때문에 RDMBS마다 제공하는 SQL의 스펙이 조금씩 다르다. 예를 들면, MySQL과 ORACLE에서 사용하는 SQL의 문법이 조금씩 다르다.</p>
<hr>
<h1 id="📌sql로-db-정의">📌SQL로 DB 정의</h1>
<p>MySQL을 통해 IT 회사 관련 DB를 만들면서 실습을 진행하겠다.</p>
<h2 id="database-vs-schema">DATABASE vs SCHEMA</h2>
<ul>
<li><p>MySQL에서는 DATABASE와 SCHEMA가 같은 뜻을 의미한다.</p>
</li>
<li><p>CREATE DATABASE company = CREATE SCHEMA company</p>
</li>
<li><p>다른 RDBMS에서는 의미가 다르게 쓰인다.</p>
</li>
</ul>
<h2 id="db-만들기">DB 만들기</h2>
<table>
<thead>
<tr>
<th><img src="https://velog.velcdn.com/images/jere-gim/post/15f43d8c-2f72-4971-93cf-e15b63f79e28/image.png" alt=""></th>
<th><img src="https://velog.velcdn.com/images/jere-gim/post/c4d35590-e7ce-449f-96a2-6867e20b76dd/image.png" alt=""></th>
</tr>
</thead>
<tbody><tr>
<td>- <code>CREATE DATABASE company;</code> : database 만들기</td>
<td></td>
</tr>
</tbody></table>
<ul>
<li><code>SHOW DATABASES;</code> : 내 mysql에 존재하는 database 목록을 보여준다.</li>
<li><code>DROP DATABASE company;</code> : database 삭제</li>
<li><code>USE company;</code> : 사용할 database 선택</li>
<li><code>SELECT database();</code> : 어떤 database가 활성화되어 있는지 알려준다.</li>
</ul>
<h2 id="attribute-data-type">attribute data type</h2>
<h3 id="숫자">숫자</h3>
<p><img src="https://velog.velcdn.com/images/jere-gim/post/c9fde0fa-9d44-4729-a521-a51b8b48c6c1/image.png" alt=""></p>
<h3 id="문자열">문자열</h3>
<p><img src="https://velog.velcdn.com/images/jere-gim/post/36dce856-66cc-4277-8648-d0ec04c3425f/image.png" alt=""></p>
<h3 id="날짜와-시간">날짜와 시간</h3>
<p><img src="https://velog.velcdn.com/images/jere-gim/post/25319ef7-8210-4090-9d2d-cea6105f30aa/image.png" alt=""></p>
<h3 id="그-외">그 외</h3>
<p><img src="https://velog.velcdn.com/images/jere-gim/post/bb3bc5e8-4c56-4759-af13-1e1c12af9b31/image.png" alt=""></p>
<h2 id="table-만들기">table 만들기</h2>
<h3 id="company-db-스키마">company DB 스키마</h3>
<p><img src="https://velog.velcdn.com/images/jere-gim/post/8ef02010-bf55-4f2c-81fb-c948f5235363/image.png" alt=""></p>
<ul>
<li><p>위의 스키마를 참고하여 테이블을 생성할 수 있다.</p>
</li>
<li><p>컬럼에 밑줄이 그어진건 primary key라는 뜻이다.</p>
<h3 id="테이블-생성-sql문">테이블 생성 SQL문</h3>
<h4 id="department-table">department table</h4>
<p><img src="https://velog.velcdn.com/images/jere-gim/post/1c98f643-4525-4ff4-8811-d6d0b33ba098/image.png" alt=""></p>
<h4 id="employee-table">employee table</h4>
<p><img src="https://velog.velcdn.com/images/jere-gim/post/acbe04ee-606b-4419-83f2-4e2fa9dc38b5/image.png" alt=""></p>
<h4 id="project-table">project table</h4>
<p><img src="https://velog.velcdn.com/images/jere-gim/post/23ca0f74-4d88-46cb-8f93-01308f54a408/image.png" alt=""></p>
<h4 id="works_on-table">works_on table</h4>
<p><img src="https://velog.velcdn.com/images/jere-gim/post/4f27c23f-c489-4c77-bae1-3d6a948ed70a/image.png" alt=""></p>
</li>
</ul>
<h2 id="key-constraints">key constraints</h2>
<h3 id="primary-key">PRIMARY KEY</h3>
<ul>
<li>table의 tuple을 식별하기 위해 사용. 하나 이상의 attribute로 구성</li>
<li>중복된 값은 가질 수 없으며, NULL 값도 가질 수 없다.</li>
<li>선언 방법
<img src="https://velog.velcdn.com/images/jere-gim/post/c3a4d736-2957-4208-a370-b464ddbe6914/image.png" alt=""></li>
</ul>
<h3 id="unique">UNIQUE</h3>
<ul>
<li>UNIQUE로 지정된 attribute는 중복된 값을 가질 수 없다.</li>
<li>단, NULL은 중복을 허용할 수 있다.(RDBMS마다 다른데 MySQL은 가능)</li>
<li>선언 방법
<img src="https://velog.velcdn.com/images/jere-gim/post/94c736af-a189-4b6b-a85f-0f4708c966b2/image.png" alt=""></li>
</ul>
<h3 id="not-null">NOT NULL</h3>
<ul>
<li>attribute가 NOT NULL로 지정되면 해당 attribute는 NULL 값을 가질 수 없다.</li>
<li>선언 방법
<img src="https://velog.velcdn.com/images/jere-gim/post/14f00330-69b9-4ecd-8d16-9f7f04b66260/image.png" alt=""></li>
<li>보통 <code>NOT NULL</code>과 <code>UNIQUE</code>를 같이 많이 쓴다.</li>
</ul>
<h3 id="attribute-default">attribute DEFAULT</h3>
<ul>
<li>attribute의 default 값을 정의할 때 사용한다.</li>
<li>새로운 tuple을 저장할 때 해당 attribute에 대한 값이 없다면 default 값으로 저장된다.</li>
<li>선언 방법
<img src="https://velog.velcdn.com/images/jere-gim/post/6a385ff4-058a-4e9c-bc40-af4bde738dfc/image.png" alt=""></li>
</ul>
<h3 id="check">CHECK</h3>
<ul>
<li><p>attribute의 값을 제한하고 싶을 때 사용</p>
</li>
<li><p>선언 방법
<img src="https://velog.velcdn.com/images/jere-gim/post/378d4193-5a18-42b7-a9ae-dfd4f947c369/image.png" alt=""></p>
</li>
<li><p>MySQL 에서는 RESTRICT와 NO ACTION은 동일한 기능을 수행한다.(참조값 삭제/변경 금지)</p>
</li>
<li><p>MySQL 에서는 SET DEFAULT를 제대로 지원하지 않는다.</p>
</li>
</ul>
<h3 id="foreign-key">FOREIGN KEY</h3>
<ul>
<li>attribute가 다른 table의 primary key나 unique key를 참조할 때 사용</li>
<li>선언 방법
<img src="https://velog.velcdn.com/images/jere-gim/post/40e70f51-c990-4366-8a8c-670759c35e85/image.png" alt=""></li>
</ul>
<h3 id="constraint-이름-명시하기">constraint 이름 명시하기</h3>
<ul>
<li>이름을 붙이면 어떤 constraint를 위반했는지 쉽게 파악할 수 있다.</li>
<li>constraint를 삭제하고 싶을 때 해당 이름으로 삭제할 수 있다.</li>
<li>선언 방법
<img src="https://velog.velcdn.com/images/jere-gim/post/375f0ecd-ffc1-40be-a960-ced6d05619b1/image.png" alt=""></li>
</ul>
<h2 id="department에-외래키-추가">department에 외래키 추가</h2>
<p><img src="https://velog.velcdn.com/images/jere-gim/post/7d3edaa6-cd6f-4b63-a124-6a7751323de7/image.png" alt=""></p>
<ul>
<li>처음 departmet 테이블을 만들 때는 leader_id를 참조할 employee 테이블이 생성되지 않았기 때문에 나중에 외래키를 추가해주었다.</li>
</ul>
<h3 id="alter-table">ALTER TABLE</h3>
<ul>
<li><p>table의 schema를 변경하고 싶을 때 사용한다.</p>
</li>
<li><p>사용 방법
<img src="https://velog.velcdn.com/images/jere-gim/post/59a69cd9-24d3-4e94-97d9-4a11e159bc70/image.png" alt=""></p>
</li>
<li><p>이미 서비스 중인 table의 schema를 변경하는 것은 위험부담이 크다. 따라서, 변경 작업 때문에 서비스의 백엔드에 어떤 영향을 줄지 충분히 검토한 후에 변경해야한다.</p>
</li>
</ul>
<h3 id="drop-table">DROP TABLE</h3>
<ul>
<li>table을 삭제할 때 사용</li>
<li>DROP TABLE 테이블이름;</li>
</ul>
<h2 id="db-구조-정의시-중요한-점">db 구조 정의시 중요한 점</h2>
<p>만들려는 서비스의 스펙과 데이터 일관성, 편의성, 확장성 등을 종합적을 고려하여 <strong>DB 스키마</strong>를 적절하게 정의하는 것이 중요하다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[관계형 데이터베이스 기본 개념]]></title>
            <link>https://velog.io/@jere-gim/%EA%B4%80%EA%B3%84%ED%98%95-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EA%B8%B0%EB%B3%B8-%EA%B0%9C%EB%85%90</link>
            <guid>https://velog.io/@jere-gim/%EA%B4%80%EA%B3%84%ED%98%95-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EA%B8%B0%EB%B3%B8-%EA%B0%9C%EB%85%90</guid>
            <pubDate>Thu, 30 Mar 2023 03:59:38 GMT</pubDate>
            <description><![CDATA[<h1 id="📌relational-data-model">📌relational data model</h1>
<p><img src="https://velog.velcdn.com/images/jere-gim/post/643b791f-5568-4707-92b3-5d85cfd5f8a3/image.png" alt=""></p>
<ul>
<li><p>위의 표 전체를 relation 혹은 table이라고 한다.</p>
</li>
<li><p>추상적인 의미에서의 relation이 아니라 실제 데이터인 tuple들만의 집합을 relation(or relation state)이라고도 부른다.</p>
</li>
</ul>
<h2 id="주요-개념">주요 개념</h2>
<table>
<thead>
<tr>
<th align="center">주요 개념</th>
<th align="center">설명</th>
</tr>
</thead>
<tbody><tr>
<td align="center">domain</td>
<td align="center">set of atomic values - 더 이상 나누어질 수 없는 값들의 집합</td>
</tr>
<tr>
<td align="center">domain name</td>
<td align="center">domain 이름</td>
</tr>
<tr>
<td align="center">attribute</td>
<td align="center">domain이 relation에서 맡은 역할 이름</td>
</tr>
<tr>
<td align="center">tuple</td>
<td align="center">각 attribute의 값으로 이루어진 리스트. 일부 값은 NULL일 수 있다.</td>
</tr>
<tr>
<td align="center">relation</td>
<td align="center">set of tuples- tuple들의 집합</td>
</tr>
<tr>
<td align="center">relation name</td>
<td align="center">relation의 이름</td>
</tr>
</tbody></table>
<blockquote>
<p>NULL의 의미
1.값이 존재하지 않는다.
<br>2. 값이 존재하나 아직 그 값이 무엇인지 알지 못한다.
<br>3. 해당 사항과 관련이 없다.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/jere-gim/post/fa4062b1-fe12-4e9c-aaac-fcbf370a1645/image.png" alt=""></p>
<ul>
<li><p>봉준호의 toeic_score가 NULL인 이유는 1. 아직 토익을 안봐서 값이 존재하지 않을 수 있고 2. 토익을 봤지만 아직 테이블에 수정이 안되서 그 값을 알지 못할 수 있다.</p>
</li>
<li><p>홍상수와 박찬욱의 before_uni 값이 NULL인 이유는 해당 attribute는 편입하기 전의 대학교에 관련된 값인데 두 사람 모두 편입한게 아니기 때문에 해당 attribute와 관련이 없기 때문이다.</p>
</li>
</ul>
<h2 id="relation-schema">relation schema</h2>
<ul>
<li><p>relation의 구조를 나타낸다.</p>
</li>
<li><p>relation의 이름과 attributes 리스트로 표기된다.</p>
<ul>
<li>ex) STUDENT(id, name, grade, major, phone_num, emer_phone_num)</li>
</ul>
</li>
<li><p>attributes와 관련된 constraints(제약사항)도 포함한다.</p>
</li>
</ul>
<h2 id="degree-of-a-relation">degree of a relation</h2>
<ul>
<li><p>relation schema에서 attributes의 수를 말한다.</p>
</li>
<li><p>ex) STUDENT(id, name, grade, major, phone_num, emer_phone_num)</p>
</li>
<li><blockquote>
<p>degree(차수)는 6이다.</p>
</blockquote>
</li>
</ul>
<h2 id="relational-database">relational database</h2>
<ul>
<li><p>relational data model에 기반하여 구조화된 database</p>
</li>
<li><p>relational database는 여러 개의 relations로 구성된다.</p>
</li>
</ul>
<h2 id="relational-database-schema">relational database schema</h2>
<ul>
<li>relation schemas set + integrity constraints set</li>
</ul>
<hr>
<h1 id="📌relation의-특징들">📌relation의 특징들</h1>
<p><strong>relation은 중복된 tuple을 가질 수 없다.</strong>
<img src="https://velog.velcdn.com/images/jere-gim/post/54c3a08f-720a-4409-890a-4c75db9ab8fd/image.png" alt=""></p>
<p><strong>relation의 tuple을 식별하기 위해 attribute의 부분 집합을 key로 설정한다.</strong>
<img src="https://velog.velcdn.com/images/jere-gim/post/8e56c821-6feb-4c4c-a5c2-b40cd9ddc4b8/image.png" alt=""></p>
<p><strong>relation에서 tuple의 순서는 중요하지 않다.</strong>
<img src="https://velog.velcdn.com/images/jere-gim/post/2a69d875-7e89-4700-bfe9-dbd155a849c4/image.png" alt=""></p>
<ul>
<li>tuple의 순서가 바뀌어도 테이블의 의미나 데이터에 변화가 생기지 않는다.</li>
</ul>
<p><strong>하나의 relation에서 attribute의 이름은 중복되면 안된다.</strong>
<img src="https://velog.velcdn.com/images/jere-gim/post/fb6f871c-34a4-4527-b398-9b1bcc31d7e6/image.png" alt=""></p>
<p><strong>하나의 tuple에서 attribute의 순서는 중요하지 않다.</strong>
<img src="https://velog.velcdn.com/images/jere-gim/post/b45e9cbc-5b06-48b0-8d5a-e0fda68e6512/image.png" alt=""></p>
<p><strong>attribute는 atomic(더 이상 나누어질 수 없는) 해야 한다.</strong>
<img src="https://velog.velcdn.com/images/jere-gim/post/ce69851e-f3fb-4bd5-a921-976a2ed92272/image.png" alt=""></p>
<hr>
<h1 id="📌keys">📌keys</h1>
<h2 id="super-key">super key</h2>
<p><strong>: relation에서 tuples를 unique하게 식별할 수 있는 attributes set</strong></p>
<p>PLAYER(id, name, team_id, back_number, birth_date)의 super key는</p>
<ol>
<li>{id, name, team_id, back_number, birth_date}</li>
<li>{id, name}</li>
<li>{name, team_id, back_number} 등이 있다.</li>
</ol>
<h2 id="candidate-key">candidate key</h2>
<p><strong>: 어느 한 attribute라도 제거하면 unique하게 tuples를 식별할 수 없는 super key. minimal super key라고도 한다.</strong></p>
<p>PLAYER(id, name, team_id, back_number, birth_date)의 candidate key는</p>
<ol>
<li>{id}</li>
<li>{team_id, back_number}</li>
</ol>
<p>-&gt; {team_id, back_number} 에서 team_id를 제거하면 back_number(등번호)는 다른 팀의 선수와 중복이 될 수 있다.</p>
<h2 id="primary-key">primary key</h2>
<p><strong>: relation에서 tuples를 unique하게 식별하기 위해 **선택된</strong> candidate key**</p>
<p>PLAYER(id, name, team_id, back_number, birth_date)의 primary key는</p>
<ol>
<li><strong>{id}</strong></li>
<li>{team_id, back_number}</li>
</ol>
<p>-&gt; 보통 primary key를 고를땐 attribute가 가장 적은 candidate key를 선택한다.</p>
<h2 id="unique-keyalternate-key">unique key(alternate key)</h2>
<p><strong>: primary key가 아닌 candidate keys. alternate key라고도 한다.</strong></p>
<p>PLAYER(id(PK), name, team_id, back_number, birth_date)의 unique key는 {team_id, back_number} 이다.</p>
<h2 id="foreign-key">foreign key</h2>
<p><strong>: 다른 relation의 PK를 참조하는 attributes set</strong></p>
<p>PLAYER(id(PK), name, team_id, back_number, birth_date)
TEAM(id(PK), name, manager)
가 있을 때 foreign key는 PLAYER의 {team_id} 이다.</p>
<hr>
<h1 id="📌contraints">📌contraints</h1>
<p><strong>: relational database의 relation들이 언제나 항상 지켜줘야 하는 제약 사항</strong></p>
<h2 id="implicit-constraints">implicit constraints</h2>
<p><strong>: relational data model 자체가 가지는 constraints</strong></p>
<ul>
<li>&quot;relation은 중복되는 tuple을 가질 수 없다.&quot;</li>
<li>&quot;relation 내에서는 같은 이름의 attribute를 가질수 없다.&quot; 등이 있다.</li>
</ul>
<h2 id="explicit-constraints">explicit constraints</h2>
<p><strong>: 주로 DDL을 통해 schema에 직접 명시할 수 있는 constraints</strong></p>
<ul>
<li>schema-based constraints라고도 한다.</li>
</ul>
<p><strong>여러 종류의 explicit constraints가 있다.</strong></p>
<h3 id="domain-constraints">domain constraints</h3>
<p><strong>: attribute의 value는 해당 attribute의 domain에 속한 value여야 한다.</strong>
<img src="https://velog.velcdn.com/images/jere-gim/post/e8e06245-ee13-43e6-8695-7c1a27b9bc0a/image.png" alt=""></p>
<ul>
<li>학년을 의미하는 grade에는 1~4까지의 숫자만 올 수 있고 100이라는 숫자는 올 수 없다.</li>
</ul>
<h3 id="key-constraints">key constraints</h3>
<p><strong>: 서로 다른 tuples는 같은 value의 key를 가질 수 없다.</strong>
<img src="https://velog.velcdn.com/images/jere-gim/post/97ccb591-ed63-4cc8-88ee-a619ba459222/image.png" alt=""></p>
<ul>
<li>같은 value의 PK를 가질 수 없다.</li>
</ul>
<h3 id="null-value-constraints">NULL value constraints</h3>
<p><strong>: attribute가 NOT NULL로 명시됐다면 NULL 값을 가질 수 없다.</strong>
<img src="https://velog.velcdn.com/images/jere-gim/post/27f46a35-4286-4859-b2ab-3026fe8a9b85/image.png" alt=""></p>
<h3 id="entity-integrity-constraints">entity integrity constraints</h3>
<p><strong>: primary key는 value에 NULL을 가질 수 없다.</strong>
<img src="https://velog.velcdn.com/images/jere-gim/post/5a646d58-3f1e-4b93-8ce4-d5449b6f5b81/image.png" alt=""></p>
<ul>
<li>PK는 tuples를 unique하게 식별하는 key이기 때문에 NULL 값이 들어가게 된다면 식별할 수 없게 된다. 따라서 PK에는 NULL 값을 가질 수 없다.</li>
</ul>
<h3 id="referential-integrity-constraints">referential integrity constraints</h3>
<p><strong>: FK와 참조하는 테이블의 PK는 도메인이 같아야하고 PK에 없는 values를 FK가 가질 수 없다.</strong>
<img src="https://velog.velcdn.com/images/jere-gim/post/1b74b81c-00b8-4af4-881c-634b898a7162/image.png" alt=""></p>
<ul>
<li><p>TEAM 테이블의 id(PK)를 참조한 PLAYER 테이블의 team_id(FK)가 있다고 했을 때 team_id에는 TEAM.id에 없는 값을 가질 수 없다.</p>
</li>
<li><p>단, NULL 값은 가질 수 있는데 김민재 선수가 현재 소속팀이 없는 경우가 있을 수 있기 때문이다.</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[데이터베이스 기본 개념]]></title>
            <link>https://velog.io/@jere-gim/%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EA%B8%B0%EB%B3%B8-%EA%B0%9C%EB%85%90</link>
            <guid>https://velog.io/@jere-gim/%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EA%B8%B0%EB%B3%B8-%EA%B0%9C%EB%85%90</guid>
            <pubDate>Wed, 29 Mar 2023 03:03:16 GMT</pubDate>
            <description><![CDATA[<h1 id="📌db-dbms-db-system">📌DB, DBMS, DB System</h1>
<h2 id="dbdatabase">DB(database)</h2>
<p><strong>: 전자적으로 저장되고 사용되는 관련있는 데이터들의 조직화된 집합</strong></p>
<ul>
<li>같은 목적이나 같은 출처를 가진 데이터, 같은 서비스 아래에 있는 데이터들을 <strong>관련있는 데이터</strong>라고 한다.</li>
<li><strong>조직화된 집합</strong>이란 데이터들을 체계적으로 정리하여 검색 및 조회를 빠르게 하고 데이터의 중복 또는 불일치를 막을 수 있음을 의미한다.</li>
</ul>
<h2 id="dbms">DBMS</h2>
<p><strong>: database management systmes의 약자. 사용자에게 DB를 정의하고 만들고 관리하는 기능을 제공하는 소프트웨어 시스템</strong></p>
<ul>
<li>MySQL, ORACLE, SQL Server 등이 대표적인 프로그램이다.</li>
</ul>
<p><strong><code>metadata</code> : DB를 정의하기 위한 부가적인 데이터</strong></p>
<ul>
<li><p><code>metadata</code>는 database를 정의하거나 기술하는 data로써 <code>catalog</code> 라고도 부른다.</p>
</li>
<li><p><code>catalog</code>는 <code>metadata</code>가 저장되어 있는 장소로 불리기도 한다. </p>
</li>
<li><p><code>metadata</code>로는 데이터 유형, 구조, 제약 조건, 보안, 인덱스, 사용자 그룹 등이 있다.</p>
</li>
<li><p><code>metadata</code> 또한 DBMS를 통해 저장/관리 된다.</p>
</li>
</ul>
<h2 id="db-system">DB System</h2>
<p><strong>: database + DBMS + 연관된 applications</strong>
줄여서 database라고도 부르기 때문에 문맥에 따라 순수한 데이터의 집합으로써의 database를 의미하는 것인지 아니면 DB System을 의미하는 것인지 잘 파악해야한다.</p>
<p><img src="https://velog.velcdn.com/images/jere-gim/post/4503949b-3d15-486e-89ac-3943ade10e3a/image.png" alt="">
-간단한 DB System 동작 구조</p>
<h1 id="📌data-models">📌data models</h1>
<p><strong>: DB의 구조(structure)를 기술하는데 사용되는 개념들이 모인 집합</strong></p>
<ul>
<li>DB 구조를 <strong>추상화</strong>해서 표현할 수 있는 수단을 제공한다.<ul>
<li>DB 구조 : 데이터 유형, 데이터 관계(relationship), 제약 사항(constraints) 등</li>
</ul>
</li>
<li>data model은 여러 종류가 있으며 추상화 수준과 DB 구조화 방식이 조금씩 다르다.</li>
<li>DB에서 읽고 쓰기 위한 기본적인 동작(operations)들도 포함한다.</li>
</ul>
<h2 id="data-models-분류">data models 분류</h2>
<h3 id="conceptualhigh-level-data-models">conceptual(high-level) data models</h3>
<ul>
<li><p>일반 사용자들이 쉽게 이해할 수 있는 개념들로 이루어진 모델</p>
</li>
<li><p>추상화 수준이 가장 높음</p>
</li>
<li><p>비즈니스 요구 사항을 추상화하여 기술할 때 사용</p>
</li>
<li><p>ERD(entitiy-relationship diagram)가 대표적인 예이다.</p>
<h3 id="logicalrepresentational-data-models">logical(representational) data models</h3>
</li>
<li><p>이해하기 어렵지 않으면서도 디테일하게 DB를 구조화할 수 있는 개념들을 제공</p>
</li>
<li><p>데이터가 컴퓨터에 저장될 때의 구조와 크게 다르지 않게 DB 구조화를 가능하게 함</p>
</li>
<li><p>특정 DBMS나 storage에 종속되지 않는 수준에서 DB를 구조화할 수 있는 모델</p>
</li>
<li><p>relational data model에 대표적인 예이고 백엔드 개발자들이 가장 많이 사용하는 모델이다.
<img src="https://velog.velcdn.com/images/jere-gim/post/8d18bb67-8113-4320-80d5-e034b9ebf2b4/image.jpg" alt=""></p>
</li>
</ul>
<h3 id="physicallow-level-data-models">physical(low-level) data models</h3>
<ul>
<li><p>컴퓨터에 데이터가 어떻게 파일 형태로 저장되느지를 기술할 수 있는 수단을 제공</p>
</li>
<li><p>data format, data orderings, access path 등</p>
<ul>
<li>access path : 데이터 검색을 빠르게 하기 위한 구조체(ex) index)</li>
</ul>
</li>
</ul>
<hr>
<h1 id="📌schema--state">📌schema &amp; state</h1>
<h2 id="schema스키마">schema(스키마)</h2>
<ul>
<li><p>data model을 바탕으로 database의 구조를 기술한 것</p>
</li>
<li><p>schema는 database를 설계할 때 정해지며 한번 정해진 후에는 자주 바뀌지 않는다.
<img src="https://velog.velcdn.com/images/jere-gim/post/26f17518-86e9-42b0-a7d4-407ec6d82af1/image.png" alt=""></p>
</li>
<li><p>database schema의 예시</p>
</li>
<li><p>student, book 데이터와 그 속성 등을 기술한 것으로 이를 통해 database의 구조를 알 수 있다.</p>
</li>
</ul>
<h2 id="state">state</h2>
<ul>
<li><p>database에 있는 실제 데이터는 꽤 자주 바뀔 수 있다.</p>
</li>
<li><p>database에 있는 현재 instances의 집합이라고도 한다.
<img src="https://velog.velcdn.com/images/jere-gim/post/73d4f8b2-9626-4525-86fe-1a11a0a6fe2e/image.png" alt=""></p>
</li>
<li><p>시간이 지나면서 데이터가 삭제, 수정 등의 변화가 일어난다. 이때 특정 시점의 데이터를 database state 혹은 snapshot이라고 한다.</p>
</li>
</ul>
<h2 id="three-schema-architecture">three-schema architecture</h2>
<ul>
<li><p>database system을 구축하는 architecture 중 하나</p>
</li>
<li><p>user application으로부터 물리적인 database를 분리시키는 목적</p>
<ul>
<li>물리적인 database는 조금씩 변경될 수 있다. 이럴때에도 database를 직접 사용하는 user application은 변경에 영향을 주지 않기 위함이다.</li>
</ul>
</li>
<li><p>세 가지 level이 존재하며 각각의 level마다 schema가 정의되어 있다.
<img src="https://velog.velcdn.com/images/jere-gim/post/5762bc05-00d2-4e32-9d83-88cbadad58d2/image.jpg" alt=""></p>
</li>
</ul>
<h3 id="internal-schema-at-internal-level">internal schema at internal level</h3>
<ul>
<li><p>database와 가장 가까이 있는 스키마</p>
</li>
<li><p>물리적으로 데이터가 어떻게 저장되는지 physical data model을 통해 표현된다.</p>
</li>
<li><p>datga storage, data structure, access path 등 실체가 있는 내용을 기술한다.</p>
<h3 id="externalor-user-views-schema-at-externalor-view-level">external(or user views) schema at external(or view) level</h3>
</li>
<li><p>실제 사용자가 바라보는 스키마기 때문에 user views 라고도 불린다.</p>
</li>
<li><p>특정 유저들이 필요로 하는 데이터만 표현하고 그 외 알려줄 필요가 없는 데이터는 숨긴다.</p>
</li>
<li><p>logical data model을 통해 표현된다.</p>
<h3 id="conceptual-schema-at-conceptual-level">conceptual schema at conceptual level</h3>
</li>
<li><p>처음 database 설계 시에는 위의 2개 스키마밖에 없었다. 이에 따라 유저마다 다른 요구를 하게 되면 internal schema에 중복되는 데이터가 쌓이게 되고 관리가 힘들어지게 되었다.
이를 해결하기 위해 나온 것이 conceptual schema 이다.</p>
</li>
<li><p>internal schema를 한번 추상화시킨 스키마라고 할 수 있으며 따라서 물리적인 저장 구조에 관한 내용은 숨긴다.</p>
</li>
<li><p>대신 논리적으로 전체 database에 대한 구조를 기술한다. entities, data types, relationships, constraints(제약사항) 등에 집중해서 논리적으로 표현한 것이 conceptual schema 이다.</p>
</li>
<li><p>logical data model을 통해 표현된다.</p>
</li>
</ul>
<h3 id="결론">결론</h3>
<ul>
<li><p>각 레벨을 독립시켜서 어느 레벨에서의 변화가 상위 레벨에 영향을 주지 않기 위함이다.</p>
</li>
<li><blockquote>
<p>가령 internal schema의 내용이 변경되어도 conceptual schema를 수정할 필요가 없고 이 둘 사이의 매핑만 변경해주면 된다.</p>
</blockquote>
</li>
<li><p>다만, 대부분의 DBMS가 three level을 완벽하게 혹은 명시적으로 나누지는 않는다.</p>
</li>
<li><p>각각의 스키마는 database의 구조를 표현하는 것이고 실제 데이터가 존재하는 곳은 <strong>internal level</strong>이다.</p>
</li>
</ul>
<hr>
<h1 id="📌database-language">📌database language</h1>
<p><strong>아래의 명령어들을 통합한 relational database language를 <code>SQL</code>이라고 한다.</strong></p>
<h2 id="ddl-sdl-vdl">DDL, SDL, VDL</h2>
<p><strong>DDL : Data Definition Language. conceptual schema를 정의하기 위해 사용되는 명령어</strong></p>
<ul>
<li>테이블과 같은 데이터 구조를 정의하는데 사용되는 명령어들로 (생성, 변경, 삭제, 이름변경) 데이터 구조와 관련된 명령어들을 말한다.</li>
</ul>
<p><strong>SDL : Storage Definition Language. internal schema를 정의하는 위해 사용되는 명령어</strong></p>
<ul>
<li>하지만 요즘 relational DBMS에서는 SDL은 거의 사용하지 않고 파라미터 등의 설정으로 대체되었다.</li>
</ul>
<p><strong>VDL : View Definition Language. external schema를 정의하기 위해 사용되는 명령어</strong></p>
<ul>
<li>하지만 대부분의 DBMS에서는 DDL이 VDL의 역할까지 수행한다.</li>
</ul>
<p><strong>결국 definition language는 대부분 DDL로만 작성한다.</strong></p>
<h2 id="dml">DML</h2>
<p><strong>: Data Manipulation Language. database에 있는 실제 data를 활용하기 위한 명령어</strong></p>
<ul>
<li>data 추가, 삭제, 수정, 검색 등의 기능을 제공한다.</li>
</ul>
<h2 id="dcl">DCL</h2>
<p><strong>: Data Control Language. database에 접근하고 객체들을 사용하도록 권한을 주고 회수하는 명령어</strong></p>
<h2 id="tcl">TCL</h2>
<p><strong>Transaction Control Language. 논리적인 작업의 단위를 묶어서 DML에 의해 조작된 결과를 작업단위(트랜잭션) 별로 제어하는 명령어</strong></p>
<table>
<thead>
<tr>
<th align="center">명령어 종류</th>
<th align="center">명령어</th>
</tr>
</thead>
<tbody><tr>
<td align="center">데이터 조작어(DML)</td>
<td align="center">SELECT, INSERT, UPDATE, DELETE</td>
</tr>
<tr>
<td align="center">데이터 정의어(DDL)</td>
<td align="center">CREATE, ALTER, DROP, RENAME, TRUNCATE</td>
</tr>
<tr>
<td align="center">데이터 제어어(DCL)</td>
<td align="center">GRANT, REVOKE</td>
</tr>
<tr>
<td align="center">트랜잭션 제어어(TCL)</td>
<td align="center">COMMIT, ROLLBACK, SAVEPOINT</td>
</tr>
</tbody></table>
]]></description>
        </item>
        <item>
            <title><![CDATA[[자바의 정석 기초편] 스트림의 변환]]></title>
            <link>https://velog.io/@jere-gim/%EC%9E%90%EB%B0%94%EC%9D%98-%EC%A0%95%EC%84%9D-%EA%B8%B0%EC%B4%88%ED%8E%B8-%EC%8A%A4%ED%8A%B8%EB%A6%BC%EC%9D%98-%EB%B3%80%ED%99%98</link>
            <guid>https://velog.io/@jere-gim/%EC%9E%90%EB%B0%94%EC%9D%98-%EC%A0%95%EC%84%9D-%EA%B8%B0%EC%B4%88%ED%8E%B8-%EC%8A%A4%ED%8A%B8%EB%A6%BC%EC%9D%98-%EB%B3%80%ED%99%98</guid>
            <pubDate>Sun, 26 Mar 2023 18:15:07 GMT</pubDate>
            <description><![CDATA[<h1 id="📌스트림의-변환">📌스트림의 변환</h1>
<p><img src="https://velog.velcdn.com/images/jere-gim/post/803d01d7-4805-4b0b-88df-8b234740579c/image.png" alt="">
<img src="https://velog.velcdn.com/images/jere-gim/post/024f78c8-5585-482a-801a-c89fb1702488/image.png" alt=""></p>
<h2 id="입력-받은-숫자-int-배열로-저장">입력 받은 숫자 int[] 배열로 저장</h2>
<pre><code class="language-java">BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

int[] input = Arrays.stream(br.readLine().split(&quot; &quot;))
                    .mapToInt(Integer::parseInt)
                    .toArray();</code></pre>
<ul>
<li><code>1 2</code> 입력받았을 때 input[0] = 1, input[1] = 2 저장</li>
</ul>
<h2 id="arraylistinteger---int"><code>ArrayList&lt;Integer&gt;</code> -&gt; <code>int[]</code></h2>
<pre><code class="language-java">ArrayList&lt;Integer&gt; list = new ArrayList&lt;&gt;();

int[] arr = list.stream()
                .mapToInt(d -&gt; d)
                .toArray();
또는
int[] arr = list.stream()
                .mapToInt(Integer::intValue)
                .toArray();</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[자바의 정석 기초편] 스트림 2]]></title>
            <link>https://velog.io/@jere-gim/%EC%9E%90%EB%B0%94%EC%9D%98-%EC%A0%95%EC%84%9D-%EA%B8%B0%EC%B4%88%ED%8E%B8-%EC%8A%A4%ED%8A%B8%EB%A6%BC-2</link>
            <guid>https://velog.io/@jere-gim/%EC%9E%90%EB%B0%94%EC%9D%98-%EC%A0%95%EC%84%9D-%EA%B8%B0%EC%B4%88%ED%8E%B8-%EC%8A%A4%ED%8A%B8%EB%A6%BC-2</guid>
            <pubDate>Sun, 26 Mar 2023 03:31:17 GMT</pubDate>
            <description><![CDATA[<h1 id="📌optionalt">📌<code>Optional&lt;T&gt;</code></h1>
<p><strong>T 타입 객체의 래퍼 클래스</strong></p>
<pre><code class="language-java">public final class Optional&lt;T&gt; {
    // T타입 참조변수 -&gt; 모든 종류의 객체 저장 가능(null 까지)
    private final T value; 
    ...
}</code></pre>
<ul>
<li><p><code>T value</code> : T 타입의 참조변수로써 null 포함 모든 종류의 객체를 저장할 수 있다.</p>
</li>
<li><p>null을 간접적으로 다루기 위해 사용한다.</p>
<ul>
<li>null을 직접 다루게 되면 <code>NullPointerException</code>이 발생할 위험이 있는데 이를 방지할 수 있다.</li>
<li>null 체크를 위한 if문 작성으로 지저분해질 수 있는 코드를 간결하게 할 수 있다.</li>
</ul>
</li>
</ul>
<img src="https://velog.velcdn.com/images/jere-gim/post/65fd591d-5194-464b-9efb-f4b02380a811/image.png" width="350">

<h2 id="optionalt-객체-생성하기"><code>Optional&lt;T&gt;</code> 객체 생성하기</h2>
<p><strong><code>Optional&lt;T&gt;</code> 객체를 생성하는 방법</strong></p>
<pre><code class="language-java">String str = &quot;abc&quot;;
Optional&lt;String&gt; optStr = Optional.of(str);
Optional&lt;String&gt; optStr = Optional.of(&quot;abc&quot;);
Optional&lt;String&gt; optStr = Optional.ofNullable(null); // null을 저장할 때 사용</code></pre>
<img src="https://velog.velcdn.com/images/jere-gim/post/64d440ce-56cf-42d2-87ac-a2cd2d262757/image.png" width="350">

<p><strong>null 대신 빈 <code>Optional&lt;T&gt;</code> 객체를 사용</strong></p>
<pre><code class="language-java">Optional&lt;String&gt; optVal = null; // 가능은 하나 바람직하지 않음
Optional&lt;String&gt; optStrVal = Optional.empty();</code></pre>
<h2 id="optionalt-객체의-값-가져오기"><code>Optional&lt;T&gt;</code> 객체의 값 가져오기</h2>
<pre><code class="language-java">Optional&lt;String&gt; optVal = Optional.of(&quot;abc&quot;);

// optVal에 저장된 값을 반환. null이면 예외 발생 -&gt; 잘 쓰지 않음
String str1 = optVal.get();

// optVal에 저장된 값을 반환. null이면 &quot;&quot; 반환
String str2 = optVal.orElse(&quot;&quot;);

// optVal에 저장된 값을 반환. null이면 new String 객체 반환 
String str3 = optVal.orElseGet(String::new);
// 람다식으로도 가능
String str3 = optVal.orElseGet(() -&gt; new String());

// optVal에 저장된 값을 반환. null이면 예외 발생
String str4 = optVal.orElseThrow(NullPointerException::new);</code></pre>
<h3 id="ispresent-ifpresent">isPresent(), ifPresent()</h3>
<p><strong><code>boolean isPresent()</code></strong>
Optional 객체의 값이 null이면 false, 아니면 true 반환</p>
<pre><code class="language-java">String str = &quot;abc&quot;;
if(Optional.ofNullable(str).isPresent()) {
    System.out.println(str);
}</code></pre>
<blockquote>
<p>abc</p>
</blockquote>
<ul>
<li><code>Optional</code> 객체의 값이 null이 아닐 때 작업 수행</li>
</ul>
<p><strong><code>void ifPresnet(Consumer&lt;T&gt; action)</code></strong>
Optional 객체의 값이 null이 아닐 때 <code>ifPresent()</code> 괄호 안의 람다식 수행</p>
<pre><code class="language-java">String str = &quot;abc&quot;;
Optional.ofNullable(str).ifPresent(System.out::println);</code></pre>
<blockquote>
<p>abc</p>
</blockquote>
<h2 id="optionalint-optionallong-optionaldouble">OptionalInt, OptionalLong, OptionalDouble</h2>
<p><strong>기본형 값을 감싸는 래퍼 클래스</strong></p>
<pre><code class="language-java">public final class OptionalInt {
    private final boolean isPresent;
    private final int value;
    ...
}    </code></pre>
<ul>
<li><code>isPresent</code> : 값이 저장되어 있으면 true 반환</li>
<li><code>value</code> : int 타입의 변수</li>
</ul>
<h3 id="optionalint의-값-가져오기">OptionalInt의 값 가져오기</h3>
<table>
<thead>
<tr>
<th align="center">Optional 클래스</th>
<th align="center">get() 메서드</th>
</tr>
</thead>
<tbody><tr>
<td align="center"><code>Optional&lt;T&gt;</code></td>
<td align="center"><code>T get()</code></td>
</tr>
<tr>
<td align="center"><code>OptionalInt</code></td>
<td align="center"><code>int getAsInt()</code></td>
</tr>
<tr>
<td align="center"><code>OptionalLong</code></td>
<td align="center"><code>long getAsLong()</code></td>
</tr>
<tr>
<td align="center"><code>OptionalDouble</code></td>
<td align="center"><code>double getAsDouble()</code></td>
</tr>
</tbody></table>
<h3 id="빈-optionalint-객체와-비교">빈 OptionalInt 객체와 비교</h3>
<pre><code class="language-java">OptionalInt optInt1 = OptionalInt.of(0); // 0을 저장
OptionalInt optInt2 = OptionalInt.empty(); // 빈 객체 생성

System.out.println(optInt1.isPresent()); // true
System.out.println(optInt2.isPresent()); // false

System.out.println(optInt1.getAsInt()); // 0
System.out.println(optInt2.getAsInt()); // 에러

System.out.println(optInt1.equals(optInt2)); // false</code></pre>
<ul>
<li>0을 저장한 OptionalInt와 빈 객체를 생성해서 초기값이 0으로 저장된 OptionalInt를 구분하기 위해 <code>isPresent()</code>를 사용한다.</li>
</ul>
<hr>
<h1 id="📌최종-연산">📌최종 연산</h1>
<p><strong>: 연산 결과가 스트림이 아닌 연산. 스트림의 요소를 소모하기 때문에 단 한번만 수행 가능</strong></p>
<h2 id="최종-연산-메서드">최종 연산 메서드</h2>
<table>
<thead>
<tr>
<th align="left"><center>최종 연산</center></th>
<th align="left"><center>설명</center></th>
</tr>
</thead>
<tbody><tr>
<td align="left"><code>void forEach(Consumer&lt;? super T&gt; action)</code></td>
<td align="left">각 요소에 지정된 작업을 수행</td>
</tr>
<tr>
<td align="left"><code>void forEachOrdered(Consumer&lt;? super T&gt; action)</code></td>
<td align="left">병렬 스트림일 때 순서를 유지하며 각 요소에 지정된 작업을 수행</td>
</tr>
<tr>
<td align="left"><code>long count()</code></td>
<td align="left">스트림의 요소 개수를 반환</td>
</tr>
<tr>
<td align="left"><code>Optional&lt;T&gt; max(Comparator&lt;? super T&gt; comparator)</code></td>
<td align="left">스트림의 최대값을 반환</td>
</tr>
<tr>
<td align="left"><code>Optional&lt;T&gt; min(Comparator&lt;? super T&gt; comparator)</code></td>
<td align="left">스트림의 최소값을 반환</td>
</tr>
<tr>
<td align="left"><code>Optional&lt;T&gt; findAny()</code></td>
<td align="left">병렬 스트림의 요소 중 아무거나 하나를 반환(filter와 같이 사용)</td>
</tr>
<tr>
<td align="left"><code>Optional&lt;T&gt; findFirst()</code></td>
<td align="left">직렬 스트림의 요소 중 첫 번째 요소를 반환(filter와 같이 사용)</td>
</tr>
<tr>
<td align="left"><code>boolean allMatch(Predicate&lt;T&gt; p)</code></td>
<td align="left">주어진 조건을 모든 요소가 만족하면 true 반환</td>
</tr>
<tr>
<td align="left"><code>boolean anyMatch(Predicate&lt;T&gt; p)</code></td>
<td align="left">주어진 조건을 요소 중 하나라도 만족하면 true 반환</td>
</tr>
<tr>
<td align="left"><code>boolean noneMatch(Predicate&lt;T&gt; p)</code></td>
<td align="left">주어진 조건을 모든 요소가 만족하지 않으면 true 반환</td>
</tr>
<tr>
<td align="left"><code>Object[] toArray()</code><br><code>A[] toArray(IntFunction&lt;A[]&gt; generator)</code></td>
<td align="left">스트림의 모든 요소를 배열로 반환</td>
</tr>
<tr>
<td align="left"><strong>reduce()</strong></td>
<td align="left"></td>
</tr>
</tbody></table>
<table>
<thead>
<tr>
<th align="left"><center>최종 연산</center></th>
<th align="left"><center>설명</center></th>
</tr>
</thead>
<tbody><tr>
<td align="left"><code>Optional&lt;T&gt; reduce(BinaryOperator&lt;T&gt; accumulator)</code></td>
<td align="left"></td>
</tr>
<tr>
<td align="left"><code>T reduce(T identity, BinaryOperator&lt;T&gt; accumulator)</code></td>
<td align="left">스트림의 요소를 하나씩 줄여가면서 계산</td>
</tr>
<tr>
<td align="left"><code>U reduce(U identity, BiFunction&lt;U,T,U&gt; accumulator, BinaryOperator&lt;U&gt; combiner)</code></td>
<td align="left"></td>
</tr>
<tr>
<td align="left"><strong>collect()</strong></td>
<td align="left"></td>
</tr>
</tbody></table>
<table>
<thead>
<tr>
<th align="left"><center>최종 연산</center></th>
<th align="left"><center>설명</center></th>
</tr>
</thead>
<tbody><tr>
<td align="left"><code>R collect(Collector&lt;T,A,R&gt; collector)</code></td>
<td align="left"></td>
</tr>
<tr>
<td align="left"><code>R collect(Supplier&lt;R&gt; supplier, BiConsumer&lt;R,T&gt; accumulator, BiConsumer&lt;R,R&gt; combiner)</code></td>
<td align="left">스트림의 요소를 수집한다.<br>주로 요소를 그룹화하거나 분할한 결과를 컬렉션에 담아 반환하는데 사용된다.</td>
</tr>
</tbody></table>
<h3 id="foreach">forEach()</h3>
<p><strong>스트림의 모든 요소에 지정된 작업을 수행</strong></p>
<pre><code class="language-java">void forEach(Consumer&lt;? super T&gt; action)
void forEachOrdered(Consumer&lt;? super T&gt; action)</code></pre>
<ul>
<li><code>forEachOrdered()</code> 병렬 스트림일 경우에도 순서가 보장된다.<pre><code class="language-java">// 직렬 스트림, forEach() -&gt; 순서 보장 O
IntStream.rangeClosed(1,10).forEach(System.out::print);
System.out.println();
// 병렬 스트림, forEach() -&gt; 순서 보장 X
IntStream.rangeClosed(1,10).parallel().forEach(System.out::print);
System.out.println();
// 병렬 스트림, forEachOrdered() -&gt; 순서 보장 O
IntStream.rangeClosed(1,10).parallel().forEachOrdered(System.out::print);</code></pre>
<blockquote>
<p>12345678910
76395128104
12345678910</p>
</blockquote>
</li>
</ul>
<h3 id="allmatch-anymatch-nonematch">allMatch(), anyMatch(), noneMatch()</h3>
<p><strong>스트리의 요소를 조건 검사해서 boolean 타입 반환</strong></p>
<pre><code class="language-java">boolean allMatch(Predicate&lt;? super T&gt; Predicate)
boolean anyMatch(Predicate&lt;? super T&gt; Predicate)
boolean noneMatch(Predicate&lt;? super T&gt; Predicate) // allMatch와 반대</code></pre>
<ul>
<li><code>allMatch()</code> : 모든 요소가 조건을 만족하면 true</li>
<li><code>anyMatch()</code> : 한 요소라도 조건을 만족하면 true</li>
<li><code>noneMatch()</code> : 모든 요소가 조건을 만족하지 않으면 true</li>
</ul>
<h3 id="findfirst-findany">findFirst(), findAny()</h3>
<p><strong>조건에 일치하는 요소 반환. 보통 중간 연산인 <code>filter()</code>와 같이 사용</strong></p>
<pre><code class="language-java">Optional&lt;T&gt; findFirst()
Optional&lt;T&gt; findAny()

Optional&lt;Student&gt; result = stuStream.filter( s -&gt; s.getTotalScore() &lt;= 100).findFirst();
Optional&lt;Student&gt; result = stuStream.parallel().filter( s -&gt; s.getTotalScore() &lt;= 100).findAny();</code></pre>
<ul>
<li><p>결과가 null일수도 있기 때문에 반환타입이 <code>Optional&lt;T&gt;</code> 이다.</p>
</li>
<li><p><code>findFirst()</code> : 직렬 스트림에서 사용. <code>filter()</code>의 조건을 만족하는 요소 중 첫 번째 요소를 반환</p>
</li>
<li><p><code>findAny()</code> : 병렬 스트림에서 사용. <code>filter()</code>의 조건을 만족하는 요소 중 아무거나 하나를 반환</p>
</li>
</ul>
<h3 id="reduce">reduce()</h3>
<p><strong>스트림의 요소를 하나씩 줄여가면서 누적 연산(accumulator) 수행</strong></p>
<pre><code class="language-java">Optional&lt;T&gt; reduce(BinaryOperator&lt;T&gt; accumulator)
T reduce(T identity, BinaryOperator&lt;T&gt; accumulator)
U reduce(U identity, BiFunction&lt;U,T,U&gt; accumulator, BinaryOperator&lt;U&gt; combiner)
// identity : 초기값
// accumulator : 이전 연산 결과와 스트림의 요소에 수행할 연산
// combiner : 병렬 처리된 결과를 합치는데 사용할 연산(병렬 스트림)</code></pre>
<blockquote>
<p><code>Optional&lt;T&gt; reduce()</code>와 <code>T reduce()</code>
이 두 메서드는 초기값의 유무가 차이날 뿐 같은 기능을 한다. 
그런데 만약, 비어있는 스트림의 경우 <code>T reduce()</code>은 초기값을 반환한다. 그래서 초기값의 반환타입과 <code>reduce()</code>의 반환타입이 <code>T</code>로 같은 것이다.
그러나 <code>Optional&lt;T&gt; reduce()</code>는 초기값이 없어 반환값이 null이 되기 때문에 반환타입이 <code>Optional&lt;T&gt;</code>인 것이다.</p>
</blockquote>
<p><strong>최종 연산인 <code>count()</code>, <code>sum()</code>, <code>max()</code>, <code>min()</code> 등을 reduce를 활용해 구현할 수 있다.</strong></p>
<pre><code class="language-java">IntStream intStream = IntStream.of(1, 4, 2, 11, 3);
int count = intStream.reduce(0, (a, b) -&gt; a + 1);

intStream = IntStream.of(1, 4, 2, 11, 3);
int sum = intStream.reduce(0, (a, b) -&gt; a + b);

intStream = IntStream.of(1, 4, 2, 11, 3);
int max = intStream.reduce(Integer.MIN_VALUE, (a, b) -&gt; a &gt; b ? a : b);

intStream = IntStream.of(1, 4, 2, 11, 3);
int min = intStream.reduce(Integer.MAX_VALUE, (a, b) -&gt; a &gt; b ? b : a);

System.out.printf(&quot;%d, %d, %d, %d&quot;, count, sum1, max, min);</code></pre>
<blockquote>
<p>5, 21, 11, 1</p>
</blockquote>
<h3 id="실습예제">실습예제</h3>
<pre><code class="language-java">import java.util.*;
import java.util.stream.*;

class Ex14_9 {
    public static void main(String[] args) {
        String[] strArr = {
                &quot;Inheritance&quot;, &quot;Java&quot;, &quot;Lambda&quot;, &quot;stream&quot;,
                &quot;OptionalDouble&quot;, &quot;IntStream&quot;, &quot;count&quot;, &quot;sum&quot;
        };
        // 빈 문자열을 하나도 가지고 있지 않다면 true 반환
        boolean noEmptyStr = Stream.of(strArr).noneMatch(s -&gt; s.length() == 0);
        // s로 시작하는 문자열 중 첫 번째 요소를 반환
        Optional&lt;String&gt; sWord = Stream.of(strArr)
                .filter(s -&gt; s.charAt(0) == &#39;s&#39;).findFirst();

        System.out.println(&quot;noEmptyStr = &quot; + noEmptyStr);
        // s로 시작하는 문자열이 없다면 문구 출력
        System.out.println(&quot;sWord = &quot; + sWord.orElse(&quot;s로 시작하는 단어가 없습니다.&quot;));

        // Stream&lt;String[]&gt;을 IntStream으로 변환
        Stream.of(strArr).mapToInt(String::length).forEach(s -&gt; System.out.print(s + &quot; &quot;));
        System.out.println();

        IntStream intStream1 = Stream.of(strArr).mapToInt(String::length);
        int count = intStream1.reduce(0, (a, b) -&gt; a + 1);

        IntStream intStream2 = Stream.of(strArr).mapToInt(String::length);
        int sum = intStream2.reduce(0, Integer::sum);
//        int sum = intStream2.reduce(0, (a, b) -&gt; a + b); // 람다식

        // 초기값을 설정안하면 반환타입은 OptionalInt로 설정해줘야 한다.
        IntStream intStream3 = Stream.of(strArr).mapToInt(String::length);
        OptionalInt max = intStream3.reduce(Integer::max);
//        OptionalInt max = intStream3.reduce((a,b)-&gt; a &gt; b ? a : b); // 람다식

        IntStream intStream4 = Stream.of(strArr).mapToInt(String::length);
        OptionalInt min = intStream4.reduce(Integer::min);
//        OptionalInt min = intStream4.reduce((a,b)-&gt; a &gt; b ? b : a); // 람다식

        System.out.println(&quot;count = &quot; + count);
        System.out.println(&quot;sum = &quot; + sum);
        System.out.println(&quot;max = &quot; + max.orElse(0));
        System.out.println(&quot;min = &quot; + min.orElse(0));
    }
}</code></pre>
<blockquote>
<p>noEmptyStr = true
sWord = stream
11 4 6 6 14 9 5 3 
count = 8
sum = 58
max = 14
min = 3</p>
</blockquote>
<ul>
<li><p><code>IntStream intStream1 = Stream.of(strArr).mapToInt(String::length);</code> : String[] 배열의 각 요소의 길이를 IntStream으로 변환할 때 mapToInt 사용</p>
</li>
<li><p><code>OptionalInt max = intStream3.reduce(Integer::max);</code> : 초기값을 설정안하면 반환타입은 Optional 타입으로 해줘야 한다.</p>
</li>
<li><p><code>reduce((a,b)-&gt; a &gt; b ? a : b)</code> 이 람다식을 <code>reduce(Integer::max)</code> 변경 가능</p>
</li>
</ul>
<h2 id="collect와-collectors">collect()와 Collectors</h2>
<p><strong>collect()는 Collector를 매개변수로 하는 스트림의 최종 연산</strong></p>
<pre><code class="language-java">R collect(Collector&lt;T,A,R&gt; collector) // Collector를 구현한 클래스의 객체를 매개변수로 지정
R collect(Supplier&lt;R&gt; supplier, BiConsumer&lt;R,T&gt; accumulator, BiConsumer&lt;R,R&gt; combiner) // 잘 안쓰임</code></pre>
<p><strong>Collector는 수집(collect)에 필요한 메서드를 정의해 놓은 인터페이스</strong></p>
<pre><code class="language-java">// T(요소)를 A에 누적한 다음, 결과를 R로 변환해서 반환
public interface Collector&lt;T, A, R&gt; {
    // 누적할 곳 
    Supplier&lt;A&gt; supplier(); // StringBuilder::new
    // 누적 방법 
    BiConsumer&lt;A, T&gt; accumulator(); // (sb, s) -&gt; sb.append(s)
    // 결합 방법(병렬) 
    BinaryOperator&lt;A&gt; combiner(); // (sb1, sb2) -&gt; sb1.append(sb2)
    // 최종 변환 (R타입으로) 
    Function&lt;A, R&gt; finisher(); // sb -&gt; sb.toString()

    // 컬렉터 특성의 담긴 Set 반환
    Set&lt;Characteristics&gt; characteristics() ; 
    ...
}</code></pre>
<p><strong>Collectors 클래스는 다양한 기능의 컬렉터(Collector를 구현한 클래스)를 제공</strong></p>
<h3 id="tolist-toset-tomap-tocollection">toList(), toSet(), toMap(), toCollection()</h3>
<p><strong>: 스트림을 컬렉션으로 변환</strong></p>
<pre><code class="language-java">Stream&lt;Student&gt; studentStream = Stream.of(
        new Student(&quot;이빛나&quot;, 3, 300),
        new Student(&quot;김자바&quot;, 1, 200),
        new Student(&quot;안정호&quot;, 2, 100),
        new Student(&quot;박코딩&quot;, 2, 150),
        new Student(&quot;최자연&quot;, 1, 200),
        new Student(&quot;연보라&quot;, 3, 290),
        new Student(&quot;정나라&quot;, 3, 180)
);

List&lt;String&gt; list = studentStream
        .map(Student::getName)
        .collect(Collectors.toList());
System.out.println(list);</code></pre>
<blockquote>
<p>[이빛나, 김자바, 안정호, 박코딩, 최자연, 연보라, 정나라]</p>
</blockquote>
<ul>
<li>studentStream의 이름을 List로 변환</li>
</ul>
<pre><code class="language-java">ArrayList&lt;Integer&gt; arrayList = studentStream
        .map(Student::getTotalScore)
        .sorted()
        .collect(Collectors.toCollection(ArrayList::new));
System.out.println(arrayList);</code></pre>
<blockquote>
<p>[100, 150, 180, 200, 200, 290, 300]</p>
</blockquote>
<ul>
<li>ArrayList로 변환하고 싶을 때 <code>.collect(Collectors.toCollection(ArrayList::new));</code> 사용</li>
</ul>
<pre><code class="language-java">Map&lt;String, Student&gt; map = studentStream
        .collect(Collectors.toMap(Student::getName, p -&gt; p));
System.out.println(map);</code></pre>
<blockquote>
<p>{박코딩=[박코딩, 2, 150], 김자바=[김자바, 1, 200], 
이빛나=[이빛나, 3, 300], 안정호=[안정호, 2, 100], 
연보라=[연보라, 3, 290], 최자연=[최자연, 1, 200], 
정나라=[정나라, 3, 180]}</p>
</blockquote>
<ul>
<li>이름을 key로 하고 Student 객체를 value로 하는 map으로 변환</li>
<li><code>p -&gt; p</code>는 항등함수로 입력한 변수가 그대로 출력된다.</li>
</ul>
<h3 id="toarray">toArray()</h3>
<p><strong>스트림을 배열로 변환</strong></p>
<pre><code class="language-java">Student[] students = studentStream.toArray(Student[]::new);
System.out.println(Arrays.toString(students));</code></pre>
<blockquote>
<p>[[이빛나, 3, 300], [김자바, 1, 200], 
[안정호, 2, 100], [박코딩, 2, 150], 
[최자연, 1, 200], [연보라, 3, 290], 
[정나라, 3, 180]]</p>
</blockquote>
<h3 id="counting-summingint-maxby-minby">counting(), summingInt(), maxBy(), minBy()</h3>
<p><strong>스트림의 통계 정보를 제공</strong></p>
<pre><code class="language-java">long count = studentStream.count();
long count = studentStream.collect(Collectors.counting());</code></pre>
<ul>
<li>똑같이 studentStream의 요소 개수를 반환한다. 그러나 <code>count()</code>는 전체 요소만 카운팅할 수 있지만 <code>collect(Collectors.counting())</code>은 그룹별로 나눠서 카운팅할 수 있다.</li>
</ul>
<pre><code class="language-java">long totalScore = studentStream.mapToInt(Student::getTotalScore).sum();
long totalScore = studentStream.collect(Collectors.summingInt(Student::getTotalScore));

Optional&lt;Student&gt; topStudent = studentStream
        .max(Comparator.comparingInt(Student::getTotalScore));
Optional&lt;Student&gt; topStudent = studentStream
        .collect(Collectors.maxBy(Comparator.comparingInt(Student::getTotalScore)));</code></pre>
<ul>
<li><code>summingInt(), maxBy(), minBy()</code> 모두 기능은 기존 <code>sum(), max(), min()</code>과 동일하지만 그룹별로 나눠서 작업을 수행할 수 있다.</li>
</ul>
<h3 id="reducing">reducing()</h3>
<p><strong>reduce()는 전체만 리듀싱할 수 있지만 reducing()은 그룹별로 나눠서 리듀싱 할 수 있다.</strong></p>
<pre><code class="language-java">IntStream intStream = Arrays.stream(new int[]{1, 2, 3, 4, 5});

long sum = intStream.reduce(0, (a, b) -&gt; a + b);
long sum = intStream.boxed().collect(Collectors.reducing(0, (a, b) -&gt; a + b));</code></pre>
<h3 id="joining">joining()</h3>
<p><strong>문자열 스트림의 요소를 모두 연결</strong></p>
<pre><code class="language-java">String strName = studentStream
        .map(Student::getName)
        .collect(Collectors.joining(&quot;,&quot;));
System.out.println(strName);</code></pre>
<blockquote>
<p>이빛나,김자바,안정호,박코딩,최자연,연보라,정나라</p>
</blockquote>
<ul>
<li><code>map()</code>으로 이름을 뽑아내고 <code>joining(&quot;,&quot;)</code>을 통해 <code>,</code> 구분자로 해서 이름을 나열하는 String 문자열을 생성</li>
</ul>
<pre><code class="language-java">String strName = studentStream
        .map(Student::getName)
        .collect(Collectors.joining(&quot;,&quot;, &quot;[&quot;, &quot;]&quot;));
System.out.println(strName);</code></pre>
<blockquote>
<p>[이빛나,김자바,안정호,박코딩,최자연,연보라,정나라]</p>
</blockquote>
<ul>
<li><code>joining(&quot;,&quot;, &quot;[&quot;, &quot;]&quot;)</code> : <code>,</code>를 구분자로 하고 문자열의 처음과 끝에 <code>[</code>, <code>]</code> 추가</li>
</ul>
<h3 id="그룹화--분할">그룹화 &amp; 분할</h3>
<p><strong>partitioningBy() : 스트림을 2분할 한다.</strong></p>
<pre><code class="language-java">Collector partitioningBy(Predicate predicate)
Collector partitioningBy(Predicate predicate, Collector downstream)</code></pre>
<ul>
<li>괄호 안에 조건식을 넣어서 Map의 key를 true/false로 하고 조건식을 만족하는 값을 true의 value 값으로 저장한다.<h4 id="실습예제-1">실습예제</h4>
<pre><code class="language-java">import java.util.*;
import java.util.stream.*;
</code></pre>
</li>
</ul>
<p>import static java.util.stream.Collectors.<em>;
import static java.util.Comparator.</em>;</p>
<pre><code>- `Collectors.partitioningBy` -&gt; `partitioningBy` 으로 축약
- `Comparator.comparing` -&gt; `comparing` 으로 축약

```java
class Student2 {
    String name;
    boolean isMale;
    int hak;
    int ban;
    int score;

    Student2(String name, boolean isMale, int hak, int ban, int score) {
        this.name = name;
        this.isMale = isMale;
        this.hak = hak;
        this.ban = ban;
        this.score = score;
    }

    String getName() { return name; }
    boolean isMale() { return isMale; }
    int getHak() { return hak; }
    int getBan() { return ban; }
    int getScore() { return score; }

    public String toString() {
        return String.format(&quot;[%s, %s, %d학년 %d반, %3d점]&quot;,
                name, isMale ? &quot;남&quot; : &quot;여&quot;, hak, ban, score);
    }
}</code></pre><ul>
<li>Student2 변수, 생성자, getter 정의</li>
<li><code>toString()</code> 오버라이딩</li>
</ul>
<pre><code class="language-java">class Ex14_10 {
    public static void main(String[] args) {
        Student2[] stuArr = {
                new Student2(&quot;김코딩&quot;, true, 1, 1, 300),
                new Student2(&quot;정수민&quot;, false, 1, 1, 250),
                new Student2(&quot;오정호&quot;, true, 1, 1, 200),
                new Student2(&quot;김선미&quot;, false, 1, 2, 150),
                new Student2(&quot;정주일&quot;, true, 1, 2, 100),
                new Student2(&quot;이효리&quot;, false, 1, 2, 50),
                new Student2(&quot;이정현&quot;, false, 1, 3, 100),
                new Student2(&quot;유정선&quot;, false, 1, 3, 150),
                new Student2(&quot;강호동&quot;, true, 1, 3, 200),
                new Student2(&quot;이수근&quot;, true, 2, 1, 300),
                new Student2(&quot;김미희&quot;, false, 2, 1, 250),
                new Student2(&quot;최시원&quot;, true, 2, 1, 200),
                new Student2(&quot;박지원&quot;, false, 2, 2, 150),
                new Student2(&quot;안세훈&quot;, true, 2, 2, 100),
                new Student2(&quot;오채원&quot;, false, 2, 2, 50),
                new Student2(&quot;임희민&quot;, false, 2, 3, 100),
                new Student2(&quot;홍은채&quot;, false, 2, 3, 150),
                new Student2(&quot;신정우&quot;, true, 2, 3, 200)
        };</code></pre>
<ul>
<li>Student2 객체들을 정의하고 Student2[] 배열로 저장</li>
</ul>
<pre><code class="language-java">        System.out.printf(&quot;1. 단순분할(성별로 분할)%n&quot;);
        Map&lt;Boolean, List&lt;Student2&gt;&gt; stuBySex = Stream.of(stuArr)
                .collect(partitioningBy(Student2::isMale));

        List&lt;Student2&gt; maleStudent = stuBySex.get(true);
        List&lt;Student2&gt; femaleStudent = stuBySex.get(false);

        for (Student2 s : maleStudent) System.out.println(s);
        for (Student2 s : femaleStudent) System.out.println(s);</code></pre>
<blockquote>
<ol>
<li>단순분할(성별로 분할)
[김코딩, 남, 1학년 1반, 300점]ㅤ
[오정호, 남, 1학년 1반, 200점]ㅤ
[정주일, 남, 1학년 2반, 100점]ㅤ
[강호동, 남, 1학년 3반, 200점]ㅤ
[이수근, 남, 2학년 1반, 300점]ㅤ
[최시원, 남, 2학년 1반, 200점]ㅤ
[안세훈, 남, 2학년 2반, 100점]ㅤ
[신정우, 남, 2학년 3반, 200점]ㅤ
[정수민, 여, 1학년 1반, 250점]ㅤ
[김선미, 여, 1학년 2반, 150점]ㅤ
[이효리, 여, 1학년 2반,  50점]ㅤ
[이정현, 여, 1학년 3반, 100점]ㅤ
[유정선, 여, 1학년 3반, 150점]ㅤ
[김미희, 여, 2학년 1반, 250점]ㅤ
[박지원, 여, 2학년 2반, 150점]ㅤ
[오채원, 여, 2학년 2반,  50점]ㅤ
[임희민, 여, 2학년 3반, 100점]ㅤ
[홍은채, 여, 2학년 3반, 150점]ㅤ</li>
</ol>
</blockquote>
<ul>
<li><p><code>partitioningBy(Student2::isMale)</code>를 통해 성별로 2분할해서 Map의 key에 남자면 true, 여자면 false 저장, value에는 key에 해당하는 Student2 객체를 List 타입으로 저장</p>
<pre><code class="language-java">      System.out.printf(&quot;%n2. 단순분할 + 통계(성별 학생 수)%n&quot;);
      Map&lt;Boolean, Long&gt; stuNumBySex = Stream.of(stuArr)
              .collect(partitioningBy(Student2::isMale, counting()));

      System.out.println(&quot;남학생 수 :&quot; + stuNumBySex.get(true));
      System.out.println(&quot;여학생 수 :&quot; + stuNumBySex.get(false));</code></pre>
<blockquote>
<ol start="2">
<li>단순분할 + 통계(성별 학생 수)
남학생 수 :8
여학생 수 :10</li>
</ol>
</blockquote>
</li>
<li><p><code>partitioningBy(Student2::isMale, counting())</code> : 성별로 2분할(true, false)하고 각각의 요소의 개수르 카운팅</p>
<pre><code class="language-java">      System.out.printf(&quot;%n3. 단순분할 + 통계(성별 1등)%n&quot;);
      Map&lt;Boolean, Optional&lt;Student2&gt;&gt; topScoreBySex = Stream.of(stuArr)
              .collect(partitioningBy(Student2::isMale,
                      maxBy(comparingInt(Student2::getScore))));

      System.out.println(&quot;남학생 1등 :&quot; + topScoreBySex.get(true));
      System.out.println(&quot;여학생 1등 :&quot; + topScoreBySex.get(false));

      // Optional 객체의 값을 꺼내서 저장
      Map&lt;Boolean, Student2&gt; topScoreBySex2 = Stream.of(stuArr)
              .collect(partitioningBy(Student2::isMale,
                      collectingAndThen(
                              maxBy(comparingInt(Student2::getScore)),
                              Optional::get)));

      System.out.println(&quot;남학생 1등 :&quot; + topScoreBySex2.get(true));
      System.out.println(&quot;여학생 1등 :&quot; + topScoreBySex2.get(false));</code></pre>
<blockquote>
<ol start="3">
<li>단순분할 + 통계(성별 1등)
남학생 1등 :Optional[[김코딩, 남, 1학년 1반, 300점]]
여학생 1등 :Optional[[정수민, 여, 1학년 1반, 250점]]
남학생 1등 :[김코딩, 남, 1학년 1반, 300점]
여학생 1등 :[정수민, 여, 1학년 1반, 250점]</li>
</ol>
</blockquote>
</li>
<li><p>성별로 2분할 후 <code>maxBy(comparingInt(Student2::getScore)</code>를 이용하여 각 성별의 점수가 최대값이 Studeent2 객체 추출</p>
</li>
<li><p><code>maxBy()</code>의 반환타입은 Optional이기 때문에 Map의 value 반환타입을 <code>Optional&lt;Student2&gt;</code>으로 지정</p>
</li>
<li><p><code>collectingAndThen()</code>을 이용해서 Optional 객체의 값을 꺼내서 Map의 value에 저장 가능</p>
</li>
</ul>
<pre><code class="language-java">        System.out.printf(&quot;%n4. 다중분할(성별 불합격자, 100점 이하)%n&quot;);

        Map&lt;Boolean, Map&lt;Boolean, List&lt;Student2&gt;&gt;&gt; failedStuBySex =
                Stream.of(stuArr).collect(partitioningBy(Student2::isMale,
                        partitioningBy(s -&gt; s.getScore() &lt;= 100))
                );
        List&lt;Student2&gt; failedMaleStu = failedStuBySex.get(true).get(true);
        List&lt;Student2&gt; failedFemaleStu = failedStuBySex.get(false).get(true);

        for (Student2 s : failedMaleStu) System.out.println(s);
        for (Student2 s : failedFemaleStu) System.out.println(s);
    }
}</code></pre>
<blockquote>
<ol start="4">
<li>다중분할(성별 불합격자, 100점 이하)
[정주일, 남, 1학년 2반, 100점]ㅤ
[안세훈, 남, 2학년 2반, 100점]ㅤ
[이효리, 여, 1학년 2반,  50점]ㅤ
[이정현, 여, 1학년 3반, 100점]ㅤ
[오채원, 여, 2학년 2반,  50점]ㅤ
[임희민, 여, 2학년 3반, 100점]ㅤ</li>
</ol>
</blockquote>
<ul>
<li>성별로 2분할 후, value 값을 다시 점수가 100점 이하면 key를 true로 하고 value를 Student2 객체로 저장 -&gt; 다중분할</li>
</ul>
<p><strong>groupingBy() : 스트림을 n분할 한다.</strong></p>
<pre><code class="language-java">Collector groupingBy(Function classifier)
Collector groupingBy(Function classifier, Collector downstream)
Collector groupingBy(Function classifier, Supplier mapFactory, Collector downstream)</code></pre>
<ul>
<li>괄호 안에 스트림을 나눌 기준을 정해주고 그 값을 Map의 key에 저장하고 각 key에 해당하는 값을 value에 저장한다.</li>
</ul>
<h4 id="실습예제-2">실습예제</h4>
<pre><code class="language-java">import java.util.*;
import java.util.stream.*;

import static java.util.stream.Collectors.*;
import static java.util.Comparator.*;

class Student3 {
    String name;
    boolean isMale;
    int hak;
    int ban;
    int score;

    Student3(String name, boolean isMale, int hak, int ban, int score) {
        this.name = name;
        this.isMale = isMale;
        this.hak = hak;
        this.ban = ban;
        this.score = score;
    }

    String getName() { return name; }
    boolean isMale() { return isMale; }
    int getHak() { return hak; }
    int getBan() { return ban; }
    int getScore() { return score; }

    public String toString() {
        return String.format(&quot;[%s, %s, %d학년 %d반, %3d점]&quot;,
                name, isMale ? &quot;남&quot; : &quot;여&quot;, hak, ban, score);
    }

    enum Level {HIGH, MID, LOW}
}</code></pre>
<ul>
<li><p>위 예제와 동일하나 <code>enum Level {HIGH, MID, LOW}</code> 스트림을 그룹화할 enum 클래스 1개 추가</p>
<pre><code class="language-java">class Ex14_11 {
  public static void main(String[] args) {
      Student3[] stuArr = {
              new Student3(&quot;김코딩&quot;, true, 1, 1, 300),
              new Student3(&quot;정수민&quot;, false, 1, 1, 250),
              new Student3(&quot;오정호&quot;, true, 1, 1, 200),
              new Student3(&quot;김선미&quot;, false, 1, 2, 150),
              new Student3(&quot;정주일&quot;, true, 1, 2, 100),
              new Student3(&quot;이효리&quot;, false, 1, 2, 50),
              new Student3(&quot;이정현&quot;, false, 1, 3, 100),
              new Student3(&quot;유정선&quot;, false, 1, 3, 150),
              new Student3(&quot;강호동&quot;, true, 1, 3, 200),
              new Student3(&quot;이수근&quot;, true, 2, 1, 300),
              new Student3(&quot;김미희&quot;, false, 2, 1, 250),
              new Student3(&quot;최시원&quot;, true, 2, 1, 200),
              new Student3(&quot;박지원&quot;, false, 2, 2, 150),
              new Student3(&quot;안세훈&quot;, true, 2, 2, 100),
              new Student3(&quot;오채원&quot;, false, 2, 2, 50),
              new Student3(&quot;임희민&quot;, false, 2, 3, 100),
              new Student3(&quot;홍은채&quot;, false, 2, 3, 150),
              new Student3(&quot;신정우&quot;, true, 2, 3, 200)
      };</code></pre>
</li>
<li><p>객체 데이터는 위 예제와 동일</p>
<pre><code class="language-java">      System.out.printf(&quot;1. 단순그룹화(반별로 그룹화)%n&quot;);
      Map&lt;Integer, List&lt;Student3&gt;&gt; stuByBan = Stream.of(stuArr)
              .collect(groupingBy(Student3::getBan));

      for (List&lt;Student3&gt; ban : stuByBan.values()) {
          for (Student3 s : ban) {
              System.out.println(s);
          }
      }</code></pre>
<blockquote>
<ol>
<li>단순그룹화(반별로 그룹화)
[김코딩, 남, 1학년 1반, 300점]ㅤ
[정수민, 여, 1학년 1반, 250점]ㅤ
[오정호, 남, 1학년 1반, 200점]ㅤ
[이수근, 남, 2학년 1반, 300점]ㅤ
[김미희, 여, 2학년 1반, 250점]ㅤ
[최시원, 남, 2학년 1반, 200점]ㅤ
[김선미, 여, 1학년 2반, 150점]ㅤ
[정주일, 남, 1학년 2반, 100점]ㅤ
[이효리, 여, 1학년 2반,  50점]ㅤ
[박지원, 여, 2학년 2반, 150점]ㅤ
[안세훈, 남, 2학년 2반, 100점]ㅤ
[오채원, 여, 2학년 2반,  50점]ㅤ
[이정현, 여, 1학년 3반, 100점]ㅤ
[유정선, 여, 1학년 3반, 150점]ㅤ
[강호동, 남, 1학년 3반, 200점]ㅤ
[임희민, 여, 2학년 3반, 100점]ㅤ
[홍은채, 여, 2학년 3반, 150점]ㅤ
[신정우, 남, 2학년 3반, 200점]ㅤ</li>
</ol>
</blockquote>
</li>
<li><p><code>groupingBy(Student3::getBan)</code> 을 통해 반별로 그룹화 후 출력</p>
</li>
</ul>
<pre><code class="language-java">        System.out.printf(&quot;%n2. 단순그룹화(성적별로 그룹화)%n&quot;);
        Map&lt;Student3.Level, List&lt;Student3&gt;&gt; stuByLevel = Stream.of(stuArr)
                .collect(groupingBy(s -&gt; {
                    if (s.getScore() &gt;= 200) return Student3.Level.HIGH;
                    else if (s.getScore() &gt;= 100) return Student3.Level.MID;
                    else return Student3.Level.LOW;
                }));

        for (Student3.Level key : Student3.Level.values()) {
            System.out.println(&quot;[&quot; + key + &quot;]&quot;);

            for (Student3 s : stuByLevel.get(key))
                System.out.println(s);
            System.out.println();
        }</code></pre>
<blockquote>
<ol start="2">
<li>단순그룹화(성적별로 그룹화)
[HIGH]ㅤ
[김코딩, 남, 1학년 1반, 300점]ㅤ
[정수민, 여, 1학년 1반, 250점]ㅤ
[오정호, 남, 1학년 1반, 200점]ㅤ
[강호동, 남, 1학년 3반, 200점]ㅤ
[이수근, 남, 2학년 1반, 300점]ㅤ
[김미희, 여, 2학년 1반, 250점]ㅤ
[최시원, 남, 2학년 1반, 200점]ㅤ
[신정우, 남, 2학년 3반, 200점]ㅤ
<br>[MID]ㅤ
[김선미, 여, 1학년 2반, 150점]ㅤ
[정주일, 남, 1학년 2반, 100점]ㅤ
[이정현, 여, 1학년 3반, 100점]ㅤ
[유정선, 여, 1학년 3반, 150점]ㅤ
[박지원, 여, 2학년 2반, 150점]ㅤ
[안세훈, 남, 2학년 2반, 100점]ㅤ
[임희민, 여, 2학년 3반, 100점]ㅤ
[홍은채, 여, 2학년 3반, 150점]ㅤ
<br>[LOW]ㅤ
[이효리, 여, 1학년 2반,  50점]ㅤ
[오채원, 여, 2학년 2반,  50점]ㅤ</li>
</ol>
</blockquote>
<ul>
<li><p>Map의 key로 enum 클래스 Level을 지정하고 <code>groupingBy()</code>로 점수별로 그룹화</p>
<pre><code class="language-java">      System.out.printf(&quot;%n3. 단순그룹화 + 통계(성적별 학생수)%n&quot;);
      Map&lt;Student3.Level, Long&gt; stuCntByLevel = Stream.of(stuArr)
              .collect(groupingBy(s -&gt; {
                  if (s.getScore() &gt;= 200) return Student3.Level.HIGH;
                  else if (s.getScore() &gt;= 100) return Student3.Level.MID;
                  else return Student3.Level.LOW;
              }, counting()));

      for (Student3.Level key : Student3.Level.values())
          System.out.printf(&quot;[%s] - %d명%n&quot;, key, stuCntByLevel.get(key));
      System.out.println();</code></pre>
<blockquote>
<ol start="3">
<li>단순그룹화 + 통계(성적별 학생수)
[HIGH] - 8명
[MID] - 8명
[LOW] - 2명</li>
</ol>
</blockquote>
</li>
<li><p>2번과 같이 점수별로 enum 클래스의 3개 값으로 그룹화하고 counting()을 통해 각 그룹별 학생 수 카운팅</p>
<pre><code class="language-java">      System.out.printf(&quot;%n4. 다중그룹화(학년별, 반별)&quot;);
      Map&lt;Integer, Map&lt;Integer, List&lt;Student3&gt;&gt;&gt; stuByHakAndBan =
              Stream.of(stuArr)
                      .collect(groupingBy(Student3::getHak,
                              groupingBy(Student3::getBan)
                      ));

      for (Map&lt;Integer, List&lt;Student3&gt;&gt; hak : stuByHakAndBan.values()) {
          for (List&lt;Student3&gt; ban : hak.values()) {
              System.out.println();
              for (Student3 s : ban)
                  System.out.println(s);
          }
      }</code></pre>
<blockquote>
<ol start="4">
<li>다중그룹화(학년별, 반별)
[김코딩, 남, 1학년 1반, 300점]ㅤ
[정수민, 여, 1학년 1반, 250점]ㅤ
[오정호, 남, 1학년 1반, 200점]ㅤ
<br>[김선미, 여, 1학년 2반, 150점]ㅤ
[정주일, 남, 1학년 2반, 100점]ㅤ
[이효리, 여, 1학년 2반,  50점]ㅤ
<br>[이정현, 여, 1학년 3반, 100점]ㅤ
[유정선, 여, 1학년 3반, 150점]ㅤ
[강호동, 남, 1학년 3반, 200점]ㅤ
<br>[이수근, 남, 2학년 1반, 300점]ㅤ
[김미희, 여, 2학년 1반, 250점]ㅤ
[최시원, 남, 2학년 1반, 200점]ㅤ
<br>[박지원, 여, 2학년 2반, 150점]ㅤ
[안세훈, 남, 2학년 2반, 100점]ㅤ
[오채원, 여, 2학년 2반,  50점]ㅤ
<br>[임희민, 여, 2학년 3반, 100점]ㅤ
[홍은채, 여, 2학년 3반, 150점]ㅤ
[신정우, 남, 2학년 3반, 200점]ㅤ</li>
</ol>
</blockquote>
</li>
<li><p>학년을 key로 해서 그룹화하고 이 value 값을 다시 반을 key로 해서 그룹화한 다중그룹화</p>
</li>
</ul>
<pre><code class="language-java">        System.out.printf(&quot;%n5. 다중그룹화 + 통계(학년별, 반별 1등)%n&quot;);
        Map&lt;Integer, Map&lt;Integer, Student3&gt;&gt; topStuByHakAndBan =
                Stream.of(stuArr)
                        .collect(groupingBy(Student3::getHak,
                                groupingBy(Student3::getBan,
                                        collectingAndThen(
                                                maxBy(comparingInt(Student3::getScore))
                                                , Optional::get
                                        )
                                )
                        ));

        for (Map&lt;Integer, Student3&gt; ban : topStuByHakAndBan.values())
            for (Student3 s : ban.values())
                System.out.println(s);</code></pre>
<blockquote>
<ol start="5">
<li>다중그룹화 + 통계(학년별, 반별 1등)
[김코딩, 남, 1학년 1반, 300점]ㅤ
[김선미, 여, 1학년 2반, 150점]ㅤ
[강호동, 남, 1학년 3반, 200점]ㅤ
[이수근, 남, 2학년 1반, 300점]ㅤ
[박지원, 여, 2학년 2반, 150점]ㅤ
[신정우, 남, 2학년 3반, 200점]ㅤ</li>
</ol>
</blockquote>
<ul>
<li>학년별로 나누고 다시 반별로 나눈 후 그 중 점수가 가장 높은 학생을 <code>maxBy()</code>를 통해 추출</li>
</ul>
<pre><code class="language-java">        System.out.printf(&quot;%n6. 다중그룹화 + 통계(학년별, 반별 성적그룹)%n&quot;);
        Map&lt;String, Set&lt;Student3.Level&gt;&gt; stuByScoreGroup = Stream.of(stuArr)
                .collect(groupingBy(s -&gt; s.getHak() + &quot;-&quot; + s.getBan(),
                        mapping(s -&gt; {
                            if (s.getScore() &gt;= 200) return Student3.Level.HIGH;
                            else if (s.getScore() &gt;= 100) return Student3.Level.MID;
                            else return Student3.Level.LOW;
                        }, toSet())
                ));

        Set&lt;String&gt; keySet = stuByScoreGroup.keySet();

        for (String key : keySet) {
            System.out.println(&quot;[&quot; + key + &quot;]&quot; + stuByScoreGroup.get(key));
        }
    }
}</code></pre>
<blockquote>
<ol start="6">
<li>다중그룹화 + 통계(학년별, 반별 성적그룹)
[1-1][HIGH]ㅤ
[2-1][HIGH]ㅤ
[1-2][MID, LOW]ㅤ
[2-2][MID, LOW]ㅤ
[1-3][MID, HIGH]ㅤ
[2-3][MID, HIGH]ㅤ</li>
</ol>
</blockquote>
<ul>
<li>학년별로 그룹화 후 다시 반별로 그룹화하는게 아니라 <code>s -&gt; s.getHak() + &quot;-&quot; + s.getBan()</code> 하나의 문자열을 만들어서 이를 key 값으로 그룹화하고 각 그룹별 학생들의 점수에 대해 enum 클래스의 3개 값(HIGH, MID, LOW)으로 나눈 값을 value로 저장</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[자바의 정석 기초편] 스트림 1]]></title>
            <link>https://velog.io/@jere-gim/%EC%9E%90%EB%B0%94%EC%9D%98-%EC%A0%95%EC%84%9D-%EA%B8%B0%EC%B4%88%ED%8E%B8-%EC%8A%A4%ED%8A%B8%EB%A6%BC-1</link>
            <guid>https://velog.io/@jere-gim/%EC%9E%90%EB%B0%94%EC%9D%98-%EC%A0%95%EC%84%9D-%EA%B8%B0%EC%B4%88%ED%8E%B8-%EC%8A%A4%ED%8A%B8%EB%A6%BC-1</guid>
            <pubDate>Fri, 24 Mar 2023 10:50:40 GMT</pubDate>
            <description><![CDATA[<h1 id="📌스트림stream">📌스트림(Stream)</h1>
<p><strong>: 다양한 데이터 소스(컬렉션, 배열, 파일 등)를 표준화된 방법으로 다루기 위한 것</strong></p>
<pre><code class="language-java">List&lt;Integer&gt; list = Arrays.asList(1,2,3,4,5); // 컬렉션
Stream&lt;Integer&gt; intStream = list.stream(); // 컬렉션 -&gt; 스트림


Stream&lt;String&gt; strStream = Stream.of(new String[]{&quot;a&quot;, &quot;b&quot;, &quot;c&quot;}); // 배열 -&gt; 스트림
String[] strArr = new String[] {&quot;a&quot;, &quot;b&quot;, &quot;c&quot;, &quot;d&quot;}; // 배열
Stream&lt;String&gt; strStream = Arrays.stream(strArr); // 배열 -&gt; 스트림

Stream&lt;Integer&gt; evenStream = Stream.iterate(0, n-&gt;n+2); // 람다식 -&gt; 스트림 
Stream&lt;Double&gt; randomStream = Stream.generate(Math::random); // 메서드 참조 -&gt; 스트림

// Int형 최적화 Stream : IntStream
IntStream intStream = new Random().ints(5); // 난수 스트림</code></pre>
<h2 id="스트림으로-작업하는-과정">스트림으로 작업하는 과정</h2>
<ol>
<li><p>데이터 소스를 스트림으로 만들기</p>
</li>
<li><p>중간 연산(0 ~ n번 가능) : 연산 결과가 스트림인 연산. 반복적으로 적용 가능</p>
</li>
<li><p>최종 연산(0 ~ 1번만 가능) : 연산 결과가 스트림이 아닌 연산. 스트림의 요소를 소모</p>
<pre><code class="language-java">List&lt;Integer&gt; list = Arrays.asList(3, 4, 1, 2, 1, 1, 2, 5, 2);
Stream&lt;Integer&gt; intStream = list.stream(); // 스트림 만들기
intStream.distinct() // 중간 연산 - 중복 제거
     .sorted() // 중간 연산 - 정렬
     .forEach(s-&gt; System.out.print(s+&quot; &quot;)); // 최종 연산 - 출력
</code></pre>
</li>
</ol>
<p>intStream.forEach(System.out::println); // 에러</p>
<pre><code>- 최종 연산을 마친 스트림은 다시 사용할 수 없다.

## 스트림의 특징
**스트림은 데이터 소스로부터 데이터를 읽기만할 뿐 변경하지 않는다.(Read only)**
```java
List&lt;Integer&gt; list = Arrays.asList(3,1,5,4,2);
List&lt;Integer&gt; sortedList = list.stream()
                    .sorted()
                    .collect(Collections.toList()); // 정렬해서 sortedList에 저장

System.out.println(list); // [3,1,5,4,2] 
System.out.println(sortedList) ; // [1,2,3,4,5]</code></pre><ul>
<li>원본 데이터인 list는 변경되지 않았다.</li>
</ul>
<p><strong>스트림은 <code>Iterator</code>처럼 일회용이다. 최종 연산 후 필요하면 다시 스트림을 생성해야한다.</strong></p>
<pre><code class="language-java">List&lt;Integer&gt; list = Arrays.asList(1, 2, 3, 4, 5);
Stream&lt;Integer&gt; intStream = list.stream(); // 스트림 만들기

intStream.forEach(System.out::println); // 최종 연산 - 출력
intStream.forEach(System.out::println); // 에러</code></pre>
<ul>
<li>최종 연산하게 되면 스트림은 소모가 되고 다시 그 스트림으로 작업을 수행 할 수 없다.</li>
</ul>
<p><strong>스트림은 최종 연산 전까지 중간 연산이 수행되지 않는다. -&gt; 지연된 연산</strong></p>
<pre><code class="language-java">IntStream intStream = new Random().ints(1, 46); // 1~46 범위의 난수를 생성하는 무한 스트림
intStream.distinct().limit(6).sorted()
    .forEach(i -&gt; System.out.println(i + &quot; &quot;)); // 로또 번호 6개 출력하는 연산</code></pre>
<ul>
<li>1~46 범위의 난수를 무한으로 생성하는 스트림을 중복 제거하고 6개 자르는 연산이 가능한 이유는 중간 연산이 그때그때 수행되지 않고 최종 연산 때 한꺼번에 수행되기 때문이다.(지연된 연산)</li>
</ul>
<p><strong>스트림은 작업을 내부 반복으로 처리한다.</strong></p>
<ul>
<li>성능은 비효율적이지만 코드가 간결해진다.</li>
</ul>
<p><strong>스트림의 작업을 병렬로 처리할 수 있다. - 병렬 스트림</strong></p>
<pre><code class="language-java">Stream&lt;String&gt; strStream = Stream.of(&quot;dd&quot;, &quot;aaa&quot;, &quot;c&quot;, &quot;bb&quot;);
int sum = strStream.parallel()
        .mapToInt(String::length)
        .sum(); // 병렬 스트림</code></pre>
<ul>
<li><p>멀티쓰레드로 데이터를 동시에 처리하고 싶을 때 사용한다. 빅데이터를 병렬로 처리해서 작업속도를 높일 수 있다.</p>
</li>
<li><p>기본이 직렬 스트림이다.</p>
</li>
<li><p>병렬로 스트림을 수행하고 싶을 때 <code>parallel()</code> 추가</p>
</li>
<li><p>다시 직렬로 바꾸고 싶을때 <code>sequential()</code> 추가</p>
</li>
</ul>
<p><strong>기본형 스트림 - IntStream, LongStream, DoubleStream</strong></p>
<ul>
<li><p>오토 박싱&amp;언박싱의 비효율 제거된다.
: <code>Stream&lt;T&gt;</code>은 참조형 타입만 올 수 있다. 만약 int[] 배열을 스트림으로 만든다면 int 기본형 타입이 Integer 참조형 타입으로 오토 박싱된다.
따라서 빅데이터를 다룰 때 효율과 성능을 높이기 위해서 기본형 스트림을 사용한다.</p>
</li>
<li><p>숫자와 관련된 유용한 메서드를 <code>Stream&lt;T&gt;</code>보다 많이 제공한다
: 기본형 스트림의 요소들은 기본형 타입을 보장하기 때문에 <code>max()</code>, <code>min()</code>, <code>average()</code>, <code>sum()</code> 과 같은 숫자와 관련된 유용한 메서드들을 더 제공한다.</p>
</li>
</ul>
<hr>
<h1 id="📌스트림-만들기">📌스트림 만들기</h1>
<h2 id="컬렉션">컬렉션</h2>
<p><strong>Collection 인터페이스의 <code>stream()</code>으로 컬렉션을 스트림으로 변환</strong></p>
<pre><code class="language-java">List&lt;Integer&gt; list = Arrays.asList(1, 2, 3, 4, 5);
Stream&lt;Integer&gt; intStream = list.stream(); // 스트림 만들기</code></pre>
<h2 id="배열">배열</h2>
<p><strong>객체 배열로부터 스트림 생성</strong></p>
<pre><code class="language-java">Stream&lt;T&gt; Stream.of(T...values) // 가변 인자
Stream&lt;T&gt; Stream.of(T[])

Stream&lt;T&gt; Arrays.stream(T[])
// 배열의 인덱스 from ~ to(to는 포함x)까지 스트림 생성
Stream&lt;T&gt; Arrays.stream(T[] array, int from, int to) 

Integer[] intArr = {1,2,3,4,5}; //int[] 는 불가능
Stream&lt;Integer&gt; intStream = Arrays.stream(intArr) ; // intArr가 참조형(Integer)이여야 가능

Stream&lt;String&gt; strStream = Stream.of(&quot;a&quot;, &quot;b&quot;, &quot;c&quot;)
Stream&lt;String&gt; strStream = Stream.of(new String[]{&quot;a&quot;, &quot;b&quot;, &quot;c&quot;});
Stream&lt;String&gt; strStream = Arrays.stream(new String[]{&quot;a&quot;, &quot;b&quot;, &quot;c&quot;});
Stream&lt;String&gt; strStream = Arrays.stream(new String[]{&quot;a&quot;, &quot;b&quot;, &quot;c&quot;}, 0, 3);</code></pre>
<p><strong>기본형 배열로부터 스트림 생성</strong></p>
<pre><code class="language-java">IntStream IntStream.of(int...values) // 가변 인자
IntStream IntStream.of(int[])
IntStream Arrays.stream(int[])
// 배열의 인덱스 from ~ to(to는 포함x)까지 스트림 생성
IntStream Arrays.stream(int[] array, int from, int to)

IntStream intStream2 = IntStream.of(1, 2, 3);
IntStream intStream3 = IntStream.of(new int[]{1, 2, 3});
IntStream intStream4 = Arrays.stream(new int[]{1, 2, 3});
IntStream intStream5 = Arrays.stream(new int[]{1, 2, 3, 4, 5}, 1, 4);
intStream5.forEach(s -&gt; System.out.print(s + &quot; &quot;));</code></pre>
<blockquote>
<p>intStream5 : 2 3 4</p>
</blockquote>
<ul>
<li>LongStream, DoubleStream도 위 방법과 똑같이 사용가능하다.</li>
</ul>
<h2 id="난수">난수</h2>
<p><strong>임의의 수를 요소로 갖는 스트림 생성</strong></p>
<pre><code class="language-java">IntStream intStream = new Random().ints(); // 무한 스트림
intStream.limit(5).forEach(System.out::println); // 무한 스트림에서 5개의 요소만 출력

IntStream intStream = new Random().ints(5); // 유한 스트림</code></pre>
<ul>
<li>Random 클래스의 <code>ints()</code>, <code>longs()</code>, <code>doubles()</code> 메서드로 난수 스트림을 만들 수 있다.</li>
<li><code>limit()</code>를 통해 무한 스트림에서 n개의 요소만 출력 할 수 있다.</li>
<li><code>new Random().ints(5);</code> : 난수 스트림을 만들 때 크기를 지정해줄 수도 있다.</li>
</ul>
<p><strong>ints(), longs(), doubles()의 기본 범위</strong></p>
<ul>
<li><p><code>Integer.MIN_VALUE &lt;= ints() &lt;= Integer.MAX_VALUE</code></p>
</li>
<li><p><code>Long.MIN_VALUE &lt;= longs() &lt;= Long.MAX_VALUE</code></p>
</li>
<li><p><code>0.0 &lt;= doubles &lt; 1.0</code></p>
</li>
</ul>
<p><strong>지정된 범위의 난수를 요소로 갖는 스트림 생성</strong></p>
<pre><code class="language-java">// 무한 스트림
IntStream ints(int begin, int end)
LongStream longs(long begin, long end)
DoubleStream doubles(double begin, double end)

// 유한 스트림
IntStream ints(long streamSize, int begin, int end)
LongStream longs(long streamSize, long begin, long end)
DoubleStream doubles(long streamSize, double begin, double end)</code></pre>
<ul>
<li><code>ints(int begin, int end)</code> : begin ~ end 범위(end 포함x)의 난수를 생성하는 스트림</li>
<li><code>ints(long streamSize, int begin, int end)</code> : begin ~ end 범위(end 포함x)의 난수를 streamSize만큼 생성하는 스트림</li>
</ul>
<h2 id="특정-범위의-정수">특정 범위의 정수</h2>
<p><strong>특정 범위의 정수를 요소로 갖는 스트림 생성</strong></p>
<pre><code class="language-java">IntStream IntStream.range(int begin, int end)
IntStream IntStream.rangeClosed(int begin, int end)

IntStream intS = IntStream.range(1, 5) ; // 1,2,3,4
IntStream intS = IntStream.rangeClosed(1, 5) ; // 1,2,3,4,5</code></pre>
<ul>
<li>두 메서드의 차이는 end 값 포함 여부
range : end 값 포함 X
rangeClosed : end 값 포함 O</li>
</ul>
<h2 id="람다식">람다식</h2>
<p><strong>람다식을 소스로 하는 스트림 생성</strong></p>
<pre><code class="language-java">static &lt;T&gt; Stream&lt;T&gt; iterate(T seed, UnaryOperator&lt;T&gt; f) // 이전 요소에 종속적
static &lt;T&gt; Stream&lt;T&gt; generate(Supplier&lt;T&gt; s) // 이전 요소에 독립적</code></pre>
<ul>
<li><p>이 두 메서드는 매개변수로 받은 람다식에 의해 계산되는 값들을 요소로 하는 무한 스트림을 생성</p>
</li>
<li><p><code>T seed</code> 는 초기값</p>
</li>
</ul>
<h3 id="iterate">iterate()</h3>
<p><strong>: 이전 요소를 seed로 해서 다음 요소를 계산한다.</strong></p>
<pre><code class="language-java">Stream&lt;Integer&gt; evenStream = Stream.iterate(0, n -&gt; n +2);
evenStream.limit(10).forEach(s -&gt; System.out.print(s + &quot; &quot;));</code></pre>
<blockquote>
<p>0 2 4 6 8 10 12 14 16 18</p>
</blockquote>
<img src="https://velog.velcdn.com/images/jere-gim/post/0c307482-21c6-42a1-a96a-3fe2e248861b/image.png" width="250">

<ul>
<li>초기값부터 시작해서 람다식을 계산하고 이 결과를 seed로 다시 람다식을 계산한다.
즉, 이전 요소에 영향을 미친다.</li>
</ul>
<h3 id="generate">generate()</h3>
<p><strong>초기값이 없으며 이전 요소에 독립적이다.</strong></p>
<pre><code class="language-java">Stream&lt;Integer&gt; oneStream = Stream.generate(() -&gt; 1);
oneStream.limit(10).forEach(s -&gt; System.out.print(s + &quot; &quot;));</code></pre>
<blockquote>
<p>1 1 1 1 1 1 1 1 1 1</p>
</blockquote>
<ul>
<li>이전 요소와 상관없이 람다식이 계산된다.</li>
</ul>
<h2 id="파일과-빈-스트림">파일과 빈 스트림</h2>
<h3 id="파일">파일</h3>
<p><strong>파일을 소스로 하는 스트림 생성</strong>
<code>Stream&lt;Path&gt; Files.list(Path dir)</code></p>
<ul>
<li>지정 디렉토리(dir)에 있는 파일 목록을 소스로 하는 스트림을 생성해 반환한다.</li>
</ul>
<pre><code class="language-java">Stream&lt;String&gt; Files.lines(Path path)
Stream&lt;String&gt; Files.lines(Path path, Charset cs)
Stream&lt;String&gt; lines() // BufferedReader 클래스의 메서드</code></pre>
<ul>
<li>한 파일의 한 행(line)을 요소로 하는 스트림을 생성(보통 로그(Log)파일을 읽을 때 사용)</li>
</ul>
<h3 id="빈-스트림">빈 스트림</h3>
<p><strong>요소가 하나도 없는 비어있는 스트림</strong></p>
<pre><code class="language-java">Stream emptyStream = Stream.empty(); // 비어있는 스트림 생성 및 반환

long cnt = emptyStream.count(); // 비어있기 때문에 당연히 0</code></pre>
<ul>
<li>연산 수행 결과가 없을 때, null보단 빈 스트림을 반환하는 것이 낫다.</li>
</ul>
<hr>
<h1 id="📌중간-연산">📌중간 연산</h1>
<p><strong>: 연산 결과가 스트림인 연산. 0 ~ n번 사용 가능</strong></p>
<h2 id="중간-연산-메서드">중간 연산 메서드</h2>
<table>
<thead>
<tr>
<th align="left"><center>중간 연산</center></th>
<th align="left"><center>설명</center></th>
</tr>
</thead>
<tbody><tr>
<td align="left"><code>Stream&lt;T&gt; distinct()</code></td>
<td align="left">중복 제거</td>
</tr>
<tr>
<td align="left"><code>Stream&lt;T&gt; filter(Predicate&lt;T&gt; predicate</code></td>
<td align="left">조건에 안맞는 요소 제거</td>
</tr>
<tr>
<td align="left"><code>Stream&lt;T&gt; limit(long maxSize)</code></td>
<td align="left">스트림의 일부를 잘라낸다.</td>
</tr>
<tr>
<td align="left"><code>Stream&lt;T&gt; skip(long n)</code></td>
<td align="left">스트림의 일부를 건너뛴다.</td>
</tr>
<tr>
<td align="left"><code>Stream&lt;T&gt; peek(Consumer&lt;T&gt; action)</code></td>
<td align="left">스트림의 요소에 작업 수행</td>
</tr>
<tr>
<td align="left"><code>Stream&lt;T&gt; sorted()</code><br><code>Stream&lt;T&gt; sorted(Comparator&lt;T&gt; comparator)</code></td>
<td align="left">스트림의 요소를 정렬</td>
</tr>
</tbody></table>
<p><strong>map(), flatMap()</strong></p>
<table>
<thead>
<tr>
<th align="left"><center>반환타입</center></th>
<th align="left"><center>중간 연산</center></th>
<th align="left"><center>설명</center></th>
</tr>
</thead>
<tbody><tr>
<td align="left"><code>Stream&lt;R&gt;</code></td>
<td align="left"><code>map(Function&lt;T, R&gt; mapper)</code></td>
<td align="left"></td>
</tr>
<tr>
<td align="left"><code>IntStream</code></td>
<td align="left"><code>mapToInt(ToIntFunction&lt;T&gt; mapper)</code></td>
<td align="left"></td>
</tr>
<tr>
<td align="left"><code>LongStream</code></td>
<td align="left"><code>mapToLong(ToLongFunction&lt;T&gt; mapper)</code></td>
<td align="left"></td>
</tr>
<tr>
<td align="left"><code>DoubleStream</code></td>
<td align="left"><code>mapToDouble(ToDoubleFunction&lt;T&gt; mapper)</code></td>
<td align="left"></td>
</tr>
<tr>
<td align="left"><code>Stream&lt;R&gt;</code></td>
<td align="left"><code>flatMap(Function&lt;T, Stream&lt;R&gt;&gt; mapper)</code></td>
<td align="left">스트림의 요소를 변환한다.</td>
</tr>
<tr>
<td align="left"><code>IntStream</code></td>
<td align="left"><code>flatMapToInt(Function&lt;T, IntStream&gt; mapper)</code></td>
<td align="left"></td>
</tr>
<tr>
<td align="left"><code>LongStream</code></td>
<td align="left"><code>flatMapToLong(Function&lt;T, LongStream&gt; mapper)</code></td>
<td align="left"></td>
</tr>
<tr>
<td align="left"><code>DoubleStream</code></td>
<td align="left"><code>flatMapToDouble(Function&lt;T, DoubleStream&gt; mapper)</code></td>
<td align="left"></td>
</tr>
</tbody></table>
<h3 id="skip-limit">skip(), limit()</h3>
<p><strong><code>Stream&lt;T&gt; skip(long n)</code> : 앞에서부터 n개 건너뛴다.</strong></p>
<p><strong><code>Stream&lt;T&gt; limit(long maxSize)</code> : maxSize 이후의 요소는 잘라낸다.</strong></p>
<pre><code class="language-java">IntStream intStream = IntStream.rangeClosed(1, 10); // 12345678910
intStream.skip(3).limit(5).forEach(System.out::print);</code></pre>
<blockquote>
<p>45678</p>
</blockquote>
<ul>
<li><code>skip(3)</code> : 123 건너뛴다.</li>
<li><code>limit(5)</code> : 4부터 5개 요소만 잘라낸다.</li>
</ul>
<h3 id="filter-distinct">filter(), distinct()</h3>
<p><strong><code>Stream&lt;T&gt; filter(Predicate&lt;T&gt; predicate)</code> : 조건에 맞지 않는 요소를 제거한다.</strong></p>
<pre><code class="language-java">IntStream intStream = IntStream.rangeClosed(1, 10); // 12345678910
intStream.filter(i -&gt; i % 2 == 0).forEach(System.out::print);</code></pre>
<blockquote>
<p>246810</p>
</blockquote>
<pre><code class="language-java">IntStream intStream = IntStream.rangeClosed(1, 10); // 12345678910
intStream.filter(i -&gt; i % 2 == 0 &amp;&amp; i % 3 == 0).forEach(System.out::print);</code></pre>
<blockquote>
<p>6</p>
</blockquote>
<ul>
<li><code>intStream.filter(i -&gt; i % 2 == 0).filter(i -&gt; i % 3 == 0).forEach(System.out::print);
);</code> : filter를 2번 써서 <code>&amp;&amp;</code> 와 같은 동작을 수행할 수 있다.</li>
</ul>
<p><strong><code>Stream&lt;T&gt; distinct()</code> : 중복을 제거한다.</strong></p>
<pre><code class="language-java">IntStream intStream = IntStream.of(1,1,2,2,3,4,4,5);
intStream.distinct().forEach(System.out::print);</code></pre>
<blockquote>
<p>12345</p>
</blockquote>
<h3 id="sorted">sorted()</h3>
<p><strong><code>Stream&lt;T&gt; sorted()</code> : 스트림 요소의 기본 정렬 기준(Comparable)으로 정렬</strong></p>
<p><strong><code>Stream&lt;T&gt; sorted(Comparator&lt;T&gt; comparator)</code> : 지정된 Comparator으로 정렬</strong></p>
<pre><code class="language-java">Stream&lt;String&gt; strStream = Stream.of(&quot;dd&quot;, &quot;aaa&quot;, &quot;CC&quot;, &quot;cc&quot;, &quot;b&quot;);

// 기본 정렬 -&gt; 출력 결과 : CCaaabccdd
strStream.sorted(); // String 클래스의 Comparable
strStream.sorted(Comparator.naturalOrder()); // Comparator 클래스에 구현된 기본 정렬 기준 
strStream.sorted((s1, s2) -&gt; s1.compareTo(s2)); // 람다식
strStream.sorted(String::compareTo); // 메서드 참조

// 역순 정렬 -&gt; 출력 결과 : ddccbaaaCC
strStream.sorted(Comparator.reverseOrder()) // 기본 정렬의 역순
strStream.sorted(Comparator.&lt;String&gt;naturalOrder().reversed()) // 잘안씀

// 기본 정렬(대소문자 구분 X) -&gt; 출력 결과 : aaabCCccdd
strStream.sorted(String.CASE_INSENSITIVE_ORDER)

// 역순 정렬(대소문자 구분 X) -&gt; 출력 결과 : ddCCccbaa 오타 아님(대문자가 앞에 온다.)
strStream.sorted(String.CASE_INSENSITIVE_ORDER.reversed())

// 별도 기준 정렬 -&gt; 출력 결과 : bddCCccaaa
strStream.sorted(Comparator.comparing(String::length)) // 문자열 길이 순 정렬
strStream.sorted(Comparator.comparingInt(String::length))

// 별도 기준 역순 정렬 -&gt; 출력 결과 : aaaddCCccb
strStream.sorted(Comparator.comparing(String::length).reversed())</code></pre>
<h3 id="comparing-thencomparing">comparing(), thenComparing()</h3>
<p><strong>Comparator의 comparing()으로 정렬 기준을 제공</strong></p>
<pre><code class="language-java">Comparator&lt;T&gt; comparing(Function&lt;T, U&gt; keyExtractor)
Comparator&lt;T&gt; comparing(Function&lt;T, U&gt; keyExtractor, Comparator&lt;U&gt; keyComparator)</code></pre>
<p><strong>thenComparing() : 추가 정렬 기준을 제공할 때 사용</strong></p>
<pre><code class="language-java">Comparator&lt;T&gt; thenComparing(Comparator&lt;T&gt; other)
Comparator&lt;T&gt; thenComparing(Function&lt;T, U&gt; keyExtractor)
Comparator&lt;T&gt; thenComparing(Function&lt;T, U&gt; keyExtractor, Comparator&lt;U&gt; keyComparator)</code></pre>
<h4 id="실습-예제">실습 예제</h4>
<pre><code class="language-java">import java.util.*;
import java.util.stream.*;

class Student implements Comparable&lt;Student&gt; {
    private String name;
    private int ban;
    private int totalScore;

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

    // toString() 오버라이딩
    public String toString() {
        return String.format(&quot;[%s, %d, %d]&quot;, name, ban, totalScore);
    }

    public String getName() { return name; }
    public int getBan() { return ban; }
    public int getTotalScore() { return totalScore; }

    // 총점 내림차순으로 기본 정렬
    public int compareTo(Student s) {
        return s.totalScore - this.totalScore;
    }
}

class Ex14_5 {
    public static void main(String[] args) {
        Stream&lt;Student&gt; studentStream = Stream.of(
                new Student(&quot;이빛나&quot;, 3, 300),
                new Student(&quot;김자바&quot;, 1, 200),
                new Student(&quot;안정호&quot;, 2, 100),
                new Student(&quot;박코딩&quot;, 2, 150),
                new Student(&quot;최자연&quot;, 1, 200),
                new Student(&quot;연보라&quot;, 3, 290),
                new Student(&quot;정빛나&quot;, 3, 180)
        );

        studentStream.sorted(Comparator.comparing(Student::getBan)
                        .thenComparing(Comparator.naturalOrder()))
                .forEach(System.out::println);
    }
}ㅤ</code></pre>
<blockquote>
<p>[김자바, 1, 200]ㅤ
[최자연, 1, 200] ㅤ
[박코딩, 2, 150]ㅤ
[안정호, 2, 100]ㅤ
[이빛나, 3, 300]ㅤ
[연보라, 3, 290]ㅤ
[정빛나, 3, 180]</p>
</blockquote>
<ul>
<li>반 순서대로 정렬하고 기본 정렬(성적 내림차순)</li>
</ul>
<pre><code class="language-java">studentStream.sorted(Comparator.comparing(Student::getBan).reversed()
                .thenComparing(Student::getName))
        .forEach(System.out::println);</code></pre>
<blockquote>
<p>[연보라, 3, 290]ㅤ
[이빛나, 3, 300]ㅤ
[정빛나, 3, 180]ㅤ
[박코딩, 2, 150]ㅤ
[안정호, 2, 100]ㅤ
[김자바, 1, 200]ㅤ
[최자연, 1, 200]ㅤ</p>
</blockquote>
<ul>
<li>반 역순으로 정렬하고 이름 순서대로 정렬</li>
</ul>
<h3 id="map">map()</h3>
<p><strong>스트림의 요소 변환</strong></p>
<pre><code class="language-java">import java.io.*;
import java.util.stream.*;

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

        Stream&lt;File&gt; fileStream = Stream.of(fileArr);

        fileStream.map(File::getName) // Stream&lt;File&gt; -&gt; Stream&lt;String&gt;
                .filter(s -&gt; s.indexOf(&#39;.&#39;) != -1) // 확장자 없는 파일 삭제
                .map(s -&gt; s.substring(s.indexOf(&#39;.&#39;) + 1)) // 확장자명만 추출
                .map(String::toUpperCase) // 대문자로 변경
                .distinct() // 중복 제거
                .forEach(System.out::print); // JAVABAKTXT
    }
}</code></pre>
<blockquote>
<p>JAVABAKTXT</p>
</blockquote>
<h3 id="peek">peek()</h3>
<p><strong>스트림의 요소를 소비하지 않고 엿보기</strong></p>
<pre><code class="language-java">Stream&lt;T&gt; peek(Consumer&lt;T&gt; action)
void forEach(Consumer&lt;T&gt; action)</code></pre>
<ul>
<li><code>peek()</code>은 중간 연산. 스트림의 요소를 소비 X</li>
<li><code>forEach()</code>는 최종 연산. 스트림의 요소를 소비 O</li>
</ul>
<pre><code class="language-java">fileStream.map(File::getName) // Stream&lt;File&gt; -&gt; Stream&lt;String&gt;
        .filter(s -&gt; s.indexOf(&#39;.&#39;) != -1) // 확장자 없는 파일 삭제
        .peek(s -&gt; System.out.printf(&quot;파일명 : %s%n&quot;, s))
        .map(s -&gt; s.substring(s.indexOf(&#39;.&#39;) + 1)) // 확장자명만 추출
        .peek(s -&gt; System.out.printf(&quot;확장자 : %s%n&quot;, s))
        .map(String::toUpperCase) // 대문자로 변경
        .distinct() // 중복 제거
        .forEach(System.out::print); // JAVABAKTXT

System.out.println();</code></pre>
<blockquote>
<p>파일명 : Ex1.java
확장자 : JAVA
JAVA파일명 : Ex1.bak
확장자 : BAK
BAK파일명 : Ex2.java
확장자 : JAVA
파일명 : Ex1.txt
확장자 : TXT
TXT</p>
</blockquote>
<ul>
<li>메서드 중간 중간마다 수행이 잘되고 있는지 확인하는 디버깅 용도로 쓰인다.</li>
</ul>
<h3 id="flatmap">flatMap()</h3>
<p><strong>스트림의 스트림을 스트림으로 변환</strong></p>
<pre><code class="language-java">Stream&lt;String[]&gt; strArrStrm = Stream.of(
        new String[]{&quot;abc&quot;, &quot;def&quot;, &quot;jkl&quot;},
        new String[]{&quot;ABC&quot;, &quot;GHI&quot;, &quot;JKL&quot;}

Stream&lt;String&gt; strStrm = strArrStrm.flatMap(Arrays::stream);

String[] strings = strStrm.map(String::toLowerCase)
        .distinct()
        .sorted()
        .toArray(String[]::new);
System.out.println(Arrays.toString(strings));</code></pre>
<blockquote>
<p>[abc, def, ghi, jkl]</p>
</blockquote>
<ul>
<li>문자열 배열을 가지고 있는 스트림을 flatMap을 통해 각 문자열 배열의 요소들을 합쳐서 하나의 스트림으로 만든다.<pre><code class="language-java">String[] lineArr = {
      &quot;Believe or not It is true&quot;,
      &quot;Do or do not There is no try&quot;,
};
</code></pre>
</li>
</ul>
<p>Stream<String> lineStream = Arrays.stream(lineArr);
List<String> list = lineStream.flatMap(line -&gt; Stream.of(line.split(&quot; +&quot;)))
        .map(String::toLowerCase)
        .distinct()
        .sorted()
        .collect(Collectors.toList());
System.out.println(list);</p>
<pre><code>&gt;[believe, do, is, it, no, not, or, there, true, try]

- 문장들을 요소로 하는 String[] 배열을 flatMap을 통해 각 문장의 단어들을 하나로 합친 스트림으로 만든다.


</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[Section 2] 자료구조]]></title>
            <link>https://velog.io/@jere-gim/Section-2-%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0</link>
            <guid>https://velog.io/@jere-gim/Section-2-%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0</guid>
            <pubDate>Mon, 20 Mar 2023 15:21:12 GMT</pubDate>
            <description><![CDATA[<h1 id="📌재귀">📌재귀</h1>
<ol>
<li><p>메서드 안에서 또 자신의 메서드를 호출하는 것을 말한다.</p>
</li>
<li><p>모든 재귀함수는 for 문으로 바꿀 수 있다.</p>
</li>
<li><p>반복해서 메서드를 호출하므로 call stack에 메서드가 종료되지 않고 계속 쌓이게 된다. 이 과정이 for 문보다 메모리를 더 사용한다.</p>
</li>
<li><p>메서드를 호출하고 매서드가 종료된 이후에 복귀를 위한 컨텍스트 스위칭 비용이 발생하게 된다.</p>
</li>
</ol>
<h2 id="재귀-사용방법">재귀 사용방법</h2>
<ol>
<li>종료조건을 반드시 작성</li>
<li>문제를 작은 단위로 쪼갠다.</li>
</ol>
<h2 id="예시---factorial">예시 - factorial</h2>
<pre><code class="language-java">public class Factorial {
    public int factorial(int num) {
        int result;

        // base case
        if (num == 1) return result = 1;

        // recursive case
        result = num * factorial(num - 1);
        return result;
    }
}

class Factorial_Test {
    public static void main(String[] args) {
        Factorial f = new Factorial();

        int output = f.factorial(3);
        System.out.println(output);
    }
}</code></pre>
<blockquote>
<p>output = 6</p>
</blockquote>
<ul>
<li><p>factorial(3) = 3x2x1 이다. factorial(2) = 2x1
즉, factorial(3) = 3 x factorial(2)가 된다.</p>
</li>
<li><p>main메서드에서 factorial(3)를 호출</p>
<ul>
<li>factorial(3) = 3 x factorial(2)</li>
<li>factorial(2) = 2 x factorial(1)</li>
<li>factorial(1) = 1</li>
</ul>
</li>
</ul>
<pre><code class="language-java">public int factorial(int n) {
    int result;

    // base case
    if (n == 1) return result = 1;

    // recursive case
    result = n * factorial(n - 1);

    return result;
}</code></pre>
<ul>
<li><p>처음 factorial(3)이 call stack에 쌓이고 메서드가 실행되다가 
<code>result = n * factorial(n - 1);</code> 여기서 다시 factorial(2)가 호출된다. 이 때 아직 result의 값은 정해지지 않았다. <code>=</code> 대입 연산자는 가장 마지막에 연산되기 때문에 재귀가 먼저 발생한다.</p>
</li>
<li><p>factorial(1)이 됐을 때 <code>if (n == 1) return result = 1;</code> 종료조건이 실행되면서 <code>result = 1</code>을 return 하면서 factorial(1) 메서드는 call stack에서 사라지고 factorial(2)로 되돌아간다.
이제 여기서</p>
<pre><code class="language-java">result = 2 * factorial(2 - 1); 
</code></pre>
</li>
</ul>
<p>// result = 2 * factorial(1) = 2 * 1 = 2<br>return result;</p>
<pre><code>이 두 문장이 실행되는 것이다. 그럼 factorial(2)는 result = 2를 리턴하고 factorial(3)으로 되돌아간다.
```java
result = 3 * factorial(3 - 1);

// result = 3 * factorial(2) = 3 * 2 = 6
return result;</code></pre><p>factorial(3)으로 와서 이 두 문장을 실행하고 result = 6 을 main메서드 output에 저장하고 출력한다.</p>
<p>그림으로 표현🔽
<img src="https://velog.velcdn.com/images/jere-gim/post/2821f193-d8ac-479a-9919-cede3caf2212/image.png" alt="">
<img src="https://velog.velcdn.com/images/jere-gim/post/ef3bb6bb-4ac9-44a3-ac4c-34334e34c971/image.png" alt=""></p>
<h2 id="예시---reversearr">예시 - reverseArr</h2>
<p><strong>배열을 입력받아 순서가 뒤집힌 배열을 리턴하는 메서드</strong></p>
<pre><code class="language-java">public class ReverseArr {
    public int[] reverseArr(int[] arr) {
        if (arr.length == 0) return new int[]{};

        int[] head = Arrays.copyOfRange(arr, arr.length - 1, arr.length);
        int[] tail = reverseArr(Arrays.copyOfRange(arr, 0, arr.length - 1));

        List&lt;int[]&gt; list = new ArrayList&lt;&gt;();
        list.add(head);
        list.add(tail);

        return list.stream().flatMapToInt(Arrays::stream).toArray();
    }
}

class ReverseArr_Test {
    public static void main(String[] args) {
        ReverseArr r = new ReverseArr();

        int[] output = r.reverseArr(new int[]{1, 2, 3});
        System.out.println(Arrays.toString(output));
    }
}</code></pre>
<blockquote>
<p>output = [3, 2, 1]</p>
</blockquote>
<p><code>int[] tail = reverseArr(Arrays.copyOfRange(arr, 0, arr.length - 1));</code> : 메서드 중간에 재귀함수가 있다. 
종료조건인 <code>if (arr.length == 0) return new int[]{};</code>가 만족되서 return이 될때까지 tail에 저장되지 않고 메서드가 계속 호출된다.
그러다가 종료조건을 통해 return 값이 생기면 이 값을 tail에 저장하고 종료조건 바로 전 메서드로 복귀하고 </p>
<pre><code class="language-java">    List&lt;int[]&gt; list = new ArrayList&lt;&gt;();
    list.add(head);
    list.add(tail);

    return list.stream().flatMapToInt(Arrays::stream).toArray();</code></pre>
<p>위의 재귀함수 이후 나머지 문장들이 수행된다. 이 문장 마지막에 return이 수행되서 메서드가 종료되고 또 이 바로 전 메서드로 복귀하고 위의 문장들을 수행한다. 
이 과정을 반복하고 마지막 메서드의 return 문의 결과값을 main 메서드에서 출력하게 되는 것이다.</p>
<p>그림으로 표현🔽
<img src="https://velog.velcdn.com/images/jere-gim/post/75569bc4-a2af-4fb0-a7a5-0a8da385950e/image.png" alt="">
<img src="https://velog.velcdn.com/images/jere-gim/post/9569d532-48c7-4efd-9c07-b17cdffc8d36/image.png" alt="">
<img src="https://velog.velcdn.com/images/jere-gim/post/2176eef2-7b21-4caa-8eea-b91decca72ee/image.png" alt=""></p>
<hr>
<h1 id="📌dfs--bfs">📌DFS / BFS</h1>
<p>DFS : 
BFS : </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[자바의 정석 기초편] 람다식]]></title>
            <link>https://velog.io/@jere-gim/%EC%9E%90%EB%B0%94%EC%9D%98-%EC%A0%95%EC%84%9D-%EA%B8%B0%EC%B4%88%ED%8E%B8-%EB%9E%8C%EB%8B%A4%EC%8B%9D</link>
            <guid>https://velog.io/@jere-gim/%EC%9E%90%EB%B0%94%EC%9D%98-%EC%A0%95%EC%84%9D-%EA%B8%B0%EC%B4%88%ED%8E%B8-%EB%9E%8C%EB%8B%A4%EC%8B%9D</guid>
            <pubDate>Tue, 14 Mar 2023 13:13:37 GMT</pubDate>
            <description><![CDATA[<h1 id="📌람다식lambda-expression">📌람다식(Lambda Expression)</h1>
<p><strong>: 함수(메서드)를 간단한 식(expression)으로 표현하는 방법</strong></p>
<p><strong>람다식은 익명 함수(이름이 없는 함수)</strong></p>
<pre><code class="language-java">int max(int a, int b) {
    return a &gt; b ? a: b;
}</code></pre>
<p>람다식으로 표현🔽</p>
<pre><code class="language-java">(int a, int b) -&gt; {
    return a &gt; b ? a : b;
}</code></pre>
<ul>
<li>반환타입과 함수 이름을 생략한다.</li>
</ul>
<blockquote>
<p>함수와 메서드의 차이</p>
</blockquote>
<ul>
<li>근본적으로 동일하지만 함수는 일반적인 용어, 메서드는 객체지향개념 용어이다.</li>
<li>함수는 클래스에 독립적이지만 메서드는 클래스에 종속적이다.</li>
<li>자바는 모든 메서드가 클래스 안에서만 정의될 수 있기 때문에 메서드라고 부른다.</li>
</ul>
<h2 id="람다식-작성하기">람다식 작성하기</h2>
<ol>
<li><p>메서드의 이름과 반환타입을 제거하고 &quot;-&gt;&quot; 추가</p>
<pre><code class="language-java">(int a, int b) -&gt; {
 return a &gt; b ? a : b;
}</code></pre>
</li>
<li><p>반환값이 있는 경우, 식이나 값만 적고 return 문 생략(<code>;</code>도 생략)
<code>(int a, int b) -&gt; a &gt; b ? a : b</code></p>
</li>
<li><p>매개변수의 타입이 추론 가능하면 생략 가능(대부분 생략 가능)
<code>(a, b) -&gt; a &gt; b ? a : b</code></p>
</li>
</ol>
<h2 id="작성-시-주의사항">작성 시 주의사항</h2>
<ol>
<li><p>매개변수가 하나인 경우 괄호 생략 가능
<code>a -&gt; a * a</code></p>
</li>
<li><p>블록 안의 문장이 하나 뿐일 때 중괄호 생략 가능(<code>;</code>도 생략)
<code>i -&gt; System.out.println(i)</code></p>
</li>
</ol>
<h2 id="람다식-예시">람다식 예시</h2>
<pre><code class="language-java">(int a, int b) -&gt; {
    return a &gt; b ? a : b;
}</code></pre>
<p>람다식🔽
<code>(a, b) -&gt; a &gt; b ? a : b</code></p>
<pre><code class="language-java">int square(int x) {
    return x * x;
}</code></pre>
<p>람다식🔽
<code>x -&gt; x * x</code></p>
<pre><code class="language-java">int roll() {
    return (int)(Math.random() * 6);
}</code></pre>
<p>람다식🔽
<code>() -&gt; (int)(Math.random() * 6)</code></p>
<ul>
<li><p>매개변수 없을 때 괄호만 써준다. 괄호 생략 불가</p>
<h2 id="람다식은-익명-객체">람다식은 익명 객체</h2>
<p><strong>람다식은 익명 함수가 아니라 익명 객체이다.</strong></p>
</li>
<li><p>람다식은 메서드를 간단하게 표현한 식이다. 그런데 자바는 메서드만 단독으로 쓰일 수 없고 항상 클래스 안에 있어야 하기 때문에 익명 객체로 람다식을 감싼 것이고 따라서 람다식을 익명 개체라고 하는 것이다.</p>
<pre><code class="language-java">(a, b) -&gt; a &gt; b ? a : b
</code></pre>
</li>
</ul>
<p>// 익명 클래스
new Object() {
    int max(int a, int b) {
        return a &gt; b ? a : b ;
    }
}</p>
<p>// 참조변수에 대입
Object obj = new Object() {
    int max(int a, int b) {
        return a &gt; b ? a : b ;
    }
};
...
int value = obj.max(3,5); // 에러</p>
<pre><code>- 람다식은 익명 객체지만 Object 타입의 참조변수에 대입하면 에러가 난다. Object 타입에는 max() 라는 메서드가 정의되어 있지 않기 때문이다.
그래서 람다식을 다루기 위해 만들어진 참조타입이 **함수형 인터페이스**이다.

***
# 📌함수형 인터페이스
**단 하나의 추상 메서드만 선언된 인터페이스**
```java
@FunctionalInterface
interface MyFunction {
    int max(int a, int b);
}

public class Ex14_0 {
    public static void main(String[] args) {
        MyFunction f = (a, b) -&gt; a &gt; b ? a : b;
        int value = f.max(3, 5);
        System.out.println(value);
    }
}</code></pre><blockquote>
<p>5</p>
</blockquote>
<ul>
<li>람다식을 다루기 위해 참조타입을 함수형 인터페이스로 선언한다.</li>
<li><code>@FunctionalInterface</code> : 함수형 인터페이스라는 것을 알리는 애너테이션. 이 애너테이션이 붙은 인터페이스에 2개 이상의 추상 메서드를 작성하면 에러가 난다. -&gt; 하나의 추상 메서드만 작성 가능</li>
</ul>
<p><strong>익명 객체를 람다식으로 대체</strong></p>
<pre><code class="language-java">List&lt;String&gt; list = Arrays.asList(&quot;abc&quot;, &quot;aaa&quot;, &quot;bbb&quot;, &quot;ddd&quot;);
Collections.sort(list, new Comparator&lt;String&gt;() {
    @Override
    public int compare(String o1, String o2) {
        return o2.compareTo(o1);
    }
});</code></pre>
<ul>
<li>내림차순으로 정렬하는 기준을 익명 객체로 구현</li>
</ul>
<p>람다식으로 대체🔽</p>
<pre><code class="language-java">Collections.sort(list,(s1, s2) -&gt; s2.compareTo(s1));</code></pre>
<ul>
<li><code>Comparator&lt;T&gt;</code>도 <code>compare(T o1, T o2)</code> 추상 메서드 하나만을 가진 함수형 인터페이스이다.</li>
</ul>
<h2 id="함수형-인터페이스-타입의-매개변수-반환타입">함수형 인터페이스 타입의 매개변수, 반환타입</h2>
<h3 id="매개변수">매개변수</h3>
<pre><code class="language-java">@FunctionalInterface
interface MyFunction {
    void myMethod() ;
}

...
void aMethod(MyFunction f) {
    f.myMethod();
}
...
Myfunction f = () -&gt; System.out.println(&quot;myMethod()&quot;);
aMethod(f);</code></pre>
<ul>
<li><p>함수형 인터페이스 타입의 매개변수는 매개변수로 람다식을 넣는다. 그럼 이 메서드가 람다식을 호출하는 것이다.</p>
</li>
<li><p><code>aMethod(() -&gt; System.out.println(&quot;myMEthod()&quot;));</code> 한 줄로 줄일 수 있다.</p>
</li>
</ul>
<h3 id="반환타입">반환타입</h3>
<pre><code class="language-java">MyFunction getMyFunction() {
    return () -&gt; System.out.println(&quot;myMethod&quot;);
}</code></pre>
<ul>
<li>함수형 인터페이스 타입의 반환타입은 람다식을 반환하는 것이다.</li>
</ul>
<h2 id="실습-예제">실습 예제</h2>
<pre><code class="language-java">@FunctionalInterface
interface MyFunction {
    void run();  // public abstract void run();
}

public class Ex14_1 {
    static void execute(MyFunction f) {
        f.run();
    }

    static MyFunction getMyFunction() {
        return () -&gt; System.out.println(&quot;f3.run()&quot;);
    }

    public static void main(String[] args) {
        MyFunction f1 = () -&gt; System.out.println(&quot;f1.run()&quot;);

        MyFunction f2 = getMyFunction(); // 함수형 인터페이스 타입의 반환타입

        f1.run();
        f2.run();

        execute(f1); // 함수형 인터페이스 타입의 매개변수
        execute(() -&gt; System.out.println(&quot;run()&quot;));
    }
}</code></pre>
<blockquote>
<p>f1.run()
f2.run()
f1.run()
run()</p>
</blockquote>
<ul>
<li><code>execute(f1);</code> : 매개변수가 함수형 인터페이스 타입인 메서드 <code>excute()</code>는 매개변수로 람다식이 들어간다.</li>
</ul>
<hr>
<h1 id="📌javautilfunction-패키지">📌java.util.function 패키지</h1>
<p><strong>: 자주 사용되는 다양한 함수형 인터페이스를 제공하는 패키지이다.</strong>
-&gt; 람다식을 쓸 때마다 새로운 함수형 인터페이스를 정의하는 것보다 이 패키지 안에 있는 이미 만들어진 함수형 인터페이스를 사용하는 것이 <strong>재사용성 / 유지보수</strong> 측면에서 좋고 <strong>표준화</strong>가 된다는 장점이 있다.</p>
<p><img src="https://velog.velcdn.com/images/jere-gim/post/94c6c229-8517-4f47-9f75-58b1dd374a6e/image.png" alt=""></p>
<h2 id="quiz">Quiz</h2>
<p>빈칸에 들어갈 알맞은 함수형 인터페이스는?</p>
<p>( ① ) f = () -&gt; (int)(Math.random()*100) + 1;
A. ① : <code>Supplier&lt;Integer&gt;</code></p>
<ul>
<li>입력값은 없고 난수만 반환하기 때문에</li>
</ul>
<p>( ② ) f = i -&gt; System.out.print(i + &quot;, &quot;);
A. ② : <code>Consumer&lt;Integer&gt;</code></p>
<ul>
<li>입력값(i)은 있는데 반환값은 없고 그냥 출력만 하기 때문에</li>
</ul>
<p>( ③ ) f = i -&gt; i % 2 == 0;
A. ③ : <code>Predicate&lt;Integer&gt;</code></p>
<ul>
<li><code>i % 2 == 0</code> 이라는 조건식을 반환하기 때문에</li>
</ul>
<p>( ④ ) f = i -&gt; i / 10 * 10;
A. ④ : <code>Function&lt;Integer, Integer&gt;</code></p>
<ul>
<li>입력값과 출력값이 모두 있기 때문에</li>
</ul>
<h2 id="매개변수가-2개인-함수형-인터페이스">매개변수가 2개인 함수형 인터페이스</h2>
<p><img src="https://velog.velcdn.com/images/jere-gim/post/39318e60-e12b-4c31-8bc5-98b253ba00b6/image.png" alt=""></p>
<ul>
<li>BiSupplier는 없다. 함수는 반환값이 2개가 될 수 없기 때문에</li>
</ul>
<h2 id="매개변수의-타입과-반환타입이-일치하는-함수형-인터페이스">매개변수의 타입과 반환타입이 일치하는 함수형 인터페이스</h2>
<p><img src="https://velog.velcdn.com/images/jere-gim/post/0a5e6336-8052-4837-930b-5e92f4b72c13/image.png" alt=""></p>
<pre><code class="language-java">@FunctionalInterface
public interface UnaryOperator&lt;T&gt; extends Function&lt;T, T&gt; {
    static &lt;T&gt; UnaryOperator&lt;T&gt; identity() {
        return t -&gt; t;
    }
}</code></pre>
<ul>
<li><code>identity()</code> : 항등함수라고 한다. 입력값 t를 입력하면 그대로 t가 반환되는 함수이다.</li>
</ul>
<h2 id="실습-예제-1">실습 예제</h2>
<pre><code class="language-java">import java.util.function.*;
import java.util.*;

class Ex14_2 {
    public static void main(String[] args) {
        Supplier&lt;Integer&gt; s = () -&gt; (int) (Math.random() * 100) + 1; // 난수 생성
        Consumer&lt;Integer&gt; c = i -&gt; System.out.print(i + &quot;, &quot;); // 입력값 출력
        Predicate&lt;Integer&gt; p = i -&gt; i % 2 == 0; // 짝수인지 검사
        Function&lt;Integer, Integer&gt; f = i -&gt; i / 10 * 10; // 일의 자리 버림

        List&lt;Integer&gt; list = new ArrayList&lt;&gt;();
        makeRandomList(s, list);
        System.out.println(list);
        printEvenNum(p, c, list);
        List&lt;Integer&gt; newList = doSomething(f, list);
        System.out.println(newList);
    }

    static &lt;T&gt; List&lt;T&gt; doSomething(Function&lt;T, T&gt; f, List&lt;T&gt; list) {
        // list와 크기가 같은 newList를 새로 생성
        List&lt;T&gt; newList = new ArrayList&lt;T&gt;(list.size());
        // Function으로 일의 자리 버림한 수를 newList에 add
        for (T i : list) {
            newList.add(f.apply(i));
        }

        return newList;
    }

    static &lt;T&gt; void printEvenNum(Predicate&lt;T&gt; p, Consumer&lt;T&gt; c, List&lt;T&gt; list) {
        System.out.print(&quot;[&quot;);
        for (T i : list) {
            // Predicate로 짝수인지 검사하고 맞으면
            if (p.test(i))
                // Consumer로 짝수값 출력
                c.accept(i);
        }
        System.out.println(&quot;]&quot;);
    }

    static &lt;T&gt; void makeRandomList(Supplier&lt;T&gt; s, List&lt;T&gt; list) {
        // Supplier를 통해 생성된 난수 10개를 list에 저장
        for (int i = 0; i &lt; 10; i++) {
            list.add(s.get());
        }
    }
}</code></pre>
<ul>
<li><p><code>makeRandomList(s, list);</code> : Supplier를 통해 생성된 난수 10개를 list에 저장</p>
</li>
<li><p><code>printEvenNum(p, c, list);</code> : Predicate로 list의 요소들을 짝수인지 검사하고 짝수가 맞으면 Consumer를 통해 출력</p>
</li>
<li><p><code>List&lt;Integer&gt; newList = doSomething(f, list);</code> : Function을 통해 list의 값들을 일의 자리 버림한 수들을 newList에 저장</p>
</li>
</ul>
<hr>
<h1 id="📌predicate의-결합">📌Predicate의 결합</h1>
<h2 id="and-or-negate로-두-predicate를-하나로-결합default-메서드">and(), or(), negate()로 두 Predicate를 하나로 결합(default 메서드)</h2>
<pre><code class="language-java">Predicate&lt;Integer&gt; p = i -&gt; i &lt; 100;
Predicate&lt;Integer&gt; q = i -&gt; i &lt; 200;
Predicate&lt;Integer&gt; r = i -&gt; i % 2 == 0;</code></pre>
<p>Predicate 결합🔽</p>
<pre><code class="language-java">Predicate&lt;Integer&gt; notP = p.ngeate();
Predicate&lt;Integer&gt; all = notP.and(q).or(r);
Predicate&lt;Integer&gt; all2 = notP.and(q.or(r));</code></pre>
<blockquote>
<p>notP = i &gt;= 100
all = i &gt;= 100 &amp;&amp; i &lt; 200 || i % 2 == 0
all2 = i &gt;= 100 &amp;&amp; (i &lt; 200 || i % 2 == 0)</p>
</blockquote>
<pre><code class="language-java">System.out.println(all.test(2));
System.out.println(all2.test(2));</code></pre>
<blockquote>
<p>true
false</p>
</blockquote>
<p><code>all.test(2)</code></p>
<ul>
<li>2 &gt;= 100 -&gt; false</li>
<li>2 &lt; 200 -&gt; true</li>
<li>2 % 2 == 0 -&gt; true</li>
<li><blockquote>
<p>false &amp;&amp; true || true = false || true = true</p>
</blockquote>
</li>
</ul>
<p><code>all2.test(2)</code></p>
<ul>
<li>2 &gt;= 100 -&gt; false</li>
<li>2 &lt; 200 -&gt; true</li>
<li>2 % 2 == 0 -&gt; true
-&gt; false &amp;&amp; (true || true) = false &amp;&amp; true = false</li>
</ul>
<h2 id="등가비교를-predicate-작성---isequal-static-메서드">등가비교를 Predicate 작성 - isEqual() (static 메서드)</h2>
<pre><code class="language-java">boolean result = Predicate.isEqual(str1).test(str2);</code></pre>
<ul>
<li>str1과 str2 비교</li>
</ul>
<h2 id="andthen---function-결합">andThen() - Function 결합</h2>
<p><strong>2개 이상의 함수를 하나로 연결할 때 사용</strong></p>
<pre><code class="language-java">// 입력값 : String / 반환값 : Integer
Function&lt;String, Integer&gt; f = (s) -&gt; Integer.parseInt(s, 16) 
// 입력값 : Integer / 반환값 : String
Function&lt;Integer, String&gt; g = (i) -&gt; Integer.toBinaryString(i) 

Function&lt;String, String&gt; h = f.andThen(g); // 두 함수를 하나의 함수로 결합

System.out.println(h.apply(&quot;FF&quot;));</code></pre>
<ul>
<li><p><code>Function&lt;String, String&gt; h = f.andThen(g)</code></p>
<ul>
<li><p>h의 입력타입은 f의 입력타입과 같고 h의 반환타입은 g의 반환타입과 같다.</p>
<ul>
<li>f의 반환타입과 g의 입력타입이 같아야 한다.</li>
</ul>
</li>
</ul>
</li>
<li><p><code>h.apply(&quot;FF&quot;)</code>
-&gt; <code>Integer.parseInt(FF, 16)</code> : &quot;FF&quot;를 16진수로 변환 = 255
-&gt; <code>Integer.toBinaryString(255)</code> : 255를 2진수로 변환 = 11111111 출력</p>
</li>
</ul>
<hr>
<h1 id="📌컬렉션-프레임워크와-함수형-인터페이스">📌컬렉션 프레임워크와 함수형 인터페이스</h1>
<p><strong>함수형 인터페이스를 사용하는 컬렉션 프레임워크의 메서드(와일드 카드 생략)</strong></p>
<table>
<thead>
<tr>
<th align="center">Interface</th>
<th align="left"><center>Method</center></th>
<th align="left"><center>설명</center></th>
</tr>
</thead>
<tbody><tr>
<td align="center">Collection</td>
<td align="left"><code>boolean removeIf(Predicate&lt;E&gt; filter)</code></td>
<td align="left">조건에 맞는 요소 삭제</td>
</tr>
<tr>
<td align="center">List</td>
<td align="left"><code>void replaceAll(UnaryOperator&lt;E&gt; operator)</code></td>
<td align="left">List 내 모든 요소 변환하여 대체</td>
</tr>
<tr>
<td align="center">Iterable</td>
<td align="left"><code>void forEach(Consumer&lt;T&gt; action)</code></td>
<td align="left">모든 요소에 작업(action) 수행</td>
</tr>
<tr>
<td align="center">Map</td>
<td align="left"><code>V compute(K key, BiFunction&lt;K,V,V&gt; f)</code></td>
<td align="left">지정된 키의 값에 작업(f) 수행</td>
</tr>
<tr>
<td align="center"></td>
<td align="left"><code>V computeIfAbsent(K key, Function&lt;K,V&gt; f)</code></td>
<td align="left">키가 없으면, 값에 작업(f) 수행 &amp; 추가</td>
</tr>
<tr>
<td align="center"></td>
<td align="left"><code>V computeIfPresent(K key, BiFunction&lt;K,V,V&gt; f)</code></td>
<td align="left">지정 키가 있으면, 값에 작업(f) 수행</td>
</tr>
<tr>
<td align="center"></td>
<td align="left"><code>V merge(K key, V value, BiFunction&lt;V,V,V&gt; f)</code></td>
<td align="left">모든 요소에 병합 작업(f) 수행</td>
</tr>
<tr>
<td align="center"></td>
<td align="left"><code>void forEach(BiConsumer&lt;K,V&gt; action)</code></td>
<td align="left">모든 요소에 작업(action) 수행</td>
</tr>
<tr>
<td align="center"></td>
<td align="left"><code>void replaceAll(BiFunction&lt;K,V,V&gt; f)</code></td>
<td align="left">모든 요소에 치환작업(f) 수행</td>
</tr>
</tbody></table>
<h2 id="실습-예제-2">실습 예제</h2>
<pre><code class="language-java">import java.util.*;

class Ex14_4 {
    public static void main(String[] args) {
        ArrayList&lt;Integer&gt; list = new ArrayList&lt;&gt;();
        for (int i = 0; i &lt; 10; i++)
            list.add(i);

        // list의 모든 요소 출력
        list.forEach(i -&gt; System.out.print(i + &quot;,&quot;));
        System.out.println();

        // list에서 2 또는 3의 배수 제거
        list.removeIf(x -&gt; x % 2 == 0 || x % 3 == 0);
        System.out.println(list);

        // list의 모든 요소에 x10
        list.replaceAll(i -&gt; i * 10);
        System.out.println(list);

        Map&lt;String, String&gt; map = new HashMap&lt;&gt;();
        map.put(&quot;1&quot;, &quot;1&quot;);
        map.put(&quot;2&quot;, &quot;2&quot;);
        map.put(&quot;3&quot;, &quot;3&quot;);
        map.put(&quot;4&quot;, &quot;4&quot;);

        // map의 모든 요소를 {k, v} 형식으로 출력
        map.forEach((k, v) -&gt; System.out.print(&quot;{&quot; + k + &quot;,&quot; + v + &quot;},&quot;));
        System.out.println();
    }
}</code></pre>
<ul>
<li><code>map.forEach((k, v) -&gt; System.out.print(&quot;{&quot; + k + &quot;,&quot; + v + &quot;},&quot;));</code> : iterator() 반복자를 써서 컬렉션을 출력하는 대신 forEach() 메서드를 통해 코드를 간결하게 바꿀 수 있다.</li>
</ul>
<hr>
<h1 id="📌메서드-참조method-reference">📌메서드 참조(method reference)</h1>
<p><strong>: 하나의 메서드만 호출하는 람다식은 &quot;메서드 참조&quot;로 더욱 간단히 할 수 있다.</strong></p>
<table>
<thead>
<tr>
<th align="center">종류</th>
<th align="center">람다식</th>
<th align="center">메서드 참조</th>
</tr>
</thead>
<tbody><tr>
<td align="center">static 메서드 참조</td>
<td align="center">x -&gt; ClassName.method(x)</td>
<td align="center">CalssName::method</td>
</tr>
<tr>
<td align="center">인스턴스 메서드 참조</td>
<td align="center">(obj, x) -&gt; obj.method(x)</td>
<td align="center">CalssName::method</td>
</tr>
</tbody></table>
<h2 id="static-메서드-참조">static 메서드 참조</h2>
<pre><code class="language-java">Integer method(string s) {
    return Integer.parseInt(s);
}</code></pre>
<p>람다식🔽
<code>Function&lt;String, Integer&gt; f = s -&gt; Integer.parseInt(s);</code></p>
<p>메서드 참조🔽
<code>Function&lt;String, Integer&gt; f = Integer::parseInt;</code></p>
<h2 id="생성자-메서드-참조">생성자 메서드 참조</h2>
<p><strong>매개변수가 없는 생성자</strong></p>
<pre><code class="language-java">// 생성자 람다식
Supplier&lt;MyClass&gt; s = () -&gt; new MyClass();</code></pre>
<ul>
<li>매개변수가 없는 생성자는 입력값이 없기 때문에 Supplier로 작성</li>
</ul>
<p>메서드 참조🔽
<code>Supplier&lt;MyClass&gt; s = MyClass::new;</code></p>
<p><strong>매개변수가 있는 생성자</strong></p>
<pre><code class="language-java">Function&lt;Integer, MyClass&gt; f = (i) -&gt; new MyClass(i); //람다식
Function&lt;Integer, MyClass&gt; f = MyClass::new; // 메서드 참조</code></pre>
<ul>
<li>매개변수라는 입력값이 있기 때문에 Function을 사용한다.</li>
</ul>
<p><strong>매개변수가 2개 이상인 생성자</strong></p>
<pre><code class="language-java">// 매개변수가 2개 이상인 생성자
BiFunction&lt;Integer, String, MyClass&gt; bf = (i, s) -&gt; new MyClass(i, s); // 람다식
BiFunction&lt;Integer, String, MyClass&gt; bf = MyClass::new; // 메서드 참조</code></pre>
<ul>
<li>BiFunction 사용</li>
</ul>
<h2 id="배열-메서드-참조">배열 메서드 참조</h2>
<pre><code class="language-java">Function&lt;Integer, int[]&gt; arrF = x -&gt; new int[x]; // 람다식
Function&lt;Integer, int[]&gt; arrF = int[]::new; // 메서드 참조 

// String 배열
Function&lt;Integer, String[]&gt; arrF2 = String[]::new ;</code></pre>
<ul>
<li>배열을 생성할 땐 배열의 크기를 정해줘야 하므로 입력값이 있다. 따라서 Function을 이용한다.</li>
</ul>
<p><strong>어떤 함수형 인터페이스를 사용할지 모를땐 입력값과 반환값의 유무를 잘 생각해보면 된다.</strong></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[자바의 정석 기초편] 쓰레드 2]]></title>
            <link>https://velog.io/@jere-gim/%EC%9E%90%EB%B0%94%EC%9D%98-%EC%A0%95%EC%84%9D-%EA%B8%B0%EC%B4%88%ED%8E%B8-%EC%93%B0%EB%A0%88%EB%93%9C-2</link>
            <guid>https://velog.io/@jere-gim/%EC%9E%90%EB%B0%94%EC%9D%98-%EC%A0%95%EC%84%9D-%EA%B8%B0%EC%B4%88%ED%8E%B8-%EC%93%B0%EB%A0%88%EB%93%9C-2</guid>
            <pubDate>Mon, 13 Mar 2023 13:58:29 GMT</pubDate>
            <description><![CDATA[<h1 id="📌쓰레드의-실행제어">📌쓰레드의 실행제어</h1>
<p><strong>쓰레드의 실행을 제어할 수 있는 메서드들</strong></p>
<table>
<thead>
<tr>
<th align="left"><center>메서드</center></th>
<th align="left"><center>설명</center></th>
</tr>
</thead>
<tbody><tr>
<td align="left"><code>static void sleep(long millis)</code><br><code>static void sleep(long millis, int nanos)</code></td>
<td align="left">지정된 시간(milli second 단위) 동안 쓰레드 일시 정지<br>시간지나면 자동적으로 다시 실행 대기 상태 전환</td>
</tr>
<tr>
<td align="left"><code>void join()</code><br><code>void join(long millis)</code><br><code>void join(long millis, int nanos)</code></td>
<td align="left">지정된 시간(milli second 단위) 동안 쓰레드 독점실행<br>시간 경과되거나 작업이 종료되면 <code>join()</code>을 호출했던 쓰레드로 다시 돌아와 실행을 계속한다.</td>
</tr>
<tr>
<td align="left"><code>void interrupt()</code></td>
<td align="left">깨우는 것 -&gt; sleep() 이나 join()으로 일시정지 상태인 쓰레드를 실행 대기 상태로 변환<br>(해당 쓰레드 : InterruptedException 발생 → 일시정지상태 탈출)</td>
</tr>
<tr>
<td align="left"><code>void stop()</code></td>
<td align="left">쓰레드를 즉시 종료시킨다.</td>
</tr>
<tr>
<td align="left"><code>void suspend()</code></td>
<td align="left">쓰레드를 일시정지시킨다.</td>
</tr>
<tr>
<td align="left"><code>void resume()</code></td>
<td align="left">suspend()에 의해 일시정지 상태에 있는 쓰레드를 실행대기 상태로 만든다.</td>
</tr>
<tr>
<td align="left"><code>static void yield()</code></td>
<td align="left">실행 중에 자신에게 주어진 실행시간을 다른 쓰레드에게 양보하고 자신은 실행대기 상태가 된다.</td>
</tr>
</tbody></table>
<ul>
<li><code>static</code> 메서드인 <code>sleep()</code>과 <code>yield()</code>는 다른 쓰레드에게 적용할 수 없고  쓰레드 자기 자신에게만 호출이 가능하다.</li>
</ul>
<h2 id="sleep">sleep()</h2>
<p><strong>: 현재 쓰레드를 지정된 시간동안 멈추게 한다.</strong></p>
<p><strong>예외처리를 해야 한다.(InterruptedException이 발생하면 깨어남)</strong></p>
<pre><code class="language-java">try{
    Thread.sleep(5000); // 5초동안 멈춤
} catch(InterruptedException e) {}</code></pre>
<ul>
<li><p><code>InterruptedException</code>는 Exception의 자손이기 때문에 예외처리가 필수이다.</p>
</li>
<li><p><code>sleep()</code>을 깨우는 방법은 2가지이다. </p>
<ol>
<li><code>time-out</code> : 지정된 시간이 지나서 try-catch문을 정상적으로 빠져나온다.</li>
<li><code>interrupt()</code> : 이 메서드를 호출하면 <code>InterruptedException</code> 예외가 발생하게 되고 이로 인해 try-catch문을 중간에 빠져나오게 된다.
어떤 문제가 있어서 예외처리를 하는 것이 아니라 잠자는 상태를 중간에 벗어나기 위해 try-catch문을 이용한 것이다. 따라서 catch문 안에 아무것도 쓰지 않는다.</li>
</ol>
</li>
</ul>
<p><strong>매번 try-catch문을 작성하기 불편하기 때문에 보통 메서드를 따로 만들어서 사용한다.</strong></p>
<pre><code class="language-java">void delay(long millis) {
    try{
        Thread.sleep(millis);
    } catch(InterruptedException e) {}
}</code></pre>
<blockquote>
<p>delay(5000); // 5초동안 멈춤</p>
</blockquote>
<p><strong>특정 쓰레드를 지정해서 멈추게 하는 것은 불가능하다.</strong></p>
<pre><code class="language-java">//main쓰레드에서 실행
try {
    th1.sleep(2000); // 에러는 발생하지 않음
} catch (InterruptedException e) {}</code></pre>
<ul>
<li><code>th1.sleep(2000);</code> : 에러는 발생하지 않지만 main 쓰레드에서 실행했기 때문에 멈춘건 th1 쓰레드가 아니라 main 쓰레드이다.</li>
</ul>
<p>올바른 작성법🔽</p>
<pre><code class="language-java">try {
    Thread.sleep(2000);
} catch (InterruptedException e) {}</code></pre>
<ul>
<li><code>Thread.sleep(2000);</code> : 이렇게 작성해야 오해의 여지가 없고 실수도 하지 않게 된다.</li>
</ul>
<h2 id="interrupt">interrupt()</h2>
<p><strong>: 대기 상태(WAITING)인 쓰레드를 실행 대기 상태(RUNNABLE)로 만든다.</strong></p>
<h3 id="interrupt-관련-메서드">interrupt() 관련 메서드</h3>
<table>
<thead>
<tr>
<th align="left"><center>메서드</center></th>
<th align="left"><center>설명</center></th>
</tr>
</thead>
<tbody><tr>
<td align="left"><code>void interrupt()</code></td>
<td align="left">쓰레드의 interrupted 상태를 false에서 true로 변경</td>
</tr>
<tr>
<td align="left"><code>boolean isInterrupted()</code></td>
<td align="left">쓰레드의 interrupted 상태를 반환</td>
</tr>
<tr>
<td align="left"><code>static boolean interrupted()</code></td>
<td align="left">현재 쓰레드의 interrupted 상태를 알려주고, false로 초기화</td>
</tr>
</tbody></table>
<ul>
<li><p><code>static boolean interrupted()</code>은 상태를 반환하고 false로 초기화한다. <code>interrupt()</code> 메서드를 호출하면 interrupted 상태를 true로 변경하는데 false에서 true가 되야 interrupted가 됐다는걸 알 수 있다. 
false로 초기화하지 않고 또 호출하게 되면 true -&gt; true가 되므로 누가 호출했는지, 제대로 호출됐는지를 알 수 없게 된다.</p>
</li>
<li><p><code>boolean isInterrupted()</code> 메서드는 그저 interrupted 상태를 반환한다.</p>
<pre><code class="language-java">class DownloadThread extends Thread {
  public void run() {
      ...
      while (downloaded &amp;&amp; !isInterrupted()) {
          /*download 작업을 수행*/
          ...
      }

      System.out.println(&quot;다운로드가 끝났습니다.&quot;);
  }
}</code></pre>
</li>
<li><p><code>while (downloaded &amp;&amp; !isInterrupted())</code></p>
<ul>
<li><code>downloaded</code> 다운로드가 완료되서 while 문을 빠져나갈수도 있고<br><code>!isInterrupted()</code> 다운로드 도중 취소 버튼을 누르면 interrupted 상태가 true가 되고 <code>!isInterrupted() == false</code>가 되면서 while 문을 빠져나갈 수도 있다.</li>
</ul>
</li>
</ul>
<h3 id="실습-예제">실습 예제</h3>
<pre><code class="language-java">import javax.swing.JOptionPane;

class Ex13_9 {
    public static void main(String[] args) throws Exception {
        ThreadEx9_1 th1 = new ThreadEx9_1();
        th1.start();

        String input = JOptionPane.showInputDialog(&quot;아무 값이나 입력하세요.&quot;);
        System.out.println(&quot;입력하신 값은 &quot; + input + &quot; 입니다.&quot;);
        th1.interrupt();  // interrupted 상태를 true로 변경
        System.out.println(&quot;isInterrupted():&quot;+ th1.isInterrupted()); // true
        System.out.println(&quot;isInterrupted():&quot;+ th1.isInterrupted()); // true
        System.out.println(&quot;Interrupted():&quot;+ th1.interrupted());
        System.out.println(&quot;Interrupted():&quot;+ th1.interrupted()); 
    }
}

class ThreadEx9_1 extends Thread {
    public void run() {
        int i = 5;

        while(i!=0 &amp;&amp; !isInterrupted()) {
            System.out.println(i--);
            for(long x=0;x&lt;2500000000L;x++); // 시간 지연
        }
        System.out.println(&quot;카운트가 종료되었습니다.&quot;);
    }
}</code></pre>
<blockquote>
<p>5
4
3
2
입력하신 값은 apple 입니다.
isInterrupted():true
isInterrupted():true
Interrupted():false
Interrupted():false
카운트가 종료되었습니다.</p>
</blockquote>
<ul>
<li>카운트다운을 하다가 값을 입력하면 <code>th1.interrupt()</code> 메서드를 호출해서 카운트다운을 종료하는 프로그램이다.</li>
</ul>
<pre><code class="language-java">System.out.println(&quot;isInterrupted():&quot;+ th1.isInterrupted()); // true
System.out.println(&quot;isInterrupted():&quot;+ th1.isInterrupted()); // true</code></pre>
<ul>
<li><code>th1.isInterrupted()</code> 메서드는 그저 interrupted 상태를 반환하기 때문에 몇번을 호출해도 true가 나온다.</li>
</ul>
<pre><code class="language-java">System.out.println(&quot;Interrupted():&quot;+ th1.interrupted());
System.out.println(&quot;Interrupted():&quot;+ th1.interrupted()); </code></pre>
<blockquote>
<p>Interrupted():false
Interrupted():false</p>
</blockquote>
<ul>
<li><code>th1.interrupted()</code> 메서드는 interrupted 상태를 반환하고 false로 초기화시킨다. 예상한 결과는 true, false가 나와야 하는데 실제 결과는 두 번 모두 false가 나왔다. 왜 그런걸까 ?</li>
<li><blockquote>
<p>이유는 <code>interrupted()</code> 는 static 메서드이기 때문에 현재 쓰레드를 반환한다. 즉, <code>th1.interrupted()</code>는 main쓰레드의 상태를 반환하는 것이다. 따라서 <code>ThreadEx9_1</code> 쓰레드 안에서 호출을 해줘야 한다.</p>
</blockquote>
</li>
</ul>
<p>수정된 코드🔽</p>
<pre><code class="language-java">import javax.swing.JOptionPane;

class Ex13_9 {
    public static void main(String[] args) throws Exception {
        ThreadEx9_1 th1 = new ThreadEx9_1();
        th1.start();

        String input = JOptionPane.showInputDialog(&quot;아무 값이나 입력하세요.&quot;);
        System.out.println(&quot;입력하신 값은 &quot; + input + &quot; 입니다.&quot;);
        th1.interrupt();  // interrupted 상태를 true로 변경
    }
}

class ThreadEx9_1 extends Thread {
    public void run() {
        int i = 5;

        while(i!=0 &amp;&amp; !isInterrupted()) {
            System.out.println(i--);
            for(long x=0;x&lt;2500000000L;x++); // 시간 지연
        }
        System.out.println(&quot;isInterrupted():&quot;+ this.isInterrupted()); // true
        System.out.println(&quot;isInterrupted():&quot;+ this.isInterrupted()); // true
        System.out.println(&quot;Interrupted():&quot;+ Thread.interrupted());
        System.out.println(&quot;Interrupted():&quot;+ Thread.interrupted()); 
        System.out.println(&quot;카운트가 종료되었습니다.&quot;);
    }
}</code></pre>
<blockquote>
<p>5
4
입력하신 값은 bbbb 입니다.
isInterrupted():true
isInterrupted():true
Interrupted():true
Interrupted():false
카운트가 종료되었습니다.</p>
</blockquote>
<ul>
<li><p><code>this.isInterrupted()</code>는 static 메서드가 아니기 때문에 main 쓰레드에서 <code>th1.isInterrupted()</code> 로 작성해도 문제가 없지만 해당 쓰레드안에서 작성할 때 <code>this(생략가능)</code> 붙여도 된다는걸 보여주기 위해 수정하였다.</p>
</li>
<li><p><code>Thread.interrupted()</code> : 이렇게 작성해야 예상했던대로 결과가 올바르게 출력된다.<br>첫 번째 <code>Thread.interrupted()</code>는 true를 반환하고 false로 초기화<br>두 번째 <code>Thread.interrupted()</code>는 초기화한 false를 반환</p>
</li>
</ul>
<h2 id="suspend-resume-stop">suspend(), resume(), stop()</h2>
<p><strong>쓰레드의 실행을 일시정지, 재개, 완정정지 시킨다.</strong>
-&gt; 이 3개의 메서드는 교착상태(dead-lock)에 빠지기 쉬워서 deprecated 되었다. </p>
<pre><code class="language-java">class Ex13_10 {
    public static void main(String[] args) {
        RunImplEx10 th1 = new RunImplEx10(&quot;*&quot;);
        RunImplEx10 th2 = new RunImplEx10(&quot;**&quot;);
        RunImplEx10 th3 = new RunImplEx10(&quot;***&quot;);
        th1.start();
        th2.start();
        th3.start();

        try {
            Thread.sleep(2000);
            th1.suspend();
            Thread.sleep(2000);
            th2.suspend();
            Thread.sleep(3000);
            th1.resume();
            Thread.sleep(3000);
            th1.stop();
            th2.stop();
            Thread.sleep(2000);
            th3.stop();
        } catch (InterruptedException e) {}
    } // main
}

class RunImplEx10 implements Runnable {
    volatile boolean suspended = false;
    volatile boolean stopped = false;

    Thread thread;

    public RunImplEx10(String name) {
        thread = new Thread(this, name);
    }

    public void start() {
        thread.start();
    }

    public void stop() {
        stopped = true;
    }

    public void suspend() {
        suspended = true;
    }

    public void resume() {
        suspended = false;
    }

    public void run() {
        while (!stopped) {
            if (!suspended) {
               System.out.println(Thread.currentThread().getName());
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                }
            }
        }
    } // run()
}</code></pre>
<pre><code class="language-java">class RunImplEx10 implements Runnable {
    volatile boolean suspended = false;
    volatile boolean stopped = false;

    Thread thread;

    public RunImplEx10(String name) {
        thread = new Thread(this, name);
    }
    ...</code></pre>
<ul>
<li><p>Runnable을 구현한 클래스 안에 Thread을 선언하고 생성자를 통해서 Thread 객체를 생성할 수 있다. 그럼 main메서드에서는 <code>RunImplEx10 th1 = new RunImplEx10(&quot;*&quot;);</code> 이렇게 생성해주면 된다.</p>
</li>
<li><p><code>volatile boolean suspended = false;</code> : volatile을 붙이는 이유는 자주 바뀌는 변수이기 때문이다. 멀티 쓰래드 어플리케이션에서 각 쓰래드들은 성능적이 이유로 메인 메모리로 부터 변수를 읽어 CPU 캐시에 복사하고 작업한다.
이때 같은 변수에 대해 복사본을 가지고 각 쓰레드들이 작업을 하게 되면 동기화가 안되는 문제가 발생할 수 있다. 이를 방지하기 위해 volatile 키워드를 붙이게 되면 변수를 CPU 캐시에 복사하는게 아니라 메인 메모리에서 직접 변수를 가져와서 작업을 한다.</p>
</li>
<li><p>suspended 변수와 stopped 변수를 선언하고 <code>suspended()</code>, <code>start()</code>, <code>resume()</code>, <code>stop()</code> 메서드를 만든 후 run() 메서드 안에 <code>while (!stopped)</code>, <code>if (!suspended)</code> 두 조건문을 붙여주면 deprecated 경고가 안뜬다.</p>
</li>
</ul>
<h2 id="join">join()</h2>
<p><strong>: 지정된 시간동안 특정 쓰레드가 작업하는 것을 기다린다.</strong>
<code>void join()</code> : 작업이 모두 끝날 때까지 기다린다.
<code>void join(long millis)</code> : millis 시간동안 기다린다. (단위 천분의 일초)
<code>void join(long millis, int nanos)</code> : 천분의 일초 + 나노초 동안 기다린다.</p>
<p><strong>예외처리를 해야한다.(InterruptedException이 발생하면 작업 재개)</strong></p>
<ul>
<li><p><code>sleep()</code> 메서드와 똑같이 해당 작업을 중간에 빠져나오기 위해 <code>InterruptedException</code>을 이용하는 것이다.</p>
<pre><code class="language-java">class Ex13_11 {
static long startTime = 0;

public static void main(String[] args) {
    ThreadEx11_1 th1 = new ThreadEx11_1();
    ThreadEx11_2 th2 = new ThreadEx11_2();
    th1.start();
    th2.start();
    startTime = System.currentTimeMillis();

    try {
        th1.join(); // main쓰레드가 th1의 작업이 종료될 때까지 기다린다.
        th2.join(); // main쓰레드가 th2의 작업이 종료될 때까지 기다린다.
    } catch (InterruptedException e) {
    }

    System.out.print(&quot;소요시간 : &quot; + (System.currentTimeMillis() - Ex13_11.startTime));
} // main
}
</code></pre>
</li>
</ul>
<p>class ThreadEx11_1 extends Thread {
    public void run() {
        for (int i = 0; i &lt; 300; i++) {
            System.out.print(&quot;-&quot;);
        }
    } // run()
}</p>
<p>class ThreadEx11_2 extends Thread {
    public void run() {
        for (int i = 0; i &lt; 300; i++) {
            System.out.print(&quot;|&quot;);
        }
    } // run()
}</p>
<pre><code>&gt;--|||||----------------------|||||||||------||||||||||||||||||||||||||||||||||||||||||||---------|||||||||||||||||||||||||||||||||||||||||||||||||||---------------|||||||||||||||||||||||||||||||||----------------------------------------|||||||--------------------------------------------------------------------------------------------------|||-----------------------|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||----|||||||||---------------------------------------------------------------------------------소요시간 : 14

- th1과 th2쓰레드가 모두 작업을 마칠 때까지 main쓰레드를 기다리게 한 뒤 소요시간을 출력한다. 

- `join()`을 주석처리하게 되면 main쓰레드는 th1과 th2.start()를 호출하고 소요시간 출력 후 종료되기 때문에 소요시간이 0이 찍힌다.

## yield()
**남은 시간을 다음 쓰레드에게 양보하고, 자신(현재 쓰레드)은 실행대기 상태가 된다.**

**yield()와 interrupt()를 적절히 사용하면, 응답성과 효율을 높일 수 있다.**
```java
public void run() {
    while (!stopped) {
        if (!suspended) {
           System.out.println(Thread.currentThread().getName());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
            }
        } else {
        Thread.yield();
        }
    }
} // run()
...

public void stop() {
    stopped = true;
    thread.interrupt();
}

public void suspend() {
    suspended = true;
    thread.interrupt();
}</code></pre><ul>
<li><p><code>if (!suspended)</code> -&gt; false 이고 <code>while (!stopped)</code> -&gt; true라면 쓰레드가 종료되지 않고 일시정지된 상태에서 while문만 계속 돌고 있는 상태가 된다. 이런 것을 busy-waint 상태라고 하고 이를 방지하기 위해 <code>else { Thread.yield(); }</code> 추가해줄 수 있다.</p>
</li>
<li><p><code>stop()</code>, <code>suspend()</code>에 <code>interrupt()</code>를 추가해 응답성을 높일 수 있다. 보통 쓰레드를 만들 때 특정 쓰레드가 작업을 독점하지 않도록 sleep() 메서드를 넣어준다.
그런데 만약 일시정지를 했을때 sleep() 메서드로 인해 쓰레드가 잠을 자고 있었다면 <code>interrupt()</code> 메서드로 그 즉시 sleep()을 빠져나가게 할 수 있도록 해야 한다.
이를 통해, 응답성을 높일수 있다.</p>
</li>
</ul>
<p><strong>yield() 메서드도 static 메서드이기 때문에 자기 자신한테만 사용할 수 있다.</strong></p>
<p><strong>하지만 yield() 또한 OS 스케쥴러에게 희망사항을 전달하는 것 뿐이다. 반드시 동작한다고 볼 수는 없다. 결국 OS 스케쥴러가 전체 프로그램들의 작업 효율을 따져 시간 배분을 한다.</strong></p>
<hr>
<h1 id="📌쓰레드의-동기화synchronization">📌쓰레드의 동기화(synchronization)</h1>
<p><strong>: 진행중인 작업이 다른 쓰레드에게 간섭하지 못하게 막는 것을 &quot;동기화&quot;라고 한다.</strong></p>
<p><strong>멀티 쓰레드 프로세스에서는 다른 쓰레드의 작업에 영향을 미칠 수 있다.</strong></p>
<p><strong>동기화하려면 간섭 받지 않아야 하는 문장들을 &quot;임계 영역(critical section)&quot;으로 설정한다.</strong></p>
<p><strong>임계 영역은 락(lock)을 얻은 단 하나의 쓰레드만 출입이 가능하다.(객체 1개마다 락 1개만 보유)</strong></p>
<h2 id="synchronized를-이용한-동기화">synchronized를 이용한 동기화</h2>
<p><strong>synchronized로 임계 영역을 설정하는 2가지 방법</strong></p>
<ol>
<li><p>메서드 전체를 임계 영역으로 지정</p>
<pre><code class="language-java">public synchronized void withdraw(int money) {
 if (balance &gt;= money) {
     try {
         Thread.sleep(1000);
     } catch (Exception e) {}

     balance -= money;
 }
}</code></pre>
</li>
<li><p>특정한 영역을 임계 영역으로 지정</p>
<pre><code class="language-java">public void withdraw(int money) {
 synchronized(this) {
     if (balance &gt;= money) {
         try {
             Thread.sleep(1000);
         } catch (Exception e) {}

         balance -= money;
     }
 } // synchronized(this)
}</code></pre>
</li>
</ol>
<ul>
<li>임계 영역은 개수도 최소화하고 영역도 최소화하는게 좋다. 임계 영역에는 1번에 1개의 쓰레드만 접근할 수 있기 때문에 너무 많이, 넓게 설정한다면 프로그램의 성능이 저하된다.</li>
</ul>
<h2 id="실습-예제-1">실습 예제</h2>
<pre><code class="language-java">class Ex13_12 {
    public static void main(String[] args) {
        Runnable r = new RunnableEx12();
        new Thread(r).start();
        new Thread(r).start();
    }
}

class Account {
    private int balance = 500;

    public int getBalance() {
        return balance;
    }

    // 잔고(balance)에서 돈을 출금하는 메서드
    public void withdraw(int money) {
        if (balance &gt;= money) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {}
            balance -= money;
        }
    } // withdraw
}

class RunnableEx12 implements Runnable {
    Account acc = new Account();

    public void run() {
        while (acc.getBalance() &gt; 0) {
            int money = (int) (Math.random() * 3 + 1) * 100; // 100, 200, 300 중 랜덤한 money 생성
            acc.withdraw(money);
            System.out.println(&quot;balance:&quot; + acc.getBalance());
        }
    } // run()
}</code></pre>
<blockquote>
<p>balance:400
balance:400
balance:300
balance:100
balance:0
balance:-100</p>
</blockquote>
<p><code>withdraw()</code> 메서드를 살펴보면 <code>if (balance &gt;= money)</code> 이런 조건일 때만 메서드가 동작하게 되어있다. 즉, 잔고(balance)가 출금하려는 돈(money)보다 많거나 같을 때만 출금할 수 있는 것이다. 따라서 잔고는 -가 출력이 될 수 없는데 출력을 보면 -100이 출력되었다. 왜 그럴까?
-&gt; 이유는 두 쓰레드가 동시에 <code>withdraw()</code> 접근했기 때문이다. 
예를 들어, 한 쓰레드가 잔고가 100일 때 <code>withdraw()</code>에 접근했다. 잔고가 남아있기 때문에 if 조건문을 통과하고 잔고에서 돈을 빼는 작업을 수행하려고 할 때 다른 쓰레드가 거의 동시에 <code>withdraw()</code> 접근하게 되면 이 쓰레드 또한 아직 잔고는 100이기 때문에 if 조건문을 통과하게 된다. 
여기서 문제가 발생한다. 먼저 접근한 쓰레드가 돈을 빼가서 잔고가 0이 되었음에도 불구하고 나중에 접근한 쓰레드는 if 조건문을 이미 통과했기 때문에 0인 잔고에서 또 돈을 빼가는 상황이 벌어지고 결과적으로 <code>balance:-100</code>이 찍히게 되는 것이다.</p>
<p><strong>이러한 문제를 해결하기 위해 나온것이 &quot;동기화&quot;이다.</strong></p>
<p><strong>동기화를 통해 문제를 해결한 소스 코드🔽</strong></p>
<pre><code class="language-java">...
class Account {
    private int balance = 500; // private으로 설정해야 동기화가 의미가 있다.

    // 잔고를 확인하는 중간에 출금이 되면 안되니까 여기도 동기화를 설정해줘야 한다.
    public synchronized int getBalance() {
        return balance;
    }

    // 돈을 출금할 때는 한 쓰레드만 접근할 수 있도록 동기화 설정
    public synchronized void withdraw(int money) {
        if (balance &gt;= money) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {}
            balance -= money;
        }
    } // withdraw
}
...</code></pre>
<ul>
<li>잔고(balance)는 <code>private</code>으로 설정해줘야 동기화하는 의미가 있다. 출금하는 메서드를 동기화해줘도 잔고 자체에 다른 쓰레드가 직접 접근을 해서 변경하면 동기화해주는 의미가 없기 떄문이다.</li>
</ul>
<ul>
<li><code>withdraw()</code>에 동기화를 해주는 것은 물론 <code>getBalance()</code> 또한 동기화를 해주어야 한다. <code>getBalance()</code>를 통해 잔고를 확인하는 동안에 출금하는 메서드가 수행되어 잔고가 바껴서는 안되기 때문이다.
잔고를 읽고 쓰는 메서드에는 모두 동기화해줘야 한다.</li>
</ul>
<hr>
<h1 id="📌wait과-notify">📌wait()과 notify()</h1>
<p><strong>동기화를 하게 되면 한번에 한 쓰레드만 접근할 수 있기 때문에 프로그램의 효율이 떨어진다.
동기화의 효율을 높이기 위해 wait(), notify()를 사용한다.</strong></p>
<h2 id="실습-예제-2">실습 예제</h2>
<pre><code class="language-java">import java.util.ArrayList;

class Customer2 implements Runnable {
    private Table2 table;
    private String food;

    Customer2(Table2 table, String food) {
        this.table = table;
        this.food = food;
    }

    public void run() {
        while (true) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
            }
            String name = Thread.currentThread().getName();

            table.remove(food);
            System.out.println(name + &quot; ate a &quot; + food);
        } // while
    }
}

class Cook2 implements Runnable {
    private Table2 table;

    Cook2(Table2 table) {
        this.table = table;
    }

    public void run() {
        while (true) {
            int idx = (int) (Math.random() * table.dishNum());
            table.add(table.dishNames[idx]);
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
            }
        } // while
    }
}

class Table2 {
    String[] dishNames = {&quot;donut&quot;, &quot;donut&quot;, &quot;burger&quot;};
    final int MAX_FOOD = 6;
    private ArrayList&lt;String&gt; dishes = new ArrayList&lt;&gt;();

    public synchronized void add(String dish) {
        // while문 : 테이블에 음식이 가득찼으면 COOK 쓰레드 wait()
        while (dishes.size() &gt;= MAX_FOOD) {
            String name = Thread.currentThread().getName();
            System.out.println(name + &quot; is waiting.&quot;);
            try {
                wait();
                Thread.sleep(500);
            } catch (InterruptedException e) {}
        }
        dishes.add(dish);
        // 음식을 추가했으니 waiting pool에서 대기중인 CUST 쓰레드 notify()
        notify();
        System.out.println(&quot;Dishes:&quot; + dishes.toString());
    }

    public void remove(String dishName) {
        synchronized (this) {
            String name = Thread.currentThread().getName();

            while (dishes.size() == 0) {
                System.out.println(name + &quot; is waiting.&quot;);
                try {
                    // 테이블에 음식이 없으니 CUST 쓰레드 wait()
                    wait();
                    Thread.sleep(500);
                } catch (InterruptedException e) {}
            }

            while (true) {
                for (int i = 0; i &lt; dishes.size(); i++) {
                    if (dishName.equals(dishes.get(i))) {
                        dishes.remove(i);
                        // 음식을 하나 먹었으니 테이블이 꽉차서 waiting pool에 있던 COOK 쓰레드 notify()
                        notify();
                        return;
                    }
                }

                try {
                    System.out.println(name + &quot; is waiting.&quot;);
                    // 위의 for 문에서 자기가 원하는 음식을 찾지 못한 CUST 쓰레드 wait()
                    wait();
                    Thread.sleep(500);
                } catch (InterruptedException e) {}
            } // while(true)
        } // synchronized
    }

    public int dishNum() {
        return dishNames.length;
    }
}

class Ex13_15 {
    public static void main(String[] args) throws Exception {
        Table2 table = new Table2();

        // 요리사 쓰레드
        new Thread(new Cook2(table), &quot;COOK&quot;).start();
        // donut만 먹는 CUST1 쓰레드, burger만 먹는 CUST2 쓰레드
        new Thread(new Customer2(table, &quot;donut&quot;), &quot;CUST1&quot;).start();
        new Thread(new Customer2(table, &quot;burger&quot;), &quot;CUST2&quot;).start();
        Thread.sleep(2000);
        System.exit(0);
    }
}</code></pre>
<ul>
<li>총 4개의 쓰레드가 있다.<ul>
<li>2초 일시정지했다가 프로그램을 종료해버리는 <code>main 쓰레드</code></li>
<li>donut, burger 2가지 음식을 만드는 <code>COOK 쓰레드</code></li>
<li>donut만 먹는 <code>CUST1 쓰레드</code></li>
<li>burger만 먹는 <code>CUST2 쓰레드</code></li>
</ul>
</li>
</ul>
<ul>
<li>Table 클래스에는 음식을 만드는 <code>add()</code>와 음식을 먹는(제거하는) <code>remove()</code>가 있다. 이 두 메서드는 여러 쓰레드가 동시에 접근하면 안되기 때문에 동기화를 해주었다.</li>
</ul>
<p><strong>wait()</strong></p>
<pre><code class="language-java">public synchronized void add(String dish) {
    // while문 : 테이블에 음식이 가득찼으면 COOK 쓰레드 wait()
    while (dishes.size() &gt;= MAX_FOOD) {
        String name = Thread.currentThread().getName();
        System.out.println(name + &quot; is waiting.&quot;);
       try {
            wait();
            Thread.sleep(500);
        } catch (InterruptedException e) {}
    }
    ...</code></pre>
<ul>
<li>COOK 쓰레드는 Table 객체에 <code>dishes</code>라는 ArrayList에 <code>add()</code>를 통해서 음식을 추가한다. 
이때 <code>dishes.size()</code>가 <code>MAX_FOOD</code> 이상이 되면 <code>wait()</code> 을 통해 락을 풀고 waiting pool에서 음식이 제거될 때까지 대기한다.</li>
</ul>
<pre><code class="language-java">public void remove(String dishName) {
    synchronized (this) {
        String name = Thread.currentThread().getName();

        while (dishes.size() == 0) {
            System.out.println(name + &quot; is waiting.&quot;);
            try {
                // 테이블에 음식이 없으니 CUST 쓰레드 wait()
                wait();
                Thread.sleep(500);
            } catch (InterruptedException e) {}
        }
        ...</code></pre>
<ul>
<li>CUST 쓰레드는 <code>remove()</code>로 음식을 먹는다. 만약, <code>dishes.size()</code>가 0이라면 음식이 없다는 뜻이고 이때 CUST 쓰레드는 <code>wait()</code> 을 통해 락을 풀고 waiting pool에서 음식이 추가될 때까지 대기한다.</li>
</ul>
<pre><code class="language-java">public void remove(String dishName) {
    synchronized (this) {
        String name = Thread.currentThread().getName();
        while (dishes.size() == 0) {
            ...
       }

       while (true) {
           for (int i = 0; i &lt; dishes.size(); i++) {
               if (dishName.equals(dishes.get(i))) {
                   dishes.remove(i);
                   // 음식을 하나 먹었으니 테이블이 꽉차서 waiting pool에 있던 COOK 쓰레드 notify()
                   notify();
                   return;
               }
           }

           try {
               System.out.println(name + &quot; is waiting.&quot;);
               // 위의 for 문에서 자기가 원하는 음식을 찾지 못한 CUST 쓰레드 wait()
               wait();
               Thread.sleep(500);
           } catch (InterruptedException e) {}
       } // while(true)
   } // synchronized
}</code></pre>
<ul>
<li><code>remove()</code>의 두 번째 while 문에서 CUST 쓰레드는 음식을 먹는 작업을 수행한다.(두 번째 while 문안의 for 문)
CUST1 쓰레드는 도넛만 먹고 CUST2 쓰레드는 버거만 먹는데 <code>dishes</code>에 원하는 음식이 없다면 for 문을 통과하게 되고 이때 자기가 원하는 음식이 나올 때까지 <code>wait()</code> 을 통해 락을 풀고 waiting pool에서 대기한다.</li>
</ul>
<p><strong>notify()</strong></p>
<pre><code class="language-java">public synchronized void add(String dish) {
    ...
    dishes.add(dish);
        // 음식을 추가했으니 waiting pool에서 대기중인 CUST 쓰레드 notify()
    notify();
    System.out.println(&quot;Dishes:&quot; + dishes.toString());
}</code></pre>
<ul>
<li>음식이 추가됐으니 아까 <code>dishes.size()</code>가 0이어서 wait() 했던 CUST 쓰레드를 <code>notify()</code>를 통해 깨운다.</li>
</ul>
<pre><code class="language-java">public void remove(String dishName) {
    synchronized (this) {
        String name = Thread.currentThread().getName();
        while (dishes.size() == 0) {
            ...
       }

       while (true) {
           for (int i = 0; i &lt; dishes.size(); i++) {
               if (dishName.equals(dishes.get(i))) {
                   dishes.remove(i);
                   // 음식을 하나 먹었으니 테이블이 꽉차서 waiting pool에 있던 COOK 쓰레드 notify()
                   notify();
                   return;
               }
            }
            ...
       } // while(true)
   } // synchronized
}</code></pre>
<ul>
<li>두 번째 while 문안에서 for 문을 통해 음식을 하나 먹었으니 아까 <code>dishes.size()</code>가 꽉차서 <code>wait()</code>로 대기중인 COOK 쓰레드를 <code>notify()</code>를 통해 깨운다.</li>
</ul>
<p><strong>예제에서도 알 수 있듯이 <code>wait()</code>, <code>notify()</code> 어떤 쓰레드를 대기시키고 깨우는지 알 수 없다. 이를 보완하기 위해 <code>Lock</code> 인터페이스와 <code>Condition</code> 인터페이스가 나왔다.이 두 인터페이스를 이용하면 쓰레들을 구분해서 작업할 수 있다.</strong></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Section 1] 기술 면접 준비]]></title>
            <link>https://velog.io/@jere-gim/Section-1-%EA%B8%B0%EC%88%A0-%EB%A9%B4%EC%A0%91-%EC%A4%80%EB%B9%84</link>
            <guid>https://velog.io/@jere-gim/Section-1-%EA%B8%B0%EC%88%A0-%EB%A9%B4%EC%A0%91-%EC%A4%80%EB%B9%84</guid>
            <pubDate>Mon, 13 Mar 2023 01:53:50 GMT</pubDate>
            <description><![CDATA[<h1 id="📌자바-객체지향-프로그래밍oop에-대해-설명해주세요">📌자바 객체지향 프로그래밍(OOP)에 대해 설명해주세요.</h1>
<p>자바 객체지향 프로그래밍은 프로그램을 객체라는 단위로 나누고 이들 객체 간의 상호작용으로 프로그램을 구성하는 방식입니다. 객체는 데이터와 데이터를 다루는 메서드로 이루어져 있으며 이러한 객체들은 클래스라는 틀을 이용하여 생성됩니다. </p>
<p>객체지향 프로그래밍은 캡슐화, 상속, 추상화, 다형성 등의 개념을 기반으로 하며, 이를 통해 프로그램의 유지보수와 확장을 용이하게 만들고 코드의 가독성과 재사용성을 높일 수 있습니다.</p>
<ul>
<li><p>캡슐화는 객체의 데이터와 메서드를 하나로 묶고, 외부에서 직접 접근하지 못하도록 정보를 은닉하는 것을 의미합니다. 객체의 내부 구현 세부사항을 숨김으로써, 객체 간의 결합도가 느슨해지고 코드의 변화와 확장에 용이해집니다.</p>
</li>
<li><p>상속은 상위 클래스(부모 클래스)의 특성을 하위 클래스(자식 클래스)가 물려받아 사용할 수 있도록 하는 것을 의미합니다. 상속을 통해 클래스 간의 관계를 형성하고, 하위 클래스는 상위 클래스의 모든 멤버를 물려받기 때문에 코드의 재사용성을 높일 수 있습니다.</p>
</li>
<li><p>추상화는 객체의 복잡한 세부사항을 숨기고, 핵심적인 부분만을 표현하는 것을 의미합니다. 즉, 여러 클래스들의 공통되는 부분을 뽑아내서 추상 클래스 혹은 인터페이스로 만드는 것입니다. 이를 통해, 구체적인 구현에 의지하는 것이 아닌 추상적인 역할에 의지할 수 있게 되고 코드의 중복이 줄어들고 코드의 가독성이 향상됩니다.</p>
</li>
<li><p>다형성은 하나의 객체가 여러가지 형태를 가질 수 있는 것을 의미합니다. 즉, 상위타입의 참조변수로 타입의 객체를 다룰 수 있습니다. 예를 들어, 참조형 매개변수를 가진 메서드를 호출할 때, 하위타입의 인스턴스를 넘겨줄 수 있고 상위타입의 배열에 하위타입의 객체를 담을 수 있습니다. 이를 통해, 코드의 유연성과 확장성이 높아집니다.</p>
</li>
</ul>
<h1 id="📌oop의-장점과-단점에-대해-설명해주세요">📌OOP의 장점과 단점에 대해 설명해주세요.</h1>
<p>OOP의 장점에는 여러가지가 있습니다.</p>
<ul>
<li><p>상속을 통해 기존의 클래스를 확장하고, 추상화를 통해 공통적인 부분을 추출하여 코드의 재사용성을 높일 수 있습니다.</p>
</li>
<li><p>캡슐화를 통해 내부 구현 세부사항을 외부로부터 숨기고, 필요한 부분만 드러냄으로써 외부에서의 예측하지 못한 상태 변경에 대한 영향을 최소화할 수 있습니다. 이로 인해 코드의 안정성과 보안성이 향상되고, 객체 간의 결합도가 낮아져 코드의 유지보수가 쉬워집니다.</p>
</li>
<li><p>다형성을 통해 상위타입의 참조변수로 하위타입의 여러 객체를 다룰 수 있고, 상속을 통해 확장이 쉬워지므로, 코드의 유연성이 높아집니다.</p>
</li>
<li><p>추상화를 통해 객체의 복잡한 세부사항을 숨기고 공통적인 부분만을 모아 표현하기 때문에 코드의 가독성이 높아집니다.</p>
</li>
</ul>
<p>단점은 실행 속도가 상대적으로 느리다는 것입니다. 객체를 많이 만들어 놓으니 절차지향언어에 비해 실행 속도가 상대적으로 느리다고 합니다. 또한 설계시 많은 시간과 노력이 필요하다는 것도 단점 중 하나입니다.</p>
<h1 id="📌추상-클래스와-인터페이스의-차이는-무엇인가요">📌추상 클래스와 인터페이스의 차이는 무엇인가요?</h1>
<p>추상 클래스는 추상 메서드를 포함하고 있는 클래스를 말하고 인터페이스는 추상 메서드로만 이루어져 있는 것을 말합니다.
추상 클래스는 일바 클래스처럼 인스턴스 변수, 인스턴스 메서드, 생성자 등을 가질 수 있지만 인터페이스는 위의 것들을 가질 수 없습니다.
추상 클래스는 다중상속이 불가능하지만, 인터페이스는 다중상속이 가능합니다.</p>
<h1 id="📌컬렉션과-스트림의-차이에-대해서-설명해주세요">📌컬렉션과 스트림의 차이에 대해서 설명해주세요.</h1>
<ol>
<li><p>데이터를 다루는 목적이 다릅니다.
컬렉션은 데이터를 저장하고 조회하는 것이 목적입니다. 예를 들어, List를 순회하며 요소를 삭제하거나 추가할 수 있습니다.
스트림은 데이터를 처리하는 것이 목적입니다. 스트림을 사용하면 데이터를 필터링, 매핑, 정렬 등의 다양한 작업을 수행할 수 있습니다.</p>
</li>
<li><p>데이터를 반복 처리하는 방식이 다릅니다.
컬렉션은 명시적인 for-each 루프나 iterator 반복자를 사용하여 데이터를 외부 반복을 통해 처리합니다.
스트림은 내부 반복을 사용하여 데이터를 처리합니다. 스트림은 내부적으로 for-each 루프를 사용하지만 개발자가 이를 직접 구현할 필요가 없습니다.</p>
</li>
<li><p>다루는 데이터 소스의 유형이 다릅니다.
컬렉션은 메모리에 저장된 데이터를 처리합니다. 즉, 모든 데이터가 메모리에 로드되어 있으므로 데이터를 쉽게 검색하고 수정할 수 있습니다.
스트림은 컬렉션, 배열, 파일 등 다양한 데이터 소스를 처리할 수 있습니다. 스트림은 데이터를 순차적으로 읽어 들이는 방식으로 처리되므로, 전체 데이터를 한 번에 로드하지 않아도 됩니다. 이러한 특성은 대용량의 데이터를 처리할 때 유용합니다.</p>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[[자바의 정석 기초편] 쓰레드 1]]></title>
            <link>https://velog.io/@jere-gim/%EC%9E%90%EB%B0%94%EC%9D%98-%EC%A0%95%EC%84%9D-%EA%B8%B0%EC%B4%88%ED%8E%B8-%EC%93%B0%EB%A0%88%EB%93%9C-1</link>
            <guid>https://velog.io/@jere-gim/%EC%9E%90%EB%B0%94%EC%9D%98-%EC%A0%95%EC%84%9D-%EA%B8%B0%EC%B4%88%ED%8E%B8-%EC%93%B0%EB%A0%88%EB%93%9C-1</guid>
            <pubDate>Sat, 11 Mar 2023 16:31:05 GMT</pubDate>
            <description><![CDATA[<h1 id="📌프로세스와-쓰레드">📌프로세스와 쓰레드</h1>
<p><strong>프로세스 : 실행 중인 프로그램. 자원(resources)과 쓰레드로 구성</strong></p>
<ul>
<li>자원 : 메모리, CPU, 하드 디스크, 키보드 등</li>
</ul>
<p><strong>쓰레드 : 프로세스 내에서 실제 작업을 수행. 모든 프로세스는 최소한 하나의 쓰레드를 가지고 있다.</strong></p>
<ul>
<li>싱글쓰레드 프로세스 = 자원 + 쓰레드</li>
<li>멀티쓰레드 프로세스 = 자원 + 쓰레드 + 쓰레드 +...+ 쓰레드</li>
</ul>
<blockquote>
<center>프로세스 : 쓰레드 = 공장 : 일꾼</center>
</blockquote>
<p><strong>하나의 새로운 프로세스를 생성하는 것보다 하나의 새로운 쓰레드를 생성하는 것이 더 적은 비용이 든다.</strong></p>
<ul>
<li>2 프로세스 1 쓰레드 vs 1 프로세스 2 쓰레드 </li>
<li><blockquote>
<p>1 프로세스 2 쓰레드가 더 효율적</p>
</blockquote>
</li>
</ul>
<hr>
<h1 id="📌멀티쓰레드의-장단점">📌멀티쓰레드의 장단점</h1>
<p><strong>대부분의 프로그램이 멀티쓰레드로 작성되어 있다. 하지만 멀티쓰레드 프로그래밍이 장점만 있는 것은 아니다.</strong></p>
<h2 id="장점">장점</h2>
<ul>
<li><p>시스템 자원을 보다 효율적으로 사용할 수 있다.</p>
</li>
<li><p>사용자에 대한 응답성이 향상된다.</p>
</li>
<li><p>작업이 분리되어 코드가 간결해진다.</p>
<h2 id="단점">단점</h2>
</li>
<li><p>동기화(synchronization)에 주의해야 한다.</p>
<blockquote>
<p>쓰레드 동기화는 여러 쓰레드가 동일한 리소스를 공유하여 사용하게 되면 서로의 결과에 영향을 주기 때문에 이를 방지하는 기법이다.</p>
</blockquote>
</li>
<li><p>교착상태(dead-lock)가 발생하지 않도록 주의해야 한다.</p>
<blockquote>
<p>교착상태(dead-lock) : 둘 이상의 작업이 서로 상대방 작업이 끝나기만 기다리고 있어서 서로 다음 단계로 진행하는 못하는 상태로 서로 무한 대기 상태로 빠지게 된다.   </p>
</blockquote>
</li>
<li><p>기아가 발생하지 않도록 각 쓰레드가 효율적으로 고르게 실행될 수 있도록 해야 한다.</p>
<blockquote>
<p>기아 : 특정 쓰레드보다 우선순위가 높은 쓰레드만 계속 실행되면서 특정 쓰레드가 실행될 기회를 얻지 못하는 상태</p>
</blockquote>
</li>
</ul>
<hr>
<h1 id="📌쓰레드의-구현과-실행">📌쓰레드의 구현과 실행</h1>
<h2 id="구현">구현</h2>
<p><strong>쓰레드를 구현하는 방법은 2가지가 있다. 
<code>Thread 클래스를 상속</code>
<code>Runnable 인터페이스를 구현</code></strong></p>
<p>상속으로 구현을 하게 되면 자바는 단일 상속이기 때문에 다른 클래스를 상속 못한다는 단점이 있다.
그래서 일반적으로 <code>Runnable 인터페이스를 구현</code> 방법을 사용한다.</p>
<ol>
<li>Thread 클래스를 상속<pre><code class="language-java">class Thread1 extends Thread {
 public void run() {
 /*작업내용*/
 }
}</code></pre>
</li>
</ol>
<ul>
<li><code>run()</code> 메서드는 Thread 클래스의 <code>run()</code> 메서드를 오버라이딩한 것이다.</li>
</ul>
<ol start="2">
<li>Runnable 인터페이스를 구현<pre><code class="language-java">class Thread2 implements Runnable {
 public void run() {
 /*작업내용*/
 }
}</code></pre>
</li>
</ol>
<ul>
<li><code>run()</code> 메서드는 Runnable 인터페이스의 <code>run()</code> 추상 메서드를 구현한 것이다.</li>
</ul>
<h2 id="실행">실행</h2>
<pre><code class="language-java">class Ex13_1 {
    public static void main(String[] args) {
        Thread1 t1 = new Thread1();

        Runnable r = new Thread2();
        Thread t2 = new Thread(r); // 생성자 Thread(Runnable target)

        t1.start();
        t2.start();
    }
}

class Thread1 extends Thread {
    public void run() { // 쓰레드가 수행할 작업을 작성
        for (int i = 0; i &lt; 5; i++) {
            // 조상인 Thread의 getName()을 호출
            System.out.printf(&quot;(%s)%n&quot;, getName());
        }
    }
}

class Thread2 implements Runnable {
    public void run() {
        for (int i = 0; i &lt; 5; i++) {
            // 현재 실행중인 Thread의 이름을 반환
            System.out.printf(&quot;(%s)%n&quot;, Thread.currentThread().getName());
        }
    }
}</code></pre>
<blockquote>
<p>(Thread-0)
(Thread-0)
(Thread-0)
(Thread-1)
(Thread-1)
(Thread-1)
(Thread-1)
(Thread-1)
(Thread-0)
(Thread-0)</p>
</blockquote>
<ul>
<li>t1.start()을 먼저 적어줬다고 해서 t1이 먼저 실행되지는 않는다. 이는 OS의 스케쥴러가 결정해준다.</li>
<li>실행할 때마다 순서는 바뀔 수 있다.<pre><code class="language-java">Runnable r = new Thread2();
Thread t2 = new Thread(r); // 생성자 Thread(Runnable target)</code></pre>
</li>
<li>Runnable을 구현한 쓰레드는 객체를 생성할 때 생성자 <code>Thread(Runnable target)</code>를 이용해줘야 한다.</li>
<li><code>Thread t2 = new Thread(new Thread2());</code> : 이렇게 한 줄로 바꿔줄 수도 있다.</li>
</ul>
<pre><code class="language-java">// 조상인 Thread의 getName()을 호출
System.out.printf(&quot;(%s)%n&quot;, getName());
...
// 현재 실행중인 Thread의 이름을 반환
System.out.printf(&quot;(%s)%n&quot;, Thread.currentThread().getName());</code></pre>
<ul>
<li>Thread를 상속한 쓰레드는 조상의 메서드인 <code>getName()</code>을 호출해서 쓰레드의 이름을 출력할 수 있다.</li>
<li>Runnable을 구현한 쓰레드는 <code>Thread.currentThread().getName());</code> 메서드를 통해서 현재 쓰레드의 이름을 반환할 수 있다.</li>
</ul>
<h3 id="start">start()</h3>
<p><strong>쓰레드를 생성한 후에 start()를 호출해야 쓰레드가 작업 가능한 상태가 된다.</strong></p>
<pre><code class="language-java">t1.start();
t2.start();</code></pre>
<ul>
<li><p>start()를 호출하면 쓰레드가 실행 가능한 상태가 되는 것이지 실행이 되는 것이 아니다. 
실제로 언제 실행이 될지는 OS의 스케쥴러가 실행 순서를 결정한다.</p>
</li>
<li><p><code>t1.start();</code> 먼저 적는다고 해도 먼저 호출되는 것이 아니다.</p>
</li>
</ul>
<p><strong><code>run()</code> 메서드를 통해 수행할 작업을 작성했지만 호출은 <code>start()</code> 메서드로 하는 이유</strong></p>
<ol>
<li><p>호출 스택에서 <code>start()</code> 메서드가 새로운 호출 스택 생성
<img src="https://velog.velcdn.com/images/jere-gim/post/eea37b69-19b3-457d-a62f-70038465fbe5/image.png" alt=""></p>
</li>
<li><p><code>start()</code> 메서드는 종료되고 새로운 호출 스택에서 <code>run()</code> 메서드가 동작함으로써 서로 독립적인 작업 수행이 가능(멀티쓰레드)
<img src="https://velog.velcdn.com/images/jere-gim/post/d7086dad-4a71-4db2-8599-44e41d8184b1/image.png" alt=""></p>
</li>
</ol>
<p><strong><code>t1.start();</code>가 아닌 <code>t1.run();</code>을 하게될 경우</strong> 
<img src="https://velog.velcdn.com/images/jere-gim/post/b3e70bd3-1ac3-460a-a804-01c6b5ab079d/image.png" alt=""></p>
<ul>
<li>멀티쓰레드가 아닌 main쓰레드에서 싱글쓰레드로 동작하게 된다.</li>
</ul>
<hr>
<h1 id="📌main쓰레드">📌main쓰레드</h1>
<p><strong>: main메서드의 코드를 수행하는 쓰레드</strong></p>
<p><strong>쓰레드는 &quot;사용자 쓰레드&quot;와 &quot;데몬 쓰레드&quot; 두 종류가 있다.</strong></p>
<ul>
<li>데몬 쓰레드는 보조 쓰레드 개념이다.</li>
<li>실행 중인 사용자 쓰레드가 하나도 없을 때 프로그램은 종료된다.(데몬 쓰레드의 실행 여부는 신경X)</li>
</ul>
<pre><code class="language-java">public class MainThread {
    public static void main(String[] args) {
        Thread t1 = new Thread(new MyThread1());
        Thread t2 = new Thread(new MyThread2());

        t1.start();
        t2.start();

        for (int i = 0; i &lt; 100; i++) {
            System.out.print(0);
        }
    }
}

class MyThread1 implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i &lt; 100; i++) {
            System.out.print(1);
        }
    }
}

class MyThread2 implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i &lt; 100; i++) {
            System.out.print(2);
        }
    }
}
</code></pre>
<blockquote>
<p>00002112222222222222222222222220000000000000000000000000000000000000000000000022222222222222222221111111111111222222222222222222220000000000000000000000000000000000000000000000000<code>main쓰레드 종료시점</code>222222222222222222222222222222222222<code>MyThread2 종료시점</code>1111111111111111111111111111111111111111111111111111111111111111111111111111111111111<code>MyThread1 종료시점</code></p>
</blockquote>
<ul>
<li><code>main</code>, <code>MyThread2</code>, <code>MyThread1</code> 총 3개의 &quot;사용자 쓰레드&quot;가 모두 종료될 때까지 프로그램은 종료되지 않는다.</li>
</ul>
<hr>
<h1 id="📌싱글쓰레드와-멀티쓰레드">📌싱글쓰레드와 멀티쓰레드</h1>
<p><strong>싱글쓰레드</strong></p>
<pre><code class="language-java">public class MainThread {
        for (int i = 0; i &lt; 100; i++) {
            System.out.print(0);
        }
        for (int i = 0; i &lt; 100; i++) {
            System.out.print(1);
        }
    }
}</code></pre>
<blockquote>
<p>00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111</p>
</blockquote>
<ul>
<li>0을 다 출력하고나서 1이 출력된다.</li>
</ul>
<p><strong>멀티쓰레드</strong></p>
<pre><code class="language-java">public class MainThread {
    public static void main(String[] args) {
        Thread t1 = new Thread(new MyThread1());

        t1.start();

        for (int i = 0; i &lt; 100; i++) {
            System.out.print(0);
        }
    }
}

class MyThread1 implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i &lt; 100; i++) {
            System.out.print(1);
        }
    }
}</code></pre>
<blockquote>
<p>01000000000000000000000000000000111111111111110000000000000000000000000000000000000000000000000000010011111111111111111111111111111111111111111111111111111111111111100000000000000111111111111111111111</p>
</blockquote>
<ul>
<li>멀티쓰레드로 작성하면 0과 1이 번갈아가면서 출력된다.</li>
</ul>
<h2 id="싱글쓰레드와-멀티쓰레드-그래프">싱글쓰레드와 멀티쓰레드 그래프</h2>
<p><strong>싱글쓰레드와 멀티쓰레드를 그래프로 그려보면 다음과 같다.</strong>
<img src="https://velog.velcdn.com/images/jere-gim/post/7d544ea3-6e58-44db-952a-d6076e9e6761/image.png" alt=""></p>
<ul>
<li><p>가로축을 보게 되면 멀티쓰레드가 시간이 약간 더 걸린다는 것을 알 수 있다.
그 이유는 main쓰레드가 실행되다가 t1쓰레드가 실행되면 쓰레드 간의 작업 전환(context switching)이 발생하면서 시간이 조금씩 소모하기 때문이다.</p>
</li>
<li><p>사실 위의 멀티쓰레드 그래프는 이상적인 그래프이다. 해당 쓰레드가 언제 얼마만큼 실행될지는 OS의 스케쥴러가 결정하기 때문에 실제로는 위의 그래프처럼 일정한 시간동안 두 쓰레드가 번갈아가며 실행되지 않는다.
OS 스케쥴러는 OS 전체의 프로세스와 쓰레드를 총괄하기 때문에 시시각각 변하는 모든 프로레스와 쓰레드의 상황을 고려해서 실행순서와 실행기간을 정해 스케쥴링을 한다.
따라서, 우리가 작성한 쓰레드는 실행할 때마다 결과가 달라지는 것이다.</p>
</li>
</ul>
<h2 id="쓰레드의-io-블락킹blocking">쓰레드의 I/O 블락킹(blocking)</h2>
<p><strong>싱글쓰레드로 작성</strong></p>
<pre><code class="language-java">import javax.swing.JOptionPane;

class Ex13_4 {
    public static void main(String[] args) throws Exception {
        String input = JOptionPane.showInputDialog(&quot;아무 값이나 입력하세요.&quot;);
        System.out.println(&quot;입력하신 값은 &quot; + input + &quot;입니다.&quot;);

        for (int i = 5; i &gt; 0; i--) {
            System.out.println(i);
            try {
                Thread.sleep(500);
            } catch (Exception e) {
            }
        }
    }
}</code></pre>
<p><img src="https://velog.velcdn.com/images/jere-gim/post/f10e8734-3d29-4bbb-a8f7-ae1a4d5e6c7e/image.webp" alt=""></p>
<ul>
<li>입력을 받을 때까지 다음 작업은 실행되지 않고 대기상태에 머무르게 된다. 즉, for 문(카운트다운)이 실행되지 않는다. 이것을 <code>I/O Blocking</code> 이라고 한다.</li>
</ul>
<p><strong>멀티쓰레드로 작성</strong></p>
<pre><code class="language-java">import javax.swing.JOptionPane;

class Ex13_4 {
    public static void main(String[] args) throws Exception {
        Thread t1 = new ThreadEx5();
        t1.start();

       String input = JOptionPane.showInputDialog(&quot;아무 값이나 입력하세요.&quot;);
        System.out.println(&quot;입력하신 값은 &quot; + input + &quot;입니다.&quot;);
    }
}

class ThreadEx5 extends Thread {
    public void run() {
        for (int i = 5; i &gt; 0; i--) {
            System.out.println(i);
            try {
                sleep(500);
            } catch (Exception e) {
            }
        }
    }
}</code></pre>
<p><img src="https://velog.velcdn.com/images/jere-gim/post/250c1be8-7ae9-4517-a2af-0b017784c4f1/image.webp" alt=""></p>
<ul>
<li>멀티쓰레드로 작성하게 되면 입력을 받지 않아도 멀티쓰레드인 t1쓰레드가 실행되면서 for 문(카운트다운)이 동작하게 된다.</li>
</ul>
<p><strong>결론 : 멀티쓰레드를 통해서 I/O Blocking을 해결할 수 있다.</strong></p>
<hr>
<h1 id="📌쓰레드의-우선순위priority-of-thread">📌쓰레드의 우선순위(Priority of thread)</h1>
<p><strong>: 작업의 중요도에 따라 쓰레드의 우선순위를 다르게 하여 특정 쓰레드가 더 많은 작업 시간을 갖게 할 수 있다.</strong></p>
<blockquote>
<p><code>void setPriority(int newPriority)</code> : 지정한 값으로 우선순위 변경
<code>int getPriority()</code> : 우선순위 반환
우선순위 범위 : 1 (최소) ~ 10 (최대)
우선순위 default 값 : 5
main쓰레드 우선순위 : 5</p>
</blockquote>
<p>JVM의 우선순위는 범위가 1 ~ 10으로 지정할 수 있도록 정해져있고 Windows OS의 경우에는 우선순위 범위가 32단계로 나뉘어져 있다.
JVM에서 설정된 쓰레드들의 우선순위를 OS 스케줄러에 전달하는 것이다.</p>
<p>그러나 우리가 쓰레드들의 우선순위를 각자 지정한다고 현재 OS 전체 작업을 다 제끼고 우선적으로 설정되는 것이 아니라 그저 <strong>희망 사항</strong>을 스케쥴러에 전달하는 것 뿐이다.</p>
<p>OS 스케쥴러는 우리가 전달한 희망 사항을 참고할 뿐 결국 OS 내에서 돌아가는 전체 프로그램들의 작업 효율을 따져 실행 순서를 정한다.</p>
<hr>
<h1 id="📌쓰레드-그룹">📌쓰레드 그룹</h1>
<p><strong>서로 관련된 쓰레드를 그룹으로 묶어서 다루기 위한 것</strong></p>
<p><strong>모든 쓰레드는 반드시 하나의 쓰레드 그룹에 포함되어 있어야 하고 쓰레드 그룹을 지정하지 않고 생성한 쓰레드는 자동으로 <code>main쓰레드 그룹</code>에 속한다.</strong></p>
<p><strong>자신을 생성한 쓰레드(부모 쓰레드)의 그룹과 우선순위를 상속받는다.</strong></p>
<p><strong>Thread 생성자</strong></p>
<pre><code class="language-java">Thread(ThreadGroup group, String name) // Thread 클래스 상속받아서 만들어진 경우
Thread(ThreadGroup group, Runnable target) // Runnable 인터페이스 구현해서 만들어진 경우 
Thread(ThreadGroup group, Runnable target, String name)
Thread(ThreadGroup group, Runnable target, String name , long stackSize) // 생성할 호출스택 사이즈 설정 </code></pre>
<h2 id="쓰레드-그룹-메서드">쓰레드 그룹 메서드</h2>
<table>
<thead>
<tr>
<th align="left"><center>Method</center></th>
<th align="left"><center>설명</center></th>
</tr>
</thead>
<tbody><tr>
<td align="left"><strong><center>생성자</center></strong></td>
<td align="left"></td>
</tr>
<tr>
<td align="left">ThreadGroup getThreadGroup()</td>
<td align="left">자신이 속한 쓰레드 그룹 반환</td>
</tr>
<tr>
<td align="left">ThreadGroup(String name)</td>
<td align="left">지정된 이름의 새로운 쓰레드 그룹 생성</td>
</tr>
<tr>
<td align="left">ThreadGroup(ThreadGroup parent, String name)</td>
<td align="left">지정된 쓰레드 그룹에 속하는 지정된 이름의 새로운 쓰레드 그룹 생성</td>
</tr>
<tr>
<td align="left"><strong><center>반환</center></strong></td>
<td align="left"></td>
</tr>
<tr>
<td align="left">int activeCount()</td>
<td align="left">쓰레드 그룹에 포함된 활성상태 쓰레드 수 반환</td>
</tr>
<tr>
<td align="left">int activeGroupCount()</td>
<td align="left">쓰레드 그룹에 포함된 활성상태 쓰레드 그룹 수 반환</td>
</tr>
<tr>
<td align="left">int getMaxPriority()</td>
<td align="left">쓰레드 그룹의 최대 우선순위 반환</td>
</tr>
<tr>
<td align="left">String getName()</td>
<td align="left">쓰레드 그룹의 이름 반환</td>
</tr>
<tr>
<td align="left">ThreadGroup getParent()</td>
<td align="left">쓰레드 그룹의 상위 쓰레드 그룹 반환</td>
</tr>
<tr>
<td align="left">void list()</td>
<td align="left">소속 쓰레드와 하위 쓰레드 그룹 정보 출력</td>
</tr>
<tr>
<td align="left"><strong><center>조회/확인</center></strong></td>
<td align="left"></td>
</tr>
<tr>
<td align="left">boolean isDaemon()</td>
<td align="left">해당 쓰레드 그룹이 데몬 쓰레드 그룹인지 확인</td>
</tr>
<tr>
<td align="left">boolean isDestroyed()</td>
<td align="left">해당 쓰레드 그룹이 삭제되었는지 확인</td>
</tr>
<tr>
<td align="left">boolean parentOf(ThreadGroup g)</td>
<td align="left">해당 쓰레드 그룹이 지정된 쓰레드 그룹의 상위 쓰레드 그룹인지 확인</td>
</tr>
<tr>
<td align="left">void checkAccess()</td>
<td align="left">현재 실행 중인 쓰레드가 쓰레드 그룹을 변경할 권한이 있는지 체크</td>
</tr>
<tr>
<td align="left"><strong><center>삭제</center></strong></td>
<td align="left"></td>
</tr>
<tr>
<td align="left">void destroy()</td>
<td align="left">쓰레드 그룹과 하위 쓰레드 그룹까지 전부 삭제</td>
</tr>
<tr>
<td align="left">int enumerate(Thread[] list)<br>int enumerate( Thread[] list, boolean recurse)<br>int enumerate(ThreadGroup[] list)<br>int enumerate(ThreadGroup[] list, boolean recurse)</td>
<td align="left">그룹에 속한 쓰레드와하위 쓰레드 그룹의 목록을 지정된 배열에 담고 개수 반환</td>
</tr>
<tr>
<td align="left"><strong><center>설정</center></strong></td>
<td align="left"></td>
</tr>
<tr>
<td align="left">void setDaemon(boolean daemon)</td>
<td align="left">쓰레드 그룹을 데몬 쓰레드 그룹으로 설정/해제</td>
</tr>
<tr>
<td align="left">void setMaxPriority(int Priority)</td>
<td align="left">그룹의 최대 우선순위 설정</td>
</tr>
<tr>
<td align="left">void interrupt()</td>
<td align="left">그룹에 속한 모든 쓰레드를 interrupt()</td>
</tr>
</tbody></table>
<p><strong>void uncaughtException(Thread th, Throwable e) 메서드</strong></p>
<ul>
<li>쓰레드 그룹에 있는 쓰레드가 예외처리를 하지 못하고 종료되었을 때 실행될 예외처리 동작을 오버라이딩 해주는 메서드. 
JVM에 의해 자동적으로 호출된다.</li>
</ul>
<pre><code class="language-java">class Demo implements Runnable {
    @Override
    public void run() {
        int x = 10 / 0; // ArithmeticException 발생
    }
}

class MyHandler implements Thread.UncaughtExceptionHandler {
    @Override
    public void uncaughtException(Thread t, Throwable e) {
        System.out.println(&quot;쓰레드 &quot; + t.getName() + &quot;에서 &quot; + e.getMessage() + &quot; 발생&quot;);
        // 로깅 작업 등을 할 수 있다.
    }
}

public class GFG {
    public static void main(String[] args) {
        Thread t = new Thread(new Demo());
        t.setUncaughtExceptionHandler(new MyHandler()); // uncaughtException 설정
        t.start();
    }
}</code></pre>
<blockquote>
<p>쓰레드 Thread-0에서 / by zero 발생</p>
</blockquote>
<h2 id="실습-예제">실습 예제</h2>
<pre><code class="language-java">public class MainThread {
    public static void main(String[] args) {
        ThreadGroup root = new ThreadGroup(&quot;rootGroup&quot;); // 루트 그룹 생성
        ThreadGroup childGroup = new ThreadGroup(root, &quot;childGroup&quot;); // 루트 그룹에 속하는 자식 그룹 생성
        rootThread rt = new rootThread(root, &quot;rt&quot;); // 루트 그룹에 속하는 쓰레드 생성
        Thread ch1 = new Thread(childGroup, new childThread1(), &quot;ch1&quot;); // 자식 그룹에 속하는 쓰레드 생성
        Thread ch2 = new Thread(new childThread2(), &quot;ch2&quot;); // 쓰레드 그룹 미지정 -&gt; 자동으로 main쓰레드 그룹에 속함

        rt.start(); // 루트 그룹의 쓰레드 시작
        ch1.start(); // 자식 그룹의 쓰레드 시작
        ch2.start(); // main쓰레드 그룹의 쓰레드 시작

        root.list(); // 루트 그룹과 하위 그룹들의 정보 출력
        System.out.println();
    }
}

class childThread1 implements Runnable {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + &quot;가 속한 쓰레드 그룹 : &quot; + Thread.currentThread().getThreadGroup().getName());
    }
}

class childThread2 implements Runnable {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + &quot;가 속한 쓰레드 그룹 : &quot; + Thread.currentThread().getThreadGroup().getName());
    }
}

class rootThread extends Thread {
    public rootThread(ThreadGroup group, String name) {
        super(group, name);
    }

    @Override
    public void run() {
        System.out.println(getName() + &quot;가 속한 쓰레드 그룹 : &quot; + getThreadGroup().getName()); // rt의 쓰레드 그룹 이름 출력
    }
}</code></pre>
<ul>
<li><p>쓰레드에 어떤 명령을 내릴 때 쓰레드 그룹에 한번에 명령을 내릴 수 있다.</p>
</li>
<li><p><code>rootThread rt = new rootThread(root, &quot;rt&quot;);</code> : <code>Thread(ThreadGroup group, String name)</code> 생성자 사용</p>
</li>
<li><p><code>Thread ch1 = new Thread(childGroup, new childThread1(), &quot;ch1&quot;);</code> : <code>Thread(ThreadGroup group, Runnable target, String name)</code> 생성자 사용</p>
</li>
<li><p>쓰레드를 생성할 땐 생성자를 통해 쓰레드 그룹을 지정해줘야 하지만 지정해주지 않으면 자동으로 <code>main쓰레드 그룹</code>에 속하게 된다. -&gt; <code>Thread ch2 = new Thread(new childThread2(), &quot;ch2&quot;);</code></p>
</li>
</ul>
<hr>
<h1 id="📌데몬-쓰레드daemon-thread">📌데몬 쓰레드(Daemon thread)</h1>
<p><strong>: 일반 쓰레드의 작업을 돕는 보조적인 역할을 수행하는 쓰레드</strong></p>
<p><strong>일반 쓰레드가 모두 종료되면 자동적으로 종료된다.(일반 쓰레드의 보조적인 역할로써 일반 쓰레드가 종료되면 존재의 의미가 없어지기 때문에)</strong></p>
<p><strong>데몬 쓰레드 예시 : 가비지 컬렉터(GC), 자동 저장, 화면 자동갱신 등</strong></p>
<h2 id="데몬-쓰레드-작성-방법">데몬 쓰레드 작성 방법</h2>
<p><strong>: 무한루프와 조건문을 이용해서 실행 후 대기 상태에 있다가 특정 조건이 만족되면 작업을 수행하고 다시 대기하도록 작성</strong></p>
<ul>
<li><p><code>boolean isDaemon()</code> : 쓰레드가 데몬 쓰레드인지 확인. 데몬 쓰레드면 true 반환</p>
</li>
<li><p><code>void setDaemon(boolean on)</code> : 쓰레드를 데몬 쓰레드로 또는 일반 쓰레드로 변경. 매개변수로 true를 지정하면 데몬 쓰레드가 된다.</p>
</li>
<li><p><em><code>void setDaemon(boolean on)</code>는 반드시 <code>start()</code> 메서드를 호출하기 전에 실행되어야 한다. 그렇지 않으면 <code>IllegalThreadStateException</code> 발생*</em></p>
</li>
</ul>
<pre><code class="language-java">class Ex13_7 implements Runnable {
    static boolean autoSave = false;

    public static void main(String[] args) {
        Thread t = new Thread(new Ex13_7());
        t.setDaemon(true); // 쓰레드 t를 데몬 쓰레드로 변경
        t.start();

        for (int i = 1; i &lt;= 10; i++) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
            }
            System.out.println(i);

            if (i == 5) autoSave = true; // 5초가 지나면 autoSave를 true로 설정
        }

        System.out.println(&quot;프로그램을 종료합니다.&quot;);
    }

    public void run() {
        while (true) { // 무한루프로 작성
            try {
                Thread.sleep(3 * 1000);
            } catch (InterruptedException e) {
            }

            if (autoSave) autoSave(); // autoSave가 true 조건을 만족할 때만 작업을 수행
        }
    }

    public void autoSave() {
        System.out.println(&quot;작업 파일이 자동 저장 되었습니다.&quot;);
    }
}</code></pre>
<blockquote>
<p>1
2
3
4
5
작업 파일이 자동 저장 되었습니다.
6
7
8
작업 파일이 자동 저장 되었습니다.
9
10
프로그램을 종료합니다.</p>
</blockquote>
<ul>
<li><p>5초 이후 autoSave를 true로 변경하고 이 때부터 데몬 쓰레드의 if 조건문이 true가 되면서 3초마다 자동 저장하는 데몬 쓰레드가 동작한다.</p>
</li>
<li><p>일반 쓰레드가 10초 후 프로그램 종료가 되도록 설정되었기 때문에 10초 후 데몬 쓰레드도 자동적으로 종료된다.</p>
</li>
<li><p><code>t.setDaemon(true);</code> : 쓰레드 t를 데몬 쓰레드로 변경. 반드시 <code>start()</code> 메서드 전에 실행되어야 한다.</p>
</li>
</ul>
<hr>
<h1 id="📌쓰레드의-상태">📌쓰레드의 상태</h1>
<table>
<thead>
<tr>
<th align="left"><center>상태</center></th>
<th align="left"><center>설명</center></th>
</tr>
</thead>
<tbody><tr>
<td align="left">NEW</td>
<td align="left">생성은 되었지만 아직 start() 호출이 안된 상태</td>
</tr>
<tr>
<td align="left">RUNNABLE</td>
<td align="left">실행 중 또는 실행 가능한 상태</td>
</tr>
<tr>
<td align="left">BLOCKED</td>
<td align="left">동기화 블럭에 의해 일시 정지된 상태(풀릴 때까지 기다리는 상태)</td>
</tr>
<tr>
<td align="left">WAITING / TIMED_WAITING</td>
<td align="left">종료는 아니지만 실행 불가능한 일시 정지 상태<br>(TIMED_WAITING은 시간이 지정된 일시정지)</td>
</tr>
<tr>
<td align="left">TERMINATED</td>
<td align="left">쓰레드의 작업이 종료된 상태 -&gt; 소멸</td>
</tr>
</tbody></table>
<p><img src="https://velog.velcdn.com/images/jere-gim/post/15ef6e11-36e0-4dfd-9d92-d760747267be/image.png" alt=""></p>
<ul>
<li><p>쓰레드가 생성되었지만 아직 <code>start()</code> 메서드가 호출되기 전을 <code>NEW</code> 상태라고 한다.</p>
</li>
<li><p>실행 뿐만 아니라 실행 후에 다시 자신 차례가 올 때까지 실행 대기를 하는 상태 또한 <code>RUNNABLE</code> 상태라고 한다.</p>
</li>
<li><p>작업을 다 마치거나 <code>stop()</code> 메서드가 호출되면 <code>TERMINATED</code> 상태가 된다.</p>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/jere-gim/post/ca9b9987-436e-4903-a195-bac51bd355c6/image.png" alt=""></p>
<ul>
<li><p><code>suspend()</code> ↔ <code>resume()</code></p>
<ul>
<li><code>suspend()</code> 메서드가 호출되면 쓰레드가 <code>WAITING / BLOCKED</code> 상태가 된다.</li>
<li><code>resume()</code> 메서드를 호출하면 다시 <code>RUNNALBLE</code> 상태가 된다.</li>
</ul>
</li>
<li><p><code>sleep()</code> ↔ <code>time-out</code>, <code>interrupt()</code></p>
<ul>
<li><code>sleep()</code> 메서드가 호출되면 쓰레드가 지정한 시간동안 <code>WAITING / BLOCKED</code> 상태가 된다. </li>
<li>지정된 시간이 모두 지나면 <code>time-out</code>이 되고 다시 <code>RUNNALBLE</code> 상태가 된다.</li>
<li>지정된 시간이 모두 지나기 전에 <code>interrupt()</code> 메서드를 호출하면 중간에 바로 <code>RUNNABLE</code> 상태가 된다.</li>
</ul>
</li>
<li><p><code>wait()</code> ↔ <code>notify()</code></p>
<ul>
<li><code>wait()</code> 메서드가 호출되면 쓰레드가 <code>WAITING / BLOCKED</code> 상태가 된다.</li>
<li><code>notify()</code> 메서드를 호출하면 다시 <code>RUNNALBLE</code> 상태가 된다.</li>
</ul>
</li>
<li><p><code>join()</code> : 지정한 다른 쓰레드가 종료될 때까지 기다리는 메서드</p>
<ul>
<li>Ex) main 쓰레드에서 <code>t1.join()</code> 을 호출하면 main 쓰레드는 t1 쓰레드가 종료될 때까지 일시정지 상태에 있게 된다.</li>
</ul>
</li>
<li><p><code>I/O block</code> : 입출력이 완료될 때까지 대기</p>
</li>
</ul>
<h2 id="suspend-vs-wait">suspend() vs wait()</h2>
<p><strong><code>suspend()</code>와 <code>wait()</code>는 자바에서 쓰레드를 일시 정지시키는 메서드이다.</strong> 
그러나 두 메소드는 다음과 같은 차이점이 있습니다.</p>
<ul>
<li><p><code>suspend()</code>는 쓰레드가 속한 Thread 클래스의 메서드이고,<br><code>wait()</code>는 모든 객체가 상속하는 Object 클래스의 메서드이다.</p>
</li>
<li><p><code>suspend()</code>는 쓰레드를 일시 정지시키면서 락을 해제하지 않는다. 이로 인해 다른 쓰레드가 공유 자원에 접근할 수 없게 되어 교착 상태(dead-lock)가 발생할 수 있다.<br>반면 <code>wait()</code>는 쓰레드를 일시 정지시키면서 락을 해제한다. 이로 인해 다른 쓰레드가 공유 자원에 접근하고 통지(notify)를 보낼 수 있다.</p>
</li>
<li><p><code>suspend()</code>는 <code>resume()</code> 메서드로만 재개될 수 있다. 이 때 <code>resume()</code> 메서드가 호출되기 전에 <code>suspend()</code>된 쓰레드가 종료되면 문제가 발생할 수 있다.<br>반면 <code>wait()</code>는 <code>notify()</code>, <code>notifyAll()</code>, <code>interrupt()</code>, <code>time-out</code> 등의 방법으로 재개될 수 있다.</p>
</li>
</ul>
<p><strong>따라서 <code>suspend()</code>와 <code>resume()</code>은 권장되지 않는 방법이며, 대신 <code>wait()/notify()</code>, <code>sleep()/interrupt()</code> 등의 방법을 사용하는 것이 더 좋다.</strong></p>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[[자바의 정석 기초편] 열거형(Enum), 애너테이션(Annotation)]]></title>
            <link>https://velog.io/@jere-gim/%EC%9E%90%EB%B0%94%EC%9D%98-%EC%A0%95%EC%84%9D-%EA%B8%B0%EC%B4%88%ED%8E%B8-%EC%97%B4%EA%B1%B0%ED%98%95Enum-%EC%95%A0%EB%84%88%ED%85%8C%EC%9D%B4%EC%85%98Annotation</link>
            <guid>https://velog.io/@jere-gim/%EC%9E%90%EB%B0%94%EC%9D%98-%EC%A0%95%EC%84%9D-%EA%B8%B0%EC%B4%88%ED%8E%B8-%EC%97%B4%EA%B1%B0%ED%98%95Enum-%EC%95%A0%EB%84%88%ED%85%8C%EC%9D%B4%EC%85%98Annotation</guid>
            <pubDate>Thu, 09 Mar 2023 12:20:08 GMT</pubDate>
            <description><![CDATA[<h1 id="📌열거형enum">📌열거형(Enum)</h1>
<p><strong>: 서로 관련된 상수들을 같이 묶어 놓은 것.</strong></p>
<pre><code class="language-java">class Card {
    static final int CLOVER = 0;
    static final int HEART = 1;
    static final int DIAMOND = 2;
    static final int SPADE = 3;

    static final int TWO = 0;
    static final int THREE = 1;
    static final int FOUR = 2;
}</code></pre>
<p>열거형 사용🔽</p>
<pre><code class="language-java">class Card {
    enum Kind {CLOVER, HEART, DIAMOND, SPADE} 
    enum Value {TWO, THREE, FOUR}

    final Kind kind;
    final Value value;
}</code></pre>
<ul>
<li>값은 0부터 자동으로 매겨진다. CLOVER = 0, HEART = 1, DIAMOND = 2, SPADE = 3</li>
</ul>
<pre><code class="language-java">if(Card.CLOVER == Card.TWO) // true</code></pre>
<ul>
<li>결과는 true로 나오지만 의미상으로는 false가 나와야 맞는 조건이다.
열거형 비교🔽<pre><code class="language-java">if(Card.Kind.CLOVER == Card.Value.TWO) // 컴파일 에러</code></pre>
</li>
<li>자바의 열거형은 값과 타입을 모두 체크하기 때문에 타입이 다르다고 컴파일 에러가 난다. 이러한 이유 때문에 자바는 타입에 안전한 열거형을 제공한다고 한다.</li>
</ul>
<h2 id="열거형의-정의와-사용">열거형의 정의와 사용</h2>
<p><strong>정의</strong>
<code>enum 열거형 이름 {상수명1, 상수명2 ...}</code></p>
<p>예시) <code>enum Direction {EAST, SOUTH, WEST, NORTH}</code></p>
<p><strong>열거형 타입의 변수를 선언하고 사용</strong></p>
<pre><code class="language-java">class Unit {
    Direction dir = Direction.EAST;
    ...
}</code></pre>
<p><strong>열거형 상수의 비교는 <code>==</code> 와 <code>compareTo()</code> 사용 가능</strong></p>
<pre><code class="language-java">if (dir == Direction.EAST) { // OK
    ...
} else if (dir &lt; Direction.WEST) { // 에러
    ...
} else if (dir.compareTo(Direction.SOUTH) &gt; 0 ) { // OK
    ...
}</code></pre>
<ul>
<li><code>else if (dir &lt; Direction.WEST)</code> : 열거형 상수에는 비교연산자 사용 불가</li>
</ul>
<h2 id="열거형의-조상---javalangenum">열거형의 조상 - java.lang.Enum</h2>
<p><strong>모든 열거형이 상속 받는 메서드들</strong></p>
<table>
<thead>
<tr>
<th align="center">메서드</th>
<th align="center">설명</th>
</tr>
</thead>
<tbody><tr>
<td align="center">Class<code>&lt;E&gt;</code> getDeclaringClass()</td>
<td align="center">열거형의 Class 객체를 반환</td>
</tr>
<tr>
<td align="center">String name()</td>
<td align="center">열거형 상수의 이름을 문자열로 반환</td>
</tr>
<tr>
<td align="center">int ordinal()</td>
<td align="center">열거형 상수가 정의된 순서를 반환(0부터 시작)</td>
</tr>
<tr>
<td align="center">T valueOf(Class<code>&lt;T&gt;</code> enumType, String name)</td>
<td align="center">지정된 열거형에서 name과 일치하는 열거형 상수를 반환</td>
</tr>
</tbody></table>
<p><strong>컴파일러가 자동으로 추가해주는 메서드</strong></p>
<table>
<thead>
<tr>
<th align="center">메서드</th>
<th align="center">설명</th>
</tr>
</thead>
<tbody><tr>
<td align="center">static E[] values()</td>
<td align="center">열거형 상수가 가지고 있는 모든 값들을 배열로 반환</td>
</tr>
<tr>
<td align="center">static E valueOf(String name)</td>
<td align="center">열거형 상수 이름(name)을 반환</td>
</tr>
</tbody></table>
<h2 id="실습-예제">실습 예제</h2>
<p><strong>열거형 상수 정의 및 변수 선언</strong></p>
<pre><code class="language-java">enum Direction {EAST, SOUTH, WEST, NORTH} // values = 0, 1, 2, 3

class Ex12_5 {
    public static void main(String[] args) {
        Direction d1 = Direction.EAST;
        Direction d2 = Direction.valueOf(&quot;WEST&quot;);
        Direction d3 = Enum.valueOf(Direction.class, &quot;EAST&quot;);

        System.out.println(&quot;d1 = &quot; + d1);
        System.out.println(&quot;d2 = &quot; + d2);
        System.out.println(&quot;d3 = &quot; + d3);
        ...</code></pre>
<blockquote>
<p>d1 = EAST
d2 = WEST
d3 = EAST</p>
</blockquote>
<ul>
<li><p>값을 따로 지정해주지 않으면 자동으로 0에서부터 값이 할당된다.</p>
</li>
<li><p><code>Direction d1 = Direction.EAST;</code> : 보통 이 방법을 제일 많이 쓴다. 나머지 2가지 방법도 기능은 똑같다.</p>
</li>
</ul>
<p><strong>열거형 상수 비교</strong></p>
<pre><code class="language-java">        ...
        System.out.println(&quot;d1 == d2 ? &quot; + (d1 == d2));
        System.out.println(&quot;d1 == d3 ? &quot; + (d1 == d3));
        System.out.println(&quot;d1.equals(d3) ? &quot; + d1.equals(d3));
//        System.out.println(&quot;d2 &gt; d3 ? &quot;+ (d1 &gt; d3)); // 컴파일 에러
        System.out.println(&quot;d1.compareTo(d3) ? &quot; + (d1.compareTo(d3)));
        System.out.println(&quot;d1.compareTo(d2) ? &quot; + (d1.compareTo(d2)));
        ...</code></pre>
<blockquote>
<p>d1 == d2 ? false
d1 == d3 ? true
d1.equals(d3) ? true
d1.compareTo(d3) ? 0
d1.compareTo(d2) ? -2</p>
</blockquote>
<ul>
<li>열거형 상수들은 기본형이 아닌 하나하나가 객체이기 때문에 비교연산자 사용 불가</li>
</ul>
<p><strong>values() 메서드</strong></p>
<pre><code class="language-java">        ...
        Direction[] dArr = Direction.values();

        for (Direction d : dArr)
            System.out.printf(&quot;%s = %d%n&quot;, d.name(), d.ordinal());
        ...</code></pre>
<blockquote>
<p>EAST = 0
SOUTH = 1
WEST = 2
NORTH = 3</p>
</blockquote>
<ul>
<li><p><code>Direction.values()</code> : 열거형의 모든 상수를 배열로 반환</p>
</li>
<li><p><code>for (Direction d : dArr)</code> : for (Direction d : Direction.values()) 와 동일</p>
</li>
<li><p><code>d.name()</code> : 열거형 상수의 이름 반환</p>
</li>
<li><p><code>d.ordinal()</code> : 열거형 상수의 순서 반환(주의 : 값 아님)</p>
</li>
</ul>
<h2 id="열거형에-멤버-추가">열거형에 멤버 추가</h2>
<pre><code class="language-java">enum Direction {
    EAST(1, &quot;▶&quot;), SOUTH(2, &quot;▼&quot;), WEST(3, &quot;◀&quot;), NORTH(4, &quot;▲&quot;);

    private final int value;
    private final String symbol;

    Direction(int value, String symbol) {
        this.value = value;
        this.symbol = symbol;
    }

    public int getValue() {
        return value;
    }

    public String getSymbol() {
        return symbol;
    }
}    </code></pre>
<ul>
<li><p><code>EAST(1, &quot;▶&quot;), SOUTH(2, &quot;▼&quot;), WEST(3, &quot;◀&quot;), NORTH(4, &quot;▲&quot;);</code> : 원하는 값을 괄호안에 적는다.</p>
</li>
<li><p><code>private final int value;</code>, <code>private final String symbol;</code> : 괄호안에 들어갈 값을 저장할 인스턴스 변수를 추가해준다.</p>
<pre><code class="language-java">Direction(int value, String symbol) {
  this.value = value;
  this.symbol = symbol;
}</code></pre>
</li>
<li><p>추가한 iv값을 매개변수로 하는 생성자를 만들어준다.</p>
</li>
<li><p>열거형의 생성자는 항상 묵시적으로 private 접근 제어자이기 때문에, 외부에서 객체 생성 불가</p>
<ul>
<li><code>Direction d = new Direction(1, &quot;▶&quot;);</code> : 에러. 외부에서 호출 불가</li>
</ul>
</li>
</ul>
<pre><code class="language-java">public int getValue() {
    return value;
}

public String getSymbol() {
    return symbol;
}</code></pre>
<ul>
<li>private으로 생성한 iv 값에 대한 getter을 public 제어자로 만들어준다.</li>
</ul>
<h3 id="열거형-상수-출력">열거형 상수 출력</h3>
<pre><code class="language-java">public class EnumExample {
    public static void main(String[] args) {
        for (Direction dir : Direction.values()) {
            System.out.printf(&quot;%-5s : value : %d, symbol : %s, ordinal: %d%n&quot;,
                    dir.name(), dir.getValue(), dir.getSymbol(), dir.ordinal());
        }
    }
}</code></pre>
<p><img src="https://velog.velcdn.com/images/jere-gim/post/9d6c5c73-658c-4a01-a834-c1d4fc01ca6a/image.png" alt=""></p>
<ul>
<li><code>dir.getValue()</code>와 <code>dir.ordinal()</code> 차이<ul>
<li><code>dir.getValue()</code>는 열거형 상수 괄호안에 들어간 값</li>
<li><code>dir.ordinal()</code>는 열거형 상수들의 순서(0부터 시작)</li>
</ul>
</li>
</ul>
<hr>
<h1 id="📌애너테이션annotation">📌애너테이션(Annotation)</h1>
<p><strong>: 주석처럼 프로그래밍 언어에 영향을 미치지 않으며, 유요한 정보를 제공</strong></p>
<h2 id="표준-애너테이션">표준 애너테이션</h2>
<p><strong>: Java에서 제공하는 애너테이션</strong></p>
<table>
<thead>
<tr>
<th align="left">애너테이션</th>
<th align="left">설명</th>
</tr>
</thead>
<tbody><tr>
<td align="left">@Override</td>
<td align="left">해당 메서드를 오버라이딩 하는 것이다.</td>
</tr>
<tr>
<td align="left">@Deprecated</td>
<td align="left">앞으로 사용하지 않을 것을 권장한다.</td>
</tr>
<tr>
<td align="left">@SuppressWarnings</td>
<td align="left">컴파일러 특정 경고메세지 차단</td>
</tr>
<tr>
<td align="left">@SafeVarargs</td>
<td align="left">지네릭스 타입의 가변인자 사용</td>
</tr>
<tr>
<td align="left">@FunctionalInterface</td>
<td align="left">함수형 인터페이스이다.</td>
</tr>
<tr>
<td align="left">@Native</td>
<td align="left">native메서드에서 참조되는 상수 앞에 붙인다.</td>
</tr>
<tr>
<td align="left"><strong>애너테이션 만들 때 사용하는 메타 애너테이션</strong></td>
<td align="left"></td>
</tr>
</tbody></table>
<table>
<thead>
<tr>
<th align="left">애너테이션</th>
<th align="left">설명</th>
</tr>
</thead>
<tbody><tr>
<td align="left">@Taget*</td>
<td align="left">애너테이션 적용 가능 대상 지정</td>
</tr>
<tr>
<td align="left">@Documented*</td>
<td align="left">해당 애너테이션 정보가 javadoc 작성 문자에 포함되게끔</td>
</tr>
<tr>
<td align="left">@Inherited*</td>
<td align="left">&quot; 자손 클래스 &quot;에상속되도록</td>
</tr>
<tr>
<td align="left">@Retention*</td>
<td align="left">애너테이션 유지 범위 지정</td>
</tr>
<tr>
<td align="left">@Repeatable*</td>
<td align="left">애너테이션 반복 적용 허용</td>
</tr>
</tbody></table>
<h3 id="override">@Override</h3>
<p><strong>오버라이딩을 제대로 했는지 컴파일러가 체크하게 한다.</strong></p>
<p><strong>오버라이딩할 때 메서드 이름을 잘못 적는 실수를 예방</strong></p>
<pre><code class="language-java">class Product {
    void operate() {}
}

class Tv extends Product {
    @Override
    void opearte() {} // 컴파일 에러
}</code></pre>
<ul>
<li><code>@Override</code> 애너테이션을 안붙이면 철자 실수로 인해 내 작성 의도와 다르게 새로운 메서드를 작성한 것처럼 되서 컴파일은 정상적으로 작동한다.
그러나 <code>@Override</code> 를 붙임으로써 작성한 메서드가 조상 클래스에 없는 클래스라고 컴파일러가 알려준다.</li>
</ul>
<h3 id="deprecated">@Deprecated</h3>
<p><strong>앞으로 사용하지 않을 것을 권장하는 필드나 메서드에 붙인다.</strong></p>
<pre><code class="language-java">class Parent {
    void parentMethod() { }
}

class Child extends Parent {
    @Override
    @Deprecated
    void parentMethod() { }
}

public class Ex12_7 {
    public static void main(String[] args) {
        Child child = new Child();
        child.parentMethod();
    }
}</code></pre>
<p><img src="https://velog.velcdn.com/images/jere-gim/post/9b874d2a-f2bf-4378-a03e-9cd61975dacc/image.png" alt=""></p>
<ul>
<li>cmd 에서 javac Ex12_7.java를 치면 <code>deprecated API</code>를 썼다는 경고문구가 뜬다. </li>
<li><code>javac -Xlint:deprecation Ex12_7.java</code>로 더 자세한 경고 내용을 볼 수 있다.
warning: [deprecation] parentMethod() in Child has been deprecated
child.parentMethod();</li>
</ul>
<h3 id="functionalinterface">@FunctionalInterface</h3>
<p><strong>함수형 인터페이스는 단 하나의 추상 메서드만 가질 수 있다. 이 애너테이션을 붙이면 컴파일러가 제대로 작성했는지 체크해준다.</strong></p>
<pre><code class="language-java">@FunctionalInterface
interface Testable {
    void test();
    void test2(); // 컴파일 에러
}</code></pre>
<ul>
<li><code>@FunctionalInterface</code> 이 애너테이션을 붙인 인터페이스가 2개 이상의 추상 메서드를 작성하면 에러가 난다.</li>
<li>함수형 인터페이스는 하나의 추상 메서드만 가질 수 있다.</li>
</ul>
<h3 id="suppresswarnings">@SuppressWarnings</h3>
<p><strong>컴파일러의 경고 메시지가 나타나지 않게 억제</strong></p>
<pre><code class="language-java">@SuppressWarnings(&quot;unchecked&quot;)
ArrayList list = new ArrayList();
list.add(1);</code></pre>
<ul>
<li><p>괄호 안에 억제하고자 하는 경고의 종류를 문자열로 지정한다.</p>
</li>
<li><p><code>@SuppressWarnings(&quot;unchecked&quot;)</code> : 제네릭스와 관련된 경고를 억제</p>
</li>
<li><p>경고가 실행에 영향을 주진 않지만 내가 이 경고를 보고 확인했다는 의미에서 이 애너테이션을 붙인다.</p>
</li>
</ul>
<p><strong>둘 이상의 경고를 동시에 억제하는 법</strong></p>
<pre><code class="language-java">@SuppressWarnings({&quot;deprecation&quot;, &quot;unchecked&quot;, &quot;varargs&quot;})</code></pre>
<h2 id="메타-애너테이션">메타 애너테이션</h2>
<p><strong>애너테이션을 정의할 때 사용하는 애터네이션. java.lang.annotation 패키지에 포함</strong></p>
<h3 id="target">@Target</h3>
<p><strong>애너테이션을 정의할 때, 적용 대상 지정에 사용된다.</strong></p>
<pre><code class="language-java">@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, MODULE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
    String[] value();
}</code></pre>
<ul>
<li><code>@SuppressWarnings</code> 애너테이션의 적용 대상 타입 설정해준다.</li>
</ul>
<table>
<thead>
<tr>
<th align="center">대상 타입</th>
<th align="center">설명</th>
</tr>
</thead>
<tbody><tr>
<td align="center">ANNOTAION_TYPE</td>
<td align="center">애너테이션</td>
</tr>
<tr>
<td align="center">CONSTRUCTOR</td>
<td align="center">생성자</td>
</tr>
<tr>
<td align="center">FIELD</td>
<td align="center">필드 (iv, cv)</td>
</tr>
<tr>
<td align="center">LOCAL_VARIABLE</td>
<td align="center">지역변수</td>
</tr>
<tr>
<td align="center">METHOD</td>
<td align="center">메서드</td>
</tr>
<tr>
<td align="center">PACKAGE</td>
<td align="center">패키지</td>
</tr>
<tr>
<td align="center">PARAMETER</td>
<td align="center">매개변수</td>
</tr>
<tr>
<td align="center">TYPE</td>
<td align="center">타입(class, interface)</td>
</tr>
<tr>
<td align="center">🔽 JDK1.8 이상부터 가능 🔽</td>
<td align="center">ㅡㅡㅡㅡㅡㅡㅡㅡㅡ</td>
</tr>
<tr>
<td align="center">TYPE_PARAMETER</td>
<td align="center">타입 매개변수</td>
</tr>
<tr>
<td align="center">TYPE_USE</td>
<td align="center">타입이 사용되는 모든 곳</td>
</tr>
</tbody></table>
<h3 id="retention">@Retention</h3>
<p><strong>애너테이션이 유지(retention)되는 기간을 정하는데 사용</strong></p>
<table>
<thead>
<tr>
<th align="left">유지 기준</th>
<th align="left"><center>의미</center></th>
</tr>
</thead>
<tbody><tr>
<td align="left">SOURCE</td>
<td align="left">소스파일에만 존재. 클래스 파일에 X(애너테이션 정보가 클래스 파일에 남아있지 않음.)</td>
</tr>
<tr>
<td align="left">CLASS(기본값)</td>
<td align="left">클래스 파일에 존재 -&gt; 실행시 사용 불가</td>
</tr>
<tr>
<td align="left">RUNTIME</td>
<td align="left">클래스 파일에 존재 -&gt; 실행시 사용 가능</td>
</tr>
</tbody></table>
<ul>
<li><code>CLASS</code>는 잘 쓰이지 않고 <code>SOURCE</code>와 <code>RUNTIME</code>이 자주 사용된다.</li>
</ul>
<p><strong>컴파일러에 의해 사용되는 애너테이션의 유지 정책은 <code>SOURCE</code> 이다.</strong></p>
<pre><code class="language-java">@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {}</code></pre>
<ul>
<li><code>@Override</code> 는 컴파일러가 오버라이딩 체크하고 끝이기 때문에 실행 시에 필요가 없다. 따라서 유지 정책이 클래스 파일에 존재하지 않는 <code>SOURCE</code> 이다.</li>
</ul>
<p><strong>실행 시 사용 가능한 애너테이션의 유지 정책은 <code>RUNTIME</code> 이다.</strong></p>
<pre><code class="language-java">@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface FunctionalInterface {}</code></pre>
<ul>
<li><code>RUNTIME</code> 유지 정책은 실행 시에만 존재하는게 아니라 컴파일 타임부터 실행 시까지 유지되는 것이다.</li>
</ul>
<h3 id="documented-inherited">@Documented, @Inherited</h3>
<p><strong><code>@Documented</code> : javadoc으로 작성한 문서에 포함시킬 때 붙인다.</strong></p>
<p><strong><code>@Inherited</code> : 애너테이션을 자손 클래스에 상속하고자할 때 붙인다.</strong></p>
<pre><code class="language-java">@Inherited
@interface MyAnno{}

@MyAnno
class Parent{}

class Child extends Parent{} // Parent클래스에 붙어있던 MyAnno가 동일하게 붙은 것으로 인식</code></pre>
<ul>
<li>Child 클래스에는 <code>@MyAnno</code>이 붙어있지 않지만 <code>@Inherited</code> 으로 인해 조상의 애너테이션을 상속받아 <code>@MyAnno</code>이 붙어있는 것으로 인식된다.</li>
</ul>
<h3 id="repeatable">@Repeatable</h3>
<p><strong>반복해서 붙일 수 있는 애너테이션을 정의할 때 사용</strong></p>
<pre><code class="language-java">// @ToDo를 @Repeatable으로 지정
@Repeatable(ToDos.class) 
@interface ToDo {
    String value();
}
// 여러번 반복해서 붙일 수 있다.
@ToDo(&quot;update codes&quot;)
@ToDo(&quot;override inherited methods&quot;)
class MyClass {
    ...
}</code></pre>
<ul>
<li><code>@Repeatable</code>이 붙은 애너테이션으 반복해서 붙일 수 있다.</li>
</ul>
<pre><code class="language-java">// @ToDo를 하나로 묶을 컨테이너 애너테이션
@interface ToDos {
    ToDo[] value(); // 반드시 value
}</code></pre>
<ul>
<li><code>@Repeatable</code>로 반복해서 붙일 수 있도록 정의한 애너테이션을 하나로 묶을 컨테이너 애너테이션도 정의해줘야 한다.</li>
</ul>
<h2 id="애너테이션-타입-정의하기">애너테이션 타입 정의하기</h2>
<pre><code class="language-java">@interface 애너테이션 이름 {
    타입 요소이름();
    ...
}</code></pre>
<p><strong>애너테이션의 메서드는 추상 메서드이며, 애너테이션을 적용할 때 지정(순서X)</strong></p>
<pre><code class="language-java">enum TestType { FIRST, FINAL }

@interface DateTime {
    String yymmdd() ;
    String hhmmss() ;
}

@interface Test {
    int count(); // 정수
    String testedBy() ; // 문자열
    String[] testTools() ; // 문자열 배열
    TestType testType(); // Enum
    DateTime testDate(); // 다른 애너테이션 
}</code></pre>
<ul>
<li>애너테이션의 추상 메서드 타입으로 enum이 올 수 있고, 다른 애너테이션이 올 수 있다.<pre><code class="language-java">@Test(
  count = 3,
  testedBy = &quot;Jung&quot;,
  testTools = {&quot;JUnit&quot;, &quot;AutoTester&quot;},
  testType = TestType.FIRST 
  testDate = @DateTime(yymmdd=&quot;&quot;, hhmmss=&quot;&quot;)
)
public class NewTest {
  ... // Test 애너테이션 사용가능
}</code></pre>
</li>
<li>요소의 타입과 이름에 맞게 값을 지정해주고 순서는 상관 없다. 다만, 모든 요소에 대해 값을 적용해줘야 한다.</li>
</ul>
<h2 id="애너테이션의-요소">애너테이션의 요소</h2>
<p><strong>적용 시 값을 지정하지 않으면, 사용될 수 있는 기본값 지정 가능(null 제외)</strong></p>
<pre><code class="language-java">@interface Test {
    int count() default 1;
}

@Test
public class NewTest {
    ... // Test 애너테이션 사용가능
}</code></pre>
<ul>
<li>원래는 <code>@Test(count=1)</code> 이런 식으로 애너테이션의 정의된 요소의 값을 전부 정의해줘야 한다. 하지만 애너테이션을 생성할 때 default 값을 줬다면 <code>@Test</code> 이런 식으로만 써줘도 된다.</li>
</ul>
<p><strong>요소가 하나이고 이름이 value일 때는 요소의 이름을 생략할 수 있다.</strong></p>
<pre><code class="language-java">@interface Test {
    String value();
}

@Test(&quot;passed&quot;)
public class NewTest {
    ... // Test 애너테이션 사용가능
}</code></pre>
<ul>
<li>원래는 <code>@Test(value=&quot;passed&quot;)</code> 라고 적어줘야 하지만 요소가 하나이고 그 요소의 이름이 <code>value</code>일 때는 요소의 이름을 생략하고 값만 적어줘도 된다.</li>
</ul>
<p><strong>요소의 타입이 배열인 경우, 중괄호{}를 사용해야 한다.</strong></p>
<pre><code class="language-java">@interface Test {
    String[] testTools();
}

@Test(testTools={&quot;JUnit&quot;, &quot;Auto&quot;})
public class NewTest {
    ... // Test 애너테이션 사용가능
}</code></pre>
<ul>
<li><p><code>@Test(testTools={&quot;JUnit&quot;, &quot;Auto&quot;})</code> : 배열의 값을 적어줄 때 중괄호를 적어줘야 한다.</p>
</li>
<li><p><code>@Test(testTools=&quot;JUnit&quot;)</code> : 배열의 값이 1개일 때는 중괄호 생략 가능</p>
</li>
<li><p><code>@Test(testTools={})</code> : 배열에 값이 없을 때는 반드시 빈 중괄호<code>{}</code>를 적어줘야 한다.</p>
</li>
</ul>
<h2 id="모든-애너테이션의-조상">모든 애너테이션의 조상</h2>
<p><strong>Annotation 인터페이스는 모든 애너테이션의 조상이지만 상속은 불가하다.</strong></p>
<pre><code class="language-java">package java.lang.annotation;

public interface Annotation {
    boolean equals(Object obj);
    int hashCode();
    String toString();

    Class&lt;? extends Annotation&gt; annotationType();
}</code></pre>
<ul>
<li>Annotation 인터페이스는 4개의 추상 메서드를 가지고 있고 모든 애너테이션은 구현하지 않아도 이 4개의 추상 메서드를 사용할 수 있다.</li>
<li><code>Class&lt;? extends Annotation&gt; annotationType();</code> : 애너테이션의 타입을 반환</li>
</ul>
<h2 id="마커-애너테이션">마커 애너테이션</h2>
<p><strong>요소가 하나도 정의되지 않은 애너테이션</strong></p>
<pre><code class="language-java">@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {} // 정의된 요소가 하나도 없다.</code></pre>
<ul>
<li><code>@Override</code> : 대표적인 마커 애너테이션</li>
</ul>
<h2 id="애너테이션-요소의-규칙">애너테이션 요소의 규칙</h2>
<p><strong>애너테이션의 요소를 선언할 때 지켜야할 규칙들</strong></p>
<ul>
<li><p>요소의 타입은 기본형, String, enum, 애너테이션, Class(설계도 객체)만 허용된다.</p>
</li>
<li><p>괄호()안에 매개변수를 선언할 수 없다.</p>
</li>
<li><p>예외를 선언할 수 없다.</p>
</li>
<li><p>요소를 타입 매개변수로 정의할 수 없다.</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[자바의 정석 기초편] 제네릭스]]></title>
            <link>https://velog.io/@jere-gim/%EC%9E%90%EB%B0%94%EC%9D%98-%EC%A0%95%EC%84%9D-%EA%B8%B0%EC%B4%88%ED%8E%B8-%EC%A0%9C%EB%84%A4%EB%A6%AD%EC%8A%A4</link>
            <guid>https://velog.io/@jere-gim/%EC%9E%90%EB%B0%94%EC%9D%98-%EC%A0%95%EC%84%9D-%EA%B8%B0%EC%B4%88%ED%8E%B8-%EC%A0%9C%EB%84%A4%EB%A6%AD%EC%8A%A4</guid>
            <pubDate>Thu, 09 Mar 2023 08:00:23 GMT</pubDate>
            <description><![CDATA[<h1 id="📌제네릭스generics">📌제네릭스(Generics)</h1>
<p><strong>: 컴파일 시 타입을 체크해주는 기능</strong></p>
<pre><code class="language-java">ArrayList&lt;Tv&gt; tvList = new ArrayList&lt;&gt;();

tvList.add(new Tv());
tvList.add(new DVD()); // 컴파일 에러</code></pre>
<ul>
<li><code>&lt;Tv&gt;</code> : ArrayList에 Tv 객체 타입만 저장할 수 있게 설정해주는 제네릭스이다.</li>
</ul>
<p><strong>객체의 타입 안정성을 높이고 형변환의 번거로움을 줄여준다.</strong></p>
<pre><code class="language-java">ArrayList list = new ArrayList();
list.add(10);
list.add(&quot;20&quot;); // String 추가

Integer i = (Integer) list.get(1); // 컴파일 OK</code></pre>
<ul>
<li><code>(Integer) list.get(1)</code> 은 컴파일 에러가 나지 않는다. <code>get()</code> 메서드의 반환 타입이 Object라서 그 안의 값이 String 타입인지 Integer 타입인지 컴파일러가 구분할 수 없기 때문이다. 
그러나 실행 시에 형변환 에러인 <code>ClassCastException</code> 예외가 발생하게 된다. 실행 시에 발생하는 예외를 제네릭스들 통해 컴파일에게 타입의 정보를 알려줌으로써 컴파일 때 예외를 체크할 수 있게 해준다. 이로써, 객체의 타입 안정성을 높일 수 있다.</li>
</ul>
<pre><code class="language-java">ArrayList&lt;Tv&gt; tvList = new ArrayList&lt;&gt;();

tvList.add(new Tv());
Tv t = tvList.get(0);</code></pre>
<ul>
<li><code>get()</code> 메서드는 반환 타입이 Object라서 <code>Tv t = (Tv) tvList.get(0);</code> 이런 식으로 (Tv)를 붙여 형변환을 해줘야 한다. 
하지만 tvList는 제네릭스를 통해 Tv 객체만 저장하도록 설정해줬기 때문에 tvList에 저장되어 있는 객체들은 모두 Tv 타입인 걸 알 수 있다. 따라서, 형변환을 해주지 않아도 된다.</li>
</ul>
<hr>
<h1 id="📌타입-변수">📌타입 변수</h1>
<p><strong>클래스를 작성할 때, Object 타입 대신 타입 변수<code>&lt;E&gt;</code>를 선언해서 사용</strong></p>
<p><strong>객체 생성 시, 타입 변수 대신 실제 타입을 대입</strong></p>
<pre><code class="language-java">public class ArrayList&lt;E&gt; ... {
    public boolean add(E e) {
        modCount++;
        add(e, elementData, size);
        return true;
    }
    ...
        public E get(int index) {
        Objects.checkIndex(index, size);
        return elementData(index);
    }
}</code></pre>
<ul>
<li><code>ArrayList&lt;E&gt;</code> 클래스 안의 <code>add(E e)</code> 메서드는 매개변수가 타입 변수 <code>&lt;E&gt;</code>로 되어 있고 <code>public E get(int index)</code> 메서드는 반환 타입이 타입 변수 <code>&lt;E&gt;</code>로 되어 있다. </li>
</ul>
<h2 id="제네릭-타입과-다형성">제네릭 타입과 다형성</h2>
<p><strong>참조 변수와 생성자의 대입된 타입은 일치해야 한다.</strong></p>
<pre><code class="language-java">class Product {}
class Tv extends Produce {}
...
ArrayList&lt;Tv&gt; list = new ArrayList&lt;Tv&gt;();
ArrayList&lt;Product&gt; list = new ArrayList&lt;Tv&gt;(); // 에러</code></pre>
<ul>
<li>대입된 타입은 다형성이 성립 안되고 무조건 일치해야 한다.</li>
</ul>
<p><strong>제네릭 클래스간의 다형성은 성립된다.</strong></p>
<pre><code class="language-java">List&lt;Product&gt; list = new ArrayList&lt;Product&gt;();
List&lt;Product&gt; list = new LinkedList&lt;Product&gt;(); // OK</code></pre>
<ul>
<li>ArrayList, LinkedList가 List 인터페이스를 구현했기 때문에 다형성 성립</li>
</ul>
<p><strong>매개변수의 다형성도 성립된다.</strong></p>
<pre><code class="language-java">ArrayList&lt;Product&gt; list = new ArrayList&lt;Product&lt;();

list.add(new Product());
list.add(new Tv()); // OK</code></pre>
<ul>
<li><code>list.add(new Tv());</code> : 매개변수 타입이 Product이기 때문에 다형성에 의해 그 자손들도 매개변수 타입으로 올 수 있다.</li>
</ul>
<p><strong>반환타입의 다형성 성립(형변환 필요)</strong></p>
<pre><code class="language-java">ArrayList&lt;Product&gt; list = new ArrayList&lt;Product&lt;();

list.add(new Product());
list.add(new Tv()); // OK

Product p = list.get(0);
Tv t = (Tv) list.get(1);</code></pre>
<ul>
<li><code>Tv t = (Tv) list.get(1);</code> : list.get(1)의 반환타입은 Product기 때문에 (Tv)로 형변환을 해줘야 한다.</li>
</ul>
<hr>
<h1 id="📌제한된-제네릭-클래스">📌제한된 제네릭 클래스</h1>
<p><strong>extends로 대입할 수 있는 타입을 제한</strong></p>
<pre><code class="language-java">class FruitBox&lt;T extends Fruit&gt; {}
...
interface Eatable {}
class FruitBox&lt;T extends Eatable&gt; {}</code></pre>
<ul>
<li><code>&lt;T extends Fruit&gt;</code> : Fruit과 Fruit의 자손만 타입으로 지정할 수 있게 제한</li>
<li><code>&lt;T extends Eatable&gt;</code> : 타입을 제한할 때 인터페이스도 <code>implements</code>가 아닌 <code>extends</code>를 쓴다.(주의)</li>
</ul>
<hr>
<h1 id="📌제네릭스의-제약">📌제네릭스의 제약</h1>
<p><strong>타입 변수에 대입은 인스턴스별로 다르게 가능하다.</strong></p>
<pre><code class="language-java">class FruitBox&lt;T extends Fruit&gt; {}
class Fruit {}
class Apple extends Fruit {}
class Grape extends Fruit {}
...
FruitBox&lt;Fruit&gt; fruitBox = new FruitBox&lt;&gt;();
FruitBox&lt;Apple&gt; appleBox = new FruitBox&lt;&gt;(); // OK
FruitBox&lt;Grape&gt; grapeBox = new FruitBox&lt;&gt;(); // OK</code></pre>
<p><strong>static 멤버에는 타입 변수 사용 불가</strong></p>
<pre><code class="language-java">class Box&lt;T&gt; {
    static T item; // 에러
    static void add(T item) {} // 에러
}</code></pre>
<ul>
<li>이유 : static이 붙으면 모든 인스턴스에 공통으로 사용이 가능하기 때문에 인스턴스마다 다르게 대입이 가능한 제네릭스는 사용이 불가하다.</li>
</ul>
<p><strong>객체(배열) 생성할 때 타입 변수 사용 불가. 타입 변수로 배열 선언은 가능</strong></p>
<pre><code class="language-java">T[] itemArr; // OK

T[] tmpArr = new T(itemArr.length); // 사용 불가</code></pre>
<ul>
<li>new 연산자 다음에는 확정된 타입이 와야 한다. </li>
</ul>
<hr>
<h1 id="📌와일드-카드-">📌와일드 카드 <code>&lt;?&gt;</code></h1>
<pre><code class="language-java">class Product {}
class Tv extends Product {}
...
ArrayList&lt;? extends Product&gt; list = new ArrayList&lt;Tv&gt;(); // OK
ArrayList&lt;? super Tv&gt; list = new ArrayList&lt;Product&gt;(); // OK
ArrayList&lt;?&gt; list = new ArrayList&lt;Object&gt;();</code></pre>
<ul>
<li><code>&lt;? extends T&gt;</code> : T와 그 자손들만 가능</li>
<li><code>&lt;? super T&gt;</code> : T와 그 조상들만 가능</li>
<li><code>&lt;?&gt;</code> : 제한 없음. <code>&lt;? extends Object&gt;</code>와 동일</li>
</ul>
<p><strong>메서드의 매개변수에 와일드 카드 사용</strong></p>
<pre><code class="language-java">class Juicer {
    static Juice makeJuice(FruitBox&lt;? extends Fruit&gt; box) {}
    ...
}
...
Juicer.makeJuice(new FruitBox&lt;Fruit&gt;()); // OK
Juicer.makeJuice(new FruitBox&lt;Apple&gt;()); // OK</code></pre>
<hr>
<h1 id="📌제네릭-메서드">📌제네릭 메서드</h1>
<p><strong>: 제네릭 타입이 선언된 메서드(타입 변수는 메서드 내에서만 유효)</strong></p>
<pre><code class="language-java">class FruitBox&lt;T&gt; {
    static &lt;T&gt; void sort(List&lt;T&gt;) {}
    ...
}</code></pre>
<ul>
<li>클래스 <code>FruitBox&lt;T&gt;</code>의 타입변수 <code>&lt;T&gt;</code>와 메서드 <code>static &lt;T&gt; void sort(List&lt;T&gt;)</code> 의 타입변수 <code>&lt;T&gt;</code> 은 별개의 타입변수이다.</li>
</ul>
<p><strong>메서드를 호출할 때마다 타입을 대입해야 한다.(대부분 생략 가능)</strong></p>
<pre><code class="language-java">static &lt;T extends Fruit&gt; Juice makeJuice(FruitBox&lt;T&gt; box) {}
...

FruitBox&lt;Fruit&gt; fruitBox = new FruitBox&lt;&gt;();
FruitBox&lt;Apple&gt; appleBox = new FruitBox&lt;&gt;();

System.out.println(Juicer.&lt;Fruit&gt;makeJuice(fruitBox));
System.out.println(Juicer.&lt;Apple&gt;makeJuice(appleBox));</code></pre>
<ul>
<li><p><code>makeJuice(FruitBox&lt;T&gt; box)</code> : makeJuice 메서드는 <code>&lt;T extends Fruit&gt;</code> 타입을 제한하고 있기 때문에 <code>FruitBox&lt;T&gt;</code> 에서 <code>&lt;T&gt;</code>에는 Fruit과 그 자손만 올 수 있다.</p>
</li>
<li><p><code>Juicer.&lt;Fruit&gt;makeJuice(fruitBox)</code> 여기서 <code>&lt;Fruit&gt;</code> 생략 가능하다.
참조변수 fruitBox 생성할 때 이미 <code>&lt;Fruit&gt;</code> 타입인지 알고있기 때문이다.</p>
</li>
</ul>
<h2 id="와일드카드가-사용된-메서드와-비교">와일드카드가 사용된 메서드와 비교</h2>
<pre><code class="language-java">// 와일드카드가 사용된 메서드
static Juice makeJuice(FruitBox&lt;? extends Fruit&gt; box) {}
// 제네릭 메서드
static &lt;T extends Fruit&gt; Juice makeJuice(FruitBox&lt;T&gt; box) {}</code></pre>
<ul>
<li><p>와일드카드가 사용된 메서드는 하나의 참조변수로 서로 다른 타입이 대입된 여러 제네릭 객체를 다루기 위한 것</p>
</li>
<li><p>제네릭 메서드는 메서드를 호출할 때마다 다른 제네릭 타입을 대입할 수 있게 한 것</p>
</li>
<li><p>둘은 서로 다른 메서드지만 기능은 똑같다. 그래서 와일드카드를 쓸 수 없을 때 제네릭 메서드를 사용한다.</p>
</li>
</ul>
<hr>
<h1 id="📌제네릭-타입의-형변환">📌제네릭 타입의 형변환</h1>
<p><strong>제네릭 타입과 원시 타입간의 형변환은 가능하지만 바람직하지 않다.(경고 발생)</strong></p>
<pre><code class="language-java">Box b = null;
Box&lt;String&gt; bStr = null;

b = (Box) bStr; // 가능하지만 경고 발생
bStr = (Box&lt;String&gt;) b; // 가능하지만 경고 발생</code></pre>
<ul>
<li>원시 타입 자체를 쓰지 않는게 좋다.</li>
</ul>
<p><strong>제네릭 타입 간의 형변환 불가능. 와일드 카드가 사용된 제네릭 타입은 형변환 가능</strong></p>
<pre><code class="language-java">Box&lt;Object&gt; bObj = new Box&lt;String&gt;(); // 에러

Box&lt;? extends Object&gt; bWild = new Box&lt;String&gt;(); // OK</code></pre>
<ul>
<li><code>Box&lt;? extends Object&gt; bWild = new Box&lt;String&gt;();</code> 이 문장은 양변의 타입이 불일치하기 때문에 형변환을 해줘야한다. 
사실 <code>Box&lt;? extends Object&gt; bWild = (Box&lt;? extends Object&gt;) new Box&lt;String&gt;();</code> 이 문장에서 형변환해주는 <code>(Box&lt;? extends Object&gt;)</code> 이게 생략되어있는 것이다.</li>
</ul>
<hr>
<h1 id="📌제네릭-타입의-제거">📌제네릭 타입의 제거</h1>
<p><strong>컴파일러는 제네릭 타입을 제거하고, 필요한 곳에 형변환을 넣는다.</strong></p>
<pre><code class="language-java">class  Box&lt;T extends Fruit&gt; {
    void add(T t) {};
}</code></pre>
<p>컴파일🔽</p>
<pre><code class="language-java">class  Box {
    void add(Fruit t) {};
}</code></pre>
<ul>
<li><p>기본적으로는 컴파일 하면 <code>&lt;T&gt;</code> -&gt; Object로 바뀌지만 제한된 제네릭 타입은 조상 타입으로 바뀐다.</p>
</li>
<li><p>자바는 하위호환성(안정성)을 중요하게 생각하기 때문에 제네릭스가 나오기 전에 Object를 쓴 것처럼 컴파일을 하면 컴파일러가 제네릭 타입을 제거하고 Object로 바꾼다.</p>
</li>
</ul>
<pre><code class="language-java">T get(int i) {
    return list.get(i);
}</code></pre>
<p>컴파일🔽</p>
<pre><code class="language-java">Fruit get(int i) {
    return (Fruit)list.get(i);
}</code></pre>
<ul>
<li>제네릭 타입을 쓰면 형변환을 생략해도 된다는 장점이 있다. 그러나 컴파일을 하면 컴파일러가 자동으로 제네릭 타입을 제거하고 형변환을 추가한다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[자바의 정석 기초편] 컬렉션 프레임워크 2]]></title>
            <link>https://velog.io/@jere-gim/%EC%9E%90%EB%B0%94%EC%9D%98-%EC%A0%95%EC%84%9D-%EA%B8%B0%EC%B4%88%ED%8E%B8-%EC%BB%AC%EB%A0%89%EC%85%98-%ED%94%84%EB%A0%88%EC%9E%84%EC%9B%8C%ED%81%AC-2</link>
            <guid>https://velog.io/@jere-gim/%EC%9E%90%EB%B0%94%EC%9D%98-%EC%A0%95%EC%84%9D-%EA%B8%B0%EC%B4%88%ED%8E%B8-%EC%BB%AC%EB%A0%89%EC%85%98-%ED%94%84%EB%A0%88%EC%9E%84%EC%9B%8C%ED%81%AC-2</guid>
            <pubDate>Tue, 07 Mar 2023 12:44:16 GMT</pubDate>
            <description><![CDATA[<h1 id="📌arrays---배열을-다루기-편리한-메서드static-제공">📌Arrays - 배열을 다루기 편리한 메서드(static) 제공</h1>
<h2 id="tostring-deeptostring">toString(), deepToString()</h2>
<p><strong>: 배열의 출력. 다양한 타입의 배열을 문자열 형태로 출력</strong></p>
<pre><code class="language-java">int[] arr = {0,1,2,3,4};
int[][]    arr2D = {{11,12,13}, {21,22,23}};

System.out.println(&quot;arr = &quot;+Arrays.toString(arr));
System.out.println(&quot;arr2D = &quot;+Arrays.deepToString(arr2D));</code></pre>
<blockquote>
<p>arr = [0, 1, 2, 3, 4]
arr2D = [[11, 12, 13], [21, 22, 23]]</p>
</blockquote>
<ul>
<li>deepToString() : 다차원 배열을 출력할 때 사용</li>
</ul>
<h2 id="copyof-copofrange">copyOf(), copOfRange()</h2>
<p><strong>: 배열의 복사. 새로운 배열을 생성해서 반환</strong></p>
<pre><code class="language-java">int[] arr2 = Arrays.copyOf(arr, arr.length);
int[] arr3 = Arrays.copyOf(arr, 3);
int[] arr4 = Arrays.copyOf(arr, 7);
int[] arr5 = Arrays.copyOfRange(arr, 2, 4);
int[] arr6 = Arrays.copyOfRange(arr, 0, 7);

System.out.println(&quot;arr2 = &quot; + Arrays.toString(arr2));
System.out.println(&quot;arr3 = &quot; + Arrays.toString(arr3));
System.out.println(&quot;arr4 = &quot; + Arrays.toString(arr4));
System.out.println(&quot;arr5 = &quot; + Arrays.toString(arr5));
System.out.println(&quot;arr6 = &quot; + Arrays.toString(arr6));</code></pre>
<blockquote>
<p>arr2 = [0, 1, 2, 3, 4]
arr3 = [0, 1, 2]
arr4 = [0, 1, 2, 3, 4, 0, 0]
arr5 = [2, 3]
arr6 = [0, 1, 2, 3, 4, 0, 0]</p>
</blockquote>
<ul>
<li><code>Arrays.copyOfRange(arr, 0, 7);</code> : 뒤에 남는 나머지 공간은 0으로 채운다.</li>
</ul>
<h2 id="fill-setall">fill(), setAll()</h2>
<p><strong>: 배열 채우기</strong></p>
<pre><code class="language-java">int[] arr7 = new int[5];
Arrays.fill(arr7, 9);
System.out.println(&quot;arr7 = &quot; + Arrays.toString(arr7));

Arrays.setAll(arr7, i -&gt; (int) (Math.random() * 6) + 1);
System.out.println(&quot;arr7 = &quot; + Arrays.toString(arr7));</code></pre>
<blockquote>
<p>arr7 = [9, 9, 9, 9, 9]
arr7 = [5, 3, 6, 2, 5]</p>
</blockquote>
<ul>
<li><code>Arrays.fill(arr7, 9);</code> : arr7 배열을 9로 채운다.</li>
<li><code>i -&gt; (int) (Math.random() * 6) + 1</code> : 람다식. 1~6까지의 난수로 채운다.</li>
</ul>
<h2 id="sort-binarysearch">sort(), binarySearch()</h2>
<p><strong>: 배열의 정렬과 탐색. <code>binarySearch()</code> - 이진 탐색</strong></p>
<pre><code class="language-java">char[] chArr = {&#39;C&#39;, &#39;A&#39;, &#39;D&#39;, &#39;B&#39;, &#39;E&#39;};

System.out.println(&quot;chArr = &quot; + Arrays.toString(chArr));
System.out.println(&quot;index of B = &quot; + Arrays.binarySearch(chArr, &#39;B&#39;));
System.out.println(&quot;= After sorting = &quot;);
Arrays.sort(chArr);
System.out.println(&quot;chArr = &quot; + Arrays.toString(chArr));
System.out.println(&quot;index of B = &quot; + Arrays.binarySearch(chArr, &#39;B&#39;));</code></pre>
<blockquote>
<p>chArr = [C, A, D, B, E]
index of B = -1
= After sorting = 
chArr = [A, B, C, D, E]
index of B = 1</p>
</blockquote>
<ul>
<li><code>Arrays.binarySearch(chArr, &#39;B&#39;);</code> : 결과는 -1. <code>binarySearch()</code> 는 정렬된 배열에서만 사용 가능하다.</li>
<li><code>Arrays.sort(chArr);</code> 로 정렬 후 <code>Arrays.binarySearch(chArr, &#39;B&#39;);</code> 하니까 제대로 된 결과인 1이 나온다.</li>
</ul>
<h3 id="순차-탐색과-이진-탐색">순차 탐색과 이진 탐색</h3>
<p><strong>순차 탐색 : 정렬하지 않고 처음부터 순서대로 탐색하는 방법</strong></p>
<ul>
<li>10개의 요소 중 평균 5.5회 만에 탐색한다.</li>
</ul>
<p><strong>이진 탐색 : 정렬 후에 반복적으로 반으로 나눠서 탐색하는 방법</strong></p>
<ul>
<li>반으로 나눈 후 가운데 요소와 찾는 요소와 비교해서 계속 범위를 줄여나간다.</li>
<li>10개의 요소 중 평균 3~4번 만에 탐색한다.</li>
<li>탐색을 하기 전 정렬을 해야만 한다는 단점이 있다.</li>
</ul>
<h2 id="deepequals">deepEquals()</h2>
<p><strong>: 다차원 배열의 비교</strong></p>
<pre><code class="language-java">String[][] str2D = new String[][]{{&quot;aaa&quot;, &quot;bbb&quot;}, {&quot;AAA&quot;, &quot;BBB&quot;}};
String[][] str2D2 = new String[][]{{&quot;aaa&quot;, &quot;bbb&quot;}, {&quot;AAA&quot;, &quot;BBB&quot;}};

System.out.println(Arrays.equals(str2D, str2D2));     
System.out.println(Arrays.deepEquals(str2D, str2D2)); </code></pre>
<blockquote>
<p>false
true</p>
</blockquote>
<ul>
<li>다차원 배열을 비교할 땐 <code>equals()</code> 대신 <code>deepEquals()</code> 사용</li>
</ul>
<h2 id="aslistobject-a">asList(Object... a)</h2>
<p><strong>: 배열을 List로 변환. (Object... a) - 갯수가 정해져 있지 않은 가변 매개변수</strong></p>
<pre><code class="language-java">List list = Arrays.asList(1,2,3,4,5);
list.add(6); // UnsupportedOperationException 예외 발생

List list = new ArrayList(Arrays.asList(1,2,3,4,5));
list.add(6); // 가능</code></pre>
<ul>
<li><p><code>List list = Arrays.asList(1,2,3,4,5);</code> : 읽기만 가능하다. <code>add()</code> 메서드 사용 불가</p>
</li>
<li><p><code>new ArrayList(Arrays.asList(1,2,3,4,5));</code> : List의 내용을 복사해서 List를 생성자로 하는 new ArrayList를 새로 만들면 변경이 가능하다.</p>
</li>
</ul>
<hr>
<h1 id="📌comparator와-comparable">📌Comparator와 Comparable</h1>
<p><strong>객체 정렬에 필요한 메서드(정렬 기준 제공)를 정의한 인터페이스</strong></p>
<ul>
<li>Comparable : 기본 정렬 기준을 구현하는데 사용</li>
<li>Comparator : 기본 정렬 기준 외에 다른 기준으로 정렬할 때 사용</li>
</ul>
<pre><code class="language-java">public interface Comparable {
    int compareTo(Object o);
}

public interface Comparator {
    int compare(Object o1, Object o2);
}</code></pre>
<ul>
<li><code>int compareTo(Object o);</code> : 주어진 객체(o)를 자신과 비교</li>
<li><code>int compare(Object o1, Object o2);</code> : o1, o2 두 객체를 비교</li>
<li>반환 타입이 int인 이유<ul>
<li>0이면 두 객체가 같다.</li>
<li>양수면 왼쪽이 크다.</li>
<li>음수면 오른쪽이 크다.</li>
</ul>
</li>
</ul>
<p><strong>정렬은 1. 두 객체를 비교 2. 자리 바꿈 이 2가지를 반복하는 것이다.</strong></p>
<h2 id="실습-예제">실습 예제</h2>
<pre><code class="language-java">class Ex11_7 {
    public static void main(String[] args) {
        String[] strArr = {&quot;cat&quot;, &quot;Dog&quot;, &quot;lion&quot;, &quot;tiger&quot;};

        Arrays.sort(strArr); // String의 Comparable 구현에 의한 정렬
        System.out.println(&quot;strArr=&quot; + Arrays.toString(strArr));

        Arrays.sort(strArr, String.CASE_INSENSITIVE_ORDER); // 대소문자 구분안함
        System.out.println(&quot;strArr=&quot; + Arrays.toString(strArr));

        Arrays.sort(strArr, new Descending()); // 역순 정렬
        System.out.println(&quot;strArr=&quot; + Arrays.toString(strArr));
    }
}

class Descending implements Comparator { 
    public int compare(Object o1, Object o2){
        if( o1 instanceof Comparable &amp;&amp; o2 instanceof Comparable) {
            Comparable c1 = (Comparable)o1;
            Comparable c2 = (Comparable)o2;
            return c1.compareTo(c2) * -1 ;
        }
        return -1;
    } 
}</code></pre>
<blockquote>
<p>strArr=[Dog, cat, lion, tiger]
strArr=[cat, Dog, lion, tiger]
strArr=[tiger, lion, cat, Dog]</p>
</blockquote>
<ul>
<li><p><code>Arrays.sort(strArr);</code> : 정렬 기준은 String의 Comparable 구현에 의한 정렬이다. 객체 배열에 저장된 객체(strArr)가 정렬 기준을 구현하고 있어야지만 정렬 기준을 생략할 수 있다. </p>
</li>
<li><p><code>strArr=[Dog, cat, lion, tiger]</code> : Comparable 인터페이스는 기본 정렬 기준을 제공한다. 문자열의 기본 정렬 기준을 사전순서이다. 따라서 대문자가 먼저 오고 그 다음 알파벳 순서대로 정렬이 된다.</p>
</li>
<li><p><code>Arrays.sort(strArr, String.CASE_INSENSITIVE_ORDER);</code> : 정렬 기준은 <code>String.CASE_INSENSITIVE_ORDER</code> - 대소문자 구분안하는 정렬 기준</p>
</li>
</ul>
<pre><code class="language-java">class Descending implements Comparator { 
    public int compare(Object o1, Object o2){
        if( o1 instanceof Comparable &amp;&amp; o2 instanceof Comparable) {
            Comparable c1 = (Comparable)o1;
            Comparable c2 = (Comparable)o2;
            return c1.compareTo(c2) * -1; // c2.compareTo(c1); 도 가능
        }
        return -1;
    } 
}</code></pre>
<ul>
<li><p>Comparator 인터페이스의 <code>compare(Object o1, Object o2)</code> 메서드를 오버라이딩해서 구현해준다.</p>
</li>
<li><p><code>c1.compareTo(c2) * -1;</code> : 기본 정렬 기준에 * -1을 해줌으로써 오름차순이었던 정렬 기준을 내림차순으로 바꿔준다.</p>
</li>
<li><p><code>c2.compareTo(c1);</code> 으로 해도 정렬 기준이 내림차순으로 바뀐다.</p>
</li>
</ul>
<h2 id="integer와-comparable">Integer와 Comparable</h2>
<pre><code class="language-java">public final class Integer extends Number implements Comparable&lt;Integer&gt; {
    ...
    public int compareTo(Integer anotherInteger) {
        return compare(this.value, anotherInteger.value);
    }
    public static int compare(int x, int y) {
        return (x &lt; y) ? -1 : ((x == y) ? 0 : 1);
    }
    ...
}</code></pre>
<ul>
<li><p><code>return (x &lt; y) ? -1 : ((x == y) ? 0 : 1);</code> : 비교하는 값이 같으면 0, 오른쪽(y)이 크면 -1, 왼쪽(x)이 크면 1 반환</p>
</li>
<li><p>Integer, String, Double 등의 클래스들은 기본 정렬 기준을 제공하는 Comparable 인터페이스를 구현하고 있다. 정렬 기준이 없을 때 기본 정렬 기준을 제공한다.</p>
</li>
<li><p>정렬 방법은 이미 코드가 잘 짜여져 있다.(불변) 따라서, 정렬 기준(가변)만 우리가 제공해주면 된다.</p>
</li>
</ul>
<hr>
<h1 id="📌hashset---순서x-중복x">📌HashSet - 순서X, 중복X</h1>
<p><strong>: Set 인터페이스를 구현한 대표적인 컬렉션 클래스</strong></p>
<ul>
<li>순서를 유지하려면 LinkedHashSet 클래스를 사용</li>
</ul>
<p><strong>TreeSet</strong></p>
<ul>
<li>범위 검색과 정렬에 유리한 컬렉션 클래스</li>
<li>HashSet 보다 데이터 추가, 삭제에 시간이 더 걸린다.</li>
</ul>
<h2 id="hashset의-메서드">HashSet의 메서드</h2>
<h3 id="생성자">생성자</h3>
<ul>
<li><p>HashSet() : 기본 생성자</p>
</li>
<li><p>HashSet(Collection c) : 컬렉션끼리 변환할 때 사용하는 생성자</p>
</li>
<li><p>HashSet(int initialCapacity) : 초기 용량 정해주는 생성자</p>
</li>
<li><p>HashSet(int initialCapacity, float loadFactor) : loadFactor 만큼 용량이 차면 용량을 x2 해준다.(loadFactor = 0.8 -&gt; 80% 찼을 때)</p>
</li>
</ul>
<h3 id="주요-메서드">주요 메서드</h3>
<ul>
<li><p>boolean add(Object o) : 추가</p>
</li>
<li><p>boolean addAll(Collection c) : 합집합</p>
</li>
<li><p>boolean remove(Object o) : 삭제</p>
</li>
<li><p>boolean removeAll(Collection c) : 차집합</p>
</li>
<li><p>boolean retainAll(Collection c) : 교집합</p>
</li>
<li><p>void clear() : 전체 삭제</p>
</li>
<li><p>boolean contains(Object o) : 객체(o)를 포함하고 있는지</p>
</li>
<li><p>boolean containsAll(Collection c) : 컬렉션(c)에 담긴 객체들이 모두 포함하고 있는지</p>
</li>
<li><p>Iterator iterator() : 컬렉션의 요소들을 읽어오는 메서드</p>
</li>
<li><p>boolean isEmpty() : 비어있으면 true</p>
</li>
<li><p>int size() : 저장된 객체의 개수 반환</p>
</li>
<li><p>Object[] toArray() : Set에 저장되어 있는 객체를 객체 배열로 반환</p>
</li>
<li><p>Object[] toArray(Object[] a) : Set에 저장되어 있는 객체를 객체 배열로 반환</p>
</li>
</ul>
<h2 id="실습-예제-1">실습 예제</h2>
<pre><code class="language-java">Object[] objArr = {&quot;1&quot;, new Integer(1), &quot;2&quot;, &quot;3&quot;, &quot;3&quot;, &quot;4&quot;, &quot;4&quot;};
Set set = new HashSet();

for (int i = 0; i &lt; objArr.length; i++) {
    set.add(objArr[i]);
}

System.out.println(set);</code></pre>
<blockquote>
<p>[1, 1, 2, 3, 4]</p>
</blockquote>
<ul>
<li><p>중복은 전부 제거되었다.</p>
</li>
<li><p>1이 2개 있는 이유는 하나는 문자열 &quot;1&quot; 이고 나머지 하나는 new Integer(1) 이기 때문이다.</p>
</li>
</ul>
<p><strong>로또 번호 6자리 출력하는 예제</strong></p>
<pre><code class="language-java">Set set = new HashSet();

for (int i = 0; i &lt; set.size() &lt; 6; i++) {
    int num = (int)(Math.random() * 45) + 1;
    set.add(num);
}

List list = new LinkedList(set); // LinkedList(Collection c)
Collections.sort(list); // Collections.sort(List list)
System.out.println(list);</code></pre>
<blockquote>
<p>[5, 12, 18 ,22, 33, 44]</p>
</blockquote>
<ul>
<li><p>for 문은 <code>Math.random()</code>을 이용해서 6개의 난수 번호를 set에 저장한다.</p>
</li>
<li><p><code>Collections.sort(List list)</code> 은 정렬해주는 메서드인데 매개변수로 List 인터페이스만 올 수 있다. 따라서, Set 은 정렬을 할 수 없기 때문에 <code>LinkedList(Collection c)</code> 생성자를 이용해서 Set을 List로 변환해주고 <code>Collections.sort(List list)</code> 로 정렬해준다.</p>
</li>
</ul>
<p><strong>HashSet은 객체를 저장하기 전에 기존에 같은 객체가 있는지 확인한다. 같은 객체가 없으면 저장하고, 있으면 저장하지 않는다.</strong></p>
<p><strong>boolean add(Object o)는 저장할 객체의 equals()와 hashCode()를 호출한다. 따라서 equals()와 hashCode()가 오버라이딩 되어 있어야 한다.</strong></p>
<pre><code class="language-java">class Ex11_11 {
    public static void main(String[] args) {
        HashSet&lt;Person&gt; set = new HashSet&lt;&gt;();

        set.add(new Person(&quot;David&quot;,10));
        set.add(new Person(&quot;David&quot;,10));

        System.out.println(set);
    }
}

class Person {
    String name;
    int age;

    Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String toString() {
        return name +&quot;:&quot;+ age;
    }
}</code></pre>
<blockquote>
<p>[David:10, David:10]</p>
</blockquote>
<ul>
<li>Person 클래스에서 equals()와 hashCode()를 오버라이딩 해주지 않아서 HashSet에 중복으로 저장이 된다.</li>
</ul>
<p><strong>equals()와 hashCode() 오버라이딩하는 방법</strong></p>
<pre><code class="language-java">@Override
public int hashCode() {
    return Objects.hash(name, age);
}

@Override
public boolean equals(Object obj) {
    if (!(obj instanceof Person)) return false;

    Person p = (Person) obj;
    return this.name.equals(p.name) &amp;&amp; this.age == p.age;
}</code></pre>
<blockquote>
<p>[David:10]</p>
</blockquote>
<ul>
<li>equals()와 hashCode() 오버라이딩하니까 중복이 제거되었다.</li>
</ul>
<p><strong>집합 예제</strong></p>
<pre><code class="language-java">class Ex11_12 {
    public static void main(String args[]) {
        HashSet setA   = new HashSet();
        HashSet setB   = new HashSet();
        HashSet setHab = new HashSet();
        HashSet setKyo = new HashSet();
        HashSet setCha = new HashSet();

        setA.add(&quot;1&quot;);     setA.add(&quot;2&quot;);  setA.add(&quot;3&quot;);
        setA.add(&quot;4&quot;);  setA.add(&quot;5&quot;);
        System.out.println(&quot;A = &quot;+setA);

        setB.add(&quot;4&quot;);     setB.add(&quot;5&quot;);  setB.add(&quot;6&quot;);        
      setB.add(&quot;7&quot;);  setB.add(&quot;8&quot;);
        System.out.println(&quot;B = &quot;+setB);

        Iterator it = setB.iterator();
        // 교집합
        while(it.hasNext()) {
            Object tmp = it.next();
            if(setA.contains(tmp))
                setKyo.add(tmp);
        }
        // 차집합
        it = setA.iterator();
        while(it.hasNext()) {
            Object tmp = it.next();
            if(!setB.contains(tmp))
                setCha.add(tmp);
        }
        // 합집합
        it = setA.iterator();
        while(it.hasNext())
            setHab.add(it.next());

        it = setB.iterator();
        while(it.hasNext())
            setHab.add(it.next());

        System.out.println(&quot;A ⋂ B = &quot; + setKyo);
        System.out.println(&quot;A U B = &quot; + setHab);
        System.out.println(&quot;A - B = &quot; + setCha); 
    }
}</code></pre>
<blockquote>
<p>A = [1, 2, 3, 4, 5]
B = [4, 5, 6, 7, 8]
A ⋂ B = [4, 5]
A U B = [1, 2, 3, 4, 5, 6, 7, 8]
A - B = [1, 2, 3]</p>
</blockquote>
<p><strong>교집합, 차집합, 합집합 더 쉽게 구하는 방법</strong></p>
<pre><code class="language-java">setA.retainAll(setB); // 교집합
setA.addAll(setB); // 합집합
setA.removeAll(setB); // 차집합</code></pre>
<ul>
<li><p><code>setA.retainAll(setB);</code> : 교집합. 공통된 요소만 남기고 삭제</p>
</li>
<li><p><code>setA.addAll(setB);</code> : 합집합. setB의 모든 요소를 추가(중복 제외)</p>
</li>
<li><p><code>setA.removeAll(setB);</code> : 차집합. setB와 공통 요소를 제거</p>
</li>
</ul>
<hr>
<h1 id="📌treeset---범위-탐색-정렬">📌TreeSet - 범위 탐색, 정렬</h1>
<p><strong>이진 탐색 트리(binary search tree)로 구현. 범위 탐색과 정렬에 유리</strong></p>
<p><strong>이진 트리는 모든 노드가 최대 2개(0~2개)의 하위 노드를 갖는다.</strong></p>
<ul>
<li>각 요소(node)가 나무(tree)형태로 연결되어있다.(LinkedList의 변형)<img src="https://velog.velcdn.com/images/jere-gim/post/a991fd9e-3f66-4c31-98c9-bd8465c40502/image.png" width="400">
```java
class TreeNode {
TreeNode left; // 왼쪽 자식노드
Object element; // 저장할 객체
TreeNode right; // 오른쪽 자식노드
}
```
- 노드 하나를 트리노드라고 부르고 총 3개의 변수가 저장되어 있다.

</li>
</ul>
<h2 id="이진-탐색-트리binary-search-tree">이진 탐색 트리(Binary search tree)</h2>
<p><strong>부모보다 작은 값은 왼쪽. 큰 값은 오른쪽에 저장한다.</strong>
<img src="https://velog.velcdn.com/images/jere-gim/post/dd167bf2-bf4d-4a01-bf71-79654b5014e7/image.png" alt=""></p>
<p><strong>단점 : 데이터가 많아질수록 비교 횟수 증가에 따라 추가, 삭제에 시간이 더 걸린다.</strong></p>
<h2 id="treeset-데이터-저장-과정">TreeSet 데이터 저장 과정</h2>
<p><strong>TreeSet에 7, 4, 9, 1, 5의 순서로 데이터를 저장하면 아래의 과정을 거친다.</strong>
<img src="https://velog.velcdn.com/images/jere-gim/post/aa1a579f-1356-4baa-8a5f-8d38e12a6e00/image.png" alt=""></p>
<ul>
<li>요소를 저장해감에 따라 비교 횟수가 점점 늘어나면서 저장하는 시간이 많이 걸린다.</li>
</ul>
<h2 id="treeset의-메서드">TreeSet의 메서드</h2>
<p><strong>add(), remove(), size(), iterator()와 같은 기본적인 메서드들은 적지 않았다.</strong></p>
<h3 id="생성자-1">생성자</h3>
<ul>
<li><p>TreeSet() : 기본 생성자. 정렬 기준이 없으면 저장하는 객체의 기본 정렬 기준인 Comparable 사용</p>
</li>
<li><p>TreeSet(Collection c) : 컬렉션(c)를 TreeSet으로 변환하는 생성자</p>
</li>
<li><p>TreeSet(Comparator comp) : 주어진 정렬 기준으로 정렬하는 생성자</p>
</li>
</ul>
<h3 id="주요-메서드-1">주요 메서드</h3>
<ul>
<li><p>Object first() : 정렬된 순서에서 첫 번째 객체를 반환한다.(오름차순일 때 제일 작은 값)</p>
</li>
<li><p>Object last() : 정렬된 순서에서 마지막 객체를 반환한다.(오름차순일 때 제일 큰 값)</p>
</li>
<li><p>Object ceiling(Object o) : 객체(o)와 같은 객체를 반환. 없으면 객체보다 큰 값 중 가장 가까운 값의 객체를 반환. 없으면 null</p>
</li>
<li><p>Object floor(Object o) : 객체(o)와 같은 객체를 반환. 없으면 객체보다 작은 값 중 가장 가까운 값의 객체를 반환. 없으면 null</p>
</li>
<li><p>Object higher(Object o) : 객체(o)보다 큰 값 중 가장 가까운 값의 객체를 반환. 없으면 null</p>
</li>
<li><p>Object lower(Object o) : 객체(o)보다 작은 값 중 가장 가까운 값의 객체를 반환. 없으면 null</p>
</li>
<li><p>SortedSet subSet(Object from, Object to) : from~to 사이의 값을 반환한다.(to 포함x)</p>
</li>
<li><p>SortedSet headSet(Object to) : to보다 작은 값의 객체들을 반환한다.</p>
</li>
<li><p>SortedSet tailSet(Object from) : from보다 큰 값의 객체들을 반환한다.</p>
</li>
</ul>
<h2 id="실습-예제-2">실습 예제</h2>
<pre><code class="language-java">Set set = new TreeSet();

for (int i = 0; set.size() &lt; 6 ; i++) {
    int num = (int)(Math.random()*45) + 1;
    set.add(num);  // set.add(new Integer(num));
}

System.out.println(set);</code></pre>
<blockquote>
<p>[11, 15, 20, 33, 36, 40]</p>
</blockquote>
<ul>
<li>TreeSet은 따로 정렬을 하지 않아도 자동으로 정렬이 된다.</li>
<li>Integer 클래스가 Comparable 구현의 의한 기본 정렬 기준을 가지고 있다.</li>
</ul>
<pre><code class="language-java">class Ex11_13 {
    public static void main(String[] args) {
        Set set = new TreeSet();

        set.add(new Test());

        System.out.println(set);
    }
}

class Test {}</code></pre>
<blockquote>
<p>Exception in thread &quot;main&quot; java.lang.ClassCastException
에러가 난다.</p>
</blockquote>
<ul>
<li>에러가 나는 이유는 비교 기준이 없기 때문이다.</li>
</ul>
<p><strong>비교 기준을 만들어준다.</strong></p>
<pre><code class="language-java">class Ex11_13 {
    public static void main(String[] args) {
        Set set = new TreeSet(new TestComp()); // 정렬 기준 넣어준다.
        //Set set = new TreeSet();

        set.add(new Test());

        System.out.println(set);
    }
}

class Test implements Comparable {
    public int compareTo(Object o) {
    return 0;
    }
}

class TestComp implements Comparator {
    public int compare(Object o1, Object o2) {
    return 0;
    }
}</code></pre>
<ul>
<li><p><code>Set set = new TreeSet(new TestComp());</code> : Comparator 인터페이스를 구현한 정렬 기준을 <code>TreeSet(Comparator comp)</code> 생성자를 통해 정의해준다.</p>
</li>
<li><p><code>Set set = new TreeSet();</code> : Test 클래스가 Comparable 인터페이스를 구현한다면 정렬 기준을 적지 않아도 기본 정렬 기준으로 정렬된다.</p>
</li>
<li><p>TreeSet 클래스는 정렬 기준이 필요하기 때문에 1. 저장할 객체가 Comparable구현에 의한 정렬 기준을 가지고 있던가 2. TreeSet이 생성자를 통해 정렬 기준을 가지고 있던가 둘 중 하나는 해야 한다.</p>
</li>
</ul>
<pre><code class="language-java">class Ex11_15 {
    public static void main(String[] args) {
        TreeSet set = new TreeSet();
        int[] score = {80, 95, 50, 35, 45, 65, 10, 100};

        for(int i=0; i &lt; score.length; i++)
            set.add(new Integer(score[i]));

        System.out.println(&quot;50보다 작은 값 :&quot; + set.headSet(new Integer(50)));
        System.out.println(&quot;50보다 큰 값 :&quot;  + set.tailSet(new Integer(50)));
        System.out.println(&quot;40~80사이의 값 :&quot;  + set.subSet(40, 80));
    }
}</code></pre>
<blockquote>
<p>50보다 작은 값 :[10, 35, 45]
50보다 큰 값 :[50, 65, 80, 95, 100]
40~80사이의 값 :[45, 50, 65]</p>
</blockquote>
<ul>
<li><p><code>set.headSet(new Integer(50));</code> : 50보다 작은 값. 50 포함X</p>
</li>
<li><p><code>set.tailSet(new Integer(50));</code> : 50보다 큰 값. 50 포함O</p>
</li>
<li><p><code>set.subSet(40, 80)</code> : 40 ~ 80 사이의 값. 80 포함X
<img src="https://velog.velcdn.com/images/jere-gim/post/7e343500-9c57-4f88-9b2d-0b5e55116ec6/image.png" alt=""></p>
</li>
<li><p>이런 방식으로 동작하기 때문에 범위 검색에 유리하다.</p>
</li>
</ul>
<h2 id="트리-순회tree-traversal">트리 순회(Tree traversal)</h2>
<p><strong>: 이진 트리의 모든 노드를 한번씩 읽는 것</strong></p>
<ul>
<li>전위, 중위, 후위 순회법이 있으며, 중위 순회하면 오름차순으로 정렬된다.</li>
</ul>
<img src="https://velog.velcdn.com/images/jere-gim/post/5204171d-bab4-4d73-97d9-c83de91e9e72/image.png" width="400">
<img src="https://velog.velcdn.com/images/jere-gim/post/72fba05f-a45f-4ac8-b007-b2cc2de1390f/image.png" width="400">
<img src="https://velog.velcdn.com/images/jere-gim/post/1b87368a-1931-40a7-90f4-4d64a480f271/image.png" width="400">
<img src="https://velog.velcdn.com/images/jere-gim/post/0a1b7e8a-479b-4305-b074-79b984022213/image.png" width="400">

<ul>
<li>이러한 동작 방식 때문에 TreeSet은 정렬에 유리하다.</li>
</ul>
<hr>
<h1 id="📌hashmap---순서x-중복키x-값o">📌HashMap - 순서X, 중복(키X, 값O)</h1>
<p><strong>: Map 인터페이스를 구현. 데이터를 키와 값의 쌍으로 저장</strong></p>
<ul>
<li>순서를 유지하려면 LinkedHashMap 클래스를 사용</li>
</ul>
<p><strong>Hashtable은 동기화O, HashMap은 동기화X</strong></p>
<p><strong>TreeMap - TreeSet과 특성이 유사</strong></p>
<ul>
<li>범위 검색과 정렬에 유리한 컬랙션 클래스</li>
<li>HashMap보다 데이터 추가, 삭제에 시간이 더 걸린다.</li>
</ul>
<h2 id="hashmap의-키key와-값value">HashMap의 키(key)와 값(value)</h2>
<p><strong>해싱(hashing)기법으로 데이터를 저장한다. 데이터가 많아도 검색이 빠르다.</strong></p>
<p><strong>키는 중복이 안되고 값은 중복이 된다.</strong></p>
<pre><code class="language-java">HashMap&lt;String, String&gt; hashMap = new HashMap&lt;&gt;();
hashMap.put(&quot;myid&quot;, &quot;asdf&quot;);
hashMap.put(&quot;yourid&quot;, &quot;1122&quot;);
hashMap.put(&quot;yourid&quot;, &quot;asdf&quot;);</code></pre>
<table>
<thead>
<tr>
<th align="center">키(key)</th>
<th align="center">값(value</th>
</tr>
</thead>
<tbody><tr>
<td align="center">myid</td>
<td align="center">asdf</td>
</tr>
<tr>
<td align="center">yourid</td>
<td align="center">asdf</td>
</tr>
</tbody></table>
<ul>
<li><code>hashMap.put(&quot;yourid&quot;, &quot;asdf&quot;);</code> : 같은 키에 다른 값을 2번 넣으면 나중에 넣은 값이 덮어쓴다.</li>
</ul>
<p><strong>키(key)와 값(value) 한 쌍이 Entry[] table 이라는 엔트리 배열에 저장된다.</strong></p>
<h2 id="해싱hashing">해싱(Hashing)</h2>
<p><strong>: 해시 함수(hash function)로 해시 테이블에 데이터를 저장, 검색하는 기법</strong></p>
<p><strong>해시 테이블은 배열과 LinkedList가 조합된 형태이다.</strong>
<img src="https://velog.velcdn.com/images/jere-gim/post/01c6c84e-85e7-4bea-8e2c-9df1509e5f45/image.png" alt=""></p>
<ul>
<li>배열의 장점인 접근성과 LinkedList의 장점인 변경에 유리함을 조합한 것이다.</li>
</ul>
<p><strong>해시 테이블에 저장된 데이터를 가져오는 과정</strong>
<img src="https://velog.velcdn.com/images/jere-gim/post/1cf24118-cc55-4cf1-b6f8-24829f10df31/image.png" alt=""></p>
<ol>
<li><p>키(key)로 해시 함수를 호출해서 해시 코드(배열의 index)를 얻는다.</p>
</li>
<li><p>해시 코드(해시 함수의 반환값)에 대응하는 LinkedList를 배열에서 찾는다.</p>
</li>
<li><p>LinkedList에서 키(key)와 일치하는 데이터를 찾는다.</p>
<ul>
<li>해시 함수는 같은 키에 대해 항상 같은 해시 코드(배열의 index)를 반환해야 한다.</li>
<li>서로 다른 키가 같은 값의 해시 코드를 반환할 수 있다.(75xxx와 72xxx의 해시 코드는 모두 7이다. = 같은 배열 data[7]에 있다.)</li>
</ul>
</li>
</ol>
<h2 id="hashmap의-메서드">HashMap의 메서드</h2>
<h3 id="생성자-2">생성자</h3>
<ul>
<li><p>HashMap() : 기본 생성자</p>
</li>
<li><p>HashMap(int initialCapacity) : HashMap은 해시 테이블에 데이터를 저장한다. 해시 테이블은 배열 + LinkedList로 이루어져 있기 때문에 생성자를 통해 배열의 초기 용량을 정해줄 수 있다.</p>
</li>
<li><p>HashMap(int initialCapacity, float loadFactor) : 용량이 loadFactor만큼 차면 용량을 x2 해주는 생성자</p>
</li>
<li><p>HashMap(Map m) : 다른 Map을 HashMap으로 변환해주는 생성자</p>
</li>
</ul>
<h3 id="주요-메서드-2">주요 메서드</h3>
<ul>
<li><p>Object put(Object key, Object value) : 키(key)와 값(value)을 저장</p>
</li>
<li><p>void putAll(Map m) : Map의 데이터들이 전부 저장된다.</p>
</li>
<li><p>Object remove(Object key) : 키로 저장된 값(객체)을 제거</p>
</li>
<li><p>Object replace(Object key, Object value) : 지정된 키의 값을 지정된 값으로 변경</p>
</li>
<li><p>boolean replace(Object key, Object oldValue, Object newValue) : 지정된 키(key)와 값(oldValue)을 새로운 값(newValue)으로 변경</p>
</li>
<li><p>Set entrySet() : 키와 값이 쌍으로 이루어진 entry를 Set 타입으로 저장해서 반환</p>
</li>
<li><p>Set keySet() : 저장된 모든 키를 Set 타입으로 저장해서 반환</p>
</li>
<li><p>Collection values() : 저장된 모든 값을 Collection 타입으로 저장해서 반환</p>
</li>
<li><p>Object get(Object key) : 키에 해당하는 값을 반환</p>
</li>
<li><p>Object getOrDefault(Object key, Object dafaultValue) : 해당하는 키가 없을 때 dafaultValue 값을 반환</p>
</li>
<li><p>boolean containsKey(Object key) : 지정된 키를 포함하고 있으면 true</p>
</li>
<li><p>boolean containsValue(Object value) : 지정된 값을 포함하고 있으면 true</p>
</li>
<li><p>Object clone() : 복제하는 메서드</p>
</li>
</ul>
<h2 id="실습-예제-3">실습 예제</h2>
<p><strong>로그인</strong></p>
<pre><code class="language-java">class Ex11_16 {
    public static void main(String[] args) {
        HashMap map = new HashMap();
        map.put(&quot;myId&quot;, &quot;1234&quot;);
        map.put(&quot;asdf&quot;, &quot;1111&quot;);
        map.put(&quot;asdf&quot;, &quot;1234&quot;);

        Scanner s = new Scanner(System.in);    // 화면으로부터 라인단위로 입력받는다.

        while(true) {
            System.out.println(&quot;id와 password를 입력해주세요.&quot;);
            System.out.print(&quot;id :&quot;);
            String id = s.nextLine().trim();

            System.out.print(&quot;password :&quot;);
            String password = s.nextLine().trim();
            System.out.println();

            if(!map.containsKey(id)) {
                System.out.println(&quot;입력하신 id는 존재하지 않습니다. 다시 입력해주세요.&quot;);
                continue;
            } 

            if(!(map.get(id)).equals(password)) {
                System.out.println(&quot;비밀번호가 일치하지 않습니다. 다시 입력해주세요.&quot;);
            } else {
                System.out.println(&quot;id와 비밀번호가 일치합니다.&quot;);
                break;
            }
        }
    }
}</code></pre>
<ul>
<li><code>if(!(map.get(id)).equals(password))</code> : <code>map.get(id)</code> 키가 id인 값을 반환하고 이 값을 password와 비교해서 같지 않으면 비밀번호가 틀리다는 문구 출력</li>
</ul>
<p><strong>entry</strong></p>
<pre><code class="language-java">class Ex11_17 {
    public static void main(String[] args) {
        HashMap&lt;String, Integer&gt; map = new HashMap&lt;&gt;();
        map.put(&quot;김자바&quot;, 90);
        map.put(&quot;김자바&quot;, 100); // 이 값이 덮어쓴다.
        map.put(&quot;이자바&quot;, 100);
        map.put(&quot;강자바&quot;, 80);
        map.put(&quot;안자바&quot;, 90);

        Set&lt;Map.Entry&lt;String, Integer&gt;&gt; set = map.entrySet();
        Iterator&lt;Map.Entry&lt;String, Integer&gt;&gt; it = set.iterator();

        while (it.hasNext()) {
            Map.Entry&lt;String, Integer&gt; e = it.next();
            System.out.println(&quot;이름 : &quot; + e.getKey() + &quot;, 점수 : &quot; + e.getValue());
        }

        System.out.println(&quot;참가자 명단 : &quot; + map.keySet());

        Collection&lt;Integer&gt; values = map.values();
        Iterator&lt;Integer&gt; it2 = values.iterator();

        int total = 0;
        while (it2.hasNext()) {
            int i = it2.next();
            total += i;
        }

        System.out.println(&quot;총점 : &quot; + total);
        System.out.println(&quot;평균 : &quot; + (float) total / set.size());
        System.out.println(&quot;최고점수 : &quot; + Collections.max(values));
        System.out.println(&quot;최저점수 : &quot; + Collections.min(values));
    }
}</code></pre>
<blockquote>
<p>이름 : 안자바, 점수 : 90
이름 : 김자바, 점수 : 100
이름 : 강자바, 점수 : 80
이름 : 이자바, 점수 : 100
참가자 명단 : [안자바, 김자바, 강자바, 이자바]
총점 : 370
평균 : 92.5
최고점수 : 100
최저점수 : 80</p>
</blockquote>
<ul>
<li><code>Set&lt;Map.Entry&lt;String, Integer&gt;&gt; set = map.entrySet();</code> : Map 인터페이스의 내부 인터페이스인 Entry 인터페이스</li>
</ul>
<pre><code class="language-java">public class Solution { 
  public HashMap&lt;Character, Integer&gt; countAllCharacter(String str) {
    HashMap&lt;Character, Integer&gt; hashMap = new HashMap&lt;&gt;();

    for (int i = 0; i &lt; str.length(); i++) {
      char character = str.charAt(i);

      if (hashMap.containsKey(character))
        hashMap.put(character, hashMap.get(character) + 1);
      else
        hashMap.put(character, 1);
    }

    return str.length() == 0 ? null : hashMap;
  }
}</code></pre>
<blockquote>
<p>HashMap&lt;Character, Integer&gt; output = countAllCharacter(&quot;banana&quot;);
System.out.println(output); // --&gt; {b=1, a=3, n=2}</p>
</blockquote>
<ul>
<li>for 문 : 문자열의 첫 번째 문자부터 hashMap에 키로 포함하고 있으면 값을 +1하고 포함하고 있지 않다면 값에 1 저장 -&gt; 문자열에 같은 문자가 몇번 반복되는지 세는 로직</li>
</ul>
<hr>
<h1 id="📌collections---컬렉션을-위한-메서드static-제공">📌Collections - 컬렉션을 위한 메서드(static) 제공</h1>
<h2 id="fill-copy-sort-binarysearch">fill(), copy(), sort(), binarySearch()</h2>
<p><strong>: 컬렉션 채우기, 복사, 정렬, 이진 검색</strong></p>
<h2 id="synchronizedxxx">synchronizedXXX()</h2>
<p><strong>: 컬렉션의 동기화</strong></p>
<pre><code class="language-java">static Collection synchronizedCollection( Collection c )
static List synchronizedList( List list )
static Set synchronizedSet( Set s )
static Map synchronizedMap( Map m )
static SortedSet synchronizedSortedSet( SortedSet s )
static SortedMap synchronizedSortedMap( SortedMap m )

/* 사용 방법 */
List synchroList = Collections.synchronizedList( new ArrayList(...) ) ; // ArrayList는 동기화 되지 않은 List</code></pre>
<h2 id="unmodifiablexxx">unmodifiableXXX()</h2>
<p><strong>: 변경 불가(read Only) 컬렉션 만들기</strong></p>
<pre><code class="language-java">static Collection unmodifiableCollection( Collection c )
static List unmodifiableList( List list )
static Set unmodifiableSet( Set s )
static Map unmodifiableMap( Map m )

static SortedSet unmodifiableSortedSet( SortedSet s )
static NavigableSet unmodifiableNavigableSet( NavigableSet s )
static SortedMap unmodifiableSortedMap( SortedMap m )
static NavigableMap unmodifiableNavigableMap( NavigableMap m )</code></pre>
<h2 id="singletonxxx">singletonXXX()</h2>
<p><strong>: 싱글톤 컬렉션 만들기. 객체 1개만 저장 가능한 컬렉션</strong></p>
<pre><code class="language-java">static List singletonList( Object o )
static Set singleton( Object o ) // Set는 그냥 singleton
static Map singletonMap( Object key, Object value )</code></pre>
<h2 id="checkedxxx">checkedXXX()</h2>
<p><strong>: 한 종류의 객체만 저장하는 컬렉션 만들기</strong></p>
<pre><code class="language-java">static Collection checkedCollection( Collection c, Class type )
static List checkedList( List list, Class type )
static Set checkedSet( Set s, Class type )
static Map checkedMap( Map m, Class keyType, Class valueType ) // 각 각 Type 지정

static Queue checkedQueue( Queue queue, Class type )

static SortedSet checkedSortedSet( SortedSet s, Class type  )
static NavigableSet checkedNavigableSet( NavigableSet s, Class type  )
static SortedMap checkedSortedMap( SortedMap m, Class keyType, Class valueType )
static NavigableMap checkedNavigableMap( NavigableMap m, Class keyType, Class valueType )

/* 사용 방법 */
List list = new ArrayList() ;
List checkedList = checkedList( list, String.class ) ; //String만 저장 가능하도록

checkedList.add(&quot;abc&quot;) ; // 가능
checkedList.add(new Integer(123)) ; // 불가능(에러) : ClassCastException 발생</code></pre>
<ul>
<li>JDK 1.5 버전 이후에는 제네릭스로 대체되었다.</li>
</ul>
<h2 id="addall배열--">addAll([배열] , ...)</h2>
<p><strong>: 지정 Collection 에 &#39;입력된 값들( 가변인자 )&#39;을 추가</strong></p>
<pre><code class="language-java">List list = new ArrayList();

addAll(list, 1,2,3,4,5); 
System.out.println(list);</code></pre>
<blockquote>
<p>[1, 2, 3, 4, 5]</p>
</blockquote>
<h2 id="rotate배열--int-move">rotate([배열] , int move)</h2>
<p><strong>: 오른쪽으로 move칸씩 이동</strong></p>
<pre><code class="language-java">rotate(list, 2);  // 오른쪽으로 두 칸씩 이동 
System.out.println(list);</code></pre>
<blockquote>
<p>[4, 5, 1, 2, 3]</p>
</blockquote>
<h2 id="swap배열-int-idx1-int-idx2">swap([배열], int idx1, int idx2)</h2>
<p><strong>: &quot;idx1 위치의 값&quot;과 &quot;idx2 위치의 값&quot;과 교환</strong></p>
<pre><code class="language-java">swap(list, 0, 2); // 첫 번째와 세 번째를 교환(swap)
System.out.println(list);</code></pre>
<blockquote>
<p>[1, 5, 4, 2, 3]</p>
</blockquote>
<h2 id="shuffle배열">shuffle([배열])</h2>
<p><strong>: 무작위 섞기</strong></p>
<pre><code class="language-java">shuffle(list);    // 저장된 요소의 위치를 임의로 변경 
System.out.println(list);</code></pre>
<blockquote>
<p>[3, 4, 2, 1, 5]</p>
</blockquote>
<h2 id="sort배열-reverseorder">sort([배열], reverseOrder())</h2>
<p><strong>: 역순 정렬. reverse([배열])과 동일</strong></p>
<pre><code class="language-java">sort(list, reverseOrder()); // 역순 정렬 reverse(list);와 동일 
System.out.println(list);</code></pre>
<blockquote>
<p>[5, 4, 3, 2, 1]</p>
</blockquote>
<h2 id="ncopies배열size-object-value">nCopies([배열].size(), Object value)</h2>
<p><strong>: 지정 배열과 같은 크기의 새로운 리스트를 생성하고 value로 해당 리스트 채움</strong></p>
<pre><code class="language-java">List newList = nCopies(list.size(), 2); 
System.out.println(&quot;newList=&quot;+newList);</code></pre>
<blockquote>
<p>newList=[2, 2, 2, 2, 2]</p>
</blockquote>
<h2 id="disjoint배열-1-배열-2">disjoint([배열 1], [배열 2])</h2>
<p><strong>: 두 배열에 공통 요소가 없으면 true 반환</strong></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[자바의 정석 기초편] 컬렉션 프레임워크 1]]></title>
            <link>https://velog.io/@jere-gim/%EC%9E%90%EB%B0%94%EC%9D%98-%EC%A0%95%EC%84%9D-%EA%B8%B0%EC%B4%88%ED%8E%B8-%EC%BB%AC%EB%A0%89%EC%85%98-%ED%94%84%EB%A0%88%EC%9E%84%EC%9B%8C%ED%81%AC-1</link>
            <guid>https://velog.io/@jere-gim/%EC%9E%90%EB%B0%94%EC%9D%98-%EC%A0%95%EC%84%9D-%EA%B8%B0%EC%B4%88%ED%8E%B8-%EC%BB%AC%EB%A0%89%EC%85%98-%ED%94%84%EB%A0%88%EC%9E%84%EC%9B%8C%ED%81%AC-1</guid>
            <pubDate>Mon, 06 Mar 2023 05:09:38 GMT</pubDate>
            <description><![CDATA[<h1 id="📌컬렉션-프레임워크collections-framework">📌컬렉션 프레임워크(Collections framework)</h1>
<p><strong>컬렉션(collection) : 여러 객체(데이터)를 모아 놓은 것</strong></p>
<p><strong>프레임워크(framework) : 표준화, 정형화된 체계적인 프로그래밍 방식</strong></p>
<p><strong>컬렉션 프레임워크(collections framework)</strong></p>
<ul>
<li>컬렉션(다수의 객체)를 다루기 위한 표준화된 프로그래밍 방식</li>
<li>컬렉션을 쉽고 편리하게 다룰 수 있는 다양한 클래스들을 제공</li>
<li>java.util 패키지에 포함</li>
</ul>
<p><strong>컬렉션 클래스(collection class)</strong></p>
<ul>
<li>다수의 데이터를 저장할 수 있는 클래스(Vector, ArrayList, HashSet 등)</li>
</ul>
<hr>
<h1 id="컬렉션-프레임워크의-핵심-인터페이스">컬렉션 프레임워크의 핵심 인터페이스</h1>
<h2 id="list-인터페이스---저장-순서o-중복o">List 인터페이스 - 저장 순서O, 중복O</h2>
<p><strong>: 저장 순서가 있는 데이터의 집합. 데이터의 중복을 허용한다.</strong></p>
<ul>
<li><p>예시) 대기자 명단</p>
</li>
<li><p>구현 클래스 : ArrayList, LinkedList, Stack, Vector 등</p>
<h3 id="list-인터페이스-계층-구조">List 인터페이스 계층 구조</h3>
<img src="https://velog.velcdn.com/images/jere-gim/post/b9157e6c-110c-4dbb-b4ee-02ab68adf75b/image.png" width="400"></li>
<li><p>핵심 : ArrayList, LinkedList</p>
</li>
</ul>
<h3 id="list-인터페이스의-메서드">List 인터페이스의 메서드</h3>
<p><img src="https://velog.velcdn.com/images/jere-gim/post/ece90dd7-55f2-47fd-b26b-2d0f74c72cc8/image.png" alt=""></p>
<ul>
<li><code>get()</code> : 읽기, <code>set()</code> : 변경, <code>indexOf()</code> : 검색, <code>sort()</code> : 정렬</li>
</ul>
<h2 id="set-인터페이스---저장-순서x-중복x">Set 인터페이스 - 저장 순서X, 중복X</h2>
<p><strong>: 저장 순서를 유지하지 않는 데이터의 집합. 데이터의 중복을 허용하지 않는다.</strong></p>
<ul>
<li><p>예시) 소수의 집합, 육식동물의 집합</p>
</li>
<li><p>List와 특징이 정반대</p>
</li>
<li><p>구현 클래스 : HashSet, TreeSet 등</p>
<h3 id="set-인터페이스-계층-구조">Set 인터페이스 계층 구조</h3>
<img src="https://velog.velcdn.com/images/jere-gim/post/187d87ea-8f8e-4db0-bb8a-be8601bef5cd/image.png" width="300"></li>
<li><p>핵심 : HashSet, TreeSet</p>
</li>
</ul>
<h3 id="set-인터페이스의-메서드">Set 인터페이스의 메서드</h3>
<p><strong>Collection 인터페이스의 메서드와 동일</strong>
<img src="https://velog.velcdn.com/images/jere-gim/post/9df09099-5481-4cde-821c-1ddc95310ece/image.png" alt=""></p>
<p><strong>집합과 관련된 메서드들(Collection에 변화가 있으면 true, 아니면 false를 반환)</strong></p>
<table>
<thead>
<tr>
<th align="left">메서드</th>
<th align="left">설명</th>
</tr>
</thead>
<tbody><tr>
<td align="left">boolean addAll(Collection c)</td>
<td align="left">저장된 Collection의 객체들을 Collection에 추가한다.(합집합)</td>
</tr>
<tr>
<td align="left">boolean containsAll(Collection c)</td>
<td align="left">저장된 Collection의 객체들이 Collection에 포함되어 있는지 확인한다.(부분집합)</td>
</tr>
<tr>
<td align="left">boolean removeAll(Collection c)</td>
<td align="left">저장된 Collection에 포함된 객체들을 삭제한다.(차집합)</td>
</tr>
<tr>
<td align="left">boolean retainAll(Collection c)</td>
<td align="left">저장된 Collection에 포함된 객체만을 남기고 나머지는 Collection에서 삭제한다.(교집합)</td>
</tr>
</tbody></table>
<h2 id="map-인터페이스---저장-순서x-중복키x-값o">Map 인터페이스 - 저장 순서X, 중복(키X, 값O)</h2>
<p><strong>: 키(key)와 값(value)의 쌍(pair)으로 이루어진 데이터의 집합. 저장 순서는 유지하지 않으며, 키는 중복을 허용하지 않고 값은 중복을 허용한다.</strong></p>
<ul>
<li><p>예시) 아이디(key)-패스워드(value)</p>
</li>
<li><p>구현 클래스 : HashMap, TreeMap, Hashtable, Properties 등</p>
<h3 id="map-인터페이스-계층-구조">Map 인터페이스 계층 구조</h3>
<img src="https://velog.velcdn.com/images/jere-gim/post/61decf61-8339-4b08-ab1c-d3bae8b1c620/image.png" width="400"></li>
<li><p>핵심 : HashMap, TreeMap</p>
</li>
<li><p>LinkedHashMap : 저장 순서가 있음</p>
</li>
<li><p>Hashtable과 HashMap은 유사. 동기화 유무 차이(Hashtable 동기화O, HashMap 동기화X)</p>
</li>
</ul>
<h3 id="map-인터페이스의-메서드">Map 인터페이스의 메서드</h3>
<p><img src="https://velog.velcdn.com/images/jere-gim/post/fce70a1f-3f2d-4c38-9473-45f8036df283/image.png" alt=""></p>
<ul>
<li><p><code>put()</code>, <code>putAll()</code> : 추가, <code>containsKey()</code>, <code>containsValue()</code>, <code>get()</code> : 검색, <code>remove()</code> : 삭제</p>
</li>
<li><p><code>Set keySet()</code> : 키만 읽기, <code>Collection values()</code> : 값만 읽기, <code>Set entrySet()</code> : Entry(키-값) 읽기</p>
</li>
<li><p><code>Collection values()</code> : 리턴 타입이 Collection이기 때문에 순서와 중복이 상관 없음</p>
</li>
<li><p><code>Set keySet()</code>, <code>Set entrySet()</code> : 리턴 타입이 Set이기 때문에 순서X, 중복X</p>
</li>
</ul>
<h2 id="collection-인터페이스">Collection 인터페이스</h2>
<h3 id="collection-인터페이스-계층-구조">Collection 인터페이스 계층 구조</h3>
<img src="https://velog.velcdn.com/images/jere-gim/post/b3016ac1-6785-4c43-ab88-4f0f9e0fbe0b/image.png" width="400">
- List 인터페이스와 Set 인터페이스의 공통 부분을 뽑아서 Collection 인터페이스를 만들었다.

<h3 id="collection-인터페이스의-메서드">Collection 인터페이스의 메서드</h3>
<p><img src="https://velog.velcdn.com/images/jere-gim/post/9df09099-5481-4cde-821c-1ddc95310ece/image.png" alt=""></p>
<ul>
<li><code>add()</code> : 추가, <code>contains()</code> : 검색, <code>remove()</code> : 삭제, <code>clear()</code> : 전체 삭제</li>
</ul>
<hr>
<h1 id="📌arraylist">📌ArrayList</h1>
<p><strong>ArrayList는 기존의 Vector를 개선한 것으로 구현 원리와 기능적으로 동일. ArrayList는 동기화X, Vector는 동기화O</strong></p>
<p><strong>List 인터페이스를 구현하므로, 저장 순서가 유지되고 중복을 허용한다.</strong></p>
<p><strong>데이터의 저장 공간으로 배열을 사용한다.(배열 기반)</strong></p>
<h2 id="arraylist의-메서드">ArrayList의 메서드</h2>
<h3 id="생성자">생성자</h3>
<ul>
<li><p>ArrayList() : 기본 생성자</p>
</li>
<li><p>ArrayList(Collection c) : 컬렉션끼리 변환할 때 사용</p>
</li>
<li><p>ArrayList(int initialCapacity) : 배열의 길이 지정</p>
</li>
</ul>
<h3 id="추가">추가</h3>
<ul>
<li><p>boolean add(Object o) : 객체를 추가. 성공하면 true, 실패하면 false</p>
</li>
<li><p>void add(int index, Object element) : 저장 위치(index) 지정 가능</p>
</li>
<li><p>boolean addAll(Collection c) : 컬렉션의 모든 요소를 추가</p>
</li>
<li><p>boolean addAll(int index, Collection c) : 컬렉션의 모든 요소를 저장 위치(index)에 추가</p>
</li>
</ul>
<h3 id="삭제">삭제</h3>
<ul>
<li><p>boolean remove(Object o) : 객체 삭제</p>
</li>
<li><p>Object remove(int index) : index의 객체 삭제</p>
</li>
<li><p>boolean removeAll(Collection c) : 지정한 컬렉션의 객체들을 삭제</p>
</li>
<li><p>void clear() : 모든 객체 삭제</p>
</li>
</ul>
<h3 id="검색">검색</h3>
<ul>
<li><p>int indexOf(Object o) : 객체가 몇 번째 index에 있는지 반환, 못 찾으면 -1 반환</p>
</li>
<li><p>int lastindexOf(Object o) : 끝에서부터 객체를 찾음.</p>
</li>
<li><p>boolean contains(Object o) : 객체가 존재하는지 물어봄. 있으면 true, 없으면 false</p>
</li>
<li><p>Object get(int index) : index 위치의 객체를 읽음</p>
</li>
<li><p>Object set(int index, Object element) : index의 위치에 있는 객체를 변경</p>
</li>
</ul>
<h3 id="기타">기타</h3>
<ul>
<li><p>List subList(int from, int to) : from부터 to까지의 인덱스를 뽑아내서 새로운 리스트를 만듬(to는 포함x)</p>
</li>
<li><p>Object[] toArray() : ArrayList의 객체 배열을 반환</p>
</li>
<li><p>boolean isEmpty() : ArrayList가 비어있으면 true, 아니면 false</p>
</li>
<li><p>void trimToSize() : 빈 공간을 제거</p>
</li>
<li><p>int size() : 저장된 객체의 개수를 반환</p>
</li>
</ul>
<h2 id="실습-예제">실습 예제</h2>
<pre><code class="language-java">ArrayList list1 = new ArrayList(10);
list1.add(new Integer(5));
list1.add(4);
list1.add(new Integer(2));
list1.add(new Integer(0));
list1.add(new Integer(1));
list1.add(new Integer(3))

ArrayList list2 = new ArrayList(list1.subList(1,4)); </code></pre>
<blockquote>
<p>list1 = [5, 4, 2, 0, 1, 3]
list2 = [4, 2, 0]</p>
</blockquote>
<ul>
<li><p><code>ArrayList list1 = new ArrayList(10);</code> : 기본 길이(용량, Capacity)가 10인 ArrayList 생성</p>
</li>
<li><p><code>list1.add(4);</code> : 이렇게 해도 추가가 된다. 컴파일러가 오토 박싱을 통해서 <code>list1.add(new Integer(4));</code> 이렇게 자동으로 변환해준다.</p>
</li>
<li><p><code>list1.subList(1,4)</code> : 인덱스 1부터 4 전까지의 객체를 뽑아 새로운 ArrayList를 만든다. 인덱스 4는 포함x</p>
</li>
<li><p><code>ArrayList list2 = new ArrayList(list1.subList(1,4));</code> : <code>ArrayList(Collection c)</code> 생성자를 통해 list2를 만들어준다.</p>
</li>
</ul>
<pre><code class="language-java">Collections.sort(list1);
Collections.sort(list2);</code></pre>
<blockquote>
<p>list1 = [0, 1, 2, 3, 4, 5]
list2 = [0, 2, 4]</p>
</blockquote>
<ul>
<li>오름차순으로 정렬</li>
<li><code>Collections</code> 는 유틸 클래스이다. 헷갈리지 말자. <code>Collection</code> 이 인터페이스</li>
</ul>
<pre><code class="language-java">System.out.println(&quot;list1.containsAll(list2) = &quot; 
+ list1.containsAll(list2));</code></pre>
<blockquote>
<p>list1.containsAll(list2) = true</p>
</blockquote>
<ul>
<li>list1이 list2의 모든 요소를 포함하고 있는지</li>
</ul>
<pre><code class="language-java">list2.add(&quot;B&quot;);
list2.add(&quot;C&quot;);
list2.add(3, &quot;A&quot;);</code></pre>
<blockquote>
<p>list2 = [0, 2, 4, A, B, C]</p>
</blockquote>
<ul>
<li><code>list2.add(&quot;B&quot;);</code> : list2의 맨 뒤에 추가된다.</li>
<li><code>list2.add(3, &quot;A&quot;);</code> : 인덱스가 3인 위치에 &quot;A&quot;가 추가된다.</li>
</ul>
<pre><code class="language-java">list2.set(3, &quot;AA&quot;);</code></pre>
<blockquote>
<p>list2 = [0, 2, 4, AA, B, C]</p>
</blockquote>
<ul>
<li><code>list2.set(3, &quot;AA&quot;);</code> : 인덱스가 3인 위치의 객체가 &quot;AA&quot;로 변경된다.</li>
</ul>
<pre><code class="language-java">list1.add(0, &quot;1&quot;);
int result1 = list1.indexOf(&quot;1&quot;);
int result2 = list1.indexOf(1);</code></pre>
<blockquote>
<p>list1 = [1, 0, 1, 2, 3, 4, 5]
result1 = 0
result2 = 2</p>
</blockquote>
<ul>
<li>list1에서 인덱스가 0인 첫 번째 1은 String 타입의 &quot;1&quot; 이고 인덱스가 2인 두 번째 1은 Integer 타입의 new Integer(1) 이다.</li>
</ul>
<pre><code class="language-java">list1.remove(1);</code></pre>
<blockquote>
<p>list1 = [1, 1, 2, 3, 4, 5]</p>
</blockquote>
<ul>
<li>왜 0이 없어진걸까? 객체 new Integer(1) 이 아닌 인덱스가 1인 new Integer(0) 객체가 제거됐기 때문이다. 따라서 new Integer(1) 객체를 삭제하고 싶다면 정확하게 <code>list1.remove(new Integer(1));</code> 라고 해줘야 한다.</li>
</ul>
<pre><code class="language-java">System.out.println(&quot;list1.retainAll(list2) = &quot; 
+ list1.retainAll(list2));</code></pre>
<blockquote>
<p>list1.retainAll(list2) = true
list1 = [2, 4]
list2 = [0, 2, 4, AA, B, C]</p>
</blockquote>
<ul>
<li><code>list1.retainAll(list2)</code> : list1에서 list2와 겹치는 부분만 남기고 나머지는 모두 삭제</li>
</ul>
<pre><code class="language-java">for (int i = list2.size() - 1; i &gt;= 0; i--) {
    if (list1.contains(list2.get(i)))
        list2.remove(i);
}</code></pre>
<blockquote>
<p>list1 = [2, 4]
list2 = [0, AA, B, C]</p>
</blockquote>
<ul>
<li><p>list2에서 list1에 포함된 객체들을 삭제</p>
</li>
<li><ol>
<li>get(i)로 list2에서 객체를 하나씩 꺼낸다.</li>
<li>contains()로 꺼낸 객체가 list1에 있는지 확인한다.</li>
<li>remove(i)로 해당 객체를 list2에서 삭제한다.</li>
</ol>
</li>
</ul>
<h2 id="arraylist에-저장된-객체의-삭제-과정">ArrayList에 저장된 객체의 삭제 과정</h2>
<p><strong>ArrayList인 alphabet에서 &quot;C&quot;를 삭제하는 과정</strong>    </p>
<pre><code class="language-java">ArrayList&lt;String&gt; alphabet = new ArrayList&lt;&gt;(7); // 배열의 길이 7
alphabet.add(&quot;A&quot;);
alphabet.add(&quot;B&quot;);
alphabet.add(&quot;C&quot;);
alphabet.add(&quot;D&quot;);
alphabet.add(&quot;E&quot;);

alphabet.remove(2); // 인덱스가 2인 위치의 &quot;C&quot; 삭제</code></pre>
<p><img src="https://velog.velcdn.com/images/jere-gim/post/655bacfd-27d8-452f-8484-300688b9a2c3/image.png" alt=""></p>
<p><strong>1. 삭제할 데이터의 다음 데이터부터 마지막 데이터까지 한 칸씩 위로 복사해서 삭제할 데이터를 덮어쓴다.</strong></p>
<pre><code class="language-java">System.arraycopy(alphabet, 3, alphabet, 2, 2);</code></pre>
<ul>
<li>alphabet[3] 부터 2(마지막 인자)만큼의 길이를 alphabet[2]에 복사한다는 뜻이다.</li>
</ul>
<p><strong>2. 데이터가 모두 한 칸씩 이동했으므로 마지막 데이터를 null로 변경한다.</strong></p>
<pre><code class="language-java">alphabet[size - 1] = null;</code></pre>
<p><strong>3. 데이터가 삭제되어 데이터의 개수가 줄었으므로 size의 값을 감소시킨다.</strong></p>
<pre><code class="language-java">size--;</code></pre>
<p>[참고] 마지막 데이터를 삭제하는 경우는 1번의 과정(배열 복사)이 필요 없다.</p>
<p>결국, 배열의 복사가 일어나는 1번 과정이 부담이 많이 가는 과정이기 때문에 되도록이면 1번 과정이 없는 것이 좋다.</p>
<h2 id="arraylist에-저장된-첫-번째-객체부터-삭제하는-경우배열의-복사-발생">ArrayList에 저장된 첫 번째 객체부터 삭제하는 경우(배열의 복사 발생)</h2>
<pre><code class="language-java">for(int i = 0; i &lt; alphbet.size(); i++) {
    alphabet.remove(i);
}</code></pre>
<p><img src="https://velog.velcdn.com/images/jere-gim/post/fe942e30-1cd9-464c-8ca4-0d86b8a53efe/image.png" alt=""></p>
<ul>
<li>첫 번째 객체부터 삭제하면 배열의 복사(1번 과정)가 반복되기 때문에 전부 삭제가 되지 않는다.</li>
</ul>
<h2 id="arraylist에-저장된-마지막-객체부터-삭제하는-경우배열의-복사-발생x">ArrayList에 저장된 마지막 객체부터 삭제하는 경우(배열의 복사 발생X)</h2>
<pre><code class="language-java">for(int i = alphbet.size() -1; i &gt;= 0; i--) {
    alphabet.remove(i);
}</code></pre>
<ul>
<li>마지막부터 삭제하면 배열의 복사가 발생하지 않기 때문에 제대로 전부 삭제가 된다.</li>
<li>배열의 복사가 발생하지 않기 때문에 부담이 안가서 성능도 더 좋다.</li>
</ul>
<hr>
<h1 id="📌linkedlist">📌LinkedList</h1>
<p><strong>배열의 장단점</strong>
장점 : 배열은 구조가 간단하고 데이터를 읽는데 걸리는 시간(접근 시간, access time)이 짧다.</p>
<p>단점1 : 크기를 변경할 수 없다.</p>
<ul>
<li>크기를 변경해야 하는 경우 새로운 배열을 생성 후 데이터를 복사해야 한다.
(더 큰 배열 생성 -&gt; 복사 -&gt; 참조 변경)</li>
<li>크기 변경을 피하기 위해 충분히 큰 배열을 생성하면 메모리가 낭비된다.</li>
</ul>
<p>단점2 : 비순차적인 데이터의 추가, 삭제에 시간이 많이 걸린다.    </p>
<ul>
<li>중간 데이터를 추가, 삭제하기 위해 다른 데이터를 옮겨야 한다.</li>
<li>순차적인 데이터 추가(끝에 추가), 삭제(끝에 삭제)는 빠르다.</li>
</ul>
<p><strong>배열의 단점을 복사하기 위해 나온게 LinkedList 이다.</strong></p>
<ul>
<li>배열과 달리 LinkedList는 불연속적으로 존재하는 데이터를 연결(link)한다.
<img src="https://velog.velcdn.com/images/jere-gim/post/22bbc1c8-1a52-414b-953d-37aa92d5dbd5/image.png" alt=""></li>
</ul>
<pre><code class="language-java">class Node {
    Node next;
    Object data;
}</code></pre>
<ul>
<li><p>next : 다음 노드의 주소</p>
</li>
<li><p>data : 데이터 값</p>
</li>
<li><p>데이터 삭제 : 단 한번의 참조 변경만으로 가능
<img src="https://velog.velcdn.com/images/jere-gim/post/647747db-f5af-47ed-9672-85fce70b01e8/image.png" alt=""></p>
</li>
<li><p>데이터 추가 : 한번의 Node 객체 생성과 두번의 참조 변경만으로 가능
<img src="https://velog.velcdn.com/images/jere-gim/post/73baea33-49ba-411a-9313-946c98053716/image.png" alt=""></p>
</li>
</ul>
<h2 id="linkedlist---이중-연결-리스트">LinkedList - 이중 연결 리스트</h2>
<p><strong>LinkedList : 연결리스트. 데이터 접근성이 나쁘다.</strong></p>
<p><strong>더블리 링크드 리스트(doubly linked list) : 이중 연결 리스트. 접근성 향상</strong>
<img src="https://velog.velcdn.com/images/jere-gim/post/be3f6a40-3601-4de0-9749-d3dfa1bb1200/image.png" alt=""></p>
<p><strong>더블리 써큘러 링크드 리스트(doubly circular linked list) : 이중 원형 연결 리스트</strong>
<img src="https://velog.velcdn.com/images/jere-gim/post/68847bc5-1578-452c-b3cb-1148a80aad6c/image.png" alt=""></p>
<h2 id="arraylist-vs-linkedlist-성능-비교">ArrayList vs. LinkedList 성능 비교</h2>
<p><img src="https://velog.velcdn.com/images/jere-gim/post/f52828b7-48e4-4748-bb87-aec1d5b0cc8a/image.png" alt=""></p>
<ul>
<li><p>순차적으로 데이터 추가/삭제 : ArrayList가 빠르다.</p>
</li>
<li><p>비순차적으로 데이터 추가/삭제 : LinkedList가 빠르다.</p>
</li>
<li><p>접근 시간(access time) : ArrayList가 빠르다.</p>
<blockquote>
<p>인덱스가 n인 데이터의 주소 = 배열의 주소 + n * 데이터 타입의 크기</p>
</blockquote>
</li>
</ul>
<p><strong>ArrayList : 배열의 복사를 줄이기 위해 배열을 크게 설정하게 되면 비효율적인 메모리 사용이 된다.(배열 기반 - 연속적)</strong></p>
<p><strong>LinkedList : 데이터가 많을수록 접근성이 떨어진다.(연결 기반 - 불연속적)</strong></p>
<hr>
<h1 id="📌스택과-큐stack--queue">📌스택과 큐(Stack &amp; Queue)</h1>
<p><strong>스택(Stack) : LIFO(Last In First Out) 구조. 마지막에 저장된 것을 제일 먼저 꺼낸다. ArrayList로 구현하는게 유리</strong></p>
<p><strong>큐(Queue) : FIFO(First In First Out) 구조. 제일 먼저 저장한 것을 제일 먼저 꺼낸다. LinkedList로 구현하는게 유리</strong></p>
<p><img src="https://velog.velcdn.com/images/jere-gim/post/045f6055-24ce-446d-ba0b-72165dc2195d/image.png" alt=""></p>
<h2 id="스택stack의-메서드">스택(Stack)의 메서드</h2>
<table>
<thead>
<tr>
<th align="center">메서드</th>
<th align="center">설명</th>
</tr>
</thead>
<tbody><tr>
<td align="center">boolean empty()</td>
<td align="center">Stack이 비어있는지 알려준다.</td>
</tr>
<tr>
<td align="center">Object peek()</td>
<td align="center">Stack 맨 위에 저장된 객체를 반환한다. pop()과 달리 객체를 꺼내지는 앟는다.(비어있는 Stack이면 EmptyStackException 발생)</td>
</tr>
<tr>
<td align="center">Object pop()</td>
<td align="center">Stack 맨 위에 저장된 객체를 꺼낸다.(비어있는 Stack이면 EmptyStackException 발생)</td>
</tr>
<tr>
<td align="center">Object push(Object item)</td>
<td align="center">Stack에 item을 저장한다.</td>
</tr>
<tr>
<td align="center">int search(Object o)</td>
<td align="center">Stack에서 주어진 객체(o)를 찾아서 그 위치를 반환한다. 못 찾으면 -1 반환(배열과 달리 위치가 0이 아닌 1부터 시작한다.)</td>
</tr>
</tbody></table>
<p><strong>Stack 클래스가 있기 때문에 <code>Stack st = new Stack();</code> 사용 가능</strong></p>
<h2 id="큐queue의-메서드">큐(Queue)의 메서드</h2>
<table>
<thead>
<tr>
<th align="center">메서드</th>
<th align="center">설명</th>
</tr>
</thead>
<tbody><tr>
<td align="center">boolean add(Object o)</td>
<td align="center">저장된 객체를 Queue에 추가한다. 성공하면 true, 저장 공간이 부족하면 IlliegalStateException 발생</td>
</tr>
<tr>
<td align="center">Object remove()</td>
<td align="center">Queue에서 객체를 꺼내 반환한다. 비어있으면 NoSuchElementException 발생</td>
</tr>
<tr>
<td align="center">Object element()</td>
<td align="center">삭제 없이 요소를 읽어온다. peek()과 달리 Queue가 비어있으면 NoSuchElementException 발생</td>
</tr>
<tr>
<td align="center">boolean offer(Object o)</td>
<td align="center">Queue에 객체를 저장한다. 성공하면 true. 실패하면 false 반환. 예외 발생x</td>
</tr>
<tr>
<td align="center">Object poll()</td>
<td align="center">Queue에서 객체를 꺼내서 반환한다. 비어있으면 null 반환. 예외 발생x</td>
</tr>
<tr>
<td align="center">Object peek()</td>
<td align="center">삭제 없이 요소를 읽어온다. Queue가 비어있으면 null 반환. 예외 발생x</td>
</tr>
</tbody></table>
<p><strong>Queue는 인터페이스이기 때문에 Queue를 구현한 클래스로 객체를 생성해야 한다.</strong></p>
<ul>
<li>Queue를 구현한 클래스 목록
<img src="https://velog.velcdn.com/images/jere-gim/post/32797dae-9442-48bf-9a1a-b1b5c7b25a2e/image.png" alt=""></li>
</ul>
<pre><code class="language-java">Queue q = new LinkedList();
q.offer(&quot;A&quot;);</code></pre>
<ul>
<li>참조변수 타입을 Queue로 하게 되면 나중에 객체를 다른 클래스로 변경해도 그 아래의 코드들을 검토할 필요가 없다. 참조 변수 타입이 Queue라면 Queue에 있는 메서드들만 사용했다는 의미가 되기 때문이다.</li>
</ul>
<h2 id="스택과-큐의-활용">스택과 큐의 활용</h2>
<p><strong>스택의 활용 예 : 수식 계산, 수식 괄호 검사, 워드프로세서의 undo/redo, 웹브라우저의 뒤로/앞으로</strong></p>
<h3 id="수식-괄호-검사">수식 괄호 검사</h3>
<pre><code class="language-java">Stack&lt;String&gt; st = new Stack&lt;&gt;();
String expression = &quot;((2+4)*7)/2&quot;;
System.out.println(&quot;expression = &quot; + expression);

try {
    for (int i = 0; i &lt; expression.length(); i++) {
       if (expression.charAt(i) == &#39;(&#39;)
            st.push(expression.charAt(i) + &quot;&quot;);
        else if (expression.charAt(i) == &#39;)&#39;)
            st.pop();
    }
    if (st.empty())
        System.out.println(&quot;괄호가 일치합니다.&quot;);
    else
        System.out.println(&quot;괄호가 불일치합니다.&quot;);

} catch (EmptyStackException e) {
    System.out.println(&quot;괄호가 불일치합니다. 예외 발생&quot;);
}</code></pre>
<blockquote>
<p>expression = ((2+4)*7)/2
괄호가 일치합니다.</p>
</blockquote>
<blockquote>
<p>expression = ((2+4)*7/2
괄호가 불일치합니다.</p>
</blockquote>
<ul>
<li>스택 안에 &#39;(&#39;가 아직 남아있어서 괄호가 불일치합니다 라고 출력된다.</li>
</ul>
<blockquote>
<p>expression = ((2+4)*7))))/2
괄호가 불일치합니다. 예외 발생</p>
</blockquote>
<ul>
<li>스택이 비어있는데도 <code>st.pop()</code>이 실행되서 <code>EmptyStackExcpetion</code> 예외가 발생</li>
</ul>
<p><strong>큐의 활용 예 : 최근 사용 문서, 인쇄 작업 대기 목록, 버퍼(buffer)</strong></p>
<h3 id="최근-사용-명령어-출력">최근 사용 명령어 출력</h3>
<pre><code class="language-java">public static void save(String input) {
    if (!&quot;&quot;.equals(input))
        q.offer(input);

    if (q.size() &gt; MAX_SIZE)
        q.poll();
}</code></pre>
<ul>
<li><code>if (!&quot;&quot;.equals(input))</code> : 빈 문자열이 입력 받은 것과 같지 않다면 동작. <code>!input.equals(&quot;&quot;)</code> 이렇게 적어도 되지만 이러면 input이 null 일때 <code>NullPointerException</code>이 발생한다. <code>!&quot;&quot;.equals(input)</code> 이렇게 작성해주면 input이 null 이어도 예외가 발생하지 않고 빈 문자열인 <code>&quot;&quot;</code> 도 null이 될수 없기 때문에 이렇게 작성해준다.</li>
</ul>
<pre><code class="language-java">final int SIZE = list.size();
for (int i = 0; i &lt; SIZE; i++) {
    System.out.println((i + 1) + &quot;. &quot; + list.get(i));
}</code></pre>
<ul>
<li>범위를 <code>i &lt; list.size()</code> 이렇게 지정해주면 for 문을 돌 때 마다 list.size()를 호출하게 된다. for 문을 돌 때 size()는 바뀌지 않으므로 <code>final int SIZE = list.size();</code> 로 size()를 한번만 알아내고 for 문을 돌 때는 <code>i &lt; SIZE</code> 이렇게 상수를 이용하는게 좋은 코드이다.</li>
</ul>
<hr>
<h1 id="📌iterator">📌Iterator</h1>
<p><strong>컬렉션에 저장된 데이터를 접근하는데 사용되는 인터페이스</strong></p>
<table>
<thead>
<tr>
<th align="center">메서드</th>
<th align="center">설명</th>
</tr>
</thead>
<tbody><tr>
<td align="center">boolean hasNext()</td>
<td align="center">읽어올 요소가 남아있는지 확인한다. 있으면 true, 없으면 false 반환</td>
</tr>
<tr>
<td align="center">Object next()</td>
<td align="center">다음 요소를 읽어온다.</td>
</tr>
</tbody></table>
<p><strong>컬렉션에 저장된 요소들을 읽어오는 방법을 표준화한 것</strong></p>
<pre><code class="language-java">List list = new ArrayList(); // 다른 컬렉션으로 바꿀 때 이 부분만 고치면 된다.
Iterator it = list.iterator();

while (it.hasNext())
    System.out.println(it.next());</code></pre>
<ul>
<li>컬렉션마다 저장된 객체들을 읽어오는 방법이 다르다. Iterator 인터페이스를 쓰게 되면 다른 컬렉션으로 변경되도 읽어오는 방법은 다 똑같기 때문에 코드의 변경이 줄어든다.</li>
</ul>
<pre><code class="language-java">public interface Collection {
    ...
    public Iterator iterator();
    ...
}    </code></pre>
<ul>
<li><code>iterator()</code> 메서드는 <code>Collection</code> 인터페이스 안에 정의되어 있기 때문에 List 인터페이스와 Set 인터페이스에 전부 구현 되어있는 메서드이다.</li>
</ul>
<h2 id="실습-예제-1">실습 예제</h2>
<pre><code class="language-java">import java.util.*;

class Ex11_5 {
    public static void main(String[] args) {
        Collection&lt;Integer&gt; c = new ArrayList&lt;&gt;();
//        Collection&lt;Integer&gt; c = new LinkedList&lt;&gt;();
        c.add(1);
        c.add(2);
        c.add(3);
        c.add(4);

        Iterator&lt;Integer&gt; it = c.iterator();
        while (it.hasNext()) {
            System.out.println(it.next());
        }
    }
}</code></pre>
<ul>
<li><code>Collection&lt;Integer&gt; c = new ArrayList&lt;&gt;();</code> : 참조변수 타입을 Collecion으로 하게 되면 컬렉션을 LinkedList 로 바꿔도 그 다음의 코드를 바꾸거나 검토할 필요가 없게 된다. 참조 변수 타입이 Collecion 이기 때문에 그 다음의 코드는 Collection에 있는 메서드를 사용했음을 의미하고 따라서 객체를 다른 컬렉션으로 바꿔도 동작한다는 의미이기 때문이다.</li>
</ul>
<h2 id="map과-iterator">Map과 Iterator</h2>
<p><strong>Map에는 iterator()가 없다.</strong></p>
<p><strong><code>Set keySet()</code>, <code>Set entrySet()</code>, <code>Collection values()</code>로 키 또는 값을 Set, Collecion 형식으로 바꾼 후에 iterator() 사용</strong></p>
<p><strong>짝수의 값(value)을 전부 더하는 예제</strong></p>
<h3 id="collection-values-사용">Collection values() 사용</h3>
<pre><code class="language-java">HashMap&lt;Character, Integer&gt; hashMap = new HashMap&lt;Character, Integer&gt;(){{
    put(&#39;a&#39;, 1);
    put(&#39;b&#39;, 4);
    put(&#39;c&#39;, 6);
    put(&#39;d&#39;, 9);
}};

int sum = 0;
Collection&lt;Integer&gt; c = hashMap.values();
Iterator&lt;Integer&gt; it = c.iterator();

while (it.hasNext()) {
int i = it.next();
if (i % 2 == 0)
    sum += i;
}
System.out.println(&quot;sum = &quot; + sum);</code></pre>
<blockquote>
<p>sum = 10</p>
</blockquote>
<h3 id="set-keyset-사용">Set keySet() 사용</h3>
<pre><code class="language-java">HashMap&lt;Character, Integer&gt; hashMap = new HashMap&lt;Character, Integer&gt;(){{
    put(&#39;a&#39;, 1);
    put(&#39;b&#39;, 4);
    put(&#39;c&#39;, 6);
    put(&#39;d&#39;, 9);
}};

int sum = 0;
Set&lt;Character&gt; keySet = hashMap.keySet();
Iterator&lt;Character&gt; it = keySet.iterator();

while (it.hasNext()) {
int i = hashMap.get(it.next());
if (i % 2 == 0)
    sum += i;
}
System.out.println(&quot;sum = &quot; + sum);</code></pre>
<blockquote>
<p>sum = 10</p>
</blockquote>
<h3 id="set-entryset-사용">Set entrySet() 사용</h3>
<pre><code class="language-java">HashMap&lt;Character, Integer&gt; hashMap = new HashMap&lt;Character, Integer&gt;(){{
    put(&#39;a&#39;, 1);
    put(&#39;b&#39;, 4);
    put(&#39;c&#39;, 6);
    put(&#39;d&#39;, 9);
}};

int sum = 0;
Set&lt;Map.Entry&lt;Character, Integer&gt;&gt; entrySet = hashMap.entrySet();
Iterator&lt;Map.Entry&lt;Character, Integer&gt;&gt; it = entrySet.iterator();

while (it.hasNext()) {
int i = it.next().getValue();
if (i % 2 == 0)
    sum += i;
}
System.out.println(&quot;sum = &quot; + sum);</code></pre>
<blockquote>
<p>sum = 10</p>
</blockquote>
<ul>
<li><code>getValue()</code> : Entry 객체의 Value 객체를 반환</li>
</ul>
]]></description>
        </item>
    </channel>
</rss>