자바로 PS를 처음 해보다보니, 여러 시행착오를 겪고 있다.
파이썬이었으면,
input = sys.stdin.readline().rstrip()
a,b = map(int, input().split())
이면 끝날 일인데 ㅋ
오류를 발생시킨 코드는 다음과 같았다.
class Solution {
public static void main(String args[]) throws IOException
{
for(int test_case = 1; test_case <= T; test_case++)
{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
int n1 = Integer.parseInt(st.nextToken());
int n2 = Integer.parseInt(st.nextToken());
.....
개선한 코드는 다음과 같았다.
class Solution {
public static void main(String args[]) throws IOException
{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
for(int test_case = 1; test_case <= T; test_case++)
{
st = new StringTokenizer(br.readLine());
int n1 = Integer.parseInt(st.nextToken());
int n2 = Integer.parseInt(st.nextToken());
.....
BufferedReader, StringTokenizer를 여러번 초기화 했기 때문에 발생한 에러였다.
for loop 밖에서, 한 번만 초기화 하도록 하자.
그런데, 여러번 초기화 하면 왜 에러가 날까?
NoSuchElementException이 발생하는 이유는 BufferedReader와 관련된 InputStreamReader 또는 System.in과 같은 입력 스트림을 여러 번 초기화하는 과정에서 발생할 수 있습니다. 이 문제를 이해하기 위해 몇 가지 주요 포인트를 살펴보겠습니다:
- InputStream의 소진: System.in과 같은 표준 입력 스트림은 한 번 소진되면 자동으로 리셋되지 않습니다. BufferedReader와 InputStreamReader를 사용할 때마다 이 입력 스트림을 다시 사용하게 되면, 이미 읽은 데이터 이후의 부분부터 시작하게 됩니다. 만약 모든 데이터를 이미 읽었다면, 더 이상 읽을 데이터가 없어 NoSuchElementException이 발생할 수 있습니다.
- BufferedReader의 독립적인 버퍼: 각 BufferedReader 인스턴스는 독립적인 버퍼를 가지고 있습니다. 프로그램에서 BufferedReader를 여러 번 초기화하면, 각 인스턴스가 입력 스트림의 다른 부분을 읽을 수 있습니다. 이로 인해 예상치 못한 위치에서 데이터를 읽게 되고, 필요한 데이터가 이미 다른 버퍼에 의해 소비되어 버릴 수 있습니다.
- 동시성 문제: 멀티 스레드 환경에서 동일한 입력 스트림에 대해 여러 BufferedReader 객체를 사용하면, 스레드 간의 동기화 문제로 인해 NoSuchElementException이 발생할 수 있습니다.
이러한 이유로, 표준 입력 스트림이나 파일 입력 스트림을 사용할 때는 해당 스트림을 한 번만 초기화하고, 필요한 모든 읽기 작업에 대해 같은 BufferedReader 인스턴스를 재사용하는 것이 바람직합니다. 이렇게 하면 입력 스트림의 관리가 더욱 일관되고 효율적으로 이루어질 수 있습니다.
자바의 InputStream을 이해하기 위해, 상수 흐름으로 물을 받는 컵에 비유할 수 있습니다. 상상해보세요, 물이 지속적으로 흐르는 수도꼭지가 있고, 당신은 그 물을 컵으로 받아내려고 합니다.
- 물의 흐름 = 데이터의 흐름: 수도꼭지에서 흐르는 물은 InputStream을 통해 흐르는 데이터에 해당합니다. 데이터는 연속적으로 흐르며, 당신은 필요한 만큼 그 데이터를 '읽어낼' 수 있습니다.
- 컵 = 버퍼: 컵은 BufferedReader와 같은 버퍼링 기능에 비유될 수 있습니다. 컵은 한 번에 많은 양의 물을 담을 수 있어, 물을 조금씩 여러 번 받아내는 것보다 효율적입니다. 마찬가지로, BufferedReader는 한 번에 많은 양의 데이터를 읽어 메모리에 저장(버퍼링)하여, 데이터 접근을 더 빠르고 효율적으로 만듭니다.
- 한 번의 읽기 = 컵으로 물을 한 번 채움: InputStream에서 데이터를 읽는 행위는 컵으로 물을 한 번 채우는 것과 같습니다. 한 번의 '읽기' 작업으로 많은 양의 데이터를 처리할 수 있습니다.
- 데이터 소진 = 물 소진: 마찬가지로, InputStream에서 데이터를 읽으면 그 부분의 데이터는 '소진'됩니다. 다시 읽으려면, 스트림을 처음부터 다시 시작하거나, 새로운 데이터 소스에 접근해야 합니다. 수도꼭지에서 컵으로 물을 받으면, 그 물은 다시 수도꼭지로 돌아가지 않습니다.
- 스트림 닫기 = 수도꼭지 닫기: 마지막으로, 데이터를 모두 사용한 후에는 InputStream을 '닫아야' 합니다. 이것은 수도꼭지를 닫아 물의 흐름을 멈추는 것과 같습니다. 이렇게 하면 시스템 자원이 해제되고 다른 프로세스나 스레드에서 해당 자원을 사용할 수 있습니다.
이 비유를 통해 InputStream의 작동 방식을 좀 더 직관적으로 이해할 수 있습니다. 데이터를 읽는 것은 물을 받는 것과 유사하며, 효율적인 데이터 처리를 위해서는 적절한 '컵'(버퍼링 기법)을 사용하는 것이 중요합니다.
끝.
'Software Development > Trial-and-error' 카테고리의 다른 글
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!