최대값

※ Point :
9개의 정수의 값을 입력후, 해당 값 중 가장 큰 값을 출력하고, 그 값이 몇 번째에 위치해 있는지를 출력하는 문제입니다.
일단 가장 먼저 생각난 방법은 for문을 통해 배열에 값을 입력 후, 다시 for문을 돌려 최대값 변수와 인덱스 요소값을 비교하여 더 큰 값을 최대값 변수에 대입하는 방식으로 최대값을 찾는 방식이었습니다.
여기까지는 이전 문제와 비슷하지만 문제는 인덱스값을 찾는 것인데,
저는 따로 메서드를 분리하여 해당 메서드에서 인자로 전달받은 배열과, 최대값을 for문으로 비교한 후 일치하는 요소에 위치한 인덱스 번호 i를 반환하는 방식으로 풀었습니다.
틀린답
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.IOException;
import java.util.StringTokenizer;
import java.util.Arrays;
public class Main {
public static int findMaxnum(int[] arr, int target) {
for (int i = 0; i < arr.length; i++) {
if (arr[i] == target) {
return i;
}
}
return -1;
}
public static void main(String args[]) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
StringTokenizer st = new StringTokenizer(br.readLine(), " ");
int[] arr = new int[9];
for(int i = 0; i< arr.length; i++) {
if (st.hasMoreTokens()) {
arr[i] = Integer.parseInt(st.nextToken());
//System.out.println(arr[i]);
}
}
int maxNumber = arr[0];
for(int i = 0; i < arr.length; i++) {
if(maxNumber < arr[i]) {
maxNumber = arr[i];
}
}
System.out.println(maxNumber);
// System.out.println(Arrays.asList(arr).indexOf(maxNumber));
System.out.println(findMaxnum(arr, maxNumber));
br.close();
bw.flush();
bw.close();
}//main
}
하지만 이 방식으로는 정답이 인정되지 않았고, 다른 방법을 찾아봐야 했습니다.
제일 먼저 활용할 수 있는건 indexOf()메서드를 사용하는 것입니다. 아래와 같이 코드를 작성해보았습니다.
Arrays.asList(arr).indexOf(maxNumber);
하지만 문제가 생겼습니다.
indexOf() 메서드를 활용하고 싶어도 해당 메서드가 속해있는 List 인터페이스에서는 String이나 객체 배열의 인덱스를 찾는 데 사용하기 때문에, int형과 같은 primitive type에서는 직접 사용할 수 없다는 것입니다.
위에서 걸어놓은 주석을 풀어놓고 실행하면, -1을 반환합니다. maxNumber가 int[] 배열 내의 원소 값이 아니라, 배열 객체 자체와 비교되기 때문입니다.
따라서 배열 요소의 인덱스를 출력하기 위해서는 번거롭지만 래퍼 타입인 Integer객체의 배열로 변환해야합니다. 이를 위해서는 아래와 같이 코드를 작성할 수 있습니다.
정답
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;
import java.util.Arrays;
import java.util.StringTokenizer;
import java.util.stream.IntStream;
public class Main {
public static void main(String args[]) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int[] arr = new int[9];
for(int i = 0; i < arr.length; i++) {
arr[i] = Integer.parseInt(br.readLine());
}
int maxNumber = Arrays.stream(arr).max().getAsInt();
// int 배열을 Integer 배열로 변환
Integer[] boxedArray = IntStream.of(arr).boxed().toArray(Integer[]::new);
// Integer 배열을 List로 변환하여 indexOf 메서드를 사용
int index = Arrays.asList(boxedArray).indexOf(maxNumber) + 1; // 인덱스는 0부터 시작하므로 +1
System.out.println(maxNumber);
System.out.println(index);
br.close();
}//main
}
먼저 배열 arr에 for문으로 값을 넣는 것까지는 똑같습니다.
이후 크게 3가지 단계로 해결하는데,
① Arrays.stream(arr).max().getAsInt();를 사용하여 최댓값을 찾고,
② IntStream.of(arr).boxed().toArray(Integer[]::new);를 통해 int[] 배열을 Integer[] 배열로 변환한 후
③ Arrays.asList(boxedArray).indexOf(maxNumber) + 1;를 사용하여 최댓값의 인덱스를 찾습니다.
Arrays.stream(arr).max().getAsInt();
먼저 Arrays.stream(arr)으로 배열을 스트림으로 변환합니다. (스트림 API를 사용하여 배열 데이터를 처리하기 위함입니다.) 그리고 .max() 으로 스트림의 최대값을 찾는데, 이 메서드는 해당값을 OptionalInt 객체로 반환하는 특징을 가지고 있습니다. 그리고 .getAsInt() 메서드로 max()가 반환한 OptionalInt 객체에서 실제 int값을 추출합니다.
주의할 점 :
.max()에서 스트림이 비어있어 최대값이 없을 경우, OptionalInt 객체는 빈값을 반환하여 getAsInt() 메서드에서 NoSuchElementException 에러를 던질 수도 있습니다. 따라서, getAsInt()를 호출하기 전에 isPresent() 메서드를 사용하여 OptionalInt 객체가 값이 있는지 확인하는 것이 좋습니다. 하지만 이 문제는 이미 9개의 고정값이 주어져있기때문에 값이 항상 있다고 각정하고 이 부분은 넘어가겠습니다.
IntStream.of(arr).boxed().toArray(Integer[]::new);
다음으로 IntStream.of(arr)으로 기본 타입 스트림을 객체 스트림으로 변환합니다. (기본 타입 데이터인 int형을 객체 기반의 처리로 전환하기 위함입니다.) 이를 통해 int[] arr 배열을 IntStream으로 변환할 수 있습니다. 이렇게 하면 arr 배열의 각 int 요소가 스트림의 요소로 사용됩니다.
IntStream이란?
IntStream은 기본 타입 int의 스트림으로, int 값들을 효율적으로 처리할 수 있는 메서드를 제공합니다.
이후 .boxed() 메서드를 사용하여 IntStream을 Stream<Integer>로 변환합니다. boxed() 메서드는 IntStream에서 작업 중인 각 int 값을 Integer 객체로 박싱(Boxing)하는 역할을 수행합니다. 이 변환 과정을 통해서 기본 타입 값인 int형을 해당 래퍼 타입인 Integer 객체로 감싸게 되고, 이후의 연산에서 Integer 객체에 대한 메서드를 사용할 수 있게 됩니다.
마지막으로 .toArray(Integer[]::new); 메서드를 사용하여 박싱을 사용해 Integer객체로 변환한 데이터로부터 Integer[] 배열을 생성합니다. toArray 메서드는 스트림의 모든 요소를 배열로 수집하는데, 여기서 Integer[]::new는 생성할 배열의 타입과 크기를 결정하는 생성자 참조입니다. 결과적으로, 이 코드는 Stream<Integer>의 모든 요소를 포함하는 새로운 Integer[] 배열을 생성하는 일련의 과정을 수행합니다.
.toArray(Integer[]::new) 는 무엇인가?
Integer[]::new는 람다식을 더 간결하게 표현한 Java의 메소드 참조(method reference) 문법 중 하나로, 특정 타입의 새 배열을 생성하는 생성자 참조입니다. 이 경우 Integer[]::new는 Integer 타입의 새 배열을 생성하는 생성자를 참조합니다.
IntStream.of(arr).boxed()를 통해 생성한 Integer 객체 배열 스트림의 요소들을 toArray 메소드가 수집한 후
Integer[]::new 를 통해 배열의 생성자 참조를 인자로 받아, 수집한 객체 배열 스트림을 Integer 타입의 배열로 만들게 됩니다.
한마디로 요약하자면, toArray는 스트림의 요소들을 해당 타입의 배열로 수집하고 반환하는 기능을 가진 메서드입니다.
메서드 참조란?
메소드 참조(Method Reference)
메소드 참조는 Java 8에서 도입된 기능으로, 메소드나 생성자를 하나의 값처럼 참조할 수 있게 해줍니다. 메소드 참조는 람다 표현식을 더 간단하게 표현할 수 있게 해주며, 다음과 같은 형태를 가집니다.
- 클래스명::메소드명
- 클래스명::new (생성자 참조)
Integer[]::new는 실질적으로 다음과 같은 람다 표현식의 축약형입니다
size -> new Integer[size]
이 람다 표현식은 배열의 크기를 인자로 받아 Integer 타입의 새 배열을 생성합니다. 메소드 참조 Integer[]::new를 사용함으로써, toArray 메소드에 필요한 배열 생성자를 간결하게 생성할 수 있습니다.
이 코드의 전체적인 흐름을 다시 정리하자면 아래와 같습니다.
전체적인 흐름:
- int[] 배열이 주어졌을 때, IntStream.of(arr)을 통해 이 배열을 IntStream으로 변환합니다.
- .boxed()를 통해 IntStream 내의 각 int 값을 Integer 객체로 변환하며, 이로 인해 Stream<Integer>가 됩니다.
- 마지막으로 .toArray(Integer[]::new)를 사용하여 변환된 스트림을 다시 Integer[] 배열로 수집합니다.
위와 같은 과정을 거쳐 생성된 Integer[] 배열을 이용해, maxNumber의 인덱스값을 찾는 작업을 아래와 같이 수행하면 끝입니다.
Arrays.asList(boxedArray).indexOf(maxNumber) + 1;
위에서 만든 Integer배열을 변수 boxedArray에 담고, 이 값을 최종적으로 indexOf 메서드를 사용해서 몇 번째 요소인지 확인하는 역할을 합니다.
먼저 Arrays.asList(boxedArray)를 통해 Integer[] 배열을 List<Integer>로 변환합니다. 이렇게 변환한 리스트에서 indexOf() 메서드를 사용해 maxNumber의 인덱스를 찾습니다. 이렇게 하면 최댓값 maxNumber가 원래 배열 arr에서 몇 번째 위치하는지를 찾아낼 수 있게 됩니다
주의할 점은 배열의 인덱스는 0부터 시작하기 때문에, 문제에서 요구하는 "몇 번째 수인지"를 출력하기 위해서는 +1을 해줘야 합니다.
다른 사람의 풀이
index값을 같이 계산하기
제가 했던 코드보다 훨씬 간단하고 효율적인 것 같습니다.
Scanner 객체를 써서 입력할 때에는 원소의 개수가 9개로 고정이므로 직접 초기화를 해줬고,
for문으로 배열을 돌면서 최대값 변수 max에 값을 입력해주면서, 동시에 index 변수에도 값을 넣어줘서 최대값과, 해당 최대값이 위치한 인덱스를 같이 구해줍니다.
이외에 count 변수를 추가한 다음, for문에서 count++를 해주면서 최종값을 index 변수에 대입해주는 방식도 있습니다.
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int[] arr = { in.nextInt(), in.nextInt(), in.nextInt(),
in.nextInt(), in.nextInt(), in.nextInt(),
in.nextInt(), in.nextInt(), in.nextInt() };
in.close();
int max = 0;
int index = 0;
for(int i = 0 ; i < 9 ; i++) {
if(value > max) {
max = value;
index = (i + 1) ;
}
}
System.out.print(max + "\n" + index);
}
}
결론
더 간결하고 더 효율적인 코드를 만드는 것도 실력이라는 것을 깨달았습니다.
괜히 쓸데없이 복잡한 방식을 찾아서 사서고생했다는 느낌이 드는군요. 문제의 의도는 이게 아니었을 거 같은데...아무튼 반성합니다.
※ 참고문서 :
'알고리즘(코테) > 백준(BACKJOON) 알고리즘 - 자바' 카테고리의 다른 글
| [백준(BACKJOON) 알고리즘] 10818번 자바 문제풀이 - 최소, 최대 (0) | 2024.02.18 |
|---|---|
| [백준(BACKJOON) 알고리즘] 10871번 자바 문제풀이 - Scanner VS BufferedReader (0) | 2024.02.14 |
| [백준(BACKJOON) 알고리즘] 10807번 자바 문제풀이 (0) | 2024.02.09 |
댓글