Add StackSet
This commit is contained in:
parent
52b09474ba
commit
06a5e1c525
10
src/main/java/org/warp/commonutils/type/AddStrategy.java
Normal file
10
src/main/java/org/warp/commonutils/type/AddStrategy.java
Normal file
@ -0,0 +1,10 @@
|
||||
package org.warp.commonutils.type;
|
||||
|
||||
public enum AddStrategy {
|
||||
OVERWRITE_POSITION,
|
||||
KEEP_POSITION;
|
||||
|
||||
public static AddStrategy getDefault() {
|
||||
return KEEP_POSITION;
|
||||
}
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
package org.warp.commonutils.type;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.ObjectLinkedOpenHashSet;
|
||||
import org.warp.commonutils.error.IndexOutOfBoundsException;
|
||||
|
||||
public class FastUtilStackSetWrapper<T> implements StackSet<T> {
|
||||
|
||||
private final AddStrategy addStrategy;
|
||||
private final ObjectLinkedOpenHashSet<T> linkedHashSet;
|
||||
|
||||
public FastUtilStackSetWrapper(ObjectLinkedOpenHashSet<T> linkedHashSet) {
|
||||
this(linkedHashSet, AddStrategy.getDefault());
|
||||
}
|
||||
|
||||
public FastUtilStackSetWrapper(ObjectLinkedOpenHashSet<T> linkedHashSet, AddStrategy addStrategy) {
|
||||
this.addStrategy = addStrategy;
|
||||
this.linkedHashSet = linkedHashSet;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean push(T o) {
|
||||
switch (addStrategy) {
|
||||
case KEEP_POSITION:
|
||||
return linkedHashSet.add(o);
|
||||
case OVERWRITE_POSITION:
|
||||
return linkedHashSet.addAndMoveToLast(o);
|
||||
default:
|
||||
throw new UnsupportedOperationException("Unsupported strategy type: " + addStrategy);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public T pop() {
|
||||
return linkedHashSet.removeLast();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return linkedHashSet.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public T top() {
|
||||
return linkedHashSet.last();
|
||||
}
|
||||
|
||||
@Override
|
||||
public T peek(int i) {
|
||||
var size = linkedHashSet.size();
|
||||
int positionFromBottom = size - 1 - i;
|
||||
|
||||
if (positionFromBottom < 0 || positionFromBottom >= size) {
|
||||
throw new IndexOutOfBoundsException(positionFromBottom, 0, size);
|
||||
}
|
||||
|
||||
var it = linkedHashSet.iterator();
|
||||
// Skip middle elements
|
||||
if (positionFromBottom > 0) {
|
||||
it.skip(positionFromBottom);
|
||||
}
|
||||
return it.next();
|
||||
}
|
||||
}
|
14
src/main/java/org/warp/commonutils/type/HashStackSet.java
Normal file
14
src/main/java/org/warp/commonutils/type/HashStackSet.java
Normal file
@ -0,0 +1,14 @@
|
||||
package org.warp.commonutils.type;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.ObjectLinkedOpenHashSet;
|
||||
|
||||
public class HashStackSet<T> extends FastUtilStackSetWrapper<T> {
|
||||
|
||||
public HashStackSet() {
|
||||
super(new ObjectLinkedOpenHashSet<>());
|
||||
}
|
||||
|
||||
public HashStackSet(AddStrategy addStrategy) {
|
||||
super(new ObjectLinkedOpenHashSet<>(), addStrategy);
|
||||
}
|
||||
}
|
@ -0,0 +1,73 @@
|
||||
package org.warp.commonutils.type;
|
||||
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.NoSuchElementException;
|
||||
import org.warp.commonutils.error.IndexOutOfBoundsException;
|
||||
|
||||
public class JavaStackSetWrapper<T> implements StackSet<T> {
|
||||
|
||||
private final LinkedHashSet<T> linkedHashSet;
|
||||
|
||||
public JavaStackSetWrapper(LinkedHashSet<T> linkedHashSet) {
|
||||
this.linkedHashSet = linkedHashSet;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean push(T o) {
|
||||
return linkedHashSet.add(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T pop() {
|
||||
var it = linkedHashSet.iterator();
|
||||
if (!it.hasNext()) {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
// Go to the last element
|
||||
T lastValue;
|
||||
do {
|
||||
lastValue = it.next();
|
||||
} while (it.hasNext());
|
||||
// Remove the last element
|
||||
it.remove();
|
||||
return lastValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return linkedHashSet.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public T top() {
|
||||
if (linkedHashSet.isEmpty()) {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
|
||||
var it = linkedHashSet.iterator();
|
||||
T lastValue;
|
||||
do {
|
||||
lastValue = it.next();
|
||||
} while (it.hasNext());
|
||||
return lastValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T peek(int i) {
|
||||
var size = linkedHashSet.size();
|
||||
int positionFromBottom = size - 1 - i;
|
||||
|
||||
if (positionFromBottom < 0 || positionFromBottom >= size) {
|
||||
throw new IndexOutOfBoundsException(positionFromBottom, 0, size);
|
||||
}
|
||||
|
||||
var it = linkedHashSet.iterator();
|
||||
// Skip middle elements
|
||||
if (positionFromBottom > 0) {
|
||||
for (int j = 0; j < positionFromBottom; j++) {
|
||||
it.next();
|
||||
}
|
||||
}
|
||||
return it.next();
|
||||
}
|
||||
}
|
85
src/main/java/org/warp/commonutils/type/StackSet.java
Normal file
85
src/main/java/org/warp/commonutils/type/StackSet.java
Normal file
@ -0,0 +1,85 @@
|
||||
package org.warp.commonutils.type;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.ObjectLinkedOpenHashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
/**
|
||||
* A stack but with the behavior of a Linked HashSet
|
||||
*
|
||||
* <p>A stack must provide the classical {@link #push(Object)} and
|
||||
* {@link #pop()} operations, but may be also <em>peekable</em>
|
||||
* to some extent: it may provide just the {@link #top()} function,
|
||||
* or even a more powerful {@link #peek(int)} method that provides
|
||||
* access to all elements on the stack (indexed from the top, which
|
||||
* has index 0).
|
||||
*/
|
||||
|
||||
public interface StackSet<K> {
|
||||
|
||||
|
||||
/** Pushes the given object on the stack.
|
||||
*
|
||||
* @param o the object that will become the new top of the stack.
|
||||
* @return true if added, false if already present
|
||||
*/
|
||||
|
||||
boolean push(K o);
|
||||
|
||||
/** Pops the top off the stack.
|
||||
*
|
||||
* @return the top of the stack.
|
||||
* @throws NoSuchElementException if the stack is empty.
|
||||
*/
|
||||
|
||||
K pop();
|
||||
|
||||
/** Checks whether the stack is empty.
|
||||
*
|
||||
* @return true if the stack is empty.
|
||||
*/
|
||||
|
||||
boolean isEmpty();
|
||||
|
||||
/** Peeks at the top of the stack (optional operation).
|
||||
*
|
||||
* <p>This default implementation returns {@link #peek(int) peek(0)}.
|
||||
*
|
||||
* @return the top of the stack.
|
||||
* @throws NoSuchElementException if the stack is empty.
|
||||
*/
|
||||
|
||||
default K top() {
|
||||
return peek(0);
|
||||
}
|
||||
|
||||
/** Peeks at an element on the stack (optional operation).
|
||||
*
|
||||
* <p>This default implementation just throws an {@link UnsupportedOperationException}.
|
||||
*
|
||||
* @param i an index from the stop of the stack (0 represents the top).
|
||||
* @return the {@code i}-th element on the stack.
|
||||
* @throws IndexOutOfBoundsException if the designated element does not exist..
|
||||
*/
|
||||
|
||||
default K peek(int i) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
static <T> StackSet<T> create() {
|
||||
return new HashStackSet<>();
|
||||
}
|
||||
|
||||
static <T> StackSet<T> wrap(LinkedHashSet<T> linkedHashSet) {
|
||||
return new JavaStackSetWrapper<>(linkedHashSet);
|
||||
}
|
||||
|
||||
static <T> StackSet<T> wrap(ObjectLinkedOpenHashSet<T> linkedHashSet) {
|
||||
return new FastUtilStackSetWrapper<>(linkedHashSet);
|
||||
}
|
||||
|
||||
static <T> StackSet<T> wrap(ObjectLinkedOpenHashSet<T> linkedHashSet, AddStrategy addStrategy) {
|
||||
return new FastUtilStackSetWrapper<>(linkedHashSet, addStrategy);
|
||||
}
|
||||
|
||||
}
|
144
src/test/java/org/warp/commonutils/type/TestStackSet.java
Normal file
144
src/test/java/org/warp/commonutils/type/TestStackSet.java
Normal file
@ -0,0 +1,144 @@
|
||||
package org.warp.commonutils.type;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.ObjectLinkedOpenHashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Set;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.warp.commonutils.error.IndexOutOfBoundsException;
|
||||
|
||||
public class TestStackSet {
|
||||
|
||||
@Test
|
||||
public void testStackSetEmptyTop() {
|
||||
for (StackSet<String> implementation : getImplementations()) {
|
||||
Assertions.assertThrows(NoSuchElementException.class, implementation::top);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStackSetTop() {
|
||||
for (StackSet<String> implementation : getImplementations()) {
|
||||
implementation.push("testBottom");
|
||||
implementation.push("testMiddle");
|
||||
implementation.push("testTop");
|
||||
Assertions.assertEquals("testTop", implementation.top());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStackSetItemPeekBottom() {
|
||||
for (StackSet<String> implementation : getImplementations()) {
|
||||
implementation.push("testBottom");
|
||||
implementation.push("testMiddle");
|
||||
implementation.push("testTop");
|
||||
Assertions.assertEquals("testBottom", implementation.peek(2));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStackSetItemPeekMiddle() {
|
||||
for (StackSet<String> implementation : getImplementations()) {
|
||||
implementation.push("testBottom");
|
||||
implementation.push("testMiddle");
|
||||
implementation.push("testTop");
|
||||
Assertions.assertEquals("testMiddle", implementation.peek(1));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStackSetItemPeekTop() {
|
||||
for (StackSet<String> implementation : getImplementations()) {
|
||||
implementation.push("testBottom");
|
||||
implementation.push("testMiddle");
|
||||
implementation.push("testTop");
|
||||
Assertions.assertEquals("testTop", implementation.peek(0));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStackSetItemPeekTopSingle() {
|
||||
for (StackSet<String> implementation : getImplementations()) {
|
||||
implementation.push("testTop");
|
||||
Assertions.assertEquals("testTop", implementation.peek(0));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStackSetEmptyIsEmpty() {
|
||||
for (StackSet<String> implementation : getImplementations()) {
|
||||
Assertions.assertTrue(implementation.isEmpty());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStackSetFullIsEmpty() {
|
||||
for (StackSet<String> implementation : getImplementations()) {
|
||||
implementation.push("testTop");
|
||||
Assertions.assertFalse(implementation.isEmpty());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStackSetEmptyPeekTop() {
|
||||
for (StackSet<String> implementation : getImplementations()) {
|
||||
Assertions.assertThrows(IndexOutOfBoundsException.class, () -> implementation.peek(0));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStackSetPeekOverRange() {
|
||||
for (StackSet<String> implementation : getImplementations()) {
|
||||
implementation.push("testTop");
|
||||
Assertions.assertThrows(IndexOutOfBoundsException.class, () -> implementation.peek(10));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStackSetPeekUnderRange() {
|
||||
for (StackSet<String> implementation : getImplementations()) {
|
||||
implementation.push("testTop");
|
||||
Assertions.assertThrows(IndexOutOfBoundsException.class, () -> implementation.peek(-10));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStackSetItemPop() {
|
||||
for (StackSet<String> implementation : getImplementations()) {
|
||||
implementation.push("testBottom");
|
||||
implementation.push("testMiddle");
|
||||
implementation.push("testTop");
|
||||
implementation.push("testExtra");
|
||||
Assertions.assertEquals("testTop", implementation.peek(1));
|
||||
implementation.pop();
|
||||
Assertions.assertEquals("testTop", implementation.peek(0));
|
||||
Assertions.assertEquals("testTop", implementation.top());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStackSetOneItemOnePop() {
|
||||
for (StackSet<String> implementation : getImplementations()) {
|
||||
implementation.push("testExtra");
|
||||
implementation.pop();
|
||||
Assertions.assertThrows(IndexOutOfBoundsException.class, () -> implementation.peek(0));
|
||||
Assertions.assertThrows(NoSuchElementException.class, implementation::top);
|
||||
Assertions.assertTrue(implementation.isEmpty());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStackSetItemEmptyPop() {
|
||||
for (StackSet<String> implementation : getImplementations()) {
|
||||
Assertions.assertThrows(NoSuchElementException.class, implementation::pop);
|
||||
}
|
||||
}
|
||||
|
||||
private Set<StackSet<String>> getImplementations() {
|
||||
return Set.of(new HashStackSet<>(),
|
||||
new JavaStackSetWrapper<>(new LinkedHashSet<>()),
|
||||
new FastUtilStackSetWrapper<>(new ObjectLinkedOpenHashSet<>())
|
||||
);
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user