Основы объектно-ориентированного программирования
Для определения типа объекта, извлеченного из контейнера, можно использовать бинарный оператор instanceof, возвращающего true, если его левый операнд принадлежит классу C, указанному в качестве правого операнда. Такое же значение будет и в тех случаях, когда левый операнд является экземпляром любого подкласса класса C, реализует интерфейс C, или принадлежит подклассу, реализующему интерфейс C. В остальных случаях значением данного оператора будет false.
Хранить в динамическом векторе целые или действительные числа, не являющиеся объектами, нельзя. Можно, однако, помещать в вектор объекты типов Integer или Double, соответствующие этим величинам. Подобная техника работы была проиллюстрирована выше на примере класса Enum.
Так как нашей очередной задачей после описания интерфейсов контейнерных классов для нас будет задача их реализации, то мы позволим себе упростить ее. Будем считать, что все рассматриваемые далее контейнеры предназначены для работы не с произвольными объектами, а исключительно с целыми числами.
Стек (см. рис. 10.1) — контейнер, который можно представлять себе в виде трубы с одним запаянным концом, в которую можно добавлять элементы (и вынимать их). Говорят, что стек реализует дисциплину обслуживания LIFO (Last in — first out, последним пришел — первым ушел).
Пример интерфейса
interface Stack { // Пуст ли стек? boolean empty(); // Сделать стек пустым. void clear(); // Добавить число в стек (на вершину). void push(int val); // Удалить число с вершины стека. int pop() throws Exception; // Получить вершину стека (не удаляя ее). int top() throws Exception; }
Из пустого стека нельзя взять элемент. По этой причине при работе методов pop и top может возникнуть исключительная ситуация.
Очередь можно представлять себе в виде трубы с двумя концами, движение по которой возможно лишь в одном направлении — от конца очереди к ее началу (рис. 10.2). Говорят, что очередь реализует дисциплину обслуживания FIFO (First in — first out, первым пришел — первым ушел).
Пример интерфейса
interface Queue { // Пуста ли очередь? boolean empty(); // Сделать очередь пустой. void clear(); // Добавить число в очередь (в конец). void push(int val); // Удалить число из начала очереди. int pop() throws Exception; // Получить начало очереди (не удаляя его). int front() throws Exception; }
Из пустой очереди, как и из стека, элементы извлекать нельзя.
Дек (double ended queue, двусторонняя очередь) — симбиоз стека и очереди. Его можно представлять себе в виде трубы с двумя открытыми концами, с каждым из которых можно работать независимо (рис. 10.3).
Пример интерфейса
interface Deq { // Пуст ли дек? boolean empty(); // Сделать дек пустым. void clear(); // Добавить число в начало дека. void pushFront(int val); // Добавить число в конец дека. void pushBack(int val); // Удалить первый элемент дека. int popFront() throws Exception; // Удалить последний элемент дека. int popBack() throws Exception; // Получить первый элемент дека (не удаляя его). int front() throws Exception; // Получить последний элемент дека (не удаляя его). int back() throws Exception; }
Пустой дек ведет себя аналогично ранее рассмотренным контейнерам.
Понятие множества является общеизвестным, как и основные операции над ним. Напомним только, что если в множество добавляется элемент, который в нем уже содержался, то множество не меняется. Множество остается неизменным и при удалении элемента, которого в нем не было. Так как последнее замечание применимо и к пустому множеству, то при работе с данным контейнером исключительная ситуация возникнуть не может.
Пример интерфейса
interface Set { // Пусто ли множество? boolean empty(); // Сделать множество пустым. void clear(); // Содержится ли число в множестве? boolean search(int val); // Добавить число в множество. void insert(int val); // Удалить число из множества. void erase(int val); }