연이율을 이용해 원금이 2배가 되는 시점을 년단위로 리턴해보자.
public class Solution {
public int computeWhenDouble(double interestRate) {
double principal = 100;
int result = 0;
for(int i; i<100; i++){
result=i;
principal = principal*interestRate;
if(principal>=200) {
break;
}
}
return result;
}
}
// error: variable i might not have been initialized
int i << 값을 선언만 하고 할당을 안했다 (실수)
public class Solution {
public int computeWhenDouble(double interestRate) {
double principal = 100;
int result = 0;
for(int i=1; i<100; i++){
principal = principal*(100+interestRate)/100;
if(principal>=200) {
result=i;
break;
}
}
return result;
}
}
for문 수행식도 문제가 있어 조금 바꾸니 정상적으로 작동했다.
ans
public class Solution {
public int computeWhenDouble(double interestRate) {
double rate = 1 + interestRate / 100;
double principal = 1;
int year = 0;
while (principal < 2) {
principal = principal * rate;
year++;
}
return year;
}
}
while을 쓰는게 좀 더 깔끔했을 것 같다.
데이터 교환을 위해 만들어진 객체 형태의 포맷
(자바 외의 프로그램과 데이터 교환하기 위한 포맷)
jackson
라이브러리에서 제공하는 ObjectMapper클래스를 사용하여
JSON형태로 변경하는 방법
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(message); // 직렬화(serialize)
System.out.println(json);
writeValueAsString(objectMapperName)
= 직렬화(serialize), 데이터를 JSON으로 변환하는 메서드
다시 객체의 형태로 만드는 방법
ObjectMapper mapper = new ObjectMapper();
String json = "{\"createdAt\":\"2021-01-12,10:10:10\",\"receiver\":\"박해커\",\"sender\":\"김코딩\",\"message\":\"밥먹을래?\"}";
Map<String, String> deserializedData = mapper.readValue(json, Map.class); // 역직렬화(deserialize)
System.out.println(deserializedData);
readValue(jsonName, Map.class)
= 역직렬화(deserialize), JSON을 데이터로 변환하는 메서드
key
에 쌍따옴표를 붙여야 함Value
)을 쌍따옴표로 감싸야 함key
-value
(쌍) 사이에 공백이 없어야 함Java 객체를 JSON타입으로 만들어주는
stringify
메서드를 직접 만들어 보자.
git주소에서 fork, clone부터 해보자.
fork한 다음 내 저장소에서 code버튼 누르면 아래와 같이 나온다.
터미널 -> 우분투에서
// clone받을 파일 위치로 이동
cd /mnt/d/AAWonJong/it/java/intelliJ/be-sprint-stringify-json
// git 실행
git init
// 주소 복사해서 clone
git clone HTTPS주소
clone 해온 파일을 열어 Java 객체를 JSON타입으로 만들어주는 stringify
메서드를 직접 만들어 보았다.
//초기 상태
public class stringifyJSON {
public String ObjectMapper(Object data) throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
return mapper.writeValueAsString(data);
}
public String stringify(Object data) {
//입력된 값이 문자열일 경우
//입력된 값이 Integer일 경우
//입력된 값이 Boolean일 경우
//입력된 값이 Object[]일 경우
//입력된 값이 HashMap일 경우
//지정되지 않은 타입의 경우에는 "null"을 리턴합니다.
}
}
//입력된 값이 문자열일 경우
if(data instanceof String){
String text = "\""; // """로 쓰면 오류 \가 "를 인식해준다.
return text+data+text;
}
문자열 앞뒤에 "
를 붙여서 출력하면 끝
if(data instanceof String){
return String.format("\"%s\"",data); // format을 이용해 더 간단하게 정리가능.
}
format을 이용하면 한줄로도 가능하다.
//입력된 값이 Integer일 경우
else if(data instanceof Integer){
String changed = Integer.toString((Integer) data);
return changed;
}
처음에 String일 경우와 마찬가지로 양끝에 "
를 붙여줬었는데 그럴필요 없이 Integer를 String타입으로 변환만 해주면 JSON타입이 됐다.
String타입으로 변환하는 방법은 아래와 같이 여러가지 방법이 있다.
//입력된 값이 Integer일 경우
else if(data instanceof Integer){
return data + ""; // 문자열과 Integer를 합치면 자동으로 문자열로 변한다.
return String.format("%d",(Integer)data);
return String.valueOf(data); // valueOf의 리턴타입은 열거형
return data.toString(); // 주소값이 출력될 수 있음. 좋은방법은 아님.
}
toString()
과 valueOf()
모두 Object의 값을 String으로 변환하지만
toString()
은 null값을 입력받는 경우 Null PointerException(NPE)을 발생시킨다.
(cf. NPE = 실행 예외 클래스)
valueOf()
는 null값을 입력받는 경우 "null"이라는 문자열이 할당된다.
(cf. valueOf()
는 열거형, Integer 등 문자열 외에 다양한 형태변환에도 사용가능)
//처음푼 방법
//입력된 값이 Boolean일 경우
else if(data instanceof Boolean){
String changed = String.valueOf(data);
return changed;
}
Integer일 경우와 마찬가지다.
else if(data instanceof Boolean){
return data + "";
return String.format("%b", data);
return data.toString();
if((Boolean)data) return "true";
else return "false"; //좀 미련한 방법.. 하드코딩..
}
만들고자하는 형태
// "[8,"hi"]"
// "[8,[[],3,4]]"
// "[[[]]]"
위와 같은 형태로 배열들이 String으로 출력되도록 만들어 보려고 한다.
// 입력된 값이 Object[]일 경우
else if(data instanceof Object[]){
if(((Object[]) data).length == 0) return "[]";
String str = "[";
Arrays.asList(data).stream()
.flatMap(Array -> (Object)Array) // flatMap 자체는 이중배열이 아닌 요소를 문자단위로 다 뜯어버린다. 즉, 잘못된 방법이다.
.forEach();// stream 1차 실패... inner타입이 뭘지 알 수 없어서 형변환 했는데 안됨.
return str;
처음에 Stram을 이용해 이중,삼중 배열들을 다 피고 각 요소를 String으로 타입변환시켜 str에 더해주는 방식으로 하려고 했지만 재귀를 사용하기 적합한 형태도 아니고 flatMap 자체는 이중배열이 아닌 요소를 문자단위로 다 뜯어버린다는 것을 늦게 알게되었다. 즉, 잘못된 방법이다.
(또한 data타입 뭘지 정해지지 않아 람다식을 전개하는데 오류도 계속 났다.)
else if(data instanceof Object[]){
if(((Object[]) data).length == 0) return "[]";
String str = "[";
for (Object o : Arrays.asList(data)) { // data타입 선언 잘못해서 무한루프... 돌았었다..
str += stringify(o)+",";
}
str = str.substring(0,str.length()-1);
str += "]";
return str;
}
우선 빈배열일 경우 빈배열모양의 String을 출력하도록 하고 str를 이용해 대괄호 사이에 요소들의 String형을 for문과 재귀를 이용해 쌓아가도록 해봤다.
java.lang.StackOverflowError
무한루프 오류가 계속 생겼다.
배열인 data를 ArrayList형으로 바꾼 다음 String으로 바꾸려 한 것인데
Arrays.asList(data).toString()
이렇게 쓰니까 toString()
에서 오류가 난다고 지워버렸더니 컴파일 에러가 안난다고 넘겨버리고 수행식을 만들어 버렸다. (런타임에러가 당연히 일어날걸 생각도 못함)
그래서 타입을 다시 맞추다보니 굳이 list형으로 바꿔서 할 필요가 없다는 생각이 들어 아래와 같이 했다.
else if(data instanceof Object[]){
if(((Object[]) data).length == 0) return "[]";
String str = "[";
for (Object o : (Object[])data) { // data타입 선언 잘못해서 무한루프... 돌았었다..
str += stringify(o)+",";
}
str = str.substring(0,str.length()-1);
str += "]";
return str;
정상적으로 기능수행하는 것을 확인했다.
위에서 언급한 내용과 같이 굳이 list를 사용할 필요는 없지만 사용해도 풀 수 있을 것 같아 해보았다.
else if(data instanceof Object[]){
if(((Object[]) data).length == 0) return "[]";
String str = "[";
Iterator<?> it = Arrays.asList((Object[])data).iterator();
while (it.hasNext()) {
str += stringify(it.next()) + ",";
}
str = str.substring(0,str.length()-1);
str += "]"; // Iterator 2차 실패... 무한루프..
return str;
}
어떤 타입의 요소가 들어있을지 알 수 없어서 와일드카드(<?>
)를 이용해주었다.
( 그냥 Object타입으로 해도 된다. )
똑같이 정상적으로 기능수행 한다.
else if(data instanceof Object[]){
Object[] arr = (Object[]) data;
for(int i = 0; i < arr.length; i++) {
arr[i] = stringify(arr[i]);
}
return Arrays.toString(arr).replaceAll(" ", "");
}
일반적인 for문으로도 위와 같이 가능하다. 어찌보면 제일 간단하다...
array(배열)을 바로 String으로 바꿀 수 있는 것을 까먹고 있었다.
//입력된 값이 HashMap일 경우
/*
만들고자하는 형태
ad : 3, fg : 5 -> "{"ad":"3","fg":"5"}"
"{"a":"apple"}"
"{"bar":false,"foo":true,"baz":null}"
"{"bar":false,"foo":true,"baz":null,"
*/
else if(data instanceof HashMap){
if(((HashMap) data).isEmpty()){
return "{}";
}
String res = "{";
for (Map.Entry<?, ?> o : ((HashMap<?, ?>) data).entrySet()) {
res += stringify(o.getKey())+ ":" + stringify(o.getValue())+",";
}
res = res.substring(0,res.length()-1);
res += "}";
return res;
}
//지정되지 않은 타입의 경우에는 "null"을 리턴합니다.
return "null";
}
와일드카드를 이용해 요소의 타입에 상관없이 기능하도록 했지만 Object로 해도 상관없다.
위 방법은 res라는 String변수에 키와값을 하나씩 String으로 변환 시킨 다음 더해서 쌓아가는 방식이다.
아래와 같이 요소값들을 먼저 string타입으로 변환 시킨 후 map을 통째로 string으로 변화 시켜도 된다.
else if(data instanceof HashMap){
HashMap<?,?> map = (HashMap<?,?>) data;
HashMap<String,String> result = new LinkedHashMap<>(); // 그냥 HashMap은 순차저장x LinkedHashMap은 순차적으로 저장해준다.
for(Map.Entry<?,?> entry : map.entrySet()){
String value = stringify(entry.getValue());
String key = stringify(entry.getKey());
result.put(key, value);
}
return result.toString().replaceAll("=",":").replaceAll(" ", "");
}
for문안에 있는 재귀 내용이 빠지면 아래와 같이 요소값들이 JSON형태를 안하고 있어 실패하는 것을 확인할 수 있다.
public String stringify(Object data) {
//입력된 값이 문자열일 경우
if(data instanceof String){
String text = "\"";
return text+data+text;
}
//입력된 값이 Integer일 경우
else if(data instanceof Integer){
String changed = Integer.toString((Integer) data);
return changed;
}
//입력된 값이 Boolean일 경우
else if(data instanceof Boolean){
String changed = String.valueOf(data);
return changed;
}
//입력된 값이 Object[]일 경우
else if(data instanceof Object[]){
if(((Object[]) data).length == 0) return "[]";
String str = "[";
Iterator<?> it = Arrays.asList((Object[])data).iterator();
while (it.hasNext()) {
str += stringify(it.next()) + ",";
}
str = str.substring(0,str.length()-1);
str += "]"; // Iterator 2차 실패... 무한루프..
return str;
}
//입력된 값이 HashMap일 경우
else if(data instanceof HashMap){
if(((HashMap) data).isEmpty()){
return "{}";
}
String res = "{";
for (Map.Entry<?, ?> o : ((HashMap<?, ?>) data).entrySet()) {
res += stringify(o.getKey())+ ":" + stringify(o.getValue())+",";
}
res = res.substring(0,res.length()-1);
res += "}";
return res;
}
//지정되지 않은 타입의 경우에는 "null"을 리턴합니다.
return "null";
}
// 간략하게 바꾸기
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
public class JSON {
public String stringify(Object data) {
//입력된 값이 문자열인 경우
if(data instanceof String){
//String text = "\""; // """로 쓰면 오류 \가 "를 인식해준다.
//return text+data+text;
return String.format("\"%s\"",data); // format을 이용해 더 간단하게 정리가능.
}
//입력된 값이 Integer일 경우
else if(data instanceof Integer){
// String changed = Integer.toString((Integer) data);
// return changed;
//return data + ""; // 문자열과 Integer를 합치면 자동으로 문자열로 변한다.
//return String.format("%d",(Integer)data);
return String.valueOf(data);
// toString()은 null값을 입력받는 경우 Null PointerException(NPE)을 발생시킨다.
// valueOf()는 null값을 입력받는 경우 "null"이라는 문자열이 할당된다.
//return data.toString(); // 주소값이 출력될 수 있음. 좋은방법은 아님.
}
//입력된 값이 Boolean일 경우
else if(data instanceof Boolean){
// String changed = String.valueOf(data);
// return changed;
//return data + "";
//return String.format("%b", data);
if((Boolean)data) return "true";
else return "false"; //이렇게 하드코딩해도 됨.
//return data.toString();
}
// 입력된 값이 Object[]일 경우
// "[8,"hi"]"
// "[8,[[],3,4]]"
// "[[[]]]"
else if(data instanceof Object[]){
//if(((Object[]) data).length == 0) return "[]";
//String str = "[";
// Arrays.asList(data).stream()
// .flatMap(Array -> (Object)Array) // flatMap 자체는 이중배열이 아닌 요소를 문자단위로 다 뜯어버린다. 즉, 잘못된 방법이다.
// .forEach();// stream 1차 실패... inner타입이 뭘지 알 수 없어서 형변환 했는데 안됨.
// Iterator<?> it = Arrays.asList((Object[])data).iterator();
// while (it.hasNext()) {
// str += stringify(it.next()) + ",";
// }
// str = str.substring(0,str.length()-1);
// str += "]"; // Iterator 2차 실패... 무한루프..
// return str;
// for (Object o : (Object[])data) { // data타입 선언 잘못해서 무한루프... 돌았었다..
// str += stringify(o)+",";
// }
// str = str.substring(0,str.length()-1);
// str += "]";
// return str;
Object[] arr = (Object[]) data;
for(int i = 0; i < arr.length; i++) {
arr[i] = stringify(arr[i]);
}
return Arrays.toString(arr).replaceAll(" ", "");
//replaceAll(" ", "") = 공백 제거, regex를 replacement로 바꿔라.
// Arrays.stream(data).flatMap(innerArray -> Arrays.stream(innerArray))
// iter
//Iterator<Object> iterator = data.iterator();
//for (Object o : (Object[]) data) {
// return new stringifyJSON(o);
// }
}
//입력된 값이 HashMap일 경우
else if(data instanceof HashMap){
/*
ad : 3, fg : 5
"{"ad":"3","fg":"5"}"
"{"a":"apple"}"
"{"bar":false,"foo":true,"baz":null}"
"{"bar":false,"foo":true,"baz":null,"
*/
// if(((HashMap) data).isEmpty()){
// return "{}";
// }
//
// String res = "{";
// for (Map.Entry<?, ?> o : ((HashMap<?, ?>) data).entrySet()) {
// res += stringify(o.getKey())+ ":" + stringify(o.getValue())+",";
// }
// res = res.substring(0,res.length()-1);
// res += "}";
//
// return res;
HashMap<?,?> map = (HashMap<?,?>) data;
HashMap<String,String> result = new LinkedHashMap<>(); // 그냥 HashMap은 순차저장x LinkedHashMap은 순차적으로 저장해준다.
for(Map.Entry<?,?> entry : map.entrySet()){
String value = stringify(entry.getValue());
String key = stringify(entry.getKey());
result.put(key, value);
}
return result.toString().replaceAll("=",":").replaceAll(" ", "");
}
//지정되지 않은 타입의 경우에는 "null"을 리턴합니다.
return "null";
}
}
이제 완료한 과제를 pr(Pull requests)해보자.
// push 하는 방법
git add .
(git status == add 확인)
git commit -m "변경내용"
(git log == commit 확인)
git remote add 주소명 HTTPS주소
(git remote --v == 주소 확인)
git push 주소명 main
이렇게 push한 뒤 git사이트 나의 저장소에서 pr하면 된다.
타입에 살고 타입에 죽는다.
타입 잘맞추자.