[ Optional 개념에 앞서, NPE를 먼저 알아보자. ]

* NPE(Null Pointer Exception)란?

개발할 때 가장 많이 발생하는 예외 중 하나로, null을 호출하게 될 때 발생하는 예외사항이다.

이를 방지하기 위해 null 검사를 해야 하는데 변수가 많을수록 코드가 복잡해질 수 있다.

 

[ Optional ]

java.util.Optional<T> 클래스 :
Optional<T> 클래스는 Integer나 Double 클래스처럼 'T'타입의 객체를 포장해 주는 래퍼 클래스(Wrapper class)입니다.
따라서 Optional 인스턴스는 모든 타입의 참조 변수를 저장할 수 있습니다.

이러한 Optional 객체를 사용하면 예상치 못한 NullPointerException 예외를 제공되는 메소드로 간단히 회피할 수 있습니다.
즉, 복잡한 조건문 없이도 널(null) 값으로 인해 발생하는 예외를 처리할 수 있게 됩니다.

(출처 : 코딩의 시작, TCP School)

 

[ Optional 사용 예시 ]

public Optional<Member> findById(Long id) {
	return Optional.ofnullable(store.get(id)); //ofnullable : 값이 null이 들어올 수도 있는 경우에 사용
}

 

[ Optional 객체 접근 ]

Optional<String> opt = Optional.ofNullable("자바 opt 객체")
System.out.println(opt.get());

get() 메소드를 사용하여 Optional 객체 저장된 값에 접근할 수 있다.

이때 저장된 값이 null이면 NoSuchElementException 예외가 발생한다.

 

[ Optional 호출 시 null 체크 ]

get() 사용 시 null이면 예외를 발생시키므로 bool 타입을 반환하는 isPresent()메소드 통해 null 체크가 필요하다.

String text = getText();
Optional<String> optText = Optional.ofNullable(text);
int length;
if(optText.isPresent()) {
	length = optText.get().length();
} else {
	length = 0;
]

이를 통해 의문점이 들 수 있다.
NPE 방지를 위해 복잡한 null 체크를 버리고 Optional를 쓰는 건데 또 null 체크를 하다니?
이 부분은 밑에 블로그에서 잘 설명되어 있어 링크로 대신 첨부해 논다.

 

자바8 Optional 2부: null을 대하는 새로운 방법

Engineering Blog by Dale Seo

www.daleseo.com

 

 

자바8 Optional 3부: Optional을 Optional답게

Engineering Blog by Dale Seo

www.daleseo.com

 

[ Optional 정리 ]

Optional은 NPE null 체크의 복잡한 부담을 줄이기 위해 나온 Wrapper 클래스이다.

Optional은 Wrapping하고 다시 풀고 null일 경우 대체하는 함수를 호출하는 등의 오버헤드가 있으므로 잘못 사용하면 시스템 성능이 저하될 수 있다. 그렇기 때문에 반환 값이 null이 절대 아니라면 사용하지 않는 것이 좋다.

결과가 null이 될 수 있으며 null에 의해 오류가 발생할 가능성이 매우 높은 경우에만 사용되어야 한다.

'Java' 카테고리의 다른 글

[Java] Reflection API  (0) 2024.02.18
[Java] Iterator, Enumeration  (0) 2023.10.17
[Java] import static 사용하는 이유  (0) 2023.08.09
[Java] Static - 정적 변수와 메서드  (0) 2023.06.13
Intellij tomcat 연동 및 설정 (gradle)  (0) 2023.05.19
 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

 

✏️ 내가 작성한 코드

def solution(genres, plays):
    play_dic = {} # 인덱스, 재생수 딕셔너리
    total_play_dic = {} # 총 재생수 딕셔너리
    album_list = [] # 고유번호가 들어있는 최종 베스트 앨범

    for i,genre in enumerate(genres):
        try:
            total_play_dic[genre] += plays[i]
            play_dic[genre] += [(i, plays[i])]
        except:
            total_play_dic[genre] = plays[i]
            play_dic[genre] = [(i,plays[i])]

    for i in sorted(total_play_dic.items(),key=lambda x:x[1],reverse=True):
        sort_play_dic = sorted(play_dic[i[0]], key=lambda x:(-x[1],x[0])) # 내림차순은 -x, 오름차순은 x
        i_cnt = 0
        for play in sort_play_dic:
            if i_cnt == 2: # 장르별로 노래 2개씩만 앨범에 담음. 따라서 2개 되면 break
                break
            album_list.append(play[0])
            i_cnt += 1

    return album_list

 

✏️ 참고

1 ) 첫번째 for문에서 try, except하는 이유 :

처음에는 딕셔너리 내에 key가 존재하지않기 때문에 total_play_dic[genre], play_dic[genre]에 += 구문을 사용하여 value에 값을 더하려고 하면 에러가 발생한다.

그래서 예외처리로 키와 값을 지정하여 아래처럼 각 딕셔너리에 첫 값을 지정해준다.             

total_play_dic[genre] = plays[i]
play_dic[genre] = [(i,plays[i])]

 

2 ) 딕셔너리.items()를 사용하면 value값으로 정렬이 가능하다. (sorted() 사용)

* sort() : 딕셔너리, 튜플, 문자열에는 사용 불가,
sorted() :딕셔너리, 튜플, 문자열에도 사용 가능

 

3 ) sorted에서 key를 여러개 하여 정렬할때, lamda에서 x 통해 여러개 지정 가능.

ex ) key=lambda x:(-x[1],x[0]

reverse대신 '-' 사용하여 내림차순 사용 > x 앞에 - 붙으면 '내림차순', 안붙으면 '오름차순'

✏️ 웹브라우저에 url을 입력했을 때, 스프링 부트 동작 및 환경

< 1 >

@Controller 
public class HelloController {

    @GetMapping("hello")
    public String hello(Model model) {
        model.addAttribute("data", "hello!!"); //hello.html에서 {data}부분에 hello!!들어감
        return "hello";
    }

1. 웹브라우저에 localhost:8080/hello url을 입력한다.

2. 스프링부트는 '톰캣'이라는 웹서버를 내장하는데, 이 서버에서 url을 받아 스프링컨테이너에 보낸다.

3. 스프링컨테이너는 url의 hello와 @GetMapping("hello")를 보고 매핑하여 해당 메서드(hello)를 실행한다.

4. 스프링이 model을 만들어서 매개변수 부분에 넣어준다.

5. 그 model에 addAttribute. (key는 data 값은 "hello!!")

6. return "hello" : hello.html을 찾아서 위에 실행한 것들을 렌더링한다. (viewResolver가 hello 화면을 찾아서 처리해줌)


< 2 >

 @GetMapping("hello-string")
    @ResponseBody
    public String helloString(@RequestParam("name") String name){
        return "hello" + name;
    }

💡 @ResponseBody : http의 header, body 중 body 부분에 직접 내용을 넣어서 전달하게 해줌.

1. localhost:8080/hello-string?name=spring url 입력

2. 이 메서드 부분은 html없이 hello spring 그대로 출력된다. (페이지 소스보기 해도 html없이 hello spring 뜸)


< 3 >

@GetMapping("hello-api")
    @ResponseBody
    public Hello helloApi(@RequestParam("name") String name) {
        Hello hello = new Hello();
        hello.setName(name);
        return hello;
    }

    static class Hello {
        private String name;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }
    }

💡 url 입력하면 json( {키:값}) 형태로 확인해볼수있다.

1. url 입력받는다.

2. 컨테이너에서 hello-api 찾는다.

3. 찾았는데 메서드에 @ResponseBody가 있다.

4. <2>번 예시에서(helloString) return이 문자면 문자 그대로 http에 던짐.

helloApi에서는 객체를 반환한다. (return hello)

5. 객체가 오면 기본적으로 json 방식으로 데이터를 만들어서 http응답에 반환하는것이 기본 정책. 따라서 json방식으로 응답한다.

* 기존에 @ResponseBody가 없을때는(<1>번예시) viewResolver가 동작하지만 @ResponseBody가 존재하면 HttpMessageConverter가 동작된다. 

6. 단순 문자라면?  StringConverter : StringHttpMessageConverter 동작

객체라면? JsonConverter : MappingJackson2HttpMessageConverter 동작

(💡 jackson이란 객체를 json으로 바꿔주는 라이브러리. 스프링은 jackson을 기본으로 탑재한다.)

해당 Converter에 의해서 객체가 json으로 변환된다.

 

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

 

✏️ 내가 작성한 코드

def solution(n, words):
    answer = [0,0]

    for idx,word in enumerate(words):
        if idx < 1:
            pass
        else:
            if (word in words[:idx]) or (word[0] != words[idx-1][-1]): # 전에 등장한 단어, 이전단어 끝자리로 시작 안하는 단어인 경우
                answer[0] = (idx%n)+1
                answer[1] = (idx//n)+1
                break
    return answer
 

2164번: 카드2

N장의 카드가 있다. 각각의 카드는 차례로 1부터 N까지의 번호가 붙어 있으며, 1번 카드가 제일 위에, N번 카드가 제일 아래인 상태로 순서대로 카드가 놓여 있다. 이제 다음과 같은 동작을 카드가

www.acmicpc.net

 

✏️ 내가 작성한 코드 - 1

import sys
from collections import deque

N = int(sys.stdin.readline().strip())
lst = [i for i in range(1,N+1)] # 리스트컴프리헨션 사용 후 deque()
lst = deque(lst)
while(len(lst)>1): # 1개 남을때까지 반복
    lst.popleft() # 왼쪽에서 뺌
    lst.append(lst.popleft()) # 왼쪽에서 뺀거 오른쪽에 더함

print(lst.pop())

 

✏️ 내가 작성한 코드 - 2

import sys
from collections import deque

N = int(sys.stdin.readline().strip()) # 입력
lst = deque(range(1,N+1)) # deque안에서 range사용

while(len(lst)>1):# 1개 남을때까지 반복
    lst.popleft() # 맨 왼쪽값 꺼내기
    lst.rotate(-1) # 맨 앞에값 맨 뒤로

print(lst.pop())

 

✏️ 내가 작성한 코드 - 3 (rotate() 사용)

import sys
from collections import deque

N = int(sys.stdin.readline().strip()) # 입력
lst = deque()
for i in range(1, N+1):
    lst.append(i) # deque에 값 차례대로 append

while(len(lst)>1):# 1개 남을때까지 반복
    lst.popleft() # 맨 왼쪽값 꺼내기
    lst.rotate(-1) # 맨 앞에값 맨 뒤로, rotate() 사용

print(lst.pop())

 

✏️ 참고

* deque() 에서 괄호안에 range사용이 가능하다는 것을 처음 알았다.

* rotate() - deque의 목록회전이 가능한 rotate를 활용하면 훨씬 간편하게 앞의 값을 뒤로 보낼 수 있다.

맨앞의 값을 맨뒤로 보낼땐 rotate(-1) : 왼쪽이동 , 맨뒤 값을 맨앞으로 보낼땐 rotate(1) : 오른쪽이동 사용하기.

✏️ import static 사용 전

import org.assertj.core.api.Assertions;
Assertions.assertThat(member.getName()).isEqualTo(findMember.getName());

 

✏️ import static 사용 후

import static org.assertj.core.api.Assertions.*;
assertThat(member.getName()).isEqualTo(findMember.getName());

 

💡 import static을 사용하면 Assertions 즉, 클래스 이름을 붙이지 않고 바로 assertThat 메서드 사용이 가능하다.  ➜  코드가 간결해짐. 가독성 올라감.

'Java' 카테고리의 다른 글

[Java] Reflection API  (0) 2024.02.18
[Java] Iterator, Enumeration  (0) 2023.10.17
[Java] Optional이란? Optional 개념 및 사용  (0) 2023.10.05
[Java] Static - 정적 변수와 메서드  (0) 2023.06.13
Intellij tomcat 연동 및 설정 (gradle)  (0) 2023.05.19
 

1181번: 단어 정렬

첫째 줄에 단어의 개수 N이 주어진다. (1 ≤ N ≤ 20,000) 둘째 줄부터 N개의 줄에 걸쳐 알파벳 소문자로 이루어진 단어가 한 줄에 하나씩 주어진다. 주어지는 문자열의 길이는 50을 넘지 않는다.

www.acmicpc.net

 

✏️ 내가 작성한 코드 - 1 (for문 2개 사용)

import sys
N = int(sys.stdin.readline().strip())
lst = []

for _ in range(N):
    n = sys.stdin.readline().strip()
    if n not in lst: # 중복 단어 안들어가게
        lst.append(n)

lst.sort() # 문자열 사전 순으로 정리 됨
lst.sort(key=len) # 원소들 길이별로 정리됨

for cha in lst:
    print(cha)

 

✏️ 내가 작성한 코드 - 2 (set() 사용)

import sys
N = int(sys.stdin.readline().strip())
lst = []

for _ in range(N):
    lst.append(sys.stdin.readline().strip())

lst = list(set(lst)) # 중복 제거 위해 set사용. sort위해 다시 list()로.
lst.sort()
lst.sort(key=len)

for cha in lst:
    print(cha)

 

✏️ 내가 작성한 코드 - 3 (set(),join() 사용)

import sys
N = int(sys.stdin.readline().strip())
lst = []

for _ in range(N):
    lst.append(sys.stdin.readline().strip())

lst = list(set(lst))
lst.sort()
lst.sort(key=len)

print('\n'.join(lst)) # join()함수 사용.

 

✏️ 참고

위에서부터 set(),join() 사용 / set() 사용 / for문 2개 사용 채점 결과다.

set(),join() 사용 시 시간이 더 줄어든 것을 확인할 수 있다.

 

sort()는 원소가 문자열일 때 사전 순으로 정렬해준다.

sort(key=len)은 원소들의 길이별로 정렬해준다

 

10817번: 세 수

첫째 줄에 세 정수 A, B, C가 공백으로 구분되어 주어진다. (1 ≤ A, B, C ≤ 100)

www.acmicpc.net

 

✏️ 내가 작성한 코드

import sys
num = list(map(int,sys.stdin.readline().strip().split()))
print(sorted(num)[1])

 

 

+ Recent posts