
배열에서 특정 값을 찾는 가장 쉬운 방법은 배열의 맨 앞에서부터 차례대로 찾아나가는 방법입니다. 이는 순차 탐색(Sequential Search) 혹은 선형 탐색(Linear Search)이라고 하며 최대 n개의 원소들을 확인해야 하므로 O(n)의 시간복잡도를 가집니다.
하지만 배열이 오름 차순이나 내림 차순으로 정렬되어 있음이 보장된다면, 이 정보를 활용해 특정 값을 더 빠르게 찾을 수 있습니다. 예를 들어, 오름 차순으로 정렬된 A = [?, ?, ?, 16, ?, ?, ?] 라는 배열이 존재할 때, 15라는 값이 배열 내 몇 번째에 존재하는지 찾고자 한다고 가정할게요. 이 때, 배열의 3번 인덱스 즉, index = 3을 탐색했을 때, A[3] = 16입니다. 그럼 우리가 찾고 싶은 15라는 값은 적어도 index = 3보다 작은 위치에 존재할 것입니다. 그 다음 0 ~3의 중간값인 index = 1을 탐색했을 때, A[1] = 6이라면 A[2]는 6 ~ 16 사이의 값을 가지고 있다는 것을 확신할 수 있죠. 물론 그 값이 15인지 아닌지는 직접 탐색하기 전까지는 알 수 없지만요.
이처럼 정렬된 배열을 절반으로 나누어 찾고자 하는 값이 존재하는 범위를 찾아 탐색하는 방식을 이분 탐색(Binary Search) 혹은 이진 탐색이라고 합니다. n개의 원소로 이루어진 배열을 절반으로 나눈 뒤, 찾고자 하는 값이 왼쪽 배열, 오른쪽 배열 중 어디 있는지 찾아나가면 되기 때문에 시간복잡도는 O(log n)입니다. n = 2⁹⁹⁹⁹⁹ 처럼 큰 배열도 99,999번만 탐색하면 찾을 수 있는 획기적인 방법이에요.
자바는 배열 관련 클래스 Arrays의 Arrays.binarySearch() 메소드, 자료 구조 관련 클래스 Collections의 Collections.binarySearch() 메소드로 이분 탐색을 제공합니다. 특히 Collections에서 제공하는 이분 탐색 메소드가 재미있게 구현되어 있어 이 포스트를 작성하는 동기가 되었습니다. 그럼 Arrays.binarySearch() 부터 알아봅시다!
Arrays 클래스는 java.util 패키지의 일부로, 배열을 다루기 위한 다양한 메소드를 제공합니다. 이분 탐색 뿐만 아니라 배열을 정렬하는 Arrays.sort() 메소드, 배열을 특정 값으로 채우는 Arrays.fill() 메소드, 배열을 복사하는 Arrays.copyOf() 메소드 등도 말이죠. 특히 배열을 정렬하는 메소드 구현이 재미있는데, 이건 추후에 다른 포스트에서 소개하겠습니다.
Arrays 클래스에서 주목할 만한 또 다른 점은 boolean을 제외한 int, long, short, char, byte, double, float 와 같은 원시 타입 배열에 대해서 각각 메소드가 구현되어 있다는 점입니다. 즉, 동일한 이름의 메소드 sort, fill, copyOf, binarySearch 등에 대해서 매개변수 타입에 따라 오버로딩되어 있습니다.
JDK 17버전을 사용했습니다. 대표적으로 long 타입에 대한 Arrays.binarySearch()의 구현을 보도록 할게요. 나머지 타입도 구현은 같습니다.

