<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>min-ji99.log</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Thu, 25 May 2023 08:19:41 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>min-ji99.log</title>
            <url>https://images.velog.io/images/min-ji99/profile/9570f988-3422-451d-bbeb-c94ea1895a8a/선글라스.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. min-ji99.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/min-ji99" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[알고리즘] 슬라이딩 윈도우]]></title>
            <link>https://velog.io/@min-ji99/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EC%8A%AC%EB%9D%BC%EC%9D%B4%EB%94%A9-%EC%9C%88%EB%8F%84%EC%9A%B0</link>
            <guid>https://velog.io/@min-ji99/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EC%8A%AC%EB%9D%BC%EC%9D%B4%EB%94%A9-%EC%9C%88%EB%8F%84%EC%9A%B0</guid>
            <pubDate>Thu, 25 May 2023 08:19:41 GMT</pubDate>
            <description><![CDATA[<h3 id="슬라이딩-윈도우">슬라이딩 윈도우</h3>
<p>슬라이딩 윈도우란 고정된 사이즈의 윈도우가 이동하면서 윈도우 내에 있는 데이터를 이용해 문제를 풀이하는 알고리즘이다. 투 포인터와 유사하지만 일반적으로 고정 사이즈 윈도우를 사용하는 경우 슬라이딩 윈도우를 구분한다. 또한, 투포인터는 주로 정렬된 리스트를 대상으로 하지만 슬라이딩 윈도우는 정렬 여부에 관계 없이 사용 가능하다.</p>
<p><strong>예시</strong></p>
<p>[1, 5, -3, 6, 10, -14, -2] 라는 배열이 있는데 연속적인 4개의 숫자의 합이 최대인 값을 찾는 경우를 들 수 있다.</p>
<table>
<thead>
<tr>
<th>부분 리스트</th>
<th>합</th>
</tr>
</thead>
<tbody><tr>
<td>[1, 5, -3, 6], 10, -14, -2</td>
<td>9</td>
</tr>
<tr>
<td>1, [5, -3, 6, 10], -14, -2</td>
<td>18</td>
</tr>
<tr>
<td>1, 5, [-3, 6, 10, -14], -2</td>
<td>-1</td>
</tr>
<tr>
<td>1, 5, -3, [6, 10, -14, -2]</td>
<td>0</td>
</tr>
</tbody></table>
<p><strong>구현</strong></p>
<pre><code class="language-python">array=[1, 5, -3, 6, 10, -14, -2]
n = len(array)
size=5
window=sum(array[:size])
answer=window

for i in range(size, n) :
        window += array[i] - array[i-size]
        answer=max(answer, window)

print(answer)</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Springboot]n+1 문제 해결하기]]></title>
            <link>https://velog.io/@min-ji99/Springboot%EC%BF%BC%EB%A6%AC-%EC%84%B1%EB%8A%A5-%EA%B0%9C%EC%84%A0%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@min-ji99/Springboot%EC%BF%BC%EB%A6%AC-%EC%84%B1%EB%8A%A5-%EA%B0%9C%EC%84%A0%ED%95%98%EA%B8%B0</guid>
            <pubDate>Mon, 17 Apr 2023 13:27:12 GMT</pubDate>
            <description><![CDATA[<p>JPA와 queryDSL을 사용해서 제목과 해시태그로 검색하는 기능을 구현했다. 그런데 검색을 했을 때 select문이 여러개가 나가는 현상이 발생해서 찾아봤다.</p>
<h1 id="jpa-n1-문제">JPA n+1 문제</h1>
<h2 id="n1문제">n+1문제</h2>
<p>연관관계가 설정된 엔티티를 조회할 경우에 조회된 데이터의 개수(n)만큼 연관관계 조회 쿼리가 추가로 발생하여 데이터를 읽어오는 현상을 말한다. </p>
<h2 id="테이블-간의-관계">테이블 간의 관계</h2>
<p>챌린지에 대한 정보가 들어있는 챌린지 테이블과 해시태그의 정보가 있는 해시태그 테이블이 있고 챌린지와 해시태그를 매핑해주는 중간테이블이 존재한다. 
<img src="https://velog.velcdn.com/images/min-ji99/post/b2126e96-0f9d-47b4-8bde-d5339b616378/image.png" alt=""></p>
<h2 id="상황-설명">상황 설명</h2>
<p>챌린지를 조회하면 해당 챌린지의 해시태그도 가져와서 보여주어야한다. 또한, 검색 keyword가 챌린지 제목에 포함된 경우와 keyword와 일치하는 해시태그가 존재하는 챌린지를 검색해서 보여준다.</p>
<h2 id="해결">해결</h2>
<h3 id="applicationyml-추가-설정">application.yml 추가 설정</h3>
<p>쿼리를 좀 더 편하게 관리하기 위해 추가 설정을 했다.</p>
<pre><code>spring:
    jpa:
        show-sql: true //퀴리 출력
        properties:
          hibernate:
            format_sql: true //출력되는 쿼리의 포맷 예쁘게
            generate_statistics: true</code></pre><p>만약 application.properties의 경우</p>
<pre><code>spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.properties.hibernate.generate_statistics=true</code></pre><h3 id="수정-전">수정 전</h3>
<pre><code class="language-java">@Override
public List&lt;Challenge&gt; findSearchTitleSortById(String keyword) {
    QChallengeHashTag challengehashtag=QChallengeHashTag.challengeHashTag;
    QChallenge challenge=QChallenge.challenge;
    QHashTag hashTag= QHashTag.hashTag;
    return queryFactory.selectFrom(challenge)
            .leftJoin(challengehashtag).on(challengehashtag.challenge.id.eq(challenge.id))
            .leftJoin(hashTag).on(challengehashtag.hashTag.id.eq(hashTag.id))
            .where(challenge.title.contains(keyword).or(hashTag.contents.eq(keyword)), challenge.vigibility.eq(true), challenge.startDate.after(LocalDate.now()))
            .orderBy(challenge.id.desc())
            .fetch();

}</code></pre>
<pre><code class="language-shell">Hibernate: 
    select
        c1_0.id,
        c1_0.authentication_type,
        c1_0.created_at,
        c1_0.deleted_at,
        c1_0.description,
        c1_0.end_date,
        c1_0.modified_at,
        c1_0.start_date,
        c1_0.title,
        c1_0.user_id,
        c1_0.vigibility 
    from
        challenge c1_0 
    left join
        challenge_hashtag c2_0 
            on c2_0.challenge_id=c1_0.id 
    left join
        hashtag h1_0 
            on c2_0.hash_tag_id=h1_0.id 
    where
        (
            c1_0.deleted_at is NULL
        ) 
        and (
            c1_0.title like ? escape &#39;!&#39; 
            or h1_0.contents=?
        ) 
        and c1_0.vigibility=? 
        and c1_0.start_date&gt;? 
    order by
        c1_0.id desc
Hibernate: 
    select
        h1_0.id,
        h1_0.contents 
    from
        hashtag h1_0 
    where
        h1_0.id=?
Hibernate: 
    select
        h1_0.id,
        h1_0.contents 
    from
        hashtag h1_0 
    where
        h1_0.id=?
2023-04-23T18:02:58.662+09:00  WARN 78053 --- [nio-8080-exec-1] actStandardFragmentInsertionTagProcessor : [THYMELEAF][http-nio-8080-exec-1][challenges/list] Deprecated unwrapped fragment expression &quot;fragments/head :: frag-header&quot; found in template challenges/list, line 5, col 15. Please use the complete syntax of fragment expressions instead (&quot;~{fragments/head :: frag-header}&quot;). The old, unwrapped syntax for fragment expressions will be removed in future versions of Thymeleaf.
2023-04-23T18:02:58.714+09:00  WARN 78053 --- [nio-8080-exec-1] actStandardFragmentInsertionTagProcessor : [THYMELEAF][http-nio-8080-exec-1][challenges/list] Deprecated unwrapped fragment expression &quot;fragments/nav :: menu&quot; found in template challenges/list, line 9, col 72. Please use the complete syntax of fragment expressions instead (&quot;~{fragments/nav :: menu}&quot;). The old, unwrapped syntax for fragment expressions will be removed in future versions of Thymeleaf.
2023-04-23T18:02:58.737+09:00  WARN 78053 --- [nio-8080-exec-1] actStandardFragmentInsertionTagProcessor : [THYMELEAF][http-nio-8080-exec-1][challenges/list] Deprecated unwrapped fragment expression &quot;fragments/footer :: frag-footer&quot; found in template challenges/list, line 52, col 19. Please use the complete syntax of fragment expressions instead (&quot;~{fragments/footer :: frag-footer}&quot;). The old, unwrapped syntax for fragment expressions will be removed in future versions of Thymeleaf.
2023-04-23T18:02:58.740+09:00  INFO 78053 --- [nio-8080-exec-1] i.StatisticalLoggingSessionEventListener : Session Metrics {
    7215250 nanoseconds spent acquiring 1 JDBC connections;
    0 nanoseconds spent releasing 0 JDBC connections;
    440875 nanoseconds spent preparing 5 JDBC statements;
    39466042 nanoseconds spent executing 5 JDBC statements;
    0 nanoseconds spent executing 0 JDBC batches;
    0 nanoseconds spent performing 0 L2C puts;
    0 nanoseconds spent performing 0 L2C hits;
    0 nanoseconds spent performing 0 L2C misses;
    0 nanoseconds spent executing 0 flushes (flushing a total of 0 entities and 0 collections);
    82166 nanoseconds spent executing 3 partial-flushes (flushing a total of 0 entities and 0 collections)
}</code></pre>
<p>select문이 여러개가 나오는걸 확인할 수 있다. 이경우 query dsl의 fetch join을 사용하여 해결할 수 있다. </p>
<h3 id="fetch-join">fetch join</h3>
<p>조인하는 대상 데이터를 즉시 로딩해서 가져온다.</p>
<p><strong>첫번째 시도</strong></p>
<pre><code class="language-java">@Override
public List&lt;Challenge&gt; findSearchTitleSortById(String keyword) {
    QChallengeHashTag challengehashtag=QChallengeHashTag.challengeHashTag;
    QChallenge challenge=QChallenge.challenge;
    QHashTag hashTag= QHashTag.hashTag;
    return queryFactory.selectFrom(challenge)
            .leftJoin(challengehashtag).on(challengehashtag.challenge.id.eq(challenge.id))
            .fetchJoin()
            .leftJoin(hashTag).on(challengehashtag.hashTag.id.eq(hashTag.id))
            .fetchJoin()
            .where(challenge.title.contains(keyword).or(hashTag.contents.eq(keyword)), challenge.vigibility.eq(true), challenge.startDate.after(LocalDate.now()))
            .orderBy(challenge.id.desc())
            .fetch();

}</code></pre>
<p>fetchJoin()을 사용했는데도 똑같았다... 그래서 찾아본 결과 querydsl에서 on()절로 매핑하면 두 엔티티가 연관관계라는 것을 인식하지 못한다. 그렇기 때문에 leftJoin대신 join으로 수정해서 해결했다.</p>
<p><strong>두번째 시도</strong></p>
<pre><code class="language-java">@Override
public List&lt;Challenge&gt; findSearchTitleSortById(String keyword) {
    QChallengeHashTag challengehashtag=QChallengeHashTag.challengeHashTag;
    QChallenge challenge=QChallenge.challenge;
    QHashTag hashTag= QHashTag.hashTag;
    return queryFactory.selectFrom(challenge)
            .leftJoin(challengehashtag).on(challengehashtag.challenge.id.eq(challenge.id))
            .fetchJoin()
            .leftJoin(hashTag).on(challengehashtag.hashTag.id.eq(hashTag.id))
            .fetchJoin()
            .where(challenge.title.contains(keyword).or(hashTag.contents.eq(keyword)), challenge.vigibility.eq(true), challenge.startDate.after(LocalDate.now()))
            .orderBy(challenge.id.desc())
            .fetch();

}</code></pre>
<pre><code class="language-shell">Hibernate: 
    select
        c1_0.id,
        c1_0.authentication_type,
        c2_0.challenge_id,
        c2_0.id,
        h1_0.id,
        h1_0.contents,
        c1_0.created_at,
        c1_0.deleted_at,
        c1_0.description,
        c1_0.end_date,
        c1_0.modified_at,
        c1_0.start_date,
        c1_0.title,
        c1_0.user_id,
        c1_0.vigibility 
    from
        challenge c1_0 
    join
        challenge_hashtag c2_0 
            on c1_0.id=c2_0.challenge_id 
    join
        hashtag h1_0 
            on h1_0.id=c2_0.hash_tag_id 
    where
        (
            c1_0.deleted_at is NULL
        ) 
        and (
            c1_0.title like ? escape &#39;!&#39; 
            or h1_0.contents=?
        ) 
        and c1_0.vigibility=? 
        and c1_0.start_date&gt;? 
    order by
        c1_0.id desc
2023-04-23T17:56:46.808+09:00  WARN 77804 --- [nio-8080-exec-6] actStandardFragmentInsertionTagProcessor : [THYMELEAF][http-nio-8080-exec-6][challenges/list] Deprecated unwrapped fragment expression &quot;fragments/head :: frag-header&quot; found in template challenges/list, line 5, col 15. Please use the complete syntax of fragment expressions instead (&quot;~{fragments/head :: frag-header}&quot;). The old, unwrapped syntax for fragment expressions will be removed in future versions of Thymeleaf.
2023-04-23T17:56:46.808+09:00  WARN 77804 --- [nio-8080-exec-6] actStandardFragmentInsertionTagProcessor : [THYMELEAF][http-nio-8080-exec-6][challenges/list] Deprecated unwrapped fragment expression &quot;fragments/nav :: menu&quot; found in template challenges/list, line 9, col 72. Please use the complete syntax of fragment expressions instead (&quot;~{fragments/nav :: menu}&quot;). The old, unwrapped syntax for fragment expressions will be removed in future versions of Thymeleaf.
2023-04-23T17:56:46.819+09:00  WARN 77804 --- [nio-8080-exec-6] actStandardFragmentInsertionTagProcessor : [THYMELEAF][http-nio-8080-exec-6][challenges/list] Deprecated unwrapped fragment expression &quot;fragments/footer :: frag-footer&quot; found in template challenges/list, line 52, col 19. Please use the complete syntax of fragment expressions instead (&quot;~{fragments/footer :: frag-footer}&quot;). The old, unwrapped syntax for fragment expressions will be removed in future versions of Thymeleaf.
2023-04-23T17:56:46.844+09:00  INFO 77804 --- [nio-8080-exec-6] i.StatisticalLoggingSessionEventListener : Session Metrics {
    9087750 nanoseconds spent acquiring 1 JDBC connections;
    0 nanoseconds spent releasing 0 JDBC connections;
    447209 nanoseconds spent preparing 3 JDBC statements;
    26491625 nanoseconds spent executing 3 JDBC statements;
    0 nanoseconds spent executing 0 JDBC batches;
    0 nanoseconds spent performing 0 L2C puts;
    0 nanoseconds spent performing 0 L2C hits;
    0 nanoseconds spent performing 0 L2C misses;
    0 nanoseconds spent executing 0 flushes (flushing a total of 0 entities and 0 collections);
    12667 nanoseconds spent executing 3 partial-flushes (flushing a total of 0 entities and 0 collections)
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Web] URL과 URI 차이]]></title>
            <link>https://velog.io/@min-ji99/Web-URL%EA%B3%BC-URI-%EC%B0%A8%EC%9D%B4</link>
            <guid>https://velog.io/@min-ji99/Web-URL%EA%B3%BC-URI-%EC%B0%A8%EC%9D%B4</guid>
            <pubDate>Wed, 05 Apr 2023 03:19:46 GMT</pubDate>
            <description><![CDATA[<p>URL과 URI는 종종 혼용해서 사용한다. 하지만 둘은 차이점이 존재한다. </p>
<h2 id="url">URL</h2>
<p>URL은 Uniform Resouce Locator의 약자로 흔히 웹 주소라고도 하며, 컴퓨터 네트워크 상에서 리소스가 어디 있는지 알려주기 위한 규약이다. URL은 웹 사이트 주소뿐만 아니라 컴퓨터 네트워크상의 자원을 모두 나타내는 표기법이다.</p>
<h2 id="uri">URI</h2>
<p>URI는 Uniform Resource Identifier의 약자로 특정 리소스를 식별하는 통합 자원 식별자를 의미한다. 웹 기술에서 사용하는 논리적 또는 물리적 리소스를 식별하는 고유한 문자열 시퀀스다.</p>
<p>➡️ <strong>URL은 URI에 포함된다</strong></p>
<h2 id="예시">예시</h2>
<p>1) <a href="http://example.com">http://example.com</a></p>
<p>위의 경우 <strong>URL이면서 URI이다.</strong></p>
<p>2) <a href="http://example.com/index">http://example.com/index</a>
이 경우는 example.com 서버의 index라는 네트워크 상의 자원의 위치를 의미하기 때문에 <strong>URL이면서 URI이다.</strong></p>
<p>3) <a href="http://example.com/user/1">http://example.com/user/1</a>
이 경우는 user 정보에 식별자 1을 포함하기 때문에 URId이다. 즉, <strong>URI이지만 URL은 아니다.</strong></p>
<p>4) <a href="http://example.com/user?id=1">http://example.com/user?id=1</a>
이 경우에 <a href="http://example.com/user%EA%B9%8C%EC%A7%80%EB%8A%94">http://example.com/user까지는</a> 자원의 실제 위치를 나타내기 때문에 URL이라고 할 수 있지만, 뒤는 쿼리 스트링 식별자를 포함하기 때문에 URI이다,.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Springboot] QueryDsl 적용]]></title>
            <link>https://velog.io/@min-ji99/Springboot-QueryDsl-%EC%A0%81%EC%9A%A9</link>
            <guid>https://velog.io/@min-ji99/Springboot-QueryDsl-%EC%A0%81%EC%9A%A9</guid>
            <pubDate>Wed, 01 Mar 2023 14:21:20 GMT</pubDate>
            <description><![CDATA[<h1 id="query-dsl">Query DSL</h1>
<p>QueryDsl은 정적 타입을 이용해서 SQL과 같은 쿼리를 생성할 수 있도록 해주는 프레임워크이다.</p>
<p>JPA가 기본적으로 제공해주는 기능을 사용하더라도, 원하는 조건의 데이터를 수집하기 위해서는 필연적으로 JPQL을 작성하게 된다. 간단한 로직을 구현할 때는 상관없지만 복잡한 로직의 경우 쿼리가 길어진다. 또한 쿼리를 실행하기 전까지 에러를 발견할 수 없다.</p>
<h2 id="querydsl장점"><strong>QueryDSL</strong>장점</h2>
<ol>
<li>문자가 아닌 코드로 쿼리를 작성함으로써, 컴파일 시점에 문법 오류를 쉽게 확인할 수 있다.</li>
<li>자동 완성 등 IDE의 도움을 받을 수 있다.</li>
<li>동적인 쿼리 작성이 편리하다.</li>
<li>쿼리 작성 시 제약 조건 등을 메서드 추출을 통해 재사용할 수 있다.</li>
</ol>
<h2 id="querydsl-설정">QueryDsl 설정</h2>
<blockquote>
<p>💡 <strong>버전</strong>
springboot 3.0.1
java 17</p>
</blockquote>
<p>래퍼런스를 참조해서 querydsl denpendency를 추가하려 하는데 springboot 3.0 이상 버전에서는 기존과는 다르게 설정해주어야했다. </p>
<p><strong>build.gradle</strong></p>
<pre><code class="language-java">dependencies {
    //...
    //querydsl dependencies 추가(스프링부트 3.0 이상)
    implementation &#39;com.querydsl:querydsl-jpa:5.0.0:jakarta&#39;
    annotationProcessor &quot;com.querydsl:querydsl-apt:${dependencyManagement.importedProperties[&#39;querydsl.version&#39;]}:jakarta&quot;

    annotationProcessor &quot;jakarta.annotation:jakarta.annotation-api&quot;
    annotationProcessor &quot;jakarta.persistence:jakarta.persistence-api&quot;
}</code></pre>
<p><strong>QueryDslConfig</strong></p>
<pre><code class="language-java">dependencies {
    //...
    //querydsl dependencies 추가(스프링부트 3.0 이상)
    implementation &#39;com.querydsl:querydsl-jpa:5.0.0:jakarta&#39;
    annotationProcessor &quot;com.querydsl:querydsl-apt:${dependencyManagement.importedProperties[&#39;querydsl.version&#39;]}:jakarta&quot;

    annotationProcessor &quot;jakarta.annotation:jakarta.annotation-api&quot;
    annotationProcessor &quot;jakarta.persistence:jakarta.persistence-api&quot;
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Springboot] RequestBody boolean 바인딩 에러]]></title>
            <link>https://velog.io/@min-ji99/Springboot-RequestBody-boolean-%EB%B0%94%EC%9D%B8%EB%94%A9-%EC%97%90%EB%9F%AC</link>
            <guid>https://velog.io/@min-ji99/Springboot-RequestBody-boolean-%EB%B0%94%EC%9D%B8%EB%94%A9-%EC%97%90%EB%9F%AC</guid>
            <pubDate>Thu, 26 Jan 2023 01:42:07 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/min-ji99/post/31b8f941-c2fe-4ed8-881c-24eb01cb611a/image.png" alt="">
<img src="https://velog.velcdn.com/images/min-ji99/post/1d7fd0ef-1d84-4168-b727-f6c6a1de4c81/image.png" alt=""></p>
<p>RequestBody로 boolean 타입을 true로 보냈는데 false로 DB에 저장되는 것을 발견했다. </p>
<h2 id="원인">원인</h2>
<p>@Getter, @Setter 어노테이션을 사용하는 경우 boolean 타입에 붙는 prefix는 get이 아니라 is이기 때문에 @RequestBody에서 찾을 수가 없어 바인딩이 되지 않아 false로 저장된다. </p>
<h2 id="해결-방법">해결 방법</h2>
<ol>
<li><p>Boolean타입 사용하기</p>
<pre><code class="language-java">//...
Boolean isVigibility;</code></pre>
</li>
<li><p>getIsXXX() getter 메소드 직접 작성하기</p>
<pre><code class="language-java">public boolean getIsVigilibity(){
 return this.isVigility;
}</code></pre>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[[SpringBoot] 다대다 매핑]]></title>
            <link>https://velog.io/@min-ji99/%EC%97%B0%EA%B4%80%EB%A7%A4%ED%95%91</link>
            <guid>https://velog.io/@min-ji99/%EC%97%B0%EA%B4%80%EB%A7%A4%ED%95%91</guid>
            <pubDate>Thu, 19 Jan 2023 04:13:06 GMT</pubDate>
            <description><![CDATA[<h1 id="서론">서론</h1>
<hr>
<p>동기부여를 위한 챌린지 서비스에서 사용자가 챌린지를 생성하기도 하고 이미 존재하는 챌린지에 참여를 할 수도 있다. 이 때 유저와 챌린지 테이블간의 다대다 관계가 발생했다. 
다대다 관계는 서로가 서로를 맵핑하기 때문에 단순하게 서로가 서로를 갖도록 구현하면 JPA에서 여러가지 에러가 발생하게 된다.</p>
<h1 id="해결">해결</h1>
<hr>
<p>중간 테이블을 생성해서 N:M 관계를 1:N, M:1로 분리시켰다. 즉, 유저와 챌린지의 관계를 중간에 참여 테이블을 생성해서 유저와 참여, 참여와 챌린지 테이블로 나누게 된다. 
챌린지 테이블에서는 챌린지의 정보만 갖게 되고 참여테이블에서는 어떤 유저가 어떤 챌린지를 참여하고 있는지를 관리하도록 했다.</p>
<p><strong>Challenge</strong></p>
<pre><code class="language-java">@Entity
@Getter
@Builder
@AllArgsConstructor
@NoArgsConstructor
@EntityListeners(AuditingEntityListener.class)
@Where(clause=&quot;deleted_at is NULL&quot;)
public class Challenge {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String title;
    private String description;
    private boolean vigibility;
    @Enumerated(EnumType.STRING)
    private AuthenticationType authenticationType;

    private Long userId;

    private LocalDate startDate;
    private LocalDate endDate;

    @CreatedDate
    private LocalDateTime createdAt;

    @LastModifiedDate
    private LocalDateTime modifiedAt;

    private LocalDateTime deletedAt;

    //...
}</code></pre>
<p><strong>User</strong></p>
<pre><code class="language-java">@Builder
@AllArgsConstructor
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
@Entity
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false)
    private String name;

    @Column(nullable = false)
    private String email;

    private String picture;

    private String oauthId;

    @Enumerated(EnumType.STRING)
    @Column(nullable = false)
    private UserRole role;

    //...
}</code></pre>
<p><strong>Participation</strong></p>
<pre><code class="language-java">@Entity
@Builder
@Getter
@AllArgsConstructor
@NoArgsConstructor
public class Participation {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name=&quot;user_id&quot;)
    private User user;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name=&quot;challenge_id&quot;)
    private Challenge challenge;

    //...
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Git]git commit/git push 취소하기]]></title>
            <link>https://velog.io/@min-ji99/Gitgit-commitgit-push-%EC%B7%A8%EC%86%8C%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@min-ji99/Gitgit-commitgit-push-%EC%B7%A8%EC%86%8C%ED%95%98%EA%B8%B0</guid>
            <pubDate>Sat, 12 Nov 2022 05:31:26 GMT</pubDate>
            <description><![CDATA[<h2 id="git-commit-취소하기">git commit 취소하기</h2>
<ul>
<li>commit 목록 확인<pre><code class="language-shell">git log</code></pre>
</li>
<li>방법 1 : commit을 취소하고 해당 파일들은 staged 상태로 Working Directory에 보존<pre><code class="language-shell">git reset --soft HEAD^</code></pre>
</li>
<li>방법 2 : commit을 취소하고 해당 파일들은 unstaged 상태로 Working Directory에 보존<pre><code class="language-shell">git reset --mixed HEAD^
git reset HEAD^</code></pre>
</li>
<li>방법 3 : commit을 취소하고 해당 파일들은 unstaged 상태로 Working Directory에 삭제<pre><code class="language-shell">git reset --hard HAED^</code></pre>
</li>
</ul>
<p>💡 reset 옵션</p>
<ul>
<li>soft : index 보존(add한 상태, staged 상태), 워킹 디렉터리의 파일 보존.
<img src="https://velog.velcdn.com/images/min-ji99/post/07f7f49a-32b7-4991-8c4c-abbb0f189052/image.png" alt=""></li>
<li>mixed : index 취소(add하기 전 상태, unstaged 상태). 워킹 디렉터리의 파일 보존
<img src="https://velog.velcdn.com/images/min-ji99/post/3ecb9641-2a0e-4407-b884-96ac0c4a6e60/image.png" alt=""></li>
<li>hard : index 취소(add하기 전 상태, unstaged 상태). 워킹 디렉터리의 파일 삭제</li>
</ul>
<h2 id="git-push-취소하기">git push 취소하기</h2>
<p>이 경우는 자신의 local 내용을 remote에 강제로 덮어쓰기 하는 것이기 때문에 주의해야한다. </p>
<h3 id="가장-최근의-commit을-취소하고-working-directory를-되돌리는-경우">가장 최근의 commit을 취소하고 Working Directory를 되돌리는 경우</h3>
<pre><code class="language-shell">git reset HEAD^</code></pre>
<h3 id="원하는-시점으로-working-directory를-되돌리는-경우">원하는 시점으로 Working Directory를 되돌리는 경우</h3>
<ul>
<li>reflog(브랜치와 HEAD가 가리켰었던 커밋) 목록 확인<pre><code class="language-shell">git reflog
//or
git log -g</code></pre>
</li>
<li>원하는 시점으로 Working Directory 되돌리기<pre><code class="language-shell">git reset HEAD@{number}
//or
git reset [commit id]</code></pre>
</li>
<li>되돌린 시점으로 remote 변경하는 경우<pre><code class="language-shell">git push -f origin main
git push -force 브랜치 이름</code></pre>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[백준]1516 게임개발(위상정렬)]]></title>
            <link>https://velog.io/@min-ji99/%EB%B0%B1%EC%A4%801516-%EA%B2%8C%EC%9E%84%EA%B0%9C%EB%B0%9C</link>
            <guid>https://velog.io/@min-ji99/%EB%B0%B1%EC%A4%801516-%EA%B2%8C%EC%9E%84%EA%B0%9C%EB%B0%9C</guid>
            <pubDate>Tue, 08 Nov 2022 09:54:13 GMT</pubDate>
            <description><![CDATA[<h1 id="문제">문제</h1>
<p><a href="https://www.acmicpc.net/problem/1516">https://www.acmicpc.net/problem/1516</a></p>
<h2 id="📝문제">📝문제</h2>
<p>숌 회사에서 이번에 새로운 전략 시뮬레이션 게임 세준 크래프트를 개발하기로 하였다. 핵심적인 부분은 개발이 끝난 상태고, 종족별 균형과 전체 게임 시간 등을 조절하는 부분만 남아 있었다.</p>
<p>게임 플레이에 들어가는 시간은 상황에 따라 다를 수 있기 때문에, 모든 건물을 짓는데 걸리는 최소의 시간을 이용하여 근사하기로 하였다. 물론, 어떤 건물을 짓기 위해서 다른 건물을 먼저 지어야 할 수도 있기 때문에 문제가 단순하지만은 않을 수도 있다. 예를 들면 스타크래프트에서 벙커를 짓기 위해서는 배럭을 먼저 지어야 하기 때문에, 배럭을 먼저 지은 뒤 벙커를 지어야 한다. 여러 개의 건물을 동시에 지을 수 있다.</p>
<p>편의상 자원은 무한히 많이 가지고 있고, 건물을 짓는 명령을 내리기까지는 시간이 걸리지 않는다고 가정하자.</p>
<h3 id="입력">입력</h3>
<p>첫째 줄에 건물의 종류 수 N(1 ≤ N ≤ 500)이 주어진다. 다음 N개의 줄에는 각 건물을 짓는데 걸리는 시간과 그 건물을 짓기 위해 먼저 지어져야 하는 건물들의 번호가 주어진다. 건물의 번호는 1부터 N까지로 하고, 각 줄은 -1로 끝난다고 하자. 각 건물을 짓는데 걸리는 시간은 100,000보다 작거나 같은 자연수이다. 모든 건물을 짓는 것이 가능한 입력만 주어진다.</p>
<h3 id="출력">출력</h3>
<p>N개의 각 건물이 완성되기까지 걸리는 최소 시간을 출력한다.
<strong>예제입력1</strong></p>
<pre><code>5
10 -1
10 1 -1
4 1 -1
4 3 1 -1
3 3 -1</code></pre><p><strong>예제출력</strong></p>
<pre><code>10
20
14
18
17</code></pre><hr>
<h1 id="풀이">풀이</h1>
<p>이 문제는 어떤 건물을 짓기 위해서 다른 건물을 먼저 지어야 할 수도 있기 때문에 순서가 정해져 있는 작업이다. 이런 순서가 정해져 있는 작업을 차례로 수행해야할 때 그 순서를 결정해주기 위한 알고리즘이 위상정렬이다.</p>
<h2 id="위상정렬">위상정렬</h2>
<p>위상 정렬은 <strong>순서가 정해져 있는 작업</strong>을 차례로 수행해야 할 때 그 순서를 결정해주기 위해 사용하는 알고리즘이다. 위의 문제를 예시로 순서를 확인해보면 아래 그림과 같다.이때, 만약 사이클이 발생한다면 위상 정렬을 수행할 수 없다. 
<img src="https://velog.velcdn.com/images/min-ji99/post/b98fb103-2176-4a30-abcc-51963806efeb/image.png" alt="">
위상 정렬 : 1번 집-&gt;2번, 3번 집-&gt;4번, 5번 집
위상 정렬 알고리즘을 구현하는 방법에는 스택을 이용하는 방법과 큐를 이용하는 방법이 있다. </p>
<h3 id="과정">과정</h3>
<p><strong>과정</strong></p>
<ol>
<li>진입차수가 0인 정점을 큐/스택에 삽입한다.</li>
<li>큐/스택에서 원소를 꺼내고 해당 원소와 연결된 모든 간선을 제거한다.</li>
<li>간선 제거 이후 진입차수가 0인 정점을 큐/스택에 삽입한다.</li>
<li>2번~3번 과정을 큐/스택에 더 이상 원소가 없을 때까지 반복한다. </li>
<li>모든 원소를 방문하기 전에 큐/스택가 빈다면 사이클이 존재하는 것이고 모든 원소를 방문했다면 큐/스택에서 꺼낸 순서가 위상 정렬 결과이다.
<img src="https://velog.velcdn.com/images/min-ji99/post/880d6785-06cf-426a-b9ad-6ac33aa59e71/image.png" alt=""></li>
</ol>
<p>그래프에서 1번 집이 진입차수가 0이기 때문에 큐에 해당 정점을 삽입한다.
<code>queue = [1]</code>
큐에서 원소를 꺼내고 해당 원소와 연결된 모든 간선을 제거한다. 즉, 1번집에서 2, 3번집으로 연결된 간선을 제거한다. 진입차수가 0인 2, 3번 정점을 큐에 삽입한다.
<img src="https://velog.velcdn.com/images/min-ji99/post/5ed1027c-64da-44c5-94ad-72ad1e375e87/image.png" alt=""></p>
<p><code>queue=[2, 3]</code>
<code>answer=[1]</code>
큐에서 2번 원소를 꺼내고 2번 원소와 연결된 모든 간선을 제거한다. 
<code>queue=[3]</code>
<code>answer=[1, 2]</code>
이 예시에는 2번과 연결된 간선이 없기 때문에 생략하고 3번 원소를 꺼낸다. 4, 5번 정점과 연결된 간선을 제거하고 진입차수가 0인 정점을 큐에 삽입한다.
<img src="https://velog.velcdn.com/images/min-ji99/post/fdde1598-91e9-4ea5-bda7-61d8f35271e7/image.png" alt=""></p>
<p><code>queue=[4, 5]</code>
<code>answer=[1, 2, 3]</code>
위 처럼 2, 3번 과정을 반복하면 큐가 비어있게 되고 <code>answer=[1, 2, 3, 4, 5]</code>가 된다. 이 결과값이 위상정렬 값이다.</p>
<h2 id="🖥문제-구현">🖥문제 구현</h2>
<pre><code class="language-python">import sys
from collections import deque
input=sys.stdin.readline

n=int(input())
building=[[] for _ in range(n+1)]
degree=[0]*(n+1) #진입차수
cost=[0]*(n+1)
answer=[0]*(n+1)
q=deque()
for i in range(1, n+1) :
    data=list(map(int, input().split()))
    cost[i]=data[0]
    for data in data[1:-1] :
        building[data].append(i)
        degree[i]+=1

for i in range(1, n+1):
    if degree[i]==0:
        q.append(i)
        answer[i]=cost[i]

while q :
    x=q.popleft()
    for i in building[x] :
        degree[i]-=1
        answer[i]=max(answer[i], answer[x]+cost[i])
        if degree[i]==0:
            q.append(i)
for i in range(1, n+1):
    print(answer[i])</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Git]커밋 메시지 변경]]></title>
            <link>https://velog.io/@min-ji99/Git</link>
            <guid>https://velog.io/@min-ji99/Git</guid>
            <pubDate>Sun, 06 Nov 2022 15:36:09 GMT</pubDate>
            <description><![CDATA[<h2 id="push-전-commit-메시지-변경">push 전 commit 메시지 변경</h2>
<h3 id="가장-최근-commit-수정">가장 최근 commit 수정</h3>
<pre><code class="language-shell">  git commit --amend</code></pre>
<p>명령어를 입력하면 다음과 같은 화면을 볼 수 있다.
<img src="https://velog.velcdn.com/images/min-ji99/post/a3b6ee15-cc33-42a0-b845-9da1a564c493/image.png" alt=""></p>
<p><code>i</code>를 입력해서 INSERT 모드로 바꾼 후 커밋 메시지를 수정하면 된다.
저장을 할 때는 <code>esc</code>-&gt;<code>:wq</code>를 입력해주면 된다. 수정하지 않고 종료할 때는 <code>:q</code>를 입력해주면 된다.</p>
<h3 id="여러-commit-수정">여러 commit 수정</h3>
<ul>
<li><p>commit 목록</p>
<pre><code class="language-shell">git log</code></pre>
<p><img src="https://velog.velcdn.com/images/min-ji99/post/dbcbc82c-633d-4125-a07b-e8bdf40aea99/image.png" alt=""></p>
<p>로그에서 지금까지 커밋들을 확인 후 어떤 커밋을 수정할 것인지 확인을 한다. 두번째 커밋을 수정하고 싶다면 아래 명령어를 입력한다.</p>
<ul>
<li>수정할 commit 선택<pre><code class="language-shell">git rebase -i HEAD~3 </code></pre>
<img src="https://velog.velcdn.com/images/min-ji99/post/5fa9db49-0f1c-4ef5-a117-20d0f06a21e7/image.png" alt=""></li>
</ul>
<p>가장 최근 commit 2개를 보여준다. 수정하고 싶은 커밋 옆의 pick을 reword로 바꾼 후(<code>i</code>를 입력해 수정모드로 변경) <code>esc</code>-&gt;<code>:wq</code>로 저장해준다. 이러면 수정할 커밋 창이 순서대로 띄워진다. 커밋 메시지를 수정하고 저장을 한다. <code>git log</code>를 통해 수정이 된 것을 확인할 수 있다.</p>
</li>
</ul>
<p><strong>💡 commit 하지 않은 파일이 있는 경우</strong></p>
<pre><code class="language-shell">error: cannot rebase: You have unstaged changes.
error: Please commit or stash them.</code></pre>
<p>커밋을 하지 않은 파일이 있는 경우 위와 같은 error가 발생한다. 이 경우는 저장을 해야하는 경우는 commit을 하거나 git stash로 변경사항을 임시저장하면 된다.</p>
<ul>
<li>git stash 
아직 마무리하지 않은 작업을 스택에 잠시 저장할 수 있도록 하는 명령어이다. 아직 완료하지 않은 일을 commit 하지 않고 나중에 다시 꺼내와 마무리할 수 있다.<pre><code class="language-shell">git stash
git stash save</code></pre>
위의 명령어 중 하나를 실행하면 새로운 stash가 만들어지고 working directory가 깨끗해진다. 이제 다시 새로운 브랜치를 생성할 수 있고 commit 메시지를 수정할 수 있다.</li>
<li>git stash 목록<pre><code class="language-shell">git stash list</code></pre>
</li>
<li>임시저장한 내용을 다시 가져오기<pre><code class="language-shell">//가장 최근의 stash를 가져와서 적용
git stash apply
//stash 이름에 해당하는 stash를 적용
git stash apply [stash 이름]</code></pre>
</li>
<li>stash 제거하기<pre><code class="language-shell">//가장 최근 stash 제거
git stash drop
//stash 이름에 해당하는 stash 제거
git stash drop [stash 이름]</code></pre>
<h2 id="push-후-commit-메시지-변경">push 후 commit 메시지 변경</h2>
커밋을 이미 push 해 remote에 올린 상황인 경우는 push하기 전과 동일하게 commit 메시지를 수정한 후 force를 통해 수정된 커밋을 강제로 push 해 주어야한다. <pre><code class="language-shell">git push -force 브랜치 이름
git push -f origin main</code></pre>
하지만 이런 상황을 최대한 자제해야한다. push된 커밋 로그를 갖고 있던 다른 팀원들이 로그를 수동으로 수정해주어야하는 상황이 발생하기 때문이다. 그렇기 때문에 push를 하기 전에는 <strong>신중</strong>해야한다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[SpringBoot] 대용량 데이터 CSV 파일을 DB에 Insert하는 방법]]></title>
            <link>https://velog.io/@min-ji99/SpringBoot-%EB%8C%80%EC%9A%A9%EB%9F%89-%EB%8D%B0%EC%9D%B4%ED%84%B0-DB-Insert</link>
            <guid>https://velog.io/@min-ji99/SpringBoot-%EB%8C%80%EC%9A%A9%EB%9F%89-%EB%8D%B0%EC%9D%B4%ED%84%B0-DB-Insert</guid>
            <pubDate>Wed, 02 Nov 2022 04:44:49 GMT</pubDate>
            <description><![CDATA[<p><a href="https://github.com/Min-ji99/hospital-api">GitHub hospital-api</a>
100,000건이 넘는 병원정보 csv 파일을 가져와 DB에 저장하는 프로젝트이다.</p>
<p><strong>과정</strong></p>
<ol>
<li>csv 파일 불러와서 parsing</li>
<li>parsing한 데이터 DB insert</li>
</ol>
<h2 id="csv-파일-불러와서-parsing">CSV 파일 불러와서 Parsing</h2>
<p><strong>ReadLineContext.java</strong>
파일에서 한줄씩 읽어와서 parse 메서드를 통해 파싱을 한 결과를 result 리스트에 추가한다.</p>
<pre><code class="language-java">package com.springboot.hospitalsearchapi.parser;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

public class ReadLineContext&lt;T&gt; {
    Parser&lt;T&gt; parser;

    public ReadLineContext(Parser&lt;T&gt; parser) {
        this.parser = parser;
    }

    public List&lt;T&gt; readByLine(String filename) throws IOException {
        // 삽
        List&lt;T&gt; result = new ArrayList&lt;&gt;();
        BufferedReader reader = new BufferedReader(
                new FileReader(filename)
        );
        String str;
        while ((str = reader.readLine()) != null) {
            try {
                result.add(parser.parse(str));
            }catch(Exception e){
                System.out.printf(&quot;파싱 중 문제가 생겨 이 라인은 넘어갑니다. 파일내용 : %s\n&quot;, str.substring(0, 20));
            }
        }
        reader.close();
        return result;
    }

}
</code></pre>
<p><strong>Parser.java</strong>
Parser 인터페이스이다. </p>
<pre><code class="language-java">package com.springboot.hospitalsearchapi.parser;

public interface Parser&lt;T&gt; {
    T parse(String str);
}</code></pre>
<p><strong>ParserFactory.java</strong></p>
<pre><code class="language-java">package com.springboot.hospitalsearchapi.parser;

import com.springboot.hospitalsearchapi.domain.Hospital;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ParserFactory {
    @Bean
    public ReadLineContext&lt;Hospital&gt; hospitalReadLineContextest(){
        return new ReadLineContext&lt;Hospital&gt;(new HospitalParser());
    }
}</code></pre>
<p><strong>HospitalParser.java</strong>
병원정보 한줄을 파싱하고 Hospital 객체를 생성해서 반환한다.</p>
<pre><code class="language-java">package com.springboot.hospitalsearchapi.parser;

import com.springboot.hospitalsearchapi.domain.Hospital;

import java.time.LocalDate;
import java.time.LocalDateTime;

public class HospitalParser implements Parser&lt;Hospital&gt;{

    @Override
    public Hospital parse(String str) {
        String[] splitted = str.split(&quot;\&quot;,\&quot;&quot;);
        Hospital hospital = new Hospital();
        hospital.setId(Integer.parseInt(splitted[0].replace(&quot;\&quot;&quot;,&quot;&quot;)));
        hospital.setOpenServiceName(splitted[1]);
        hospital.setOpenLocalGovernmentCode(Integer.parseInt(splitted[3]));
        hospital.setManagementNumber(splitted[4]);

        int year=Integer.parseInt(splitted[5].substring(0, 4));
        int month=Integer.parseInt(splitted[5].substring(4, 6));
        int day=Integer.parseInt(splitted[5].substring(6, 8));
        hospital.setLicenseDate(LocalDateTime.of(year, month, day, 0, 0, 0));

        hospital.setBusinessStatus(Integer.parseInt(splitted[7]));
        hospital.setBusinessStatusCode(Integer.parseInt(splitted[9]));
        hospital.setPhone(splitted[15]);
        hospital.setFullAddress(splitted[18]);
        hospital.setRoadNameAddress(splitted[19]);
        hospital.setHospitalName(splitted[21]);
        hospital.setBusinessTypeName(splitted[25]);
        hospital.setHealthcareProviderCount(Integer.parseInt(splitted[29]));
        hospital.setPatientRoomCount(Integer.parseInt(splitted[30]));
        hospital.setTotalNumberOfBeds(Integer.parseInt(splitted[31]));
        hospital.setTotalAreaSize(Float.parseFloat(splitted[32]));
        return hospital;
    }
}</code></pre>
<h2 id="parsing한-데이터-db-insert">Parsing한 데이터 DB insert</h2>
<p>데이터가 십만건이상이라 많은 시간이 걸린다. 
<strong>HospitalDao.java</strong>
JdbcTemplate를 이용해 DB에 Insert하고 Delete해주는 메서드를 구현했다.</p>
<pre><code class="language-java">package com.springboot.hospitalsearchapi.dao;

import com.springboot.hospitalsearchapi.domain.Hospital;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Component;

import javax.sql.DataSource;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;

@Component
@Slf4j
public class HospitalDao {
    private final DataSource dataSource;
    private final JdbcTemplate jdbcTemplate;

    public HospitalDao(DataSource dataSource, JdbcTemplate jdbcTemplate){
        this.dataSource=dataSource;
        this.jdbcTemplate=jdbcTemplate;
    }

    public void add(final Hospital hospital){
        String sql = &quot;Insert into nation_wide_hospitals values &quot; +
                &quot;(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)&quot;;
        this.jdbcTemplate.update(sql, hospital.getId(), hospital.getOpenServiceName(),
                hospital.getOpenLocalGovernmentCode(), hospital.getManagementNumber(),
                hospital.getLicenseDate(), hospital.getBusinessStatus(),
                hospital.getBusinessStatusCode(), hospital.getPhone(),
                hospital.getFullAddress(), hospital.getRoadNameAddress(),
                hospital.getHospitalName(), hospital.getBusinessTypeName(),
                hospital.getHealthcareProviderCount(), hospital.getPatientRoomCount(),
                hospital.getTotalNumberOfBeds(), hospital.getTotalAreaSize());
    }

    public int deleteAll(){
        return this.jdbcTemplate.update(&quot;delete from nation_wide_hospitals&quot;);
    }

    public int getCount(){
        return this.jdbcTemplate.queryForObject(
                &quot;select count(*) from nation_wide_hospitals&quot;, Integer.class);
    }
}
</code></pre>
<p><strong>HospitalParserTest.java</strong>
데이터가 잘 들어가는지 Test하는 코드입니다.</p>
<pre><code class="language-java">package com.springboot.hospitalsearchapi.parser;

import com.springboot.hospitalsearchapi.dao.HospitalDao;
import com.springboot.hospitalsearchapi.domain.Hospital;
import com.springboot.hospitalsearchapi.service.HospitalService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.io.IOException;
import java.time.LocalDateTime;
import java.util.List;

import static org.junit.jupiter.api.Assertions.*;

@SpringBootTest
class HospitalParserTest {

    @Autowired
    ReadLineContext&lt;Hospital&gt; hospitalReadLineContext;

    @Autowired
    HospitalDao hospitalDao;

    @Autowired
    HospitalService hospitalService;

    @Test
    @DisplayName(&quot;병원정보 전체 add 잘되는지&quot;)
    void addAllTest() throws IOException {
        String filename=&quot;/Users/minji/Documents/likelion/file/fulldata_01_01_02_P_의원.csv&quot;;
        hospitalDao.deleteAll();
        int cnt=hospitalService.insertLargeVolumHospitalData(filename);
        assertEquals(cnt, hospitalDao.getCount());
    }

    @Test
    @DisplayName(&quot;Hospital deleteAll, getCount 잘 되는지&quot;)
    void deleteAllTest(){
        hospitalDao.deleteAll();
        assertEquals(0, hospitalDao.getCount());
    }

}</code></pre>
<h3 id="병렬처리">병렬처리</h3>
<p>데이터가 다 들어가는데 한시간 걸리던 작업이 병렬처리를 하고 나서는 10분 이내로 끝난다.
<strong>HospitalService.java</strong> 추가</p>
<pre><code class="language-java">package com.springboot.hospitalsearchapi.service;

import com.springboot.hospitalsearchapi.dao.HospitalDao;
import com.springboot.hospitalsearchapi.domain.Hospital;
import com.springboot.hospitalsearchapi.parser.ReadLineContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.util.List;
import java.util.Optional;

@Service
public class HospitalService {

    private final ReadLineContext&lt;Hospital&gt; hospitalReadLineContext;

    private final HospitalDao hospitalDao;

    public HospitalService(ReadLineContext&lt;Hospital&gt; hospitalReadLineContext, HospitalDao hospitalDao) {
        this.hospitalReadLineContext = hospitalReadLineContext;
        this.hospitalDao = hospitalDao;
    }

    @Transactional
    public int insertLargeVolumeHospitalData(String filename) {
        List&lt;Hospital&gt; hospitalList;
        try {
            hospitalList = hospitalReadLineContext.readByLine(filename);
            System.out.println(&quot;파싱이 끝났습니다.&quot;);
            hospitalList.stream()
                    .parallel()
                    .forEach(hospital -&gt; {
                        try {
                            this.hospitalDao.add(hospital); // db에 insert하는 구간
                        } catch (Exception e) {
                            System.out.printf(&quot;id:%d 레코드에 문제가 있습니다.\n&quot;,hospital.getId());
                            throw new RuntimeException(e);
                        }
                    });
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        if (!Optional.of(hospitalList).isEmpty()) {
            return hospitalList.size();
        } else {
            return 0;
        }
    }
}</code></pre>
<p><strong>HospitalParserTest.java</strong> 수정</p>
<pre><code class="language-java">package com.springboot.hospitalsearchapi.parser;

import com.springboot.hospitalsearchapi.dao.HospitalDao;
import com.springboot.hospitalsearchapi.domain.Hospital;
import com.springboot.hospitalsearchapi.service.HospitalService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.io.IOException;
import java.time.LocalDateTime;
import java.util.List;

import static org.junit.jupiter.api.Assertions.*;

@SpringBootTest
class HospitalParserTest {

    @Autowired
    ReadLineContext&lt;Hospital&gt; hospitalReadLineContext;

    @Autowired
    HospitalDao hospitalDao;

    @Autowired
    HospitalService hospitalService;

    @Test
    @DisplayName(&quot;병원정보 전체 add 잘되는지&quot;)
    void addAllTest() throws IOException {
        String filename=&quot;/Users/minji/Documents/likelion/file/fulldata_01_01_02_P_의원.csv&quot;;
        hospitalDao.deleteAll();
        int cnt=hospitalService.insertLargeVolumHospitalData(filename);
        assertEquals(cnt, hospitalDao.getCount());
    }

    @Test
    @DisplayName(&quot;Hospital deleteAll, getCount 잘 되는지&quot;)
    void deleteAllTest(){
        hospitalDao.deleteAll();
        assertEquals(0, hospitalDao.getCount());
    }

}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[백준]2589 보물섬]]></title>
            <link>https://velog.io/@min-ji99/%EB%B0%B1%EC%A4%802589-%EB%B3%B4%EB%AC%BC%EC%84%AC</link>
            <guid>https://velog.io/@min-ji99/%EB%B0%B1%EC%A4%802589-%EB%B3%B4%EB%AC%BC%EC%84%AC</guid>
            <pubDate>Mon, 24 Oct 2022 12:06:24 GMT</pubDate>
            <description><![CDATA[<h1 id="문제">문제</h1>
<p><a href="https://www.acmicpc.net/problem/2589">https://www.acmicpc.net/problem/2589</a></p>
<h3 id="📝-문제">📝 문제</h3>
<p>보물섬 지도를 발견한 후크 선장은 보물을 찾아나섰다. 보물섬 지도는 아래 그림과 같이 직사각형 모양이며 여러 칸으로 나뉘어져 있다. 각 칸은 육지(L)나 바다(W)로 표시되어 있다. 이 지도에서 이동은 상하좌우로 이웃한 육지로만 가능하며, 한 칸 이동하는데 한 시간이 걸린다. 보물은 서로 간에 최단 거리로 이동하는데 있어 가장 긴 시간이 걸리는 육지 두 곳에 나뉘어 묻혀있다. 육지를 나타내는 두 곳 사이를 최단 거리로 이동하려면 같은 곳을 두 번 이상 지나가거나, 멀리 돌아가서는 안 된다.</p>
<h3 id="입력">입력</h3>
<p>첫째 줄에는 보물 지도의 세로의 크기와 가로의 크기가 빈칸을 사이에 두고 주어진다. 이어 L과 W로 표시된 보물 지도가 아래의 예와 같이 주어지며, 각 문자 사이에는 빈 칸이 없다. 보물 지도의 가로, 세로의 크기는 각각 50이하이다.</p>
<h3 id="출력">출력</h3>
<p>첫째 줄에 보물이 묻혀 있는 두 곳 사이를 최단 거리로 이동하는 시간을 출력한다.</p>
<p><strong>예제 입력 1</strong></p>
<pre><code>5 7
WLLWWWL
LLLWLLL
LWLWLWW
LWLWLLL
WLLWLWW</code></pre><p><strong>예제 출력 1</strong></p>
<pre><code>8</code></pre><h1 id="구현">구현</h1>
<p>입력받은 maps을 돌면서 해당 위치가 육지인 경우는 bfs로 최단거리를 탐색한다. 
<strong>첫시도</strong></p>
<pre><code class="language-python">import sys
from collections import deque
input=sys.stdin.readline

n, m=map(int, input().split())
maps=[list(map(str, input().strip())) for _ in range(n)]

dx=[0, 0, -1, 1]
dy=[-1, 1, 0, 0]

def bfs(a, b):
    q=deque()
    q.append([a, b])
    visit=[[0]*m for _ in range(n)]
    visit[a][b]=1
    time=0
    while q:
        x, y=q.popleft()
        for i in range(4):
            nx, ny=x+dx[i], y+dy[i]
            if 0&lt;=nx&lt;n and 0&lt;=ny&lt;m :
                if maps[nx][ny]==&#39;L&#39; and visit[nx][ny]==0:
                    q.append([nx, ny])
                    # 보물은 서로 간에 최단 거리로 이동하는데 있어 가장 긴 시간이 걸리는 육지 두 곳에 있음
                    visit[nx][ny] = visit[x][y] + 1
                    time=max(visit[nx][ny], time)

    return time-1


time=0

for i in range(n) :
    for j in range(m) :
        if(maps[i][j]==&#39;L&#39;):
            time=max(bfs(i, j), time)

print(time)</code></pre>
<p>python은 시간초과가 발생해서 pypy로 제출해서 통과했다.</p>
<p><strong>두번째 시도</strong>
보물은 이동하는데 있어서 가장 긴 시간이 걸리는 육지 두곳에 나뉘어 묻혀있다. 그렇기 때문에 maps[i][j]가 다른 두 육지 사이에 있는 경우는 가장 긴 시간이 걸리는 경우가 아니기 때문에 탐색을 할 필요가 없다. 이 부분을 추가해주니까 python으로 제출해도 통과를 했다.</p>
<pre><code class="language-python">import sys
from collections import deque
input=sys.stdin.readline

n, m=map(int, input().split())
maps=[list(map(str, input().strip())) for _ in range(n)]

dx=[0, 0, -1, 1]
dy=[-1, 1, 0, 0]

def bfs(a, b):
    q=deque()
    q.append([a, b])
    visit=[[0]*m for _ in range(n)]
    visit[a][b]=1
    time=0
    while q:
        x, y=q.popleft()
        for i in range(4):
            nx, ny=x+dx[i], y+dy[i]
            if 0&lt;=nx&lt;n and 0&lt;=ny&lt;m :
                if maps[nx][ny]==&#39;L&#39; and visit[nx][ny]==0:
                    q.append([nx, ny])
                    # 보물은 서로 간에 최단 거리로 이동하는데 있어 가장 긴 시간이 걸리는 육지 두 곳에 있음
                    visit[nx][ny] = visit[x][y] + 1
                    time=max(visit[nx][ny], time)

    return time-1


time=0

for i in range(n) :
    for j in range(m) :
        #다른 육지 사이에 있는 경우
        if i&gt;0 and i+1&lt;n :
            if maps[i-1][j]==&#39;L&#39;and maps[i+1][j]==&#39;L&#39;:
                continue
        if j&gt;0 and j+1&lt;m :
            if maps[i][j-1]==&#39;L&#39; and maps[i][j+1]==&#39;L&#39;:
                continue
        if(maps[i][j]==&#39;L&#39;):
            time=max(bfs(i, j), time)

print(time)</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[IntelliJ] new에서 class file 생성이 안보일때 해결]]></title>
            <link>https://velog.io/@min-ji99/IntelliJ-new%EC%97%90%EC%84%9C-class-file-%EC%83%9D%EC%84%B1%EC%9D%B4-%EC%95%88%EB%B3%B4%EC%9D%BC%EB%95%8C-%ED%95%B4%EA%B2%B0</link>
            <guid>https://velog.io/@min-ji99/IntelliJ-new%EC%97%90%EC%84%9C-class-file-%EC%83%9D%EC%84%B1%EC%9D%B4-%EC%95%88%EB%B3%B4%EC%9D%BC%EB%95%8C-%ED%95%B4%EA%B2%B0</guid>
            <pubDate>Sat, 15 Oct 2022 05:48:11 GMT</pubDate>
            <description><![CDATA[<p>아래와 같이 프로젝트 폴더에서 우클릭해서 클래스 파일을 생성하려 할 때 Java Class 파일 생성이 안뜨는 경우가 있다.</p>
<div>
<img src=blob:https://velog.io/2fe5b2a3-f791-4bba-93e7-e10d29bd88e7 width=400></div>

<h2 id="해결방법">해결방법</h2>
<p>프로젝트 폴더 우클릭 ➡️ [Mark Directory as] ➡️ [Sources Root] 를 클릭하면 해결이 된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[백준]9663 N-Queen(python, Java)]]></title>
            <link>https://velog.io/@min-ji99/%EB%B0%B1%EC%A4%809663-N-Queenpython-Java</link>
            <guid>https://velog.io/@min-ji99/%EB%B0%B1%EC%A4%809663-N-Queenpython-Java</guid>
            <pubDate>Sat, 15 Oct 2022 05:16:42 GMT</pubDate>
            <description><![CDATA[<h2 id="문제">문제</h2>
<p>N-Queen 문제는 크기가 N × N인 체스판 위에 퀸 N개를 서로 공격할 수 없게 놓는 문제이다.
N이 주어졌을 때, 퀸을 놓는 방법의 수를 구하는 프로그램을 작성하시오.</p>
<p><strong>입력</strong>
첫째 줄에 N이 주어진다. (1 ≤ N &lt; 15)</p>
<p><strong>출력</strong>
첫째 줄에 퀸 N개를 서로 공격할 수 없게 놓는 경우의 수를 출력한다.</p>
<p><strong>예제 입력</strong></p>
<pre><code>8</code></pre><p><strong>예제 출력</strong></p>
<pre><code>92</code></pre><h2 id="풀이">풀이</h2>
<p>백트레킹으로 푸는 문제이다. 첫번째 행에 퀸을 놓고 두번째 줄로 내려간다. 두번째 줄 가능한 자리에 놓고 세번째 자리로 내려간다. 그러다 안되는 경우가 발생하면 다시 두번째 줄로 올라가 그 다음 가능한 자리로 가서 확인을 한다.</p>
<p>퀸의 같은 경우 같은 행이나 열에 있으면 서로 공격이 가능하므로 1차원 배열을 사용해서 같은 행에 존재하는 경우를 배제한다.</p>
<p>boards[x]=y의 의미는 (x+1)행의 (y+1)열에 퀸이 존재한다는 의미이다.</p>
<h2 id="구현">구현</h2>
<p><strong>python</strong>
pypy로 제출해야 통과한다.</p>
<pre><code class="language-python">n=int(input())
boards=[0]*n
ans=0
def check(x):
    for i in range(x) :
        if boards[x]==boards[i] :
            return False
        elif abs(boards[x]-boards[i])==abs : # 같은 열에 존재하는지 확인
            return False
    return True
def dfs(x):
    global ans
    if x==n :
        ans+=1
        return
    else :
        for i in range(n) :
            boards[x]=i
            if check(x)==True:
                dfs(x+1)

dfs(0)
print(ans)</code></pre>
<p><strong>java</strong></p>
<pre><code class="language-java">import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

public class BOJ_9663 {
    static int n;
    static int ans;
    static List&lt;Integer&gt; boards=new ArrayList&lt;&gt;();

    public static boolean check(int x){
        for(int i=0; i&lt;x; i++){
            if(boards.get(x) == boards.get(i)){
                return false;
            }else if(Math.abs(boards.get(x) - boards.get(i))==x-i){
                return false;
            }
        }
        return true;
    }
    public static void dfs(int x){
        if(x==n){
            ans+=1;
            return;
        }
        for(int i=0; i&lt;n; i++){
            boards.set(x, i);
            if(check(x)==true){
                dfs(x+1);
            }
        }
    }
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        n = sc.nextInt();
        for(int i=0; i&lt;n; i++){
            boards.add(0);
        }
        ans=0;
        dfs(0);
        System.out.println(ans);
    }
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JAVA]단위테스트]]></title>
            <link>https://velog.io/@min-ji99/JAVA%EB%8B%A8%EC%9C%84%ED%85%8C%EC%8A%A4%ED%8A%B8</link>
            <guid>https://velog.io/@min-ji99/JAVA%EB%8B%A8%EC%9C%84%ED%85%8C%EC%8A%A4%ED%8A%B8</guid>
            <pubDate>Fri, 14 Oct 2022 08:47:41 GMT</pubDate>
            <description><![CDATA[<h1 id="tddtest-driven-development">TDD(Test Driven Development)</h1>
<p>테스트를 먼저 만들고 테스트를 통과하기 위한 것을 짜는 것 즉, 만드는 과정에서 우선 테스트를 작성하고 그걸 통과하는 코드를 만들고를 반복하면서 제대로 동작하는지에 대한 피드백을 적극적으로 받는 것이다. </p>
<h2 id="단위테스트-vs-통합테스트">단위테스트 vs 통합테스트</h2>
<h3 id="단위-테스트unit-test">단위 테스트(Unit Test)</h3>
<p><strong>하나의 모듈을 기준으로 독립적으로 진행되는 가장 작은 단위의 테스트</strong>이다. 여기서 모듈은 애플리케이션에서 작동하는 하나의 기능 또는 메소드이다. </p>
<h3 id="통합-테스트integration-test">통합 테스트(Integration Test)</h3>
<p><strong>모듈을 통합하는 과정에서 모듈 간의 호환성을 확인하기 위해 수행되는 테스트</strong>이다. 일반적으로 애플리케이션은 여러 개의 모듈들로 구성이 되고, 모듈들끼리 메시지를 주고 받으면서 기능을 수행한다. </p>
<h2 id="단위-테스트-작성의-필요성">단위 테스트 작성의 필요성</h2>
<p>통합테스트는 실제 여러 컴포넌트들 간의 상호작용을 테스트하기 때문에 모든 컴포넌트들이 구동된 상태에서 테스트를 하게 된다. 그렇기 때문에 시스템을 구성하는 컴포넌트들이 많아질수록 테스트를 위한 비용(시간)이 상당히 커진다. 반면에 단위 테스트는 <strong>해당 부분만 독립적으로 테스트하기 때문에 어떤 코드를 리팩토링하여도 빠르게 문제 여부를 확인</strong>할 수 있다.</p>
<h2 id="intellij에서-test-파일-만드는-방법">IntelliJ에서 Test 파일 만드는 방법</h2>
<ol>
<li>클래스 이름 블럭 지정 후 마우스 오른쪽 버튼 또는 cmd+n</li>
<li>[Generate]-[Test…]
<img src="https://velog.velcdn.com/images/min-ji99/post/8461eb4e-c659-4a72-b20c-e30475900d5a/image.png" alt=""></li>
</ol>
<ol>
<li>Test Annotation 사용</li>
</ol>
<p><code>@Test</code> 는 해당 메소드가 단위 테스트임을 명시하는 어노테이션이다. JUnit은 테스트 패키지 하위의 @Test 어노테이션이 붙은 메소드를 단위 테스트로 인식하여 실행시킨다. 이 상태로 실행하면 테스트 이름이 함수이름으로 default 되는데 @DisplayName 어노테이션을 사용하면 읽기 좋은 다른 이름을 부여할 수 있다.</p>
<pre><code class="language-java">@DisplayName(&quot;이름테스트&quot;)
@Test
void name() {
            ...
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JAVA]Collections Framework]]></title>
            <link>https://velog.io/@min-ji99/JAVACollections-Framework</link>
            <guid>https://velog.io/@min-ji99/JAVACollections-Framework</guid>
            <pubDate>Thu, 06 Oct 2022 08:23:35 GMT</pubDate>
            <description><![CDATA[<h1 id="collections-framework">Collections Framework</h1>
<hr>
<p>컬렉션(다수의 객체)를 다루기 위한 표준화된 프로그래밍 방식으로 컬렉션을 쉽고 편리하게 다룰 수 있는 다양한 클래스를 제공한다. 컬렉션 프레임워크에는 List, Set, Map 인터페이스가 있다.</p>
<h2 id="collection-인터페이스의-메서드">Collection 인터페이스의 메서드</h2>
<table>
<thead>
<tr>
<th align="left">메서드</th>
<th align="left">설명</th>
</tr>
</thead>
<tbody><tr>
<td align="left">boolean add(Object o)<br>boolean addAll(Collection c)</td>
<td align="left">지정된 객체(o) 또는 Collection(c)의 객체들을 Collectiondp 추가한다.</td>
</tr>
<tr>
<td align="left">void clear()</td>
<td align="left">Collection의 모든 객체를 삭제한다.</td>
</tr>
<tr>
<td align="left">boolean contains(Object o)<br>boolean containsAll(Collection c)</td>
<td align="left">지정된 객체 또는 Collection 객체들이 Collection에 포함되어 있는지 확인한다.</td>
</tr>
<tr>
<td align="left">boolean equals(Object o)</td>
<td align="left">동일한 Collection인지 비교한다</td>
</tr>
<tr>
<td align="left">int hashCode()</td>
<td align="left">Collection의 hash code를 반환한다</td>
</tr>
<tr>
<td align="left">boolean isEmpty()</td>
<td align="left">Collection이 비어있는지 확인한다</td>
</tr>
<tr>
<td align="left">Iterator iterator()</td>
<td align="left">Collection의 iterator를 얻어서 반환한다</td>
</tr>
<tr>
<td align="left">boolean remove(Object o)</td>
<td align="left">지정된 객체를 삭제한다</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에서 삭제한다.<br> 이 작업으로 인해 Collection에 변화가 있으면 true 그렇지 않으면 false 반환</td>
</tr>
<tr>
<td align="left">int size()</td>
<td align="left">Collection에 저장된 객체의 개수를 반환한다</td>
</tr>
<tr>
<td align="left">Object[] toArray()</td>
<td align="left">Collection에 저장된 객체를 객체배열(Object[])로 반환한다</td>
</tr>
<tr>
<td align="left">Object[] toArray(Object[] a)</td>
<td align="left">지정된 배열에 Collection의 객체를 저장해서 반환한다</td>
</tr>
</tbody></table>
<hr>
<h2 id="list">List</h2>
<ul>
<li>순서가 있는 데이터의 집합으로 데이터의 중복을 허용한다. </li>
<li>구현 클래스로는 <strong>ArrayList, LinkedList</strong>, Stack, Vector 등이 있다.<h3 id="list인터페이스-메서드">List인터페이스 메서드</h3>
<table>
<thead>
<tr>
<th align="left">메서드</th>
<th align="left">설명</th>
</tr>
</thead>
<tbody><tr>
<td align="left">void add(int index, Object element)<br>boolean addAll(int index, Collection c)</td>
<td align="left">지정된 위치에 객체 또는 컬렉션에 포함된 객체들을 추가</td>
</tr>
<tr>
<td align="left">Object get(int index)</td>
<td align="left">지정된 위치에 있는 객체 반환</td>
</tr>
<tr>
<td align="left">int indexOf(Object o)</td>
<td align="left">지정된 객체의 위치를 반환(첫번째 요소부터 순방향으로)</td>
</tr>
<tr>
<td align="left">int lastIndexOf(Object o)</td>
<td align="left">지정된 객체의 위치를 반환(마지막요소부터 역방향으로</td>
</tr>
<tr>
<td align="left">Object remove(int index)</td>
<td align="left">지정된 위치에 있는 객체를 삭제하고 삭제된 객체를 반환</td>
</tr>
<tr>
<td align="left">Object set(int index, Object element)</td>
<td align="left">지정된 위치에 객체를 저장</td>
</tr>
<tr>
<td align="left">void sort(Comparator c)</td>
<td align="left">지정된 비교자로 List 정렬</td>
</tr>
<tr>
<td align="left">List subList(int fromIndex, int toIndex)</td>
<td align="left">지정된 범위에 있는 객체를 반환</td>
</tr>
</tbody></table>
</li>
</ul>
<h3 id="arraylist">ArrayList</h3>
<p>저장순서가 유지되고 중복을 허용하며 데이터 저장공간으로 배열을 사용한다.</p>
<ul>
<li>추가 : add와 addAll 메서드</li>
<li>삭제 : remove와 clear 메서드(clear는 전체 삭제)</li>
<li>검색<ul>
<li>int indexOf(Object o), int lastIndexOf(Object o) : 존재하면 index 반환, 없으면 -1 반환(lastIndexOf는 제일 뒤부터 검색)</li>
<li>boolean contains(Object o) : 객체가 존재하면 true, 존재하지 않으면 false 반환</li>
<li>Object get(int index) : 특정 위치에 있는 객체 반환</li>
<li>Object set(int index, Object element) : 특정 위치에 있는 객체 변경</li>
</ul>
</li>
<li><em>ArrayList 생성*</em><pre><code class="language-java">ArrayList&lt;Integer&gt; integers1 = new ArrayList&lt;Integer&gt;(); // 타입 지정
ArrayList&lt;Integer&gt; integers2 = new ArrayList&lt;&gt;(); // 타입 생략 가능
ArrayList&lt;Integer&gt; integers3 = new ArrayList&lt;&gt;(10); // 초기 용량 설정
ArrayList&lt;String&gt; colors = new ArrayList&lt;&gt;(Arrays.asList(&quot;Blue&quot;, &quot;Black&quot;, &quot;White&quot;, &quot;Red&quot;));</code></pre>
</li>
<li><em>ArrayList element 추가/변경*</em><pre><code class="language-java">ArrayList&lt;String&gt; colors = new ArrayList&lt;&gt;();
</code></pre>
</li>
</ul>
<p>//추가
colors.add(&quot;Black&quot;);
colors.add(&quot;White&quot;);
colors.add(0, &quot;Green&quot;); //0번째에 Green 추가
colors.add(&quot;Red&quot;);</p>
<p>// 변경
colors.set(0, &quot;Blue&quot;); //0번째 Blue로 변경</p>
<p>System.out.println(colors);</p>
<pre><code></code></pre><p>[Blue, Black, White, Red]</p>
<pre><code>**ArrayList elements 제거**
```java
ArrayList&lt;String&gt; colors = new ArrayList&lt;&gt;(Arrays.asList(&quot;Blue&quot;, &quot;Black&quot;, &quot;White&quot;, &quot;Red&quot;));
colors.remove(0); //0번째 인덱스 값 제거
System.out.println(colors);

colors.remove(&quot;Black&quot;);//Black 제거
System.out.println(colors);

colors.clear(); //전체 삭제</code></pre><pre><code>[Black, White, Red]
[White, Red]</code></pre><p><strong>ArrayList elements 존재 유무</strong></p>
<pre><code class="language-java">ArrayList&lt;String&gt; colors = new ArrayList&lt;&gt;(Arrays.asList(&quot;Black&quot;, &quot;White&quot;, &quot;Green&quot;, &quot;Red&quot;, &quot;Black&quot;));

boolean contains = colors.contains(&quot;Black&quot;); //존재하면 true, 존재하지 않으면 false
System.out.println(contains);

int index = colors.indexOf(&quot;Green&quot;); //존재하는 인덱스 값 반환, 존재하지 않으면 -1
System.out.println(index);

index = colors.lastIndexOf(&quot;Black&quot;);//뒤에서부터 역방향으로 찾아보며 존재하는 인덱스 값 반환, 존재하지 않으면 -1
System.out.println(index);
index = colors.lastIndexOf(&quot;Blue&quot;);
System.out.println(index);</code></pre>
<pre><code>true
2
4
-1</code></pre><hr>
<h2 id="set">Set</h2>
<ul>
<li>순서를 유지하지 않는 데이터의 집합으로 데이터의 중복을 허용하지 않는다. - 구현 클래스로는 <strong>HashSet</strong>, TreeSet 등이 있다.</li>
<li>Set 인터페이스의 메서드는 Collection 인터페이스와 동일하다<h3 id="집합과-관련된-메서드">집합과 관련된 메서드</h3>
Collection에 변화가 있으면 true, 아니면 false를 반환한다.</li>
</ul>
<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(Collectoin c)</td>
<td align="left">지정된 Collection에 포함된 객체들만 남기고 나머지는 삭제(교집합)</td>
</tr>
</tbody></table>
<h3 id="hashset">HashSet</h3>
<p>Set 인터페이스를 구현한 대표적인 컬렉션 클래스이고 순서를 유지하려면 LinkedHashSet클래스를 사용하면 된다.</p>
<ul>
<li>추가 : add, addAll(합집합)</li>
<li>삭제 : remove, removeAll(교집합), clear, retainAll(차집합)</li>
<li>포함 : contains, containsAll</li>
<li><em>HashSet 생성*</em><pre><code class="language-java">HashSet&lt;String&gt; colors1 = new HashSet&lt;String&gt;(); // 타입 지정
HashSet&lt;String&gt; colors2 = new HashSet&lt;&gt;(); // 타입 생략 가능
HashSet&lt;String&gt; colors3 = new HashSet&lt;&gt;(10); // 초기 용량 설정
HashSet&lt;String&gt; colors4 = new HashSet&lt;&gt;(Arrays.asList(&quot;Blue&quot;, &quot;Black&quot;, &quot;White&quot;));</code></pre>
</li>
<li><em>HashSet element 추가*</em><pre><code class="language-java">HashSet&lt;String&gt; colors = new HashSet&lt;&gt;();
colors.add(&quot;Black&quot;);
colors.add(&quot;White&quot;);
colors.add(&quot;Green&quot;);
colors.add(&quot;Red&quot;);
colors.add(&quot;White&quot;);
</code></pre>
</li>
</ul>
<p>System.out.println(colors);</p>
<pre><code></code></pre><p>[Red, White, Black, Green]</p>
<pre><code>중복되는 값은 제거 되기 때문에 White가 하나만 들어가 있는 것을 확인할 수 있다.
**HashSet element 삭제**
```java
HashSet&lt;String&gt; colors = new HashSet&lt;&gt;(Arrays.asList(&quot;Blue&quot;, &quot;Black&quot;, &quot;White&quot;, &quot;Red&quot;, &quot;Green&quot;));

colors.remove(&quot;Red&quot;); //[White, Blue, Black, Green]
color.removeAll(Arrays.asList(&quot;White&quot;, &quot;Blue&quot;)); //[Black, Green]</code></pre><p><strong>HashSet element 전체 출력</strong></p>
<pre><code class="language-java">HashSet&lt;String&gt; colors = new HashSet&lt;&gt;(Arrays.asList(&quot;Blue&quot;, &quot;Black&quot;, &quot;White&quot;, &quot;Red&quot;, &quot;Green&quot;));

for (String color : colors) {
    System.out.print(color + &quot;  &quot;);
}
System.out.println();

//Iterator 이용
Iterator it = colors.iterator();
while(it.hasNext()) {
    System.out.print(it.next());
}</code></pre>
<pre><code>Red  White  Blue  Black  Green  
Red White Blue Black Green </code></pre><p><strong>값 존재 유무</strong></p>
<pre><code class="language-java">HashSet&lt;String&gt; colors = new HashSet&lt;&gt;(Arrays.asList(&quot;Blue&quot;, &quot;Black&quot;, &quot;White&quot;, &quot;Red&quot;, &quot;Green&quot;));
System.out.println(colors.contains(&quot;Green&quot;)); // true
System.out.println(colors.contains(&quot;Yellow&quot;)); // false</code></pre>
<hr>
<h2 id="map">Map</h2>
<ul>
<li>키(key)와 값(value)의 쌍으로 이루어진 데이터의 집합 </li>
<li>순서는 유지되지 않으며, 키는 중복을 허용하지 않고, 값은 중복을 허용한다. - 구현클래스로는 <strong>HashMap</strong>, TreeMap, Hashtable, Properties 등이 있다.<h3 id="map-인터페이스-메서드">Map 인터페이스 메서드</h3>
</li>
</ul>
<table>
<thead>
<tr>
<th align="left">메서드</th>
<th align="left">설명</th>
</tr>
</thead>
<tbody><tr>
<td align="left">Object put(Object key, Object value)</td>
<td align="left">Map에 value 객체를 key객체에 연결하여 저장</td>
</tr>
<tr>
<td align="left">void putAll(Map t)</td>
<td align="left">지정된 Map의 모든 key-value 쌍을 추가</td>
</tr>
<tr>
<td align="left">boolean containsKey(Object kdy)</td>
<td align="left">지정된 key객체와 일치하는 Map의 keyr객체가 있는지 확인</td>
</tr>
<tr>
<td align="left">boolean containsValue(Object value)</td>
<td align="left">지정된 value 객체와 일치하는 Map의 value객체가 있는지 확인</td>
</tr>
<tr>
<td align="left">Object get(Object key)</td>
<td align="left">지정된 key객체에 대응하는 value 객체를 찾아서 반환</td>
</tr>
<tr>
<td align="left">Set entrySet()</td>
<td align="left">Map에 저장되어 있는 key-value 쌍을 Map.Entry타입의 객체로 저장한 Set으로 반환</td>
</tr>
</tbody></table>
<h3 id="hashmap">HashMap</h3>
<p>HashMap은 키와 값으로 구성된 Entry객체를 저장하는 구조를 가지고 있는 자료구조인 Map의 성질을 그대로 갖고 있다. 여기서 키와 값은 모두 객체이고, 값은 중복 저장될 수 있지만 키는 중복 저장될 수 없다. 만약 기존에 저장된 키와 동일한 키로 값을 저장하면 기존의 값은 없어지고 새로운 값으로 대치된다
<strong>HashMap 생성</strong></p>
<pre><code class="language-java">HashMap&lt;String,String&gt; map1 = new HashMap&lt;String,String&gt;();//HashMap생성
HashMap&lt;String,String&gt; map2 = new HashMap&lt;&gt;();//new에서 타입 파라미터 생략가능
HashMap&lt;String,String&gt; map3 = new HashMap&lt;&gt;(map1);//map1의 모든 값을 가진 HashMap생성
HashMap&lt;String,String&gt; map4 = new HashMap&lt;&gt;(10);//초기 용량(capacity)지정
HashMap&lt;String,String&gt; map5 = new HashMap&lt;&gt;(10, 0.7f);//초기 capacity,load factor지정
HashMap&lt;String,String&gt; map6 = new HashMap&lt;String,String&gt;(){{//초기값 지정
    put(&quot;a&quot;,&quot;b&quot;);
}};</code></pre>
<p><strong>HashMap element 추가</strong></p>
<pre><code class="language-java">HashMap&lt;Integer,String&gt; colors = new HashMap&lt;&gt;();//new에서 타입 파라미터 생략가능
colors.put(1,&quot;Black&quot;); //값 추가
colors.put(2,&quot;Blue&quot;);
colors.put(3,&quot;Green&quot;);</code></pre>
<p><strong>HashMap element 삭제</strong></p>
<pre><code class="language-java">HashMap&lt;Integer,String&gt; colors = new HashMap&lt;Integer,String&gt;(){{//초기값 지정
    put(1,&quot;Black&quot;);
    put(2,&quot;Blue&quot;);
    put(3,&quot;Green&quot;);
    put(4, &quot;Red&quot;);
}};
map.remove(1); //key값 1 제거(Black 제거)
map.clear(); //모든 값 제거</code></pre>
<p><strong>HashMap element 출력</strong></p>
<pre><code class="language-java">HashMap&lt;Integer,String&gt; colors = new HashMap&lt;Integer,String&gt;(){{//초기값 지정
            put(1,&quot;Black&quot;);
            put(2,&quot;Blue&quot;);
            put(3,&quot;Green&quot;);
            put(4, &quot;Red&quot;);
        }};

System.out.println(colors); //{1=Black, 2=Blue, 3=Green, 4=Red}
System.out.println(colors.get(3)); //Green

for(Map.Entry&lt;Integer, String&gt; entry : colors.entrySet()){
    System.out.println(&quot;key : &quot;+entry.getKey()+&quot; value : &quot;+entry.getValue());
}
 /*결과 
key : 1 value : Black
key : 2 value : Blue
key : 3 value : Green
key : 4 value : Red
*/</code></pre>
<p><strong>HashMap iterator</strong></p>
<pre><code class="language-java">HashMap&lt;Integer,String&gt; colors = new HashMap&lt;Integer,String&gt;(){{//초기값 지정
            put(1,&quot;Black&quot;);
            put(2,&quot;Blue&quot;);
            put(3,&quot;Green&quot;);
            put(4, &quot;Red&quot;);
}};

Iterator&lt;Map.Entry&lt;Integer, String&gt;&gt; entries = colors.entrySet().iterator();
while(entries.hasNext()){
    Map.Entry&lt;Integer, String&gt; entry = entries.next();
    System.out.println(&quot;[Key]:&quot; + entry.getKey() + &quot; [Value]:&quot; +  entry.getValue());
}

//keySet().iterator()
Iterator&lt;Integer&gt; keys = colors.keySet().iterator();
while(keys.hasNext()){
    int key = keys.next();
    System.out.println(&quot;[Key]:&quot; + key + &quot; [Value]:&quot; +  colors.get(key));
}
/* 둘다 결과 동일
[Key]:1 [Value]:Black
[Key]:2 [Value]:Blue
[Key]:3 [Value]:Green
[Key]:4 [Value]:Red
*/</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JAVA] 배열 정렬]]></title>
            <link>https://velog.io/@min-ji99/JAVA-%EB%B0%B0%EC%97%B4-%EC%A0%95%EB%A0%AC</link>
            <guid>https://velog.io/@min-ji99/JAVA-%EB%B0%B0%EC%97%B4-%EC%A0%95%EB%A0%AC</guid>
            <pubDate>Tue, 04 Oct 2022 14:20:12 GMT</pubDate>
            <description><![CDATA[<h1 id="java-배열-정렬-함수">JAVA 배열 정렬 함수</h1>
<p>JAVA에서는 Arrays.sort()를 이용하면 배열을 쉽게 정렬할 수 있다. sort()는 Comparable에 의해 리턴되는 값을 비교하여 오름차순 또는 내림차순으로 정렬한다.</p>
<h2 id="1차원-배열">1차원 배열</h2>
<p><strong>오름차순 정렬</strong></p>
<pre><code class="language-java">int[] arr = {1, 10, 4, 17, 9, 25, 3};
Arrays.sort(arr);
System.out.println(Arrays.toString(arr));</code></pre>
<pre><code>[1, 3, 4, 9, 10, 17, 25]</code></pre><p><strong>내림차순 정렬</strong></p>
<pre><code class="language-java">Integer[] arr = {1, 10, 4, 17, 9, 25, 3};
Arrays.sort(arr, Collections.reverseOrder());
System.out.println(Arrays.toString(arr));

//Comparator 구현
Integer[] arr2 = {1, 10, 4, 17, 9, 25, 3};
Arrays.sort(arr2, new Comparator&lt;Integer&gt;(){
    @Override
    public int compare(Integer i1, Integer i2) {
        return i2 - i1;
    }
});
System.out.println(Arrays.toString(arr2));

//lambda 이용
Integer[] arr3 = {1, 10, 4, 17, 9, 25, 3};
Arrays.sort(arr3, (i1, i2) -&gt; i2 - i1);
System.out.println(Arrays.toString(arr3));</code></pre>
<pre><code>[25, 17, 10, 9, 4, 3, 1]</code></pre><h3 id="배열-부분-정렬">배열 부분 정렬</h3>
<p>정렬할 범위인 처음 index와 마지막 index를 전달하여 해당 범위만 정렬할 수 있다.</p>
<pre><code class="language-java">int[] arr = {1, 10, 4, 17, 9, 25, 3};
Arrays.sort(arr, 0, 4);
System.out.println(Arrays.toString(arr));</code></pre>
<pre><code>[1, 4, 10, 17, 9, 25, 3]</code></pre><h3 id="string-길이로-정렬">String 길이로 정렬</h3>
<pre><code class="language-java">String[] arr = {&quot;Apple&quot;, &quot;Kiwi&quot;, &quot;Orange&quot;, &quot;Banana&quot;, &quot;Watermelon&quot;, &quot;Cherry&quot;};

Arrays.sort(arr, new Comparator&lt;String&gt;() {
    @Override
    public int compare(String s1, String s2) {
        return s1.length() - s2.length();
    }
});

System.out.println(&quot;Sorted arr[] : &quot; + Arrays.toString(arr));

//lambda 이용
String[] arr2 = {&quot;Apple&quot;, &quot;Kiwi&quot;, &quot;Orange&quot;, &quot;Banana&quot;, &quot;Watermelon&quot;, &quot;Cherry&quot;};

Arrays.sort(arr2, (s1, s2) -&gt; s1.length() - s2.length());

System.out.println(Arrays.toString(arr2));</code></pre>
<pre><code>[Kiwi, Apple, Orange, Banana, Cherry, Watermelon]</code></pre><h2 id="2차원-배열">2차원 배열</h2>
<p><strong>오름차순</strong></p>
<pre><code class="language-java">int [][] arr = {{3,5}, {4,2}, {3,4}};
Arrays.sort(arr, new Comparator&lt;int[]&gt;() {
    @Override
    public int compare(int[] o1, int[] o2) {
        if(o1[0] == o2[0]) {
            return o1[1] - o2[1];
        }else {
            return o1[0] - o2[0]; 
        }
    }
});</code></pre>
<pre><code>3 5 
4 2 
5 4 </code></pre><p><strong>내림차순</strong></p>
<pre><code class="language-java">int [][] arr = {{3,5}, {4,2}, {3,4}};
Arrays.sort(arr, new Comparator&lt;int[]&gt;() {
    @Override
    public int compare(int[] o1, int[] o2) {
        if(o1[0] == o2[0]) {
            return o2[1] - o1[1];
        }else {
            return o2[0] - o1[0]; 
        }
    }
});</code></pre>
<pre><code>5 4 
4 2 
3 5 </code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[JAVA]Generics]]></title>
            <link>https://velog.io/@min-ji99/JAVAGenerics</link>
            <guid>https://velog.io/@min-ji99/JAVAGenerics</guid>
            <pubDate>Mon, 03 Oct 2022 05:03:12 GMT</pubDate>
            <description><![CDATA[<h1 id="generics">Generics</h1>
<h2 id="generics-1">Generics</h2>
<p>컴파일시 타입을 체크해주는 기능으로 객체의 타입 안정성을 높이고, 형변환의 번거로움을 줄여준다.</p>
<pre><code class="language-java">//Tv 객체만 저장할 수 있는 ArrayList 생성
ArrayList&lt;Tv&gt; tvList = new ArrayList&lt;Tv&gt;();

tvList.add(new Tv());
tvList.add(new Audio()); //컴파일 에러</code></pre>
<p>위의 예제와 같이 지정된 객체 외 다른 객체가 들어오면 컴파일러가 막아준다. 런타임에러(실행 시 발생하는 에러)를 컴파일 에러로 변경해 사전에 예방할 수 있다.</p>
<pre><code class="language-java">ArrayList tvList = new ArrayList();
tvList.add(new Tv());
Tv t = (Tv)tvList.get(0); //get()의 반환 타입이 Object이기 때문에 형변환 필요</code></pre>
<pre><code class="language-java">ArrayList&lt;Tv&gt; tvList = new ArrayList&lt;Tv&gt;();
tvList.add(new Tv());
Tv t = tvList.get(0); //형변환 불필요</code></pre>
<p>지네릭스를 사용하지 않은 경우 get()의 반환 타입은 Object이기 t의 타입은 Tv이기 때문에 형변환이 빌표한 반면, 지네릭스를 사용하는 경우는 반환타입도 Tv이기 때문에 형변환이 필요없다.</p>
<blockquote>
<p>지네릭스의 장점</p>
</blockquote>
<ul>
<li>타입의 안정성을 제공한다.</li>
<li>타입체크의 형변환을 생략할 수 있으므로 코드가 간결해 진다.</li>
</ul>
<h3 id="지네릭스-용어">지네릭스 용어</h3>
<p>Box&lt;T&gt; : 지네릭 클래스, &#39;T의 Box&#39; 또는 &#39;T Box&#39;라고 읽는다.
T : 타입 변수 또는 타입 매개변수(T는 타입문자)
Box : 원시 타입</p>
<pre><code class="language-java">class Box&lt;T&gt; { }</code></pre>
<h2 id="타입변수">타입변수</h2>
<p>Generics 클래스를 작성할 때, Object 타입 대신 타입 변수(E)를 선언해서 사용한다. </p>
<pre><code class="language-java">public class ArrayList&lt;E&gt; extends AbstractList&lt;E&gt; {
    private transient E[] elementData;
    public boolean add(E o) { ... }
    public E get(int index) { ... }
    ...
}</code></pre>
<p>객체를 생성할 때, 타입 변수(E) 대신 실제 타입을 지정(대입)하면 된다.</p>
<pre><code class="language-java">ArrayList&lt;Tv&gt; tvList = new ArrayList&lt;Tv&gt;();</code></pre>
<h2 id="지네릭스-다형성">지네릭스 다형성</h2>
<p>참조 변수와 생성자의 대입된 타입은 일치해야 한다.</p>
<pre><code class="language-java">class Product { }
class Tv extends Product { }</code></pre>
<pre><code class="language-java">ArrayList&lt;Product&gt; list = new ArrayList&lt;Tv&gt;(); //에러</code></pre>
<p>위와 같이 Product 클래스가 Tv 클래스의 조상 클래스여도 에러가 발생한다. 참조 변수와 생성자의 대입된 타입은 일치해야한다.
단, 지네릭 클래스간의 다형성은 성립한다.</p>
<pre><code class="language-java">List&lt;Tv&gt; list = new ArrayList&lt;Tv&gt;();</code></pre>
<p>매개변수의 다형성도 성립한다.</p>
<pre><code class="language-java">ArrayList&lt;Product&gt; list = new ArrayList&lt;Product&gt;();
list.add(new Product());
list.add(new Tv()); //Ok</code></pre>
<p>위와 같이 매개변수의 경우 Product의 자손들이 와도 성립한다. </p>
<h2 id="제네릭-클래스">제네릭 클래스</h2>
<h3 id="iterator-e">Iterator &lt;E&gt;</h3>
<p>클래스를 작성할 때, Object 타입 대신 T와 같은 타입 변수 사용</p>
<pre><code class="language-java">//지네릭스 사용 전
public interface Iterator {
    boolean hasNext();
    Object next();
    void remove();
}
//지네릭스 사용 후 
public interface Iterator&lt;E&gt; {
    boolean hasNext();
    E next();
    void remove();
}</code></pre>
<pre><code class="language-java">//지네릭스 사용 전
Iterator it = list.iterator();
while(it.hasNext()) {
    Student s = (Student)it.next();
    ...
}
//지네릭스 사용 후
Iterator&lt;Student&gt; it = list.iterator();
while(it.hasNext()) {
    Student s = it.next();
    ...
}</code></pre>
<p>Oject 타입이 E로 바뀌었기 때문에 형변환을 해주지 않아도 된다.</p>
<h3 id="hashmapk-v">HashMap&lt;K, V&gt;</h3>
<p>여러 개의 타입 변수가 필요한 경우, 콤마(,)로 구분자 선언</p>
<pre><code class="language-java">public class HashMap&lt;K, V&gt; extends AbstractMap&lt;K, V&gt; {
    ...
    public V get(Object key) { ... }
    public V put(K key, V value) { ... }
    public V remove(Object key) { ... }
    ...
}</code></pre>
<ul>
<li>예시<pre><code class="language-java">HashMap&lt;String, Student&gt; map = new HashMap&lt;String, Student&gt;(); //생성
map.put(&quot;자바&quot;, new Student(&quot;자바&quot;, 1, 1, 100, 100, 100)); //데이터 저장</code></pre>
<pre><code class="language-java">public class HashMap extends AbstractMap {
  ...
  public Student get(Object key) { ... }
  public Student put(String key, Student value) { ... }
  public Student remove(Object key) { ... }
     ...
}</code></pre>
</li>
</ul>
<h2 id="제한된-지네릭-클래스">제한된 지네릭 클래스</h2>
<p>extends로 대입할 수 있는 타입을 제한한다(인터페이스인 경우에도 extends를 사용한다)</p>
<pre><code class="language-java">class FruitBox&lt;T extends Fruit&gt; {
    ArrayList&lt;T&gt; list = new ArrayList&lt;T&gt;();
}</code></pre>
<p>위의 예제는 Fruit의 자손만 T의 타입으로 지정가능하게 제한 것과 같다.</p>
<h2 id="지네릭스의-제약">지네릭스의 제약</h2>
<ul>
<li>타입 변수에 대입은 인스턴스 별로 다르게 가능하다. 그러므로 static 멤버(모든 인스턴스에 공통)에는 타입 변수를 사용할 수 없다. <pre><code class="language-java">class Box&lt;T&gt; {
  static T item; //error
  static int compare(T t1, T t2) { ... } //error
}</code></pre>
</li>
<li>타입 변수로 배열 선언은 가능하지만 배열 생성할 때 타입 변수를 사용할 수 없다. new 연산자는 뒤에 타입이 확정되어 있어야하는데 T는 어떤 타입이 올지 모르기 때문에 에러가 발생한다. <pre><code class="language-java">class Box&lt;T&gt; {
  T[] itemArr; // 선언할 때 ok
  T[] temArr = new T[10]; //생성할 때 error
}</code></pre>
</li>
</ul>
<h2 id="와일드-카드-">와일드 카드&lt; ? &gt;</h2>
<p>하나의 참조 변수로 대입된 타입이 다른 객체를 참조 가능하다. </p>
<pre><code class="language-java">ArrayList&lt;? extends Product&gt; list = new ArrayList&lt;Tv&gt;();
ARrayList&lt;? extends Product&gt; list = new ArrayList&lt;Audio&gt;();</code></pre>
<h3 id="종류">종류</h3>
<blockquote>
</blockquote>
<p>&lt; ? extends T &gt; : 와일드 카드의 상한 제한. T와 그 자손들만 가능
&lt; ? super T &gt; : 와일드 카드의 하한 제한. T와 그 조상들만 가능
&lt; ? &gt; :  제한 없음. 모든 타입이 가능 &lt; ? extends Object &gt;와 동일</p>
<ul>
<li>메서드의 매개변수에도 와일드 카드를 사용할 수 있다.<pre><code class="language-java">static Juice makeJuice(FruitBox&lt;? extends Fruit&gt; box) {
  String tmp = &quot;&quot;;
  for(Fruit f : box.getList()) tmp += f + &quot; &quot;;
  return new Juice(tmp);
}</code></pre>
<pre><code class="language-java">System.out.println(Juicer.makeJuice(new FruitBox&lt;Apple&gt;()));</code></pre>
<h2 id="지네릭-메서드">지네릭 메서드</h2>
지네릭 타입이 선언된 메서드(타입 변수는 메서드 내에서만 유효)이다. 이때 클래스 타입 매개변수 &lt;T&gt;와 메서드의 타입 매개변수 &lt;T&gt;는 별개이다. 지네릭 메서드 내에서는 지네릭 메서드의 타입 변수가 우선이 된다.<pre><code class="language-java">static &lt;T&gt; void sort(List&lt;T&gt; list, Comparator&lt;? super T&gt; c)</code></pre>
메서드를 호출하라 때마다 타입을 대입해야하지만 대부분 생략 가능하다. 메서드를 호출할 때 타입을 생략하지 않을 때는 클래스 이름 생략이 불가능하다.<pre><code class="language-java">static &lt;T extends Fruit&gt; Juice makeJuice(FruitBox&lt;T&gt; box) {
  ...
}</code></pre>
<pre><code class="language-java">System.out.println(Juicer.&lt;Fruit&gt;makeJuice(fruitBox));
System.out.println(Juicer.&lt;Apple&gt;makeJuice(appleBox));
System.out.println(&lt;Fruit&gt;makeJuice(fruitBox)); //error</code></pre>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JAVA] 클래스들]]></title>
            <link>https://velog.io/@min-ji99/JAVA-%ED%81%B4%EB%9E%98%EC%8A%A4%EB%93%A4</link>
            <guid>https://velog.io/@min-ji99/JAVA-%ED%81%B4%EB%9E%98%EC%8A%A4%EB%93%A4</guid>
            <pubDate>Sun, 02 Oct 2022 07:25:17 GMT</pubDate>
            <description><![CDATA[<h2 id="math-클래스">Math 클래스</h2>
<p>수학관련 static 메서드들의 집합이다. </p>
<h3 id="math클래스의-메서드">Math클래스의 메서드</h3>
<table>
<thead>
<tr>
<th>메서드</th>
<th>설명</th>
<th>예제</th>
<th>결과</th>
</tr>
</thead>
<tbody><tr>
<td>static double abe(double s)<br>static float abs(float f)<br>static int abs(int f)<br>static long abs(long f)</td>
<td>주어진 값의 절댓값을 반환</td>
<td>int i = Math.abs(-10);<br>double d = Math.abs(-10.0);</td>
<td>i=10<br>d=10.0</td>
</tr>
<tr>
<td>static double ceil(double a)</td>
<td>주어진 값을 올림하여 반환</td>
<td>double d = Math.ceil(10.1);<br>double d2 = Math.ceil(-10.1);<br>double d3 = Math.ceil(10.000015);</td>
<td>d=11.0<br>d2=-10.0<br>d3=11.0</td>
</tr>
<tr>
<td>static double floor(double a)</td>
<td>주어진 값을 버림하여 반환</td>
<td>double d = Math.floor(10.8);<br>double d2 = Math.floor(-10.8);</td>
<td>d=10.0<br>d2=11.0</td>
</tr>
<tr>
<td>static double max(double a, double b)<br>static float max(float a, float b)<br>staic int max(int a, int b)<br>static long max(long a, long b)</td>
<td>주어진 값을 비교하여 큰 값을 반환</td>
<td>double d = Math.max(9.5, 9.50001);<br>int i = Math.max(0, -1);</td>
<td>d=9.50001<br>i=0</td>
</tr>
<tr>
<td>static double min(double a, double b)<br>static float min(float a, float b)<br>staic int min(int a, int b)<br>static long min(long a, long b)</td>
<td>주어진 두 값을 비교하여 작은 값을 반환</td>
<td>double d = Math.max(9.5, 9.50001)<br>int i = Math.max(0, -1)</td>
<td>d=9.5<br>i=-1</td>
</tr>
<tr>
<td>static double random()</td>
<td>0.0~1.0범위의 임의의 double 값을<br> 반환(1.0은 범위에 포함 X)</td>
<td>double d = Math.random();<br>int i=(int)Math.random()*10 +1</td>
<td>0.0≤d&lt;1.0<br>1≤i&lt;11</td>
</tr>
<tr>
<td>static double rint(double a)</td>
<td>주어진 double 값과 가장 가까운 정수값을<br> double형으로 반환.<br>단, 두 정수의 정가운데 있는 값은 짝수를 반환</td>
<td>double d = Math.rint(1.2)<br>double d2 = Math.rint(2.6);<br>double d3 = Math.rint(3.5);<br>double d4 = Math.rint(4.5);</td>
<td>d = 1.0<br>d2 = 3.0<br>d3 = 4.0<br>d4 = 4.0</td>
</tr>
<tr>
<td>static long round(double a)<br>static long round(float a)</td>
<td>소수점 첫째자리에서 반올림한 정수값(long)을 반환.<br> 두 정수의 정가운데 있는 값은 항상 큰 정수를 반환</td>
<td>long l = Math.round(1.2);<br>long l2 = Math.round(2.6);<br>long l3 = Math.round(3.5);<br>long l4 = Math.round(4.5);</td>
<td>l = 1<br>l2 = 3<br>l3 = 4<br>l4 = 5</td>
</tr>
</tbody></table>
<h3 id="round-예제">round() 예제</h3>
<aside>
💡 소수점 아래 세 번째 자리에서 반올림하기

<ol>
<li><p>원래 값에 100을 곱한다.</p>
</li>
<li><p>위의 결과에 Math.round()를 사용한다.</p>
</li>
<li><p>위의 결과를 다시 100.0으로 나눈다.</p>
<p> ❗️ 100으로 나누면 int형으로 바뀜</p>
</li>
</ol>
</aside>

<pre><code class="language-java">double d = 90.7552
double d2 = Math.round(d*100)/100.0; //소수점 셋째자리에서 반올림

// d=90.7552
// d2=90.76</code></pre>
<h2 id="wrapper-클래스">Wrapper 클래스</h2>
<p>8개의 기본형(객체 X)을 객체로 다뤄야할 때 사용하는 클래스이다. </p>
<table>
<thead>
<tr>
<th>자료형</th>
<th>래퍼클래스</th>
<th>생성자</th>
<th>활용</th>
</tr>
</thead>
<tbody><tr>
<td>boolean</td>
<td>Boolean</td>
<td>Boolean(boolean value)</td>
<td></td>
</tr>
<tr>
<td>Boolean(String s)</td>
<td>Boolean b = new Boolean(true);</td>
<td></td>
<td></td>
</tr>
<tr>
<td>Boolean b2 = new Boolean(”true”);</td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>char</td>
<td>Character</td>
<td>Character(char value)</td>
<td>Character c = new Character(’a’);</td>
</tr>
<tr>
<td>byte</td>
<td>Byte</td>
<td>Byte(byte value)</td>
<td></td>
</tr>
<tr>
<td>Byte(String s)</td>
<td>Byte b = new Byte(10);</td>
<td></td>
<td></td>
</tr>
<tr>
<td>Byte b2 = new Byte(”10”);</td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>int</td>
<td>Integer</td>
<td>Integer(int value)</td>
<td></td>
</tr>
<tr>
<td>Integer(String s)</td>
<td>Integer i = new Integer(100);</td>
<td></td>
<td></td>
</tr>
<tr>
<td>Integer i2 = new Integer(”100”);</td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>short</td>
<td>Short</td>
<td>Short(short value)</td>
<td></td>
</tr>
<tr>
<td>Short(String s)</td>
<td>Short s = new Short(10);</td>
<td></td>
<td></td>
</tr>
<tr>
<td>Short s2 = new Short(”10”);</td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>long</td>
<td>Long</td>
<td>Long(int value)</td>
<td></td>
</tr>
<tr>
<td>Long(String s)</td>
<td>Long l = new Long(100);</td>
<td></td>
<td></td>
</tr>
<tr>
<td>Long l2 = new Long(”100”);</td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>float</td>
<td>Float</td>
<td>Float(double value)</td>
<td></td>
</tr>
<tr>
<td>Float(float value)</td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>float(string s)</td>
<td>Float f = new Float(1.0);</td>
<td></td>
<td></td>
</tr>
<tr>
<td>Float f2 = new float(1.0f);</td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>Float f3 = new Float(”1.0f”);</td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>double</td>
<td>Double</td>
<td>Double(double value)</td>
<td></td>
</tr>
<tr>
<td>Double(String s)</td>
<td>Double d = new Double(1.0);</td>
<td></td>
<td></td>
</tr>
<tr>
<td>Double d2 = new Double(”1.0”);</td>
<td></td>
<td></td>
<td></td>
</tr>
</tbody></table>
<h1 id="chapter-10-calendar-클래스">[Chapter 10] Calendar 클래스</h1>
<hr>
<h2 id="calendar-클래스">Calendar 클래스</h2>
<p>추상클래스이므로 객체를 생성할 수 없고 getInstance()를 통해 구현된 객체를 얻어야한다.</p>
<pre><code class="language-java">Calendar cal = Calendar.getInstance();</code></pre>
<pre><code class="language-java">Calendar cal = Calendar.getInstance(); //현재 날짜와 시간으로 생성
int thisYear = cal.get(Calendar.YEAR); //올해가 몇년인지
int lastDayOfMonth = cal.getActualMaximum(Calendar.DATE); //이 달의 마지막날</code></pre>
<h3 id="calendar에-정의된-필드">Calendar에 정의된 필드</h3>
<table>
<thead>
<tr>
<th>필드명</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>YEAR</td>
<td>년</td>
</tr>
<tr>
<td>MONTH</td>
<td>월(0부터 시작)</td>
</tr>
<tr>
<td>WEEK_OF_YEAR</td>
<td>그 해의 몇 번째 주</td>
</tr>
<tr>
<td>WEEK_OF_MONTH</td>
<td>그 달의 몇 번째 주</td>
</tr>
<tr>
<td>DATE</td>
<td>일</td>
</tr>
<tr>
<td>DAY_OF_MONTH</td>
<td>그 달의 몇 번째 일</td>
</tr>
<tr>
<td>DAY_OF_YEAR</td>
<td>그 해의 몇 번째 일</td>
</tr>
<tr>
<td>DAY_OF_WEEK</td>
<td>요일</td>
</tr>
<tr>
<td>DAY_OF_WEEK_IN_MONTH</td>
<td>그 달의 몇 번째 요일</td>
</tr>
<tr>
<td>HOUR</td>
<td>시간(0~11)</td>
</tr>
<tr>
<td>HOUR_OF_DAY</td>
<td>시간(0~23)</td>
</tr>
<tr>
<td>MINUTE</td>
<td>분</td>
</tr>
<tr>
<td>SECOND</td>
<td>초</td>
</tr>
<tr>
<td>MILLISECOND</td>
<td>천분의 일초</td>
</tr>
<tr>
<td>ZONE_OFFSET</td>
<td>GMT 기준 시차(천분의 일초 단위</td>
</tr>
<tr>
<td>AM_PM</td>
<td>오전/오후</td>
</tr>
<tr>
<td></td>
<td></td>
</tr>
</tbody></table>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JAVA] Object 클래스]]></title>
            <link>https://velog.io/@min-ji99/JAVA-Object-%ED%81%B4%EB%9E%98%EC%8A%A4</link>
            <guid>https://velog.io/@min-ji99/JAVA-Object-%ED%81%B4%EB%9E%98%EC%8A%A4</guid>
            <pubDate>Thu, 29 Sep 2022 04:33:40 GMT</pubDate>
            <description><![CDATA[<h1 id="object-클래스">Object 클래스</h1>
<p>Object 클래스는 모든 클래스의 최고 조상으로 오직 11개의 메서드만 가지고 있다. 11개의 메서드 중 wait(), notify() 등은 쓰레드 관련 메서드이다.</p>
<table>
<thead>
<tr>
<th align="left">Object 클래스의 메서드</th>
<th align="left">설명</th>
</tr>
</thead>
<tbody><tr>
<td align="left">protected Object clone()</td>
<td align="left">객체 자신의 복사본을 반환한다</td>
</tr>
<tr>
<td align="left">public boolean equals(Object obj)</td>
<td align="left">객체 자신과 객체 obj가 같은 객체인지 알려준다같으면 true 반환)</td>
</tr>
<tr>
<td align="left">protected void finalize()</td>
<td align="left">객체가 소면될 때 가비지 컬랙터에 의해 자동적으로 호출된다. 이때 수행되어야하는 코드가 있을 때 오버라이딩한다</td>
</tr>
<tr>
<td align="left">public Class getClass()</td>
<td align="left">객체 자신의 클래스 정보를 담고 있는 Class 인스턴스를 반환한다</td>
</tr>
<tr>
<td align="left">public int hashCode()</td>
<td align="left">객체 자신의 해시코드를 반환한다</td>
</tr>
<tr>
<td align="left">public String toString()</td>
<td align="left">객체 자신의 정보를 문자열로 반환한다</td>
</tr>
<tr>
<td align="left">public void notify()</td>
<td align="left">객체 자신을 사용하려고 기다리는 쓰레드를 하나만 깨운다.</td>
</tr>
<tr>
<td align="left">public void notifyall()</td>
<td align="left">객체 자신을 사용하려고 기다리는 모든 쓰레드를 깨운다</td>
</tr>
<tr>
<td align="left">public void wait()<br> public void wait(long timeout)<br> pubilc void wait(long timeout, int nanos)</td>
<td align="left">다른 쓰레드가 notify()나 notifyAll()을 호출할 때까지 현재 쓰레드를 무한히 또는 지정된 시간동안 기다리게 한다</td>
</tr>
</tbody></table>
<h2 id="equalsobject-obj">equals(Object obj)</h2>
<p>객체 자신(this)과 주어진 객체(obj)를 비교해서 같으면 true, 다르면 false를 반환한다. Object 클래스의 equals()는 객체의 주소를 비교(참조변수 값 비교)하기 때문에 주소가 같아야 true가 반환된다.</p>
<pre><code class="language-java">class Value {
    int value;

    Value(int value) {
        this.value = value;
    }
}
class EqualsTest {
    public static void main(String[] args) {
        Value v1 = new Value(10);
        Value v2 = new Value(10);

        System.out.println(v1.equals(v2));
    }
}</code></pre>
<p>위의 예제를 실행시켜보면 서로 다른 두 객체는 항상 주소가 다르기때문에 false가 출력된다. 인스턴스 변수의 값들을 비교하고 싶다면 equals 메서드를 오바라이딩 해야한다. </p>
<h3 id="equalsojbect-obj의-오버라이딩">equals(Ojbect obj)의 오버라이딩</h3>
<pre><code class="language-java">class Value {
    private int value;

    Value(int value) {
        this.value = value;
    }

    void int getValue(){
        return value;
    }

    @Override
    public boolean equals(Object obj) {
        if(obj instanceof Value) {
            Value v = (Value)obj;
              if(this.value==obj.getValue()){
                retrun true;
            }
        }
        return false;
    }
}</code></pre>
<p>위와 같이 해주면 인스턴스 변수의 값을 비교할 수 있다. 이때 형변환을 하는 이유는 obj는 Object 타입이므로 Value 타입 변환해야하기 때문이다.</p>
<blockquote>
<p>💡 주의
형변환을 하기 전에 instanceof 연산자로 형변환이 가능한지 확인을 해주어야한다.</p>
</blockquote>
<h2 id="tostring">toString()</h2>
<p>객체를 문자열(String)으로 변환하기 위한 메서드이다.</p>
<pre><code class="language-java">public String toString() {
    return getClass().getName()+&quot;@&quot;+Integer.toHexString(hashCode());
}</code></pre>
<p>클래스이름과 주소를 반환하는 함수이다. </p>
<h3 id="tostring의-오버라이딩">toString()의 오버라이딩</h3>
<pre><code class="language-java">public String toString() {
    return &quot;kind : &quot; + kind + &quot;, number: &quot; + number;
}</code></pre>
<p>원래는 클래스이름과 주소를 반환하는 함수를 오버라이딩을 통해 인스턴스 변수를 출력하도록 오버라이딩할 수 있다.</p>
<pre><code class="language-java">String strl = new String(&quot;abc&quot;); 
String str2 = new String(&quot;abc&quot;); 
System.out.println(strl.equals(str2)); // true 
System.out.println(strl.hashCode()); // 96354 
System.out.println(str2.hashCode()); // 96354</code></pre>
<h2 id="hashcode">hashCode()</h2>
<p>객체의 해시코드를 반환하는 메서드로 객체의 주소를 int로 변환해서 반환한다.
equals()의 결과가 true인 두 객체의 해시코드는 같아야하기 때문에 equals()를 오버라이딩하면, hashCode()도 오버라이딩해야한다. </p>
<pre><code class="language-java">String strl = new String(&quot;abc&quot;); 
String str2 = new String(&quot;abc&quot;); 
System.out.println(strl.equals(str2)); // true 
System.out.println(strl.hashCode()); // 96354 
System.out.println(str2.hashCode()); // 96354</code></pre>
<h1 id="string클래스">String클래스</h1>
<h2 id="string클래스의-특징">String클래스의 특징</h2>
<ul>
<li>문자형 배열(char[])과 그에 관련된 메서드가 정의되어 있다.</li>
</ul>
<p>문자형 배열, 관련메서드가 정의된 부분</p>
<pre><code class="language-java">public final class String implements java.io.Serializable. comparable{
    /** The value is used for character storage. */
    private char[] value;
    ...</code></pre>
<ul>
<li>String인스턴스의 내용은 변경이 불가능하다.</li>
</ul>
<p>String인스턴스 내용변경 불가능 예시</p>
<pre><code class="language-java">String a = &quot;a&quot;;
String b = &quot;b&quot;;
String a = a + b;</code></pre>
<p><img src="https://velog.velcdn.com/images/min-ji99/post/f7da9032-5af6-4c18-b457-9a04f3c9ec15/image.jpg" alt=""></p>
<p>String a에 “a”를 저장 후,  String a에 다시 a+b를 저장할 시 기존의 “a”값은 </p>
<p>변경되지 않고 그대로 메모리에 남아있게 된다.</p>
<p>String형 변수에 “abc”와 new String(”abc”)의 차이</p>
<pre><code class="language-java">String str1 = &quot;abc&quot;;
String str2 = &quot;abc&quot;;
String str3 = new String(&quot;abc&quot;);
String str4 = new String(&quot;abc&quot;);

System.out.println(str1==str2);//true
System.out.println(str3==str4);//false
System.out.println(str1.equals(str2));//true
System.out.println(str3.equals(str4));//true</code></pre>
<p><img src="https://velog.velcdn.com/images/min-ji99/post/4f84267e-7a13-47cd-b884-c91801de3ae9/image.jpg" alt=""></p>
<p>str1와 str2는 “abc”가 저장된 하나의 메모리주소를 공유한다</p>
<p>str3와 str4는 각각의 객체를 생성하여 “abc”가 저장된 서로 다른 </p>
<p>메모리주소를 가진다.</p>
<h2 id="빈-문자열-empty-string">빈 문자열(””, empty string)</h2>
<p>내용이 없는 문자열, 크키가 0인 char형 배열을 저장하는 문자열이다.</p>
<p>크기가 0인 배열을 생성하는 것은 어느 타입이나 가능하다</p>
<p>크기가 0인 배열 생성 예시</p>
<pre><code class="language-java">char[] cArr = new char[0]; // 크기가 0인 char배열
int[] iArr = (); // 크기가 0인 int배열</code></pre>
<p>String str = “”;는 가능하나 char c = “”;는 불가능하다.</p>
<p>String은 참조형의 기본값인 null보다 빈 문자열로 초기화하고 char형은 기본값인 \u0000보다는 공백으로 초기화는 게 좋다.</p>
<p>String과 char형의 기본값 예시</p>
<pre><code class="language-java">String s = null;
char c = &#39;\u0000&#39;;</code></pre>
<p>String과 char형의 공백초기화 예시</p>
<pre><code class="language-java">String s = &quot;&quot;; // 빈 문자열로 초기화
char c = &#39; &#39;; // 공백으로 초기화</code></pre>
<h2 id="string클래스의-생성자-메서드">String클래스의 생성자, 메서드</h2>
<p>String클래스에는 많은 생성자와 메서드가 존재한다.</p>
<table>
<thead>
<tr>
<th>메서드</th>
<th>설명</th>
<th>예제</th>
<th>결과</th>
</tr>
</thead>
<tbody><tr>
<td>String(String s)</td>
<td>주어진 문자열(s)를 갖는 <br>String인스턴스를 생성</td>
<td>String s = new String(”Hello”);</td>
<td>a = “Hello”</td>
</tr>
<tr>
<td>String(char[] value)</td>
<td>주어진 문자열(value)를 갖는<br> 인스턴스를 생성</td>
<td>char[] c = {’H’, ‘e’, ‘l’, ‘l’, ‘o’};<br>String s = new String(c);</td>
<td>s = “Hello”</td>
</tr>
<tr>
<td>String(StringBuffer buf)</td>
<td>StringBuffer인스턴스가 갖고 있는 문자열과<br> 같은 내용의 String인스턴스를 생성</td>
<td>StringBuffer sb = new StringBuffer(”Hello”);<br>String s = new String(sb);</td>
<td>s = “Hello”</td>
</tr>
<tr>
<td>char charAt(int index)</td>
<td>지정된 위치(index)에 있는 문자를 알려준다.<br>index는 0부터 시작</td>
<td>String s = “Hello”;<br>String n = “0123456”;<br>char c = s.charAt(1);<br>char c2 = n.charAt(1);</td>
<td>c = ‘e’<br>c2 = ‘1’</td>
</tr>
<tr>
<td>String concat(String str)</td>
<td>문자열(str)을 뒤에 덧붙인다.</td>
<td>String s = “Hello”;<br>String s2 = s.concat(” World”);</td>
<td>s2 = “Hello World”</td>
</tr>
<tr>
<td>boolean cantains(CharSequence s)</td>
<td>지정된 문자열(s)이 포함되었는지 검사</td>
<td>String s = “abcdefg”;<br>boolean b = s.contains(”bc”);</td>
<td>b = true</td>
</tr>
<tr>
<td>boolean endsWith(String suffix)</td>
<td>지정된 문자열(suffix)로 끝나는지 검사</td>
<td>String file = “Hello.txt”;<br>boolean b = file.exdWith(”txt”);</td>
<td>b = true</td>
</tr>
<tr>
<td>boolean equals(Object obj)</td>
<td>매개변수로 받은 문자열(obj)과<br> String인스턴스의 문자열을 비교한다.<br>obj가 String이거나 문자열이 다르면 false를 반환</td>
<td>String s = “Hello”;<br>boolean b = s.equals(”Hello”);<br>boolean b2 = s.equals(”hello”);</td>
<td>b = true<br>b2 = false</td>
</tr>
<tr>
<td>boolean equalsIgnoreCase(String str)</td>
<td>문자열과 String인스턴스의 문자열을<br> 대소문자 구분없이 비교</td>
<td>String s = “Hello”;<br>boolean b = s.equalsIgnoreCase(”HELLO”);<br>boolean b2 = s.equalsIgnoreCase(”heLLo”);</td>
<td>b = true<br>b2 = true</td>
</tr>
<tr>
<td>int indexOf(int ch)</td>
<td>주어진 문자(ch)가 문자열에 존재하는지 확인하여<br> 위치(index)를 알려주며 찾지 못하면 -1을 반환<br>index는 0부터 시작</td>
<td>String s = “Hello”;<br>int idx1 = s.indexOf(’o’);<br>int idx2 = s.indexOf(’k’);</td>
<td>idx1 = 4<br>idx2 = -1</td>
</tr>
<tr>
<td>int indexOf(String str)</td>
<td>주어진 문자열이 존재하는지 확인하여<br> 그 위치(index)를 알려주며 찾지 못하면 -1을 반환<br>index는 0부터 시작</td>
<td>String s = “ABCDEFG”;<br>int dex = s.indexOf(”CD”);</td>
<td>idx = 2</td>
</tr>
<tr>
<td>String intern()</td>
<td>문자열을 constant pool에 등록한다.<br> 등록 시 이미 constant pool에 같은 내용의<br> 문자열이 있을 경우 그 문자열의 주소값을 반환</td>
<td>String s = new String(”abc”);<br>String s2 = new String(”abc”);<br>boolean b = (s==s2);<br>boolean b2 = s.equals(s2);<br>boolean b3 = (s.intern() == s2.intern());</td>
<td>b = false<br>b2 = true<br>b3 = true</td>
</tr>
<tr>
<td>int lastIndexOf(int ch)</td>
<td>지정된 문자 또는 문자코드를 문자열의 오른쪽 끝에서부터<br> 찾아서 위치(index)를 알려주며 찾지 못하면 -1을 반환</td>
<td>String = “java.lang.java”;<br>int idx1 = s.lastIndexOf(”java”);<br>int idx2 = s.indexOf(”java);</td>
<td>idx1 = 9<br>idx2 = 4</td>
</tr>
<tr>
<td>int length()</td>
<td>문자열의 길이를 알려준다.</td>
<td>String s = “Hello”;<br>int length s = s.length();</td>
<td>length = 5</td>
</tr>
<tr>
<td>String replace(char old, char nw)</td>
<td>문자열 중의 문자(old)를 새로운 문자열(nw)로<br> 모두 바꾼 문자열을 반환</td>
<td>String s = “Hellollo”;<br>String s1 = s.replace(”ll”,”LL”);</td>
<td>s1 = “HeLLoLLo”</td>
</tr>
<tr>
<td>String replaceAll(String regex, String replacement)</td>
<td>문자열 중에서 지정된 문자열(regex)과 일치하는 것을<br> 새로운 문자열(replacement)로 모두 변경</td>
<td>String ab = “AABBAABB”;<br>String r = ab.replaceAll(”BB”,“bb”);</td>
<td>r = “AAbbAAbb”</td>
</tr>
<tr>
<td>String replaceFirst(String regex, String replacement)</td>
<td>문자열 중에서 지정된 문자열(regex)과 일치하는 것 중,<br> 첫 번째 것만 새로운 문자열(replacement)로 변경</td>
<td>String ab = “AABBAABB”;<br>String r = ab.replaceFirst(”BB”,”bb);</td>
<td>r = “AAbbAABB”</td>
</tr>
<tr>
<td>String[] split(String regex)</td>
<td>문자열을 지정된 분리자(regex)로 나누어 문자열 배열에 담아 반환</td>
<td>String animals = “dog,cat,bear”;<br>String[] arr = animals.split(”,”);</td>
<td>arr[0] = “dog”<br>arr[1] = “cat”<br>arr[2] = “bear”</td>
</tr>
<tr>
<td>String[] split(String regex, int limit)</td>
<td>문자열을 지정된 분리자(regex)로 나누어 문자열배열에 담아 반환<br>문자열 전체를 지정된 수(limit)로 자른다.</td>
<td>String animals = “dog, cat, bear”;<br>String[] arr = animals.split(”,”,2);</td>
<td>arr[0] = “dog”<br>arr[1] = “cat,bear”</td>
</tr>
<tr>
<td>boolean startsWith(String prefix)</td>
<td>주어진 문자열(prefix)로 시작하는지 검사</td>
<td>String s = “java.lang.Object”;<br>boolean b = s.startsWith(”java”);<br>boolean b2 = s.startsWith(”lang”);</td>
<td>b = true<br>b2 = false</td>
</tr>
<tr>
<td>String substring(int begin)<br>String substring(int begin, int end)</td>
<td>주어진 시작위치(begin)부터 끝 위치(end)범위에 포함된 문자열을 얻는다.<br> 이 때, 시작위치의 문자는 범위에 포함되나, 끝 위치의 문자는 미포함한다.</td>
<td>String s = “java.lang.Object”;<br>String c = s.substring(10);<br>String p = s.substring(5,9);</td>
<td>c = “Object”<br>p = “lang”</td>
</tr>
<tr>
<td>String toLowerCase()</td>
<td>String인스턴스에 저장되어 있는 모든 문자열을 소문자로 변환하여 반환</td>
<td>String s = “Hello”;<br>String s1 = s.toLowerCase();</td>
<td>s1 = “hello”</td>
</tr>
<tr>
<td>String toString()</td>
<td>String인스턴스에 저장되어 있는 문자열 반환</td>
<td>String s = “Hello”;<br>String s1 = s.toString();</td>
<td>s1 = “Hello”</td>
</tr>
<tr>
<td>String toUpperCase()</td>
<td>String인스턴스에 저장되어 있는 모든 문자열을 대문자로 변환하여 반환</td>
<td>String s = “Hello”;<br>String s1 = s.toUpperCase();</td>
<td>s1 = “HELLO”</td>
</tr>
<tr>
<td>String trim()</td>
<td>문자열의 왼쪽 끝과 오른쪽 끝에 있는 공백을<br> 없앤 결과를 반환하며 이 때 문자열<br> 중간에 있는 공백은 제거되지 않는다.</td>
<td>String s = “   Hello World   “;<br>String s1 = s.trim();</td>
<td>s1 = “Hello World”</td>
</tr>
<tr>
<td>static String valueOf(boolean b)<br>static String valueOf(char c)<br>static String valueOf(int i)<br>static String valueOf(long l)<br>static String valueOf(float f)<br>static String valueOf(double d)<br>static String valueOf(Object o)</td>
<td>지정된 값을 문자열로 변환하여 반환하며<br> 참조변수의 경우, toString()을 호출한 결과를 반환</td>
<td>String b = String.valueOf(true);<br>String c = String.valueOf(’a’);<br>String i = String.valueOf(100);<br>String l = String.valueOf(100L);<br>String f = String.valueOf(10f);<br>String d = String.valueOf(10.0);<br>java.util.Date dd = new java.util.Date();<br>String date = String.valueOf(dd);</td>
<td>b = “true”<br>c = “a”<br>i = “100”<br>l = “100”<br>f = “10.0”<br>d = “10.0”<br>date = “Sun Jan 27 21:26:29 KST 2008”</td>
</tr>
</tbody></table>
<h2 id="문자열과-기본형간의-변환">문자열과 기본형간의 변환</h2>
<ul>
<li>기본형 값을 문자열로 변환하는 방법은 두 가지가 있다.</li>
</ul>
<p>기본형 값의 문자열 변환방법 예시</p>
<pre><code class="language-java">int i = 100;
String str1 = i + &quot;&quot;; // 100을  &quot;100&quot;으로, 변환 방법1
String str2 = String.valueOf(i); // 100을 &quot;100&quot;으로, 변환 방법2</code></pre>
<p>문자열 값의 기본형 변환방법 예시</p>
<pre><code class="language-java">int i = Integer.parseInt(&quot;100&quot;); // &quot;100&quot;을 100으로, 변환 방법1
int i2 = Integer.valueOf(&quot;100&quot;); // &quot;100&quot;을 100으로, 변환 방법2
char c = &quot;A&quot;.charAt(0); // 문자열 &quot;A&quot;를 문자 &#39;A&#39;로, 변환</code></pre>
<p>기본형과 문자열 간 변환함수
<img src="https://velog.velcdn.com/images/min-ji99/post/cb5710f1-e67a-455c-b6e6-b8cc3f868215/image.jpg" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JAVA] 예외처리]]></title>
            <link>https://velog.io/@min-ji99/JAVA-%EC%98%88%EC%99%B8%EC%B2%98%EB%A6%AC</link>
            <guid>https://velog.io/@min-ji99/JAVA-%EC%98%88%EC%99%B8%EC%B2%98%EB%A6%AC</guid>
            <pubDate>Wed, 28 Sep 2022 02:44:59 GMT</pubDate>
            <description><![CDATA[<h1 id="예외처리">예외처리</h1>
<h2 id="프로그램-오류">프로그램 오류</h2>
<ul>
<li>컴파일 에러 : 컴파일 할 때 발생하는 에러</li>
<li>런타임 에러 : 실행할 때 발생하는 에러</li>
<li>논리적 에러 : 작성 의도와 다르게 동작<h3 id="java의-런타임-에러">Java의 런타임 에러</h3>
</li>
<li>에러(error) : 프로그램 코드에 의해서 수습될 수 없는 심각한 오류</li>
<li>예외(exception) : 프로그램 코드에 의해서 수습될 수 있는 다소 미약한 오류
➡️ 에러는 어쩔 수 없지만, 예외는 처리하자.</li>
</ul>
<h2 id="예외처리-1">예외처리</h2>
<p>예외처리는 프로그램 실행 시 발생할 수 있는 예외의 발생에 대비한 코드를 작성하는 것으로 프로그램의 비정상 종료를 막고, 정상적인 실행상태를 유지하기 위해 예외처리가 필요하다.</p>
<h3 id="exception과-runtimeexception">Exception과 RuntimeException</h3>
<ul>
<li>Exception 클래스 : 사용자의 실수와 같은 외적인 요인에 의해 발생하는 예외
IOException(입출력 예외), ClassNotFoundException(클래스가 존재하지 않을 때) ...  </li>
<li>RuntimeException 클래스 : 프로그래머의 실수로 발생하는 예외
ArithmeticException(산술계산 예외), ClassCastException(형변환 예외), NullPointerException(널 포인터), IndexOutOfBoundsException(배열 범위 벗어남) ...<h3 id="예외-처리하기try-catch-문">예외 처리하기(try-catch 문)</h3>
<pre><code class="language-java">try{
  //예외가 발생할 가능성이 있는 문장
}catch(Exception1 e1){
  //Exception1이 발생했을 경우, 이를 처리하기 위한 문장
}catch(Exception2 e2){
  //Exception2가 발생했을 경우 이를 처리하기 위한 문장
}finally{
  //예외 발생여부에 관계없이 항상 수행되어야하는 문장
  //try-catch문의 맨 마지막에 위치
}</code></pre>
</li>
<li>try블럭 내에서 예외가 발생한 경우, 발생한 예외와 일치하는 catch 블럭이 있는지 확인한다. 일치하는 catch블럭을 찾게 되면, 그 catch 블럭 내의 문장들을 수행하고 전체 try-catch문을 빠져나가서 그 다음 문장을 계속해서 수행한다. 일치하는 catch블럭을 찾지 못하면 예외 처리가 되지 않는다.</li>
<li>try블럭 내에서 예외가 발생하지 않은 경우는 catch블럭을 거치지 않고 전체 try-catch문을 빠져나가서 수행된다.</li>
<li>finally 블럭 : 예외 발생여부와 관계없이 수행되는 블럭으로 try 블럭 안에 return 문이 있어서 try 블럭을 벗어날 때도 finally 블럭이 실행된다. </li>
</ul>
<blockquote>
<p>💡 Exception e
모든 예외의 최고조상이기 때문에 모든 예외를 처리할 수 있다. 그렇기 때문에 Exception e가 선언된 catch 블럭은 제일 마지막에 와야한다. (위에 있으면 아래 예외블럭을 처리하지 못한다)</p>
</blockquote>
<h3 id="exception-에러-출력">Exception 에러 출력</h3>
<p>try 블럭에서 예외가 발생하면 예외 객체가 생성된다. 이 객체에 발생한 예외에 대한 정보가 들어있고 printStackTrace(), getMessage() 등의 메서드를 갖는다.</p>
<ul>
<li>printStackTrace() : 예외발생 당시의 호출스택에 있었던 메서드의 정보와 예외 메시지를 화면에 출력한다.</li>
<li>getMessage() : 발생한 예외클래스의 인스턴스에 저장된 메시지를 얻을 수 있다.</li>
</ul>
<h3 id="예외-발생시키기">예외 발생시키기</h3>
<p>연산자 new를 이용해서 발생시키려는 예외 클래스의 객체를 만든 다음 키워드 <code>throw</code>를 이용해서 예외를 발생시키면 된다.</p>
<pre><code class="language-java">public static void main(String args[]) {
    try {
        Exception e = new Exception(&quot;에러 발생&quot;);
        throw e;
    } catch (Exception e) {
        System.out.println(&quot;에러 메시지 : &quot; + e.getMessage());
        e.printStackTrace();
    }
}</code></pre>
<h3 id="checked예외와-unchecked-예외">checked예외와 unchecked 예외</h3>
<ul>
<li>checked예외 : 컴파일러가 예외 처리 여부를 체크(예외 처리 필수)
Exception과 그 자손들<pre><code class="language-java">public static void main(String[] args) {
    throw new Exception();
}</code></pre>
try-catch문을 이용해 예외처리를 해주어야하는데 하지 않았기 때문에 컴파일 오류가 발생한다. 즉, Exception 예외는 예외 처리를 필수로 해야하기 때문에 checked 예외라는 의미이다.</li>
<li>unchecked예외 : 컴파일러가 예외 처리 여부를 체크 안함(예외 처리 선택)
RuntimeException과 그 자손들<pre><code class="language-java">public static void main(String [] args) {
    throw new RuntimeException();
}</code></pre>
RuntimeExcepton은 unchecked 예외이기 때문에 예외 처리가 선택적이므로 try-catch문이 없어도 컴파일 에러가 발생하지 않고 예외가 발생했다는 결과가 출력된다.</li>
</ul>
<h3 id="throws">throws</h3>
<p>예외 선언하기는 <code>throws</code>키워드를 사용하고 메서드가 호출 시 발생가능한 예외를 호출하는 쪽에 알리는 것이다.</p>
<pre><code class="language-java">void method() throws Exception1, Exception2, ... ExceptionN }
    //메서드의 내용
}
void method() throws Exception {
    //메서드의 내용
}</code></pre>
<blockquote>
<p>💡 main 함수에 throws 를 사용한 경우
main함수에서 예외처리가 발생하면 try-catch문 없어서 비정상종된다. 그리고 JVM에게 예외를 맡기게 된다.</p>
</blockquote>
<h3 id="사용자-정의-예외-만들기">사용자 정의 예외 만들기</h3>
<p>직접 예외 클래스를 정의할 수 있다. 조상은 Exception과 RuntimeException 중에서 선택하고 생성자를 작성해주어야한다.</p>
<pre><code class="language-java">class MyException extends Exception{
    MyException(String msg) { //문자열을 매개변수로 받는 생성자
        super(msg); //조상인 Exception클래스의 생성자를 호출
    }
}</code></pre>
<p>💡 Exception은 필수 처리 예외이기 때문에 반드시 try-catch문을 써야하고 RuntimeException은 선택 처리 예외이기 때문에 try-catch문을 사용하지 않아도 된다. 그렇기 때문에 가능하면 선택처리가 가능한 RuntimeException을 사용하는게 좋다.</p>
<h3 id="예외-되던지기">예외 되던지기</h3>
<p>예외 되던지기는 예외를 처리한 후에 다시 예외를 발생시키는 것을 의미한다. 즉, <strong>호출한 메서드와 호출된 메서드 양쪽 모두에서 예외를 처리</strong>하게 된다.</p>
<pre><code class="language-java">public static void main(String[] args) {
    try {
        method1();
    } catch (Exception e) {
        System.out.println(&quot;main 메서드에서 예외가 처리되었습니다.&quot;);
    }
}
static void method1() throws Exception {
    try {
        throw new Exception();
    } catch (Exception e) {
        System.out.println(&quot;method1메서드에서 예외가 처리되었습니다.);
        throw e; //다시 예외를 발생시킨다.
    }
}
/*결과
method1메서드에서 예외가 처리되었니다.
main 메서드에서 예외가 처리되었습니다.
*/</code></pre>
<p>method1()에서 try-catch문으로 예외를 처리하고 난 다음에 throw e를 통해 다시 예외를 발생시킨다. 그렇게 되면 method1()을 호출한 main함수로 예외가 전달된다. 이렇게 호출한 메서드와 호출된 메서드 양쪽에서 모두 예외 처리하게 된다.</p>
<hr>
<p>[출처]
자바의 정석 - <a href="https://www.youtube.com/playlist?list=PLW2UjW795-f6xWA2_MUhEVgPauhGl3xIp">https://www.youtube.com/playlist?list=PLW2UjW795-f6xWA2_MUhEVgPauhGl3xIp</a></p>
]]></description>
        </item>
    </channel>
</rss>