상냥한 세상 :: [백준]10869번: 사칙연산(용어정리)

본문으로 바로가기

https://www.acmicpc.net/problem/10869

 

10869번: 사칙연산

두 자연수 A와 B가 주어진다. 이때, A+B, A-B, A*B, A/B(몫), A%B(나머지)를 출력하는 프로그램을 작성하시오. 

www.acmicpc.net

  • 문제

이번에는 사칙연산이다. 보는바와같이 7과 3을가지고 각각 덧셈/뺄셈/곱셈/나눗셈(나머지는버린다)/나누고 나머지 를 출력한다. 

시작하기 전에 앞서 간단한 사칙연산에 쓰일 기호부터 설명하겠다.(덧/뺄/곱셈은생략)


A/B: A를 B로 나눈다

※자료형에 따라 답이 다를수 있다.

ex)int(정수형)으로 출력할경우 5를 3으로 나눈 몫은 1이므로 1을 출력한다. 

ex)double(실수형)으로 출력할 경우 10.0를 4.0으로 나눈 몫인 2.5을 출력한다

int result=5/3

System.out.println(result);

//결과: 1
double result1 = 10/4;
double result2 = 10.0/4; //둘중하나만 실수형으로 적어주면 결과값이 정상출력됨
double result3 = (double)10/4; //마찬가지로 정수로적고 앞에 (double)을 붙여주면 값을 읽을때 실수형으로 읽음
System.out.println(result1);
System.out.println(result2);

//결과1: 2.0 
//결과2: 2.5
//결과3: 2.5

 

A%B: A를 B로 나눈 나머지

※자료형에 따라 답이 다를수 있다. 

ex)int(정수형)으로 출력할경우 5를 3으로 나눈 나머지는 2이므로 2을 출력한다. 

ex)double(실수형)으로 출력할 경우 11.5를 4.0으로 나눈 나머지는 3.5이므로 3.5를 출력한다

 

int result = 5%3;

System.out.println(result);

//결과:2
double result1 = 11.5%4;
System.out.println(result1);


//결과1: 3.5

 

'\n': 줄바꿈

System.out.println( "Hello World!" + '\n'+ "Nice To Meet You");

//결과
Hello World 
Nice To Meet You

  • 코드1
  • //Scanner을 이용한 방법
    import java.util.Scanner;
    
    public class Main{
        public static void main(String[] args){
            Scanner in = new Scanner(System.in);
            int A=in.nextInt();
            int B=in.nextInt();
            
            in.close();
            
            StringBuilder sb=new StringBuilder();
            sb.append(A+B);
            sb.append('\n');
            
            sb.append(A-B);
            sb.append('\n');
            
            sb.append(A*B);
            sb.append('\n');
            
            sb.append(A/B);
            sb.append('\n');
            
            sb.append(A%B);
            
            System.out.print(sb);
            
        }
    }

진행하기에 앞서, 이번에도 새롭게 보는 용어들이 보인다. StringBuilder와 append. 우선 이 두가지를 정의하고 시작하고자 한다. 

Process(프로세스):실행중인 Program(프로그램). 사용자가 작성한 프로그램이 운영체제에 의해 메모리 공간을 할당받아 실행 중인 것

※Process는 데이터와 메모리등의 자원 그리고 Thread(쓰레드)로 구성된다.

Thread(쓰레드): 프로세스(process) 내에서 실제로 작업을 수행하는 주체

 

아주 쉽게 설명된 스레드

※Multi-threaded process: 두개이상의 스레드를 가지고 진행하는 프로세스,여러번 작업요청이 들어오는것 (String부분 참고)

Instance(인스턴스):객체를 소프트웨어에 실체화한 것. ※냉장고(객체)를 설계도(클레스)를 바탕으로 진짜만든 냉장고(인스턴스)

Synchronized(동기화): Multi-Threaded process에서 동시화 요청이 오면 모든 Thread가 동기화되어 있기 때문에 한 Thread의 요청이 끝날때 까지 기다려야함. (자세한 설명은 아래참고)

 

 

  • 둘 이상의 쓰레드가 공동의 자원(파일이나 메모리 블록)을 공유하는 경우, 순서를 잘 맞추어 다른 쓰레드가 자원을 사용하고 있는 동안 한 쓰레드가 절대 자원을 변경할 수 없도록 해야 한다. 한 쓰레드가 파일에서 레코드를 수정하는데, 다른 쓰레드가 동시에 같은 레코드를 수정하면 심각한 문제가 발생할 수 있다. 이런 상황을 처리할 수 있는 한 방법은 관련된 쓰레드에 대한 동기화(synchronization)를 이용하는 것이다.동기화의 목적은 여러 개의 쓰레드가 하나의 자원에 접근하려 할 때 주어진 순간에는 오직 하나의 쓰레드만이 접근 가능하도록 하는 것이다. 동기화를 이용해 쓰레드의 실행을 관리할 수 있는 방법은 두 가지가 있다.
    • 코드를 메소드 수준에서 관리할 수 있다. - 동기화 메소드
    • 코드를 블록 수준에서 관리할 수 있다. - 동기화 블록
    동기화 메소드와 동기화 블록은 모두 synchronized 를 이용하여서 구현된다. synchronized 는 어떤 객체에서도 특정 문장 블록에 대해 lock 설정을 할 수 있다.
  • 사용방법
    // 1. 함수에 사용하는 경우
    public synchronized void method() {
    	 //code
    }
    
    // 2. 객체 변수에 사용하는 경우
    private Object obj = new Object();
    public void exampleMethod() {
    	synchronized(obj) {
    		//code
    	}
    }​
  • 샘플
  • 아래 샘플은 3개의 스레드가 돌아가면서 User 클래스에 있는 Add 함수를 호출하는 과정입니다. add() 함수를 동기화하지 않고 그대로 두게 되면 출력값이 차례대로 되지 않고 바뀌는 경우가 생깁니다. 예를 들어 B2 스레드가 번호를 4로 증가하고 출력할려고 하는데 C3 스레드가 방금 증가한 4에 하나를 더해서 먼저 5를 출력한 것입니다. 그래서 아래 결과값에 B2, C3 가 뒤바뀐 것입니다.
    class User {
    	private int userNo = 0;
    
    	// 임계 영역을 지정하는 synchronized메소드
    	public void add(String name) {
    		System.out.println(name + " : " + userNo++ + "번째 사용");
    	}
    }
    
    class UserThread extends Thread {
    	User user;
    
    	UserThread(User user, String name) {
    		super(name);
    		this.user = user;
    	}
    
    	public void run() {
    		try {
    			for (int i = 0; i < 3; i++) {
    				user.add(getName());
    				sleep(500);
    			}
    		} catch (InterruptedException e) {
    			System.err.println(e.getMessage());
    		}
    	}
    }
    
    public class SyncThread {
    
    	public static void main(String[] args) {
    
    		User user = new User();
    
    		// 3개의 스레드 객체 생성
    		UserThread p1 = new UserThread(user, "A1");
    		UserThread p2 = new UserThread(user, "B2");
    		UserThread p3 = new UserThread(user, "C3");
    
    		// 스레드 스케줄링 : 우선순위 부여
    		p1.setPriority(p1.MAX_PRIORITY);
    		p2.setPriority(p2.NORM_PRIORITY);
    		p3.setPriority(p3.MIN_PRIORITY);
    
    		System.out.println("-----------------------");
    		System.out.println("sychronized 적용안한 경우");
    		System.out.println("-----------------------");
    
    		// 스레드 시작
    		p1.start();
    		p2.start();
    		p3.start();
    	}
    }
    // 결과 
    -----------------------
    sychronized 적용안한 경우
    -----------------------
    A1 : 0번째 사용
    B2 : 1번째 사용
    C3 : 2번째 사용
    A1 : 3번째 사용
    C3 : 5번째 사용
    B2 : 4번째 사용
    C3 : 7번째 사용
    A1 : 6번째 사용
    B2 : 8번째 사용
  • 동기화 미사용시 문제
  • 동기화를 하지 않으면 어떤 일이 발생할지 모릅니다. 사용자가 의도한대로 차례대로 출력할려면 출력이 끝날 때 까지 add() 함수로 아무도 못들어오게 막아야 겠죠. 아래 소스는 add() 함수에 synchronized 적용한 것입니다. 번호 증가가 차례대로 되는 것을 알수 있습니다.
    class User {
    	private int userNo = 0;
    
    	// 임계 영역을 지정하는 synchronized메소드
    	public synchronized void add(String name) {
    		System.out.println(name + " : " + userNo++ + "번째 사용");
    	}
    }
    
    class UserThread extends Thread {
    	User user;
    
    	UserThread(User user, String name) {
    		super(name);
    		this.user = user;
    	}
    
    	public void run() {
    		try {
    			for (int i = 0; i < 3; i++) {
    				user.add(getName());
    				sleep(500);
    			}
    		} catch (InterruptedException e) {
    			System.err.println(e.getMessage());
    		}
    	}
    }
    
    public class SyncThread {
    
    	public static void main(String[] args) {
    
    		User user = new User();
    
    		// 3개의 스레드 객체 생성
    		UserThread p1 = new UserThread(user, "A1");
    		UserThread p2 = new UserThread(user, "B2");
    		UserThread p3 = new UserThread(user, "C3");
    
    		// 스레드 스케줄링 : 우선순위 부여
    		p1.setPriority(p1.MAX_PRIORITY);
    		p2.setPriority(p2.NORM_PRIORITY);
    		p3.setPriority(p3.MIN_PRIORITY);
    
    		System.out.println("-----------------------");
    		System.out.println("sychronized 적용안한 경우");
    		System.out.println("-----------------------");
    
    		// 스레드 시작
    		p1.start();
    		p2.start();
    		p3.start();
    	}
    }
    // 결과 
    -----------------------
    sychronized 적용안한 경우
    -----------------------
    A1 : 0번째 사용
    C3 : 1번째 사용
    B2 : 2번째 사용
    A1 : 3번째 사용
    B2 : 4번째 사용
    C3 : 5번째 사용
    A1 : 6번째 사용
    B2 : 7번째 사용
    C3 : 8번째 사용

String, StringBuffer, StringBuilder들은 모두 문자열을 저장하고 관리하는 클래스이다. 각 클래스 별로 약간의 차이점들이 있다. 

시작에 앞서, 이것들의 특성을 크게 나누면 Immutable(불변) VS Muttable(가변) 이다. 
Immutable: String
Muttable: StringBuffer, StringBuilder

하지만 이것들을 제대로 설명하려면 StringPool에 대한 개념을 먼저 짚고 넘어가야할 필요성이 있다. 

 

StringPool: HeapMemory영역에 위치해 있으며, String을 저장하는곳

※Heap과 같은 용어들은 아래 게시글을 참고

https://kindstudent.tistory.com/23

 

Introduction of Java-2

자바 프로그램의 실행 과정 Java->(Java Compile)-> Byte code (. class)->(JVM의 Interpreters)->Machine Language  - 컴파일러와 인터프리터 (Compilers and Interpreters)  → 컴파일러 - 한 언어로 작성된..

kindstudent.tistory.com

String s1 = "Cat";
String s2 = "Dog";
String s3 = new String("Cat");

 

String을 통해 객체를 만드는 방식은 s1,s2와 같이 큰따음표(" ")와 s3와 같이 new생성자, 총 2가지가 있다. 

위사진에서, 큰따음표를 통해 만든 객체는 String pool 영역을 가리키고 있고, new생성자는 heap memory영역을 가리키고 있다.

 

진짜 그럴까? 아래코드를 보면 가시적으로 확인할 수 있다. 

String s1 = "Cat";
String s2 = "Cat";
String s3 = new String("Cat");
String s4 = new String("Cat");
 
System.out.println(s1 == s2);  // true
System.out.println(s1 == s3);  // false
System.out.println(s3 == s4);  // false

//진짜 그렇다

※== 연산자는 메모리 주소가 같은지 비교하게 된다.

System.out.println(s1 == s2); s1과 s2의 메모리주소가 StringPool 로 같으므로 s1==s2는 true이다. 

System.out.println(s1 == s3); s1의 메모리주소는 StringPool인 반면, s3는 heap이므로 s1==s3는 false이다. 
System.out.println(s3 == s4); s3와 s4둘다 동적으로 할당된 heap에 메모리를 할당받지만, 각각에 할당된 메모리가 별도이고 다르므로 s3==s4는 false이다.  

 

Flyweight design Pattern: 인스턴스의 생성을 최소화 하여 메모리의 사용을 최대한 아끼는 방법 

StringPool(Flyweight pattern): 런타임시 String객체가 얼마나 많이 생성되든 간에 많은 양의 메모리 공간을 절약할 수 있음.

위에서 설명한 바와 같이 큰따음표(" ")를 통해 String 생성시 Java는 먼저 StringPool에 생성하려는 값과 동일한 값의 String객체가 있는지 탐색한다. 만약 같은 객체가 있다면 새로운 객체를 따로 생성하지 않고 탐색중에 찾은 주소값을 리턴하고 없으면 따로 새롭게 객체를 만들어 String Pool에 할당한뒤 그에 해당하는 주소를 리턴한다. 

 

New생성자를 통해 String을 생성하는것도 StringPool과 방식은 비슷해보이지만 좀 다르다. new를 통해 객체 생성시 heap메모리 영역에 객체를 따로 생성하고 그 주소값을 리턴한다.  

 

하지만 경우에 따라서 new생성자로 만든 String 객체는 Heap에 하나 StringPool에 하나 이렇게 각각의 영역에 총2개의 객체를 생성할 수 있다. 

 

예를들어 아래처럼 s1의 "Cat"의 주소값은 StringPool에 위치하지만 동시에 s2의 "Cat"은 Heap에도 똑같이 존재할수도 있다는 뜻이다. 

Case1: StringPool에 이미 "Cat"이 있지만, Heap에는 없었을경우-Heap에만 "Cat"객체를 하나생성

Case2: StringPool에는 없고 Heap에만 "Cat"이 있었을경우-StringPool에만 "Cat"객체를 하나생성

String s1="Cat";
String s2 = new String("Cat");

 

 

아래의 코드를 보면 보다 정확히 가시적으로 확인가능하다. 

intern(): 새롭게 만들어진 String객체를 상수화한다. 만들어진 String 객체가 이미 상수로 만들어진 문자열이라면, 지금 만들어진 객체을 버리고, 상수를 가리키게 한다.즉, Heap에 새롭게 만들어진 객체를 버리고, 상수로 대체한다. 

String s1 = new String("Cat");
String s2 = s1.intern();
String s3 = "Cat";
 
System.out.println(s1 == s2);  // false
System.out.println(s2 == s3);  // true
System.out.println(s1.intern() == s3);  // true

  • String 클래스 생성법
    //1.new연산자를 이용하지 않고 인스턴스를 만드는 경우 
    String str1 = "hello"; 
    String str2 = "hello"; 
    
    /* "hello"라는 문자열이 메모리 중에서 상수가 저장되는 영역에 저장된다. 
    상수는 변하지 않는 값을 의미. 
    String str2 = "hello"; 이 문장이 실행될 때에 hello 라는 문자열 상수는 
    이미 만들어져 있으므로 str1이 참조하는 인스턴스를 str2도 참조한다. 
    */ 
    
    
    
    
    
    //2.new연산자를 이용해서 인스턴스를 만드는 경우 
    String str3 = new String("hello"); 
    String str4 = new String("hello");
    
    
    /* new연산자를 이용하여 인스턴스를 만들면 인스턴스는 무조건 새롭게 만들어진다. 
    String str4 = new String("hello"); 이 문장이 실행될때도 새롭게 만들게 되므로, 
    str3 과 str4는 서로 다른 인스턴스를 참조한다. 
    */

 

String은 Immutable(불변)하다는 말이 있다. 그런데 아래와 같이 보기엔 Muttable(가변)한것 처럼 보인다

String str="first";
str="second";

System.out.println(str);

//결과: second

 

자바에서 변수는 언제든지 바뀔 수 있다. str이라는 변수에 first를 저장(초기화)해놓고 second로 바꾸는것쯤이야 늘 가능하단 얘기이다. 

만약 정말 바꾸고 싶지 않다면 final 을 붙여서 임의로 변수자체를 Immutable상태로 바꿔주면 된다.

final String str="first";
str="second"; //error!!!

 

그러면 String은 Immutable(불변)하다는 소리는 어디서 나온걸까?

Java에서 String은 Heap영역안에 있는 StringPool에서 관리한다.

※Java7이전엔 Perm 영역이였으나 이후 Heap으로 바뀌었다. 

 

int의 num변수는 할당된 스택메모리에 값을 바로저장하고 있기 때문에 10에서 20으로 바꾸어도 변수가 갖고있는 메모리에서 값을 변경해버린다. 10이라는 값이 할당되있는 상태에서 20으로 메모리값을 변경해버린것이므로 Muttable(가변)하다. 

 

그런데, String데이터를 잘보면 스택에 바로저장되는게 아니라, Heap영역의 StringPool이라는곳에 메모리를 할당받아 거기에 값을 저장하고 str은 그 값을 참조하게 된다. (즉str이 num처럼 스택에서 참조해오는게 아니라 사실은 Heap의 StringPool에있는 주소를 참조)

그래서 str="abc"라고 선언후에 str="def"라고 바로 선언해도 단지 변수가 '참조하는' 메모리값이 0x11에서 0x22로 변경된걸뿐, 실제 "abc"가 저장되있는 0x11 메모리주소의 데이터가 바뀌는게 아니란것이다. 이것이 String이 Immutable(불변)한 이유이다. 

 

정리: String은 단지 '참조하는'메모리값만 변경된것뿐, 해당 메모리주소의 '데이터가 바뀌는게 아님'=Immutable(불변)

int num = 10;
num = 20;
 
String str = "abc";
str = "def";

 

String str = "hello"; // String str = new String("hello"); 
str = str + " world"; // [ hello world ]

 

 

  • StringBuffer/StringBuilder 생성법
  • //StringBuffer
    StringBuffer sb = new StringBuffer(); 
    sb.append("banan");
    sb.append("a");
    
    //StringBuilder
    StringBUilder sb=new StrinbBuilder();
    sb.append("banan");
    sb.append("a");
    
    //자료예시
    StringBuffer sb= new StringBuffer("hello"); 
    sb.append(" world");
     
  • StringBuffer/StringBuilder 예제
  • public class StringBuffer_Ex {
        public static void main(String[] args)  {
            String s = "abcdefg";
            StringBuffer sb = new StringBuffer(s); // String -> StringBuffer
    		
            System.out.println("처음 상태 : " + sb); //처음상태 : abcdefg
            System.out.println("문자열 String 변환 : " + sb.toString()); //String 변환하기
            System.out.println("문자열 추출 : " + sb.substring(2,4)); //문자열 추출하기
            System.out.println("문자열 추가 : " + sb.insert(2,"추가")); //문자열 추가하기
            System.out.println("문자열 삭제 : " + sb.delete(2,4)); //문자열 삭제하기
            System.out.println("문자열 연결 : " + sb.append("hijk")); //문자열 붙이기
            System.out.println("문자열의 길이 : " + sb.length()); //문자열의 길이구하기
            System.out.println("용량의 크기 : " + sb.capacity()); //용량의 크기 구하기
            System.out.println("문자열 역순 변경 : " + sb.reverse()); //문자열 뒤집기
            System.out.println("마지막 상태 : " + sb); //마지막상태 : kjihgfedcba
        }
    }
    
    public class StringBuilder_Ex {
        public static void main(String[] args)  {
            String s = "abcdefg";
            StringBuilder sb = new StringBuilder(s); // String -> StringBuilder
    		
            System.out.println("처음 상태 : " + sb); //처음상태 : abcdefg
            System.out.println("문자열 String 변환 : " + sb.toString()); //String 변환하기
            System.out.println("문자열 추출 : " + sb.substring(2,4)); //문자열 추출하기
            System.out.println("문자열 추가 : " + sb.insert(2,"추가")); //문자열 추가하기
            System.out.println("문자열 삭제 : " + sb.delete(2,4)); //문자열 삭제하기
            System.out.println("문자열 연결 : " + sb.append("hijk")); //문자열 붙이기
            System.out.println("문자열의 길이 : " + sb.length()); //문자열의 길이구하기
            System.out.println("용량의 크기 : " + sb.capacity()); //용량의 크기 구하기
            System.out.println("문자열 역순 변경 : " + sb.reverse()); //문자열 뒤집기
            System.out.println("마지막 상태 : " + sb); //마지막상태 : kjihgfedcba
        }
    }

 

StringBuffer결과
StringBuffer결과

 

StrignBuffer/StringBuilder은 Muttable(가변) 하다는 말이 있다. 

append() , delete()와 같은 메소드를 사용하여 동일한 객체내에서 문자열을 변경하는게 가능하므로 문자열의 추가/수정/삭제가 빈번하게 발생될 경우라면 가변성을 지닌 StringBuffer/StringBuilder을 쓰는게 좋다. 

좀 더 정확하게는, 문자열을 여러번 수정하거나 해야할 경우엔 String을 사용하지 않는이유는 다음과 같다. 

1.String으로 만든 객체는 크기가 고정되있다.

2.문자열을 수정할때마다 새로운 문자열을 리턴한다.

String s = "ABC";
s += "DEF";

String으로 저장(초기화)된 "ABC"에서 연산자 +=를 통해 "DEF"를 추가하면 결과적으로 "ABCDEF"로 값이 변경된다고 생각하기 쉽다. 하지만 앞서 말했던 String의 Immutable한 점으로 인해, '값이 변경되는게 아닌', '새로운 String객체가 생성'된다. 그리고 data변수는 새로 생성된 String객체를 참조하게 된다. 

 

그럼 기존에 있던 "ABC"는 어떻게되는가? 다행히도 자바에는 GC가 더이상 참조하지않는 메모리를 해체해준다. 그런데 이렇게 연산자+를 사용해서 계속 계속 새로운 String객체를 만들면 그 수도 당연히 늘어나기 때문에 프로그램성능이 느려질수밖에 없다. 

 

String에 연산자를 1000번가했을경우 처리속도가 앞도적으로 느려짐을 확인 할 수 있다.

public class Main {
    private static final int MAX_COMPARE = 100000;
 
    public static void main(String[] args) {
        // String Test
        String test1 = "";
        long start = System.currentTimeMillis();
        for(int i = 0; i < MAX_COMPARE; i++) {
            test1 += "a";
        }
        long end = System.currentTimeMillis();
        System.out.println("String : " + (end - start) + " ms");
 
        // StringBuilder Test
        StringBuilder sb = new StringBuilder();
        start = System.currentTimeMillis();
        for(int i = 0; i < MAX_COMPARE; i++) {
            sb.append("a");
        }
        end = System.currentTimeMillis();
        System.out.println("StringBuilder : " + (end - start) + " ms");
    }
}

 

  • StringBuffer/StringBuilder메소드
  • sb.append(값)
    - StringBuffer, StringBuilder 뒤에 값을 붙인다
     
    sb.insert(인덱스, 값)
    - 특정 인덱스부터 값을 삽입한다
     
    sb.delete(인덱스, 인덱스)
    - 특정 인덱스부터 인덱스까지 값을 삭제한다
     
    sb.indexOf(값)
    - 값이 어느 인덱스에 들어있는지 확인한다
     
    sb.substring(인덱스, 인덱스)
    - 인덱스부터 인덱스까지 값을 잘라온다
     
    sb.length()
    - 길이 확인
     
    sb.replace(인덱스, 인덱스, 값)
    - 인덱스부터 인덱스까지 값으로 변경
     
    sb.reverse()
    - 글자 순서를 뒤집는다
  • String vs StringBuffer vs StringBuilder 상황별 차이

    String         
           :  문자열 연산이 적고, 멀티쓰레드 환경일 경우
    StringBuffer     :  문자열 연산이 많을때. 공통메소드가 동기화가 가능하므로 멀티쓰레드에 안전. 값이 예상치못하게 변경됨을 방지
    StringBuilder   :  문자열 연산이 많을때. 동기화가 불가능하지만 그렇기에 단일쓰레드환경에서 Buffer보다 더욱 성능이좋다.  

글이 너무 길어진것 같다;; 그런데 개발자를 자처한다면 위에 개념들은 꼭꼭 알고가야하기 때문에 백준에서 연습문제를 푸는겸 정리도 할겸 이렇게 적어봤다. 

 

이정도 길이면 적었던 코드가 뭔지 기억이 안날법한데 다시 가져오겠다. 

//Scanner을 이용한 방법
import java.util.Scanner;

public class Main{
    public static void main(String[] args){
        Scanner in = new Scanner(System.in);
        int A=in.nextInt();
        int B=in.nextInt();
        
        in.close();
        
        StringBuilder sb=new StringBuilder();
        sb.append(A+B);
        sb.append('\n');
        
        sb.append(A-B);
        sb.append('\n');
        
        sb.append(A*B);
        sb.append('\n');
        
        sb.append(A/B);
        sb.append('\n');
        
        sb.append(A%B);
        
        System.out.print(sb);
        
    }
}

이제 왜 StringBuilder을 사용했는지 알거라고 생각한다. append메소드를 통해 문자열을 하나로 연결해준다음, 한번에 출력하게 된다.


  • 결과


  • 코드2
//BufferedWriter을 이용한 방법
import java.io.BufferedWriter;
import java.io.OutputStreamWriter;
import java.io.IOException;
import java.util.Scanner;

public class Main {
    public static void main(String[] args)throws IOException{
        Scanner in= new Scanner(System.in);
        int A=in.nextInt();
        int B=in.nextInt();
        
        in.close();
        /*
        
        bw.write(s);//출력
        bw.newLine(); //줄바꿈
        bw.flush();//남아있는 데이터를 모두 출력시킴
        bw.close();//스트림을 닫음
        */
        
        BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(System.out));
        bw.write((A+B)+"\n");
        bw.write((A-B)+"\n");
        bw.write((A*B)+"\n");
        bw.write((A/B)+"\n");
        bw.write((A%B)+"\n");
   
        

        bw.flush();
        bw.close();
    }
}

방금막 코드1에서 새롭게 개념을 배우고와서 끝났나 싶었는데 여기서또 새롭게 보는게 있다. (사실 BufferWriter은 이전포스트에서 개념정리로 간단하게나마 설명함) 바로 BufferWriter이다. 

 

이참에 다시한번 BufferedReader와 BufferedWriter의 개념을 다시 정리하고 넘어가겠다.

 

Buffer: 데이터를 한 곳에서 다른 하나 곳으로 전송하는 동안 일시적으로 그 데이터를 보관하는 임시 메모리 영역

BufferedReader/BufferedWriter: 버퍼를 통해 읽고 쓰는 함수

※입출력 데이터가 바로 전달되지 않고 중간에 버퍼링이 된 후 전달된다

Buffer Flush: 버퍼에 남아있는 데이터를 출력시킴(버퍼를 비우는 동작)

 

  • BufferedReader 사용법
    ※입력 스트림에서 문자를 읽는 함수
    ※문자나 배열, 라인들을 효율적으로 읽기 위해 문자들을 Buffer에 저장하고(버퍼링)읽음
    ※버퍼사이즈는 사용자가 지정가능하나, 그렇지 못한경우 디폴트 사이즈가 사용됨
  • readLine()
    
    -데이터 라인 단위로 읽기 가능
    -Scanner 처럼 공백단위로 끊어주지는 못한다. 엔터만 경계로 인식함
    -return 값이 String으로 고정되기 때문에 String이 아닌 다른 타입으로 입력 받으려면 형변환을 해줘야한다.
    
    BufferedReader br = new BufferedReader( new InputStreamReader( System.in ) );
    int T = Integer.parseInt( br.readLine() );

 

  • BufferedWriter 사용법
    -한번에 모았다가 출력이 가능하다
    -버퍼를 정의해줬기 때문에 반드시 flush()/close()를 호출해 뒤처리를 해줘야한다
    -System.out.println() 처럼 자동개행 기능이 없기 때문에 개행이 필요한 경우 \n 을 통해 따로 처리해 주어야한다.
    -일반적으로 자바의 System.out.println을 이용할 경우에는 자동으로 줄바꿈을 해주지만, 
    해당 부분을 와일드카드( \n ) 으로 처리해야 한다는 정도의 차이
    -write 안에 쓰고자 하는 문자열을 쓰고 사용
    -마지막에 flush() 함수나 close() 함수를 써 주지 않을 경우 제대로 출력되지 않을 수 있다
    
    BufferedWriter bw = new BufferedWriter( new OutputStreamWriter( System.out ) );
    bw.write( "Hello World!!\n" );
    bw.flush();
    bw.close();​
  • Buffer메소드
    주요 메소드
    
    메서드 명	기능
    BufferedReader(Reader rd)	rd에 연결되는 문자입력 버퍼스트림 생성
    BufferedWriter(Writer wt)	wt에 연결되는 문자 출력 버퍼스트림 생성
    int read()	스트림으로부터 한 문자를 읽어서 int 형으로 리턴
    int read(char[] buf)	문자 배열 buf의 크기만큼 문자를 읽어들임. 읽어들인 문자 수를 리턴
    int read(char[] buf, int offset, int length)	buf 의 offset 위치에서부터 length 길이 만큼 문자를 스트림으로부터 읽어들임
    String readLine()	스트림으로부터 한 줄을 읽어 문자열로 리턴
    void mark()	현재 위치를 마킹, 차후 reset() 을 이용하여 마킹위치부터 시작함
    void reset()	마킹이 있으면 그 위치에서부터 다시 시작, 그렇지 않으면 처음부터 다시 시작
    long skip(int n)	n 개의 문자를 건너 뜀
    void close()	스트림 닫음
    void write(int c)	int 형으로 문자 데이터를 출력문자 스트림으로 출력
    void write(String s, int offset, int length)	문자열 s를 offset위치부터 length 길이 만큼을 출력스트림으로 출력
    void write(char[] buf, int offset, int length)	문자배열 buf의 offset 위치부터 length 길이 만큼을 출력스트림으로 출력
    void newLine()	줄바꿈 문자열 출력
    void flush()	남아 있는 데이터를 모두 출력시킴​

  • 결과


  • 코드3
//BufferedReader을 이용한 방법
import java.util.StringTokenizer;
import java.io.IOException;
import java.io.BufferedReader;
import java.io.InputStreamReader;

public class Main{
    public static void main(String[] args) throws IOException{
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st=new StringTokenizer(br.readLine()," ");
        int A=Integer.parseInt(st.nextToken());
        int B=Integer.parseInt(st.nextToken());
        
        System.out.println(A+B);
		System.out.println(A-B);
		System.out.println(A*B);
		System.out.println(A/B);
		System.out.println(A%B);
        
    }
}

  • 결과


  • 코드1,2,3 속도비교

Scanner사용
BufferedWriter+Scanner사용
BufferedBuilder+StringTokenizer사용

'Computer Science > BAEKJOON JAVA Practice' 카테고리의 다른 글

[백준]2588번: 곱셈  (0) 2021.12.25
[백준]10430번: 나머지  (0) 2021.12.25
[백준]10098번: AxB  (0) 2021.12.22
[백준]1001번: A-B  (0) 2021.12.22
[백준]1000번: A+B (용어 정리포함)  (0) 2021.12.22