With this assignment you will demonstrate:
Please:
Work in teams of 1 to 2 students.
Your work will be done in a private GitHub repository called lmu-cmsi-2120 or something similar. Submit to BrightSpace a single text file or pdf that contains the link to this repo. Submit only one submission per team.
Structure your repository, for now, as follows:
. ├── README.md ├── .gitignore ├── homework1/ │ └── (existing files from previous assignments) ├── homework2/ │ └── (existing files from previous assignments) └── homework3/ ├── README.md (Include ALL students’ names) ├── SimpleLinkedList.java (starter code on course notes) ├── SimpleImmutableList.java (starter code on course notes) ├── SimpleLinkedListTest.java (given to you below) └── SimpleImmutableListTest.java (given to you below)
When grading, I will clone your repository, and run the tests. Since the repo is to be private, please add me as a contributor to your repo (so I can run and comment on your work). My github name is rtoal.
You will be graded on your programming style, so make sure you are set up so that your editor or IDE auto-formats your code. You should also install Sonar Lint because it is amazing. Environment set up was carried out in class earlier, so you should be good already.
Your homework submission will be the state of your repository on the branch master, at 23:59 in the America/Los Angeles time zone on the due date above.
Make sure your README has the names of all the students that have worked on the project.
For this assignment, you will not be building anything from scratch. Instead, you will start with the classes I built on my notes on Lists, namely the simple (doubly-) linked list and the simple (singly-linked) immutable list.
To the mutable, doubly-linked list implementation, add the following mutating methods. You MUST implement take, drop, and reverse, by manipulating links only. There is to be no creation of new nodes and no copying data between nodes. You may ONLY adjust links in existing nodes.
take(n)
: Keep only the first n elements of the list, removing the restdrop(n)
: Remove the first n elements of this listreverse()
: Reverse this listappend(other)
: Append all the elements of the other list to the end of this listmap(f)
: Replace all items x in this list with f(x)filter(p)
: Keep only the items in this list for which the predicate p holdsTo the immutable, singly-linked list implementation, add the following methods:
take(n)
: Return a new list containing the first n elements of this listdrop(n)
: Return a new list containing all but the first n elements of this listreversed()
: Return a new list containing the elements of this list but in reverse orderappend(other)
: Return a new list containing all the elements of this list followed by all the elements of the other listmap(f)
: Return a new list made from applying f to all items x in this listfilter(p)
: Return a new list made from only those elements in this list for which the predicate p holdsTo both list implementations, add:
last()
: Return the last element of this listevery(p)
: Return whether every item in this list satisfies predicate psome(p)
: Return whether at least item in this list satisfies predicate pHere are the testers:
import java.util.NoSuchElementException; import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThrows; import org.junit.jupiter.api.Test; public class SimpleLinkedListTest { // A utility to make many of the tests easier private String asString(SimpleLinkedList list) { var builder = new StringBuilder(); list.forEach(builder::append); return builder.toString(); } @Test public void testListIsEmptyOnConstruction() { var list = new SimpleLinkedList(); assertEquals(0, list.size()); } @Test public void testGetAndIndexOf() { var list = new SimpleLinkedList(); list.addFirst(3); list.addFirst(2); list.addLast(4); list.addLast(5); list.addFirst(1); assertEquals(5, list.size()); assertEquals(1, list.get(0)); assertEquals(2, list.get(1)); assertEquals(3, list.get(2)); assertEquals(4, list.get(3)); assertEquals(5, list.get(4)); assertEquals(0, list.indexOf(1)); assertEquals(1, list.indexOf(2)); assertEquals(2, list.indexOf(3)); assertEquals(3, list.indexOf(4)); assertEquals(4, list.indexOf(5)); } @Test public void testAddAtTheEnds() { var list = new SimpleLinkedList(); list.addFirst(3); list.addFirst(2); list.addLast(4); list.addLast(5); list.addFirst(1); assertEquals(5, list.size()); assertEquals("12345", asString(list)); } @Test public void testAddAtPosition() { var list = new SimpleLinkedList(); for (var i = 1; i <= 5; i++) { list.add(i - 1, i); } // 1 2 3 4 5 list.add(1, 9); // 1 9 2 3 4 5 list.add(6, 8); // 1 9 2 3 4 5 8 list.add(3, 7); // 1 9 2 7 3 4 5 8 assertEquals("19273458", asString(list)); assertThrows(IndexOutOfBoundsException.class, () -> list.add(-1, "oops")); assertThrows(IndexOutOfBoundsException.class, () -> list.add(9, "oops")); } @Test public void testSet() { var list = new SimpleLinkedList(); for (var i = 1; i <= 5; i++) { list.add(i - 1, i); } // 1 2 3 4 5 for (var i = 1; i <= 5; i++) { list.set(i - 1, i * 10); } // 10 20 30 40 50 assertEquals(5, list.size()); assertEquals("1020304050", asString(list)); } @Test public void testRemoves() { var list = new SimpleLinkedList(); for (var i = 1; i <= 10; i++) { list.addLast(i); } list.remove(7); // 1 2 3 4 5 6 7 9 10 list.remove(3); // 1 2 3 5 6 7 9 10 list.remove(7); // 1 2 3 5 6 7 9 list.remove(0); // 2 3 5 6 7 9 list.remove(1); // 2 5 6 7 9 assertEquals(5, list.size()); assertEquals("25679", asString(list)); } @Test public void testForEach() { var list = new SimpleLinkedList(); var builder = new StringBuilder(); list.forEach(builder::append); // First make sure it has no effect if empty assertEquals("", builder.toString()); for (var i = 1; i <= 10; i++) { list.addLast(i); } list.forEach(builder::append); assertEquals("12345678910", builder.toString()); } @Test public void testDropAndTake() { var list = new SimpleLinkedList(); list.take(0); assert (list.size() == 0); list.addLast(1); list.addLast(2); list.addLast(3); list.addLast(4); assertEquals("1234", asString(list)); assertThrows(IllegalArgumentException.class, () -> list.take(-1)); assertThrows(IllegalArgumentException.class, () -> list.take(5)); list.take(2); assert (list.size() == 2); assertEquals("12", asString(list)); list.drop(1); assert (list.size() == 1); assertEquals("2", asString(list)); list.drop(1); assert (list.size() == 0); list.take(0); assertThrows(IllegalArgumentException.class, () -> list.take(1)); } @Test public void testReverse() { var list = new SimpleLinkedList(); // Reverse the empty list list.reverse(); assertEquals(0, list.size()); assertEquals("", asString(list)); // Reverse a one-element list list.addLast(1); list.reverse(); assertEquals(1, list.size()); assertEquals("1", asString(list)); // Reverse a four-element list list.addLast(2); list.addLast(3); list.addLast(4); assertEquals(4, list.size()); list.reverse(); assertEquals("4321", asString(list)); } @Test public void testAppend() { var list = new SimpleLinkedList(); var other = new SimpleLinkedList(); // Empty appended with empty list.append(other); assertEquals(0, list.size()); other.addLast(2); other.addLast(5); // Empty appended with nonempty list.append(other); assertEquals(2, list.size()); assertEquals("25", asString(list)); // Do it again to make sure other list did not change list.append(other); assertEquals(4, list.size()); assertEquals("2525", asString(list)); // Append to self assertThrows(IllegalArgumentException.class, () -> list.append(list)); // Append an empty list list.append(new SimpleLinkedList()); assertEquals("2525", asString(list)); } @Test public void testMap() { var list = new SimpleLinkedList(); // Map the empty list list.map(x -> 2 * (Integer) x); assertEquals(0, list.size()); assertEquals("", asString(list)); // Map a one-element list list.addLast(1); list.map(x -> 2 * (Integer) x); assertEquals(1, list.size()); assertEquals("2", asString(list)); // map a four-element list list.addLast(2); list.addLast(3); list.addLast(4); assertEquals(4, list.size()); list.map(x -> 2 * (Integer) x); assertEquals("4468", asString(list)); } @Test public void testFilter() { var list = new SimpleLinkedList(); for (var i = 1; i <= 10; i++) { list.addLast(i); } list.filter(x -> (Integer) x % 5 != 0); assertEquals(8, list.size()); assertEquals("12346789", asString(list)); list.filter(x -> (Integer) x % 3 != 0); assertEquals(5, list.size()); assertEquals("12478", asString(list)); // Remove the first element through a filter list.filter(x -> (Integer) x != 1); assertEquals(4, list.size()); assertEquals("2478", asString(list)); // Take it down to one element list.filter(x -> (Integer) x >= 8); assertEquals(1, list.size()); assertEquals("8", asString(list)); // Filter down to empty list.filter(x -> false); assertEquals(0, list.size()); assertEquals("", asString(list)); // Filter on empty has no effect list.filter(x -> true); assertEquals(0, list.size()); assertEquals("", asString(list)); } @Test public void testOf() { var list = SimpleLinkedList.of(); assertEquals(0, list.size()); assertEquals("", asString(list)); list = SimpleLinkedList.of(100); assertEquals(1, list.size()); assertEquals("100", asString(list)); list = SimpleLinkedList.of(13, 8, 21, 13, 55); assertEquals(5, list.size()); assertEquals("138211355", asString(list)); } @Test public void testLast() { var list = new SimpleLinkedList(); assertThrows(NoSuchElementException.class, list::last); list.addLast(1); assertEquals(1, list.last()); list.addLast(2); list.addLast(3); assertEquals(3, list.last()); } @Test public void testEveryAndSome() { var list = new SimpleLinkedList(); assertTrue(list.every(x -> true)); assertFalse(list.some(x -> true)); for (var i = 1; i <= 10; i++) { list.addLast(i); } assertTrue(list.every(x -> (Integer) x > 0)); assertFalse(list.every(x -> (Integer) x > 5)); assertTrue(list.some(x -> (Integer) x > 5)); assertFalse(list.some(x -> (Integer) x > 10)); } }
import java.util.NoSuchElementException; import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThrows; import org.junit.jupiter.api.Test; public class SimpleImmutableListTest { // A utility to make many of the tests easier private String asString(SimpleImmutableList list) { var builder = new StringBuilder(); list.forEach(builder::append); return builder.toString(); } @Test public void testListIsEmptyOnConstruction() { var list = SimpleImmutableList.of(); assertEquals(0, list.size()); } @Test public void testOf() { assertEquals("", asString(SimpleImmutableList.of())); assertEquals("5", asString(SimpleImmutableList.of(5))); assertEquals("58132", asString(SimpleImmutableList.of(5, 8, 13, 2))); } @Test public void testFrom() { var list = SimpleImmutableList.from(5, new EmptyList()); assertEquals(1, list.size()); assertEquals("5", asString(list)); list = SimpleImmutableList.from(8, list); list = SimpleImmutableList.from(13, list); list = SimpleImmutableList.from(2, list); assertEquals(4, list.size()); assertEquals("21385", asString(list)); } @Test public void testHeadAndTail() { assertThrows(NoSuchElementException.class, () -> new EmptyList().head()); assertThrows(NoSuchElementException.class, () -> new EmptyList().tail()); var list = SimpleImmutableList.of(100); assertEquals(100, list.head()); assertTrue(list.tail() instanceof EmptyList); assertEquals(0, list.tail().size()); list = SimpleImmutableList.of(55, 34, 13, 21); assertEquals(55, list.head()); assertEquals(3, list.tail().size()); assertEquals("341321", asString(list.tail())); } @Test public void testAt() { assertThrows(NoSuchElementException.class, () -> new EmptyList().at(1)); var list = SimpleImmutableList.of(3, 5, 8, 3, 3, 13, 2); assertThrows(NoSuchElementException.class, () -> list.at(-1)); assertThrows(NoSuchElementException.class, () -> list.at(7)); assertEquals(3, list.at(0)); assertEquals(5, list.at(1)); assertEquals(8, list.at(2)); assertEquals(3, list.at(3)); assertEquals(13, list.at(5)); assertEquals(2, list.at(6)); } @Test public void testForEach() { var list = SimpleImmutableList.of(); var builder = new StringBuilder(); list.forEach(builder::append); assertEquals("", builder.toString()); list = SimpleImmutableList.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); list.forEach(builder::append); assertEquals("12345678910", builder.toString()); } @Test public void testDropAndTake() { // Take 0 from empty is ok assertEquals(0, SimpleImmutableList.of().take(0).size()); assertEquals(0, SimpleImmutableList.of().drop(0).size()); // But you cannot take or drop even 1 from empty assertThrows(IllegalArgumentException.class, () -> new EmptyList().take(1)); assertThrows(IllegalArgumentException.class, () -> new EmptyList().drop(1)); // Do all the takes and drops on non-empties var oneThroughFour = SimpleImmutableList.of(1, 2, 3, 4); assertThrows(IllegalArgumentException.class, () -> oneThroughFour.take(-1)); assertEquals("", asString(oneThroughFour.take(0))); assertEquals("1", asString(oneThroughFour.take(1))); assertEquals("12", asString(oneThroughFour.take(2))); assertEquals("123", asString(oneThroughFour.take(3))); assertEquals("1234", asString(oneThroughFour.take(4))); assertThrows(IllegalArgumentException.class, () -> oneThroughFour.take(5)); assertThrows(IllegalArgumentException.class, () -> oneThroughFour.drop(-1)); assertEquals("1234", asString(oneThroughFour.drop(0))); assertEquals("234", asString(oneThroughFour.drop(1))); assertEquals("34", asString(oneThroughFour.drop(2))); assertEquals("4", asString(oneThroughFour.drop(3))); assertEquals("", asString(oneThroughFour.drop(4))); assertThrows(IllegalArgumentException.class, () -> oneThroughFour.drop(5)); } @Test public void testReversed() { // Reverse the empty list assertEquals("", asString(SimpleImmutableList.of().reversed())); // Reverse a one-element list assertEquals("1", asString(SimpleImmutableList.of(1).reversed())); // Reverse a four-element list assertEquals("4321", asString(SimpleImmutableList.of(1, 2, 3, 4).reversed())); } @Test public void testAppend() { var empty = SimpleImmutableList.of(); var twoFive = SimpleImmutableList.of(2, 5); var emptyTwoFive = empty.append(twoFive); var twoFiveEmpty = twoFive.append(empty); var twoFiveTwoFive = twoFive.append(twoFive); assertEquals("25", asString(emptyTwoFive)); assertEquals("25", asString(twoFiveEmpty)); assertEquals("2525", asString(twoFiveTwoFive)); } @Test public void testMap() { var list = SimpleImmutableList.of(); // Map the empty list list = list.map(x -> 2 * (Integer) x); assertEquals(0, list.size()); assertEquals("", asString(list)); // Map a one-element list list = SimpleImmutableList.of(1); list = list.map(x -> 2 * (Integer) x); assertEquals(1, list.size()); assertEquals("2", asString(list)); // map a four-element list list = SimpleImmutableList.of(2, 2, 3, 4); assertEquals(4, list.size()); list = list.map(x -> 2 * (Integer) x); assertEquals("4468", asString(list)); } @Test public void testFilter() { var list = SimpleImmutableList.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); var threes = list.filter(x -> (Integer) x % 3 == 0); assertEquals(3, threes.size()); assertEquals("369", asString(threes)); // Filter without leading elements var biggerHalf = list.filter(x -> (Integer) x > 5); assertEquals(5, biggerHalf.size()); assertEquals("678910", asString(biggerHalf)); // Filter is empty var negatives = list.filter(x -> (Integer) x < 0); assertEquals(0, negatives.size()); assertEquals("", asString(negatives)); // Filter on empty has no effect var nothing = new EmptyList().filter(x -> true); assertEquals(0, nothing.size()); assertEquals("", asString(nothing)); } @Test public void testLast() { var list = SimpleImmutableList.of(); assertThrows(NoSuchElementException.class, list::last); list = SimpleImmutableList.from(1, list); assertEquals(1, list.last()); list = SimpleImmutableList.from(2, list); list = SimpleImmutableList.from(3, list); assertEquals(1, list.last()); } @Test public void testEveryAndSome() { var list = SimpleImmutableList.of(); assertTrue(list.every(x -> true)); assertFalse(list.some(x -> true)); list = SimpleImmutableList.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); assertTrue(list.every(x -> (Integer) x > 0)); assertFalse(list.every(x -> (Integer) x > 5)); assertTrue(list.some(x -> (Integer) x > 5)); assertFalse(list.some(x -> (Integer) x > 10)); } }
As before, the following elements will all contribute to your grade:
.gitignore
fileIn addition, I will be scoring code quality, which is sometimes subjective to be sure, but you deserve feedback.