Главная » Java » Создание реализаций итераторов

0

 

   Объекты итераторов находят самое широкое применение, и новый тип итератора может потребоваться даже в том случае, когда конкретная задача не предполагает использования классов коллекций. Следующий код демонстрирует основные приемы реализации типа Iterator на примере класса-итератора (iterator), который исключает из рассмотрения строки с длиной, превышающей заданную:

 

import java.util.*;

 

public class ShortStrings  implements  iterator {

       private iterator strings;       // источник данных private

       private String nextshort;       // null,  если очередной строки нет

       private final   int maxLen;       // Допустимая длина строк

 

       public ShortStrings(lterator strings,   int maxLen)   {

             this.strings = strings;

             this.maxLen = maxLen;

             nextshort = null;

}

public  boolean  hasNext()   {

 if  (nextshort   != null)     // Строка уже найдена

     return  true;

while  (strings.hasNext())   {

    nextshort = (String)  strings.nextO ;

     if  (nextshort.lengthO  <= maxLen)

         return true;

}

nextshort = null;       // Строка не найдена

return false;

}

public Object next()   {

if (nextshort == null  &&  !hasNext())

   throw new NoSuchElementException();

String n = nextshort;       // Запомнить nextshort

nextshort = null;              // Очистить ссылку nextshort

return n;             // возвратить nextshort

}

public void  remove()   {

     throw new unsupportedOperationException();

   }

}

Класс ShortStrings представляет тип итератора, обеспечивающий прием объектов String от другого итератора и возврат тех из них, длина строк которых не превышает заданного предела. Конструктору передаются объект итератора, выполняющий функцию источника данных, и величина допустимой длины строки— оба значения сохраняются в private-полях объекта ShortStrings. Поле nextshort содержит очередную строку, удовлетворяющую заданному ограничению, либо null, если таковая не найдена. Если значение nextshort равно null, метод hasNext предпринимает попытку найти очередную "короткую строку и сохранить ее в поле nextshort. Если в процессе итерации источник данных оказывается исчерпанным, метод возвращает false.

   Метод next выполняет проверку, существует ли очередная строка, удовлетворяющая ограничению, и возвращает такую строку, если она есть, либо выбрасывает исключение типа NoSuchElementException, коль скоро возвратить нечего. Обратите внимание, что всю реальную работу, связанную с поиском строк, выполняет на самом деле метод hasNext, a next просто возвращает результат и присваивает полю nextshort значение null, свидетельствующее о том, что очередная "короткая" строка, если она существует, еще подлежит отысканию.

   Наконец, метод remove не поддерживается рассматриваемой реализацией итератора, и поэтому вызов remove приведет к выбрасыванию исключения типа UnsupportedOperationException.

   Еще несколько небольших замечаний.  Во-первых, метод hasNext, написа ный довольно тщательно,  допускает неоднократное обращение к нему,  минуя next. Такое поведение вполне объяснимо — код-инициатор должен иметь во можность обращаться к hasNext в промежутке между вызовами next столь раз, сколько это необходимо. Во-вторых, столь же аккуратно реализован и метод next — программист, который к нему обращается, вправе полностью «позабыть» о наличии метода hasNext. Хотя подобную практику нельзя признать  хорошей, формально в ней нет ничего предосудительного — вполне возможно, не обращаяськ hasNext, последовательно вызывать next до тех пор, пока не будет сгенерировано исключение, указывающее на то, что источник данных иссяк.

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

it.next();

 it.hasNext();

it. remove();

 

Вообразим, например, что указанный код выполняется в тот момент, когда в коллекции, поддерживаемой итератором, остается только одна "короткая" строка, сопровождаемая несколькими "длинными". Вызов next приводит к возврату последней "короткой" строки, существующей в коллекции. Затем метод hasNext, просматривая коллекцию, обнаруживает, что "коротких" строк больше нет, и возвращает false. При обращении к remove вызывается метод remove итератора-источника, удаляющий последнюю ("длинную") строку, отвергнутую методом hasNext. Это, разумеется, неправильно. Но поскольку код вполне законен, проблему нельзя устранить, просто запретив возможность использования именно такой последовательности инструкций. Мы, как может показаться, зашли в тупик. Однако решение существует и состоит в отказе от возможности построения одного фильтрующего итератора на основе другого объекта Iterator; зато в качестве источника данных мы вправе использовать объект Listlterator, так как тот позволяет вернуться к ранее возвращенной "короткой" строке.

  Методы интерфейса Listlterator следуют контрактам, во многом схожим с теми, которые лежат в основе методов интерфейса Iterator, — мы упоминали об этом раньше. Объекты Listlterator могут быть применены в некоторых обстоятельствах, где допустимо использовать и объекты Iterator. Если вы занимаетесь проектированием прикладных классов широкого назначения, следует везде, где это возможно, пытаться реализовать Listlterator вместо Iterator.

 

Источник: Арнолд, Кен, Гослинг, Джеймс, Холмс, Дэвид. Язык программирования Java. 3-е изд .. : Пер. с англ. – М. : Издательский дом «Вильяме», 2001. – 624 с. : ил. – Парал. тит. англ.

По теме:

  • Комментарии