Java/C/Python
- 문제
- 1. [Java] 암호 해독 - cipher.charAt(i - 1)
- 2. [Java] 대문자와 소문자
- 3. [Java] 영어가 싫어요 - numbers.replaceAll(a,b);
- 4. [Java] 인덱스 바꾸기 - return new String(arr);
- 5. [Java] 한 번만 등장한 문자 (문자열) - sb.toString();
- 6. [Java] 약수구하기 - list.toArray(new String[0]);
- 7. [Java] 가장 큰 수 찾기- return new int[]{max, idx};
- 8. [Java] 문자열 계산하기 - Character.isDigit(c)
- 9. [Java] 배열의 유사도(배열, Set) - set.contains(str)
- 10. [Java] 숫자 찾기 - String.indexOf(), String.valueOf()
- 11. [Java] 삼각형의 완성조건(2): case2는 왜 longer - shorter인가?
- 12. [Java] 옹알이(1): break vs continue
- 주제별 정리
- Java Coding Test Cheatsheet
- Java 배열 관련 주요 메서드 정리
- Java 표현식(Expression)
- Java 이스케이프 시퀀스 (Escape Sequences)
- Java 고급 출력 포맷 (이스케이프 시퀀스, printf, format)
- Java 정규식(Regular Expression)
- Java Arrays.sort() 정리
- [C언어] for루프의 실행 순서 (정보처리기사 25년 1회 실기)
- [Java] 부모의 생성자와 Override (정보처리기사 25년 1회 실기)
- [Java] 재귀함수 (정보처리기사 25년 1회 실기)
- [Python] __init__(클래스 생성자) & 이진트리 (정보처리기사 25년 1회 실기)
- [Java] 상속: 생성자 호출 순서
- [Java] 상속/생성자에서 this()의 의미
문제
1. [Java] 암호 해독 - cipher.charAt(i - 1)
https://school.programmers.co.kr/learn/courses/30/lessons/120892
오답
class Solution {
public String solution(String cipher, int code) {
StringBuilder sb = new StringBuilder();
for (int i = code; i <= cipher.length(); code++;) {
sb.append(cipher[i-1]); // 인덱스는 0부터 시작
}
return sb.toString();
}
}
✅ for문의 증가식 오류
for (int i = code; i <= cipher.length(); code++;)
- → code++→ 증가식 자리에 있어야 할 변수는 i (반복 제어 변수)
- → code++는 code 값을 계속 바꿔버려서 논리적으로도 틀림
✅ 문자열 인덱스 접근 오류
- Java에서 문자열은 []로 접근 불가 → charAt(index) 사용해야 함
정답 코드
class Solution {
public String solution(String cipher, int code) {
StringBuilder sb = new StringBuilder();
for (int i = code; i <= cipher.length(); i += code) {
sb.append(cipher.charAt(i - 1)); // 인덱스는 0부터 시작
}
return sb.toString();
}
}
2. [Java] 대문자와 소문자
https://school.programmers.co.kr/learn/courses/30/lessons/120893
오답
class Solution {
public String solution(String my_string) {
StringBuilder sb = new StringBuilder();
for(int i = 0; i < my_string.length(); i++) {
if(Character.isUpperCase((my_string.charAt(i)))) {
sb.append(Character.toLowerCase(my_string.charAt(i)));
} else {
sb.append(Character.toUpperCase(my_string.charAt(i)));
}
}
return sb.toString();
}
}
✅ my_string.charAt(i)는 위로 빼서 char c = my_string.charAt(i); 로 만들어 주면 코드가 간단해진다.
개선한 코드
class Solution {
public String solution(String my_string) {
StringBuilder sb = new StringBuilder();
for(int i = 0; i < my_string.length(); i++) {
char c = my_string.charAt(i);
if(Character.isUpperCase(c)) {
sb.append(Character.toLowerCase(c));
} else {
sb.append(Character.toUpperCase(c));
}
}
return sb.toString();
}
}
3. [Java] 영어가 싫어요 - numbers.replaceAll(a,b);
https://school.programmers.co.kr/learn/courses/30/lessons/120894
틀린 코드
class Solution {
public long solution(String numbers) {
String[] words = {
"zero", "one", "two", "three", "four", "five",
"six", "seven", "eight", "nine"
};
for (int i = 0; i < words.length; i++) {
numbers = numbers.replace(words[i], String.valueOf(i));
}
return Integer.parseInt(numbers);
}
}
테스트는 통과했으나 답안 제출시 런타임 에러
✅ Integer.parseInt() 사용 시 런타임 에러
- 만약 숫자 문자열이 int의 범위를 초과하면 런타임 에러가 발생한다.
- 그래서 일부 테스트 케이스에서 에러가 난 것
- 예를 들어서, 만약 13자리 숫자가 들어오면 int(32비트 정수)의 최대값 2,147,483,647을 초과함
- → Integer.parseInt()는 처리 못 함 → 런타임 에러 (NumberFormatException) 발생
- 따라서 return 타입을 int가 아니라 long 타입으로 변경해야 한다.
개선한 코드
class Solution {
public long solution(String numbers) {
String[] words = {
"zero", "one", "two", "three", "four", "five",
"six", "seven", "eight", "nine"
};
for (int i = 0; i < words.length; i++) {
numbers = numbers.replaceAll(words[i], String.valueOf(i));
}
return Long.parseLong(numbers); // ✅ int → long 으로 변경
}
}
기본형 VS 래퍼클래스
✅ 기본형 (Primitive type)
- int → 기본형 정수 타입 (primitive int)
- long → 기본형 정수 타입 (primitive long)
👉 메모리에 값 자체를 저장함
👉 성능이 빠르고 메모리 사용이 효율적
✅ 참조형 (Reference type, Wrapper class)
- Integer → 참조형 (래퍼 클래스, Wrapper class for int)
- Long → 참조형 (래퍼 클래스, Wrapper class for long)
👉 객체이기 때문에 null 값을 가질 수 있음
👉 ArrayList<Integer>처럼 제네릭에 사용 가능
👉 기본형은 제네릭에서 사용할 수 없음 → 참조형 필요
4. [Java] 인덱스 바꾸기 - return new String(arr);
https://school.programmers.co.kr/learn/courses/30/lessons/120895
틀린 코드
class Solution {
public String solution(String my_string, int num1, int num2) {
String[] arr = String.toCharArray(my_string);
char tmp = arr[num1];
arr[num1] = arr[num2];
arr[num2] = tmp;
return arr.toString();
}
}
✅ String[] arr = String.toCharArray(my_string);
- → String.toCharArray()는 char[]를 리턴하므로 타입은 String[]이 아니라 char[]이어야 한다.
✅ String.toCharArray() 용법
- → my_string.toCharArray();
- toCharArray()는 String 클래스의 인스턴스 메서드이다.
- 그래서 String.toCharArray() ❌ (클래스 메서드 아님)
- 반드시 인스턴스(my_string)에 붙여야 한다.
✅ return arr.toString();
- char[]에서 toString()을 쓰면 주소값이 출력된다.
- → 대신 new String(arr)을 사용해야 문자열로 출력된다.
개선한 코드
class Solution {
public String solution(String my_string, int num1, int num2) {
char[] arr = my_string.toCharArray();
char tmp = arr[num1];
arr[num1] = arr[num2];
arr[num2] = tmp;
return new String(arr);
}
}
클래스 메서드 VS 인스턴스 메서드
자바에서 메서드는 크게 인스턴스 메서드와 **클래스 메서드(static 메서드)**로 나뉘는데, 이 둘은 메서드를 어떻게 호출하고 언제 사용하는지에 따라 중요한 차이를 가진다.
클래스 메서드는 static 키워드로 정의되어 있어 객체를 만들지 않아도 클래스 이름으로 바로 호출할 수 있는 메서드이다. 클래스 메서드는 객체의 고유 상태와는 무관하며, 보통 모든 인스턴스에서 공통으로 사용하는 로직을 구현할 때 사용됩니다. 대표적인 예로 Integer.parseInt("123")는 문자열을 정수로 변환하는 클래스 메서드로, Integer라는 클래스 이름만으로 바로 호출할 수 있다.
반면 인스턴스 메서드는 특정 객체가 생성되어야만 사용할 수 있는 메서드이다. 즉, new 키워드로 객체를 만든 후에야 호출이 가능하며, 주로 그 객체의 상태(멤버 변수)를 기반으로 동작한다. 예를 들어, str.length()는 문자열 객체 str의 길이를 반환하는 인스턴스 메서드인데, 이는 str이라는 객체가 존재해야만 호출할 수 있다.
인스턴스 메서드는 객체마다 상태가 다를 수 있기 때문에, 같은 메서드를 호출하더라도 객체에 따라 결과가 달라진다. 예를 들어, Person이라는 클래스에 sayHello()라는 인스턴스 메서드가 있다면, person1.sayHello()와 person2.sayHello()는 각각 다른 이름을 출력한다. 이는 각 인스턴스가 가지고 있는 name 값이 다르기 때문이다.
이처럼 인스턴스 메서드는 객체의 고유한 데이터를 다루고 조작할 때 유용하며, 클래스 메서드는 그와 무관한 공통 기능을 제공할 때 사용된다. 개발자는 기능의 목적에 따라 두 메서드 중 적절한 쪽을 선택해야 한다. 객체마다 달라지는 동작이 필요하다면 인스턴스 메서드, 객체 없이 호출해도 되는 범용적인 기능이라면 클래스 메서드를 사용하는 것이 좋다.
✅ 1. 인스턴스 메서드 (Instance Method)
정의
- 객체를 생성해야 호출할 수 있는 메서드
- 객체의 상태(필드 값)에 따라 동작이 달라질 수 있음
특징
- new 키워드로 인스턴스를 만든 후 사용
- 보통 객체 내부의 데이터를 다룰 때 사용됨
예시
public class Person {
String name;
public void sayHello() {
System.out.println("Hello, my name is " + name);
}
}
// 사용
Person p = new Person();
p.name = "Dain";
p.sayHello(); // 출력: Hello, my name is Dain
✅ 2. 클래스 메서드 (Class Method)
정의
- 객체 생성 없이 호출 가능한 메서드
- static 키워드로 정의됨
특징
- 클래스 이름으로 직접 호출 가능
- 객체의 상태와 무관하게 동작함
- 보통 공통 기능, 유틸리티 기능 등에 사용됨
예시
public class MathUtil {
public static int add(int a, int b) {
return a + b;
}
}
// 사용
int result = MathUtil.add(3, 5); // 객체 없이 호출 가능
5. [Java] 한 번만 등장한 문자 (문자열) - sb.toString();
https://school.programmers.co.kr/learn/courses/30/lessons/120896
틀린 코드
import java.util.*;
class Solution {
public String solution(String s) {
int[] cnt = new int[26];
for(char c : s.toCharArray()) {
cnt[c - 'a']++;
}
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 26; i++) {
if (cnt[i] == 1) sb.append((char)i+'a');
}
return sb.toString();
}
}
✅ sb.append((char)i + 'a');
- 이 int를 char 타입으로 형변환하려는 의도지만, 실제로는 그냥 int로 연산된다.
- 예를 들어, i = 0일 때 char는 연산 시 int로 자동 변환되므로 → 0 + 97
(char)i + 'a' // => 정수 + 정수 → int로 연산됨
→ i + 'a' 전체를 char로 캐스팅해야 문자로 제대로 변환된다.
sb.append((char)(i + 'a'));
개선한 코드
import java.util.*;
class Solution {
public String solution(String s) {
int[] cnt = new int[26];
for(char c : s.toCharArray()) {
cnt[c - 'a']++;
}
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 26; i++) {
if (cnt[i] == 1) sb.append((char)(i + 'a'));
}
return sb.toString();
}
}
6. [Java] 약수구하기 - list.toArray(new String[0]);
https://school.programmers.co.kr/learn/courses/30/lessons/120897
틀린 코드
import java.util.*;
class Solution {
public int[] solution(int n) {
ArrayList<Integer> list = new ArrayList<>();
for (int i = 0; i <= n; i++) {
if(n%i==0) list.append(i);
}
int[] answer = list.toArray(new String[0]);
return answer;
}
}
✅ for (int i = 0; i <= n; i++)
- ❗ i = 0일 때 n % 0은 ArithmeticException (0으로 나눌 수 없음!)
- ✅ 따라서 i = 1부터 시작해야 한다.
✅ list.add(i)
- ArrayList에서 값을 추가할 때는 append()가 아니라 add()를 사용해야 한다.
- list.append(i) → list.add(i)
✅ int[] answer = list.toArray(new String[0]);
- List → Array 로 변환할때 참조형은 toArray() 메서드로 가능하지만 int 같은 기본형은 직접 변환할 수 없다.
- 직접 반복문을 돌려서 변환해야 한다.
개선한 코드
import java.util.*;
class Solution {
public int[] solution(int n) {
ArrayList<Integer> list = new ArrayList<>();
for (int i = 1; i <= n; i++) {
if(n%i==0) list.add(i);
}
int[] answer = new int[list.size()];
for(int i = 0; i < list.size(); i++) {
answer[i] = list.get(i);
}
return answer;
}
}
Stream 사용 버전
import java.util.*;
import java.util.stream.*;
class Solution {
public int[] solution(int n) {
return IntStream.rangeClosed(1, n) // 1부터 n까지 숫자 생성
.filter(i -> n % i == 0) // 약수만 필터링
.toArray(); // int[]로 반환
}
}
List → Array 형변환
List 타입 | 변환 방법 |
---|---|
|
|
|
|
| 반복문으로 수동 변환 필요 |
✅ 1. List → Array (참조형, 예: Integer, String 등)
- toArray() 메서드로 변환이 가능하다.
List<String> list = Arrays.asList("a", "b", "c");
String[] arr = list.toArray(new String[0]);
- list.toArray(new String[0]): 타입을 명시한 배열로 변환
- new String[0]은 사이즈가 0이어도 자동으로 맞춰준다.
List<Integer> list = Arrays.asList(1, 2, 3);
Integer[] arr = list.toArray(new Integer[0]);
✅ 2. List → Array (기본형, 예: int)
- Java의 List<Integer>는 int[]로 직접 변환 불가능
- 직접 반복문으로 넣어줘야 한다.
List<Integer> list = Arrays.asList(1, 2, 3);
int[] arr = new int[list.size()];
for (int i = 0; i < list.size(); i++) {
arr[i] = list.get(i); // 언박싱 자동 수행
}
Array → List 형변환
변환 방법 | |
---|---|
|
|
|
|
| Arrays.stream(arr).boxed().collect(Collectors.toList()) |
✅ 1. 참조형 배열 → List (예: String[], Integer[] 등)
String[] arr = {"a", "b", "c"};
List<String> list = Arrays.asList(arr);
- Arrays.asList()는 배열을 고정 크기 리스트로 바꿔준다.
- 추가/삭제는 불가능 → 변경 가능한 리스트로 바꾸려면
List<String> modifiableList = new ArrayList<>(Arrays.asList(arr));
✅ 2. 기본형 배열 → List (예: int[], double[] 등)
- 자바의 int[]는 바로 List<Integer>로 바꿀 수 없다.
- Stream + boxed()를 활용한다.
int[] arr = {1, 2, 3, 4};
List<Integer> list = Arrays.stream(arr) // IntStream 생성
.boxed() // int → Integer (박싱)
.collect(Collectors.toList());
7. [Java] 가장 큰 수 찾기- return new int[]{max, idx};
https://school.programmers.co.kr/learn/courses/30/lessons/120899
틀린 코드
import java.util.*;
class Solution {
public int[] solution(int[] array) {
Arrays.sort(array);
int[] answer = new int[2];
answer[0] = array[array.length-1];
answer[1] = array.length-1;
return answer;
}
}
✅ index 계산 잘못됨
- array[array.length-1]는 정렬된 배열에서 최댓값이 맞지만, array.length-1은 최댓값의 원래 인덱스가 아님.
- 배열을 오름차순 정렬해버리면, 원래 배열에서 최댓값의 인덱스는 사라진다.
- 따라서 최댓값을 담을 변수 max, 인덱스를 담을 변수 idx를 선언하고 반복문을 돌려 최댓값과 인덱스를 갱신한 후, 두 변수를 배열로 변환해서 반환한다.
개선한 코드
import java.util.*;
class Solution {
public int[] solution(int[] array) {
int max = array[0];
int idx = 0;
for(int i = 1; i < array.length; i++) {
if (array[i] > max) {
max = array[i];
idx = i;
}
}
return new int[]{max, idx};
}
}
배열 리터럴을 반환할 때 쓰는 코드
int형 두 개짜리 배열을 직접 생성하고, 그 배열을 메서드의 반환값으로 사용하는 코드이다.
return new int[]{max, idx};
아래와 같이 배열을 만들고 반환하는 코드인데 한 줄로 줄인 것이다.
int[] result = new int[]{max, idx};
return result;
Java에서 return은 값을 반환하기만 하면 되기 때문에, 굳이 int[] result = ...처럼 중간에 변수에 담을 필요가 없다.
배열 리터럴은 선언 없이 쓰면 컴파일 오류가 난다.
int[] a;
a = {1, 2}; // ❌ 컴파일 오류
→ 대신 이렇게 써야 한다.
int[] a = new int[]{1, 2};
8. [Java] 문자열 계산하기 - Character.isDigit(c)
https://school.programmers.co.kr/learn/courses/30/lessons/120902
틀린 코드 (1차 시도)
class Solution {
public int solution(String my_string) {
char[] arr = my_string.toCharArray();
int sum = 0;
for (char c : arr ) {
if (c != '+' && c != '-' && c !=' ') {
sum+= Integer.parseInt(c);
}
}
return sum;
}
}
✅ Integer.parseInt()
- String을 int형으로 변환하는 코드를 써서 에러가 났다.
- char타입을 정수로 변환할 때는 Integer.parseInt() 대신 char 타입 변수에 - '0'을 한다. (ASCII 코드)
String → int VS char → int
1. String을 int로 변환할 때는 Integer.parseInt()
String str = "123";
// 문자열 → int
int num = Integer.parseInt(str); // 123
2. char을 숫자로 변환할 때는 char 타입 변수 - '0'
char c = '7';
// 문자 → 숫자 (정수값)
int digit = c - '0'; // '7' - '0' = 7
틀린 코드 (2차 시도)
class Solution {
public int solution(String my_string) {
char[] arr = my_string.toCharArray();
int sum = 0;
for (char c : arr ) {
if (c != '+' && c != '-' && c !=' ') {
sum+= c - '0';
}
}
return sum;
}
}
내가 쓴 코드는 모든 숫자를 "한 자리 숫자"로 처리한다. 즉, "12"가 있으면 1 + 2 = 3으로 계산한다.
String my_string = "a12b3";
→ arr = ['a', '1', '2', 'b', '3']
→ 결과: 1 + 2 + 3 = 6 (❗ 실제 12 + 3이 아님)
문제에서 원하는 것은 만약 "12 + 3" → 15로 계산하는 것이다.
String num 변수를 밖으로 빼고, Character.isDigit() 으로 c가 0~9사이 숫자형 문자열이면 num에 저장하는 형식으로 코드를 짜 주었다.
틀린 코드 (3차 시도)
class Solution {
public int solution(String my_string) {
char[] arr = my_string.toCharArray();
int sum = 0;
String num = "";
for (char c : arr ) {
if (Character.isDigit(c)) {
num += c;
} else {
if (!num.isEmpty()) {
sum += Integer.parseInt(num);
num = "";
}
}
}
if (!num.isEmpty()) {
sum += Integer.parseInt(num);
}
return sum;
}
}
덧셈만 하는게 아니라 뺄셈도 있었다. 연산자(+, -)에 따라 연산을 모두 처리하는 코드를 짜야 한다.
- 문자열을 " "(공백) 기준으로 나눠서
- 처음 값은 result에 저장
- 이후에는 연산자(+ 또는 -)와 숫자가 번갈아 등장
- 연산자에 따라 +면 더하고, -면 빼야 함
틀린 코드 (4차 시도)
class Solution {
public int solution(String my_string) {
String[] splited = my_string.split(" ");
int answer = Integer.parseInt(splited[0]);
for(int i = 1; i < splited.length; i += 2) {
String op = splited[i];
int num = Integer.parseInt(splited[i + 1]);
if (op == '+')) {
answer += num;
} else if (op == '-') {
answer -= num;
}
}
return answer;
}
}
✅ if(op == '+')
- 이 코드는 컴파일 에러가 난다.
- op는 String 타입으로 선언되었는데 '+'는 char로 타입이 달라서 비교 불가능하기 때문이다.
- Java에서는 문자열 내용을 비교할 때 ==를 쓰지 않는다. ==는 두 객체가 같은 주소(참조)를 가리킬 때만 true이다.
- 내용 비교는 반드시 .equals()를 써야 한다.
개선한 코드
class Solution {
public int solution(String my_string) {
String[] splited = my_string.split(" ");
int answer = Integer.parseInt(splited[0]);
for(int i = 1; i < splited.length; i += 2) {
String op = splited[i];
int num = Integer.parseInt(splited[i + 1]);
if (op.equals("+")) {
answer += num;
} else if (op.equals("-")) {
answer -= num;
}
}
return answer;
}
}
9. [Java] 배열의 유사도(배열, Set) - set.contains(str)
https://school.programmers.co.kr/learn/courses/30/lessons/120903
Set 을 활용한 코드
import java.util.*;
class Solution {
public int solution(String[] s1, String[] s2) {
Set<String> set = new HashSet<>(Arrays.asList(s1));
int cnt = 0;
for (String str : s2) {
if (set.contains(str)) cnt++;
}
return cnt;
}
}
✅ Set<String> set = new HashSet<>(Arrays.asList(s1));
- Set은 중복을 허용하지 않고, 검색 속도가 빠르기 때문에 비교 대상인 s1을 Set으로 변환하면 s2의 각 원소가 포함되어 있는지 빠르게 확인할 수 있다.
이중 for문(반복문)을 사용한 코드
class Solution {
public int solution(String[] s1, String[] s2) {
int cnt = 0;
for (int i = 0; i < s1.length; i++) {
for (int j = 0; j < s2.length; j++) {
if (s1[i].equals(s2[j])) {
cnt++;
break;
}
}
}
return cnt;
}
}
- 중복 카운팅을 피하려면 break;를 해야 한다.
- 문자열끼리 내용 비교는 .equals() 메서드를 사용해야 한다 ( == 은 주소값 비교)
10. [Java] 숫자 찾기 - String.indexOf(), String.valueOf()
https://school.programmers.co.kr/learn/courses/30/lessons/120904
틀린 코드
class Solution {
public int solution(int num, int k) {
String numToString = Integer.toString(num);
char[] arr = numToString.toCharArray();
char kToChar = (char) k;
int idx = -1;
for(int i = 0; i < arr.length; i++ ) {
if (arr[i] == kToChar) {
return i+1;
}
}
return idx;
}
}
✅ char kToChar = (char) (k + '0');
- 틀린 부분: char kToChar = (char) k;
- int 타입을 char 타입으로 변환하는 방법이 잘못됨
- → int 3을 (char)로 바꾸면 '3'이 아니라 아스키 문자 'ETX' 같은 이상한 문자가 됨
- → char kToChar = (char)(k + '0'); 또는 Character.forDigit(k, 10); 로 고쳐야 함
개선한 코드
class Solution {
public int solution(int num, int k) {
String numToString = Integer.toString(num);
char[] arr = numToString.toCharArray();
char kToChar = (char) (k + '0');
int idx = -1;
for(int i = 0; i < arr.length; i++ ) {
if (arr[i] == kToChar) {
return i+1;
}
}
return idx;
}
}
더 간단한 방법 (String API 활용)
public int solution(int num, int k) {
String s = String.valueOf(num);
int idx = s.indexOf(String.valueOf(k));
return idx == -1 ? -1 : idx + 1;
}
✅ String.indexOf()
문자열 내에서 특정 문자나 문자열이 처음 등장하는 위치의 인덱스를 알려주는 매우 유용한 메서드이다.
📌 기본 사용법
String s = "29183";
int idx = s.indexOf("1"); // 결과: 2
- 문자열 "1"이 "29183"에서 0-based index 2에 처음 등장
- 못 찾으면 -1을 반환
📌 자바 공식 문법
int indexOf(int ch) // 문자 또는 문자 코드 (ex: 'a', 97)
int indexOf(String str) // 문자열
✅ 삼항연산자
자바의 삼항 연산자(ternary operator)는 if-else를 한 줄로 간단하게 쓸 수 있게 해주는 조건 연산자이다.
📌 기본 사용법
조건식 ? 참일 때 값 : 거짓일 때 값;
- 조건식이 true이면 ? 다음의 값을 반환
- false이면 : 다음의 값을 반환
📌 예제
int age = 20;
String result = (age >= 18) ? "성인" : "미성년자";
System.out.println(result); // 출력: 성인
11. [Java] 삼각형의 완성조건(2): case2는 왜 longer - shorter인가?
1. 문제 요약
- https://school.programmers.co.kr/learn/courses/30/lessons/120868
- 프로그래머스 삼각형의 완성조건(2)
- 두 변이 주어졌을 때, 나머지 한 변
x
가 삼각형이 될 수 있는 정수 개수를 구하는 문제 - 삼각형이 되기 위한 조건: 가장 긴 변 < 나머지 두 변의 합
2. 정수 범위 공식
- x ∈ (longer - shorter, longer + shorter)
- 즉, 가능한 정수 x의 범위는
- longer - shorter + 1 ≤ x ≤ longer + shorter - 1
코드
import java.util.*;
class Solution {
public int solution(int[] sides) {
Arrays.sort(sides); // 배열 오름차순 정렬
int shorter = sides[0];
int longer = sides[1];
// case1: x가 가장 긴 변일 때 -> x < a + b
int case1 = longer + shorter - 1;
// case2: x가 짧거나 중간인 경우 -> x > max - min
int case2 = longer - shorter;
return case1 - case2;
}
}
3. longer - shorter + 1이 아닌 이유
- 왜
case2 = longer - shorter
인가? case2
는 가능한 x의 시작값이 아님- 실제 시작값은
longer - shorter + 1
- 하지만 정수 개수를 계산할 때는 시작값 - 1을 기준으로 계산해야 하므로
case2 = longer - shorter
로 설정하는 것이 정확함
4. 예시
- 입력: sides = [3, 6]
- 가능한 x: 4, 5, 6, 7, 8 → 총 5개
- 계산:
case1 = 3 + 6 - 1 = 8
case2 = 6 - 3 = 3
answer = 8 - 3 = 5 ✅
요약
case2
는 정수 개수 계산용 보조값- 실제 시작값보다 1 작게 설정되어야 하므로
+1
이나-1
필요 없음 - 수식 하나로 정확히 계산 가능
- (shorter + longer - 1) - (longer - shorter)
12. [Java] 옹알이(1): break vs continue
1. 문제 요약
- https://school.programmers.co.kr/learn/courses/30/lessons/120956
- 프로그래머스 옹알이(1)
- 머쓱이의 조카가 발음할 수 있는 단어의 개수 찾기
2. 전체 코드
import java.util.*;
class Solution {
public int solution(String[] babbling) {
String[] canSay = {"aya", "ye", "woo", "ma"};
int count = 0;
for (String word : babbling) {
String temp = word;
boolean isValid = true;
for (String say : canSay) {
// 중복된 발음이 있는지 확인
if (temp.contains(say + say)) {
isValid = false;
break;
}
}
if (!isValid) continue;
// 발음 가능 단어 제거
for (String say : canSay) {
temp = temp.replace(say, " ");
}
// 모두 제거되었는지 확인 (공백만 남은 경우 trim()으로 제거)
if (temp.trim().isEmpty()) {
count++;
}
}
return count;
}
}
3. 같은 단어 중복인지 확인하는 코드
if (temp.contains(say + say)) {
isValid = false;
break;
}
- 이 코드는 같은 단어가 두 번 연속 사용된 문자열을 걸러내기 위해 사용된다.
- 조카는 "ayaaya"처럼 같은 단어를 두 번 연속 발음하지 못하기 때문이다.
- 그래서 "ayaaya", "mama", "woowoo" 같은 경우는 불가능한 발음이다.
- 예르 들어, say = "aya" 라고 할 때 say + say는 "ayaaya"이다.
- temp.contains("ayaaya")는 temp 문자열 안에 "ayaaya"가 들어 있는지를 확인한다.
4. continue
- isValid가 false일 경우 → 이 단어는 조카가 말할 수 없는 단어이므로 아래 로직은 더 이상 실행하지 않고, 다음 단어로 넘어감
- 반복문 안에서 continue는 현재 반복을 중단하고 다음 반복으로 건너뜀을 의미한다.
for (String word : babbling) {
// 어떤 조건으로 유효한 발음인지 검사
if (!isValid) continue;
// 여기 있는 코드는 건너뜀!
}
- !isValid가 true이면, 즉 isValid == false이면
- → 현재 word는 조건에 맞지 않으니까
- → 아래 replace 등의 처리를 하지 않고
- → 바로 다음 word로 넘어감.
5. break
- break의 역할: "반복문 하나만 탈출"해서 즉시 종료
- 이중 루프 중에 안쪽에 있으면 그 안쪽 루프만 빠져나온다.
for (String say : canSay) {
if (temp.contains(say + say)) {
isValid = false;
break; // ✅ 이 for 루프를 빠져나옴!
}
}
여기서 break;는 바로 canSay 배열을 순회하는 for문을 탈출한다.
따라서 그 다음 실행되는 건 → continue; 실행됨 → 이 단어는 skip하고 다음 단어로 넘어가는 로직이 실행된다.
if (!isValid) continue;
6. continue VS break
for each word in babbling:
for each say in canSay:
if word contains say+say:
isValid = false
break ← 여기서 안쪽 for문 탈출
if (!isValid) continue ← 이 줄로 와서 현재 word 스킵
- break :가장 가까운 반복문 전체를 탈출
- continue : 현재 반복을 건너뛰고, 다음 반복을 바로 실행
주제별 정리
Java Coding Test Cheatsheet
0. Frequently Used Libraries
import java.util.*; // Data structures
import java.io.*; // Fast I/O
import java.math.*; // BigInteger, BigDecimal
1. Variable & Array Declaration
String[] arr1 = new String[5];
int[] arr2 = {1, 2, 3};
int N = 3;
int[] arr3 = new int[N];
2. Arrays Utility
Arrays.sort(arr); // Ascending
Arrays.sort(arr, Collections.reverseOrder()); // Descending
Arrays.sort(arr, 0, 4); // Partial sort
Arrays.binarySearch(arr, 2); // Binary search
List<String> list = Arrays.asList(arr1); // Convert to List
int[] tmp = Arrays.copyOfRange(arr, 0, 3); // Slice
3. length / length() / size()
arr.length // Arrays
str.length() // String
list.size() // Collections
4. String Handling
str.split(" ");
str.substring(0, 5);
str.charAt(i);
str.toUpperCase();
str.toLowerCase();
String[] letters = str.split("");
String newStr = str.substring(0,4) + "X" + str.substring(5);
5. HashMap
Map<String, Integer> map = new HashMap<>();
map.put("key", 1);
map.get("key");
map.containsKey("key");
map.getOrDefault("key", 0);
for (String k : map.keySet()) map.get(k);
6. ArrayList
List<String> list = new ArrayList<>();
list.add("a");
list.set(0, "b");
list.remove("b");
list.contains("a");
list.indexOf("a");
7. Queue (LinkedList)
Queue<Integer> q = new LinkedList<>();
q.offer(1);
q.poll();
q.peek();
q.clear();
q.isEmpty();
8. PriorityQueue
PriorityQueue<Integer> pq = new PriorityQueue<>();
PriorityQueue<Integer> maxPq = new PriorityQueue<>(Collections.reverseOrder());
9. Math
Math.max(a, b);
Math.min(a, b);
Math.abs(x);
Math.ceil(x);
Math.floor(x);
Math.round(x);
String.format("%.2f", d); // Round to 2 decimal
Math.pow(a, b);
Math.sqrt(x);
10. HashSet
Set<String> set = new HashSet<>();
set.add("a");
set.remove("a");
set.contains("a");
11. Stack
Stack<Integer> stack = new Stack<>();
stack.push(1);
stack.pop();
stack.peek();
12. Deque (ArrayDeque)
Deque<Integer> dq = new ArrayDeque<>();
dq.addFirst(1);
dq.addLast(2);
dq.pollFirst();
dq.pollLast();
13. TreeSet
TreeSet<Integer> ts = new TreeSet<>();
ts.add(5);
ts.first();
ts.last();
ts.lower(5);
ts.higher(5);
14. TreeMap
TreeMap<String, Integer> tm = new TreeMap<>();
tm.put("apple", 3);
tm.firstKey();
tm.lastKey();
15. Fast I/O
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
int a = Integer.parseInt(st.nextToken());
String[] parts = br.readLine().split(" ");
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
bw.write("Hello\n");
bw.flush();
bw.close();
16. DFS
void dfs(int node) {
visited[node] = true;
for (int next : graph.get(node)) {
if (!visited[next]) dfs(next);
}
}
17. BFS
Queue<Integer> q = new LinkedList<>();
q.offer(start);
visited[start] = true;
while (!q.isEmpty()) {
int cur = q.poll();
for (int next : graph.get(cur)) {
if (!visited[next]) {
q.offer(next);
visited[next] = true;
}
}
}
18. Sorting
list.sort(Comparator.naturalOrder());
list.sort(Comparator.reverseOrder());
people.sort(Comparator.comparingInt(p -> p.age));
19. DP (Fibonacci)
int[] dp = new int[N+1];
dp[0] = 0; dp[1] = 1;
for (int i = 2; i <= N; i++) dp[i] = dp[i-1] + dp[i-2];
20. Union-Find
int find(int x) {
if (x != parent[x]) parent[x] = find(parent[x]);
return parent[x];
}
void union(int a, int b) {
a = find(a); b = find(b);
if (a != b) parent[b] = a;
}
21. Bitmask
int bit = 0;
bit |= (1 << 3);
bit &= ~(1 << 3);
boolean on = (bit & (1 << 3)) != 0;
Integer.bitCount(bit);
22. Greedy
int[] coins = {500, 100, 50, 10};
int count = 0;
for (int coin : coins) {
count += target / coin;
target %= coin;
}
23. Prefix Sum
int[] prefix = new int[N+1];
for (int i = 0; i < N; i++) prefix[i+1] = prefix[i] + arr[i];
24. Sliding Window
int sum = 0;
for (int i = 0; i < k; i++) sum += arr[i];
int max = sum;
for (int i = k; i < N; i++) {
sum += arr[i] - arr[i-k];
max = Math.max(max, sum);
}
25. Sieve of Eratosthenes
boolean[] isPrime = new boolean[N+1];
Arrays.fill(isPrime, true);
for (int i = 2; i*i <= N; i++) {
if (isPrime[i]) {
for (int j = i*i; j <= N; j += i) isPrime[j] = false;
}
}
26. Dijkstra
PriorityQueue<int[]> pq = new PriorityQueue<>(Comparator.comparingInt(a -> a[1]));
int[] dist = new int[N+1];
Arrays.fill(dist, INF);
dist[start] = 0;
pq.offer(new int[]{start, 0});
27. Tree / LCA
void dfs(int node, int par, int d) {
parent[node] = par;
depth[node] = d;
for (int next : tree.get(node)) {
if (next != par) dfs(next, node, d+1);
}
}
28. Coding Test Patterns
- Use BufferedReader & BufferedWriter
- HashMap, HashSet, TreeMap, PriorityQueue
- Always validate inputs
- Use boolean[], Set, or visited[][] for visited states
- Practice common patterns (DFS/BFS, DP, Greedy, Two Pointers)
29. Backtracking (Permutation, Combination)
void permute(List<Integer> list, boolean[] used) {
if (list.size() == N) {
System.out.println(list);
return;
}
for (int i = 0; i < N; i++) {
if (!used[i]) {
used[i] = true;
list.add(arr[i]);
permute(list, used);
list.remove(list.size() - 1);
used[i] = false;
}
}
}
30. Binary Search / Parametric Search
int left = 1, right = maxVal;
while (left <= right) {
int mid = (left + right) / 2;
if (condition(mid)) {
answer = mid;
right = mid - 1;
} else {
left = mid + 1;
}
}
31. Recursion Optimization
- Tail recursion is not optimized in Java.
- Prefer loops for heavy stack recursion.
- Consider memoization or iterative conversion.
32. String Algorithms (KMP)
int[] makeTable(String pattern) {
int[] table = new int[pattern.length()];
int j = 0;
for (int i = 1; i < pattern.length(); i++) {
while (j > 0 && pattern.charAt(i) != pattern.charAt(j)) j = table[j-1];
if (pattern.charAt(i) == pattern.charAt(j)) table[i] = ++j;
}
return table;
}
33. Permutations / Combinations / Bitmask
// Bitmask Combination
for (int mask = 0; mask < (1 << N); mask++) {
for (int i = 0; i < N; i++) {
if ((mask & (1 << i)) != 0) {
// element i is selected
}
}
}
34. BigInteger / BigDecimal
BigInteger a = new BigInteger("12345678901234567890");
BigInteger b = new BigInteger("98765432109876543210");
BigInteger sum = a.add(b);
BigDecimal dec = new BigDecimal("1234.5678");
BigDecimal result = dec.setScale(2, RoundingMode.HALF_UP);
출처 : chatGPT
Java 배열 관련 주요 메서드 정리
1. 배열 비교: Arrays.equals()
Arrays.equals(array1, array2);
설명 | 내용 |
---|---|
비교 대상 | 두 배열의 크기와 요소 값을 모두 비교 |
반환값 |
|
주의 |
|
🔍 예시:
int[] a = {1, 2, 3};
int[] b = {1, 2, 3};
System.out.println(Arrays.equals(a, b)); // ✅ true
System.out.println(a == b); // ❌ false (주소 비교)
2. 배열 복사: Arrays.copyOf()
Arrays.copyOf(원본배열, 복사할길이);
원본 배열의 앞에서부터 지정한 길이만큼 복사하여 새 배열을 생성한다.
반환값은새로운 배열이고, 원본과 별개의 객체이다.
🔍 예시:
int[] original = {1, 2, 3, 4};
int[] copied = Arrays.copyOf(original, 2); // [1, 2]
✨ 확장 사용: 길이 초과 시 0
으로 채움
int[] extended = Arrays.copyOf(original, 6); // [1, 2, 3, 4, 0, 0]
3. 부분 복사: Arrays.copyOfRange()
Arrays.copyOfRange(배열, 시작인덱스, 끝인덱스);
시작 인덱스부터 끝 인덱스 전까지 복사한다.
반환값은 새로운 배열 (구간 복사)이다.
한 마디로 배열의 슬라이싱을 구현한다.
🔍 예시:
int[] sub = Arrays.copyOfRange(original, 1, 3); // [2, 3]
4. 배열 정렬: Arrays.sort()
Arrays.sort(배열);
배열을 오름차순 정렬할 때 쓴다. (in-place 정렬, 반환값 없음 void
)
void타입을 반환하기 때문에 새로운 변수에 담을 수 없다.
정렬 기준(숫자: 오름차순, 문자열: 유니코드 기준)
🔍 예시:
int[] arr = {5, 1, 3};
Arrays.sort(arr); // arr → [1, 3, 5]
5. 배열 출력: Arrays.toString()
Arrays.toString(배열);
배열의 내용을 문자열로 출력한다. (디버깅용)
🔍 예시:
System.out.println(Arrays.toString(arr)); // [1, 3, 5]
6. 배열 채우기: Arrays.fill()
Arrays.fill(배열, 값);
모든 요소를 동일한 값으로 초기화한다.
🔍 예시:
int[] arr = new int[5];
Arrays.fill(arr, 7); // arr → [7, 7, 7, 7, 7]
7. 배열 이진 탐색: Arrays.binarySearch()
Arrays.binarySearch(정렬된배열, 찾을값);
정렬된 배열에서 이진 탐색을 수행한다.
반환값은 인덱스이다. (없으면 음수)
🔍 예시:
int[] sorted = {1, 2, 3, 4};
int idx = Arrays.binarySearch(sorted, 3); // 2
✅ 요약
기능 | 메서드 |
---|---|
배열 내용 비교 |
|
전체 복사 |
|
부분 복사 |
|
배열 정렬 |
|
배열 출력 |
|
배열 초기화 |
|
이진 탐색 |
|
✅ 코테에서 자주 쓰는 조합
상황 | 추천 조합 |
---|---|
두 문자열이 같은 문자로 이루어졌는지 확인 |
|
원본 배열 유지하며 정렬 |
|
배열 슬라이스 |
|
Java 표현식(Expression)
1. 표현식이란?
**표현식(Expression)**이란, 값을 생성하는 코드 조각이다.
변수, 상수, 연산 결과, 객체 생성 등 결과값이 나오는 모든 코드가 표현식이다
2. 예시로 보는 표현식
코드 | 결과 | 설명 |
---|---|---|
|
| 정수 덧셈 |
|
| 문자열 연결 |
|
| 객체 생성 |
| 배열 길이 | 배열의 길이 참조 |
|
| 비교 연산 |
3. 표현식과 문장(statement)의 차이
- 표현식: 값이 생긴다
- 문장: 동작을 수행한다 (명령)
// 표현식
3 + 4 // 값: 7
"Hello" + "World" // 값: "HelloWorld"
x > 5 // 값: true 또는 false
// 문장
int a = 3 + 4; // 변수 선언 (할당)
System.out.println("Hi"); // 메서드 호출
if (x > 5) { ... } // 조건문
4. 표현식이 쓰이는 위치
위치 | 예시 |
---|---|
return 문 |
|
변수 대입 |
|
조건문 조건 |
|
메서드 인자 |
|
5. 요약
- 표현식은 항상 값을 만든다.
- 어떤 곳에든 값이 필요한 위치에서는 표현식을 넣을 수 있다.
- 객체 생성, 연산, 변수 접근 등 다양한 코드 조각이 표현식이다.
Java 이스케이프 시퀀스 (Escape Sequences)
1. 이스케이프 시퀀스란?
이스케이프 시퀀스는 일반 문자처럼 출력할 수 없는 특수한 동작(예: 줄바꿈, 탭 등)을
문자열 안에서 표현할 수 있도록 해주는 백슬래시\
로 시작하는 문자 조합이다.
2. 주요 이스케이프 시퀀스 표
이스케이프 | 의미 | 설명 예시 |
---|---|---|
| 줄바꿈 (newline) | 줄을 바꿔줌 |
| 탭 (tab) | 띄어쓰기 4~8칸 (탭 위치에 따라 다름) |
| 큰따옴표 (") | 문자열 안에서 큰따옴표 출력 가능 |
| 작은따옴표 (') | 작은따옴표를 문자 또는 문자열 안에서 사용 |
| 백슬래시 () | 역슬래시 자체 출력 |
| 캐리지 리턴 (CR) | 커서를 맨 앞으로 이동 (거의 사용 안 함) |
| 백스페이스 | 이전 문자 삭제 (거의 사용 안 함) |
| 폼 피드 (Form feed) | 페이지 넘김 (프린터 제어용, 현재는 거의 사용 안 함) |
3. 사용 예시 코드
public class EscapeExample {
public static void main(String[] args) {
System.out.println("이름\t나이\t성별");
System.out.println("철수\t20\t남");
System.out.println("그는 말했다: \"안녕하세요\"");
System.out.println("C:\\Program Files\\Java");
}
}
출력 결과:
이름 나이 성별
철수 20 남
그는 말했다: "안녕하세요"
C:\Program Files\Java
4. 사용예시
- 문자열 내에서 **큰따옴표(
"
)나 백슬래시(\
)**를 출력해야 할 때 - 출력 형식을 정렬하거나 줄바꿈할 때
- 텍스트를 보기 좋게 정리하거나 가독성을 높이기 위해
- 이스케이프 시퀀스는 문자열(
"..."
)이나 문자('...'
) 안에서만 사용 가능
System.out.println("Hello\nWorld"); // 줄바꿈
char quote = '\''; // 작은따옴표 문자
Java 고급 출력 포맷 (이스케이프 시퀀스, printf, format)
1. 이스케이프 시퀀스란?
**이스케이프 시퀀스(Escape Sequence)**는 문자열 내에서 특수한 동작(줄바꿈, 탭, 따옴표 등)을 표현하기 위한 문자 조합이다.
Java에서는\
백슬래시로 시작한다.
1.1 주요 이스케이프 시퀀스
이스케이프 | 의미 | 예시 출력 |
---|---|---|
| 줄바꿈 | 줄을 바꿈 |
| 탭 | 띄어쓰기(보통 4~8칸) |
| 큰따옴표 출력 |
|
| 작은따옴표 출력 |
|
| 백슬래시 출력 |
|
| 캐리지 리턴 | 커서를 맨 앞으로 이동 (거의 안 씀) |
| 백스페이스 | 글자 하나 삭제 (거의 안 씀) |
| 폼 피드 | 페이지 넘김 (거의 안 씀) |
1.2 사용 예시
System.out.println("이름\t나이");
System.out.println("영희\t25");
System.out.println("그는 말했다: \"안녕하세요\"");
System.out.println("C:\\Program Files\\Java");
출력 결과:
이름 나이
영희 25
그는 말했다: "안녕하세요"
C:\Program Files\Java
2. 문자열 포맷 서식 문자
Java에서 문자열을 정해진 형식으로 출력하고 싶을 때 System.out.printf()
또는 String.format()
을 사용한다.
2.1 기본 형식
System.out.printf("형식", 값1, 값2...);
String result = String.format("형식", 값1, 값2...);
2.2 주요 서식 문자
서식 문자 | 설명 | 예시 출력 |
---|---|---|
| 문자열 |
|
| 정수 (10진수) |
|
| 실수 (소수점 가능) |
|
| 문자 |
|
| 줄바꿈 (OS별 호환) |
|
| 퍼센트(%) 출력 |
|
2.3 자리수/정렬 설정
형식 | 설명 |
---|---|
| 전체 5자리, 오른쪽 정렬 |
| 전체 5자리, 왼쪽 정렬 |
| 앞자리를 0으로 채움 (총 5자리) |
| 소수점 둘째자리까지 출력 |
| 전체 6칸 중 소수점 2자리 포함 |
2.4 사용 예시
String name = "영희";
int age = 25;
double score = 93.756;
System.out.printf("이름: %s, 나이: %d%n", name, age);
System.out.printf("점수: %.2f%n", score);
System.out.printf("진행률: %d%%%n", 100);
출력 결과:
이름: 영희, 나이: 25
점수: 93.76
진행률: 100%
3. 요약 비교표
항목 | 예시 | 설명 |
---|---|---|
이스케이프 문자 |
| 문자열 내부 제어 |
서식 문자 (printf) |
| 형식을 지정하여 출력 |
4. 콘솔 색상 출력 (ANSI 컬러)
Java 자체는 콘솔 색상 기능을 제공하지 않지만, 터미널에서 ANSI 코드를 사용하면 출력 색을 변경할 수 있다.
public class ColorExample {
public static final String RED = "\u001B[31m";
public static final String RESET = "\u001B[0m";
public static void main(String[] args) {
System.out.println(RED + "이 문장은 빨간색입니다." + RESET);
}
}
색상 | ANSI 코드 |
---|---|
빨강 |
|
초록 |
|
노랑 |
|
파랑 |
|
리셋 |
|
5. 날짜 및 시간 포맷 출력
Java에서는 java.time.LocalDateTime
과 DateTimeFormatter
를 사용해 날짜/시간을 원하는 형식으로 출력할 수 있다.
5.1 DateTimeFormatter 기본 사용
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
LocalDateTime now = LocalDateTime.now();
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
System.out.println("현재 시간: " + now.format(fmt));
5.2 주요 포맷 패턴
패턴 | 의미 | 예시 |
---|---|---|
| 연도 |
|
| 월 |
|
| 일 |
|
| 시간 (24시간제) |
|
| 시간 (12시간제) |
|
| 분 |
|
| 초 |
|
| 오전/오후 |
/
|
| 요일 |
|
5.3 사용자 지정 포맷 예시
DateTimeFormatter customFmt = DateTimeFormatter.ofPattern("yyyy년 MM월 dd일 (E) a hh:mm");
String formatted = now.format(customFmt);
System.out.println("형식화된 시간: " + formatted);
6. 로깅용 포맷 출력
Java에는 기본 로그 시스템인 java.util.logging.Logger
가 있다.
6.1 Logger 기본 사용
import java.util.logging.*;
Logger logger = Logger.getLogger("MyLogger");
logger.info("정보 메시지");
logger.warning("경고 메시지");
logger.severe("에러 메시지");
6.2 포맷 문자열과 함께 쓰기
String name = "다인";
int age = 25;
logger.info(String.format("사용자 이름: %s, 나이: %d", name, age));
6.3 로그 레벨 종류
레벨 | 설명 |
---|---|
| 심각한 오류 |
| 경고 |
| 일반 정보 |
| 설정 관련 정보 |
| 디버깅 수준의 정보 |
6.4 출력 형식 커스터마이징
- 로그 출력 위치(콘솔/파일)
- 출력 메시지 포맷 (시간, 로그 레벨, 메시지 내용 등)
logging.properties
파일로 설정 변경 가능
Java 정규식(Regular Expression)
1. 개요
정규표현식(Regular Expression)은 특정 문자열을 패턴에 맞게 검색, 대체, 검증하는 데 사용되는 강력한 도구이다. 자바에서는 java.util.regex
패키지를 통해 정규표현식을 사용할 수 있다.
1.1. 정규표현식 구성 요소
정규표현식은 기본적인 문법과 특수 문자를 조합하여 만들어지며, 자주 사용되는 몇 가지 주요 구성 요소가 있다.
- 문자 클래스:
[ ]
- 예:
[a-z]
는 소문자 알파벳을 의미한다.
- 예:
- 수량자:
{n,m}
,+
,*
,?
- 예:
a{2,4}
는 'a'가 2번 이상 4번 이하 나타나는 패턴을 의미한다.
- 예:
- 메타 문자:
^
,$
,.
,|
,\
- 예:
^abc
는 'abc'로 시작하는 문자열을 의미한다.
- 예:
1.2. 자주 사용되는 패턴
.
: 임의의 한 문자 (개행 문자를 제외)\d
: 숫자 (0-9)\w
: 알파벳 및 숫자, 밑줄 (a-zA-Z0-9_)\s
: 공백 문자 (탭, 스페이스 등)\b
: 단어 경계
2. 자바에서 정규표현식 사용하기
2.1. Pattern
클래스
Pattern
클래스는 정규표현식의 패턴을 컴파일하는 데 사용된다. 이 클래스는 정규표현식 패턴을 매칭하고 검색하는 방법을 제공한다.
2.1.1. Pattern 컴파일
Pattern pattern = Pattern.compile("a*b");
위의 코드에서 "a*b"
는 'a'가 0번 이상 반복되고 뒤에 'b'가 오는 패턴을 의미한다.
2.1.2. Matcher 클래스 사용
Matcher
클래스는 Pattern
객체를 사용하여 문자열과의 매칭을 처리한다.
Pattern pattern = Pattern.compile("a*b");
Matcher matcher = pattern.matcher("aaab");
boolean matches = matcher.matches(); // true
2.2. Pattern
과 Matcher
의 주요 메서드
matches()
: 전체 문자열이 정규표현식에 맞는지 확인find()
: 패턴이 문자열 내에서 부분적으로 일치하는지 확인replaceAll()
: 일치하는 부분을 다른 문자열로 대체
3. 정규표현식 활용 예시
3.1. 이메일 주소 검증
이메일 주소가 유효한지 검사하는 정규표현식 예시
Pattern pattern = Pattern.compile("^[a-zA-Z0-9_+&*-]+(?:\\.[a-zA-Z0-9_+&*-]+)*@(?:[a-zA-Z0-9-]+\\.)+[a-zA-Z]{2,7}$");
Matcher matcher = pattern.matcher("example@domain.com");
boolean isValid = matcher.matches(); // true
3.2. 전화번호 형식 검증
전화번호 형식이 맞는지 검사하는 정규표현식 예시
Pattern pattern = Pattern.compile("^\\d{3}-\\d{3,4}-\\d{4}$");
Matcher matcher = pattern.matcher("010-1234-5678");
boolean isValid = matcher.matches(); // true
Java Arrays.sort() 정리
1. 개요
Arrays.sort()
는 Java에서 배열을 정렬할 때 사용하는 정적 메서드이다.- 이 메서드는 배열을 제자리(in-place) 에서 정렬하며, 반환값이 없다 = (
void
를 반환한다.)
2. 기본 문법
Arrays.sort(배열);
매개변수 | 설명 |
---|---|
배열 | 정렬 대상 (예: |
3. 반환값이 void
인 이유
항목 | 설명 |
---|---|
제자리 정렬 |
|
새로운 배열 반환 안 함 | 정렬된 배열을 새로 만들어 반환하지 않음 |
메모리 절약 | 불필요한 배열 복사 없이 빠른 정렬 수행 가능 |
할당 X |
|
4. 예시
int[] numbers = {3, 1, 4};
Arrays.sort(numbers); // numbers 자체가 정렬됨
System.out.println(Arrays.toString(numbers)); // [1, 3, 4]
5. 흔한 실수 예시 ❌
int[] sorted = Arrays.sort(numbers); // ❌ 컴파일 에러: void를 int[]에 대입할 수 없음
6. 대안: 정렬된 배열을 새로 만들고 싶다면?
int[] original = {3, 1, 2};
int[] copy = Arrays.copyOf(original, original.length);
Arrays.sort(copy);
7. 관련 메서드
메서드 | 설명 |
---|---|
| 기본 오름차순 정렬 |
| 사용자 정의 기준 정렬 (객체 배열) |
| 리스트 정렬 (반환값 없음) |
[C언어] for루프의 실행 순서 (정보처리기사 25년 1회 실기)
10. 다음은 C언어에 대한 문제이다. 아래 코드를 확인하여 알맞는 출력값을 작성하시오.
#include <stdio.h>
char Data[5] = {'B', 'A', 'D', 'E'};
char c;
int main(){
int i, temp, temp2;
c = 'C';
printf("%d\n", Data[3]-Data[1]);
for(i=0;i<5;++i){
if(Data[i]>c)
break;
}
temp = Data[i];
Data[i] = c;
i++;
for(;i<5;++i){
temp2 = Data[i];
Data[i] = temp;
temp = temp2;
}
for(i=0;i<5;i++){
printf("%c", Data[i]);
}
}
정답 :
4
BACDE
1. ASCII 숫자로 변환할 때
- 0 → 48
- A → 65
- a → 97
2. for 루프의 실행 순서
char Data[5] = {'B', 'A', 'D', 'E'}; // 인덱스 0~3만 값 있음
char c = 'C';
for(i=0;i<5;++i){
if(Data[i]>c)
break;
}
break한 시점에서의 i 값은 뭐야?
- 초기화: i = 0
- 조건 검사: i < 5
- 본문 실행: { if (Data[i] > c) break; }
- 증가 연산: ++i
- 다시 조건 검사 → i는 루프 한 번 돌고 나서 다음 반복 전에 증가한다. (현재 루프 안에서 증가하는 것이 아님)
++i는 다음 루프 시작 시에만 영향을 주므로, break 시점의 i는 여전히 2인 것이다.
++i든 i++든 증가 자체는 루프의 끝에서 일어나기 때문에 결과는 똑같다.
[Java] 부모의 생성자와 Override (정보처리기사 25년 1회 실기)
13. 다음은 Java 코드에 대한 문제이다. 아래 코드를 확인하여 알맞는 출력값을 작성하시오.
public class Main {
public static void main(String[] args) {
new Child();
System.out.println(Parent.total);
}
}
class Parent {
static int total = 0;
int v = 1;
public Parent() {
total += (++v);
show();
}
public void show() {
total += total;
}
}
class Child extends Parent {
int v = 10;
public Child() {
v += 2;
total += v++;
show();
}
@Override
public void show() {
total += total * 2;
}
}
- 메모리에 Child 객체가 만들어짐 (모든 필드는 기본값으로 초기화됨)
- super() 호출로 Parent 생성자가 먼저 실행됨
- 이 시점에서 this는 Child 타입
- → this.show() 는 Child.show()를 호출하게 됨
- 그런 다음 Child 생성자가 이어서 실행됨
정답: 54
Parent클래스의 생성자 Parent()에서 show()호출하면 왜 Child 클래스의 show() 가 실행돼?
자식의 인자 없는 생성자를 호출하면 묵시적으로 부모의 인자없는 생성자가 먼저 호출된다. 이 때 부모의 Parent() 생성자에서 show() 메서드를 실행할 때 가까운 부모의 show() 메서드가 실행된다고 오해하기 쉬운데 @Override된 자식의 생성자가 실행된다.
Java에서 생성자 내에서 this.show()
를 호출할 때, 실제로 호출되는 메서드는 "현재 객체 타입 기준의 오버라이드된 메서드"이다. main 영역에서 이미 객체는 Child 타입으로 생성되었기 때문에 super() 안에서 호출되는 show()는 Child의 @Override show()가 실행되는 것이다.
이유는 Java의 "가상 메서드 호출(virtual method call)" 원칙 때문이다. Java에서는 모든 인스턴스 메서드 호출은 '실제 객체 타입' 기준으로 동작한다. 즉, Parent 생성자 안에서 this.show()를 호출하면, this는 Child 객체이므로 Child 클래스에서 @Override한 show()가 실행된다는 것이 이 원칙이다.
이건 단순히 시험 문제라서 그렇지 실제 개발에서 생성자에서 오버라이딩된 메서드를 호출하는 건 권장되지 않는다. 그 이유는자식 필드가 아직 초기화되지 않은 상태에서 메서드가 호출될 수 있기 때문이다. 예를 들면 Child의 v는 아직 10으로 초기화되지 않은 상태일 수 있다.
[Java] 재귀함수 (정보처리기사 25년 1회 실기)
16. 다음은 Java 코드에 대한 문제이다. 아래 코드를 확인하여 알맞는 출력값을 작성하시오.
public class Main {
public static void main(String[] args) {
int[] data = {3, 5, 8, 12, 17};
System.out.println(func(data, 0, data.length - 1));
}
static int func(int[] a, int st, int end) {
if (st >= end) return 0;
int mid = (st + end) / 2;
return a[mid] + Math.max(func(a, st, mid), func(a, mid + 1, end));
}
}
public class Main {
public static void main(String[] args) {
int[] data = {3, 5, 8, 12, 17};
System.out.println(func(data, 0, data.length - 1));
}
static int func(int[] a, int st, int end) {
if (st >= end) return 0;
int mid = (st + end) / 2;
return a[mid] + Math.max(func(a, st, mid), func(a, mid + 1, end));
}
}
정답: 20
재귀트리
func(0,4)
│
├── mid=2 → a[2]=8
│
├── func(0,2)
│ ├── mid=1 → a[1]=5
│ │
│ ├── func(0,1)
│ │ ├── mid=0 → a[0]=3
│ │ ├── func(0,0) = 0
│ │ └── func(1,1) = 0
│ │ └── result: 3 + max(0,0) = 3
│ │
│ ├── func(2,2) = 0
│ └── result: 5 + max(3,0) = 8
│
├── func(3,4)
│ ├── mid=3 → a[3]=12
│ ├── func(3,3) = 0
│ ├── func(4,4) = 0
│ └── result: 12 + max(0,0) = 12
│
└── 최종 result: 8 + max(8,12) = 8 + 12 = 20
문제를 복잡하게도 냈네...
변수도 mid가 다시 start 자리나 end 자리 매개변수로 들어가서 변수명도 헷갈리고 복잡하다
a는 반복적으로 들어가는 배열이므로 2,3번째 인자만으로 정리해서 반드시 재귀트리 그리면서 풀기
[Python] __init__(클래스 생성자) & 이진트리 (정보처리기사 25년 1회 실기)
17. 다음은 파이썬에 대한 문제이다. 아래 코드를 확인하여 알맞는 출력값을 작성하시오.
class Node:
def __init__(self, value):
self.value = value
self.children = []
def tree(li):
nodes = [Node(i) for i in li]
for i in range(1, len(li)):
nodes[(i - 1) // 2].children.append(nodes[i])
return nodes[0]
def calc(node, level=0):
if node is None:
return 0
return (node.value if level % 2 == 1 else 0) + sum(calc(n, level + 1) for n in node.children)
li = [3, 5, 8, 12, 15, 18, 21]
root = tree(li)
print(calc(root))
- __init__: 객체가 만들어질 때 자동으로 실행되는 함수. Node 객체 생성 시 초기값 설정
- children: 자식 노드들을 담는 리스트
- tree(): 리스트를 기반으로 이진 트리를 구성하는 함수
- calc(): 트리를 순회하면서 홀수 레벨 노드의 값을 합산하는 함수
정답: 13
왜 i는 1부터 시작할까?
li = [3, 5, 8, 12, 15, 18, 21]일 때 i = 0이면 nodes[(0 - 1) // 2] = nodes[-1]이 되기 때문에 오류가 나거나 엉뚱한 위치를 참조한다. nodes[0]은 루트 노드(root)로 사용되기 때문에, 부모가 없다. 그래서 연결할 필요도, 연결할 수 도 없다.
18, 21은 레벨 2인데, 왜 부모 인덱스가 2야? 부모 인덱스랑 레벨은 다른 개념이야?
부모 인덱스와 레벨은 완전히 다른 개념이다.
- 부모 인덱스는 배열 내 규칙으로 계산되는 "부모 노드의 위치"
- 레벨은 트리 상에서 "깊이(층수)"
용어 | 뜻 |
---|---|
인덱스(index) | 배열에서 요소의 위치 (0부터 시작) |
레벨(level) | 트리에서 루트로부터 몇 층 아래인지 나타내는 깊이 (0부터 시작) |
index 3
(값 12)의 부모 인덱스는1
→ 값은6
index 4
(값 15)의 부모 인덱스도1
→ 값은6
index 5
(값 18)의 부모 인덱스는2
→ 값은8
index 6
(값 21)의 부모 인덱스도2
→ 값은8
if node is None: 이 무슨뜻이야?
- 파이썬에서 None은 "값이 없음", **"아직 정해지지 않음"**을 나타내는 특별한 키워드이다.
- if node is None: → "이 노드가 존재하지 않는다면" 체크하는 코드입니다.
- 즉 이 코드는 node가 아무것도 없을 경우(=비어 있을 경우) 실행되는 코드이다.
트리에서 node
는 일반적으로 어떤 노드를 가리킨다. 그런데 재귀 호출을 하다 보면, 어떤 노드에는 자식이 없을 수도 있다.
3
/ \
5 8
/ \
12 15
위의 노드 트리 구조에서 12와 15는 자식이 없다. 그러면 node.children은 빈 리스트이고, 그 아래는 없으니까 None인 경우가 생길 수 있다. 그럴 때 이 줄이 작동한다. 만약 node가 없으면 0을 리턴하고 있으면 node.value를 사용하라는 뜻이다.
if node is None:
return 0 # 아무것도 없으니 계산할 것도 없음
1. 클래스 Node
class Node:
def __init__(self, value):
self.value = value # 이 노드가 가지는 값
self.children = [] # 이 노드의 자식 노드들 (리스트)
Node
는 트리의 각 노드를 나타내는 클래스value
: 이 노드가 저장하는 숫자 값children
: 이 노드의 자식 노드들 (최대 2개 — 이진 트리)
[예시]
n = Node(3)
print(n.value) # 3
print(n.children) # []
2. 트리 생성 함수 tree()
def tree(li):
nodes = [Node(i) for i in li]
for i in range(1, len(li)):
nodes[(i - 1) // 2].children.append(nodes[i])
return nodes[0]
li
리스트를 바탕으로Node
객체들을 만든 후,- 이진 트리 구조로 연결
[(i - 1) // 2]
는 부모 인덱스 계산 공식이다.
예시: li = [3, 5, 8, 12, 15, 18, 21]
index : value
0 : 3 → root
1 : 5 → 3의 왼쪽 자식
2 : 8 → 3의 오른쪽 자식
3 : 12 → 5의 왼쪽 자식
4 : 15 → 5의 오른쪽 자식
5 : 18 → 8의 왼쪽 자식
6 : 21 → 8의 오른쪽 자식
3. 계산 함수 calc()
def calc(node, level=0):
if node is None:
return 0
return (node.value if level % 2 == 1 else 0) + sum(calc(n, level + 1) for n in node.children)
- 자식 노드들을 재귀적으로 계산하는 코드이다.
- level이 홀수(1, 3, ...)인 노드의
value
만 더한다. - 아래 이 구조를 시각화한 트리에서 레벨 1인 value만 더하기 때문에 5 + 8 = 13
[Java] 상속: 생성자 호출 순서
생성자의 묵시적 호출, 매개변수 문제
class A {
A() {
System.out.printf("%d", 10); // sys → System, printf → OK
}
}
class B extends A {
B(int a) {
System.out.printf("%d", a); // Sys → System
}
}
class C extends B {
C(int a) {
super(a / 10); // B의 생성자 호출
System.out.printf("%d", a); // sys → System
}
}
public class Test {
public static void main(String[] args) {
A b = new C(1000);
}
}
A b = new C(1000);
- 업캐스팅 (Upcasting)으로 부모 타입(A)의 변수 b에 자식 클래스(C)의 객체를 대입하는 것이다.
new C(1000)
을 호출하면 C의 생성자가 실행됨.- 하지만 그 전에 자바는 항상 부모 생성자부터 차례로 호출한다.
순서별 호출 흐름
new C(1000)
실행 → C 생성자 호출- C 생성자에서
super(a / 10)
→super(1000 / 10)
→super(100)
→ B 생성자 호출 - B 생성자는 또
super()
가 생략되어 있음 → A 생성자 호출
실제 호출 순서
A()
→ 출력:10
B(100)
→ 출력:100
C(1000)
→ 출력:1000
최종 출력 결과
101001000
각 숫자는 System.out.printf("%d", ...)
에 의해 줄바꿈 없이 출력되기 때문에 이렇게 나온다.
A b = new C(1000); 관련
업캐스팅 (Upcasting)
A b = new C(1000);
이 구문은 부모 타입(A)의 변수 b
에 **자식 클래스(C)**의 객체를 대입하는 것이다.
왜 이렇게 쓰는가?
C
는B
를 상속하고,B
는A
를 상속하므로C
는 결국A
의 자식- 그래서
A
타입으로도C
객체를 참조할 수 있다.
A b = new C(1000); // ✅ 업캐스팅
- 업캐스팅은 항상 자동으로 허용된다.
- 다만 이렇게 참조하면
b
로는A
클래스의 멤버(메서드, 필드)만 접근 가능하고,C
에만 있는 것들은 직접 사용할 수 없다.
예시
class A {
void greet() { System.out.println("Hello from A"); }
}
class C extends A {
void sayBye() { System.out.println("Bye from C"); }
}
public class Main {
public static void main(String[] args) {
A obj = new C(); // 업캐스팅
obj.greet(); // ✅ 가능
// obj.sayBye(); // ❌ 컴파일 에러 (A 타입엔 없음)
}
}
[Java] 상속/생성자에서 this()의 의미
문제1
class Super {
Super() {
System.out.print('A');
}
Super(char x) {
System.out.print(x);
}
}
class Sub extends Super {
Sub() {
super(); // 호출되면 'A' 출력
System.out.print('B');
}
Sub(char x) {
this(); // Sub() 생성자 호출 → 'A' + 'B' 출력
System.out.print(x); // 이후 'D' 출력
}
}
class Test {
public static void main(String[] args) {
Super s1 = new Super('C'); // Super(char) 생성자 → 출력: C
Super s2 = new Sub('D'); // Sub(char) → this() → super() → A + B + D
}
}
// 최종출력 CABD
this()
→Sub()
생성자 호출Sub()
안에super()
호출됨 →Super()
생성자 실행 →'A'
출력Sub()
에서'B'
출력Sub()
끝나면 다시 돌아와서'D'
출력
자바에서 this()는 같은 클래스 내부의 다른 생성자를 호출할 때 사용된다.
정확히 말하면 생성자 안에서 this();를 쓰면 그 클래스(Sub)의 '매개변수가 없는 생성자'를 호출한다.
현재 실행 중이던 생성자는 잠시 멈추고, 호출된 생성자의 실행이 먼저 진행된다.
즉, 제어권(control)이 this()가 호출한 다른 생성자로 넘어가는 것이다.
Sub(char x) {
this(); // 여기서 Sub() 생성자를 먼저 호출
System.out.print(x); // 그 다음에 'D' 출력
}
- 이 때 this()는 같은 클래스 안에서만 쓸 수 있다.
- 또한 this()는 항상 현재 클래스의 다른 생성자를 호출한다.
- 매개변수가 맞아야 호출할 수 있다. (아래는 예시)
this(); // → Sub()
this('X'); // → Sub(char x)
this(1, 2); // → Sub(int a, int b)
문제2
class Parent {
int x = 100; // Parent 클래스의 인스턴스 변수 x
Parent() {
this(500); // Parent(int x) 호출
}
Parent(int x) {
this.x = x; // Parent의 x에 값 저장
}
int getX() {
return this.x; // Parent의 x 반환
}
}
class Child extends Parent {
int x = 2000; // Child 클래스의 인스턴스 변수 x
Child() {
this(5000); // Child(int x) 호출
}
Child(int x) {
this.x = x; // Child의 x에 값 저장
}
public static void main(String[] args) {
Child obj = new Child();
System.out.println(obj.getX()); // Parent의 getX() 호출
}
}
// 최종출력 500
Child()
호출- 내부에서
this(5000)
→Child(int x)
호출 - 자바가 자동으로
super()
삽입 →Parent()
호출 Parent()
안에서this(500)
→Parent(int x)
호출Parent
의x = 500
설정- 이후
Child(int x)
실행 →Child
의x = 5000
설정 obj.getX()
→Parent
의x = 500
반환
this(5000)은 같은 클래스인 Child의 생성자 중, int를 매개로 받는 생성자를 호출한다.
Child() {
this(5000); // 같은 클래스의 생성자 Child(int x) 호출
}
여기서 설정되는 건 Child 클래스의 x 필드 (int x = 2000) 이고, this.x = x;에 의해 Child의 x는 5000으로 바뀐다.
📌 그럼 Parent의 생성자는 언제 호출되는가?
항상 서브클래스 생성자가 호출되기 전에 슈퍼클래스 생성자가 먼저 호출되어야 한다.
그래서 Child(int x)가 실행되기 전에, 자동으로 super()가 먼저 붙는다.
즉, 자바가 내부적으로 다음처럼 처리한다.
Child(int x) {
super(); // 자동으로 삽입됨!
this.x = x; // Child의 x = 5000
}
그리고 Parent()는 다음처럼 생겼다.
Parent() {
this(500); // Parent(int x) 호출
}
Parent(int x) {
this.x = x; // Parent의 x = 500
}
- Parent의 x = 500
- Child의 x = 5000
📌 obj.getX()는 왜 500이 나오는가?
getX()는 Parent 클래스에 정의되어 있다.
int getX() {
return this.x;
}
하지만 중요한 건 getX()는 Parent의 x를 반환한다는 뜻이 아니라, "지금 참조 중인 인스턴스의 x 필드 중, Parent 클래스에서 접근 가능한 걸 반환한다"는 의미이다.
→ 여기서 this는 Child 인스턴스를 가리키지만, getX()는 Parent 클래스 안에 있어서 Parent의 x를 가리킨다.
📌 super()는 부모 클래스의 '기본 생성자'를 호출한다.
Child(int x)
는 이렇게 생겼다.
Child(int x) {
this.x = x;
}
그런데 이 생성자의 첫 줄에 명시적으로 super(...)가 없으면, 자바는 자동으로 super();를 삽입한다. 내부적으로는 이렇게 되는 것과 같다.
Child(int x) {
super(); // 자동으로 삽입됨
this.x = x; // Child의 x 필드
}
→ 이 super()
는 Parent()
를 호출한다. 왜냐하면 Parent
클래스에 기본 생성자 Parent()
가 존재하기 때문이다.
📌 Parent()에서의 this(500)
Parent() {
this(500); // 같은 클래스의 다른 생성자 호출
}
이 this(500)은 마찬가지로 같은 Parent()클래스 내에 있는 매개변수가 있는 생성자를 호출한다.
Parent(int x) {
this.x = x;
}
그래서 결국 아래와 같은 흐름으로 코드가 동작한다.
super(); // → Parent()
→ this(500); // → Parent(int x)
→ this.x = 500; // → Parent의 x 필드에 500 저장