设计模式——迭代器模式

本文介绍迭代器模式。

概念

提供一种顺序访问聚合对象元素的方法,并且不暴露聚合对象的内部表示。

结构和工作原理

  • Iterater:迭代器接口,定义访问和遍历元素的接口
  • ConcreteIterater:具体迭代器,迭代器的实现类,实现访问遍历元素,且记录遍历时的当前位置。
  • Container:容器接口,负责提供创建具体迭代器
  • ConcreteContainer:具体容器类,实现容器接口

迭代器模式将对聚合中对象的遍历行为进行抽象,将存储和遍历数据的职责分离,将遍历的职责赋予迭代器类,这样在不暴露聚合内部结构的情况下,提供了透明访问聚合内部数据的能力。

适用场景:

  • 访问一个聚合对象的内容而无需暴露它的内部表示
  • 需要为聚合对象提供多种遍历方式
  • 为遍历不同的聚合结构提供统一的接口

代码示例

@Setter
@Getter
@AllArgsConstructor
public class Human {

private String name;

}
public interface Iterator<E> {

boolean hasNext();

E next();

boolean hasPrevious();

E previous();

int nextIndex();

int previousIndex();

void set(E e);

void add(E e);

default void remove() {
throw new UnsupportedOperationException("remove");
}

default void forEachRemaining(Consumer<? super E> action) {
Objects.requireNonNull(action);
while (hasNext()) {
action.accept(next());
}
}
}
public interface Container<E> {

Iterator<E> getIterator();

}
@SuppressWarnings("unchecked")
public class CustomContainer<E> implements Container<E> {

private static final int DEFAULT_CAPACITY = 10;

private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

private static final Object[] DEFAULT_CAPACITY_EMPTY_ELEMENT = {};

private Object[] elements;

private int size;

private transient int modCount;

public CustomContainer() {
this.elements = DEFAULT_CAPACITY_EMPTY_ELEMENT;
}

public boolean add(E e) {
ensureCapacityInternal(size + 1);
elements[size++] = e;
return true;
}

public int size() {
return this.size;
}

public boolean isEmpty() {
return this.size == 0;
}

@SuppressWarnings("unchecked")
public E remove(int index) {
rangeCheck(index);
modCount++;
E oldValue = (E) elements[index];

int numMoved = size - index - 1;
if (numMoved > 0) {
System.arraycopy(elements, index + 1, elements, index, numMoved);
}
elements[--size] = null;
return oldValue;
}

@Override
public Iterator<E> getIterator() {
return new CustomIterator(0);
}


public CustomIterator customIterator(int index) {
if (index < 0 || index > size)
throw new IndexOutOfBoundsException("Index: " + index);
return new CustomIterator(index);
}


public E set(int index, E element) {
rangeCheck(index);
E oldValue = (E) elements[index];
elements[index] = element;
return oldValue;
}

public void add(int index, E element) {
rangeCheckForAdd(index);

ensureCapacityInternal(size + 1); // Increments modCount!!
System.arraycopy(elements, index, elements, index + 1,
size - index);
elements[index] = element;
size++;
}

private void rangeCheckForAdd(int index) {
if (index > size || index < 0) {
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
}

private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULT_CAPACITY_EMPTY_ELEMENT) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}

private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elements, minCapacity));
}

private void ensureExplicitCapacity(int minCapacity) {
modCount++;

// overflow-conscious code
if (minCapacity - elements.length > 0)
grow(minCapacity);
}

private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elements.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0) {
newCapacity = minCapacity;
}
if (newCapacity - MAX_ARRAY_SIZE > 0) {
newCapacity = hugeCapacity(minCapacity);
}
// minCapacity is usually close to size, so this is a win:
elements = CustomContainer.copyOf(elements, newCapacity);
}

@SuppressWarnings("unchecked")
public static <T> T[] copyOf(T[] original, int newLength) {
return (T[]) Arrays.copyOf(original, newLength, original.getClass());
}

private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) {
// overflow
throw new OutOfMemoryError();
}
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}

private void rangeCheck(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

private String outOfBoundsMsg(int index) {
return "Index: " + index + ", Size: " + size;
}

public class CustomIterator implements Iterator<E> {

//index of next element to return
int cursor;
// index of last element returned
int lastRet = -1;

int expectedModCount = modCount;

CustomIterator(int index) {
cursor = index;
}

@Override
public boolean hasNext() {
return cursor != size;
}

@Override
public E next() {
checkForComodification();
int i = cursor;
if (i >= size) {
throw new NoSuchElementException();
}
Object[] elementData = CustomContainer.this.elements;
if (i >= elementData.length) {
throw new ConcurrentModificationException();
}
cursor = i + 1;
return (E) elementData[lastRet = i];
}

@Override
public boolean hasPrevious() {
return cursor != 0;
}

@Override
public E previous() {
checkForComodification();
int i = cursor - 1;
if (i < 0) {
throw new NoSuchElementException();
}
Object[] elements = CustomContainer.this.elements;
if (i >= elements.length) {
throw new ConcurrentModificationException();
}
cursor = i;
return (E) elements[lastRet = i];
}

@Override
public int nextIndex() {
return cursor;
}

@Override
public int previousIndex() {
return cursor - 1;
}

@Override
public void set(E e) {
if (lastRet < 0) {
throw new IllegalStateException();
}
checkForComodification();
try {
CustomContainer.this.set(lastRet, e);
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}

}

@Override
public void add(E e) {
checkForComodification();
try {
int i = cursor;
CustomContainer.this.add(i, e);
cursor = i + 1;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}

@Override
public void remove() {
if (lastRet < 0) {
throw new IllegalStateException();
}
checkForComodification();
try {
CustomContainer.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException e) {
throw new ConcurrentModificationException();
}
}

@Override
public void forEachRemaining(Consumer<? super E> consumer) {
Objects.requireNonNull(consumer);
final int size = CustomContainer.this.size;
int i = cursor;
if (i >= size) {
return;
}
final Object[] elements = CustomContainer.this.elements;
if (i > elements.length) {
throw new ConcurrentModificationException();
}
while (i != size && modCount == expectedModCount) {
consumer.accept((E) elements[i++]);
}
cursor = i;
lastRet = i - 1;
checkForComodification();
}

final void checkForComodification() {
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}
}

}
@Slf4j
public class IteratorClient {

public static void main(String[] args) {
int i = 0;
CustomContainer<Human> customContainer = new CustomContainer();

List<Human> humanList = IntStream
.range(0, 9)
.mapToObj(num -> new Human(String.valueOf(num)))
.collect(Collectors.toList());

humanList.forEach(customContainer::add);

while (customContainer.customIterator(i).hasNext()) {
log.info("IteratorClient- main- {}", customContainer.customIterator(i).cursor);
i++;
}
}
}

参考资料

https://www.runoob.com/design-pattern/iterator-pattern.html
https://blog.csdn.net/yanbober/article/details/45497881

Author: nopainanymore
Link: http://nopainanymore.me/DesignPattern-Iterater/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.
wechat