Классы-коллекции

В листинге 5.2 мы разобрали строку на слова. Как их сохранить для дальнейшей обработки?

До сих пор для таких целей мы пользовались массивами. Они удобны, если необходимо быстро обработать однотипные элементы, например просуммировать числа, найти наибольшее и наименьшее значение, отсортировать списки. Но уже для поиска нужных сведений в большом объеме информации массивы неудобны. Для этого лучше использовать другие разновидности хранения данных, например бинарные деревья поиска.

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

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

В языке Java с самых первых версий есть класс Vector, предназначенный для хранения переменного числа элементов самого общего типа Obj ect.

Класс Vector

В классе Vector из пакета java.util хранятся элементы типа Object, а значит, ссылки любого типа. Количество элементов может быть произвольным и не определяться заранее. Элементы получают индексы 0, 1, 2 и т. д. К каждому элементу вектора можно обратиться по индексу, как и к элементу массива.

Кроме количества элементов, называемого размером (size) вектора, есть еще размер буфера — емкость (capacity) вектора. Обычно емкость совпадает с размером вектора, но можно ее увеличить методом ensureCapacity(int minCapacity) или сравнять с размером вектора методом trimToSize ( ).

В Java 2 класс Vector переработан так, чтобы включить его в иерархию классов-коллекций. Для этого добавлено много новых методов, реализующих методы соответствующих интерфейсов-коллекций. Сейчас многие действия можно совершать старыми и новыми методами. Рекомендуется использовать новые методы, поскольку старые могут быть исключены из следующих версий Java.

Как создать вектор

В классе четыре конструктора:

? Vector () — создает пустой объект нулевой длины с емкостью в 10 элементов;

? Vector (int capacity) -создает пустой объект указанной емкости capacity;

? Vector (int capacity, int increment) - формирует пустой объект указанной емкости

capacity и задает число increment, на которое увеличивается емкость при необходимости;

? Vector(Collection c) — вектор создается по указанной коллекции.

Если число capacity отрицательно, то возникает исключительная ситуация.

После создания вектора его можно заполнять элементами. В векторе разрешено хранить объекты разных типов, поскольку на самом деле в нем хранятся не значения объектов, а ссылки на них. Класс Vector настраиваемый, и если предполагается заполнять вектор ссылками одного и того же типа, то этот тип можно задать при создании вектора в шаблоне (generic) коренного типа, в угловых скобках, по такой схеме:

Vector<String> v = new Vector<String>();

или, используя "ромбовидный оператор",

Vector<String> v = new Vector<>();

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

Как добавить элемент в вектор

Метод add (Object element) позволяет добавить элемент в конец вектора (то же делает старый метод addElement (Obj ect element)).

Методом add(int index, Object element) или старым методом insertElementAt(Object element, int index) можно вставить элемент в указанное место index. Элемент, находившийся на этом месте, и все последующие элементы сдвигаются, их индексы увеличиваются на единицу.

Метод addAll(Collection coll) позволяет добавить в конец вектора все элементы коллекции coll.

Методом addAll (int index, Collection coll) возможно вставить в позицию index все элементы коллекции coll.

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

Vector v = new Vector(); v.add(new Date()); v.add("CTpoKa символов"); v.add(new Integer(10)); v.add(20);

Обратите внимание в этом примере на две последние строки. Первая из них записана по канонам работы с коллекцией: в вектор вместо числа 10 заносится ссылка на объект класса Integer, содержащий это число. В последней строке применета автоматическая упаковка типа: в методе add() записывается просто число 20, метод сам создает необходимую ссылку.

Как заменить элемент

Метод set(int index, Object element) заменяет элемент, стоявший в векторе в позиции index, на элемент element (то же самое позволяет выполнить старый метод

setElementAt(Object element, int index)).

Как узнать размер вектора

Количество элементов в векторе всегда можно узнать методом size().

Метод capacity() возвращает емкость вектора.

Логический метод isEmpty() возвращает true, если в векторе нет ни одного элемента.

Как обратиться к элементу вектора

Обратиться к первому элементу вектора можно методом firstElement(), к последнему - методом lastElement (), к любому элементу- методом get (int index) или старым

методом elementAt (int index).

Эти методы возвращают объект класса Object. Перед использованием его следует привести к нужному типу, например:

String s = (String)v.get(1);

Если при создании вектора шаблоном был указан определенный тип элементов, например,

Vector<String> v = new Vector<>(); v.add("First"); v.add("Second");

то возвращается объект именно этого типа и явное приведение типа не требуется, можно написать просто

String s = v.get(1);

Получить все элементы вектора в виде массива типа Object[] можно методами toArray() и toArray(Object[] a). Второй метод заносит все элементы вектора в массив a, если в нем достаточно места.

Как узнать, есть ли элемент в векторе

Логический метод contains(Object element) возвращает true, если элемент element находится в векторе.

Логический метод containsAll(Collection c) возвращает true, если вектор содержит все элементы указанной коллекции.

Как узнать индекс элемента

Четыре метода позволяют отыскать позицию указанного элемента element:

? indexOf (Object element) — возвращает индекс первого появления элемента в векторе;

? indexOf (Obj ect element, int begin) - ведет поиск, начиная с индекса begin включи

тельно;

? lastIndexOf (Obj ect element) — возвращает индекс последнего появления элемента в векторе;

? lastIndexOf (Obj ect element, int start) - ведет поиск от индекса start включительно

к началу вектора.

Если элемент не найден, возвращается число -1.

Как удалить элементы

Логический метод remove(Object element) удаляет из вектора первое вхождение указанного элемента element. Метод возвращает true, если элемент найден и удаление произведено.

Метод remove(int index) удаляет элемент из позиции index и возвращает его в качестве своего результата типа Object.

Аналогичные действия позволяют выполнить старые методы типа void:

removeElement(Object element) и removeElementAt(int index), не возвращающие результата.

Удалить диапазон элементов можно методом removeRange(int begin, int end), не возвращающим результата. Удаляются элементы от позиции begin включительно до позиции end исключительно.

Удалить из данного вектора все элементы коллекции coll возможно логическим методом removeAll(Collection coll).

Удалить последние элементы можно, просто урезав вектор методом setSize(int newSize).

Удалить все элементы, кроме входящих в указанную коллекцию coll, разрешает логический метод retainAll (Collection coll).

Удалить все элементы вектора можно методом clear(), старым методом removeAllElements ( ) или обнулив размер вектора методом setSize (0).

Приведем пример работы с вектором. Листинг 6.1 расширяет листинг 5.2, обрабатывая выделенные из строки слова с помощью вектора.

Листинг 6.1. Работа с вектором

import java.util.*;

class MyParser{

public static void main(String[] args){

Vector<String> v = new Vector<>();

String s = "Строка, которую мы хотим разобрать на слова."; StringTokenizer st = new StringTokenizer(s, " ,.");

while (st.hasMoreTokens()){

// Получаем слово и заносим в вектор

v.add(st.nextToken()) ; // Добавляем в конец вектора

System.out.println(v.firstElement()); // Первый элемент System.out.println(v.lastElement()); // Последний элемент v.setSize(4); // Уменьшаем число элементов

v.add("собрать."); // Добавляем в конец укороченного вектора. v.set(3, "опять"); // Ставим в позицию 3.

for (int i = 0; i < v.size(); i++) // Перебираем весь вектор.

System.out.print(v.get(i) + " ");

System.out.println();

for (String s: v) // Другой способ перебора элементов вектора.

System.out.print(s + " ");

System.out.println();

}

}

Класс Vector является примером того, как можно объекты класса Object, а значит, любые объекты, объединить в коллекцию. Этот тип коллекции упорядочивает и даже нумерует элементы. В векторе есть первый элемент, есть последний элемент. К каждому элементу обращаются непосредственно по индексу. При добавлении и удалении элементов оставшиеся элементы автоматически перенумеровываются.

Класс Stack

Второй пример коллекции-класс Stack-расширяет класс Vector.

Класс Stack из пакета java.util объединяет элементы в стек.

Стек (stack) реализует порядок работы с элементами подобно магазину винтовки — первым выстрелит патрон, положенный в магазин последним, — или подобно железнодорожному тупику — первым из тупика выйдет вагон, загнанный туда последним. Т а-кой порядок обработки называется LIFO (Last In — First Out, последним пришел — первым ушел).

Перед работой создается пустой стек конструктором Stack ().

Затем на стек кладутся и снимаются элементы, причем доступен только "верхний" элемент, тот, что положен на стек последним.

Дополнительно к методам класса Vector класс Stack содержит пять методов, позволяющих работать с коллекцией как со стеком:

? push (Obj ect item) -помещает элемент item в стек;

? pop () — извлекает верхний элемент из стека;

? peek () — читает верхний элемент, не извлекая его из стека;

? empty () — проверяет, не пуст ли стек;

? search(Object item) — находит позицию элемента item в стеке. Верхний элемент имеет позицию 1, под ним элемент 2 и т. д. Если элемент не найден, возвращается -1.

Листинг 6.2 показывает, как можно использовать стек для проверки парности символов в арифметическом выражении, записанном в строку.

Листинг 6.2. Проверка парности скобок

import java.util.*;

class StackTest{

static boolean checkParity(String expression, String open, String close){

Stack<String> stack = new Stack<>();

StringTokenizer st = new StringTokenizer(expression,

" +*/-(){}", true); while (st.hasMoreTokens()){

String tmp = st.nextToken(); if (tmp.equals(open)) stack.push(open); if (tmp.equals(close)) stack.pop();

}

if (stack.isEmpty()) return true; return false;

}

public static void main(String[] args){

System.out.println(

checkParity("a — (b — (c — a) / (b + c) — 2)", "(", ")"));

}

}

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

Еще один пример коллекции совсем другого рода — таблицы — предоставляет класс

Hashtable.