После того, как массив будет отсортирован, вы сможете быстро найти нужный элемент методом Arrays.binarySearch(). Попытка вызова binarySearch() для несортированного массива приведет к непредсказуемым последствиям. В следующем примере генераторRandomGenerator.Integer заполняет массив, после чего тот же генератор используется для получения искомых значений:
</spoiler> Цикл while генерирует случайные значения как искомые до тех пор, пока одно из них не будет найдено в массиве. Если искомое значение найдено, метод Arrays.binarySearch() возвращает неотрицательный результат. В противном случае возвращается отрицательное значение, представляющее позицию элемента при вставке (при сохранении сортировки массива). Если массив содержит повторяющиеся значения, алгоритм поиска не дает гарантий относительно того, какой именно из дубликатов будет обнаружен. Алгоритм проектировался не для поддержки дубликатов, а для того, чтобы переносить их присутствие. Если вам нужен отсортированный список без повторений элементов, используйте TreeSet (для сохранения порядка сортировки) или LinkedHashSet (для сохранения порядка вставки). Эти классы автоматически берут на себя все детали. Только в ситуациях, критичных по быстродействию, эти классы заменяются массивами с ручным выполнением операций. При сортировке объектных массивов с использованием Comparator (примитивные массивы не позволяют выполнять сортировку с Comparator) необходимо включать тот же объект Comparator, что и при использовании binarySearch() (перегруженной версии). Например, программу StringSorting.java можно модифицировать для выполнения поиска:
//: arrays/AlphabeticSearch.java
// // Поиск с Comparator.
import java.util.*;
import net.mindview.util.*;
public class AlphabeticSearch {
public static void main(String[] args) {
String[] sa = Generated.array(new String[30],
new RandomGenerator.String(5));
Arrays.sort(sa, String.CASE_INSENSITIVE_ORDER);
System.out.println(Arrays.toString(sa));
int index = Arrays.binarySearch(sa, sa[10],
String.CASE_INSENSITIVE_ORDER);
System.out.println("Index: "+ index + "\n"+ sa[index]);
</spoiler> Объект Comparator передается перегруженному методу binarySearch() в третьем аргументе. В приведенном примере успех поиска гарантирован, так как искомое значение выбирается из самого массива.
Резюме
В этой главе вы убедились в том, что язык Java предоставляет неплохую поддержку низкоуровневых массивов фиксированного размера. Такие массивы отдают предпочтение производительности перед гибкостью. В исходной версии Java низкоуровневые массивы фиксированного размера были абсолютно необходимы — не только потому, что проектировщики Java решили включить в язык примитивные типы (также по соображениям быстродействия), но и потому, что поддержка контейнеров в этой версии была крайне ограниченной.
В последующих версиях Java поддержка контейнеров была значительно улучшена. Сейчас контейнеры превосходят массивы во всех отношениях, кроме быстродействия, хотя производительность контейнеров была значительно улучшена. С добавлением автоматической упаковки и параметризации хранение примитивов в контейнерах требует меньших усилий и способствует дальнейшему переходу с низкоуровневых массивов на контейнеры. Так как параметризация открыла доступ к типизованным контейнерам, в этом отношении массивы тоже утратили исходные преимущества.
Как было показано в этой главе, параметризация плохо сочетается с контейнерами. Даже если вам удастся тем или иным способом заставить их работать вместе, во время компиляции вы будете получать предупреждения.
Все эти факторы показывают, что при программировании для последних версий Java следует отдавать предпочтение контейнерам перед массивами. На массивы следует переходить только при критичных требованиях по быстродействию — и только в том случае, если переход принесет ощутимую пользу.