From 90f9c7fe6221275b4619dee9da4095f677881987 Mon Sep 17 00:00:00 2001 From: mperor Date: Wed, 21 Oct 2026 20:32:44 +0200 Subject: [PATCH] Add new task "simple linked list" --- README.md | 1 + .../tasks/list/SimpleLinkedList.java | 132 ++++++++++++++ .../tasks/list/SimpleLinkedListTest.java | 164 ++++++++++++++++++ 3 files changed, 297 insertions(+) create mode 100644 src/main/java/pl/mperor/interview/tasks/list/SimpleLinkedList.java create mode 100644 src/test/java/pl/mperor/interview/tasks/list/SimpleLinkedListTest.java diff --git a/README.md b/README.md index 2a81122..31f9f3e 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,7 @@ are not the only possible solutions to these cases. 7. [Quiz Questions ❔](src/test/java/pl/mperor/interview/tasks/QuizQuestionsTest.java) 8. [Wolf 🐺 & Sheep 🐑](src/test/java/pl/mperor/interview/tasks/exception/WolfFullAndSheepWholeTest.java) 9. [Player Ranking System 🏆](src/test/java/pl/mperor/interview/tasks/ranking/PlayerRankingSystemTest.java) +10. [Simple Linked List 🔗](src/test/java/pl/mperor/interview/tasks/list/SimpleLinkedListTest.java) ## Sources 🔗 diff --git a/src/main/java/pl/mperor/interview/tasks/list/SimpleLinkedList.java b/src/main/java/pl/mperor/interview/tasks/list/SimpleLinkedList.java new file mode 100644 index 0000000..080a2a0 --- /dev/null +++ b/src/main/java/pl/mperor/interview/tasks/list/SimpleLinkedList.java @@ -0,0 +1,132 @@ +package pl.mperor.interview.tasks.list; + +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.Objects; + +public class SimpleLinkedList implements Iterable { + + private Node head; + private Node tail; + private int size; + + public boolean add(T value) { + Node node = new Node<>(value); + if (isEmpty()) { + head = node; + } else { + tail.next = node; + } + tail = node; + size++; + return true; + } + + public T get(int index) { + Objects.checkIndex(index, size); + Node current = head; + for (int i = 0; i < index; i++) { + current = current.next; + } + return current.value; + } + + public T getFirst() { + if (isEmpty()) + throw new NoSuchElementException("List is empty!"); + return head.value; + } + + public T getLast() { + if (isEmpty()) + throw new NoSuchElementException("List is empty!"); + return tail.value; + } + + public boolean contains(Object object) { + for (T value : this) { + if (Objects.equals(value, object)) + return true; + } + return false; + } + + public boolean remove(Object object) { + Iterator iterator = iterator(); + while (iterator.hasNext()) { + if (Objects.equals(iterator.next(), object)) { + iterator.remove(); + return true; + } + } + return false; + } + + public int size() { + return size; + } + + public boolean isEmpty() { + return size == 0; + } + + public void clear() { + head = null; + tail = null; + size = 0; + } + + @Override + public Iterator iterator() { + return new Iterator<>() { + + private Node previous; + private Node current; + private Node next = head; + private boolean removable; + + @Override + public boolean hasNext() { + return next != null; + } + + @Override + public T next() { + if (!hasNext()) + throw new NoSuchElementException(); + previous = current; + current = next; + next = next.next; + removable = true; + return current.value; + } + + @Override + public void remove() { + if (!removable) + throw new IllegalStateException(); + if (previous == null) { + head = next; + } else { + previous.next = next; + } + if (current == tail) { + tail = previous; + } + current = previous; + removable = false; + size--; + } + }; + } + + private static class Node { + + private final T value; + private Node next; + + private Node(T value) { + this.value = value; + } + } +} diff --git a/src/test/java/pl/mperor/interview/tasks/list/SimpleLinkedListTest.java b/src/test/java/pl/mperor/interview/tasks/list/SimpleLinkedListTest.java new file mode 100644 index 0000000..dd3bdd9 --- /dev/null +++ b/src/test/java/pl/mperor/interview/tasks/list/SimpleLinkedListTest.java @@ -0,0 +1,164 @@ +package pl.mperor.interview.tasks.list; + +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.NoSuchElementException; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * Simple Linked List 🔗 + * + *

Implement your own singly linked list. + * The implementation can be very simple - it doesn't have to implement all methods of the {@link List} interface. + * It should allow to: + *

    + *
  • add elements (including null) to the end of the list,
  • + *
  • get an element by index and get the first/last element,
  • + *
  • check if the list contains an element and remove it,
  • + *
  • check the size of the list, clear it and iterate through its elements.
  • + *
+ * Behave like {@link java.util.LinkedList} in edge cases (e.g. accessing elements of an empty list). + */ +public class SimpleLinkedListTest { + + @Test + public void testEmptyList() { + var list = new SimpleLinkedList(); + + assertTrue(list.isEmpty()); + assertEquals(0, list.size()); + assertFalse(list.iterator().hasNext()); + assertThrows(NoSuchElementException.class, list::getFirst); + assertThrows(NoSuchElementException.class, list::getLast); + assertThrows(IndexOutOfBoundsException.class, () -> list.get(0)); + } + + @Test + public void testAddAndGetElements() { + var list = listOf("First", "Second", "Third"); + + assertFalse(list.isEmpty()); + assertEquals(3, list.size()); + assertEquals("First", list.get(0)); + assertEquals("Second", list.get(1)); + assertEquals("Third", list.get(2)); + assertEquals("First", list.getFirst()); + assertEquals("Third", list.getLast()); + assertThrows(IndexOutOfBoundsException.class, () -> list.get(3)); + assertThrows(IndexOutOfBoundsException.class, () -> list.get(-1)); + } + + @Test + public void testIterateThroughElements() { + var list = listOf("First", "Second", "Third"); + + assertEquals(List.of("First", "Second", "Third"), toJavaList(list)); + + Iterator iterator = list.iterator(); + iterator.next(); + iterator.next(); + iterator.next(); + assertThrows(NoSuchElementException.class, iterator::next); + } + + @Test + public void testNullElements() { + var list = listOf("First", null, "Third"); + + assertEquals(3, list.size()); + assertNull(list.get(1)); + assertTrue(list.contains(null)); + assertTrue(list.contains("Third")); + assertTrue(list.remove(null)); + assertFalse(list.contains(null)); + assertEquals(List.of("First", "Third"), toJavaList(list)); + } + + @Test + public void testContains() { + var list = listOf("First", "Second", "Third"); + + assertTrue(list.contains("Second")); + assertFalse(list.contains("Fourth")); + assertFalse(list.contains(null)); + } + + @Test + public void testRemoveElements() { + var list = listOf("First", "Second", "Third", "Fourth"); + + assertTrue(list.remove("Second")); + assertEquals(List.of("First", "Third", "Fourth"), toJavaList(list)); + + assertTrue(list.remove("Fourth")); + assertEquals(List.of("First", "Third"), toJavaList(list)); + assertEquals("Third", list.getLast()); + + assertTrue(list.remove("First")); + assertEquals(List.of("Third"), toJavaList(list)); + assertEquals("Third", list.getFirst()); + + assertFalse(list.remove("Fifth")); + assertTrue(list.remove("Third")); + assertTrue(list.isEmpty()); + + list.add("New"); + assertEquals("New", list.getFirst()); + assertEquals("New", list.getLast()); + } + + @Test + public void testRemoveConsecutiveElementsWithIterator() { + var list = listOf("A", "B", "C", "D", "E"); + + Iterator iterator = list.iterator(); + assertThrows(IllegalStateException.class, iterator::remove); + while (iterator.hasNext()) { + String value = iterator.next(); + if (!value.equals("A") && !value.equals("E")) { + iterator.remove(); + } + } + assertEquals(List.of("A", "E"), toJavaList(list)); + assertEquals(2, list.size()); + + Iterator secondIterator = list.iterator(); + secondIterator.next(); + secondIterator.remove(); + assertThrows(IllegalStateException.class, secondIterator::remove); + + assertEquals(List.of("E"), toJavaList(list)); + assertEquals(1, list.size()); + assertEquals("E", list.getFirst()); + assertEquals("E", list.getLast()); + } + + @Test + public void testClear() { + var list = listOf("First", "Second", "Third"); + list.clear(); + + assertTrue(list.isEmpty()); + assertEquals(0, list.size()); + assertThrows(NoSuchElementException.class, list::getFirst); + } + + @SafeVarargs + private static SimpleLinkedList listOf(T... values) { + var list = new SimpleLinkedList(); + for (T value : values) { + assertTrue(list.add(value)); + } + return list; + } + + private static List toJavaList(SimpleLinkedList list) { + List result = new ArrayList<>(); + list.forEach(result::add); + return result; + } +}