/******************************************************************************* * Copyright (c) 2014 Bruno Medeiros and other Contributors. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Bruno Medeiros - initial API and implementation *******************************************************************************/ package melnorme.utilbox.collections; import static melnorme.utilbox.core.CoreUtil.areEqual; import java.util.Collections; import java.util.List; import java.util.ListIterator; import java.util.RandomAccess; import melnorme.utilbox.collections.iter.ImmutableListIterator; import melnorme.utilbox.misc.CollectionUtil; /** * Interface for a read-only view of an ordered, indexable, random access collection. * This is essentially the same as {@link List}, but without the methods that modify the underlying collection. * Indeed, its main purpose is as a replacement for {@link List}, * providing a <b>statically</b> checked read-only access. * This is preferable to {@link Collections#unmodifiableList(List)} because that one only checks at runtime. * * <p> * It also provides a default equals/hashcode implementation, that concrete types can use. * </p> */ public interface Indexable<E> extends Collection2<E>, RandomAccess { /** @return the element at given index. */ E get(int index); @Override public <T> Indexable<T> upcastTypeParameter(); /* ----------------- ----------------- */ public default int indexOf(Object obj) { for(int ix = 0; ix < size(); ix++) { if(areEqual(obj, get(ix))) { return ix; } } return -1; } public default int lastIndexOf(Object obj) { for(int ix = size() - 1; ix >= 0; ix--) { if(areEqual(obj, get(ix))) { return ix; } } return -1; } public default boolean contains(Object obj) { return indexOf(obj) != -1; } /* ----------------- Iteration ----------------- */ public default ListIterator<E> listIterator() { return new ImmutableListIterator<>(this, 0); } public default ListIterator<E> listIterator(int index) { return new ImmutableListIterator<>(this, index); } /* ----------------- ----------------- */ public static <T> Indexable<T> nullToEmpty(Indexable<T> indexable) { return indexable == null ? CollectionUtil.EMPTY_INDEXABLE : indexable; } @SuppressWarnings("rawtypes") public static final Indexable EMPTY_INDEXABLE = new ArrayList2<>(); /* ----------------- equals/hashcode ----------------- */ public static boolean equals(Indexable<?> collA, Object other) { if(collA == other) return true; if(other instanceof Indexable) { return indexableEquals(collA, (Indexable<?>) other); } if(collA instanceof List && other instanceof List) { return CollectionUtil.listEquals((List<?>) collA, (List<?>) other); } return false; } public static boolean indexableEquals(Indexable<?> coll1, Indexable<?> coll2) { if(coll1.size() != coll2.size()) { return false; } return CollectionUtil.iterationEquals(coll1.iterator(), coll2.iterator()); } public static int hashCode(Indexable<?> indexable) { int hashCode = 1; for(Object element : indexable) hashCode = 31*hashCode + (element==null ? 0 : element.hashCode()); return hashCode; } }