/* * Copyright 2004-2012 the Seasar Foundation and the Others. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, * either express or implied. See the License for the specific language * governing permissions and limitations under the License. */ package org.seasar.util.collection; import java.io.Externalizable; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; import java.lang.reflect.Array; import org.seasar.util.exception.SNoSuchElementException; import static org.seasar.util.misc.AssertionUtil.*; /** * Seasar2用の連結リストです。 * * @author higa * @param <E> * 要素の型 * */ public class SLinkedList<E> implements Cloneable, Externalizable { static final long serialVersionUID = 1L; private transient Entry header = new Entry(null, null, null); private transient int size = 0; /** * {@link SLinkedList}を作成します。 */ public SLinkedList() { header.next = header; header.previous = header; } /** * 最初の要素を返します。 * * @return 最初の要素 */ public E getFirst() { if (isEmpty()) { throw new SNoSuchElementException(); } return getFirstEntry().element; } /** * 最後の要素を返します。 * * @return 最後の要素 */ public E getLast() { if (isEmpty()) { throw new SNoSuchElementException(); } return getLastEntry().element; } /** * 最初のエントリを返します。 * * @return 最初のエントリ */ public Entry getFirstEntry() { if (isEmpty()) { return null; } return header.next; } /** * 最後のエントリを返します。 * * @return 最後のエントリ */ public Entry getLastEntry() { if (isEmpty()) { return null; } return header.previous; } /** * 最初の要素を削除します。 * * @return 最初の要素 */ public E removeFirst() { if (isEmpty()) { throw new SNoSuchElementException(); } final E first = header.next.element; header.next.remove(); return first; } /** * 最後の要素を削除します。 * * @return 最後の要素 */ public E removeLast() { if (isEmpty()) { throw new SNoSuchElementException(); } final E last = header.previous.element; header.previous.remove(); return last; } /** * 先頭に追加します。 * * @param element * 追加するオブジェクト */ public void addFirst(final E element) { header.next.addBefore(element); } /** * 最後に追加します。 * * @param element * 追加するオブジェクト */ public void addLast(final E element) { header.addBefore(element); } /** * 指定した位置にオブジェクトを追加します。 * * @param index * 位置 * @param element * 要素 */ public void add(final int index, final E element) { getEntry(index).addBefore(element); } /** * 要素の数を返します。 * * @return 要素の数 */ public int size() { return size; } /** * 空かどうかを返します。 * * @return 空かどうか */ public boolean isEmpty() { return size == 0; } /** * 要素が含まれているかどうかを返します。 * * @param element * 要素 * @return 要素が含まれているかどうか */ public boolean contains(final E element) { return indexOf(element) != -1; } /** * 要素を削除します。 * * @param element * 要素 * @return 削除されたかどうか */ public boolean remove(final E element) { if (element == null) { for (Entry e = header.next; e != header; e = e.next) { if (e.element == null) { e.remove(); return true; } } } else { for (Entry e = header.next; e != header; e = e.next) { if (element.equals(e.element)) { e.remove(); return true; } } } return false; } /** * 指定した位置の要素を削除します。 * * @param index * 位置 * @return 削除された要素 */ public Object remove(final int index) { final Entry e = getEntry(index); e.remove(); return e.element; } /** * 要素を空にします。 */ public void clear() { header.next = header; header.previous = header; size = 0; } /** * インデックスで指定された位置のエントリを返します。 * * @param index * インデックス * @return エントリ */ public Entry getEntry(final int index) { assertIndex(0 <= index && index < size, "Index: " + index + ", Size: " + size); Entry e = header; if (index < size / 2) { for (int i = 0; i <= index; i++) { e = e.next; } } else { for (int i = size; i > index; i--) { e = e.previous; } } return e; } /** * インデックスで指定された位置の要素を返します。 * * @param index * インデックス * @return 要素 */ public E get(final int index) { return getEntry(index).element; } /** * インデックスで指定された位置に要素を設定します。 * * @param index * インデックス * @param element * 要素 * @return 元の要素 */ public E set(final int index, final E element) { final Entry entry = getEntry(index); final E oldValue = entry.element; entry.element = element; return oldValue; } /** * 位置を返します。 * * @param element * 要素 * @return 位置 */ public int indexOf(final E element) { int index = 0; if (element == null) { for (Entry e = header.next; e != header; e = e.next) { if (e.element == null) { return index; } index++; } } else { for (Entry e = header.next; e != header; e = e.next) { if (element.equals(e.element)) { return index; } index++; } } return -1; } @Override public void writeExternal(final ObjectOutput s) throws IOException { s.writeInt(size); for (Entry e = header.next; e != header; e = e.next) { s.writeObject(e.element); } } @SuppressWarnings("unchecked") @Override public void readExternal(final ObjectInput s) throws IOException, ClassNotFoundException { final int size = s.readInt(); header = new Entry(null, null, null); header.next = header; header.previous = header; for (int i = 0; i < size; i++) { addLast((E) s.readObject()); } } @Override public Object clone() { final SLinkedList<E> copy = new SLinkedList<E>(); for (Entry e = header.next; e != header; e = e.next) { copy.addLast(e.element); } return copy; } /** * 配列に変換します。 * * @return 配列 */ public Object[] toArray() { final Object[] result = new Object[size]; int i = 0; for (Entry e = header.next; e != header; e = e.next) { result[i++] = e.element; } return result; } /** * 配列に変換します。 * * @param array * 要素の格納先の配列。配列のサイズが十分でない場合は、同じ実行時の型で新しい配列が格納用として割り当てられる * @return 配列 */ @SuppressWarnings("unchecked") public E[] toArray(E[] array) { if (array.length < size) { array = (E[]) Array.newInstance( array.getClass().getComponentType(), size); } int i = 0; for (Entry e = header.next; e != header; e = e.next) { array[i++] = e.element; } for (i = size; i < array.length; ++i) { array[i] = null; } return array; } /** * 要素を格納するエントリです。 */ public class Entry { /** 要素 */ protected E element; /** 次のエントリ */ protected Entry next; /** 前のエントリ */ protected Entry previous; Entry(final E element, final Entry next, final Entry previous) { this.element = element; this.next = next; this.previous = previous; } /** * 要素を返します。 * * @return 要素 */ public E getElement() { return element; } /** * 次のエントリを返します。 * * @return 次のエントリ */ public Entry getNext() { if (next != SLinkedList.this.header) { return next; } return null; } /** * 前のエントリを返します。 * * @return 前のエントリ */ public Entry getPrevious() { if (previous != SLinkedList.this.header) { return previous; } return null; } /** * 要素を削除します。 */ public void remove() { previous.next = next; next.previous = previous; --size; } /** * 前に追加します。 * * @param o * 要素 * @return 追加されたエントリ */ public Entry addBefore(final E o) { final Entry newEntry = new Entry(o, this, previous); previous.next = newEntry; previous = newEntry; ++size; return newEntry; } } }