blob: 6226bd25e92afca22e0e2ab9a11cede028d0cf40 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.ignite.internal.util;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Set;
import org.apache.ignite.internal.util.tostring.GridToStringInclude;
import org.apache.ignite.internal.util.typedef.internal.A;
import org.apache.ignite.internal.util.typedef.internal.S;
import org.jetbrains.annotations.Nullable;
/**
* Implementation of {@link Set} based on internal list rather than hash table.
* The order of the internal list is either insertion order or defined by
* optionally provided {@link Comparator}.
* <p>
* Note that if {@code 'strict'} flag is false, then this set allows inconsistencies
* between {@link Object#equals(Object)} and {@link Comparator#compare(Object, Object)}
* methods. The {@code equals(Object)} method will always be used to determine
* final equality. This allows to have multiple elements for which {@code compare(Object)}
* method returns {@code 0}, but {@code equals(Object)} method returns {@code false}. The
* reverse is also true, where {@code equals(Object)} may return true, but
* {@code compare(Object)} method may return non-zero values.
* <p>
* In {@code strict} mode, which is default, {@link Object#equals(Object)} and
* {@link Comparator#compare(Object, Object)} methods must be absolutely consistent with each other.
* <p>
* This set does not allow {@code null} values.
*/
public class GridListSet<V> extends GridSerializableSet<V> implements Cloneable {
/** */
private static final long serialVersionUID = 0L;
/** Internal list. */
@GridToStringInclude
private LinkedList<V> vals = new LinkedList<>();
/** Comparator. */
@GridToStringInclude
private Comparator<V> comp;
/** Strict flag. */
private boolean strict;
/**
* Creates unsorted list set. Values will be ordered in insertion order.
*/
public GridListSet() {
comp = null;
strict = true;
}
/**
* If comparator is not {@code null}, then sorted list set will be created.
* Values will be sorted according to provided comparator.
*
* @param comp Optional comparator to sort values.
*/
public GridListSet(@Nullable Comparator<V> comp) {
this.comp = comp;
strict = true;
}
/**
* If comparator is not {@code null}, then sorted list set will be created.
* Values will be sorted according to provided comparator.
*
* @param comp Optional comparator to sort values.
* @param strict Strict flag.
*/
public GridListSet(@Nullable Comparator<V> comp, boolean strict) {
this.comp = comp;
this.strict = strict;
}
/**
* Copy constructor.
*
* @param copy Set to copy from.
*/
public GridListSet(GridListSet<V> copy) {
assert copy != null;
comp = copy.comp;
strict = copy.strict;
addAll(copy);
}
/**
* Gets value of {@code strict} flag for this set.
*
* @return Value of {@code strict} flag for this set.
*/
public boolean strict() {
return strict;
}
/**
* Gets optional comparator for this set.
*
* @return Optional comparator for this set.
*/
@Nullable public Comparator<V> comparator() {
return comp;
}
/** {@inheritDoc} */
@Override public boolean add(V val) {
return addx(val) == null;
}
/**
* Either adds a value to set or does nothing if value is already present.
*
* @param val Value to add.
* @return The instance of value from this set or {@code null} if value was added.
*/
@Nullable public V addx(V val) {
A.notNull(val, "val");
if (comp == null) {
for (V v : vals)
if (v.equals(val))
return v;
vals.add(val);
return null;
}
if (strict) {
for (ListIterator<V> it = vals.listIterator(); it.hasNext();) {
V v = it.next();
// Prefer equals to comparator.
if (v.equals(val))
return v;
int c = comp.compare(v, val);
if (c == 0)
throw new IllegalStateException("Inconsistent equals and compare methods.");
if (c > 0) {
// Back up.
it.previous();
it.add(val);
return null;
}
}
vals.add(val);
return null;
}
// Full scan first.
for (V v : vals)
if (v.equals(val))
return v;
for (ListIterator<V> it = vals.listIterator(); it.hasNext();) {
V v = it.next();
if (comp.compare(v, val) > 0) {
do {
// Back up.
v = it.previous();
}
while (comp.compare(v, val) == 0);
it.add(val);
return null;
}
}
vals.add(val);
return null;
}
/**
* Removes the first element of this list.
*
* @return Removed element or {@code null} if list is empty.
*/
@Nullable public V removeFirst() {
return vals.isEmpty() ? null : vals.removeFirst();
}
/**
* Removes the last element of this list.
*
* @return Removed element or {@code null} if list is empty.
*/
@Nullable public V removeLast() {
return vals.isEmpty() ? null : vals.removeLast();
}
/**
* Gets first element of this list.
*
* @return First element or {@code null} if list is empty.
*/
@Nullable public V first() {
return vals.isEmpty() ? null : vals.getFirst();
}
/**
* Gets last element of this list.
*
* @return Last element or {@code null} if list is empty.
*/
@Nullable public V last() {
return vals.isEmpty() ? null : vals.getLast();
}
/** {@inheritDoc} */
@SuppressWarnings( {"unchecked"})
@Override public boolean remove(Object val) {
A.notNull(val, "val");
return removex((V)val) != null;
}
/**
* Removes given value from the set and returns the instance stored in the set
* or {@code null} if value was not found.
*
* @param val Value to remove.
* @return The instance that was stored in the set or {@code null}.
*/
@Nullable public V removex(V val) {
A.notNull(val, "val");
if (comp == null || !strict) {
for (Iterator<V> it = vals.iterator(); it.hasNext();) {
V v = it.next();
if (v.equals(val)) {
it.remove();
return v;
}
}
return null;
}
assert comp != null && strict;
for (Iterator<V> it = vals.iterator(); it.hasNext();) {
V v = it.next();
// Prefer equals to comparator.
if (v.equals(val)) {
it.remove();
return v;
}
if (comp.compare(v, val) > 0)
break;
}
return null;
}
/**
* Gets instance {@code e} stored in this set for which {@code e.equals(val)}
* returns {@code true}.
*
* @param val Value to check for equality.
* @return Instance stored in this set for which {@code e.equals(val)}
* returns {@code true}.
*/
@Nullable public V get(V val) {
A.notNull(val, "val");
if (comp == null || !strict) {
for (V v : vals)
if (v.equals(val))
return v;
return null;
}
assert comp != null && strict;
for (V v : vals) {
// Prefer equals to comparator.
if (v.equals(val))
return v;
if (comp.compare(v, val) > 0)
break;
}
return null;
}
/**
* Gets value at given index within internal list. Note that this method will iterate through
* the list to get a value at the specified index.
*
* @param idx Index to get value at (must be non-negative and less than {@link #size()}).
* @return Value at give index.
*/
public V get(int idx) {
A.ensure(idx >= 0 && idx < size(), "idx >= 0 && idx < size()");
return vals.get(idx);
}
/** {@inheritDoc} */
@SuppressWarnings( {"unchecked"})
@Override public boolean contains(Object val) {
A.notNull(val, "val");
if (comp != null && strict) {
for (V v : vals) {
// Prefer equals to comparator.
if (v.equals(val))
return true;
if (comp.compare(v, (V)val) > 0)
break;
}
return false;
}
return vals.contains(val);
}
/**
* Creates a copy of this set.
*
* @return Copy of this set.
*/
public GridListSet<V> copy() {
return new GridListSet<>(this);
}
/** {@inheritDoc} */
@Override public Iterator<V> iterator() {
return vals.iterator();
}
/** {@inheritDoc} */
@Override public int size() {
return vals.size();
}
/**
* Gets a copy of the internal list.
*
* @return Copy of the internal list.
*/
public List<V> values() {
return new ArrayList<>(vals);
}
/**
* Clones this set.
*
* @return Clone of this set.
* @throws CloneNotSupportedException
*/
@SuppressWarnings( {"unchecked", "OverriddenMethodCallDuringObjectConstruction"})
@Override protected Object clone() throws CloneNotSupportedException {
GridListSet<V> clone = (GridListSet<V>)super.clone();
clone.vals = (LinkedList<V>)vals.clone();
clone.comp = comp;
clone.strict = strict;
return clone;
}
/** {@inheritDoc} */
@Override public String toString() {
return S.toString(GridListSet.class, this);
}
/**
* Creates a synchronized instance of this set.
*
* @return Synchronized instance of this set.
*/
public GridListSet<V> toSynchronized() {
final GridListSet<V> set = this;
return new GridListSet<V>() {
@Override public synchronized boolean add(V val) {
return set.add(val);
}
@Override public synchronized V addx(V val) {
return set.addx(val);
}
@SuppressWarnings( {"CloneDoesntCallSuperClone"})
@Override public synchronized Object clone() throws CloneNotSupportedException {
return set.clone();
}
@Override public synchronized boolean contains(Object val) {
return set.contains(val);
}
@Override public synchronized GridListSet<V> copy() {
return set.copy();
}
@Override public synchronized V get(int idx) {
return set.get(idx);
}
@Override public synchronized V get(V val) {
return set.get(val);
}
@Override public synchronized Iterator<V> iterator() {
return set.iterator();
}
@Override public synchronized boolean remove(Object val) {
return set.remove(val);
}
@Override public synchronized V removex(V val) {
return set.removex(val);
}
@Override public synchronized int size() {
return set.size();
}
@Override public synchronized String toString() {
return set.toString();
}
@Override public synchronized GridListSet<V> toSynchronized() {
return set.toSynchronized();
}
@Override public synchronized boolean equals(Object o) {
return set.equals(o);
}
@Override public synchronized int hashCode() {
return set.hashCode();
}
@Override public synchronized boolean removeAll(Collection<?> c) {
return set.removeAll(c);
}
@Override public synchronized boolean addAll(Collection<? extends V> c) {
return set.addAll(c);
}
@Override public synchronized void clear() {
set.clear();
}
@Override public synchronized boolean containsAll(Collection<?> c) {
return set.containsAll(c);
}
@Override public synchronized boolean isEmpty() {
return set.isEmpty();
}
@Override public synchronized boolean retainAll(Collection<?> c) {
return set.retainAll(c);
}
@Override public synchronized Object[] toArray() {
return set.toArray();
}
@SuppressWarnings( {"SuspiciousToArrayCall"})
@Override public synchronized <T> T[] toArray(T[] a) {
return set.toArray(a);
}
@Override public synchronized V first() {
return set.first();
}
@Override public synchronized V removeFirst() {
return set.removeFirst();
}
@Override public synchronized List<V> values() {
return set.values();
}
@Override public V last() {
return set.last();
}
@Override public V removeLast() {
return set.removeLast();
}
};
}
}