no image
[JAVA] 이진 검색(Binary Search)
Do it! 자료구조와 함께 배우는 알고리즘 입문[자바편] 연습문제와 실습문제입니다. 이진 검색 이진 검색은 선형 검색보다 좀 더 빠르게 검색할 수 있다는 장점이 있다. 하지만 이진 검색은 데이터가 키 값으로 이미 정렬되어 있다는 전제 조건이 있어야 한다. 이진 검색은 요소가 오름차순 또는 내림차순으로 정렬된 배열에서 검색하는 알고리즘이다. 시간 복잡도 측면에서 선형 검색은 O(N)이지만 이진 검색은 O(log N)을 가지므로 이진 검색이 훨씬 효율적인 알고리즘이다. 아래와 같은 배열에 오름차순으로 정렬된 데이터에서 39를 찾는 과정을 예로 들면, 먼저 배열 중앙에 위치한 5번째 인덱스부터 검색을 시작한다. 인덱스 5의 값은 31은 39보다 작으므로 검색 대상을 뒤쪽 5개로 좁힐 수 있다. 그런 다음 검..
2023.01.27
no image
[JAVA] 선형 검색(Linear Search), 보초법(Sentinel Method)
Do it! 자료구조와 함께 배우는 알고리즘 입문[자바편] 연습문제와 실습문제입니다. 선형 검색(순차 검색) 선형 검색은 직선 모양으로 늘어선 배열에서 원하는 키 값을 갖는 요소를 만날 때까지 맨 앞부터 순서대로 요소를 검색하는 것을 말한다. 선형 검색에서 검색의 종료 조건은 아래의 2개와 같다. 검색할 값을 발견하지 못하고 배열의 끝을 지나간 경우 검색할 값과 같은 요소를 발견한 경우 첫 번째 조건이 성립하면 검색 실패, 두 번째 조건이 성립하면 검색 성공이다. 배열의 요솟수가 n개이면 조건 1, 2를 판단하는 횟수는 평균 n/2회이다. public class Main{ static int seqSearch(int[] arr, int key) { for(int i = 0; i < arr.length; ..
2023.01.26
no image
[JAVA] 날짜 계산기 알고리즘
Do it! 자료구조와 함께 배우는 알고리즘 입문[자바편] 연습문제와 실습문제입니다. 2023.01.01을 기준으로 100일 전은2022년 9월 23일(기준날 미포함)이고, 100일 후는2023년 4월 11일이다. 이러한 알고리즘을 자바로 구현하면 아래와 같다. 클래스 선언부 static class YMD { int year; int month; int day; YMD(int y, int m, int d) //생성자 { this.year = y; this.month = m; this.day = d; } int[][] arr = { {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, //평년 arr[0][] {31, 29, 31, 30, 31, 30, 31, 31, 3..
2023.01.26
no image
[JAVA] 클래스 배열
Do it! 자료구조와 함께 배우는 알고리즘 입문[자바편] 연습문제와 실습문제입니다. 클래스 배열이란 클래스로 부터 만들어진 객체로 이루어진 배열을 뜻한다. 클래스 자체가 자료형이되어서 데이터를 더 쉽게 다룰 수 있다. 아래는 이름, 키, 시력을 저장하는 클래스의 객체로 이루어진 배열을 활용하여 평균 키와 시력 분포를 출력하는 예제 코드이다. public class Main{ static class PhyscData { //이름, 키, 시력을 저장하는 클래스 String name; int height; double vision; PhyscData(String name, int height, double vision) { //생성자 this.name = name; this.height = height; t..
2023.01.25
no image
[JAVA] 한 해의 경과 일 수 / 남은 일 수를 계산하는 알고리즘
Do it! 자료구조와 함께 배우는 알고리즘 입문[자바편] 연습문제와 실습문제입니다. 4월 15일을 예로 들면 그 해의 경과 일수를 구하면 아래와 같다. 1월의 일 수 + 2월의 일 수 + 3월의 일 수 + 15 m월 d일의 그 해 경과 일수는 아래와 같다. 1, 2, ..., m-1월의 일 수의 합 + d 그런데 2월의 일 수는 평년은 28일, 윤년은 29일로 해에 따라 달라진다. 윤년, 평년 지구가 태양 둘레를 한 바퀴 도는 일수는 정확히 365일이 아니다. 이를 조정하기 위해 4로 나누어 떨어지는 해를 윤년으로 하여 1년을 366일로 한다. 하지만 그래도 정확하기 않으므로 아래와 같은 규칙을 적용한다. -해당 연도를 4로 나누어 떨어지면 우선 윤년으로 하고 -윤년 중에서 100으로 나누어 떨어지면 ..
2023.01.25
no image
금융용어정리 - 주식발행초과금
본 게시글은 유튜브 : 경제 TV 너무경 : 너무 쉬운 경제 윤성종 님의 유튜브 영상을 참고하였습니다. 개인적으로 정리하는 글임을 알립니다. 금융용어정리 - 주식발행초과금 주식발행초과금 액면가와 발행가의 차이 금액을 뜻한다. 액면가가 500원이고, 발행가를 2000원으로 책정했다면 차이 금액인 1500원이 주식발행 초과금이다. 이때, 주식을 1만 주 발행했다면 주식발행초과금은 1500원 X 1만 주 = 1500만 원이다. 주식발행초과금은 영업을 통하여 얻은 이익잉여금이 아니라 주주들과의 거래를 통해 얻은 자본잉여금이다. 주식발행초과금의 사전적 정의 주식의 액면금액을 초과하여 발행한 경우 발행금액과 액면금액의 차액이다. 이는 감자차익, 자기주식처분이익 등의 기타 자본잉여금과 함께 자본잉여금의 구성항목이며,..
2023.01.24
no image
[JAVA] n이하의 소수를 구하는 알고리즘
Do it! 자료구조와 함께 배우는 알고리즘 입문[자바편] 연습문제와 실습문제입니다. 소수 소수는 자신과 1 이외의 정수로 나누어떨어지지 않는 정수이다. 예를 들어 소수 13은 2, 3, ..., 12 가운데 어떤 정수로도 나누어 떨어지지 않는다 그러므로 어떤 정수 n에 대하여 아래의 조건을 만족하면 소수임을 알 수 있다. "소수 n은 2부터 n-1까지의 어떤 정수로도 나누어 떨어지지 않는다." 만약 나누어 떨어지는 정수가 하나 이상 존재하면 그 수는 합성수이다. n 이하의 소수를 나열하는 알고리즘 (시간복잡도 높음, 공간복잡도 낮음) static void PrimeNumber(int n) { for(int i = 2; i
2023.01.24
no image
[JAVA] Date, Calendar, LocalDateTime 클래스 (날짜와 시간 클래스)
본 게시글은 혼자 공부하는 자바 (저자 : 신용권)의 책과 유튜브 영상을 참고하였고, 개인적으로 정리하는 글임을 알립니다. 클래스 설명 Date 날짜 정보를 전달하기 위해 사용 Calendar 다양한 시간대별로 날짜와 시간을 얻을 때 사용 LocalDateTime 날짜와 시간을 조작할 때 사용 Date 클래스 Date 클래스는 날짜를 표현하는 클래스이다. Date는 객체 간에 날짜 정보를 주고받을 때 매개변수나 리턴 타입으로 주로 사용된다. Date 객체의 toString() 메소드는 영문으로 된 날짜를 리턴하기 때문에 원하는 날짜 형식의 문자열을 얻고 싶다면, java.text 패키지의 SimpleDateFromat 클래스와 함께 사용하는 것이 좋다. SimpleDateFromat 생성자의 매개값은 형..
2023.01.24

Do it! 자료구조와 함께 배우는 알고리즘 입문[자바편] 연습문제와 실습문제입니다.


이진 검색

이진 검색은 선형 검색보다 좀 더 빠르게 검색할 수 있다는 장점이 있다.

하지만 이진 검색은 데이터가 키 값으로 이미 정렬되어 있다는 전제 조건이 있어야 한다.

이진 검색은 요소가 오름차순 또는 내림차순으로 정렬된 배열에서 검색하는 알고리즘이다.

시간 복잡도 측면에서 선형 검색은 O(N)이지만 이진 검색은 O(log N)을 가지므로 이진 검색이 훨씬 효율적인 알고리즘이다.

 

아래와 같은 배열에 오름차순으로 정렬된 데이터에서 39를 찾는 과정을 예로 들면, 먼저 배열 중앙에 위치한 5번째 인덱스부터 검색을 시작한다. 

인덱스 5의 값은 31은 39보다 작으므로 검색 대상을 뒤쪽 5개로 좁힐 수 있다.

그런 다음 검색 범위의 중앙에 위치한 인덱스 8이 원하는 값인지 확인한다.

인덱스 8의 값은 68이므로 검색 대상을 앞쪽 2개로 좁힐 수 있다.

이 때 두 요소의 중앙 요소는 39나 38중 아무거나 선택해도 상관 없지만 앞쪽의 값 39를 선택하여 원하는 값인지 확인한다.(6과 7의 중앙값은 (6+7) / 2 = 6이다 (정수의 나눗셈은 나머지를 버리기 때문))

39는 원하는 키의 값과 일치하므로 검색 성공이다.

static int binSearch(int[] arr, int key) //while문으로 구현
{
    int startptr = 0;
    int endptr = arr.length -1;
    int midptr = (startptr + endptr) / 2;
    while(startptr <= endptr) //시작 지점이 끝 지점보다 크면 루프 종료
    {
        midptr = (startptr + endptr) / 2;
        if(arr[midptr] == key) return midptr; //발견 함
        else //발견 못함
        {
            if(key > arr[midptr]) startptr = midptr + 1; //키가 배열 뒷 쪽에 있음
            else endptr = midptr -1; //키가 배열 앞 쪽에 있음
        }
    }
    return -1; //찾는 값이 없음
}
static int binSearch(int[] arr, int key) //for문으로 구현
{
    for(int startptr = 0, endptr = arr.length-1, midptr = (startptr + endptr) / 2; startptr <= endptr; midptr = (startptr + endptr) / 2)
    {
        if(arr[midptr] == key) return midptr; //발견 함
        else //발견 못함
        {
            if(key > arr[midptr]) startptr = midptr + 1; //키가 배열 뒷 쪽에 있음
            else endptr = midptr -1; //키가 배열 앞 쪽에 있음
        }
    }
    return -1; //찾는 값이 없음
 }

 

실행 예제

public static void main(String[] args) {
    int arr[] = {5,7,15,28,29,31,39,58,68,70,95};

    System.out.println("key의 인덱스는 " + binSearch(arr, 39) + " 입니다.(-1이 나왔다면 찾는값이 없는 것입니다.)");
}
/*
key의 인덱스는 6 입니다.(-1이 나왔다면 찾는값이 없는 것입니다.)
*/

 

자세한 검색 과정 출력 예제

 

위 사진처럼 이진 검색의 과정을 자세히 출력하는 프로그램을 작성

(단, 각 행의 맨 왼쪽에 현재 검색하고 있는 요소의 인덱스를 출력하고, 검색범위의 맨 앞 요소 위에 <, 맨 끝 요소 위에 >, 현재 검색하고 있는 중앙 요소 위에 *를 출력)

static int binSearch(int[] arr, int key) //for문으로 구현
{
    System.out.print("  |");
    for(int i = 0; i < 2; ++i)
    {
        for(int j = 0; j < arr.length; ++j) 
        {
            if(i == 0) System.out.printf("%3d", j);
            else
            {
                if(j == 0) System.out.print("--+");
                else System.out.print("----");
            }
        }

        System.out.println();
    }

    for(int startptr = 0, endptr = arr.length-1, midptr = (startptr + endptr) / 2; startptr <= endptr; midptr = (startptr + endptr) / 2)
    {
        if(arr[midptr] == key)
        {
            for(int i = 0; i < 2; ++i)
            {
                if(i == 0)
                {
                    System.out.print("  |");
                    for(int j = 0; j < arr.length; ++j)
                    {
                        if(j == midptr)
                        {
                            System.out.printf("%3c\n", '*');
                            break;
                        }
                        else System.out.printf("%3c", ' ');
                    }
                }
                if(i == 1)
                {
                    System.out.print(" " + midptr + "|");
                    for(int j = 0; j < arr.length; ++j) System.out.printf("%3d", arr[j]);
                    System.out.println();
                }
            }
            return midptr;
        }	
        else
        {
            if(key > arr[midptr])
            {
                for(int i = 0; i < 3; ++i)
                {
                    if(i == 0)
                    {
                        System.out.print("  |");
                        for(int j = 0; j < arr.length; ++j)
                        {
                            if(j == startptr) System.out.printf("%3c", '<');
                            else if(j == endptr)
                            {
                                System.out.printf("%3c\n", '>');
                                break;
                            }
                            else if(j == midptr) System.out.printf("%3c", '*');
                            else System.out.printf("%3c", ' ');
                        }
                    }
                    else if(i == 1)
                    {
                        System.out.print(" " + midptr + "|");
                        for(int j = 0; j < arr.length; ++j) System.out.printf("%3d", arr[j]);
                        System.out.println();
                    }
                    else System.out.println("  |");
                }
                startptr = midptr + 1;
            }
            else 
            {
                for(int i = 0; i < 3; ++i)
                {
                    if(i == 0)
                    {
                        System.out.print("  |");
                        for(int j = 0; j < arr.length; ++j)
                        {
                            if(j == startptr) System.out.printf("%3c", '<');
                            else if(j == endptr)
                            {
                                System.out.printf("%3c\n", '>');
                                break;
                            }
                            else if(j == midptr) System.out.printf("%3c", '*');
                            else System.out.printf("%3c", ' ');
                        }
                    }
                    else if(i == 1)
                    {
                        System.out.print(" " + midptr + "|");
                        for(int j = 0; j < arr.length; ++j) System.out.printf("%3d", arr[j]);
                        System.out.println();
                    }
                    else System.out.println("  |");
                }
                endptr = midptr -1;
            }

        }
    }
    return -1; //찾는 값이 없음
 }

 

실행 예제

public static void main(String[] args) {
    int arr[] = {5,7,15,28,29,31,39,58,68,70,95};
    System.out.println("key의 인덱스는 " + binSearch(arr, 58) + " 입니다.(-1이 나왔다면 찾는값이 없는 것입니다.)");
}
/*
  |  0  1  2  3  4  5  6  7  8  9 10
--+----------------------------------------
  |  <              *              >
 5|  5  7 15 28 29 31 39 58 68 70 95
  |
  |                    <     *     >
 8|  5  7 15 28 29 31 39 58 68 70 95
  |
  |                    <  >
 6|  5  7 15 28 29 31 39 58 68 70 95
  |
  |                       *
 7|  5  7 15 28 29 31 39 58 68 70 95
key의 인덱스는 7 입니다.(-1이 나왔다면 찾는값이 없는 것입니다.)
*/

 

맨 앞의 원소의 인덱스를 리턴하는 이진 검색

위에 작성된 이진검색 알고리즘은 검색할 값과 같은 값을 갖는 요소가 하나 이상일 경우 그 요소 중에서 맨 앞의 요소를 찾지 못한다.

예를 들어 {1,3,5,7,7,7,7,8,8,9,9} 이렇게 배열이 이뤄져 있고, 7을 찾고 싶다면 인덱스 5밖에 리턴을 하지 못하는데, 같은 원소의 맨 앞의 인덱스를 리턴하는 이진검색 알고리즘은 아래와 같다.

static int binSearchX(int[] arr, int key) //for문으로 구현
{
    for(int startptr = 0, endptr = arr.length-1, midptr = (startptr + endptr) / 2; startptr <= endptr; midptr = (startptr + endptr) / 2)
    {
        if(arr[midptr] == key) //키를 찾음
        {
            for(int i = midptr; i > 0; --i) //맨 앞의 키를 찾는 과정
            {
                if(i == 1 && arr[i-1] == key) return i-1;
                if(arr[i-1] == key) continue;
                    return i;
            }
        }
        else //발견 못함
        {
            if(key > arr[midptr]) startptr = midptr + 1; //키가 배열 뒷 쪽에 있음
            else endptr = midptr -1; //키가 배열 앞 쪽에 있음
        }
    }
    return -1; //찾는 값이 없음
 }

 

실행 예제

public static void main(String[] args) {
    int []arr = {1,3,5,7,7,7,7,8,8,9,9};
    System.out.println("key는 " + binSearchX(arr, 7) + "에 있습니다.");
}
/*
key는 3에 있습니다.
*/

 

Arrays.binarySearch API

자바는 배열에서 이진 검색을 하는 메소드를 표준 라이브러리로 제공한다.

이진 검색 표준 라이브러리의 메소드로는 java.util.Arrays 클래스의 binarySearch 메소드가 있다.

binarySearch 메소드는 다음과 같은 장점이 있다.

  • 이진 검색 메소드를 직접 코딩할 필요가 없다.
  • 모든 자료형 배열에서 검색을 할 수 있다.

binarySearch 메소드는 위와 같이 오버로딩되어 있다.

 

검색에 성공한 경우

key와 일치하는 요소의 인덱스를 리턴한다. 일치하는 요소가 여러 개 있다면 무작위의 인덱스를 리턴한다.

맨 앞의 인덱스나 어떤 특정한 인덱스를 리턴하는 것이 아님

 

검색에 실패한 경우

리턴 값은 삽입 포인트를 x라고 할 때 -x -1을 리턴한다.

위 배열에서 key가 4이면 삽입 포인트는 3이다. 즉, -4를 리턴한다.

삽입 포인트
삽입 포인트는 검색하기 위해 지정한 key보다 큰 요소 중 첫 번째 요소의 인덱스이다. 만약 배열의 모든 요소가 key보다 작다면 배열의 길이를 삽입 포인트로 정한다.

 

 

실행 예제

import java.util.Arrays;
public class Main2{
	public static void main(String[] args) {
		int []arr = {5,7,15,28,29,31,39,58,68,70};
		System.out.println("key는 " + Arrays.binarySearch(arr, 39) + "에 있습니다.");
		System.out.println("key는 " + Arrays.binarySearch(arr, 30) + "에 있습니다.");
		System.out.println("key는 " + Arrays.binarySearch(arr, 99) + "에 있습니다.");
	}
}
/*
key는 6에 있습니다.
key는 -6에 있습니다.
key는 -11에 있습니다.
*/

Do it! 자료구조와 함께 배우는 알고리즘 입문[자바편] 연습문제와 실습문제입니다.


선형 검색(순차 검색)

선형 검색은 직선 모양으로 늘어선 배열에서 원하는 키 값을 갖는 요소를 만날 때까지 맨 앞부터 순서대로 요소를 검색하는 것을 말한다.

 

선형 검색에서 검색의 종료 조건은 아래의 2개와 같다.

  1. 검색할 값을 발견하지 못하고 배열의 끝을 지나간 경우
  2. 검색할 값과 같은 요소를 발견한 경우

 

첫 번째 조건이 성립하면 검색 실패, 두 번째 조건이 성립하면 검색 성공이다.

배열의 요솟수가 n개이면 조건 1, 2를 판단하는 횟수는 평균 n/2회이다.

public class Main{
	static int seqSearch(int[] arr, int key)
	{
		for(int i = 0; i < arr.length; ++i) {
			if(arr[i] == key) return i;
		}
		return -1;
	}
	
	public static void main(String[] args) {
		int[] arr = {22,8,55,32,120,55,70};
		System.out.println("key의 인덱스는 " + seqSearch(arr, 55) + " 입니다.(-1이 나왔다면 찾는값이 없는 것입니다.)");
	}
}
/*
key의 인덱스는 2 입니다.(-1이 나왔다면 찾는값이 없는 것입니다.)
*/

값이 key인 요소가 여러 개 존재할 경우 반환값은 검색 과정에서 처음 발견한 요소의 인덱스가 된다.(55를 검색하면 arr[2]와 arr[5]의 두 곳에 존재하지만 가장 먼저 찾음 arr[2]의 인덱스 값인 2를 반환한다.)

 

key와 일치하는 모든 요소의 수 리턴

배열의 원소 개수가 n인 배열에서 key와 일치하는 모든 원소의 인덱스를 배열 idx의 맨 앞부터 순서대로 저장하고, 일치한 원소의 수를 리턴하는 메소드를 작성

( 예를 들어 배열의 원소가 {1,9,2,9,4,6,7,9}이고 key가 9면 배열 idx에 {1,3,7}을 저장하고 3을 리턴)

static int searchIdx(int[] arr, int key) //while문으로 구현
{
    int []idx = new int[arr.length];
    int count = 0;
    for(int i = 0; i < arr.length; ++i)
    {
        if(key == arr[i]) idx[count++] = i;
    }
    return count;
}

 

실행 예제

public static void main(String[] args) {
    Scanner sc = new Scanner(System.in);
    System.out.print("입력할 데이터의 개수 입력 : ");
    int num = sc.nextInt();
    int []arr = new int[num];
    for(int i = 0; i < arr.length; ++i)
    {
        System.out.print("데이터 입력 : ");
        arr[i] = sc.nextInt();
    }
    System.out.print("찾을 데이터 입력 : ");
    num = sc.nextInt();
    System.out.println("key는 " + searchIdx(arr, num) + "개 있습니다.");
}
/*
입력할 데이터의 개수 입력 : 11
데이터 입력 : 54654
데이터 입력 : 231768
데이터 입력 : 15
데이터 입력 : 5
데이터 입력 : 9
데이터 입력 : 7
데이터 입력 : 7
데이터 입력 : 7
데이터 입력 : 6
데이터 입력 : 6
데이터 입력 : 897654
찾을 데이터 입력 : 7
key는 3개 있습니다.
*/

 

보초법(Sentinel Method)

선형 검색은 반복할 때마다 

  1. 검색할 값을 발견하지 못하고 배열의 끝을 지나간 경우
  2. 검색할 값과 같은 요소를 발견한 경우

위 두 종료 조건을 모두 판단한다. 단순한 판단이라고 생각할 수 있지만 티끌모아 태산이란 말이 있듯이 종료 조건을 검사하는 비용은 결코 무시할 수 없다.

이 비용을 절반으로 줄이는 방법이 보초법이다.

위 그림에서 배열 인덱스 0~6은 초기에 준비해 놓은 데이터이고, 검색하기 전에 검색하고자 하는 키 값을 맨 인덱스 7에 저장한다. 이때 저장하는 값을 보초라고 한다.

원하는 값이 원래의 데이터에 존재하지 않아도 보초인 인덱스 7까지 검색하면 종료 조건 2가 성립한다 이렇게 하면 원하는 키 값을 찾지 못했을 때를 판단하는 종료 조건인 1이 없어도 된다.

보초는 반복문에서 종료 판단 횟수를 2회에서 1회로 줄이는 역할을 한다.

public class Main{
	static int seqSearch(int[] arr, int key)
	{
		arr[arr.length-1] = key; //보초 추가
		int i;
		for(i = 0; i < arr.length; ++i) {
			if(arr[i] == key) break;
		}
		return i == arr.length-1 ? -1 : i;
	}
	
	public static void main(String[] args) {
		int arr[] = new int[8];
		arr[0] = 22; arr[1] = 8; arr[2] = 55; arr[3] = 32; arr[4] = 120; arr[5] = 55; arr[6] = 70;
		
		System.out.println("key의 인덱스는 " + seqSearch(arr, 120) + " 입니다.(-1이 나왔다면 찾는값이 없는 것입니다.)");
	}
}
/*
key의 인덱스는 4 입니다.(-1이 나왔다면 찾는값이 없는 것입니다.)
*/

선형 검색에선 return문이 두 개였는데, 보초법에선 return문이 한 개다.  이유는 종료 조건 1이 필요하지 않기 때문이다. 

따라서 반복 종료에 대한 판단 횟수는 절반으로 줄어든다.

반복문에 의한 반복이 완료되면 찾은 값이 배열의 원래 데이터인지 아니면 보초인지 판단해야 한다. 변수 i의 값이 배열의 길이를 -1을 한 값과 같다면 찾은 값이 보초이므로 -1을 리턴한다.

Do it! 자료구조와 함께 배우는 알고리즘 입문[자바편] 연습문제와 실습문제입니다.


2023.01.01을 기준으로 100일 전은2022년 9월 23일(기준날 미포함)이고,

100일 후는2023년 4월 11일이다.

이러한 알고리즘을 자바로 구현하면 아래와 같다.

클래스 선언부

static class YMD { 
		int year;
		int month;
		int day;

		YMD(int y, int m, int d) //생성자
		{
			this.year = y;
			this.month = m;
			this.day = d;
		}
		
		int[][] arr = {
		        {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, //평년 arr[0][]
		        {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} //윤년 arr[1][]
		};
	
		static int isLeap(int year) { //윤년이면 1을 리턴, 평년이면 0을 리턴
			return (year % 4 == 0 && year % 100 != 0 || year % 400 == 0) ? 1 : 0;
		}
		
		YMD after(int n) //n일 후 날짜를 리턴하는 메소드
		{
			YMD temp = new YMD(this.year, this.month, this.day);
			if (n < 0)
				return (before(-n));

			temp.day += n;

			while (temp.day > arr[isLeap(temp.year)][temp.month - 1]) {
				temp.day -= arr[isLeap(temp.year)][temp.month - 1];
				if (++temp.month > 12) {
					temp.year++;
					temp.month = 1;
				}
			}
			return temp;
		}
		
		YMD before(int n) //n일 전 날짜를 리턴하는 메소드
		{
			YMD temp = new YMD(this.year, this.month, this.day);
			if (n < 0)
				return (after(-n));

			temp.day -= n;

			while (temp.day < 1) {
				if (--temp.month < 1) {
					temp.year--;
					temp.month = 12;
				}
				temp.day += arr[isLeap(temp.year)][temp.month - 1];
			}
			return temp;
		}
	}

 

실행예제

public static void main(String[] args) {
    Scanner sc = new Scanner(System.in);

    System.out.print("날짜를 입력하세요. 입력 예 : 2023 1 18\n");
    int y, m, d;
    y = sc.nextInt();
    m = sc.nextInt();
    d = sc.nextInt();
    YMD date = new YMD(y, m, d);

    System.out.print("몇 일 앞/뒤의 날짜를 구할까요?:");
    int n = sc.nextInt();

    YMD d1 = date.after(n);
    System.out.printf("%d일 뒤의 날짜는 %d년 %d월 %d일입니다.\n", n, d1.year, d1.month, d1.day);

    YMD d2 = date.before(n);
    System.out.printf("%d일 앞의 날짜는 %d년 %d월 %d일입니다.\n", n, d2.year, d2.month, d2.day);
}
/*
날짜를 입력하세요. 입력 예 : 2023 1 18
2023 1 18
몇 일 앞/뒤의 날짜를 구할까요?:1000
1000일 뒤의 날짜는 2025년 10월 14일입니다.
1000일 앞의 날짜는 2020년 4월 23일입니다.
*/

네이버 날짜 계산기는 기준일을 1일로 치지만 위 알고리즘은 기준일을 0일로 삼는다.

네이버는 내일을 2일째 되는날이라고 하지만, 위 알고리즘은 1일째 되는날이라고 한다.

설명
arr의 행에 오는 isLeap()메소드는 temp의 연도가 윤년이면 1, 평년이면 0을 리턴한다.
열에오는 temp의 month에 -1을 한 이유는 배열의 인덱스는 0부터 시작하기 때문이다.
2023.01.18에 1000일을 한 temp객체는 초기에 2023.01.1018로 되어있다.
while루프는 temp의 day가 달의 일수보다 같거나 작을 때 까지 반복한다.
day는 루프를 돌면서 계속 달의 일수만큼 감소된다.
달의 일수가 감소할 때, 달은 계속 증가하게 되고, 달이 12를 넘어가면 연도를 +1해준다.

2024년은 윤년이므로 2월이 29일 인 것을 볼 수 있다.

 

 

Do it! 자료구조와 함께 배우는 알고리즘 입문[자바편] 연습문제와 실습문제입니다.


클래스 배열이란 클래스로 부터 만들어진 객체로 이루어진 배열을 뜻한다.

클래스 자체가 자료형이되어서 데이터를 더 쉽게 다룰 수 있다.

아래는 이름, 키, 시력을 저장하는 클래스의 객체로 이루어진 배열을 활용하여

평균 키와 시력 분포를 출력하는 예제 코드이다.

public class Main{
	static class PhyscData { //이름, 키, 시력을 저장하는 클래스
		String name;
		int    height;
		double vision;

		PhyscData(String name, int height, double vision) { //생성자
			this.name 	= name;
			this.height = height;
			this.vision = vision;
		}
	}

	static double aveHeight(PhyscData[] arr) { //평균 키를 리턴하는 메소드
		double sum = 0;

		for (int i = 0; i < arr.length; i++)
			sum += arr[i].height;

		return sum / arr.length;
	}

	static void distVision(PhyscData[] arr) { //시력 분포를 출력하는 메소드
		int[] distribution = new int[5];
		for (int i = 0; i < arr.length; i++)
		{
			if(0.0 <= arr[i].vision && arr[i].vision < 0.5) ++distribution[0];
			else if(arr[i].vision < 1.0) ++distribution[1];
			else if(arr[i].vision < 1.5) ++distribution[2];
			else if(arr[i].vision < 2.0) ++distribution[3];
			else if(2.0 <= arr[i].vision) ++distribution[4];
		}
		
			System.out.println("0.5 미만 " + distribution[0] + "명");
			System.out.println("1.0 미만 " + distribution[1] + "명");
			System.out.println("1.5 미만 " + distribution[2] + "명");
			System.out.println("2.0 미만 " + distribution[3] + "명");
			System.out.println("2.0 이상 " + distribution[4] + "명");
	}
	
	public static void main(String[] args) {
			PhyscData[] arr = { //PhyscData타입 객체를 저장하는 배열
			new PhyscData("박현규", 162, 0.3),
			new PhyscData("함진아", 173, 0.7),
			new PhyscData("최윤미", 175, 2.0),
			new PhyscData("홍연의", 171, 1.5),
			new PhyscData("이수진", 168, 0.4),
			new PhyscData("김영준", 174, 1.2),
			new PhyscData("박용규", 169, 0.8),
		};
		System.out.println("이름\t키\t시력");
		for(int i = 0; i < arr.length; ++i) System.out.println(arr[i].name + "\t" + arr[i].height + "\t" + arr[i].vision);
		System.out.printf("평균 키 : %.2fCM\n", aveHeight(arr));

		distVision(arr);

	}
}
/*
이름	키	시력
박현규	162	0.3
함진아	173	0.7
최윤미	175	2.0
홍연의	171	1.5
이수진	168	0.4
김영준	174	1.2
박용규	169	0.8
평균 키 : 170.29CM
0.5 미만 2명
1.0 미만 2명
1.5 미만 1명
2.0 미만 1명
2.0 이상 1명
*/

시력 분포를 위 사진처럼 기호 문자 *를 사람 수만큼 반복하여 출력

static void distVision(PhyscData[] arr) { //시력 분포를 출력하는 메소드
    int[] distribution = new int[5];
    for (int i = 0; i < arr.length; i++)
    {
        if(0.0 <= arr[i].vision && arr[i].vision < 0.5) ++distribution[0];
        else if(arr[i].vision < 1.0) ++distribution[1];
        else if(arr[i].vision < 1.5) ++distribution[2];
        else if(arr[i].vision < 2.0) ++distribution[3];
        else if(2.0 <= arr[i].vision) ++distribution[4];
    }

    for(int i = 0; i < distribution.length; ++i)
    {
        if(i == 0) System.out.print("0.5 미만 : ");
        else if(i == 1) System.out.print("1.0 미만 : ");
        else if(i == 2) System.out.print("1.5 미만 : ");
        else if(i == 3) System.out.print("2.0 미만 : ");
        else if(i == 4) System.out.print("2.0 이상 : ");
        for(int j = 0; j < distribution[i]; ++j)
        {
            System.out.print("*");
        }
        System.out.println();
    }
}

Do it! 자료구조와 함께 배우는 알고리즘 입문[자바편] 연습문제와 실습문제입니다.


4월 15일을 예로 들면 그 해의 경과 일수를 구하면 아래와 같다.

  • 1월의 일 수 + 2월의 일 수 + 3월의 일 수 + 15

m월 d일의 그 해 경과 일수는 아래와 같다.

  • 1, 2, ..., m-1월의 일 수의 합 + d

그런데 2월의 일 수는 평년은 28일, 윤년은 29일로 해에 따라 달라진다.

윤년, 평년
지구가 태양 둘레를 한 바퀴 도는 일수는 정확히 365일이 아니다.
이를 조정하기 위해 4로 나누어 떨어지는 해를 윤년으로 하여 1년을 366일로 한다.
하지만 그래도 정확하기 않으므로 아래와 같은 규칙을 적용한다.
-해당 연도를 4로 나누어 떨어지면 우선 윤년으로 하고
-윤년 중에서 100으로 나누어 떨어지면 다시 평년으로 한다.
-하지만 평년 중에서 400으로 나누어 떨어지면 다시 윤년으로 한다.

윤년인지 평년인지 구분하는 조건문은 아래와 같다.
if(y % 4 == 0 && y % 100 != 0 || y % 400 == 0) //이 조건식이 참이면 윤년이다.

else //위 조건식이 참이 아니면 평년이다.​

 

int[][] arr = {
        {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, //평년 arr[0][]
        {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} //윤년 arr[1][]
};

평년이면 위 코드처럼 2차원 배열을 선언하고 arr[0][]을 이용하고, 윤년이면 arr[1][]을 이용하면 된다.

 

한 해의 경과일 수를 계산하는 알고리즘

static int dayOfYear(int y, int m, int d)
{
    int[][] arr = {
            {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
            {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
    };
    int days = d;
    if(y % 4 == 0 && y % 100 != 0 || y % 400 == 0) for(int i = 1; i < m; ++i) days += arr[1][i-1]; //윤년일 때
    else for(int i = 1; i < m; ++i) days += arr[0][i-1]; //평년일 때

    return days;
}

 

실행 예제

public class Main{
	static int dayOfYear(int y, int m, int d)
	{
		int[][] arr = {
				{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
				{31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
		};
		int days = d;
		if(y % 4 == 0 && y % 100 != 0 || y % 400 == 0) for(int i = 1; i < m; ++i) days += arr[1][i-1]; //윤년일 때
		else for(int i = 1; i < m; ++i) days += arr[0][i-1]; //평년일 때
		
		return days;
	}
	public static void main(String[] args) {
		System.out.println(dayOfYear(2022, 3, 1)); //평년
		System.out.println(dayOfYear(2023, 3, 1)); //평년
		System.out.println(dayOfYear(2024, 3, 1)); //윤년
	}
}
/*
60
60
61
*/

 

while문으로 작성

while 문으로 변수 i와 days 없이 구현

static int dayOfYear(int y, int m, int d)
{
    int[][] arr = {
            {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
            {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
    };

    if(y % 4 == 0 && y % 100 != 0 || y % 400 == 0) while(--m != 0) d += arr[1][m-1];
    else while(--m != 0) d += arr[0][m-1];

    return d;
}

 

 

한 해의 남은 일 수를 계산하는 알고리즘

경과한 일 수를 구할 수 있다면 남은 일 수를 계산하는 것은 간단하다.

  • 윤년이면 366일에서 경과일 수를 빼준다.
  • 평년이면 365일에서 경과일 수를 빼준다.
static int leftDayOfYear(int y, int m, int d)
{
    int[][] arr = {
            {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
            {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
    };
    int days = d;
    if(y % 4 == 0 && y % 100 != 0 || y % 400 == 0) //윤년일 때
    {
        for(int i = 1; i < m; ++i) days += arr[1][i-1];
        return 366 - days;
    }
    else //평년일 때
    {
        for(int i = 1; i < m; ++i) days += arr[0][i-1];
        return 365 - days;
    }
}

 

실행 예제

public class Main{
	static int leftDayOfYear(int y, int m, int d)
	{
	    int[][] arr = {
	            {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
	            {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
	    };
	    int days = d;
	    if(y % 4 == 0 && y % 100 != 0 || y % 400 == 0) //윤년일 때
	    {
	    	for(int i = 1; i < m; ++i) days += arr[1][i-1];
	    	return 366 - days;
	    }
	    else //평년일 때
	    {
	    	for(int i = 1; i < m; ++i) days += arr[0][i-1];
	    	return 365 - days;
	    }
	}
	public static void main(String[] args) {
		System.out.println(leftDayOfYear(2022, 2, 28)); //평년
		System.out.println(leftDayOfYear(2023, 2, 28)); //평년
		System.out.println(leftDayOfYear(2024, 2, 28)); //윤년
	}
}
/*
306
306
307
*/

본 게시글은 유튜브 : 경제 TV 너무경 : 너무 쉬운 경제 윤성종 님의 유튜브 영상을 참고하였습니다. 개인적으로 정리하는  글임을 알립니다.


금융용어정리 - 주식발행초과금

주식발행초과금

액면가와 발행가의 차이 금액을 뜻한다.

액면가가 500원이고, 발행가를 2000원으로 책정했다면 차이 금액인 1500원이 주식발행 초과금이다.

이때, 주식을 1만 주 발행했다면 주식발행초과금은 1500원 X 1만 주 = 1500만 원이다.

주식발행초과금은 영업을 통하여 얻은 이익잉여금이 아니라 주주들과의 거래를 통해 얻은 자본잉여금이다.

주식발행초과금의 사전적 정의
주식의 액면금액을 초과하여 발행한 경우 발행금액과 액면금액의 차액이다. 이는 감자차익, 자기주식처분이익 등의 기타 자본잉여금과 함께 자본잉여금의 구성항목이며, 이는 반드시 회사 내에 적립하여야 하고, 주주에게 배당할 수 없으며, 자본의 결손보전에 충당하는 경우 외에는 이를 처분하지 못한다.
[네이버 지식백과] 주식발행초과금 [paid-in capital in excess of par value] ((주)조세통람, 2019. 10. 10., (주)조세통람)

 

주식발행초과금, 주식할인발행차금
예를 들어 액면가가 1000원이고, 발행가 1500원으로 주식을 발행했다면 500원이 주식발행초과금이다.
600원으로 주식을 발행했다면 400원이 주식할인발행차금이다.

 

액면가, 발행가
액면가는 주식시장에 상장되기 전에 주식회사가 최초로 주식을 발행했을 때의 한 주당 가격이다.
발행가는 증자를 하여 새로운 주식을 발행할 때 1주의 가격을 말한다.

 

 

Do it! 자료구조와 함께 배우는 알고리즘 입문[자바편] 연습문제와 실습문제입니다.


소수
소수는 자신과 1 이외의 정수로 나누어떨어지지 않는 정수이다.
예를 들어 소수 13은 2, 3, ..., 12 가운데 어떤 정수로도 나누어 떨어지지 않는다
그러므로 어떤 정수 n에 대하여 아래의 조건을 만족하면 소수임을 알 수 있다.
"소수 n은 2부터 n-1까지의 어떤 정수로도 나누어 떨어지지 않는다."
만약 나누어 떨어지는 정수가 하나 이상 존재하면 그 수는 합성수이다.

 

n 이하의 소수를 나열하는 알고리즘 (시간복잡도 높음, 공간복잡도 낮음)

static void PrimeNumber(int n)
{
    for(int i = 2; i <= n; ++i)
    {
        for(int j = 2; j <= i; ++j)
        {
            if(i == j) System.out.println(i);
            if(i % j == 0) break;
        }
    }
}
설명
바깥쪽 루프는 2부터 n까지 반복을 하고, 안쪽 루프는 2부터 i까지 반복한다.
i와 j가 같다면(안쪽 루프와 바깥쪽 루프를 모두 돌았다면) 소수인 것이므로 출력한다.
i를 j로 나눈 나머지가 0이라면 소수가 아니므로 루프(안쪽)를 탈출한다.

 

실행 예제

public class Main{
	static void PrimeNumber(int n)
	{
		for(int i = 2; i <= n; ++i)
		{
			for(int j = 2; j <= i; ++j)
			{
				if(i == j) System.out.println(i);
				if(i % j == 0) break;
			}
		}
	}
	public static void main(String[] args) {
		PrimeNumber(55);
	}
}
/*
2
3
5
7
11
13
17
19
23
29
31
37
41
43
47
53
*/

 

 

알고리즘 개선1 (시간복잡도 중간, 공간복잡도 높음)

처음 제시한 알고리즘은 불필요한 연산을 하고 있다.

어떤 수가 2 또는 3으로 나누어 떨어지지 않으면 2X2인 4 또는 2X3인 6으로도 나누어 떨어지지 않는다.

따라서 어떤 수가 소수인지 여부는 아래의 조건을 만족하는지 조사하면 된다.

"소수 n은 2부터 n-1까지 어떤 소수로도 나누어 떨어지지 않는다."

예를 들어 7이 소수인지는 7보다 작은 소수(2, 3, 5)로 나눗셈을 하면 충분하다. 이렇게 하면 성능을 향상할 수 있다.

static void PrimeNumber(int n)
{
    int []arr = new int[n]; //소수를 저장하기 위한 배열을 동적할당
    int count = 0; //소수의 개수를 저장
    arr[count++] = 2; //arr[0]에 2를 저장하고 count를 +1 증가시킴
    for(int i = 3; i <= n; i += 2) //짝수는 검사하지 않음
    {
        int j;
        for(j = 1; j < count; ++j) if(i % arr[j] == 0) break;
        if(j == count) arr[count++] = i;
    }
    for(int i = 0; i < count; ++i) System.out.println(arr[i]);
}
설명
2는 소수이므로 arr[0]에는 2를 넣고
바깥 루프는 3부터 n까지 반복을 진행하고, 안쪽 루프는 1부터 count -1까지 반복한다.
안쪽 루프에서 i를 arr[j]로 나누고 나머지가 0이면 루프를 탈출한다.
안쪽 루프를 다 돌고 j와 count가 같다면 arr[count]에 i를 대입한다.


위 알고리즘은 시간복잡도는 감소하지만, 공간복잡도는 증가한다. (연산횟수는 감소하지만, 메모리 사용량은 증가한다)

 

실행예제

public class Main{
	static void PrimeNumber(int n)
	{
		int []arr = new int[n]; //소수를 저장하기 위한 배열을 동적할당
		int count = 0; //소수의 개수를 저장
		arr[count++] = 2; //arr[0]에 2를 저장하고 count를 +1 증가시킴
		for(int i = 3; i <= n; i += 2) //짝수는 검사하지 않음
		{
			int j;
			for(j = 1; j < count; ++j) if(i % arr[j] == 0) break;
			if(j == count) arr[count++] = i;
		}
		for(int i = 0; i < count; ++i) System.out.println(arr[i]);
	}
	public static void main(String[] args) {
		PrimeNumber(55);
	}
}
/*
2
3
5
7
11
13
17
19
23
29
31
37
41
43
47
53
*/

 

알고리즘 개선2 (시간복잡도 낮음, 공간복잡도 높음)

100의 약수를 구하는 과정은 아래와 같다.

  • 2 X 50
  • 4 X 25
  • 5 X 20
  • 10 X 10
  • 20 X 5
  • 25 X 4
  • 50 X 2

10 X 10을 기준으로 위아래는 똑같다.

기준을 잡고 기준을 포함해서 위나 아래만 하나를 골라서 소수로 나눗셈을 시도하고, 그 과정에서 한 번도 나누어 떨어지지 않으면 소수라고 판단한다는 것이다.

(예를 들어 100이 5로 나누어떨어지지 않는다면 20으로도 나눠어 떨어지지 않는다.)

따라서 어떤 수가 소수인지 여부는 아래의 조건을 만족하는지 조사하면 된다.

"소수 n은 n의 제곱근 이하의 어떤 소수로도 나누어 떨어지지 않는다."

static void PrimeNumber(int k)
{
    int count = 0;
    int[] arr = new int[k];

    arr[count++] = 2;
    arr[count++] = 3;

    for (int i = 5 ; i <= k; i += 2) {
        boolean flag = false;
        for (int j = 1; arr[j] * arr[j] <= i; j++) {
            if (i % arr[j] == 0) flag = true; break;
        }
        if (!flag) arr[count++] = i;
    }
    for (int i = 0; i < count; i++)System.out.println(arr[i]);
}

 

실행예제

public class Main{
	static void PrimeNumber(int k)
	{
		int count = 0;
		int[] arr = new int[k];

		arr[count++] = 2;
		arr[count++] = 3;

		for (int i = 5 ; i <= k; i += 2) {
			boolean flag = false;
			for (int j = 1; arr[j] * arr[j] <= i; j++) {
				if (i % arr[j] == 0) flag = true; break;
			}
			if (!flag) arr[count++] = i;
		}
		for (int i = 0; i < count; i++)System.out.println(arr[i]);
	}
	public static void main(String[] args) {
		PrimeNumber(1000);
	}
}
/*
2
3
5
7
11
13
17
19
23
29
31
37
41
43
47
53
*/

 

첫번째 알고리즘의 연산 횟수는 78022

두 번째 알고리즘의 연산 횟수는 14622

세 번째 알고리즘의 연산 횟수는 3774

본 게시글은 혼자 공부하는 자바 (저자 : 신용권)의 책과 유튜브 영상을 참고하였고, 개인적으로 정리하는 글임을 알립니다.


클래스 설명
Date 날짜 정보를 전달하기 위해 사용
Calendar 다양한 시간대별로 날짜와 시간을 얻을 때 사용
LocalDateTime 날짜와 시간을 조작할 때 사용

 

Date 클래스

Date 클래스는 날짜를 표현하는 클래스이다. Date는 객체 간에 날짜 정보를 주고받을 때 매개변수나 리턴 타입으로 주로 사용된다.

Date 객체의 toString() 메소드는 영문으로 된 날짜를 리턴하기 때문에 원하는 날짜 형식의 문자열을 얻고 싶다면, java.text 패키지의 SimpleDateFromat 클래스와 함께 사용하는 것이 좋다.

SimpleDateFromat 생성자의 매개값은 형식 문자열이다. yyyy는 4자리 연도, MM은 2자리 월, dd는 2자리 일을 뜻한다.

import java.text.SimpleDateFormat;
import java.util.Date;
public class Main{
	public static void main(String[] args) {
		Date now = new Date();
		String strNow1 = now.toString();		
		System.out.println(strNow1);
		
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy년 MM월 dd일 hh시 mm분 ss초");
		String strNow2 = sdf.format(now);
		System.out.println(strNow2);
	}
}
/*
Sun Jan 15 21:53:19 KST 2023
2023년 01월 15일 09시 53분 19초
*/

 

 

Calendar 클래스

Calendar 클래스는 달력을 표현한 클래스이다.

Calendar 클래스는 추상 클래스이므로 new 연산자를 사용해서 인스턴스를 생성할 수 없다.

Calendar 클래스의 정적 메소드인 getInstance() 메소드를 이용하면 현재 운영체제에 설정되어 있는 시간대를 기준으로 한 Calendar 하위 객체를 얻을 수 있다.

import java.util.Calendar;

Calendar now = Calendar.getInstance();

int year = now.get(Calendar.YEAR); //연도를 리턴
int month = now.get(Calendar.MONTH)+1; //월을 리턴(0~11값이 리턴됨 그래서 +1을 해준 것임)
int day = now.get(Calendar.DAY_OF_MONTH); //일을 리턴
int week = now.get(Calendar.DAY_OF_WEEK);//요일을 리턴
int amPm = now.get(Calendar.AM_PM); //오전, 오후를 리턴
int hour = now.get(Calendar.HOUR); //시를 리턴
int minute = now.get(Calendar.MINUTE); //분을 리턴
int second = now.get(Calendar.SECOND); //초를 리턴

get() 메소드를 호출할 때 사용한 매개값은 모두 Calendar 클래스에 선언되어 있는 상수들이다.

 

예제 코드

import java.util.Calendar;
public class Main{
	public static void main(String[] args) {
Calendar now = Calendar.getInstance();
		
		int year    = now.get(Calendar.YEAR);                
		int month  = now.get(Calendar.MONTH) + 1;          
		int day    = now.get(Calendar.DAY_OF_MONTH);     
		
		int week    = now.get(Calendar.DAY_OF_WEEK);        
		String strWeek = null;
		switch(week) {
			case Calendar.MONDAY:
				strWeek = "월";
				break;
			case Calendar.TUESDAY:
				strWeek = "화";
				break;
			case Calendar.WEDNESDAY:
				strWeek = "수";
				break;
			case Calendar.THURSDAY:
				strWeek = "목";
				break;
			case Calendar.FRIDAY:
				strWeek = "금";
				break;
			case Calendar.SATURDAY:
				strWeek = "토";
				break;
			default:
				strWeek = "일";
		}
		
		int amPm  = now.get(Calendar.AM_PM);   
		String strAmPm = null;
		if(amPm == Calendar.AM) {
			strAmPm = "오전";
		} else {
			strAmPm = "오후";
		}
		
		int hour    = now.get(Calendar.HOUR);                 
		int minute  = now.get(Calendar.MINUTE);             
		int second  = now.get(Calendar.SECOND);              

		System.out.print(year + "년 ");
		System.out.print(month + "월 ");
		System.out.println(day + "일 ");
		System.out.print(strWeek + "요일 ");
		System.out.println(strAmPm + " ");
		System.out.print(hour + "시 ");
		System.out.print(minute + "분 ");
		System.out.println(second + "초 ");
	}
}
/*
2023년 1월 15일 
일요일 오후 
10시 13분 52초 
*/

다른 시간대 얻기

Caleandar 클래스의 오버로딩된 다른 getInstance() 메소드를 이용하면 미국/로스엔젤레스와 같은 다른 시간대의 Calendar를 얻을 수 있다.

알고 싶은 시간대의 TimeZone 객체를 얻어, getInstance() 메소드의 매개값으로 넘겨주면 된다.

아래의 코드는 TimeZone 객체의 ID를 얻는 방법이다.

여기서 원하는 TimeZone 객체의 ID를 얻고 getInstance() 메소드의 매개값으로 넘겨주면 된다.

import java.util.TimeZone;
public class PrintTimeZoneID {
	public static void main(String[] args) {
		String[] availableIDs = TimeZone.getAvailableIDs();
		for(String id : availableIDs) {
			System.out.println(id);
		}
	}
}
/*
Africa/Abidjan
Africa/Accra
Africa/Addis_Ababa
Africa/Algiers
Africa/Asmara
Africa/Asmera
Africa/Bamako
Africa/Bangui
Africa/Banjul
Africa/Bissau
Africa/Blantyre
Africa/Brazzaville
Africa/Bujumbura
Africa/Cairo
Africa/Casablanca
Africa/Ceuta
Africa/Conakry
Africa/Dakar
Africa/Dar_es_Salaam
Africa/Djibouti
Africa/Douala
....
....
엄청 많다.
*/

 

아래의 코드는 로스엔젤레스의 TimeZone 객체의 ID를 getInstance() 메소드의 매개값으로 넘겨주는 예제이다.

import java.util.Calendar;
import java.util.TimeZone;

public class LosAngelesExample {
	public static void main(String[] args) {
		TimeZone timeZone = TimeZone.getTimeZone("America/Los_Angeles");
		Calendar now = Calendar.getInstance( timeZone );

		int amPm = now.get(Calendar.AM_PM);
		String strAmPm = null;
		if(amPm == Calendar.AM) {
			strAmPm = "오전";
		} else {
			strAmPm = "오후";
		}
		int hour = now.get(Calendar.HOUR);
		int minute = now.get(Calendar.MINUTE);
		int second = now.get(Calendar.SECOND);

		System.out.print(strAmPm + " ");
		System.out.print(hour + "시 ");
		System.out.print(minute + "분 ");
		System.out.println(second + "초 ");
	}
}
/*
오전 8시 20분 44초
*/

 

 

날짜와 시간 비교

LocalDateTime 클래스는 날짜와 시간을 비교할 수 있는 아래의 메소드를 제공한다.

리턴 타입 메소드(매개변수) 설명
boolean isAfter(other) 이후 날짜인지?
isBefore(other) 이전 날짜인지?
isEqual(other) 동일 날짜인지?
long until(other, unit) 주어진 단위(unit) 차이를 리턴

 

아래의 예제는 현재 글쓰는 날의 다음날인 23.07.20 0시 0분 0초부터 24.12.31 0시 0분 0초까지를 계산하는 코드이다.

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;

public class DateTimeCompareExample {
	public static void main(String[] args) {
		DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy.MM.dd a HH:mm:ss");

		LocalDateTime startDateTime = LocalDateTime.of(2023, 7, 20, 0, 0, 0);
		System.out.println("시작일: " + startDateTime.format(dtf));
		
		LocalDateTime endDateTime = LocalDateTime.of(2024, 12, 31, 0, 0, 0);
		System.out.println("종료일: " + endDateTime.format(dtf));
 
		if(startDateTime.isBefore(endDateTime)) {
			System.out.println("진행 중입니다.");
		} else if(startDateTime.isEqual(endDateTime)) {
			System.out.println("종료합니다.");
		} else if(startDateTime.isAfter(endDateTime)) {
			System.out.println("종료했습니다.");
		}
		
		long remainYear = startDateTime.until(endDateTime, ChronoUnit.YEARS);
		long remainMonth = startDateTime.until(endDateTime, ChronoUnit.MONTHS);
		long remainDay = startDateTime.until(endDateTime, ChronoUnit.DAYS);
		long remainHour = startDateTime.until(endDateTime, ChronoUnit.HOURS);
		long remainMinute = startDateTime.until(endDateTime, ChronoUnit.MINUTES);
		long remainSecond = startDateTime.until(endDateTime, ChronoUnit.SECONDS);
		System.out.println("남은 해: " + remainYear);
		System.out.println("남은 월: " + remainMonth);
		System.out.println("남은 일: " + remainDay);
		System.out.println("남은 시간: " + remainHour);
		System.out.println("남은 분: " + remainMinute);
		System.out.println("남은 초: " + remainSecond);
	}
}
/*
시작일: 2023.07.20 오전 00:00:00
종료일: 2024.12.31 오전 00:00:00
진행 중입니다.
남은 해: 1
남은 월: 17
남은 일: 530
남은 시간: 12720
남은 분: 763200
남은 초: 45792000
*/