Классы-утилиты

В этой главе описаны средства, облегчающие работу с часто применяемыми конструкциями, в числе которых массивы, даты, случайные числа.

Работа с массивами

В классе Arrays из пакета java.util собрано множество методов для работы с массивами. Их можно разделить на несколько больших групп.

Сортировка массива

Восемнадцать статических методов класса Arrays сортируют массивы с разными типами числовых элементов в порядке возрастания чисел или просто объекты в их естественном порядке.

Восемь из них имеют простой вид:

static void sort(type[] a);

где type может быть один из семи примитивных типов: byte, short, int, long, char, float, double — или тип Object.

Восемь методов с теми же типами сортируют часть массива от индекса from включительно до индекса to исключительно:

static void sort(type[] a, int from, int to);

Оставшиеся два метода сортировки упорядочивают массив или его часть с элементами типа Object по правилу, заданному объектом c, реализующим интерфейс Comparator:

static void sort(Object[] a, Comparator c);

static void sort(Object[] a, int from, int to, Comparator c);

Бинарный поиск в массиве

После сортировки массива можно организовать в нем бинарный поиск элемента element одним из восемнадцати статических методов поиска.

Восемь методов имеют вид:

static int binarySearch(type[] a, type element);

где type один из семи примитивных типов (byte, short, int, long, char, float, double)

или тип Object.

Восемь методов сортируют часть массива, начиная от элемента с индексом from включительно до элемента с индексом to исключительно:

static int binarySearch(type[] a, int from, int to, type element);

Оставшиеся два метода поиска применяют настраиваемые типы и имеют более сложный вид:

static <T> int binarySearch(T[] a, T element, Comparator<? Super T> c); static <T> int binarySearch(T[] a, int from, int to, T element,

Comparator<? Super T> c);

Они отыскивает элемент element в массиве или его части, отсортированном в порядке, заданном объектом c.

Методы поиска возвращают индекс найденного элемента массива. Если элемент не найден, то возвращается отрицательное число, абсолютная величина которого означает индекс, с которым элемент был бы вставлен в массив в заданном порядке.

Заполнение массива

Восемнадцать статических методов класса Arrays заполняют массив или часть массива указанным значением value:

static void fill(type[], type value);

static void fill(type[], int from, int to, type value);

где type-один из восьми примитивных типов или тип Object.

Копирование массива

Восемь методов класса Arrays, написанных для всех примитивных типов, обозначенных здесь словом type:

static type[] copyOf(type[] a, int newLength);

копируют массив a, возвращая ссылку на копию массива. Они обрезают массив a до длины newLength, если newLength меньше длины массива a, или дополняют массив нулями, если его длина меньше newLength.

Следующий метод использует настраиваемый тип:

static <T> T[] copyOf(T[] a, int newLength);

Еще один метод копирования позволяет изменить тип массива:

static <T, U> T[] copyOf(U[] a, int newLength, Class<? extends T[]> newType);

При несовместимости старого и нового типов возникает исключительная ситуация.

Часть массива, начиная от элемента с индексом from и заканчивая перед элементом с индексом to, можно скопировать одним из десяти методов copyOfRange (). Восемь из них написаны для примитивных типов. Они имеют вид:

static type[] copyOfRange(type[] a, int from, int to);

Индекс to может оказаться больше длины массива, в таком случае массив-копия будет дополнен нулями.

Девятый метод использует настраиваемый тип массива:

static <T> T[] copyOfRange(T[] a, int newLength, int from, int to);

Десятый метод может изменить тип копируемого массива:

static <T, U> T[] copyOfRange(U[] a, int from, int to, Class<? extends T[]> newType);

При несовместимости старого и нового типов возникает исключительная ситуация.

Сравнение массивов

Девять статических логических методов класса Arrays сравнивают массивы:

static boolean equals(type[] a1, type[] a2);

где type-один из восьми примитивных типов или тип Object.

Массивы считаются равными и возвращается true, если они имеют одинаковую длину, и равны все элементы массивов с одинаковыми индексами.

Еще один метод сравнения массивов:

static boolean deepEquals(Object[] a1, Object[] a2);

полезен для сравнения многомерных массивов, поскольку он сравнивает элементы массивов с любым количеством индексов.

Представление массива строкой

Девять статических методов класса Arrays преобразуют массив в строку:

static String toString(type[] a);

где type-один из восьми примитивных типов или тип Object.

В формируемой строке массив записывается в квадратных скобках, а его элементы перечисляются через запятую и пробел. Каждый элемент преобразуется в строку методом String.valueOf (type).

Еще один метод преобразования массива в строку:

static String deepToString(Object[] a);

применяется для преобразования многомерных массивов. Он рекурсивно просматривает их подмассивы с любым количеством индексов.

Получение хеш-кода массива

Девять статических методов класса Arrays вычисляют хеш-код массива:

static int hashCode(type[] a);

где type один из восьми примитивных типов или тип Object.

Сначала массив представляется списком типа List, а затем вычисляется хеш-код списка методом List.hashCode ().

Еще один метод:

static int deepHashCode(Object[] a);

применяется для вычисления хеш-кода многомерных массивов. Он просматривает их подмассивы с любым количеством индексов.

В листинге 7.1 приведен простой пример работы с некоторыми из методов класса

Arrays.

Листинг 7.1. Применение методов класса Arrays

import java.util.*; class ArraysTest{

public static void main(String[] args){

int[] a = {34, -45, 12, 67, -24, 45, 36, -56}; Arrays.sort(a);

for (int i: a)

System.out.print(a[i] + " ");

System.out.println();

Arrays.fill(a, Arrays.binarySearch(a, 12), a.length, 0);

for (int i: a)

System.out.print(a[i] + " ");

System.out.println();

}

}

Локальные установки

Некоторые данные — даты, время — традиционно представляются в различных местностях по-разному. Например, дата в России выводится в формате число.месяц.год (через точку): 27.06.2011. В США принята запись месяц/число/год (через наклонную черту): 06/27/11.

Совокупность таких форматов для данной местности, как говорят на жаргоне "локаль", хранится в объекте класса Locale из пакета java.util. Для создания такого объекта достаточно знать язык language и местность country. Иногда требуется третья характеристика — вариант variant, определяющая программный продукт, например: "win", "mac",

"POSIX".

По умолчанию местные установки определяются операционной системой и читаются из системных свойств.

Посмотрите на следующие строки (см. также рис. 6.2):

user.language = ru // Язык — русский

user.region = RU // Местность — Россия

file.encoding = Cp1251 // Байтовая кодировка — CP1251

Они определяют русскую локаль и локальную кодировку байтовых символов. Локаль, установленную по умолчанию на той машине, где выполняется программа, можно выяснить статическим методом Locale.getDefault ( ).

Чтобы работать с другой локалью, ее надо прежде всего создать. Для этого в классе Locale есть два конструктора:

Locale(String language, String country);

Locale(String language, String country, String variant);

Параметр language — это строка из двух строчных букв, определенная стандартом ISO639, например: "ru", "fr", "en". Параметр country — строка из двух прописных букв, определенная стандартом ISO3166, например: "ru", "us", "gb". Параметр variant не определяется стандартом, это может быть, например, строка "Traditional".

Локаль часто указывают одной строкой "ru_RU", "en_GB", "en_US", "en_CA" и т. д.

После создания локали можно сделать ее локалью по умолчанию статическим методом:

Locale.setDefault(Locale newLocale);

Несколько статических методов класса Locale позволяют получить сведения о локали по умолчанию или локали, заданной параметром locale:

? String getCountry() — стандартный код страны из двух букв;

? String getDisplayCountry() — страна записывается словом, обычно выводящимся на экран;

? String getDisplayCountry(Locale locale) — то же для указанной локали.

Такие же методы есть для языка и варианта.

Можно просмотреть список всех локалей, определенных для данной JVM (Java Virtual Machine, виртуальная машина Java), и их параметров, выводимый в стандартном виде:

Locale[] getAvailableLocales();

String[] getISOCountries();

String[] getlSOLanguages();

Установленная локаль в дальнейшем используется при выводе данных в местном формате.

Работа с датами и временем

Методы работы с датами и показаниями времени собраны в два класса из пакета

java.util: Calendar и Date.

Объект класса Date хранит число миллисекунд, прошедших с 1 января 1970 г. 00:00:00 по Гринвичу. Это "день рождения" операционной системы UNIX, он называется "Epoch".

Класс Date удобно использовать для отсчета промежутков времени в миллисекундах.

Получить текущее число миллисекунд, прошедших с момента Epoch на той машине, где выполняется программа, можно статическим методом System.currentTimeMillis ( ).

В классе Date два конструктора. Конструктор Date () заносит в создаваемый объект текущее время машины, на которой выполняется программа, по системным часам, а конструктор Date (long millisec) — указанное число.

Получить значение, хранящееся в объекте, можно методом long getTime (), установить новое значение — методом setTime(long newTime).

Три логических метода сравнивают отсчеты времени:

? boolean after(long when) — возвращает true, если время when больше данного;

? boolean before(long when) — возвращает true, если время when меньше данного;

? boolean after(Object when) — возвращает true, если when — объект класса Date и времена совпадают.

Еще два метода, сравнивая отсчеты времени, возвращают отрицательное число типа int, если данное время меньше параметра when; нуль, если времена совпадают; положительное число, если данное время больше параметра when:

? int compareTo(Date when);

? int compareTo (Object when) — если when не относится к объектам класса Date, создается исключительная ситуация.

Преобразование миллисекунд, хранящихся в объектах класса Date, в текущее время и дату производится методами класса Calendar.

Часовой пояс и летнее время

Методы установки и изменения часового пояса (time zone), а также летнего времени (Daylight Savings Time, DST), собраны в абстрактном классе TimeZone из пакета java.util. В этом же пакете есть его реализация-подкласс SimpleTimeZone.

В классе SimpleTimeZone три конструктора, но чаще всего объект создается статическим методом getDefault (), возвращающим часовой пояс, установленный на машине, выполняющей программу.

В этих классах множество методов работы с часовыми поясами, но в большинстве случаев требуется только узнать часовой пояс на машине, выполняющей программу, статическим методом getDefault (), проверить, осуществляется ли переход на летнее время, логическим методом useDaylightTime() , и установить часовой пояс методом

setDefault(TimeZone zone).

Класс Calendar

Класс Calendar — абстрактный, в нем собраны общие свойства большинства календарей: юлианского, григорианского, лунного. В Java API пока есть только одна его реализация — подкласс GregorianCalendar.

Поскольку Calendar — абстрактный класс, его экземпляры создаются четырьмя статическими методами по заданной локали и/или часовому поясу:

Calendar getInstance();

Calendar getInstance(Locale loc);

Calendar getInstance(TimeZone tz);

Calendar getInstance(TimeZone tz, Locale loc);

Для работы с месяцами определены целочисленные константы от January до December, а для работы с днями недели — константы от Monday до Sunday.

Первый день недели можно узнать методом int getFirstDayOfWeek(), а установить — методом setFirstDayOfWeek(int day), например:

setFirstDayOfWeek(Calendar.MONDAY);

Остальные методы позволяют просмотреть время и часовой пояс или установить их.

Метод get(int field) возвращает элемент календаря, заданный параметром field. Для этого параметра в классе Calendar определены следующие статические целочисленные

константы: ERA WEEK OF YEAR DAY OF WEEK SECOND YEAR WEEK OF MONTH DAY OF WEEK IN MONTH MILLISECOND MONTH DAY OF YEAR HOUR OF DAY ZONE OFFSET DATE DAY OF MONTH MINUTE DST_OFFSET Метод set (int field, int value), использующий эти константы, устанавливает соответствующие значения даты и времени, оставляя остальные значения без изменения. Еще

несколько методов set () устанавливают дату и время по дням, часам, минутам и другим элементам.

Метод setTime(Date d), наиболее часто применяемый для заполнения календаря, устанавливает в календарь все элементы даты d полностью.

Подкласс GregorianCalendar

В григорианском календаре две целочисленные константы определяют эры: bc (Before Christ) и ad (Anno Domini).

Семь конструкторов определяют календарь по времени, часовому поясу и/или локали:

GregorianCalendar();

GregorianCalendar(int year, int month, int date);

GregorianCalendar(int year, int month, int date, int hour, int minute); GregorianCalendar(int year, int month, int date, int hour, int minute, int second); GregorianCalendar(Locale loc);

GregorianCalendar(TimeZone tz);

GregorianCalendar(TimeZone tz, Locale loc);

После создания объекта следует определить дату перехода с юлианского календаря на григорианский календарь методом setGregorianChange(Date date). По умолчанию это 15 октября 1582 г. На территории России переход на григорианский календарь был осуществлен 14 февраля 1918 г., значит, создание объекта greg надо выполнить так:

GregorianCalendar greg = new GregorianCalendar(); greg.setGregorianChange(new

GregorianCalendar(1918, Calendar.FEBRUARY, 14).getTime());

Узнать, является ли год високосным в григорианском календаре, можно логическим методом isLeapYear ().

Представление даты и времени

Различные способы представления дат и показаний времени можно осуществить методами, собранными в абстрактный класс DateFormat и его подкласс SimpleDateFormat из пакета j ava. text.

Класс DateFormat предлагает четыре стиля представления даты и времени:

? short — представляет дату и время в коротком числовом виде: 27.07.11 17:32; в локали США: 07/27/11 5:32 PM;

? medium — задает год четырьмя цифрами и показывает секунды: 27.07.2011 17:32:45; в локали США месяц представляется тремя буквами;

? long — представляет месяц словом и добавляет часовой пояс:

27 июль 2011 г. 17:32:45 GMT+03:00;

? full — в русской локали таков же, как и стиль long; в локали США добавляется еще день недели.

Есть еще стиль default, совпадающий со стилем medium.

При создании объекта класса simpleDateFormat можно задать в конструкторе шаблон, определяющий какой-либо другой формат, например:

SimpleDateFormat sdf = new SimpleDateFormat("dd-MM-yyyy hh.mm");

System.out.println(sdf.format(new Date()));

Получим вывод в таком виде: 27-07-2011 17.32.

В шаблоне буква d означает цифру дня месяца, M — цифру месяца, y — цифру года, h — цифру часа, m — цифру минут. Полностью обозначения для шаблона указаны в документации по классу SimpleDateFormat.

Эти буквенные обозначения можно изменить с помощью класса DateFormatSymbols.

Не во всех локалях можно создать объект класса SimpleDateFormat. В таких случаях используются статические методы getinstance () класса DateFormat, возвращающие объект класса DateFormat. Параметрами этих методов служат стиль представления даты и времени и, может быть, локаль.

После создания объекта метод format() класса DateFormat возвращает строку с датой и временем, согласно заданному стилю. В качестве аргумента задается объект класса

Date.

Например:

System.out.println("LONG: " + DateFormat.getDateTimeInstance( DateFormat.LONG, DateFormat.LONG).format(new Date()));

или

System.out.println("FULL: " + DateFormat.getDateTimeInstance(

DateFormat.FULL,DateFormat.FULL, Locale.US).format(new Date()));

Получение случайных чисел

Получить случайное неотрицательное число, строго меньшее единицы, в виде типа double можно статическим методом random( ) из класса java.lang.Math.

При первом обращении к этому методу создается генератор псевдослучайных чисел, который используется потом при получении следующих случайных чисел.

Более серьезные действия со случайными числами можно организовать с помощью методов класса Random из пакета java.util. В классе два конструктора:

? Random(long seed) — создает генератор псевдослучайных чисел, использующий для начала работы число seed;

? Random () — выбирает в качестве начального значения текущее время.

Создав генератор, можно получать случайные числа соответствующего типа методами nextBoolean (), nextDouble(), nextFloat(), nextGaussian(), nextInt(), nextLong(),

nextint(int max) или записать сразу последовательность случайных чисел в заранее определенный массив байтов bytes методом nextBytes (byte [ ] bytes).

Вещественные случайные числа равномерно располагаются в диапазоне от 0,0 включительно до 1,0 исключительно. Целые случайные числа равномерно распределяются по всему диапазону соответствующего типа за одним исключением: если в аргументе указано целое число max, то диапазон случайных чисел будет от нуля включительно до max исключительно.

Копирование массивов

Для копирования массивов, кроме новых методов copyOf () и copyOfRange () класса Arrays ( ), можно применить статический метод копирования массивов из класса System пакета java.lang, который использует сама исполняющая система Java. Этот метод действует быстро и надежно, его удобно применять в программах. Синтаксис:

static void arraycopy(Object src, int src ind, Object dest, int dest ind, int count);

Из массива, на который указывает ссылка src, копируется count элементов, начиная с элемента с индексом src_ind, в массив, на который указывает ссылка dest, начиная с его элемента с индексом dest_ind.

Все индексы должны быть заданы так, чтобы элементы лежали в массивах, типы массивов должны быть совместимы, а примитивные типы обязаны полностью совпадать. Ссылки на массивы не должны быть равны null.

Ссылки src и dest могут совпадать, при этом для копирования создается промежуточный буфер. Метод можно использовать, например, для сдвига элементов в массиве.

После выполнения

int [ ] arr = {5, 6, 7, 8, 9, 1, 2, 3, 4, 5, -3, -7};

System.arraycopy(arr, 2, arr, 1, arr.length — 2);

получим {5, 7, 8, 9, 1, 2, 3, 4, 5, -3, -7, -7}.

Взаимодействие с системой

Класс System позволяет осуществить и некоторое взаимодействие с системой во время выполнения программы (run time). Но кроме него для этого есть специальный класс

Runtime.

Класс Runtime содержит некоторые методы взаимодействия с JVM во время выполнения программы. Каждое приложение может получить только один экземпляр данного класса статическим методом getRuntime (). Все вызовы этого метода возвращают ссылку на один и тот же объект.

Методы freeMemory( ) и totalMemory( ) возвращают количество свободной и всей памяти, находящейся в распоряжении JVM для размещения объектов, в байтах, в виде числа типа long. Не стоит полностью полагаться на эти числа, поскольку количество памяти меняется динамически.

Метод exit(int status) запускает процесс останова JVM и передает операционной системе статус завершения status. По соглашению, ненулевой статус означает ненормальное завершение. Удобнее использовать аналогичный метод класса System, который является статическим.

Метод halt(int status) осуществляет немедленный останов JVM. Он не завершает запущенные процессы нормально и должен использоваться только в аварийных ситуациях.

Метод loadLibrary(String libName) позволяет подгрузить динамическую библиотеку во время выполнения по ее имени libName.

Метод load (String fileName) подгружает динамическую библиотеку по имени файла fileName, в котором она хранится.

Впрочем, вместо этих методов удобнее использовать статические методы класса System с теми же именами и параметрами.

Метод gc () запускает процесс освобождения ненужной оперативной памяти (garbage collection). Этот процесс периодически запускается самой виртуальной машиной Java и выполняется на фоне с небольшим приоритетом, но можно его запустить и из программы. Опять-таки здесь удобнее использовать статический метод System.gc ().

Наконец, несколько методов exec () запускают в отдельных процессах исполнимые файлы. Аргументом этих методов служит командная строка исполнимого файла.

Например, Runtime.getRuntime().exec("notepad") запускает текстовый редактор Notepad (Блокнот) на платформе MS Windows.

Методы exec () возвращают экземпляр класса Process, позволяющего управлять запущенным процессом. Методом destroy( ) можно остановить процесс, методом exitValue ( ) получить его код завершения. Метод waitFor() приостанавливает основной подпроцесс до тех пор, пока не закончится запущенный процесс.