From 8fe7434bb6db194706a1d29bb4ca456cb51b4cb2 Mon Sep 17 00:00:00 2001 From: Taras Date: Tue, 31 Jul 2018 19:56:52 +0300 Subject: [PATCH 01/22] Implement LinkedQueue.java --- .../main/java/com/bobocode/LinkedQueue.java | 23 +++++++++++--- .../src/main/java/com/bobocode/Node.java | 31 +++++++++++++++++++ 2 files changed, 50 insertions(+), 4 deletions(-) create mode 100644 linked-queue/src/main/java/com/bobocode/Node.java diff --git a/linked-queue/src/main/java/com/bobocode/LinkedQueue.java b/linked-queue/src/main/java/com/bobocode/LinkedQueue.java index d02e7c6..18e2dd1 100644 --- a/linked-queue/src/main/java/com/bobocode/LinkedQueue.java +++ b/linked-queue/src/main/java/com/bobocode/LinkedQueue.java @@ -6,23 +6,38 @@ * @param a generic parameter */ public class LinkedQueue implements Queue { + private Node head; + private int size; + @Override public void add(T element) { - throw new UnsupportedOperationException("This method is not implemented yet"); // todo: implement this method + Node newNode = Node.valueOf(element); + if (head != null) { + newNode.setNext(head); + } + head = newNode; + size++; } @Override public T poll() { - throw new UnsupportedOperationException("This method is not implemented yet"); // todo: implement this method + if (head != null) { + T element = head.getElement(); + head = head.getNext(); + size--; + return element; + } else { + return null; + } } @Override public int size() { - throw new UnsupportedOperationException("This method is not implemented yet"); // todo: implement this method + return size; } @Override public boolean isEmpty() { - throw new UnsupportedOperationException("This method is not implemented yet"); // todo: implement this method + return head == null; } } diff --git a/linked-queue/src/main/java/com/bobocode/Node.java b/linked-queue/src/main/java/com/bobocode/Node.java new file mode 100644 index 0000000..a25ee51 --- /dev/null +++ b/linked-queue/src/main/java/com/bobocode/Node.java @@ -0,0 +1,31 @@ +package com.bobocode; + +/** + * This class represents a node of a queue. + * + * @param + */ +public class Node { + private T element; + private Node next; + + public static Node valueOf(T element) { + return new Node<>(element); + } + + private Node(T element) { + this.element = element; + } + + public T getElement() { + return element; + } + + public Node getNext() { + return next; + } + + public void setNext(Node next) { + this.next = next; + } +} From f5a4dde346e90c0e184ca58d1668d8f91a22e3a2 Mon Sep 17 00:00:00 2001 From: Taras Date: Wed, 1 Aug 2018 06:16:27 +0300 Subject: [PATCH 02/22] Fix logic to follow FIFO rule Add new test --- .../main/java/com/bobocode/LinkedQueue.java | 12 ++++++++--- .../src/test/java/com/bobocode/QueueTest.java | 20 ++++++++++++++++--- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/linked-queue/src/main/java/com/bobocode/LinkedQueue.java b/linked-queue/src/main/java/com/bobocode/LinkedQueue.java index 18e2dd1..34ab827 100644 --- a/linked-queue/src/main/java/com/bobocode/LinkedQueue.java +++ b/linked-queue/src/main/java/com/bobocode/LinkedQueue.java @@ -7,15 +7,18 @@ */ public class LinkedQueue implements Queue { private Node head; + private Node tail; private int size; @Override public void add(T element) { Node newNode = Node.valueOf(element); - if (head != null) { - newNode.setNext(head); + if (head == null) { + head = tail = newNode; + } else { + tail.setNext(newNode); + tail = newNode; } - head = newNode; size++; } @@ -24,6 +27,9 @@ public T poll() { if (head != null) { T element = head.getElement(); head = head.getNext(); + if (head == null) { + tail = null; + } size--; return element; } else { diff --git a/linked-queue/src/test/java/com/bobocode/QueueTest.java b/linked-queue/src/test/java/com/bobocode/QueueTest.java index d2553fe..34a51da 100644 --- a/linked-queue/src/test/java/com/bobocode/QueueTest.java +++ b/linked-queue/src/test/java/com/bobocode/QueueTest.java @@ -35,10 +35,9 @@ public void testIsEmptyOnEmptyQueue() { public void testAddElement() { integerQueue.add(324); integerQueue.add(23); - integerQueue.add(5); - assertEquals(5, integerQueue.poll().intValue()); + assertEquals(324, integerQueue.poll().intValue()); } @Test @@ -46,8 +45,9 @@ public void testPollElement() { integerQueue.add(33); integerQueue.add(123); integerQueue.add(222); + integerQueue.add(444); - integerQueue.poll(); + integerQueue.poll(); // should poll 33 assertEquals(123, integerQueue.poll().intValue()); } @@ -71,4 +71,18 @@ public void testIsEmpty() { } + @Test + public void testPollLastElement() { + integerQueue.add(8); + integerQueue.add(123); + integerQueue.add(99); + integerQueue.add(46); + + integerQueue.poll(); // should poll 8 + integerQueue.poll(); // should poll 123 + integerQueue.poll(); // should poll 99 + + assertEquals(46, integerQueue.poll().intValue()); + } + } From e0fdefee6171113e3d719cfbda4e39be9bdbda69 Mon Sep 17 00:00:00 2001 From: Taras Date: Wed, 1 Aug 2018 13:19:11 +0300 Subject: [PATCH 03/22] Implement exercise concurrent-linked-queue-simple --- .../com/bobocode/ConcurrentLinkedQueue.java | 35 +++++++++++++++---- .../src/main/java/com/bobocode/Node.java | 31 ++++++++++++++++ 2 files changed, 60 insertions(+), 6 deletions(-) create mode 100644 concurrent-linked-queue-simple/src/main/java/com/bobocode/Node.java diff --git a/concurrent-linked-queue-simple/src/main/java/com/bobocode/ConcurrentLinkedQueue.java b/concurrent-linked-queue-simple/src/main/java/com/bobocode/ConcurrentLinkedQueue.java index f9ac891..d0c26e6 100644 --- a/concurrent-linked-queue-simple/src/main/java/com/bobocode/ConcurrentLinkedQueue.java +++ b/concurrent-linked-queue-simple/src/main/java/com/bobocode/ConcurrentLinkedQueue.java @@ -1,5 +1,7 @@ package com.bobocode; +import java.util.concurrent.atomic.AtomicInteger; + /** * This queue should be implemented using generic liked nodes. E.g. class Node. In addition, this specific * should be thread-safe, which means that queue can be used by different threads simultaneously, and should work correct. @@ -7,23 +9,44 @@ * @param a generic parameter */ public class ConcurrentLinkedQueue implements Queue { + private Node head; + private Node tail; + private AtomicInteger size = new AtomicInteger(); + @Override - public void add(T element) { - throw new UnsupportedOperationException("This method is not implemented yet"); // todo: implement this method + synchronized public void add(T element) { + Node newNode = Node.valueOf(element); + if (head == null) { + head = tail = newNode; + } else { + tail.setNext(newNode); + tail = newNode; + } + size.incrementAndGet(); } @Override - public T poll() { - throw new UnsupportedOperationException("This method is not implemented yet"); // todo: implement this method + synchronized public T poll() { + if (head != null) { + T element = head.getElement(); + head = head.getNext(); + if (head == null) { + tail = null; + } + size.decrementAndGet(); + return element; + } else { + return null; + } } @Override public int size() { - throw new UnsupportedOperationException("This method is not implemented yet"); // todo: implement this method + return size.get(); } @Override public boolean isEmpty() { - throw new UnsupportedOperationException("This method is not implemented yet"); // todo: implement this method + return head == null; } } diff --git a/concurrent-linked-queue-simple/src/main/java/com/bobocode/Node.java b/concurrent-linked-queue-simple/src/main/java/com/bobocode/Node.java new file mode 100644 index 0000000..a25ee51 --- /dev/null +++ b/concurrent-linked-queue-simple/src/main/java/com/bobocode/Node.java @@ -0,0 +1,31 @@ +package com.bobocode; + +/** + * This class represents a node of a queue. + * + * @param + */ +public class Node { + private T element; + private Node next; + + public static Node valueOf(T element) { + return new Node<>(element); + } + + private Node(T element) { + this.element = element; + } + + public T getElement() { + return element; + } + + public Node getNext() { + return next; + } + + public void setNext(Node next) { + this.next = next; + } +} From ed6ab0e12e54c9b05c732a64ed188333d7394362 Mon Sep 17 00:00:00 2001 From: tboychuk Date: Tue, 7 Aug 2018 11:09:03 +0300 Subject: [PATCH 04/22] Simplify class Node.java as a pure data structure, and hide it --- .../com/bobocode/ConcurrentLinkedQueue.java | 19 ++++++++++-- .../src/main/java/com/bobocode/Node.java | 31 ------------------- .../main/java/com/bobocode/LinkedQueue.java | 19 ++++++++++-- .../src/main/java/com/bobocode/Node.java | 31 ------------------- 4 files changed, 32 insertions(+), 68 deletions(-) delete mode 100644 concurrent-linked-queue-simple/src/main/java/com/bobocode/Node.java delete mode 100644 linked-queue/src/main/java/com/bobocode/Node.java diff --git a/concurrent-linked-queue-simple/src/main/java/com/bobocode/ConcurrentLinkedQueue.java b/concurrent-linked-queue-simple/src/main/java/com/bobocode/ConcurrentLinkedQueue.java index d0c26e6..2ba552a 100644 --- a/concurrent-linked-queue-simple/src/main/java/com/bobocode/ConcurrentLinkedQueue.java +++ b/concurrent-linked-queue-simple/src/main/java/com/bobocode/ConcurrentLinkedQueue.java @@ -9,6 +9,19 @@ * @param a generic parameter */ public class ConcurrentLinkedQueue implements Queue { + static final class Node { + private T element; + private Node next; + + static Node valueOf(T element) { + return new Node<>(element); + } + + private Node(T element) { + this.element = element; + } + } + private Node head; private Node tail; private AtomicInteger size = new AtomicInteger(); @@ -19,7 +32,7 @@ synchronized public void add(T element) { if (head == null) { head = tail = newNode; } else { - tail.setNext(newNode); + tail.next = newNode; tail = newNode; } size.incrementAndGet(); @@ -28,8 +41,8 @@ synchronized public void add(T element) { @Override synchronized public T poll() { if (head != null) { - T element = head.getElement(); - head = head.getNext(); + T element = head.element; + head = head.next; if (head == null) { tail = null; } diff --git a/concurrent-linked-queue-simple/src/main/java/com/bobocode/Node.java b/concurrent-linked-queue-simple/src/main/java/com/bobocode/Node.java deleted file mode 100644 index a25ee51..0000000 --- a/concurrent-linked-queue-simple/src/main/java/com/bobocode/Node.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.bobocode; - -/** - * This class represents a node of a queue. - * - * @param - */ -public class Node { - private T element; - private Node next; - - public static Node valueOf(T element) { - return new Node<>(element); - } - - private Node(T element) { - this.element = element; - } - - public T getElement() { - return element; - } - - public Node getNext() { - return next; - } - - public void setNext(Node next) { - this.next = next; - } -} diff --git a/linked-queue/src/main/java/com/bobocode/LinkedQueue.java b/linked-queue/src/main/java/com/bobocode/LinkedQueue.java index 34ab827..cd88da3 100644 --- a/linked-queue/src/main/java/com/bobocode/LinkedQueue.java +++ b/linked-queue/src/main/java/com/bobocode/LinkedQueue.java @@ -6,6 +6,19 @@ * @param a generic parameter */ public class LinkedQueue implements Queue { + static final class Node { + private T element; + private Node next; + + static Node valueOf(T element) { + return new Node<>(element); + } + + private Node(T element) { + this.element = element; + } + } + private Node head; private Node tail; private int size; @@ -16,7 +29,7 @@ public void add(T element) { if (head == null) { head = tail = newNode; } else { - tail.setNext(newNode); + tail.next = newNode; tail = newNode; } size++; @@ -25,8 +38,8 @@ public void add(T element) { @Override public T poll() { if (head != null) { - T element = head.getElement(); - head = head.getNext(); + T element = head.element; + head = head.next; if (head == null) { tail = null; } diff --git a/linked-queue/src/main/java/com/bobocode/Node.java b/linked-queue/src/main/java/com/bobocode/Node.java deleted file mode 100644 index a25ee51..0000000 --- a/linked-queue/src/main/java/com/bobocode/Node.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.bobocode; - -/** - * This class represents a node of a queue. - * - * @param - */ -public class Node { - private T element; - private Node next; - - public static Node valueOf(T element) { - return new Node<>(element); - } - - private Node(T element) { - this.element = element; - } - - public T getElement() { - return element; - } - - public Node getNext() { - return next; - } - - public void setNext(Node next) { - this.next = next; - } -} From 210e2f614d05bc1b7042c9a2fb8fb4c7627e1dec Mon Sep 17 00:00:00 2001 From: tboychuk Date: Tue, 7 Aug 2018 17:31:26 +0300 Subject: [PATCH 05/22] Complete exercise Add Linked List exercise to the common list of exercises --- README.md | 1 + .../main/java/com/bobocode/LinkedList.java | 191 +++++++++++------- 2 files changed, 117 insertions(+), 75 deletions(-) diff --git a/README.md b/README.md index 522d5ec..b7773dd 100644 --- a/README.md +++ b/README.md @@ -12,4 +12,5 @@ It is important to have a different type of activities, which purpose is improvi ## * [Linked queue](https://github.com/boy4uck/java-core-exercises/tree/master/linked-queue) * [Concurrent linked queue simple](https://github.com/bobocode-projects/java-core-exercises/tree/master/concurrent-linked-queue-simple) +* [Linked List](https://github.com/bobocode-projects/java-core-exercises/tree/master/linked-list) diff --git a/linked-list/src/main/java/com/bobocode/LinkedList.java b/linked-list/src/main/java/com/bobocode/LinkedList.java index 02169a0..e4d8ad6 100644 --- a/linked-list/src/main/java/com/bobocode/LinkedList.java +++ b/linked-list/src/main/java/com/bobocode/LinkedList.java @@ -1,118 +1,159 @@ package com.bobocode; -/** - * {@link LinkedList} is a list implementation that is based on singly linked generic nodes. A node is implemented as - * inner static class {@link Node} - * - * @param generic type parameter - */ +import java.util.Objects; +import java.util.stream.Stream; + public class LinkedList implements List { + final static class Node { + T element; + Node next; + + private Node(T element) { + this.element = element; + } + + static Node valueOf(T element) { + return new Node<>(element); + } + } + + private Node head; + private int size; - /** - * This method creates a list of provided elements - * - * @param elements elements to add - * @param generic type - * @return a new list of elements the were passed as method parameters - */ public static List of(T... elements) { - throw new UnsupportedOperationException("This method is not implemented yet"); // todo: implement this method + List list = new LinkedList<>(); + Stream.of(elements).forEach(list::add); + return list; } - /** - * Adds an element to the end of the list - * - * @param element element to add - */ @Override public void add(T element) { - throw new UnsupportedOperationException("This method is not implemented yet"); // todo: implement this method + Node newNode = Node.valueOf(element); + if (head == null) { + head = newNode; + } else { + Node tail = findTail(head); + tail.next = newNode; + } + size++; + } + + private Node findTail(Node head) { + Node currentNode = Objects.requireNonNull(head); + while (currentNode.next != null) { + currentNode = currentNode.next; + } + return currentNode; } - /** - * Adds a new element to the specific position in the list. In case provided index in out of the list bounds it - * throws {@link IndexOutOfBoundsException} - * - * @param index an index of new element - * @param element element to add - */ @Override public void add(int index, T element) { - throw new UnsupportedOperationException("This method is not implemented yet"); // todo: implement this method + checkBoundsToAddAt(index); + Node newNode = Node.valueOf(element); + if (index == 0) { + if (head != null) { + newNode.next = head.next; + } + head = newNode; + } else { + Node node = findNodeByIndex(index - 1); + newNode.next = node.next; + node.next = newNode; + } + size++; + } + + private void checkBoundsToAddAt(int index) { + if (index < 0 || index > size) { + throw new IndexOutOfBoundsException(); + } } - /** - * Changes the value of an list element at specific position. In case provided index in out of the list bounds it - * throws {@link IndexOutOfBoundsException} - * - * @param index an position of element to change - * @param element a new element value - */ @Override public void set(int index, T element) { - throw new UnsupportedOperationException("This method is not implemented yet"); // todo: implement this method + checkBoundsToAddAt(index); + if (index == 0) { + if (head == null) { + head = Node.valueOf(element); + size++; + } else { + head.element = element; + } + } else { + Node node = findNodeByIndex(index); + node.element = element; + } } - /** - * Retrieves an elements by its position index. In case provided index in out of the list bounds it - * throws {@link IndexOutOfBoundsException} - * - * @param index element index - * @return an element value - */ @Override public T get(int index) { - throw new UnsupportedOperationException("This method is not implemented yet"); // todo: implement this method + Node node = findNodeByIndex(index); + return node.element; + } + + private Node findNodeByIndex(int index) { + Objects.checkIndex(index, size); + verifyElementExistAt(index); + Node currentNode = head; + for (int i = 0; i < index; i++) { + currentNode = currentNode.next; + } + return currentNode; + } + + private void verifyElementExistAt(int index) { + if (index < 0 || index >= size) { + throw new IndexOutOfBoundsException(); + } } - /** - * Removes an elements by its position index. In case provided index in out of the list bounds it - * throws {@link IndexOutOfBoundsException} - * - * @param index element index - * @return an element value - */ @Override public void remove(int index) { - throw new UnsupportedOperationException("This method is not implemented yet"); // todo: implement this method + verifyElementExistAt(0); + if (index == 0) { + head = head.next; + } else { + Node previousNode = findNodeByIndex(index - 1); + previousNode.next = previousNode.next.next; + } + size--; } - /** - * Checks if a specific exists in he list - * - * @return {@code true} if element exist, {@code false} otherwise - */ @Override public boolean contains(T element) { - throw new UnsupportedOperationException("This method is not implemented yet"); // todo: implement this method + if (head == null) { + return false; + } else { + return exists(element); + } + } + + private boolean exists(T element) { + Node currentNode = head; + while (currentNode != null) { + if (currentNode.element.equals(element)) { + return true; + } + currentNode = currentNode.next; + } + + return false; } - /** - * Checks if a list is empty - * - * @return {@code true} if list is empty, {@code false} otherwise - */ @Override public boolean isEmpty() { - throw new UnsupportedOperationException("This method is not implemented yet"); // todo: implement this method + return head == null; } - /** - * Returns the number of elements in the list - * - * @return number of elements - */ @Override public int size() { - throw new UnsupportedOperationException("This method is not implemented yet"); // todo: implement this method + return size; } - /** - * Removes all list elements - */ @Override public void clear() { - throw new UnsupportedOperationException("This method is not implemented yet"); // todo: implement this method + head = null; + size = 0; } -} +} \ No newline at end of file From c7f1d802ddc4d585b64d7764bd0306ccb16d6d57 Mon Sep 17 00:00:00 2001 From: tboychuk Date: Thu, 9 Aug 2018 11:33:43 +0300 Subject: [PATCH 06/22] Fix method add(int index, T element) --- linked-list/src/main/java/com/bobocode/LinkedList.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/linked-list/src/main/java/com/bobocode/LinkedList.java b/linked-list/src/main/java/com/bobocode/LinkedList.java index e4d8ad6..7d72fff 100644 --- a/linked-list/src/main/java/com/bobocode/LinkedList.java +++ b/linked-list/src/main/java/com/bobocode/LinkedList.java @@ -52,7 +52,7 @@ public void add(int index, T element) { Node newNode = Node.valueOf(element); if (index == 0) { if (head != null) { - newNode.next = head.next; + newNode.next = head; } head = newNode; } else { From fffd4dfa8f49fc50075c5278b2ac77010f41e1c5 Mon Sep 17 00:00:00 2001 From: tboychuk Date: Thu, 9 Aug 2018 11:42:33 +0300 Subject: [PATCH 07/22] Fix index at method remove() --- linked-list/src/main/java/com/bobocode/LinkedList.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/linked-list/src/main/java/com/bobocode/LinkedList.java b/linked-list/src/main/java/com/bobocode/LinkedList.java index 7d72fff..26cd6a1 100644 --- a/linked-list/src/main/java/com/bobocode/LinkedList.java +++ b/linked-list/src/main/java/com/bobocode/LinkedList.java @@ -109,7 +109,7 @@ private void verifyElementExistAt(int index) { @Override public void remove(int index) { - verifyElementExistAt(0); + verifyElementExistAt(index); if (index == 0) { head = head.next; } else { From 791acadddd460f50a55335a2440d075e51b24d5f Mon Sep 17 00:00:00 2001 From: tboychuk Date: Thu, 9 Aug 2018 11:49:01 +0300 Subject: [PATCH 08/22] Add comment for method findTail() --- linked-list/src/main/java/com/bobocode/LinkedList.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/linked-list/src/main/java/com/bobocode/LinkedList.java b/linked-list/src/main/java/com/bobocode/LinkedList.java index 26cd6a1..b13be1f 100644 --- a/linked-list/src/main/java/com/bobocode/LinkedList.java +++ b/linked-list/src/main/java/com/bobocode/LinkedList.java @@ -38,6 +38,13 @@ public void add(T element) { size++; } + /** + * In order to make the implementation simpler this method is used instead of having one more additional parameter + * tail + * + * @param head the first element in the list + * @return the last element in the list + */ private Node findTail(Node head) { Node currentNode = Objects.requireNonNull(head); while (currentNode.next != null) { From 75ba68cb89a61a50abdaeec35997350acedd805f Mon Sep 17 00:00:00 2001 From: tboychuk Date: Mon, 13 Aug 2018 08:56:22 +0300 Subject: [PATCH 09/22] Fix LinkedList#set() --- linked-list/src/main/java/com/bobocode/LinkedList.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/linked-list/src/main/java/com/bobocode/LinkedList.java b/linked-list/src/main/java/com/bobocode/LinkedList.java index b13be1f..0faafd3 100644 --- a/linked-list/src/main/java/com/bobocode/LinkedList.java +++ b/linked-list/src/main/java/com/bobocode/LinkedList.java @@ -78,7 +78,7 @@ private void checkBoundsToAddAt(int index) { @Override public void set(int index, T element) { - checkBoundsToAddAt(index); + verifyElementExistAt(index); if (index == 0) { if (head == null) { head = Node.valueOf(element); @@ -99,7 +99,6 @@ public T get(int index) { } private Node findNodeByIndex(int index) { - Objects.checkIndex(index, size); verifyElementExistAt(index); Node currentNode = head; for (int i = 0; i < index; i++) { From 8da56dd1f4e5e15bf11ccdd3c911cbe677cad60c Mon Sep 17 00:00:00 2001 From: tboychuk Date: Tue, 14 Aug 2018 17:33:54 +0300 Subject: [PATCH 10/22] Complete FileStats.java exercise --- .../src/main/java/com/bobocode/FileStats.java | 74 ++++++++++++++++++- 1 file changed, 70 insertions(+), 4 deletions(-) diff --git a/file-stats/src/main/java/com/bobocode/FileStats.java b/file-stats/src/main/java/com/bobocode/FileStats.java index 1103cb3..0a95b7d 100644 --- a/file-stats/src/main/java/com/bobocode/FileStats.java +++ b/file-stats/src/main/java/com/bobocode/FileStats.java @@ -1,10 +1,28 @@ package com.bobocode; +import java.io.IOException; +import java.net.URISyntaxException; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Comparator; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Stream; + +import static java.util.function.Function.identity; +import static java.util.stream.Collectors.counting; +import static java.util.stream.Collectors.groupingBy; + /** * {@link FileStats} provides an API that allow to get character statistic based on text file. All whitespace characters * are ignored. */ public class FileStats { + private final Map characterCountMap; + private final char mostPopularCharacter; + /** * Creates a new immutable {@link FileStats} objects using data from text file received as a parameter. * @@ -12,7 +30,47 @@ public class FileStats { * @return new FileStats object created from text file */ public static FileStats from(String fileName) { - throw new UnsupportedOperationException("It's your job to make it work!"); //todo + return new FileStats(fileName); + } + + private FileStats(String fileName) { + Path filePath = getFilePath(fileName); + characterCountMap = computeCharacterMap(filePath); + mostPopularCharacter = findMostPopularCharacter(characterCountMap); + } + + private Path getFilePath(String fileName) { + Objects.requireNonNull(fileName); + URL fileUrl = getFileUrl(fileName); + try { + return Paths.get(fileUrl.toURI()); + } catch (URISyntaxException e) { + throw new FileStatsException("Wrong file path", e); + } + } + + private URL getFileUrl(String fileName) { + URL fileUrl = getClass().getClassLoader().getResource(fileName); + if (fileUrl == null) { + throw new FileStatsException("Wrong file path"); + } + return fileUrl; + } + + private Map computeCharacterMap(Path filePath) { + try (Stream lines = Files.lines(filePath)) { + return collectCharactersToCountMap(lines); + } catch (IOException e) { + throw new FileStatsException("Cannot read the file", e); + } + } + + private Map collectCharactersToCountMap(Stream linesStream) { + return linesStream + .flatMapToInt(String::chars) + .filter(a -> a != 32) // filter whitespace + .mapToObj(c -> (char) c) + .collect(groupingBy(identity(), counting())); } /** @@ -22,7 +80,7 @@ public static FileStats from(String fileName) { * @return a number that shows how many times this character appeared in a text file */ public int getCharCount(char character) { - throw new UnsupportedOperationException("It's your job to make it work!"); //todo + return characterCountMap.get(character).intValue(); } /** @@ -31,7 +89,15 @@ public int getCharCount(char character) { * @return the most frequently appeared character */ public char getMostPopularCharacter() { - throw new UnsupportedOperationException("It's your job to make it work!"); //todo + return mostPopularCharacter; + } + + private char findMostPopularCharacter(Map characterCountMap) { + return characterCountMap.entrySet() + .stream() + .max(Comparator.comparing(Map.Entry::getValue)) + .get() + .getKey(); } /** @@ -41,6 +107,6 @@ public char getMostPopularCharacter() { * @return {@code true} if this character has appeared in the text, and {@code false} otherwise */ public boolean containsCharacter(char character) { - throw new UnsupportedOperationException("It's your job to make it work!"); //todo + return characterCountMap.containsKey(character); } } From 0cca6c73b224b27627ac4243089c7dd23764c0d3 Mon Sep 17 00:00:00 2001 From: tboychuk Date: Tue, 14 Aug 2018 18:35:26 +0300 Subject: [PATCH 11/22] Remove redundant code from LinkedList#set() --- .../src/main/java/com/bobocode/LinkedList.java | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/linked-list/src/main/java/com/bobocode/LinkedList.java b/linked-list/src/main/java/com/bobocode/LinkedList.java index 0faafd3..3d3a825 100644 --- a/linked-list/src/main/java/com/bobocode/LinkedList.java +++ b/linked-list/src/main/java/com/bobocode/LinkedList.java @@ -79,17 +79,8 @@ private void checkBoundsToAddAt(int index) { @Override public void set(int index, T element) { verifyElementExistAt(index); - if (index == 0) { - if (head == null) { - head = Node.valueOf(element); - size++; - } else { - head.element = element; - } - } else { - Node node = findNodeByIndex(index); - node.element = element; - } + Node node = findNodeByIndex(index); + node.element = element; } @Override From 6ec5e026f52219cb3b4005b243789a45e46a38fd Mon Sep 17 00:00:00 2001 From: tboychuk Date: Tue, 14 Aug 2018 20:01:42 +0300 Subject: [PATCH 12/22] Simplify LinkedList#add(index, element) --- linked-list/src/main/java/com/bobocode/LinkedList.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/linked-list/src/main/java/com/bobocode/LinkedList.java b/linked-list/src/main/java/com/bobocode/LinkedList.java index 3d3a825..3db71f5 100644 --- a/linked-list/src/main/java/com/bobocode/LinkedList.java +++ b/linked-list/src/main/java/com/bobocode/LinkedList.java @@ -58,9 +58,7 @@ public void add(int index, T element) { checkBoundsToAddAt(index); Node newNode = Node.valueOf(element); if (index == 0) { - if (head != null) { - newNode.next = head; - } + newNode.next = head; head = newNode; } else { Node node = findNodeByIndex(index - 1); From cfc384762d4414d5bd8c0afcf5ce778daa0b3490 Mon Sep 17 00:00:00 2001 From: tboychuk Date: Wed, 15 Aug 2018 16:30:45 +0300 Subject: [PATCH 13/22] Complete exercises --- .../com/bobocode/FileReaderException.java | 7 ++++ .../main/java/com/bobocode/FileReaders.java | 34 ++++++++++++++++++- 2 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 file-reader/src/main/java/com/bobocode/FileReaderException.java diff --git a/file-reader/src/main/java/com/bobocode/FileReaderException.java b/file-reader/src/main/java/com/bobocode/FileReaderException.java new file mode 100644 index 0000000..d225b9a --- /dev/null +++ b/file-reader/src/main/java/com/bobocode/FileReaderException.java @@ -0,0 +1,7 @@ +package com.bobocode; + +public class FileReaderException extends RuntimeException { + public FileReaderException(String message, Exception e) { + super(message, e); + } +} diff --git a/file-reader/src/main/java/com/bobocode/FileReaders.java b/file-reader/src/main/java/com/bobocode/FileReaders.java index 562a15b..84ce451 100644 --- a/file-reader/src/main/java/com/bobocode/FileReaders.java +++ b/file-reader/src/main/java/com/bobocode/FileReaders.java @@ -1,5 +1,16 @@ package com.bobocode; +import java.io.IOException; +import java.net.URISyntaxException; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Objects; +import java.util.stream.Stream; + +import static java.util.stream.Collectors.joining; + /** * {@link FileReaders} privides an API that allow to read whole file into a {@link String} by file name. */ @@ -12,6 +23,27 @@ public class FileReaders { * @return string that holds whole file content */ public static String readWholeFile(String fileName) { - throw new UnsupportedOperationException("It's your job to make it work!"); //todo + Path filePath = createPathFromFileName(fileName); + try (Stream fileLinesStream = openFileLinesStream(filePath)) { + return fileLinesStream.collect(joining("\n")); + } + } + + private static Stream openFileLinesStream(Path filePath) { + try { + return Files.lines(filePath); + } catch (IOException e) { + throw new FileReaderException("Cannot create stream of file lines!", e); + } + } + + private static Path createPathFromFileName(String fileName) { + Objects.requireNonNull(fileName); + URL fileUrl = FileReaders.class.getClassLoader().getResource(fileName); + try { + return Paths.get(fileUrl.toURI()); + } catch (URISyntaxException e) { + throw new FileReaderException("Invalid file URL",e); + } } } From 89a43cb391f3fa21ef95153dd0802a193c664371 Mon Sep 17 00:00:00 2001 From: tboychuk Date: Tue, 23 Oct 2018 13:31:24 +0300 Subject: [PATCH 14/22] Upgrade LinkedList.java implementation --- .../main/java/com/bobocode/LinkedList.java | 46 ++----------------- 1 file changed, 5 insertions(+), 41 deletions(-) diff --git a/linked-list/src/main/java/com/bobocode/LinkedList.java b/linked-list/src/main/java/com/bobocode/LinkedList.java index 58753ce..d3a5e93 100644 --- a/linked-list/src/main/java/com/bobocode/LinkedList.java +++ b/linked-list/src/main/java/com/bobocode/LinkedList.java @@ -50,27 +50,12 @@ public void add(T element) { if (head == null) { head = newNode; } else { - Node tail = findTail(head); + Node tail = findNodeByIndex(size - 1); tail.next = newNode; } size++; } - /** - * In order to make the implementation simpler this method is used instead of having one more additional parameter - * tail - * - * @param head the first element in the list - * @return the last element in the list - */ - private Node findTail(Node head) { - Node currentNode = Objects.requireNonNull(head); - while (currentNode.next != null) { - currentNode = currentNode.next; - } - return currentNode; - } - /** * Adds a new element to the specific position in the list. In case provided index in out of the list bounds it * throws {@link IndexOutOfBoundsException} @@ -80,7 +65,7 @@ private Node findTail(Node head) { */ @Override public void add(int index, T element) { - checkBoundsToAddAt(index); + Objects.checkIndex(index, size + 1); Node newNode = Node.valueOf(element); if (index == 0) { newNode.next = head; @@ -93,12 +78,6 @@ public void add(int index, T element) { size++; } - private void checkBoundsToAddAt(int index) { - if (index < 0 || index > size) { - throw new IndexOutOfBoundsException(); - } - } - /** * Changes the value of an list element at specific position. In case provided index in out of the list bounds it * throws {@link IndexOutOfBoundsException} @@ -108,7 +87,7 @@ private void checkBoundsToAddAt(int index) { */ @Override public void set(int index, T element) { - verifyElementExistAt(index); + Objects.checkIndex(index, size); Node node = findNodeByIndex(index); node.element = element; } @@ -127,7 +106,7 @@ public T get(int index) { } private Node findNodeByIndex(int index) { - verifyElementExistAt(index); + Objects.checkIndex(index, size); Node currentNode = head; for (int i = 0; i < index; i++) { currentNode = currentNode.next; @@ -135,12 +114,6 @@ private Node findNodeByIndex(int index) { return currentNode; } - private void verifyElementExistAt(int index) { - if (index < 0 || index >= size) { - throw new IndexOutOfBoundsException(); - } - } - /** * Removes an elements by its position index. In case provided index in out of the list bounds it * throws {@link IndexOutOfBoundsException} @@ -150,7 +123,7 @@ private void verifyElementExistAt(int index) { */ @Override public void remove(int index) { - verifyElementExistAt(index); + Objects.checkIndex(index, size); if (index == 0) { head = head.next; } else { @@ -167,14 +140,6 @@ public void remove(int index) { */ @Override public boolean contains(T element) { - if (head == null) { - return false; - } else { - return exists(element); - } - } - - private boolean exists(T element) { Node currentNode = head; while (currentNode != null) { if (currentNode.element.equals(element)) { @@ -182,7 +147,6 @@ private boolean exists(T element) { } currentNode = currentNode.next; } - return false; } From da6aa0b1a6f467ee4a9f5739e71d9dcceddc44fd Mon Sep 17 00:00:00 2001 From: tboychuk Date: Thu, 25 Oct 2018 09:06:39 +0300 Subject: [PATCH 15/22] Upgrade LinkedList.java --- linked-list/src/main/java/com/bobocode/LinkedList.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/linked-list/src/main/java/com/bobocode/LinkedList.java b/linked-list/src/main/java/com/bobocode/LinkedList.java index cb7e32e..3e2665d 100644 --- a/linked-list/src/main/java/com/bobocode/LinkedList.java +++ b/linked-list/src/main/java/com/bobocode/LinkedList.java @@ -65,7 +65,6 @@ public void add(T element) { */ @Override public void add(int index, T element) { - Objects.checkIndex(index, size + 1); Node newNode = Node.valueOf(element); if (index == 0) { newNode.next = head; @@ -87,7 +86,6 @@ public void add(int index, T element) { */ @Override public void set(int index, T element) { - Objects.checkIndex(index, size); Node node = findNodeByIndex(index); node.element = element; } From 4370c025b4690fed0b14c9face68f71dddcc505b Mon Sep 17 00:00:00 2001 From: tboychuk Date: Fri, 26 Oct 2018 12:56:40 +0300 Subject: [PATCH 16/22] Update FileReaders.java --- .../src/main/java/com/bobocode/FileReaders.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/file-reader/src/main/java/com/bobocode/FileReaders.java b/file-reader/src/main/java/com/bobocode/FileReaders.java index 84ce451..1de4fa5 100644 --- a/file-reader/src/main/java/com/bobocode/FileReaders.java +++ b/file-reader/src/main/java/com/bobocode/FileReaders.java @@ -29,14 +29,6 @@ public static String readWholeFile(String fileName) { } } - private static Stream openFileLinesStream(Path filePath) { - try { - return Files.lines(filePath); - } catch (IOException e) { - throw new FileReaderException("Cannot create stream of file lines!", e); - } - } - private static Path createPathFromFileName(String fileName) { Objects.requireNonNull(fileName); URL fileUrl = FileReaders.class.getClassLoader().getResource(fileName); @@ -46,4 +38,12 @@ private static Path createPathFromFileName(String fileName) { throw new FileReaderException("Invalid file URL",e); } } + + private static Stream openFileLinesStream(Path filePath) { + try { + return Files.lines(filePath); + } catch (IOException e) { + throw new FileReaderException("Cannot create stream of file lines!", e); + } + } } From c0fa44249360ae8759fe5266e342d90c828a1c6b Mon Sep 17 00:00:00 2001 From: tboychuk Date: Sat, 27 Oct 2018 17:51:31 +0300 Subject: [PATCH 17/22] Upgrade the implementation of LinkedList.java --- linked-list/src/main/java/com/bobocode/LinkedList.java | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/linked-list/src/main/java/com/bobocode/LinkedList.java b/linked-list/src/main/java/com/bobocode/LinkedList.java index 3e2665d..cede97a 100644 --- a/linked-list/src/main/java/com/bobocode/LinkedList.java +++ b/linked-list/src/main/java/com/bobocode/LinkedList.java @@ -46,14 +46,7 @@ public static List of(T... elements) { */ @Override public void add(T element) { - Node newNode = Node.valueOf(element); - if (head == null) { - head = newNode; - } else { - Node tail = findNodeByIndex(size - 1); - tail.next = newNode; - } - size++; + add(size, element); } /** From 36c234820d30c4c8648c523518364b25f513e52b Mon Sep 17 00:00:00 2001 From: tboychuk Date: Sat, 27 Oct 2018 17:52:48 +0300 Subject: [PATCH 18/22] Upgrade the implementation of LinkedList.java --- linked-list/src/main/java/com/bobocode/LinkedList.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/linked-list/src/main/java/com/bobocode/LinkedList.java b/linked-list/src/main/java/com/bobocode/LinkedList.java index cede97a..e9139c0 100644 --- a/linked-list/src/main/java/com/bobocode/LinkedList.java +++ b/linked-list/src/main/java/com/bobocode/LinkedList.java @@ -113,8 +113,8 @@ private Node findNodeByIndex(int index) { */ @Override public void remove(int index) { - Objects.checkIndex(index, size); if (index == 0) { + Objects.checkIndex(index, size); head = head.next; } else { Node previousNode = findNodeByIndex(index - 1); From bb7467878899fb3edff86845a79906c5d5380730 Mon Sep 17 00:00:00 2001 From: tboychuk Date: Sat, 27 Oct 2018 17:53:11 +0300 Subject: [PATCH 19/22] Upgrade the implementation of LinkedList.java --- linked-list/src/main/java/com/bobocode/LinkedList.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/linked-list/src/main/java/com/bobocode/LinkedList.java b/linked-list/src/main/java/com/bobocode/LinkedList.java index e9139c0..49eafd4 100644 --- a/linked-list/src/main/java/com/bobocode/LinkedList.java +++ b/linked-list/src/main/java/com/bobocode/LinkedList.java @@ -11,8 +11,8 @@ */ public class LinkedList implements List { final static class Node { - T element; - Node next; + private T element; + private Node next; private Node(T element) { this.element = element; From 7bba9c00f0d3720dc6872740ab2da1e10b0c8f6a Mon Sep 17 00:00:00 2001 From: shtramak Date: Mon, 1 Jul 2019 23:12:54 +0300 Subject: [PATCH 20/22] #6 add java functional features exercises with solutions --- .../main/java/com/bobocode/CrazyLambdas.java | 41 ++++++---- .../main/java/com.bobocode/CrazyStreams.java | 78 +++++++++++++++---- .../main/java/com/bobocode/SumOfSquares.java | 13 ++-- .../src/main/java/com.bobocode/Functions.java | 8 +- 4 files changed, 99 insertions(+), 41 deletions(-) diff --git a/crazy-lambdas/src/main/java/com/bobocode/CrazyLambdas.java b/crazy-lambdas/src/main/java/com/bobocode/CrazyLambdas.java index 0b505cf..cc6ce9b 100644 --- a/crazy-lambdas/src/main/java/com/bobocode/CrazyLambdas.java +++ b/crazy-lambdas/src/main/java/com/bobocode/CrazyLambdas.java @@ -1,7 +1,7 @@ package com.bobocode; import java.math.BigDecimal; -import java.util.TreeMap; +import java.util.concurrent.ThreadLocalRandom; import java.util.function.*; public class CrazyLambdas { @@ -12,7 +12,7 @@ public class CrazyLambdas { * @return a string supplier */ public static Supplier helloSupplier() { - throw new UnsupportedOperationException("It's your job to implement this method"); // todo + return () -> "Hello"; } /** @@ -21,7 +21,7 @@ public static Supplier helloSupplier() { * @return a string predicate */ public static Predicate isEmptyPredicate() { - throw new UnsupportedOperationException("It's your job to implement this method"); // todo + return String::isEmpty; } /** @@ -31,7 +31,7 @@ public static Predicate isEmptyPredicate() { * @return function that converts adds dollar sign */ public static Function toDollarStringFunction() { - throw new UnsupportedOperationException("It's your job to implement this method"); // todo + return bigDecimal -> "$" + bigDecimal; } /** @@ -43,7 +43,7 @@ public static Function toDollarStringFunction() { * @return a string predicate */ public static Predicate lengthInRangePredicate(int min, int max) { - throw new UnsupportedOperationException("It's your job to implement this method"); // todo + return str -> str.length() >= min && str.length() < max; } /** @@ -52,7 +52,7 @@ public static Predicate lengthInRangePredicate(int min, int max) { * @return int supplier */ public static IntSupplier randomIntSupplier() { - throw new UnsupportedOperationException("It's your job to implement this method"); // todo + return () -> ThreadLocalRandom.current().nextInt(); } @@ -62,7 +62,7 @@ public static IntSupplier randomIntSupplier() { * @return int operation */ public static IntUnaryOperator boundedRandomIntSupplier() { - throw new UnsupportedOperationException("It's your job to implement this method"); // todo + return bound -> ThreadLocalRandom.current().nextInt(bound); } /** @@ -71,7 +71,7 @@ public static IntUnaryOperator boundedRandomIntSupplier() { * @return square operation */ public static IntUnaryOperator intSquareOperation() { - throw new UnsupportedOperationException("It's your job to implement this method"); // todo + return a -> a * a; } /** @@ -80,7 +80,7 @@ public static IntUnaryOperator intSquareOperation() { * @return binary sum operation */ public static LongBinaryOperator longSumOperation() { - throw new UnsupportedOperationException("It's your job to implement this method"); // todo + return (a, b) -> a + b; } /** @@ -89,7 +89,7 @@ public static LongBinaryOperator longSumOperation() { * @return string to int converter */ public static ToIntFunction stringToIntConverter() { - throw new UnsupportedOperationException("It's your job to implement this method"); // todo + return Integer::parseInt; } /** @@ -100,7 +100,7 @@ public static ToIntFunction stringToIntConverter() { * @return a function supplier */ public static Supplier nMultiplyFunctionSupplier(int n) { - throw new UnsupportedOperationException("It's your job to implement this method"); // todo + return () -> a -> n * a; } /** @@ -111,7 +111,11 @@ public static Supplier nMultiplyFunctionSupplier(int n) { * @return a thread supplier */ public static Supplier runningThreadSupplier(Runnable runnable) { - throw new UnsupportedOperationException("It's your job to implement this method"); // todo + return () -> { + Thread thread = new Thread(runnable); + thread.start(); + return thread; + }; } /** @@ -120,7 +124,7 @@ public static Supplier runningThreadSupplier(Runnable runnable) { * @return a runnable consumer */ public static Consumer newThreadRunnableConsumer() { - throw new UnsupportedOperationException("It's your job to implement this method"); // todo + return runnable -> new Thread(runnable).start(); } /** @@ -130,7 +134,11 @@ public static Consumer newThreadRunnableConsumer() { * @return a function that transforms runnable into a thread supplier */ public static Function> runnableToThreadSupplierFunction() { - throw new UnsupportedOperationException("It's your job to implement this method"); // todo + return runnable -> () -> { + Thread thread = new Thread(runnable); + thread.start(); + return thread; + }; } /** @@ -143,7 +151,7 @@ public static Function> runnableToThreadSupplierFunct * @return a binary function that receiver predicate and function and compose them to create a new function */ public static BiFunction functionToConditionalFunction() { - throw new UnsupportedOperationException("It's your job to implement this method"); // todo + return (intOperation, intPredicate) -> a -> intPredicate.test(a) ? intOperation.applyAsInt(a) : a; } /** @@ -152,7 +160,8 @@ public static BiFunction funct * @return a supplier instance */ public static Supplier>> trickyWellDoneSupplier() { - throw new UnsupportedOperationException("It's your job to implement this method"); // todo + return () -> () -> () -> "WELL DONE!"; } } + diff --git a/crazy-streams/src/main/java/com.bobocode/CrazyStreams.java b/crazy-streams/src/main/java/com.bobocode/CrazyStreams.java index 68731d7..cf46046 100644 --- a/crazy-streams/src/main/java/com.bobocode/CrazyStreams.java +++ b/crazy-streams/src/main/java/com.bobocode/CrazyStreams.java @@ -2,11 +2,19 @@ import com.bobocode.exception.EntityNotFoundException; import com.bobocode.model.Account; +import com.bobocode.model.Sex; import java.math.BigDecimal; import java.time.Month; import java.util.*; +import java.util.function.Function; +import java.util.stream.Stream; +import static java.util.Comparator.comparing; +import static java.util.function.Function.identity; +import static java.util.stream.Collectors.*; + +import static java.util.stream.Collectors.mapping; import static java.util.stream.Collectors.toMap; /** @@ -29,7 +37,8 @@ private CrazyStreams(Collection accounts) { * @return account with max balance wrapped with optional */ public Optional findRichestPerson() { - throw new UnsupportedOperationException("It's your job to implement this method"); // todo + return accounts.stream() + .max(comparing(Account::getBalance)); } /** @@ -39,7 +48,9 @@ public Optional findRichestPerson() { * @return a list of accounts */ public List findAccountsByBirthdayMonth(Month birthdayMonth) { - throw new UnsupportedOperationException("It's your job to implement this method"); // todo + return accounts.stream() + .filter(a -> a.getBirthday().getMonth().equals(birthdayMonth)) + .collect(toList()); } /** @@ -49,7 +60,8 @@ public List findAccountsByBirthdayMonth(Month birthdayMonth) { * @return a map where key is true or false, and value is list of male, and female accounts */ public Map> partitionMaleAccounts() { - throw new UnsupportedOperationException("It's your job to implement this method"); // todo + return accounts.stream() + .collect(partitioningBy(a -> a.getSex().equals(Sex.MALE))); } /** @@ -59,7 +71,8 @@ public Map> partitionMaleAccounts() { * @return a map where key is an email domain and value is a list of all account with such email */ public Map> groupAccountsByEmailDomain() { - throw new UnsupportedOperationException("It's your job to implement this method"); // todo + return accounts.stream() + .collect(groupingBy(a -> a.getEmail().split("@")[1])); } /** @@ -68,7 +81,9 @@ public Map> groupAccountsByEmailDomain() { * @return total number of letters of first and last names of all accounts */ public int getNumOfLettersInFirstAndLastNames() { - throw new UnsupportedOperationException("It's your job to implement this method"); // todo + return accounts.stream() + .mapToInt(a -> a.getFirstName().length() + a.getLastName().length()) + .sum(); } /** @@ -77,7 +92,9 @@ public int getNumOfLettersInFirstAndLastNames() { * @return total balance of all accounts */ public BigDecimal calculateTotalBalance() { - throw new UnsupportedOperationException("It's your job to implement this method"); // todo + return accounts.stream() + .map(Account::getBalance) + .reduce(BigDecimal.ZERO, BigDecimal::add); } /** @@ -86,7 +103,10 @@ public BigDecimal calculateTotalBalance() { * @return list of accounts sorted by first and last names */ public List sortByFirstAndLastNames() { - throw new UnsupportedOperationException("It's your job to implement this method"); // todo + return accounts.stream() + .sorted(comparing(Account::getFirstName) + .thenComparing(Account::getLastName)) + .collect(toList()); } /** @@ -96,7 +116,9 @@ public List sortByFirstAndLastNames() { * @return true if there is an account that has an email with provided domain */ public boolean containsAccountWithEmailDomain(String emailDomain) { - throw new UnsupportedOperationException("It's your job to implement this method"); // todo + return accounts.stream() + .map(Account::getEmail) + .anyMatch(email -> email.split("@")[1].equals(emailDomain)); } /** @@ -107,7 +129,11 @@ public boolean containsAccountWithEmailDomain(String emailDomain) { * @return account balance */ public BigDecimal getBalanceByEmail(String email) { - throw new UnsupportedOperationException("It's your job to implement this method"); // todo + return accounts.stream() + .filter(account -> account.getEmail().equals(email)) + .findFirst() + .map(Account::getBalance) + .orElseThrow(() -> new EntityNotFoundException(String.format("Cannot find Account by email=%s", email))); } /** @@ -116,7 +142,8 @@ public BigDecimal getBalanceByEmail(String email) { * @return map of accounts by its ids */ public Map collectAccountsById() { - throw new UnsupportedOperationException("It's your job to implement this method"); // todo + return accounts.stream() + .collect(toMap(Account::getId, identity())); } /** @@ -127,17 +154,20 @@ public Map collectAccountsById() { * @return map of account by its ids the were created in a particular year */ public Map collectBalancesByIdForAccountsCreatedOn(int year) { - throw new UnsupportedOperationException("It's your job to implement this method"); // todo + return accounts.stream() + .filter(account -> account.getCreationDate().getYear() == year) + .collect(toMap(Account::getEmail, Account::getBalance)); } /** * Returns a {@link Map} where key is {@link Account#lastName} and values is a {@link Set} that contains first names * of all accounts with a specific last name. * - * @return a map where key is a last name and value is a set of first names + * @return a map where key is a first name and value is a set of first names */ public Map> groupFirstNamesByLastNames() { - throw new UnsupportedOperationException("It's your job to implement this method"); // todo + return accounts.stream() + .collect(groupingBy(Account::getLastName, mapping(Account::getFirstName, toSet()))); } /** @@ -147,7 +177,9 @@ public Map> groupFirstNamesByLastNames() { * @return a map where a key is a birthday month and value is comma-separated first names */ public Map groupCommaSeparatedFirstNamesByBirthdayMonth() { - throw new UnsupportedOperationException("It's your job to implement this method"); // todo + return accounts.stream() + .collect(groupingBy(a -> a.getBirthday().getMonth(), + mapping(Account::getFirstName, joining(", ")))); } /** @@ -157,7 +189,10 @@ public Map groupCommaSeparatedFirstNamesByBirthdayMonth() { * @return a map where key is a creation month and value is total balance of all accounts created in that month */ public Map groupTotalBalanceByCreationMonth() { - throw new UnsupportedOperationException("It's your job to implement this method"); // todo + return accounts.stream() + .collect(groupingBy(a -> a.getCreationDate().getMonth(), + mapping(Account::getBalance, + reducing(BigDecimal.ZERO, BigDecimal::add)))); } /** @@ -167,7 +202,11 @@ public Map groupTotalBalanceByCreationMonth() { * @return a map where key is a letter and value is its count in all first names */ public Map getCharacterFrequencyInFirstNames() { - throw new UnsupportedOperationException("It's your job to implement this method"); // todo + return accounts.stream() + .map(Account::getFirstName) + .flatMapToInt(String::chars) + .mapToObj(c -> (char) c) + .collect(groupingBy(identity(), counting())); } /** @@ -177,7 +216,12 @@ public Map getCharacterFrequencyInFirstNames() { * @return a map where key is a letter and value is its count ignoring case in all first and last names */ public Map getCharacterFrequencyIgnoreCaseInFirstAndLastNames() { - throw new UnsupportedOperationException("It's your job to implement this method"); // todo + return accounts.stream() + .flatMap(a -> Stream.of(a.getFirstName(), a.getLastName())) + .map(String::toLowerCase) + .flatMapToInt(String::chars) + .mapToObj(c -> (char) c) + .collect(groupingBy(identity(), counting())); } } diff --git a/declarative-sum-of-squares/src/main/java/com/bobocode/SumOfSquares.java b/declarative-sum-of-squares/src/main/java/com/bobocode/SumOfSquares.java index fc0dfde..56736c2 100644 --- a/declarative-sum-of-squares/src/main/java/com/bobocode/SumOfSquares.java +++ b/declarative-sum-of-squares/src/main/java/com/bobocode/SumOfSquares.java @@ -3,6 +3,8 @@ import com.bobocode.exception.InvalidRangeException; +import java.util.stream.IntStream; + /** * This class allow to calculate a sum of squares of integer number in a certain range. It was implemented using @@ -17,7 +19,7 @@ public static void main(String[] args) { * This method calculates the sum of squares of integer in the range * * @param startInclusive first element in range - * @param endInclusive last element in range + * @param endInclusive last element in range * @return the sum of squares of each element in the range */ static int calculateSumOfSquaresInRange(int startInclusive, int endInclusive) { @@ -25,11 +27,8 @@ static int calculateSumOfSquaresInRange(int startInclusive, int endInclusive) { throw new InvalidRangeException(); } - // todo: refactor using functional approach - int sumOfSquares = 0; - for (int i = startInclusive; i <= endInclusive; i++) { - sumOfSquares += i * i; - } - return sumOfSquares; + return IntStream.rangeClosed(startInclusive, endInclusive) + .map(a -> a * a) + .sum(); } } diff --git a/lambda-math-functions/src/main/java/com.bobocode/Functions.java b/lambda-math-functions/src/main/java/com.bobocode/Functions.java index 80ebc20..f983960 100644 --- a/lambda-math-functions/src/main/java/com.bobocode/Functions.java +++ b/lambda-math-functions/src/main/java/com.bobocode/Functions.java @@ -1,5 +1,7 @@ package com.bobocode; +import static java.lang.Math.abs; + public class Functions { /** * A static factory method that creates an integer function map with basic functions: @@ -14,7 +16,11 @@ public class Functions { public static FunctionMap intFunctionMap() { FunctionMap intFunctionMap = new FunctionMap<>(); - // todo: add simple functions to the function map (abs, sng, increment, decrement, square) + intFunctionMap.addFunction("square", n -> n * n); + intFunctionMap.addFunction("abs", Math::abs); + intFunctionMap.addFunction("increment", n -> n + 1); + intFunctionMap.addFunction("decrement", n -> n - 1); + intFunctionMap.addFunction("sgn", n -> (n != 0) ? n / abs(n) : 0); return intFunctionMap; } From 3ee80b1ef0f47ea6c197ef066379f594af253414 Mon Sep 17 00:00:00 2001 From: tboychuk Date: Tue, 2 Jul 2019 14:18:36 +0300 Subject: [PATCH 21/22] Implement new methods is CrazyLambdas.java --- crazy-lambdas/src/main/java/com/bobocode/CrazyLambdas.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crazy-lambdas/src/main/java/com/bobocode/CrazyLambdas.java b/crazy-lambdas/src/main/java/com/bobocode/CrazyLambdas.java index 940a09e..6a329f1 100644 --- a/crazy-lambdas/src/main/java/com/bobocode/CrazyLambdas.java +++ b/crazy-lambdas/src/main/java/com/bobocode/CrazyLambdas.java @@ -32,7 +32,7 @@ public static Predicate isEmptyPredicate() { * @return function that repeats Strings */ public static BiFunction stringMultiplier() { - throw new UnsupportedOperationException("It's your job to implement this method"); // todo + return String::repeat; } /** @@ -120,7 +120,7 @@ public static Supplier nMultiplyFunctionSupplier(int n) { * @return function that composes functions with trim() function */ public static UnaryOperator> composeWithTrimFunction() { - throw new UnsupportedOperationException("It's your job to implement this method"); // todo + return stringFunction -> stringFunction.compose(String::trim); } /** @@ -182,7 +182,7 @@ public static BiFunction funct * @return a high-order function that fetches a function from a function map by a given name or returns identity() */ public static BiFunction, String, IntUnaryOperator> functionLoader() { - throw new UnsupportedOperationException("It's your job to implement this method"); // todo + return (functionMap, functionName) -> functionMap.getOrDefault(functionName, IntUnaryOperator.identity()); } /** From 4068f3831adae7f545e76bed354cae194b8ec07a Mon Sep 17 00:00:00 2001 From: tboychuk Date: Wed, 14 Aug 2019 13:19:42 +0300 Subject: [PATCH 22/22] GP-4 Create new exercise CrazyOptionals.java and upgrade Accounts.java --- .../java/com/bobocode/CrazyOptionals.java | 66 ++++++++++++++----- 1 file changed, 51 insertions(+), 15 deletions(-) diff --git a/crazy-optionals/src/main/java/com/bobocode/CrazyOptionals.java b/crazy-optionals/src/main/java/com/bobocode/CrazyOptionals.java index d81fe02..eb1734e 100644 --- a/crazy-optionals/src/main/java/com/bobocode/CrazyOptionals.java +++ b/crazy-optionals/src/main/java/com/bobocode/CrazyOptionals.java @@ -15,6 +15,8 @@ import java.util.Optional; import java.util.OptionalDouble; +import static java.util.Comparator.comparing; + public class CrazyOptionals { /** @@ -24,7 +26,7 @@ public class CrazyOptionals { * @return optional object that holds text */ public static Optional optionalOfString(@Nullable String text) { - throw new UnsupportedOperationException("Some people say that method does not work until you implement it"); + return Optional.ofNullable(text); // Optional.ofNullable() will use Optional.empty() if text is null } /** @@ -34,7 +36,10 @@ public static Optional optionalOfString(@Nullable String text) { * @param amount money to deposit */ public static void deposit(AccountProvider accountProvider, BigDecimal amount) { - throw new UnsupportedOperationException("Some people say that method does not work until you implement it"); + accountProvider.getAccount() + .ifPresent(account -> account.setBalance(account.getBalance().add(amount))); // instead of using if operator + // you can pass Consumer object that will be used in case Optional is not empty + // this approach is called declarative and is usually more precise } /** @@ -44,7 +49,7 @@ public static void deposit(AccountProvider accountProvider, BigDecimal amount) { * @return optional object that holds account */ public static Optional optionalOfAccount(@Nonnull Account account) { - throw new UnsupportedOperationException("Some people say that method does not work until you implement it"); + return Optional.of(account); // Optional.of() will throw NullPointerException if account is null } /** @@ -56,7 +61,8 @@ public static Optional optionalOfAccount(@Nonnull Account account) { * @return account from provider or defaultAccount */ public static Account getAccount(AccountProvider accountProvider, Account defaultAccount) { - throw new UnsupportedOperationException("Some people say that method does not work until you implement it"); + return accountProvider.getAccount() + .orElse(defaultAccount); // Optional#orElse() can be used to provide default value is case Optional is empty } /** @@ -67,7 +73,10 @@ public static Account getAccount(AccountProvider accountProvider, Account defaul * @param accountService */ public static void processAccount(AccountProvider accountProvider, AccountService accountService) { - throw new UnsupportedOperationException("Some people say that method does not work until you implement it"); + accountProvider.getAccount() + .ifPresentOrElse(accountService::processAccount, accountService::processWithNoAccount); + // one more declarative substitution of if-else operator. + // Please note its parameters: Consumer and Runnable } /** @@ -78,7 +87,12 @@ public static void processAccount(AccountProvider accountProvider, AccountServic * @return provided or generated account */ public static Account getOrGenerateAccount(AccountProvider accountProvider) { - throw new UnsupportedOperationException("Some people say that method does not work until you implement it"); + return accountProvider.getAccount() + .orElseGet(Accounts::generateAccount); // functionally it works exactly the same as Optional#orElse() + // however it is based on lazy initialization using Supplier interface, which means that default value + // will not be computed (created) until Supplier#get() is called, which means it will be only computed + // when Optional is empty. This method should be used in favor of Optional#orElse() when the creation of default + // value requires additional resources } /** @@ -88,7 +102,8 @@ public static Account getOrGenerateAccount(AccountProvider accountProvider) { * @return optional balance */ public static Optional retrieveBalance(AccountProvider accountProvider) { - throw new UnsupportedOperationException("Some people say that method does not work until you implement it"); + return accountProvider.getAccount() + .map(Account::getBalance); // a null-safe mapping that allows you to go from Optional object to its field } /** @@ -99,7 +114,9 @@ public static Optional retrieveBalance(AccountProvider accountProvid * @return provided account */ public static Account getAccount(AccountProvider accountProvider) { - throw new UnsupportedOperationException("Some people say that method does not work until you implement it"); + return accountProvider.getAccount() + .orElseThrow(() -> new AccountNotFoundException("No Account provided!")); // in case Optional is empty + // it allows to throw a custom exception } /** @@ -109,7 +126,9 @@ public static Account getAccount(AccountProvider accountProvider) { * @return optional credit balance */ public static Optional retrieveCreditBalance(CreditAccountProvider accountProvider) { - throw new UnsupportedOperationException("Some people say that method does not work until you implement it"); + return accountProvider.getAccount() + .flatMap(CreditAccount::getCreditBalance); // in case your getter already return Optional, you cannot use + // Optional#map() because it will create Optional>. In this case Optional#flatMap() should be used } @@ -121,7 +140,10 @@ public static Optional retrieveCreditBalance(CreditAccountProvider a * @return optional gmail account */ public static Optional retrieveAccountGmail(AccountProvider accountProvider) { - throw new UnsupportedOperationException("Some people say that method does not work until you implement it"); + return accountProvider.getAccount() + .filter(account -> account.getEmail().split("@")[1].equals("gmail.com")); + // in case you need to check if an Optional Account meets some criteria and return it or if it does not + // then return Optional.empty() and do that in a null-safe manner } /** @@ -134,7 +156,9 @@ public static Optional retrieveAccountGmail(AccountProvider accountProv * @return account got from either accountProvider or fallbackProvider */ public static Account getAccountWithFallback(AccountProvider accountProvider, AccountProvider fallbackProvider) { - throw new UnsupportedOperationException("Some people say that method does not work until you implement it"); + return accountProvider.getAccount() + .or(fallbackProvider::getAccount) // allows to use another Optional in case main Optional is empty + .orElseThrow(); // if both providers return Optional.empty() it throws NoSuchElementException } /** @@ -145,7 +169,9 @@ public static Account getAccountWithFallback(AccountProvider accountProvider, Ac * @return account with the highest balance */ public static Account getAccountWithMaxBalance(List accounts) { - throw new UnsupportedOperationException("Some people say that method does not work until you implement it"); + return accounts.stream() + .max(comparing(Account::getBalance)) // as you probably know Stream#min() and Stream#max() return Optional + .orElseThrow(); } /** @@ -155,7 +181,10 @@ public static Account getAccountWithMaxBalance(List accounts) { * @return the lowest balance values */ public static OptionalDouble findMinBalanceValue(List accounts) { - throw new UnsupportedOperationException("Some people say that method does not work until you implement it"); + return accounts.stream() + .map(Account::getBalance) // map all stream accounts to balances + .mapToDouble(BigDecimal::doubleValue) // map all balances to primitive double values (returns DoubleStream) + .min(); // Optional API provides special classes for primitives as well } /** @@ -165,7 +194,10 @@ public static OptionalDouble findMinBalanceValue(List accounts) { * @param accountService */ public static void processAccountWithMaxBalance(List accounts, AccountService accountService) { - throw new UnsupportedOperationException("Some people say that method does not work until you implement it"); + accounts.stream() + .max(comparing(Account::getBalance)) // returns Optional account + .ifPresent(accountService::processAccount); // declarative if statement and processing + // the last method requires Consumer as argument, it is implements using method reference } /** @@ -175,7 +207,11 @@ public static void processAccountWithMaxBalance(List accounts, AccountS * @return total credit balance */ public static double calculateTotalCreditBalance(List accounts) { - throw new UnsupportedOperationException("Some people say that method does not work until you implement it"); + return accounts.stream() + .map(CreditAccount::getCreditBalance) // transforms each element of stream into Optional + .flatMap(Optional::stream) // uses special Optional#stream() to filter all elements that are empty + .mapToDouble(BigDecimal::doubleValue) // transform BigDecimal into primitive double (returns DoubleStream) + .sum(); // calculates a sum of primitive double } }