diff --git a/SmartList/.gitignore b/SmartList/.gitignore new file mode 100644 index 0000000..345e61a --- /dev/null +++ b/SmartList/.gitignore @@ -0,0 +1,49 @@ +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff: +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/dictionaries + +# Sensitive or high-churn files: +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.xml +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml + +# Gradle: +.idea/**/gradle.xml +.idea/**/libraries + +# CMake +cmake-build-debug/ + +# Mongo Explorer plugin: +.idea/**/mongoSettings.xml + +## File-based project format: +*.iws + +## Plugin-specific files: + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties diff --git a/SmartList/.idea/compiler.xml b/SmartList/.idea/compiler.xml new file mode 100644 index 0000000..8b18b7f --- /dev/null +++ b/SmartList/.idea/compiler.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/SmartList/.idea/misc.xml b/SmartList/.idea/misc.xml new file mode 100644 index 0000000..e8942bd --- /dev/null +++ b/SmartList/.idea/misc.xml @@ -0,0 +1,13 @@ + + + + + + + + + \ No newline at end of file diff --git a/SmartList/.idea/modules.xml b/SmartList/.idea/modules.xml new file mode 100644 index 0000000..e7722ac --- /dev/null +++ b/SmartList/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/SmartList/ds.iml b/SmartList/ds.iml new file mode 100644 index 0000000..2152b2b --- /dev/null +++ b/SmartList/ds.iml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/SmartList/pom.xml b/SmartList/pom.xml new file mode 100644 index 0000000..5c5e9e3 --- /dev/null +++ b/SmartList/pom.xml @@ -0,0 +1,41 @@ + + + 4.0.0 + + ru.spbau.mit.kazakov.SpiralMatrix + SmartList + 1.0-SNAPSHOT + + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.8 + 1.8 + + + + + + + + junit + junit + 4.12 + + + org.jetbrains + annotations + 13.0 + + + org.apache.commons + commons-collections4 + 4.0 + + + + \ No newline at end of file diff --git a/SmartList/src/main/java/ru/spbau/mit/kazakov/SmartList/SmartList.java b/SmartList/src/main/java/ru/spbau/mit/kazakov/SmartList/SmartList.java new file mode 100644 index 0000000..26330c2 --- /dev/null +++ b/SmartList/src/main/java/ru/spbau/mit/kazakov/SmartList/SmartList.java @@ -0,0 +1,192 @@ +package ru.spbau.mit.kazakov.SmartList; + +import org.apache.commons.collections4.iterators.EmptyIterator; +import org.apache.commons.collections4.iterators.SingletonIterator; +import org.jetbrains.annotations.NotNull; + +import java.util.*; + +/** + * Smart list storage. Store one element using one reference, 2-5 element using an array, and more elements using ArrayList. + * @param generic type + */ +public class SmartList extends AbstractList implements List { + private int size; + private Object data; + + /** + * Creates new empty list. + */ + public SmartList() { + size = 0; + data = null; + } + + /** + * Creates new list containing elements from specified collection. + * + * @param collection specified collection + */ + public SmartList(@NotNull Collection collection) { + for (E elem : collection) { + add(elem); + } + } + + /** + * Returns elements by specified index. + * + * @param i specified index + */ + @SuppressWarnings("unchecked") + @Override + public E get(int i) { + if (i >= size) { + throw new IndexOutOfBoundsException(); + } + + if (size == 1) { + return (E) data; + } else if (size <= 5) { + return (E) ((Object[]) data)[i]; + } else { + return (E) ((ArrayList) data).get(i); + } + } + + @Override + public int size() { + return size; + } + + /** + * Removes element by specified index + * + * @param i specified index + * @return removed element + * @throws IndexOutOfBoundsException if there is no element with specified index + */ + @Override + @SuppressWarnings("unchecked") + public E remove(int i) throws IndexOutOfBoundsException { + if (i >= size) { + throw new IndexOutOfBoundsException(); + } + + E returnValue; + if (size == 1) { + returnValue = (E) data; + data = null; + } else if (size == 2) { + returnValue = (E) ((Object[]) data)[i]; + data = i == 0 ? ((Object[]) data)[1] : ((Object[]) data)[0]; + } else if (size <= 5) { + returnValue = (E) ((Object[]) data)[i]; + } else if (size == 6) { + returnValue = (E) ((ArrayList) data).get(i); + Object[] array = new Object[5]; + int j = 0; + while (j != 4) { + int cur = 0; + if (j != i) { + array[cur] = ((ArrayList) data).get(j); + cur++; + } + j++; + } + data = array; + } else { + returnValue = (E) ((ArrayList) data).remove(i); + } + + size--; + return returnValue; + } + + /** + * Adds specified element to list. + * + * @param elem specified element + * @return true + */ + @Override + public boolean add(@NotNull E elem) { + size++; + + if (size == 1) { + data = elem; + } else if (size == 2) { + Object[] array = new Object[5]; + array[0] = this.data; + array[1] = elem; + data = array; + } else if (size <= 5) { + ((Object[]) data)[size - 1] = elem; + } else if (size == 6) { + ArrayList list = new ArrayList<>(Arrays.asList((Object[]) data)); + list.add(elem); + data = list; + } else { + //noinspection unchecked + ((ArrayList) data).add(elem); + } + + return true; + } + + /** + * Checks if there is specified element in list + * + * @param elem specified element + * @return true if there is such element, and false otherwise + */ + @Override + public boolean contains(@NotNull Object elem) { + if (size == 1) { + return data.equals(elem); + } else if (size <= 5) { + boolean result = false; + for (Object i : (Object[]) data) { + result |= i.equals(elem); + } + return result; + } else { + boolean result = false; + //noinspection unchecked + for (Object i : (ArrayList) data) { + result |= i.equals(elem); + } + return result; + } + } + + /** + * Replaces the element at the specified index with the specified element. + * + * @param i specified position + * @param elem specified element + * @return the element previously at the specified index + */ + @Override + @SuppressWarnings("unchecked") + public E set(int i, @NotNull E elem) { + if (i >= size) { + throw new IndexOutOfBoundsException(); + } + + E returnValue; + if (size == 1) { + returnValue = (E) data; + data = elem; + } else if (size <= 5) { + returnValue = (E) ((Object[]) data)[i]; + ((Object[]) data)[i] = elem; + } else { + returnValue = (E) ((ArrayList) data).get(i); + ((ArrayList) data).set(i, elem); + } + + return returnValue; + } + +} diff --git a/SmartList/src/test/java/ru/spbau/mit/kazakov/SmartList/SmartListTest.java b/SmartList/src/test/java/ru/spbau/mit/kazakov/SmartList/SmartListTest.java new file mode 100644 index 0000000..f21679f --- /dev/null +++ b/SmartList/src/test/java/ru/spbau/mit/kazakov/SmartList/SmartListTest.java @@ -0,0 +1,150 @@ +package ru.spbau.mit.kazakov.SmartList; + +import org.junit.Test; + +import java.lang.reflect.InvocationTargetException; +import java.util.*; + +import static org.junit.Assert.*; + + +public class SmartListTest { + + @Test + public void testSimple() { + List list = newList(); + + assertEquals(Collections.emptyList(), list); + + list.add(1); + assertEquals(Collections.singletonList(1), list); + + list.add(2); + assertEquals(Arrays.asList(1, 2), list); + } + + @Test + public void testGetSet() { + List list = newList(); + + list.add(1); + + assertEquals(1, list.get(0)); + assertEquals(1, list.set(0, 2)); + assertEquals(2, list.get(0)); + assertEquals(2, list.set(0, 1)); + + list.add(2); + + assertEquals(1, list.get(0)); + assertEquals(2, list.get(1)); + + assertEquals(1, list.set(0, 2)); + + assertEquals(Arrays.asList(2, 2), list); + } + + @Test + public void testRemove() throws Exception { + List list = newList(); + + list.add(1); + list.remove(0); + assertEquals(Collections.emptyList(), list); + + list.add(2); + list.remove((Object) 2); + assertEquals(Collections.emptyList(), list); + + list.add(1); + list.add(2); + assertEquals(Arrays.asList(1, 2), list); + + list.remove(0); + assertEquals(Collections.singletonList(2), list); + + list.remove(0); + assertEquals(Collections.emptyList(), list); + } + + @Test + public void testIteratorRemove() throws Exception { + List list = newList(); + assertFalse(list.iterator().hasNext()); + + list.add(1); + + Iterator iterator = list.iterator(); + assertTrue(iterator.hasNext()); + assertEquals(1, iterator.next()); + + iterator.remove(); + assertFalse(iterator.hasNext()); + assertEquals(Collections.emptyList(), list); + + list.addAll(Arrays.asList(1, 2)); + + iterator = list.iterator(); + assertTrue(iterator.hasNext()); + assertEquals(1, iterator.next()); + + iterator.remove(); + assertTrue(iterator.hasNext()); + assertEquals(Collections.singletonList(2), list); + assertEquals(2, iterator.next()); + + iterator.remove(); + assertFalse(iterator.hasNext()); + assertEquals(Collections.emptyList(), list); + } + + + @Test + public void testCollectionConstructor() throws Exception { + assertEquals(Collections.emptyList(), newList(Collections.emptyList())); + assertEquals( + Collections.singletonList(1), + newList(Collections.singletonList(1))); + + assertEquals( + Arrays.asList(1, 2), + newList(Arrays.asList(1, 2))); + } + + @Test + public void testAddManyElementsThenRemove() throws Exception { + List list = newList(); + for (int i = 0; i < 7; i++) { + list.add(i + 1); + } + + assertEquals(Arrays.asList(1, 2, 3, 4, 5, 6, 7), list); + + for (int i = 0; i < 7; i++) { + list.remove(list.size() - 1); + assertEquals(6 - i, list.size()); + } + + assertEquals(Collections.emptyList(), list); + } + + private static List newList() { + try { + return (List) getListClass().getConstructor().newInstance(); + } catch (InstantiationException | ClassNotFoundException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) { + throw new RuntimeException(e); + } + } + + private static List newList(Collection collection) { + try { + return (List) getListClass().getConstructor(Collection.class).newInstance(collection); + } catch (InstantiationException | ClassNotFoundException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) { + throw new RuntimeException(e); + } + } + + private static Class getListClass() throws ClassNotFoundException { + return Class.forName("ru.spbau.mit.kazakov.SmartList.SmartList"); + } +}