В очередях и стеках нас больше всего интересует способ хранения информации и обращения к ней. И стеки, и очереди представляют собой механизмы доступа к данным, в которых хранение и извлечение информации поддерживается самой структурой, а не реализуется в программе. Такое сочетание способов хранения и обработки данных лучше всего реализуется в рамках класса, поэтому в данном проекте предстоит создать простой класс очереди.
В очереди поддерживаются две основные операции: размещение и извлечение. При выполнении операции размещения новый элемент помещается в конец очереди, а при операции извлечения очередной элемент извлекается из начала очереди. Операции с очередью являются истощающими: элемент, однажды извлеченный из очереди, не может быть извлечен из нее повторно. Очередь может быть переполнена, когда в ней не остается места для новых элементов. Но очередь может быть и пуста, когда в ней нет ни одного элемента.
И последнее замечание: существуют два типа очередей — циклические и нециклические. В циклической очереди элементы массива, на основе которого она создана, могут использоваться повторно по мере удаления данных. Нециклическая очередь не позволяет повторно использовать элементы, поэтому со временем пространство для хранения новых элементов исчерпывается. Нециклическую очередь создать намного проще, чем циклическую, поэтому именно ее мы и реализуем в данном примере для опробования. Но если немного подумать, то нециклическую очередь можно без особого труда превратить в циклическую.
Последовательность действий
Создайте новый файл QDemo.java.
Очередь можно организовать разными способами. Мы создадим ее на основании массива, выступающего в роли хранилища данных, помещаемых в очередь. Для доступа к массиву будут использованы два индекса. Индекс размещения данных определяет, в каком месте будет размещен следующий элемент очереди. А индекс извлечения данных указывает место, откуда должен быть извлечен очередной элемент очереди. Напомним, что операция извлечения является истощающей и не позволяет извлечь один и тот же элемент дважды. Создаваемая здесь очередь предназначена для хранения символов, но та же самая логика ее организации может быть использована для размещения данных любых типов, в том числе объектов. Итак, начните создание класса очереди Queue со следующих строк кода: class Queue { char q[]; // Массив для хранения элементов очереди int putloc, getloc; // Индексы размещения и извлечения элементов очереди }
Конструктор класса Queue создает очередь заданного размера. Его код приведен ниже.Queue(int size) { q = new char[size+1]; // выделить память для очереди putloc = getloc = 0; } Обратите внимание на то, что размер очереди оказывается на единицу больше размера, задаваемого параметром size. Особенности реализации очереди таковы, что один элемент массива остается неиспользованным, поэтому размер массива должен быть на единицу больше размера очереди, создаваемой на его основе. Первоначально индексы размещения и извлечения данных равны нулю.
Метод put (), помещающий элемент в очередь, имеет следующий вид:// поместить символ в очередь void put(char ch) { if(putloc==q.length-1) { System, out .println (11 - Queue is full."); return; } putloc++; q[putloc] = ch; } Прежде всего в теле данного метода проверяется, не переполнена ли очередь. Если значение переменной putloc соответствует последнему местоположению в массиве q, то места для размещения новых элементов в очереди нет. В противном случае переменная putloc инкрементируется и новый элемент располагается в указанном месте массива. Следовательно, переменная putloc всегда содержит индекс элемента, помещенного в очередь последним.
Для извлечения элементов из очереди служит метод get (), код которого приведен ниже.// извлечь символ из очереди char get () { if(getloc == putloc) { System.out.println(" - Queue is empty."); return (char) 0; } getloc++; return q[getloc]; } Сначала в данном методе проверяется, пуста ли очередь. Если значения индексов в переменных getloc и putloc совпадают, то в очереди нет ни одного элемента. Именно поэтому в конструкторе Queue переменные getloc и putloc инициализируются нулевыми значениями. Если очередь не пуста, то переменная getloc инкрементируется и из нее извлекается очередной элемент. Следовательно, переменная getloc содержит индекс последнего извлеченного элемента.
Ниже приведен весь исходный код программы из файла QDemo.java. /* Пример для опробования 5.2. Класс, реализующий очередь, для хранения символов. */ class Queue { char q[]; // Массив для хранения элементов очереди int putloc, getloc; // Индексы размещения и извлечения элементов очереди Queue(int size) { q = new char[size+1]; // выделить память для очереди putloc = getloc = 0; } // поместить символ в очередь void put(char ch) { if(putloc==q.length-1) { System.out.println(" - Queue is full."); return; } putloc++; q[putloc] = ch; } // извлечь символ из очереди char get() { if(getloc == putloc) { System.out.println(" - Queue is empty."); return (char) 0; } getloc++; return q[getloc]; } } // продемонстрировать класс Queue class QDemo { public static void main(String args[]) { Queue bigQ = new Queue (100); Queue smallQ = new Queue(4); char ch; int i; System.out.println("Using bigQ to store the alphabet."); // поместить буквенные символы в очередь bigQ for(i=0; i < 26; i++) bigQ.put((char) ('A' + i)); // извлечь буквенные символы из очереди bigQ и отобразить System.out.print("Contents of bigQ: "); for(i=0; i < 26; i++) { ch = bigQ.get(); if(ch != (char) 0) System.out.print(ch); } System.out.println("\n"); System.out.println("Using smallQ to generate errors."); // использовать небольшую очередь smallQ для генерации ошибок for(i=0; i < 5; i++) { System.out.print("Attempting to store " + (char) ('Z' - i)); smallQ.put((char) ('Z1 - i)); System.out.println(); } System.out.println(); // дополнительные ошибки при обращении к очереди smallQ System.out.print("Contents of smallQ: "); for(i=0; i < 5; i++) { ch = smallQ.get(); if(ch != (char) 0) System.out.print(ch); } } }
Ниже приведен результат выполнения данной программы. Using bigQ to store the alphabet. Contents of bigQ: ABCDEFGHIJKLMNOPQRSTUVWXYZ Using smallQ to generate errors. Attempting to store Z Attempting to store Y Attempting to store X Attempting to store W Attempting to store V - Queue is full. Contents of smallQ: ZYXW - Queue is empty.
Попробуйте самостоятельно усовершенствовать класс Queue таким образом, чтобы в очереди можно было хранить другие типы данных, например значения типа int или double. Разновидность for-each цикла for
При выполнении операций с массивами очень часто возникают ситуации, когда должен быть обработан каждый элемент массива. Например, для расчета суммы всех значений, содержащихся в массиве, нужно обратиться ко всем его элементам. То же самое приходится делать при расчете среднего значения, поиске элемента и решении многих других задач. В связи с тем что задачи, предполагающие обработку всего массива, встречаются очень часто, в Java была реализована еще одна разновидность цикла for, рационализирующая подобные операции с массивами.
Вторая разновидность оператора for реализует цикл типа for-each. В этом цикле происходит последовательное обращение к каждому элементу совокупности объектов (например, массива). За последние годы циклы for-each появились практически во всех языках программирования. Изначально в Java подобный цикл не был предусмотрен и был реализован лишь в пакете JDK 5. Разновидность for-each цикла for называется также расширенным циклом for. В данной книге употребляются оба эти термина.
Ниже приведена общая форма разновидности for-each цикла for. for(тип intr_var : коллекция) блок_операторов
где тип обозначает конкретный тип intr_var — итерационной переменной, в которой сохраняются перебираемые по очереди элементы набора данных, обозначенного как коллекция. В данной разновидности цикла for могут быть использованы разные типы коллекций, но в этой книге рассматриваются только массивы. На каждом шаге цикла очередной элемент извлекается из коллекции и сохраняется в итерационной переменной. Выполнение гщкла продолжается до тех пор, пока не будут получены все элементы коллекции. Таким образом, при обработке массива размером N в расширенном цикле for будут последовательно извлечены элементы с индексами от 0 до N—1.