Commit 366aee44 authored by Dominik Gruntz's avatar Dominik Gruntz
Browse files

01_Collections (solution)

parent dc2901c4
package ch.fhnw.depa.collections;
import java.lang.reflect.Array;
import java.util.Collection;
import java.util.Iterator;
/**
* A convenience implementation of the java.util.Collection interface. It allows
* to store <code>null<code> elements and supports modifiable collections.
*
* A subclass has to implement methods add(Object x) and iterator().
*
* @param <E>
* the element type of this collection.
*/
public abstract class AbstractCollection<E> implements Collection<E> {
@Override
public boolean isEmpty() {
return !iterator().hasNext();
}
@Override
public int size() {
int n = 0;
for (@SuppressWarnings("unused") E e : this) {
n++;
}
return n;
}
@Override
public boolean contains(Object o) {
for (E e : this) {
if (e == o || (o != null && o.equals(e))) {
return true;
}
}
return false;
}
@Override
public Object[] toArray() {
Object[] result = new Object[size()];
int i = 0;
for (E e : this) {
result[i++] = e;
}
return result;
}
@Override
@SuppressWarnings("unchecked")
public <T> T[] toArray(T[] arg) {
if (arg.length < size()) {
arg = (T[]) Array.newInstance(arg.getClass().getComponentType(), size());
}
int i = 0;
for (E e : this) {
arg[i++] = (T) e;
}
if (arg.length > size()) {
arg[i] = null;
}
return arg;
}
@Override
public boolean remove(Object o) {
Iterator<E> it = iterator();
while (it.hasNext()) {
Object x = it.next();
if (x == o || (o != null && o.equals(x))) {
it.remove();
return true;
}
}
return false;
}
@Override
public void clear() {
Iterator<E> it = iterator();
while (it.hasNext()) {
it.next();
it.remove();
}
}
@Override
public boolean containsAll(Collection<?> c) {
for (Object x : c) {
if (!contains(x)) {
return false;
}
}
return true;
}
@Override
public boolean addAll(Collection<? extends E> c) {
boolean modified = false;
for (E e : c) {
if (add(e)) {
modified = true;
}
}
return modified;
}
@Override
public boolean removeAll(Collection<?> c) {
boolean modified = false;
Iterator<?> it = iterator();
while (it.hasNext()) {
if (c.contains(it.next())) {
it.remove();
modified = true;
}
}
return modified;
}
@Override
public boolean retainAll(Collection<?> c) {
boolean modified = false;
Iterator<?> it = iterator();
while (it.hasNext()) {
if (!c.contains(it.next())) {
it.remove();
modified = true;
}
}
return modified;
}
}
\ No newline at end of file
package ch.fhnw.depa.collections;
import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.NoSuchElementException;
/**
* This is a very simple implementation of the Collection interface. A
* SimpleCollection may contain the same element more than once and it does
* explicitly not guarantee any order of traversal over its elements.
*
* Moreover it does not support removal of elements.
*
* @param <E>
* Element type of this collection
*/
public class SimpleCollection<E> extends AbstractCollection<E> {
private Node<E> root = null;
private int version = 0;
@Override
public boolean add(E e) {
root = new Node<E>(e, root);
version++;
return true;
}
@Override
public Iterator<E> iterator() {
return new SCIterator();
}
private static class Node<E> {
E val;
Node<E> next;
Node(E val, Node<E> next) {
this.val = val;
this.next = next;
}
}
@Override
public int size() {
// this optimization is possible as SimpleCollection does not support
// removal of objects and has only an empty constructor.
return version;
}
private class SCIterator implements Iterator<E> {
private Node<E> current; // current refers next element to be returned
private int version;
SCIterator() {
current = root;
version = SimpleCollection.this.version;
}
@Override
public boolean hasNext() {
if (version != SimpleCollection.this.version) {
throw new ConcurrentModificationException();
}
return current != null;
}
@Override
public E next() {
if (version != SimpleCollection.this.version) {
throw new ConcurrentModificationException();
}
if (current == null) { // end of collection reached
throw new NoSuchElementException();
}
E result = current.val;
current = current.next;
return result;
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
}
}
package ch.fhnw.depa.collections;
import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.NoSuchElementException;
/**
* This is a very simple implementation of the Collection interface. A
* SimpleCollection may contain the same element more than once and it does
* explicitly not guarantee any order of traversal over its elements.
*
* In contrast to the class SimpleCollection, this class supports removal of
* elements.
*
* @param <E>
* Element type of this collection
*/
public class SimpleCollection2<E> extends AbstractCollection<E> {
private Node<E> root = null;
private int version = 0;
private int size = 0;
@Override
public boolean add(E e) {
root = new Node<E>(e, root);
version++;
size++;
return true;
}
@Override
public Iterator<E> iterator() {
return new SCIterator();
}
private static class Node<E> {
E val;
Node<E> next;
Node(E val, Node<E> next) {
this.val = val;
this.next = next;
}
}
@Override
public int size() {
return size;
}
private class SCIterator implements Iterator<E> {
private Node<E> current; // current refers next element to be returned
private int version;
private Node<E> last;
SCIterator() {
current = root;
version = SimpleCollection2.this.version;
}
@Override
public boolean hasNext() {
if (version != SimpleCollection2.this.version) {
throw new ConcurrentModificationException();
}
return current != null;
}
@Override
public E next() {
if (version != SimpleCollection2.this.version) {
throw new ConcurrentModificationException();
}
if (current == null) { // end of collection reached
throw new NoSuchElementException();
}
E result = current.val;
last = current;
current = current.next;
return result;
}
@Override
public void remove() {
if (version != SimpleCollection2.this.version) {
throw new ConcurrentModificationException();
}
if (last == null) {
throw new IllegalStateException();
}
if(root == last) {
root = root.next;
} else {
Node<E> p = root;
while(p.next != last) p = p.next;
p.next = p.next.next;
}
SimpleCollection2.this.version++;
version++;
SimpleCollection2.this.size--;
last = null;
}
}
}
/**
* This package contains collection implementations used in the introduction lesson
* of the module Design Patterns</a> at
* <a href="www.fhnw.ch/technik">FHNW</a>.
* @author Christoph Denzler
* @author Dominik Gruntz
*
*/
package ch.fhnw.depa.collections;
\ No newline at end of file
package ch.fhnw.depa.collections;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
import java.util.Arrays;
import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.NoSuchElementException;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
public class TestSimpleCollection {
private SimpleCollection<Integer> col;
@BeforeEach
public void setUp() throws Exception {
col = new SimpleCollection<>();
}
@AfterEach
public void tearDown() throws Exception {
}
@Test
public void testAdd() {
assertEquals(0, col.size());
assertTrue(col.add(1));
assertEquals(1, col.size());
assertTrue(col.add(2));
assertTrue(col.add(4));
assertEquals(3, col.size());
assertTrue(col.add(null));
assertEquals(4, col.size());
}
@Test
public void testIterator() {
// test iterator on empty collection
Iterator<Integer> it = col.iterator();
assertNotNull(it);
assertFalse(it.hasNext());
// test iterator on one element collection
col.add(2);
it = col.iterator();
assertNotNull(it);
assertTrue(it.hasNext());
Integer i = it.next();
assertNotNull(i);
assertEquals(2, i.intValue());
assertFalse(it.hasNext());
// test iterator on three element collection
col.add(11);
col.add(11);
List<Integer> resultList = new LinkedList<>(Arrays.asList(11,11,2));
it = col.iterator();
assertNotNull(it);
assertTrue(it.hasNext());
i = it.next();
assertNotNull(i);
assertTrue(resultList.remove(i));
assertTrue(i.intValue() == 11 || i.intValue() == 2);
assertTrue(it.hasNext());
i = it.next();
assertNotNull(i);
assertTrue(resultList.remove(i));
assertTrue(i.intValue() == 11 || i.intValue() == 2);
assertTrue(it.hasNext());
i = it.next();
assertNotNull(i);
assertTrue(resultList.remove(i));
assertTrue(i.intValue() == 11 || i.intValue() == 2);
assertFalse(it.hasNext());
assertTrue(resultList.isEmpty());
// provoke a no such element exception
final Iterator<Integer> fit = it;
Assertions.assertThrows(NoSuchElementException.class, () -> fit.next());
}
@Test
public void testIteratorRemove() {
col.add(3);
col.add(5);
Iterator<Integer> it = col.iterator();
try {
it.remove();
fail();
} catch (UnsupportedOperationException e) {
}
it.next();
Assertions.assertThrows(UnsupportedOperationException.class, () -> it.remove());
}
@Test
public void testIteratorConcurrency() {
col.add(3);
col.add(5);
Iterator<Integer> it1 = col.iterator();
Integer i1 = it1.next();
assertTrue(i1.intValue() == 3 || i1.intValue() == 5);
col.add(7); // invalidate it1
try {
it1.hasNext();
fail();
} catch (ConcurrentModificationException e) {
}
try {
it1.next();
fail();
} catch (ConcurrentModificationException e) {
}
try {
it1.remove();
fail();
} catch (UnsupportedOperationException e) {
}
Iterator<Integer> it2 = col.iterator();
it2.next();
it2.next();
it2.next(); // it2 has reached end of collection
col.add(11); // invalidate it2 anyways
try {
it2.hasNext();
fail();
} catch (ConcurrentModificationException e) {
}
try {
it2.next();
fail();
} catch (ConcurrentModificationException e) {
}
try {
it2.remove();
fail();
} catch (UnsupportedOperationException e) {
}
}
@Test
public void testIsEmpty() {
assertEquals(0, col.size());
assertTrue(col.isEmpty());
col.add(1);
assertFalse(col.isEmpty());
}
@Test
public void testSize() {
assertEquals(0, col.size());
col.add(1);
assertEquals(1, col.size());
col.add(1);
assertEquals(2, col.size());
col.add(3);
assertEquals(3, col.size());
}
@Test
public void testContains() {
assertFalse(col.contains(5));
col.add(1);
col.add(2);
assertFalse(col.contains(5));
assertTrue(col.contains(2));
assertTrue(col.contains(1));
}
@Test
public void testToArray() {
Object[] arr = col.toArray();
assertNotNull(arr);
assertEquals(0, arr.length);
col.add(815);
arr = col.toArray();
assertNotNull(arr);
assertEquals(1, arr.length);
assertEquals(815, arr[0]);
col.add(4711);
arr = col.toArray();
assertNotNull(arr);
assertEquals(2, arr.length);
assertFalse(arr[0].equals(arr[1]));
assertTrue(arr[0].equals(815) || arr[1].equals(815));
assertTrue(arr[0].equals(4711) || arr[1].equals(4711));
}
@Test
public void testToArrayTArray() {
// fill some data into collection
col.add(815);
col.add(4711);
// test with empty array argument
Integer[] orig = new Integer[0];
Integer[] arr = col.toArray(orig);
assertNotNull(arr);
assertFalse(arr == orig);
assertEquals(2, arr.length);
assertFalse(arr[0].equals(arr[1]));
assertTrue(arr[0].equals(815) || arr[1].equals(815));
assertTrue(arr[0].equals(4711) || arr[1].equals(4711));
// test with correctly sized array argument
orig = new Integer[2];
arr = col.toArray(orig);
assertNotNull(arr);
assertSame(arr, orig);
assertEquals(2, arr.length);
assertFalse(arr[0].equals(arr[1]));
assertTrue(arr[0