무슨 소리일까 ..?
while도 for도 이해가 되었는데,
do ~ while은 정확하게 어떤 상황에 사용하면 좋을 지에 대한 감이 잡히지 않았다
do~while: 최초 1회 연산 수행 수 조건문을 확인해 더 반복할지 결정한다는데 ,,
그럼 일단 조건이 무엇이든지 (조건이 true인지 false인지 확인하지 않고)
우선은 do 문 안에 있는 코드를 한 번 실행한 뒤에,
while문 안에 있는 조건을 파악하여 맞으면 반복하고, 불만족이면 반복을 멈춘다는 의미로 이해했다.
그렇다면 .. 코드를 만들어보자 !
int num = 5;
do {
System.out.println(num);
} while (num < 4);
이렇게 되면, 우선은 do문에 있는 print문을 시행해 결과창에는 5가 뜨게 된다.
그 후에 while문 조건을 만족하지 못하므로 반복을 멈추게 된다.
만약, 반복이 되는 경우라면 ..?
while문에 들어가는 조건이 num > 4 이기만 해도, 조건을 만족하기 때문에 무한 반복이 되지 않을까?
int num = 5;
do {
System.out.println(num);
} while (num > 4);
.. 엄청난 무한반복으로 인해 놀라 급히 중단하고 돌아왔다.
while문은 무한반복을 조심하라고 하셨는데, 알면서도 실행해본 사람 .. That's me!
그렇다면 기존의 while문 처럼 무한반복 되지 않도록 조건을 넣어주고 싶은데,,
do 문에 넣으면 될까?
int num = 5;
do {
System.out.println(num);
num ++;
} while (num > 4);
나는 멍청이가 틀림없다.
반복을 중단하고 싶다고 했으면서 ...
num 크기를 키우면서 num이 4보다 크다는 조건을 주면..
당연히 무한 반복이 되지 않을까 ..? 정말 슬프다.. 실수하지 않게 정신을 똑바로 차려야 한다.
num 값을 1로 주고, num이 5보다 작을 때 까지만(4까지만) 반복하는 코드로 다시 바꿔보겠다.
int num = 1;
do {
System.out.println(num);
num ++ ;
} while (num < 5);
이렇게 작성하고 코드를 시행하면 원하는 대로 1부터 4까지만 출력 가능하다.
아쉬우니까 코드 하나만 더 작성해보려고 한다.
이번에는 반대로 5부터 1까지만 출력해보고자 한다.
그렇다면 초기값을 5로 주고 조건을 0보다 클 때까지로 바꿔보자 !
int num = 5;
do {
System.out.println(num);
num -- ;
} while (num > 0);
원하는대로 출력이 잘 되는 것을 확인했다 !
do ~ while문의 구조는 아래와 같다.
do {
} while (조건) ;
do에 실행할 코드를 적어주면 되는데, while에 들어간 조건의 참 혹은 거짓 여부에 관계 없이 우선 do 안에 들어간 코드가 1번 먼저 실행된다.
그리고 기존의 while문처럼 증감 여부를 나타내주고 싶다면 do문 안에 넣어주면 된다!
=> 기존의 while문과 동일하게 반복문을 do문 안에 작성하되, do문 안에 들어간 실행문은 조건의 T/F 여부와 관계없이 1회 실행된다. 그리고 조건은 do 뒤에 붙는 while문 괄호 안에 적어주면 된다.
while do~while 코드 형식 while(조건) {
}do {
} while (조건);실행 방식 조건이 true면 반복 조건 t/f 관계없이 do 1회 실행 => do~while을 알기 전에는 조건 여부에 관계 없이 코드를 한 번 실행해주고 싶다고 하면, while문 앞에 한 번 써주고, while문을 다시 작성해주는 방법을 생각했는데, do~while문을 알았으니 위와 같은 코드를 작성하고 싶다면 do~while문을 사용하면 될 것 같다!
레시피를 입력받고 출력해주는 문제였다.
입력받는 String(문자열)을 배열에 저장해서 한 번에 출력해 주어야 하는데,
콘솔(Console)창에 문장을 입력했더니 세상에 배열에 띄어쓰기 단위로 저장이 되는 것이 아닌가 ..?
입력을 받을 때 10번 입력받는 것으로 설정해두었는데,
10 문장을 다 채우기도 전에 입력 받는 것이 끝나버려서 으잉..? 했다.
그리고 출력 될 때도 띄어쓰기대로 출력이 되길래 매우 당황했다..
// console 창에서
<입력>
안녕하세요. 만나서 정말 반갑습니다.
앞으로도 잘 부탁드립니다.
// 이런식으로 입력하면
<출력>
1. 안녕하세요.
2. 만나서
3. 정말
...
이런식이었다 ..!
아무래도 입력받는 next()에 문제가 있다고 생각해서
next()메소드가 스페이스(띄어쓰기)로 입력받는지,
만약 그렇다면 엔터(줄바꿈)으로 입력받는 메소드가 있는지 검색해보았다.
아니나 다를까 .. 검색의 결과
next()는 띄어쓰기 단위로 입력을 받고, nextLine()이라는 메소드가 엔터 단위로 입력을 받는다고 한다 ..
그 사실을 모르고 next()로 입력을 받았으니 .. 과연 아름다운 결과가 발생한 것이었다 ^^ ..
그래서 입력받아주는 메소드를 변경해주었다.
// main 메소드 안에서 작성된 코드
// Scanner는 위에 import 해주어야 한다
Scanner sc = new Scanner(System.in);
String title = sc.nextLine(); // 레시피의 제목 입력받기
float star = sc.nextFloat(); // 레시피 평점 입력받기
sc.nextLine(); // 다음 줄의 줄바꿈(개행문자) 입력받기 방지
// 입력받기
String [] recipe = new String [10]; // 배열 10칸 생성
for (int i=0; i<10; i++) {
String s = sc.nextLine(); // 레시피 한 줄 씩 입력받기
recipe[i] = s; // 배열에 하나씩 문장 넣어주기
}
// 출력하기
System.out.println("[ " + title + " ]");
// 입력받은 별점의 소수점을 제거하고 출력
// & 소수점을 제거한 별점을 100% 만점으로 나타낸다
System.out.println("별점 : " + (int)star + " ("+ (float)((int)star * 20) + "%)");
// 레시피를 줄마다 번호와 함께 출력해준다
for (int i=0; i<re.length; i++) {
System.out.println((i + 1) + ". " + re[i]);
}
Scanner의 next() 메소드와 nextLine() 메소드는 입력받는 기준이 다르다!
next() nextLine() 스페이스(띄어쓰기) 기준으로 입력받음 엔터(줄바꿈) 기준으로 입력받음
nextFloat() 다음에 nextLine()으로 또 입력받는 경우, 줄바꿈(개행문자) 입력 받기 방지를 위해 nextLine()을 한 번 실행해준다!
nextFloat()는 띄어쓰기, 줄바꿈 둘 다 입력받는 것이 가능하다
다만, nextLine()은 줄바꿈(개행문자)도 입력받기 때문에
nextFloat() 다음에 nextLine()으로 엔터를 치고 입력 받으려고 하면 줄바꿈이 저장된다.
따라서 nextFloat() 다음에 바로 입력 받는다면 nextLine()을 한 번 실행해준 후
그 다음부터 nextLine()으로 입력 받으면 줄바꿈이 저장되지 않는다!
선생님께서 작성해주신 코드와 내가 작성했던 코드에서의 차이점을 발견했다.
(전반적인 코드의 모습이 다르긴 했지만 .. 실행이 됐으니까 ..)
하나씩 읽어보면서 차이점을 찾아봤는데,
그 중에서 눈에 띄었던 점이 Object.equls()와 .equlse() 였다.
나는 문자열변수.equals("문자열") 이렇게 비교했는데,
선생님의 코드에서는 Object.equals(문자열변수, "문자열") 이렇게 적혀있었다.
나는 위에서
String save = sc.nextLine();
로 입력받은 save 변수를 비교하고 싶었다.
그래서 if 안에 조건으로
if (save.equals("비교할문자") {
}
이렇게 작성해주었는데, 이렇게 하지 않고
// import java.util.Objects;
if (Objects.equals(save, "비교할문자") {
}
이렇게 작성해도 일단 눈에 보이는 결과의 차이는 없었다.
하지만? 인터넷은 정보의 바다🌊
궁금한 부분은 검색해보면 된다.
나 말고도 궁금해하시는 분들이 많으셨는지 검색창에 입력하자마자 연관검색어로 바로 떴다.
결론은 null 값이 입력되었을 때의 차이점이었다.
직접 코드를 작성해서 실행해보았다.
1. equals(): null과 비교하게 될 경우, null 에러가 발생한다.
String a = null;
String b = "문자";
System.out.println(a.equals(b));
⬆️ 이렇게 작성하면, 오류가 난다 .. (당연함)
Cannot invoke "String.equals(Object)" because "a" is null
⬆️ 라고 한다. 결국 a가 null이기 때문에 equals 메소드를 사용할 수 없다는 의미인 듯 하다.
그렇다면 ... 위치가 바뀌면 어떻게 될까?
String a = null;
String b = "문자";
System.out.println(b.equals(a));
⬆️ 결과가 나온다 .. false 라고 ..
신기하다 .. 그렇다면 equals 앞에 붙은 문자가 null일 경우에만 오류가 난다는 것이다.
둘다 a를 넣어도 결과는 같다. 결국 앞에 붙은 a가 null이기 때문이다.
2. Objects.equlas(): null일 경우에도 비교가 가능하다.
String a = null;
String b = "문자";
System.out.println(Objects.equals(a, b));
⬆️ 결과값이 false로 나온다 ..!
String a = null;
String b = null;
System.out.println(Objects.equals(a, b));
String a = "문자";
String b = "문자";
System.out.println(Objects.equals(a, b));
⬆️ 위의 두 개 코드로 비교하면 결과값이 true로 나오고,
String a = "문자";
String b = null;
System.out.println(Objects.equals(a, b));
⬆️ 다시 하나를 null로 바꿔주면 결과값이 false로 출력된다.
.equals()앞에 붙는 문자가 null일 경우 에러가 발생하니,
대신 Objects.equals()를 사용해줄 수 있다.
비교하려는 두 문자열 중, 어떤 것이 null이 될 지 알 수 없기 때문에,
Objects.equals(문자열1, 문자열2)
코드를 사용해주는 것이 좋을 듯 하다!
List / Set / Map 세 가지 자료구조를 이용하여
자료구조에 값을 입력받고, 출력해주는 코드를 작성해주어야 한다.
ArrayList와 Map은 .get(index값)으로 원소를 불러올 수 있는데,
Set은 ... 어떻게 해야할 지 ..
저장하는 것은 배웠는데 값을 하나씩 불러오는 방법은 배우지 못했다 இ௰இ
Set 을 출력하는 법을 검색했다.
그랬더니 새로운 방법을 알게 되었다.
우선 Set은 중복 값을 저장하지 않음 & 순서가 상관없음 이라는 특징을 갖고 있다.
그렇지만 나는 순서대로 저장하여 순서대로 출력하는 코드가 필요했다.
(Set은 순서가 없어 인덱스를 지정하여 값을 가져올 수 없다고 한다.)
Iterator 를 통해 가져오면 된다고 한다!!
이는 set 안의 값을 Iterator에 넣은 후에 .next()를 통해서 하나씩 값을 뽑으면 되는 방법이라고 한다.
코드로 작성해보자 !
Scanner sc = new Scanner(System.in);
Set<String> set1 = new HashSet<>();
// "끝"을 입력받으면 멈춘다
while (true) {
// 값을 입력받는 부분
String s = sc.nextLine();
if (Objects.equals(s, "끝")) {
break;
}
set1.add(s);
}
// set에 들어간 원소들을 출력해보자
// 그러기 위해서 Iterator를 사용한다
Iterator<String> iterator = set1.iterator();
// 이제 원소를 출력한다
for (int i=0; i<set1.size(); i++) {
// 순서와 함께 출력
System.out.println((i+1) + ": " +iterator.next());
}
라고 코드를 작성했는데 ...
음 ... 해결이 되지 않았다 ..
순서대로 들어가주어야 하는데, Set 자료구조 특성 상 순서가 중요하지 않아서 그런지
iterator에도 순서대로 저장되지 않은 듯 하다 ..
Set이 순서가 중요하지 않다고 하더라도 hashCode값에 따라 순서가 정해진다는데,
코드에 오류가 있는 것일까 ..?
![]() | ![]() |
---|
왜그래 ...
순서대로 나와주어야지 친구야 ..
이러지 않기로 했잖아 (뭘 ㅠ)
set의 다른 종류를 사용하면 된다 ..
모르면 검색 or 질문이 살길이다 ..!
고민하다가 튜터님께 질문을 드렸는데, 검색해 볼 내용을 알려주셨다
set의 종류에 hashset, treeset은 들었는데, linkedhashset이라는 것도 있었다!
linkedhashset이 바로 값을 넣은 순서대로 저장 가능한 set이었다..
따라서 set에서 순서가 중요한 나는 LinkedHashSet을 사용해야 하는 것이었다 !
그것도 모르고 HashSet 써놓고 왜 순서대로 안되니ㅠㅠ 이러고 있으니 .. (hashset아 미안)
다시 작성해보면 (HashSet을 LinkedHashSet으로만 바꾸면 된다)
Scanner sc = new Scanner(System.in);
Set<String> set1 = new LinkedHashSet<>();
// "끝"을 입력받으면 멈춘다
while (true) {
// 값을 입력받는 부분
String s = sc.nextLine();
if (Objects.equals(s, "끝")) {
break;
}
set1.add(s);
}
// set에 들어간 원소들을 출력해보자
// 그러기 위해서 Iterator를 사용한다
Iterator<String> iterator = set1.iterator();
// 이제 원소를 출력한다
for (int i=0; i<set1.size(); i++) {
// 순서와 함께 출력
System.out.println((i+1) + ": " +iterator.next());
}
// 혹은
int j = 0;
while (iterator.hasNext()) {
System.out.println((j+1) + ": " + iterator.next());
j ++;
}
HashSet TreeSet LinkedHashSet 중복 안됨, 순서 없음 값 (a~z & 1~9)에 따라 순서를 만들어 줌 값을 더한 순서에 따라 순서가 결정됨
Iterator의 메소드에 hasNext()와 next()가 있다.
출력하는 코드를 작성할 때, 자료 구조의 형태가 위에서 바뀌어도 출력하는 부분의 코드를 수정할 일이 없도록 (=코드 유지보수를 위해) Iterator를 사용하면 좋다.
[To. US 선생님 .. 감사합니다]
Iterator<자료형> iterator = 자료구조명.iterator();
// 입력하는 부분 생략
// 출력하는 부분
int i = 0; // 함께 출력해줄 번호 변수 선언
while(iterator.hasNext()) { // iterator에 다음 값이 있다면 while문을 돌려라
// iterator의 다음 값 출력
System.out.println((i+1) + ": " + iterator.next());
}
for(int i = 0; i < 5; i++) {
for (int j = 0; j < 5; j ++ ) {
System.out.print(" * ");
}
System.out.println();
}
⬆️ 이렇게 되면 5줄에 각각 5개씩 별이 찍혀 나온다..!
for(int i = 0; i < 5; i++) {
for (int j = 0; j < (i+1); j ++ ) {
System.out.print(" * ");
}
System.out.println();
}
⬆️ 첫 번째 줄에 별 하나, 두 번째 줄에 별 두개,,, 이런식으로 별 개수가 늘어나며 찍혀 나온다!
for(int i = 0; i < 5; i++) {
for (int j = 5; j > i; j -- ) {
System.out.print(" * ");
}
System.out.println();
}
⬆️ 첫 번째 줄에 별 다섯개, 두 번째 줄에 별 네개,,, 이런식으로 별 개수가 줄어들며 찍혀 나온다!
for(int i = 0; i < 3; i++) {
for (int j = 0; j < 5; j ++ ) {
if (((i*j) == 0 && (i+j) != 2) || ((i < j) && (i*j)==4)) {
System.out.print(" ");
} else {
System.out.print(" * ");
}
}
System.out.println();
}
⬆️ 이렇게 되면 3줄짜리 가운데 기준 별찍기 모양이 나온다 (아래의 이미지처럼)
이 코드를 좀 더 간단하게 써보고 싶은데, 규칙이 생각이 나지 않는다 ..
이중 배열을 이용하는 거라 각 방의 번호를 생각해보면 될 것 같은데
저렇게 복잡하게 밖에 규칙이 나오지 않았다 ..
조금 더 찾아보는 게 좋을 것 같다
int [] arr = {1,2,3,4};
for (int num: arr) {
System.out.print(num + " "); // 출력: 1 2 3 4
}
이런 형태로 작성한다.
기존 대로 작성하려면 for 문 안의 형태가 (int i=0; i<arr.length; i++)
이렇게 되어야 하는데, 훨씬 간단한 형태로 쓸 수 있었다..!
String [] arr2 = {"DJ" , "YS" , "GK" , "DW"};
for (String mem: arr2) {
System.out.print(mem + " "); // 출력: DJ YS GK DW
}
얕은 복사 방법으로 배열을 복사하면
int [] arr1 = {1,2,3,4};
int [] arr2 = arr1;
이렇게 코드를 작성할 수 있다
이렇게 복사된 상태에서 두 번째 배열의 2번째 원소의 값을 바꾼다면
arr2[1] = 10;
이렇게 작성해볼 수 있는데, 이 상태에서 원래 배열의 2번째 원소를 출력하면
System.out.println(arr1[1]);
바뀐 값인 10으로 출력이 된다 ..
복사 하고 나서 건드리는 값은 원본에 영향을 미치지 않을 것이라고 생각했는데, 얕은 복사의 경우 변경 사항이 같이 적용된다고 한다.
이렇게 원본에 영향을 주지 않게 하기 위해 깊은 복사 방법이 있다.
1. 새로 만든 배열의 길이를 원본 배열의 길이와 같게 생성하고, 원소 하나하나의 값을 넣어주기
int [] arr1 = {1,2,3,4};
int [] arr2 = new int [arr1.length];
for (int i=0; i<arr1.length; i++) {
arr2[i] = arr1[i];
}
// arr2의 값을 변화시켜도, arr1의 값에는 변화가 없다
arr2[0] = 10;
System.out.println(arr1[0]);
int [] arr1 = {1,2,3,4};
int [] arr2 = arr1.clone(); // 가장 간단한 방법
// arr2의 값을 변화시켜도, arr1의 값에는 변화가 없다
arr2[0] = 10;
System.out.println(arr1[0]);
// import java.util.Arrays;
int [] arr1 = {1,2,3,4};
// 배열과 함께, 배열의 길이도 매개변수로 넣어주어야 한다
int [] arr2 = Arrays.copyOf(arr1, arr1.length);
// arr2의 값을 변화시켜도, arr1의 값에는 변화가 없다
arr2[0] = 10;
System.out.println(arr1[0]);
3가지 방법 모두, 원래 배열에는 영향을 미치지 않으므로, arr1[0]을 출력했을 때, 원래 값인 1이 출력된다.