[C++ To Java #1] HackerRank - Java Introduction 파트 정리

임승현·2022년 2월 20일
0
post-thumbnail

ℹ️ 개요

Velog 시작 후, 두 달 만에 작성하는 첫 프로그래밍 포스트.
사실 이 글에서 밝혔다시피, 안드로이드 스튜디오 관련 내용을 처음 다루려 했다. 그러나 (너무나 얕은 핑계이지만,) 내게 Java라는 프로그래밍 언어에 대한 Base가 거의 존재하지 않다 보니, 기획 단계에서부터 한계를 느꼈다. (Kotlin도 마찬가지.)

그래서 기초부터 다시 다지려 한다. 작년 가을학기 때 '객체지향프로그래밍'이라는 과목에서 배운 언어인 C++에 대한 기본적인 지식을 바탕으로 (복습도 할 겸,) Java를 하나하나씩 톺아보려 한다. 문제풀이는 HackerRank를 이용해 보려 한다. HackerRank에서 풀어본 문제를 바탕으로 두 언어 사이에 어떤 차이점이 있는지, 그리고 C++과 Java의 문법에 대해서도 간단히 정리해볼 예정이다. (주로 Java가 될 것.)

포스트의 주제는 HackerRank의 Prepare -> Java -> SubDomains 기준으로, Introduction, Strings, BigNumber, Data Structures, Object-Oriented Programming, Exception Handling 으로 이루어질 것이다. Strings나 OOP, Data Structure, Exception Handling의 경우, 큰 주제 안에서 여러 개의 글로 나누어 작성될 수도 있음을 미리 밝힌다. (분량이 많기 때문에.)

이번 포스트에서는 제목 그대로, HackerRank의 Prepare -> Java -> Introduction 의 문제들을 해결하는 데 필요한 Java의 개념들을 다루고, 일부 문제의 답안을 C++로 옮겨 보며 두 언어가 같은 기능을 어떤 방식으로 다르게 구현하는지 살펴볼 예정이다.

☕️ 내용 정리

(Java에 대한 내용만을 다룰 경우, 소주제 앞에 ☕️를 붙였습니다. )

🤚 본격적으로 문제를 해결하기 전에

C++은 절차지향 언어인 C에 객체지향적인 요소를 덧붙인 언어이다. 절차지향으로 작성하든 객체지향으로 작성하든, 설계에 있어 아무런 제약을 두지 않는다. C++은 개발자의 자유도가 가장 높은 언어 중 하나이면서, 그만큼의 많은 책임이 따르는 언어라고 말할 수 있겠다. (Great Power Comes With Great Responsibility, C/C++이 그보다 high level 언어와의 가장 큰 차이를 보이는 부분 중 하나는 가비지 콜렉터의 유무이다. 다르게 말해서, 명시적인 메모리 관리의 필요성이다. )
그에 비해 Java는 (더 정확히는, Java라는 언어로 쓰여진 코드를 컴파일해 실행하려면,) Class의 작성을 강제한다. 예를 들어 HelloWorld.java 라는 파일에서 자신이 작성한 코드의 실행 결과를 본다는 것은, HelloWorld 라는 class 안에 작성된 코드의 컴파일 결과를 본다는 것이다. 동일한 결과를 출력하는 서로 다른 언어로 작성된 2개의 코드를 보면서 더 자세히 살펴보자.

Java 8 - HelloWorld.java

public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello World!");
    }
}

C++ 17

#include <iostream>
using namespace std;
int main()
{
    cout << "Hello World!" << endl;
    return 0;
}

전처리 및 소스코드 구조

먼저, 두 소스파일의 main 함수 위에 쓰인 코드들부터 살펴보자.

C++의 경우 첫 줄에서, 표준 입출력 라이브러리인 iostream include 한다. 두 번째 줄에서, std 라는 namespace(이름공간)을 이용한다는 것을 컴파일러에게 알린다. C++ 프로그램의 표준 구성 요소인 클래스, 함수, 변수 등은 std 라는 이름공간에 저장되어 있는데, 두번째 줄의 명령문은 그러한 요소들을 간단하게 사용하기 위한 방법이다. (실무에서는 이러한 방법을 사용하는 것을 추천하지 않는다고 한다. 예를 들어, 사용자가 임의로 cout()이라는 함수를 만들어 버리면, 컴파일러에서 std 클래스에 존재하는 cout과 구분하지 못하게 된다. 그래서 std::cout 과 같이, 함수 앞에 std 접두어를 사용하기를 권장한다. 자세한 내용은 위 링크 참조. 이 포스트에서는 편의상 대부분의 소스코드에 위 명령문을 포함할 예정이다.)

Java의 경우, 소스코드의 파일명과 같은 이름의 public classmain 함수를 감싸고 있다. Java 소스파일 내에 public class는 단 하나만 존재하여야 하며, 그 이름은 소스파일의 이름과 동일해야 한다. public class가 없다면, 소스파일 내에 존재하는 단일 클래스 혹은 복수 클래스 중 하나는 파일명과 동일해야 한다.
(어? 그런데 Java는 C++과 달리, 표준 입출력 라이브러리를 받아오라는 명령문이 안 보이네... 이에 관련된 내용은 뒤의 '📁 기본 입출력 방식'에서 다루겠다. )

Main 함수

main 함수를 살펴보니, 형태가 매우 다르다. 일단 간단한 것부터 살펴보자.

C++의 경우, int main(){} 의 형태이다. main 앞에 붙은 int 는 함수의 리턴 타입을 의미한다. (정수의 유한 자료형 중 하나, 범위는 2312^{-31} ~ 23112^{31}-1) main 함수의 리턴 타입과 함께 return 0;라는 명령문을 넣은 건, main 함수가 성공적으로 종료되었을 때, 정수 0을 리턴해야 하기 때문이다. 하지만 굳이 입력하지 않아도, 컴파일은 정상적으로 수행된다. (알아서 0을 리턴하며 프로그램이 종료된다.)

Java의 경우, (난잡해 보이는) public static void main(String[] args){}의 형태이다. 하나씩 뜯어보자면,

구문의미
1 public함수의 접근 제어자 부분이다. 프로그램의 모든 곳에서 접근이 가능하다는 뜻.
2static함수를 static 영역의 메모리에 할당한다는 뜻.
3void함수의 리턴 값이 존재하지 않는다는 뜻.
4(String[] args)문자열을 나타내는 String 자료형의 배열 args를, main 함수의 매개변수로 사용한다는 뜻.

Java에서 (1) public, private, protected와 같은 접근 제어자(Access Modifier)는 함수, 클래스, 변수의 사용 권한을 설정한다. (이 부분은 Object-Oriented Programming 포스트에서 더 자세히 다룰 예정이다.) main 함수는 .java 파일 실행에 기본이 되는 함수이기 때문에, 이에 접근하지 못하는 클래스가 있어선 안 된다. 그러므로, main 함수의 접근 제어자로는 public 만이 가능하다.

(2) static 제어자는, 이 제어자가 붙은 변수나 함수가 메모리에 할당되는 횟수가, 프로그램 실행 시, 단 한 번임을 의미한다. 그리고 가비지 콜렉터에 의해 정리되지 않음을 의미한다. main 함수는 (가비지 콜렉터에 의해 메모리에서 사라지지 않고,) 항상 메모리에 상주하도록 해야 하기 때문에, 함수 선언 시 static 제어자를 꼭 포함시켜야 한다.

(3) void는 함수가 종료될 때 리턴값이 없다는 뜻이다. 즉, main 함수의 리턴값은 존재하지 않아야 한다는 것이다. Java 컴파일러에서 그렇게 요구하며, (글쓴이가 사용하고 있는 IDE인) IntelliJ IDEA에서는 위와 같은 main 함수의 형식을 따르지 않을 시, 컴파일 시도 자체가 되지 않는다.

(4) (String[] args)main 함수가 인자로 받는 매개변수의 형식이 String Type의 배열임을 알려 준다. (args는 그냥 배열의 이름일 뿐이라 바꾸어도 컴파일에 전혀 지장을 주지 않는다.) 터미널에서 .java 파일을 컴파일할 때, 파일이름.java 뒤에 사용자가 문자열 파라미터를 입력할 수 있다. 이렇게 공백으로 구분되어 입력된 문자열을 컴파일러는 args[0], args[1], args[2], ...에 차례로 저장한다.

"Hello World!" 출력하기

다양한 출력 방식에 대해서는 '📁 기본 입출력 방식' 에서 다루어 보고, 여기서는 (위에서 진행했던 대로) 코드를 뜯어보는 데만 집중해 보자.

cout << "Hello World!" << endl;

C++에서는 iostream라는 표준 입출력 클래스에 포함된 cout이라는 객체를 통해 출력 작업을 진행한다. cout 객체란, C++에서 미리 정의된 출력 스트림(OutputStream)을 나타내는 것이다. 삽입 연산자 <<는 오른쪽에 위치한 출력 데이터를 출력 스트림에 삽입하고, 스트림을 통해 출력 장치로 전달된다. endl (std::endl)은 std라는 클래스에 존재하는 endl이라는 함수를 호출한다는 의미로, 이 함수는 "\n"처럼 개행 기능을 가질 뿐 아니라, 출력의 끝을 알리며 버퍼를 정리한다.

물론,

#include "stdio.h"
int main()
{
	printf("Hello World!\n");
    return 0;
}

와 같은 방식(C언어, printf()라는 함수를 사용)으로 작성해도 전혀 상관없다.

System.out.println("Hello World!");

Java에서는 java.lang 패키지에 포함된 System 클래스의 멤버변수인 out(Static Variable)을 이용해 PrintStream 객체를 받아 출력 작업을 진행한다. println()PrintStream의 메소드들 중 하나로서, 전달된 데이터(소괄호 안)를, 끝에 개행문자까지 포함시켜 출력시킨다.

📁🔜 기본 입출력 방식

Java 8 - Main.java

import java.util.Scanner;
public class Main {
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        String s = scan.next();
        System.out.println("String: " + s);
    }
}

C++ 17

#include <iostream>
#include <string>
using namespace std;
int main()
{
    string s;
    cin >> s;
    cout << "String: " << s << endl;
    return 0;
}

🆗🔁🔜 조건문과 반복문

🔁 Java Loops II - HackerRank

문제 소개(원문 🦶번역 및 의역에 주의!)
이 문제(프로그램)는 아래와 같은 수열을 출력하기 위해
(사용자로부터의) 정수형 변수 a, b, n의 입력을 필요로 합니다.

이 프로그램은 정수형 변수 a, b, n으로 구성된 q개의 쿼리를 입력받습니다.
각 쿼리마다, 정수형 변수 a, b, n의 값에 대응하는, 각각의 값이 공백으로 구분된, (한 줄의) 정수 수열을 출력합니다.

입력 형식
첫 줄에서는 쿼리의 수를 나타내는 정수형 변수 q의 값을 입력받습니다.
2번째 줄부터 q-1번째 줄까지는 공백으로 구분된 (해당 번째 쿼리의)
정수형 변수 a, b, n의 값을 한 줄씩 입력받습니다.

제약 조건

출력 형식
각 쿼리에 (포함된 a, b, n의 값에) 대응하는 수열을 (입력받은 줄의 그 다음 줄에) 출력합니다. q개의 수열 모두, (수열에 포함된) n개의 정수가 공백으로 분리되어 출력됩니다.

Answers

⛔️⚠️ SPOILER ALERT ⚠️ ⛔️
답안을 보시기 전, 🔁링크 를 클릭해 자신이 작성한 소스코드를
직접 테스트해 보시기를 추천드립니다.

Java 8

import java.util.Scanner;
public class Loop2 {
    public static void main(String []argh){
        Scanner in = new Scanner(System.in);
        int t=in.nextInt();
        for(int i = 0; i < t; i++){
            int a = in.nextInt();
            int b = in.nextInt();
            int n = in.nextInt();
            int num = a;
            for(int j = 0; j < n; j++){
                num += (Math.pow(2,j) * b);
                System.out.print(num + " ");
            }
            System.out.println();
        }
        in.close();
    }
}

C++ 17

#include <iostream>
#include <cmath>
using namespace std;
int main()
{
    int a, b, n, t;
    cin >> t;
    for(int i = 0; i < t; i++){
        cin >> a >> b >> n;
        int num = a;
        for(int j = 0; j < n; j++){
            num += (pow(2,j) * b);
            cout << num << " ";
        }
        cout << endl;
    }
}

☕️🔡🆗 (간단하게) String의 비교연산을 위한 Method들

☕️🧱🔜 Static Initializer Block

🧱 Java Static Initializer Block - HackerRank

Answers
Java 8

public class StaticInitializerBlock {
	//구현한 부분 시작
    static boolean flag;
    static int B, H;
    static {
        Scanner scan = new Scanner(System.in);
        flag = true;
        B = scan.nextInt();
        H = scan.nextInt();
        if(B <= 0 || H <= 0){
            flag = false;
            System.out.print("java.lang.Exception: Breadth and height must be positive");
        }
    }
    //구현한 부분 끝
    //
    // 문제에 주어진 부분 시작
    public static void main(String[] args){
        if(flag){
            int area=B*H;
            String str = Integer.toString(area);
            System.out.println(area);
            System.out.println(str);
        }
    }
    // 문제에 주어진 부분 끝
}

☕️🔚🔜 EOF 처리하기

☕️🗓🔜 Calendar Class

☕️🔢🔜 NumberFormat Class

📖 참고 문헌

1-1. TCPSCHOOL.com Java
1-2. TCPSCHOOL.com C++
2. Oracle Java Platform, SE 8 API Specification
3. JustKode - C++ To Java
4. 한국어 위키피디아 - C++

profile
안드로이드/아이폰 앱 개발에 막 관심이 생긴 컴퓨터공학과 (마음만은) 새내기. 현대미술과 음악, 철학적 혹은 정치적 사유, 가끔 독서도 즐김.

0개의 댓글

관련 채용 정보