diff --git a/.gitignore b/.gitignore
index 50cb889e5b..349efbcb67 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,8 +1,10 @@
*/bin/*
+bin/
*.class
# Package Files #
+*.jar
*.war
*.ear
@@ -21,11 +23,14 @@
*.iws
out/
+# VSCode
+.vscode/
+
# Mac
.DS_Store
# Maven
-log/
+log/*
target/
# Gradle
@@ -77,4 +82,12 @@ apache-avro/src/main/java/com/baeldung/avro/model/
jta/transaction-logs/
software-security/sql-injection-samples/derby.log
spring-soap/src/main/java/com/baeldung/springsoap/gen/
-/report-*.json
\ No newline at end of file
+/report-*.json
+transaction.log
+*-shell.log
+
+apache-cxf/cxf-aegis/baeldung.xml
+apache-fop/src/test/resources/input.xml
+apache-fop/src/test/resources/output_herold.pdf
+apache-fop/src/test/resources/output_html2fo.pdf
+apache-fop/src/test/resources/output_jtidy.pdf
\ No newline at end of file
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 5e86714a89..0000000000
--- a/.travis.yml
+++ /dev/null
@@ -1,24 +0,0 @@
-language: java
-
-before_install:
- - echo "MAVEN_OPTS='-Xmx2048M -Xss128M -XX:+CMSClassUnloadingEnabled -XX:+UseG1GC -XX:-UseGCOverheadLimit'" > ~/.mavenrc
-
-install: skip
-script: travis_wait 60 mvn -q install -Pdefault-first,default-second -Dgib.enabled=true
-
-sudo: required
-
-jdk:
- - oraclejdk8
-
-addons:
- apt:
- packages:
- - oracle-java8-installer
-
-cache:
- directories:
- - .autoconf
- - $HOME/.m2
-
-
diff --git a/JGit/README.md b/JGit/README.md
deleted file mode 100644
index 5c65f1101b..0000000000
--- a/JGit/README.md
+++ /dev/null
@@ -1,3 +0,0 @@
-## Relevant articles:
-
-- [A Guide to JGit](http://www.baeldung.com/jgit)
diff --git a/README.md b/README.md
index 1030cbb09c..4cad075cc3 100644
--- a/README.md
+++ b/README.md
@@ -1,12 +1,16 @@
-The "REST with Spring" Classes
+The Courses
==============================
-Here's the Master Class of REST With Spring (along with the newly announced Boot 2 material):
-**[>> THE REST WITH SPRING - MASTER CLASS](http://www.baeldung.com/rest-with-spring-course?utm_source=github&utm_medium=social&utm_content=tutorials&utm_campaign=rws#master-class)**
-And here's the Master Class of Learn Spring Security:
-**[>> LEARN SPRING SECURITY - MASTER CLASS](http://www.baeldung.com/learn-spring-security-course?utm_source=github&utm_medium=social&utm_content=tutorials&utm_campaign=lss#master-class)**
+Here's the new "Learn Spring" course:
+**[>> LEARN SPRING - THE MASTER CLASS](https://www.baeldung.com/learn-spring-course?utm_source=github&utm_medium=social&utm_content=tutorials&utm_campaign=ls#master-class)**
+
+Here's the Master Class of "REST With Spring" (along with the new announced Boot 2 material):
+**[>> THE REST WITH SPRING - MASTER CLASS](https://www.baeldung.com/rest-with-spring-course?utm_source=github&utm_medium=social&utm_content=tutorials&utm_campaign=rws#master-class)**
+
+And here's the Master Class of "Learn Spring Security":
+**[>> LEARN SPRING SECURITY - MASTER CLASS](https://www.baeldung.com/learn-spring-security-course?utm_source=github&utm_medium=social&utm_content=tutorials&utm_campaign=lss#master-class)**
@@ -15,7 +19,7 @@ Java and Spring Tutorials
This project is **a collection of small and focused tutorials** - each covering a single and well defined area of development in the Java ecosystem.
A strong focus of these is, of course, the Spring Framework - Spring, Spring Boot and Spring Security.
-In additional to Spring, the following technologies are in focus: `core Java`, `Jackson`, `HttpClient`, `Guava`.
+In additional to Spring, the modules here are covering a number of aspects in Java.
Building the project
@@ -32,8 +36,15 @@ Running a Spring Boot module
====================
To run a Spring Boot module run the command: `mvn spring-boot:run` in the module directory
-#Running Tests
+Working with the IDE
+====================
+This repo contains a large number of modules.
+When you're working with an individual module, there's no need to import all of them (or build all of them) - you can simply import that particular module in either Eclipse or IntelliJ.
+
+
+Running Tests
+=============
The command `mvn clean install` will run the unit tests in a module.
To run the integration tests, use the command `mvn clean install -Pintegration-lite-first`
diff --git a/Twitter4J/README.md b/Twitter4J/README.md
deleted file mode 100644
index 3057c1c4b2..0000000000
--- a/Twitter4J/README.md
+++ /dev/null
@@ -1,3 +0,0 @@
-### Relevant articles
-
-- [Introduction to Twitter4J](http://www.baeldung.com/twitter4j)
diff --git a/akka-http/README.md b/akka-http/README.md
index 3831b5079f..ebe6581ff6 100644
--- a/akka-http/README.md
+++ b/akka-http/README.md
@@ -1,3 +1,7 @@
-## Relevant articles:
+## Akka HTTP
+
+This module contains articles about Akka HTTP.
+
+### Relevant articles:
- [Introduction to Akka HTTP](https://www.baeldung.com/akka-http)
diff --git a/akka-streams/README.md b/akka-streams/README.md
index 7f2751422b..a59b7fde5c 100644
--- a/akka-streams/README.md
+++ b/akka-streams/README.md
@@ -1,3 +1,7 @@
+## Akka Streams
+
+This module contains articles about Akka Streams.
+
### Relevant articles
-- [Guide to Akka Streams](http://www.baeldung.com/akka-streams)
+- [Guide to Akka Streams](https://www.baeldung.com/akka-streams)
diff --git a/algorithms-genetic/README.md b/algorithms-genetic/README.md
index 39f8d59eee..1c9e831ac2 100644
--- a/algorithms-genetic/README.md
+++ b/algorithms-genetic/README.md
@@ -1,6 +1,10 @@
-## Relevant articles:
+## Genetic Algorithms
-- [Introduction to Jenetics Library](http://www.baeldung.com/jenetics)
-- [Ant Colony Optimization](http://www.baeldung.com/java-ant-colony-optimization)
+This module contains articles about genetic algorithms.
+
+### Relevant articles:
+
+- [Introduction to Jenetics Library](https://www.baeldung.com/jenetics)
+- [Ant Colony Optimization](https://www.baeldung.com/java-ant-colony-optimization)
- [Design a Genetic Algorithm in Java](https://www.baeldung.com/java-genetic-algorithm)
- [The Traveling Salesman Problem in Java](https://www.baeldung.com/java-simulated-annealing-for-traveling-salesman)
diff --git a/algorithms-miscellaneous-1/.gitignore b/algorithms-miscellaneous-1/.gitignore
deleted file mode 100644
index 30b2b7442c..0000000000
--- a/algorithms-miscellaneous-1/.gitignore
+++ /dev/null
@@ -1,4 +0,0 @@
-/target/
-.settings/
-.classpath
-.project
\ No newline at end of file
diff --git a/algorithms-miscellaneous-1/README.md b/algorithms-miscellaneous-1/README.md
index 479c2792f6..6a25f8cac8 100644
--- a/algorithms-miscellaneous-1/README.md
+++ b/algorithms-miscellaneous-1/README.md
@@ -1,18 +1,15 @@
-## Relevant articles:
+## Algorithms - Miscellaneous
-- [Validating Input With Finite Automata in Java](http://www.baeldung.com/java-finite-automata)
-- [Example of Hill Climbing Algorithm](http://www.baeldung.com/java-hill-climbing-algorithm)
-- [Monte Carlo Tree Search for Tic-Tac-Toe Game](http://www.baeldung.com/java-monte-carlo-tree-search)
-- [Binary Search Algorithm in Java](http://www.baeldung.com/java-binary-search)
-- [Introduction to Minimax Algorithm](http://www.baeldung.com/java-minimax-algorithm)
-- [How to Calculate Levenshtein Distance in Java?](http://www.baeldung.com/java-levenshtein-distance)
-- [How to Find the Kth Largest Element in Java](http://www.baeldung.com/java-kth-largest-element)
-- [Multi-Swarm Optimization Algorithm in Java](http://www.baeldung.com/java-multi-swarm-algorithm)
-- [String Search Algorithms for Large Texts](http://www.baeldung.com/java-full-text-search-algorithms)
-- [Check If a String Contains All The Letters of The Alphabet](https://www.baeldung.com/java-string-contains-all-letters)
-- [Find the Middle Element of a Linked List](http://www.baeldung.com/java-linked-list-middle-element)
-- [Calculate Factorial in Java](https://www.baeldung.com/java-calculate-factorial)
-- [Find Substrings That Are Palindromes in Java](https://www.baeldung.com/java-palindrome-substrings)
-- [Find the Longest Substring without Repeating Characters](https://www.baeldung.com/java-longest-substring-without-repeated-characters)
-- [Permutations of an Array in Java](https://www.baeldung.com/java-array-permutations)
-- [Generate Combinations in Java](https://www.baeldung.com/java-combinations-algorithm)
+This module contains articles about algorithms. Some classes of algorithms, e.g., [sorting](/../algorithms-sorting) and
+[genetic algorithms](/../algorithms-genetic), have their own dedicated modules.
+
+### Relevant articles:
+
+- [Validating Input With Finite Automata in Java](https://www.baeldung.com/java-finite-automata)
+- [Example of Hill Climbing Algorithm](https://www.baeldung.com/java-hill-climbing-algorithm)
+- [Monte Carlo Tree Search for Tic-Tac-Toe Game](https://www.baeldung.com/java-monte-carlo-tree-search)
+- [Binary Search Algorithm in Java](https://www.baeldung.com/java-binary-search)
+- [Introduction to Minimax Algorithm](https://www.baeldung.com/java-minimax-algorithm)
+- [How to Calculate Levenshtein Distance in Java?](https://www.baeldung.com/java-levenshtein-distance)
+- [How to Find the Kth Largest Element in Java](https://www.baeldung.com/java-kth-largest-element)
+- More articles: [[next -->]](/../algorithms-miscellaneous-2)
diff --git a/algorithms-miscellaneous-1/pom.xml b/algorithms-miscellaneous-1/pom.xml
index 30130208f8..affa66f147 100644
--- a/algorithms-miscellaneous-1/pom.xml
+++ b/algorithms-miscellaneous-1/pom.xml
@@ -42,7 +42,7 @@
com.github.dpaukov
combinatoricslib3
- 3.3.0
+ ${combinatoricslib3.version}
@@ -83,6 +83,7 @@
3.9.0
1.11
27.0.1-jre
+ 3.3.0
\ No newline at end of file
diff --git a/algorithms-miscellaneous-2/.gitignore b/algorithms-miscellaneous-2/.gitignore
deleted file mode 100644
index 30b2b7442c..0000000000
--- a/algorithms-miscellaneous-2/.gitignore
+++ /dev/null
@@ -1,4 +0,0 @@
-/target/
-.settings/
-.classpath
-.project
\ No newline at end of file
diff --git a/algorithms-miscellaneous-2/README.md b/algorithms-miscellaneous-2/README.md
index 462644dddb..7560fc4fe7 100644
--- a/algorithms-miscellaneous-2/README.md
+++ b/algorithms-miscellaneous-2/README.md
@@ -1,19 +1,16 @@
-## Relevant articles:
+## Algorithms - Miscellaneous
-- [Dijkstra Algorithm in Java](http://www.baeldung.com/java-dijkstra)
-- [Introduction to Cobertura](http://www.baeldung.com/cobertura)
-- [Test a Linked List for Cyclicity](http://www.baeldung.com/java-linked-list-cyclicity)
-- [Introduction to JGraphT](http://www.baeldung.com/jgrapht)
-- [A Maze Solver in Java](http://www.baeldung.com/java-solve-maze)
-- [Create a Sudoku Solver in Java](http://www.baeldung.com/java-sudoku)
-- [Displaying Money Amounts in Words](http://www.baeldung.com/java-money-into-words)
-- [A Collaborative Filtering Recommendation System in Java](http://www.baeldung.com/java-collaborative-filtering-recommendations)
-- [Check If Two Rectangles Overlap In Java](https://www.baeldung.com/java-check-if-two-rectangles-overlap)
-- [Calculate the Distance Between Two Points in Java](https://www.baeldung.com/java-distance-between-two-points)
-- [Find the Intersection of Two Lines in Java](https://www.baeldung.com/java-intersection-of-two-lines)
-- [Round Up to the Nearest Hundred](https://www.baeldung.com/java-round-up-nearest-hundred)
-- [Calculate Percentage in Java](https://www.baeldung.com/java-calculate-percentage)
-- [Converting Between Byte Arrays and Hexadecimal Strings in Java](https://www.baeldung.com/java-byte-arrays-hex-strings)
-- [Convert Latitude and Longitude to a 2D Point in Java](https://www.baeldung.com/java-convert-latitude-longitude)
-- [Reversing a Binary Tree in Java](https://www.baeldung.com/java-reversing-a-binary-tree)
-- [Find If Two Numbers Are Relatively Prime in Java](https://www.baeldung.com/java-two-relatively-prime-numbers)
+This module contains articles about algorithms. Some classes of algorithms, e.g., [sorting](/../algorithms-sorting) and
+[genetic algorithms](/../algorithms-genetic), have their own dedicated modules.
+
+### Relevant articles:
+
+- [Dijkstra Shortest Path Algorithm in Java](https://www.baeldung.com/java-dijkstra)
+- [Introduction to Cobertura](https://www.baeldung.com/cobertura)
+- [Test a Linked List for Cyclicity](https://www.baeldung.com/java-linked-list-cyclicity)
+- [Introduction to JGraphT](https://www.baeldung.com/jgrapht)
+- [A Maze Solver in Java](https://www.baeldung.com/java-solve-maze)
+- [Create a Sudoku Solver in Java](https://www.baeldung.com/java-sudoku)
+- [Displaying Money Amounts in Words](https://www.baeldung.com/java-money-into-words)
+- [A Collaborative Filtering Recommendation System in Java](https://www.baeldung.com/java-collaborative-filtering-recommendations)
+- More articles: [[<-- prev]](/../algorithms-miscellaneous-1) [[next -->]](/../algorithms-miscellaneous-3)
diff --git a/algorithms-miscellaneous-3/README.md b/algorithms-miscellaneous-3/README.md
index 4dd4b66ff2..a1456c6b20 100644
--- a/algorithms-miscellaneous-3/README.md
+++ b/algorithms-miscellaneous-3/README.md
@@ -1,6 +1,19 @@
-## Relevant articles:
+## Algorithms - Miscellaneous
+
+This module contains articles about algorithms. Some classes of algorithms, e.g., [sorting](/algorithms-sorting) and
+[genetic algorithms](/algorithms-genetic), have their own dedicated modules.
+
+## Relevant Articles:
- [Java Two Pointer Technique](https://www.baeldung.com/java-two-pointer-technique)
- [Implementing Simple State Machines with Java Enums](https://www.baeldung.com/java-enum-simple-state-machine)
-- [Converting Between Roman and Arabic Numerals in Java](http://www.baeldung.com/java-convert-roman-arabic)
-- [Practical Java Examples of the Big O Notation](http://www.baeldung.com/java-algorithm-complexity)
+- [Converting Between Roman and Arabic Numerals in Java](https://www.baeldung.com/java-convert-roman-arabic)
+- [Practical Java Examples of the Big O Notation](https://www.baeldung.com/java-algorithm-complexity)
+- [Checking If a List Is Sorted in Java](https://www.baeldung.com/java-check-if-list-sorted)
+- [Checking if a Java Graph has a Cycle](https://www.baeldung.com/java-graph-has-a-cycle)
+- [A Guide to the Folding Technique in Java](https://www.baeldung.com/folding-hashing-technique)
+- [Creating a Triangle with for Loops in Java](https://www.baeldung.com/java-print-triangle)
+- [Efficient Word Frequency Calculator in Java](https://www.baeldung.com/java-word-frequency)
+- [Interpolation Search in Java](https://www.baeldung.com/java-interpolation-search)
+- [The K-Means Clustering Algorithm in Java](https://www.baeldung.com/java-k-means-clustering-algorithm)
+- More articles: [[<-- prev]](/algorithms-miscellaneous-2) [[next -->]](/algorithms-miscellaneous-4)
diff --git a/algorithms-miscellaneous-3/pom.xml b/algorithms-miscellaneous-3/pom.xml
index c4017144c8..67923d37d7 100644
--- a/algorithms-miscellaneous-3/pom.xml
+++ b/algorithms-miscellaneous-3/pom.xml
@@ -1,5 +1,5 @@
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4.0.0
algorithms-miscellaneous-3
0.0.1-SNAPSHOT
@@ -18,6 +18,57 @@
${org.assertj.core.version}
test
+
+
+ org.apache.commons
+ commons-collections4
+ ${commons-collections4.version}
+
+
+
+ com.google.guava
+ guava
+ ${guava.version}
+
+
+
+ com.squareup.retrofit2
+ retrofit
+ ${retrofit.version}
+
+
+ com.squareup.retrofit2
+ converter-jackson
+ ${retrofit.version}
+
+
+
+ org.apache.commons
+ commons-lang3
+ 3.8.1
+
+
+
+ pl.pragmatists
+ JUnitParams
+ 1.1.0
+ test
+
+
+ org.openjdk.jmh
+ jmh-core
+ ${jmh-core.version}
+
+
+ org.openjdk.jmh
+ jmh-generator-annprocess
+ ${jmh-generator.version}
+
+
+ org.openjdk.jmh
+ jmh-generator-bytecode
+ ${jmh-generator.version}
+
@@ -34,6 +85,10 @@
3.9.0
+ 4.3
+ 28.0-jre
+ 2.6.0
+ 1.19
+ 1.19
-
\ No newline at end of file
diff --git a/algorithms-miscellaneous-3/src/main/java/com/baeldung/algorithms/breadthfirstsearch/BreadthFirstSearchAlgorithm.java b/algorithms-miscellaneous-3/src/main/java/com/baeldung/algorithms/breadthfirstsearch/BreadthFirstSearchAlgorithm.java
new file mode 100644
index 0000000000..9d301f9578
--- /dev/null
+++ b/algorithms-miscellaneous-3/src/main/java/com/baeldung/algorithms/breadthfirstsearch/BreadthFirstSearchAlgorithm.java
@@ -0,0 +1,54 @@
+package com.baeldung.algorithms.breadthfirstsearch;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.*;
+
+public class BreadthFirstSearchAlgorithm {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(BreadthFirstSearchAlgorithm.class);
+
+ public static Optional> search(T value, Tree root) {
+ Queue> queue = new ArrayDeque<>();
+ queue.add(root);
+
+ Tree currentNode;
+ while (!queue.isEmpty()) {
+ currentNode = queue.remove();
+ LOGGER.info("Visited node with value: {}", currentNode.getValue());
+
+ if (currentNode.getValue().equals(value)) {
+ return Optional.of(currentNode);
+ } else {
+ queue.addAll(currentNode.getChildren());
+ }
+ }
+
+ return Optional.empty();
+ }
+
+ public static Optional> search(T value, Node start) {
+ Queue> queue = new ArrayDeque<>();
+ queue.add(start);
+
+ Node currentNode;
+ Set> alreadyVisited = new HashSet<>();
+
+ while (!queue.isEmpty()) {
+ currentNode = queue.remove();
+ LOGGER.info("Visited node with value: {}", currentNode.getValue());
+
+ if (currentNode.getValue().equals(value)) {
+ return Optional.of(currentNode);
+ } else {
+ alreadyVisited.add(currentNode);
+ queue.addAll(currentNode.getNeighbors());
+ queue.removeAll(alreadyVisited);
+ }
+ }
+
+ return Optional.empty();
+ }
+
+}
diff --git a/algorithms-miscellaneous-3/src/main/java/com/baeldung/algorithms/breadthfirstsearch/Node.java b/algorithms-miscellaneous-3/src/main/java/com/baeldung/algorithms/breadthfirstsearch/Node.java
new file mode 100644
index 0000000000..54a589ae26
--- /dev/null
+++ b/algorithms-miscellaneous-3/src/main/java/com/baeldung/algorithms/breadthfirstsearch/Node.java
@@ -0,0 +1,31 @@
+package com.baeldung.algorithms.breadthfirstsearch;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+public class Node {
+
+ private T value;
+ private Set> neighbors;
+
+ public Node(T value) {
+ this.value = value;
+ this.neighbors = new HashSet<>();
+ }
+
+ public T getValue() {
+ return value;
+ }
+
+ public Set> getNeighbors() {
+ return Collections.unmodifiableSet(neighbors);
+ }
+
+ public void connect(Node node) {
+ if (this == node) throw new IllegalArgumentException("Can't connect node to itself");
+ this.neighbors.add(node);
+ node.neighbors.add(this);
+ }
+
+}
diff --git a/algorithms-miscellaneous-3/src/main/java/com/baeldung/algorithms/breadthfirstsearch/Tree.java b/algorithms-miscellaneous-3/src/main/java/com/baeldung/algorithms/breadthfirstsearch/Tree.java
new file mode 100644
index 0000000000..842d2837ff
--- /dev/null
+++ b/algorithms-miscellaneous-3/src/main/java/com/baeldung/algorithms/breadthfirstsearch/Tree.java
@@ -0,0 +1,34 @@
+package com.baeldung.algorithms.breadthfirstsearch;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public class Tree {
+
+ private T value;
+ private List> children;
+
+ private Tree(T value) {
+ this.value = value;
+ this.children = new ArrayList<>();
+ }
+
+ public static Tree of(T value) {
+ return new Tree<>(value);
+ }
+
+ public T getValue() {
+ return value;
+ }
+
+ public List> getChildren() {
+ return Collections.unmodifiableList(children);
+ }
+
+ public Tree addChild(T value) {
+ Tree newChild = new Tree<>(value);
+ children.add(newChild);
+ return newChild;
+ }
+}
diff --git a/algorithms-miscellaneous-3/src/main/java/com/baeldung/algorithms/checksortedlist/Employee.java b/algorithms-miscellaneous-3/src/main/java/com/baeldung/algorithms/checksortedlist/Employee.java
new file mode 100644
index 0000000000..796932728b
--- /dev/null
+++ b/algorithms-miscellaneous-3/src/main/java/com/baeldung/algorithms/checksortedlist/Employee.java
@@ -0,0 +1,34 @@
+package com.baeldung.algorithms.checksortedlist;
+
+public class Employee {
+
+ long id;
+
+ String name;
+
+ public Employee() {
+ }
+
+ public Employee(long id, String name) {
+ super();
+ this.id = id;
+ this.name = name;
+ }
+
+ public long getId() {
+ return id;
+ }
+
+ public void setId(long id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+}
diff --git a/algorithms-miscellaneous-3/src/main/java/com/baeldung/algorithms/checksortedlist/SortedListChecker.java b/algorithms-miscellaneous-3/src/main/java/com/baeldung/algorithms/checksortedlist/SortedListChecker.java
new file mode 100644
index 0000000000..ab6eb6bc14
--- /dev/null
+++ b/algorithms-miscellaneous-3/src/main/java/com/baeldung/algorithms/checksortedlist/SortedListChecker.java
@@ -0,0 +1,87 @@
+package com.baeldung.algorithms.checksortedlist;
+
+import static org.apache.commons.collections4.CollectionUtils.isEmpty;
+
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+
+import com.google.common.collect.Comparators;
+import com.google.common.collect.Ordering;;
+
+public class SortedListChecker {
+
+ private SortedListChecker() {
+ throw new AssertionError();
+ }
+
+ public static boolean checkIfSortedUsingIterativeApproach(List listOfStrings) {
+ if (isEmpty(listOfStrings) || listOfStrings.size() == 1) {
+ return true;
+ }
+
+ Iterator iter = listOfStrings.iterator();
+ String current, previous = iter.next();
+ while (iter.hasNext()) {
+ current = iter.next();
+ if (previous.compareTo(current) > 0) {
+ return false;
+ }
+ previous = current;
+ }
+ return true;
+ }
+
+ public static boolean checkIfSortedUsingIterativeApproach(List employees, Comparator employeeComparator) {
+ if (isEmpty(employees) || employees.size() == 1) {
+ return true;
+ }
+
+ Iterator iter = employees.iterator();
+ Employee current, previous = iter.next();
+ while (iter.hasNext()) {
+ current = iter.next();
+ if (employeeComparator.compare(previous, current) > 0) {
+ return false;
+ }
+ previous = current;
+ }
+ return true;
+ }
+
+ public static boolean checkIfSortedUsingRecursion(List listOfStrings) {
+ return isSortedRecursive(listOfStrings, listOfStrings.size());
+ }
+
+ public static boolean isSortedRecursive(List listOfStrings, int index) {
+ if (index < 2) {
+ return true;
+ } else if (listOfStrings.get(index - 2)
+ .compareTo(listOfStrings.get(index - 1)) > 0) {
+ return false;
+ } else {
+ return isSortedRecursive(listOfStrings, index - 1);
+ }
+ }
+
+ public static boolean checkIfSortedUsingOrderingClass(List listOfStrings) {
+ return Ordering. natural()
+ .isOrdered(listOfStrings);
+ }
+
+ public static boolean checkIfSortedUsingOrderingClass(List employees, Comparator employeeComparator) {
+ return Ordering.from(employeeComparator)
+ .isOrdered(employees);
+ }
+
+ public static boolean checkIfSortedUsingOrderingClassHandlingNull(List listOfStrings) {
+ return Ordering. natural()
+ .nullsLast()
+ .isOrdered(listOfStrings);
+ }
+
+ public static boolean checkIfSortedUsingComparators(List listOfStrings) {
+ return Comparators.isInOrder(listOfStrings, Comparator. naturalOrder());
+ }
+
+}
diff --git a/algorithms-miscellaneous-3/src/main/java/com/baeldung/algorithms/interpolationsearch/InterpolationSearch.java b/algorithms-miscellaneous-3/src/main/java/com/baeldung/algorithms/interpolationsearch/InterpolationSearch.java
new file mode 100644
index 0000000000..91423eeaa8
--- /dev/null
+++ b/algorithms-miscellaneous-3/src/main/java/com/baeldung/algorithms/interpolationsearch/InterpolationSearch.java
@@ -0,0 +1,35 @@
+package com.baeldung.algorithms.interpolationsearch;
+
+public class InterpolationSearch {
+
+ public static int interpolationSearch(int[] data, int item) {
+
+ int highEnd = (data.length - 1);
+ int lowEnd = 0;
+
+ while (item >= data[lowEnd] && item <= data[highEnd] && lowEnd <= highEnd) {
+
+ int probe = lowEnd + (highEnd - lowEnd) * (item - data[lowEnd]) / (data[highEnd] - data[lowEnd]);
+
+ if (highEnd == lowEnd) {
+ if (data[lowEnd] == item) {
+ return lowEnd;
+ } else {
+ return -1;
+ }
+ }
+
+ if (data[probe] == item) {
+ return probe;
+ }
+
+ if (data[probe] < item) {
+ lowEnd = probe + 1;
+ } else {
+ highEnd = probe - 1;
+ }
+ }
+ return -1;
+ }
+
+}
diff --git a/algorithms-miscellaneous-3/src/main/java/com/baeldung/algorithms/kmeans/Centroid.java b/algorithms-miscellaneous-3/src/main/java/com/baeldung/algorithms/kmeans/Centroid.java
new file mode 100644
index 0000000000..523d5b56a5
--- /dev/null
+++ b/algorithms-miscellaneous-3/src/main/java/com/baeldung/algorithms/kmeans/Centroid.java
@@ -0,0 +1,45 @@
+package com.baeldung.algorithms.kmeans;
+
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * Encapsulates all coordinates for a particular cluster centroid.
+ */
+public class Centroid {
+
+ /**
+ * The centroid coordinates.
+ */
+ private final Map coordinates;
+
+ public Centroid(Map coordinates) {
+ this.coordinates = coordinates;
+ }
+
+ public Map getCoordinates() {
+ return coordinates;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ Centroid centroid = (Centroid) o;
+ return Objects.equals(getCoordinates(), centroid.getCoordinates());
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(getCoordinates());
+ }
+
+ @Override
+ public String toString() {
+ return "Centroid " + coordinates;
+ }
+}
diff --git a/algorithms-miscellaneous-3/src/main/java/com/baeldung/algorithms/kmeans/Distance.java b/algorithms-miscellaneous-3/src/main/java/com/baeldung/algorithms/kmeans/Distance.java
new file mode 100644
index 0000000000..30723cb6b3
--- /dev/null
+++ b/algorithms-miscellaneous-3/src/main/java/com/baeldung/algorithms/kmeans/Distance.java
@@ -0,0 +1,20 @@
+package com.baeldung.algorithms.kmeans;
+
+import java.util.Map;
+
+/**
+ * Defines a contract to calculate distance between two feature vectors. The less the
+ * calculated distance, the more two items are similar to each other.
+ */
+public interface Distance {
+
+ /**
+ * Calculates the distance between two feature vectors.
+ *
+ * @param f1 The first set of features.
+ * @param f2 The second set of features.
+ * @return Calculated distance.
+ * @throws IllegalArgumentException If the given feature vectors are invalid.
+ */
+ double calculate(Map f1, Map f2);
+}
diff --git a/algorithms-miscellaneous-3/src/main/java/com/baeldung/algorithms/kmeans/Errors.java b/algorithms-miscellaneous-3/src/main/java/com/baeldung/algorithms/kmeans/Errors.java
new file mode 100644
index 0000000000..3228876051
--- /dev/null
+++ b/algorithms-miscellaneous-3/src/main/java/com/baeldung/algorithms/kmeans/Errors.java
@@ -0,0 +1,23 @@
+package com.baeldung.algorithms.kmeans;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Encapsulates methods to calculates errors between centroid and the cluster members.
+ */
+public class Errors {
+
+ public static double sse(Map> clustered, Distance distance) {
+ double sum = 0;
+ for (Map.Entry> entry : clustered.entrySet()) {
+ Centroid centroid = entry.getKey();
+ for (Record record : entry.getValue()) {
+ double d = distance.calculate(centroid.getCoordinates(), record.getFeatures());
+ sum += Math.pow(d, 2);
+ }
+ }
+
+ return sum;
+ }
+}
diff --git a/algorithms-miscellaneous-3/src/main/java/com/baeldung/algorithms/kmeans/EuclideanDistance.java b/algorithms-miscellaneous-3/src/main/java/com/baeldung/algorithms/kmeans/EuclideanDistance.java
new file mode 100644
index 0000000000..193d9afed1
--- /dev/null
+++ b/algorithms-miscellaneous-3/src/main/java/com/baeldung/algorithms/kmeans/EuclideanDistance.java
@@ -0,0 +1,26 @@
+package com.baeldung.algorithms.kmeans;
+
+import java.util.Map;
+
+/**
+ * Calculates the distance between two items using the Euclidean formula.
+ */
+public class EuclideanDistance implements Distance {
+
+ @Override
+ public double calculate(Map f1, Map f2) {
+ if (f1 == null || f2 == null) {
+ throw new IllegalArgumentException("Feature vectors can't be null");
+ }
+
+ double sum = 0;
+ for (String key : f1.keySet()) {
+ Double v1 = f1.get(key);
+ Double v2 = f2.get(key);
+
+ if (v1 != null && v2 != null) sum += Math.pow(v1 - v2, 2);
+ }
+
+ return Math.sqrt(sum);
+ }
+}
diff --git a/algorithms-miscellaneous-3/src/main/java/com/baeldung/algorithms/kmeans/KMeans.java b/algorithms-miscellaneous-3/src/main/java/com/baeldung/algorithms/kmeans/KMeans.java
new file mode 100644
index 0000000000..1fb8541ff9
--- /dev/null
+++ b/algorithms-miscellaneous-3/src/main/java/com/baeldung/algorithms/kmeans/KMeans.java
@@ -0,0 +1,236 @@
+package com.baeldung.algorithms.kmeans;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+import java.util.Set;
+
+import static java.util.stream.Collectors.toList;
+import static java.util.stream.Collectors.toSet;
+
+/**
+ * Encapsulates an implementation of KMeans clustering algorithm.
+ *
+ * @author Ali Dehghani
+ */
+public class KMeans {
+
+ private KMeans() {
+ throw new IllegalAccessError("You shouldn't call this constructor");
+ }
+
+ /**
+ * Will be used to generate random numbers.
+ */
+ private static final Random random = new Random();
+
+ /**
+ * Performs the K-Means clustering algorithm on the given dataset.
+ *
+ * @param records The dataset.
+ * @param k Number of Clusters.
+ * @param distance To calculate the distance between two items.
+ * @param maxIterations Upper bound for the number of iterations.
+ * @return K clusters along with their features.
+ */
+ public static Map> fit(List records, int k, Distance distance, int maxIterations) {
+ applyPreconditions(records, k, distance, maxIterations);
+
+ List centroids = randomCentroids(records, k);
+ Map> clusters = new HashMap<>();
+ Map> lastState = new HashMap<>();
+
+ // iterate for a pre-defined number of times
+ for (int i = 0; i < maxIterations; i++) {
+ boolean isLastIteration = i == maxIterations - 1;
+
+ // in each iteration we should find the nearest centroid for each record
+ for (Record record : records) {
+ Centroid centroid = nearestCentroid(record, centroids, distance);
+ assignToCluster(clusters, record, centroid);
+ }
+
+ // if the assignment does not change, then the algorithm terminates
+ boolean shouldTerminate = isLastIteration || clusters.equals(lastState);
+ lastState = clusters;
+ if (shouldTerminate) {
+ break;
+ }
+
+ // at the end of each iteration we should relocate the centroids
+ centroids = relocateCentroids(clusters);
+ clusters = new HashMap<>();
+ }
+
+ return lastState;
+ }
+
+ /**
+ * Move all cluster centroids to the average of all assigned features.
+ *
+ * @param clusters The current cluster configuration.
+ * @return Collection of new and relocated centroids.
+ */
+ private static List relocateCentroids(Map> clusters) {
+ return clusters
+ .entrySet()
+ .stream()
+ .map(e -> average(e.getKey(), e.getValue()))
+ .collect(toList());
+ }
+
+ /**
+ * Moves the given centroid to the average position of all assigned features. If
+ * the centroid has no feature in its cluster, then there would be no need for a
+ * relocation. Otherwise, for each entry we calculate the average of all records
+ * first by summing all the entries and then dividing the final summation value by
+ * the number of records.
+ *
+ * @param centroid The centroid to move.
+ * @param records The assigned features.
+ * @return The moved centroid.
+ */
+ private static Centroid average(Centroid centroid, List records) {
+ // if this cluster is empty, then we shouldn't move the centroid
+ if (records == null || records.isEmpty()) {
+ return centroid;
+ }
+
+ // Since some records don't have all possible attributes, we initialize
+ // average coordinates equal to current centroid coordinates
+ Map average = centroid.getCoordinates();
+
+ // The average function works correctly if we clear all coordinates corresponding
+ // to present record attributes
+ records
+ .stream()
+ .flatMap(e -> e
+ .getFeatures()
+ .keySet()
+ .stream())
+ .forEach(k -> average.put(k, 0.0));
+
+ for (Record record : records) {
+ record
+ .getFeatures()
+ .forEach((k, v) -> average.compute(k, (k1, currentValue) -> v + currentValue));
+ }
+
+ average.forEach((k, v) -> average.put(k, v / records.size()));
+
+ return new Centroid(average);
+ }
+
+ /**
+ * Assigns a feature vector to the given centroid. If this is the first assignment for this centroid,
+ * first we should create the list.
+ *
+ * @param clusters The current cluster configuration.
+ * @param record The feature vector.
+ * @param centroid The centroid.
+ */
+ private static void assignToCluster(Map> clusters, Record record, Centroid centroid) {
+ clusters.compute(centroid, (key, list) -> {
+ if (list == null) {
+ list = new ArrayList<>();
+ }
+
+ list.add(record);
+ return list;
+ });
+ }
+
+ /**
+ * With the help of the given distance calculator, iterates through centroids and finds the
+ * nearest one to the given record.
+ *
+ * @param record The feature vector to find a centroid for.
+ * @param centroids Collection of all centroids.
+ * @param distance To calculate the distance between two items.
+ * @return The nearest centroid to the given feature vector.
+ */
+ private static Centroid nearestCentroid(Record record, List centroids, Distance distance) {
+ double minimumDistance = Double.MAX_VALUE;
+ Centroid nearest = null;
+
+ for (Centroid centroid : centroids) {
+ double currentDistance = distance.calculate(record.getFeatures(), centroid.getCoordinates());
+
+ if (currentDistance < minimumDistance) {
+ minimumDistance = currentDistance;
+ nearest = centroid;
+ }
+ }
+
+ return nearest;
+ }
+
+ /**
+ * Generates k random centroids. Before kicking-off the centroid generation process,
+ * first we calculate the possible value range for each attribute. Then when
+ * we're going to generate the centroids, we generate random coordinates in
+ * the [min, max] range for each attribute.
+ *
+ * @param records The dataset which helps to calculate the [min, max] range for
+ * each attribute.
+ * @param k Number of clusters.
+ * @return Collections of randomly generated centroids.
+ */
+ private static List randomCentroids(List records, int k) {
+ List centroids = new ArrayList<>();
+ Map maxs = new HashMap<>();
+ Map mins = new HashMap<>();
+
+ for (Record record : records) {
+ record
+ .getFeatures()
+ .forEach((key, value) -> {
+ // compares the value with the current max and choose the bigger value between them
+ maxs.compute(key, (k1, max) -> max == null || value > max ? value : max);
+
+ // compare the value with the current min and choose the smaller value between them
+ mins.compute(key, (k1, min) -> min == null || value < min ? value : min);
+ });
+ }
+
+ Set attributes = records
+ .stream()
+ .flatMap(e -> e
+ .getFeatures()
+ .keySet()
+ .stream())
+ .collect(toSet());
+ for (int i = 0; i < k; i++) {
+ Map coordinates = new HashMap<>();
+ for (String attribute : attributes) {
+ double max = maxs.get(attribute);
+ double min = mins.get(attribute);
+ coordinates.put(attribute, random.nextDouble() * (max - min) + min);
+ }
+
+ centroids.add(new Centroid(coordinates));
+ }
+
+ return centroids;
+ }
+
+ private static void applyPreconditions(List records, int k, Distance distance, int maxIterations) {
+ if (records == null || records.isEmpty()) {
+ throw new IllegalArgumentException("The dataset can't be empty");
+ }
+
+ if (k <= 1) {
+ throw new IllegalArgumentException("It doesn't make sense to have less than or equal to 1 cluster");
+ }
+
+ if (distance == null) {
+ throw new IllegalArgumentException("The distance calculator is required");
+ }
+
+ if (maxIterations <= 0) {
+ throw new IllegalArgumentException("Max iterations should be a positive number");
+ }
+ }
+}
diff --git a/algorithms-miscellaneous-3/src/main/java/com/baeldung/algorithms/kmeans/LastFm.java b/algorithms-miscellaneous-3/src/main/java/com/baeldung/algorithms/kmeans/LastFm.java
new file mode 100644
index 0000000000..4694a845af
--- /dev/null
+++ b/algorithms-miscellaneous-3/src/main/java/com/baeldung/algorithms/kmeans/LastFm.java
@@ -0,0 +1,144 @@
+package com.baeldung.algorithms.kmeans;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import okhttp3.OkHttpClient;
+import retrofit2.Retrofit;
+import retrofit2.converter.jackson.JacksonConverterFactory;
+
+import static java.util.stream.Collectors.toSet;
+
+public class LastFm {
+
+ private static OkHttpClient okHttp = new OkHttpClient.Builder()
+ .addInterceptor(new LastFmService.Authenticator("put your API key here"))
+ .build();
+
+ private static Retrofit retrofit = new Retrofit.Builder()
+ .client(okHttp)
+ .addConverterFactory(JacksonConverterFactory.create())
+ .baseUrl("http://ws.audioscrobbler.com/")
+ .build();
+
+ private static LastFmService lastFm = retrofit.create(LastFmService.class);
+
+ private static ObjectMapper mapper = new ObjectMapper();
+
+ public static void main(String[] args) throws IOException {
+ List artists = getTop100Artists();
+ Set tags = getTop100Tags();
+ List records = datasetWithTaggedArtists(artists, tags);
+
+ Map> clusters = KMeans.fit(records, 7, new EuclideanDistance(), 1000);
+ // Print the cluster configuration
+ clusters.forEach((key, value) -> {
+ System.out.println("------------------------------ CLUSTER -----------------------------------");
+
+ System.out.println(sortedCentroid(key));
+ String members = String.join(", ", value
+ .stream()
+ .map(Record::getDescription)
+ .collect(toSet()));
+ System.out.print(members);
+
+ System.out.println();
+ System.out.println();
+ });
+
+ Map json = convertToD3CompatibleMap(clusters);
+ System.out.println(mapper.writeValueAsString(json));
+ }
+
+ private static Map convertToD3CompatibleMap(Map> clusters) {
+ Map json = new HashMap<>();
+ json.put("name", "Musicians");
+ List> children = new ArrayList<>();
+ clusters.forEach((key, value) -> {
+ Map child = new HashMap<>();
+ child.put("name", dominantGenre(sortedCentroid(key)));
+ List> nested = new ArrayList<>();
+ for (Record record : value) {
+ nested.add(Collections.singletonMap("name", record.getDescription()));
+ }
+ child.put("children", nested);
+
+ children.add(child);
+ });
+ json.put("children", children);
+ return json;
+ }
+
+ private static String dominantGenre(Centroid centroid) {
+ return centroid
+ .getCoordinates()
+ .keySet()
+ .stream()
+ .limit(2)
+ .collect(Collectors.joining(", "));
+ }
+
+ private static Centroid sortedCentroid(Centroid key) {
+ List> entries = new ArrayList<>(key
+ .getCoordinates()
+ .entrySet());
+ entries.sort((e1, e2) -> e2
+ .getValue()
+ .compareTo(e1.getValue()));
+
+ Map sorted = new LinkedHashMap<>();
+ for (Map.Entry entry : entries) {
+ sorted.put(entry.getKey(), entry.getValue());
+ }
+
+ return new Centroid(sorted);
+ }
+
+ private static List datasetWithTaggedArtists(List artists, Set topTags) throws IOException {
+ List records = new ArrayList<>();
+ for (String artist : artists) {
+ Map tags = lastFm
+ .topTagsFor(artist)
+ .execute()
+ .body()
+ .all();
+
+ // Only keep popular tags.
+ tags
+ .entrySet()
+ .removeIf(e -> !topTags.contains(e.getKey()));
+
+ records.add(new Record(artist, tags));
+ }
+ return records;
+ }
+
+ private static Set getTop100Tags() throws IOException {
+ return lastFm
+ .topTags()
+ .execute()
+ .body()
+ .all();
+ }
+
+ private static List getTop100Artists() throws IOException {
+ List artists = new ArrayList<>();
+ for (int i = 1; i <= 2; i++) {
+ artists.addAll(lastFm
+ .topArtists(i)
+ .execute()
+ .body()
+ .all());
+ }
+
+ return artists;
+ }
+}
diff --git a/algorithms-miscellaneous-3/src/main/java/com/baeldung/algorithms/kmeans/LastFmService.java b/algorithms-miscellaneous-3/src/main/java/com/baeldung/algorithms/kmeans/LastFmService.java
new file mode 100644
index 0000000000..db57deb888
--- /dev/null
+++ b/algorithms-miscellaneous-3/src/main/java/com/baeldung/algorithms/kmeans/LastFmService.java
@@ -0,0 +1,118 @@
+package com.baeldung.algorithms.kmeans;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import okhttp3.HttpUrl;
+import okhttp3.Interceptor;
+import okhttp3.Request;
+import okhttp3.Response;
+import retrofit2.Call;
+import retrofit2.http.GET;
+import retrofit2.http.Query;
+
+import static com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility.ANY;
+import static java.util.stream.Collectors.toList;
+
+public interface LastFmService {
+
+ @GET("/2.0/?method=chart.gettopartists&format=json&limit=50")
+ Call topArtists(@Query("page") int page);
+
+ @GET("/2.0/?method=artist.gettoptags&format=json&limit=20&autocorrect=1")
+ Call topTagsFor(@Query("artist") String artist);
+
+ @GET("/2.0/?method=chart.gettoptags&format=json&limit=100")
+ Call topTags();
+
+ /**
+ * HTTP interceptor to intercept all HTTP requests and add the API key to them.
+ */
+ class Authenticator implements Interceptor {
+
+ private final String apiKey;
+
+ Authenticator(String apiKey) {
+ this.apiKey = apiKey;
+ }
+
+ @Override
+ public Response intercept(Chain chain) throws IOException {
+ HttpUrl url = chain
+ .request()
+ .url()
+ .newBuilder()
+ .addQueryParameter("api_key", apiKey)
+ .build();
+ Request request = chain
+ .request()
+ .newBuilder()
+ .url(url)
+ .build();
+
+ return chain.proceed(request);
+ }
+ }
+
+ @JsonAutoDetect(fieldVisibility = ANY)
+ class TopTags {
+
+ private Map tags;
+
+ @SuppressWarnings("unchecked")
+ public Set all() {
+ List> topTags = (List>) tags.get("tag");
+ return topTags
+ .stream()
+ .map(e -> ((String) e.get("name")))
+ .collect(Collectors.toSet());
+ }
+ }
+
+ @JsonAutoDetect(fieldVisibility = ANY)
+ class Tags {
+
+ @JsonProperty("toptags") private Map topTags;
+
+ @SuppressWarnings("unchecked")
+ public Map all() {
+ try {
+ Map all = new HashMap<>();
+ List> tags = (List>) topTags.get("tag");
+ for (Map tag : tags) {
+ all.put(((String) tag.get("name")), ((Integer) tag.get("count")).doubleValue());
+ }
+
+ return all;
+ } catch (Exception e) {
+ return Collections.emptyMap();
+ }
+ }
+ }
+
+ @JsonAutoDetect(fieldVisibility = ANY)
+ class Artists {
+
+ private Map artists;
+
+ @SuppressWarnings("unchecked")
+ public List all() {
+ try {
+ List> artists = (List>) this.artists.get("artist");
+ return artists
+ .stream()
+ .map(e -> ((String) e.get("name")))
+ .collect(toList());
+ } catch (Exception e) {
+ return Collections.emptyList();
+ }
+ }
+ }
+}
diff --git a/algorithms-miscellaneous-3/src/main/java/com/baeldung/algorithms/kmeans/Record.java b/algorithms-miscellaneous-3/src/main/java/com/baeldung/algorithms/kmeans/Record.java
new file mode 100644
index 0000000000..d2a7b61c62
--- /dev/null
+++ b/algorithms-miscellaneous-3/src/main/java/com/baeldung/algorithms/kmeans/Record.java
@@ -0,0 +1,65 @@
+package com.baeldung.algorithms.kmeans;
+
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * Encapsulates all feature values for a few attributes. Optionally each record
+ * can be described with the {@link #description} field.
+ */
+public class Record {
+
+ /**
+ * The record description. For example, this can be the artist name for the famous musician
+ * example.
+ */
+ private final String description;
+
+ /**
+ * Encapsulates all attributes and their corresponding values, i.e. features.
+ */
+ private final Map features;
+
+ public Record(String description, Map features) {
+ this.description = description;
+ this.features = features;
+ }
+
+ public Record(Map features) {
+ this("", features);
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public Map getFeatures() {
+ return features;
+ }
+
+ @Override
+ public String toString() {
+ String prefix = description == null || description
+ .trim()
+ .isEmpty() ? "Record" : description;
+
+ return prefix + ": " + features;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ Record record = (Record) o;
+ return Objects.equals(getDescription(), record.getDescription()) && Objects.equals(getFeatures(), record.getFeatures());
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(getDescription(), getFeatures());
+ }
+}
diff --git a/algorithms-miscellaneous-3/src/main/java/com/baeldung/algorithms/printtriangles/PrintTriangleExamples.java b/algorithms-miscellaneous-3/src/main/java/com/baeldung/algorithms/printtriangles/PrintTriangleExamples.java
new file mode 100644
index 0000000000..156766f382
--- /dev/null
+++ b/algorithms-miscellaneous-3/src/main/java/com/baeldung/algorithms/printtriangles/PrintTriangleExamples.java
@@ -0,0 +1,61 @@
+package com.baeldung.algorithms.printtriangles;
+
+import org.apache.commons.lang3.StringUtils;
+
+public class PrintTriangleExamples {
+
+ public static String printARightTriangle(int N) {
+ StringBuilder result = new StringBuilder();
+ for (int r = 1; r <= N; r++) {
+ for (int j = 1; j <= r; j++) {
+ result.append("*");
+ }
+ result.append(System.lineSeparator());
+ }
+ return result.toString();
+ }
+
+ public static String printAnIsoscelesTriangle(int N) {
+ StringBuilder result = new StringBuilder();
+ for (int r = 1; r <= N; r++) {
+ for (int sp = 1; sp <= N - r; sp++) {
+ result.append(" ");
+ }
+ for (int c = 1; c <= (r * 2) - 1; c++) {
+ result.append("*");
+ }
+ result.append(System.lineSeparator());
+ }
+ return result.toString();
+ }
+
+ public static String printAnIsoscelesTriangleUsingStringUtils(int N) {
+ StringBuilder result = new StringBuilder();
+
+ for (int r = 1; r <= N; r++) {
+ result.append(StringUtils.repeat(' ', N - r));
+ result.append(StringUtils.repeat('*', 2 * r - 1));
+ result.append(System.lineSeparator());
+ }
+ return result.toString();
+ }
+
+ public static String printAnIsoscelesTriangleUsingSubstring(int N) {
+ StringBuilder result = new StringBuilder();
+ String helperString = StringUtils.repeat(' ', N - 1) + StringUtils.repeat('*', N * 2 - 1);
+
+ for (int r = 0; r < N; r++) {
+ result.append(helperString.substring(r, N + 2 * r));
+ result.append(System.lineSeparator());
+ }
+ return result.toString();
+ }
+
+ public static void main(String[] args) {
+ System.out.println(printARightTriangle(5));
+ System.out.println(printAnIsoscelesTriangle(5));
+ System.out.println(printAnIsoscelesTriangleUsingStringUtils(5));
+ System.out.println(printAnIsoscelesTriangleUsingSubstring(5));
+ }
+
+}
\ No newline at end of file
diff --git a/algorithms-miscellaneous-3/src/main/resources/kmeans/artists.json b/algorithms-miscellaneous-3/src/main/resources/kmeans/artists.json
new file mode 100644
index 0000000000..6cee0d1de2
--- /dev/null
+++ b/algorithms-miscellaneous-3/src/main/resources/kmeans/artists.json
@@ -0,0 +1,3384 @@
+[
+ {
+ "description": "Billie Eilish",
+ "features": {
+ "singer-songwriter": 3.0,
+ "american": 23.0,
+ "british": 3.0,
+ "experimental": 3.0,
+ "rnb": 3.0,
+ "female vocalists": 49.0,
+ "indie": 58.0,
+ "chillout": 12.0,
+ "ambient": 3.0,
+ "pop": 100.0,
+ "synthpop": 3.0,
+ "electronic": 58.0,
+ "indie pop": 83.0,
+ "seen live": 18.0,
+ "alternative": 40.0,
+ "female vocalist": 9.0
+ }
+ },
+ {
+ "description": "Ed Sheeran",
+ "features": {
+ "indie": 12.0,
+ "singer-songwriter": 78.0,
+ "male vocalists": 13.0,
+ "soul": 3.0,
+ "chillout": 2.0,
+ "Mellow": 2.0,
+ "british": 82.0,
+ "pop": 23.0,
+ "rock": 4.0,
+ "rap": 2.0,
+ "indie pop": 3.0,
+ "seen live": 21.0,
+ "Hip-Hop": 4.0,
+ "acoustic": 100.0,
+ "alternative": 8.0,
+ "guitar": 2.0,
+ "00s": 2.0,
+ "Soundtrack": 1.0,
+ "Love": 2.0,
+ "folk": 12.0
+ }
+ },
+ {
+ "description": "Lil Nas X",
+ "features": {
+ "country": 54.0,
+ "male vocalists": 8.0,
+ "american": 24.0,
+ "rock": 16.0,
+ "pop": 8.0,
+ "hip hop": 47.0,
+ "rap": 62.0,
+ "Hip-Hop": 100.0,
+ "alternative": 24.0
+ }
+ },
+ {
+ "description": "Queen",
+ "features": {
+ "favorites": 2.0,
+ "male vocalists": 3.0,
+ "british": 17.0,
+ "classic rock": 100.0,
+ "rock": 81.0,
+ "pop": 7.0,
+ "heavy metal": 2.0,
+ "oldies": 2.0,
+ "seen live": 2.0,
+ "alternative": 4.0,
+ "metal": 2.0,
+ "Progressive rock": 6.0,
+ "alternative rock": 1.0,
+ "hard rock": 38.0,
+ "70s": 8.0,
+ "80s": 41.0,
+ "90s": 2.0
+ }
+ },
+ {
+ "description": "Ariana Grande",
+ "features": {
+ "american": 13.0,
+ "beautiful": 2.0,
+ "dance": 11.0,
+ "House": 2.0,
+ "Soundtrack": 3.0,
+ "chillout": 3.0,
+ "pop": 100.0,
+ "rock": 2.0,
+ "electronic": 3.0,
+ "seen live": 3.0,
+ "female vocalist": 3.0,
+ "singer-songwriter": 5.0,
+ "rnb": 51.0,
+ "female vocalists": 54.0,
+ "soul": 10.0,
+ "synthpop": 2.0,
+ "hip hop": 3.0,
+ "Hip-Hop": 3.0
+ }
+ },
+ {
+ "description": "Post Malone",
+ "features": {
+ "american": 8.0,
+ "rap": 72.0,
+ "rnb": 14.0,
+ "pop": 8.0,
+ "synthpop": 3.0,
+ "hip hop": 35.0,
+ "seen live": 19.0,
+ "Hip-Hop": 100.0
+ }
+ },
+ {
+ "description": "Drake",
+ "features": {
+ "indie": 4.0,
+ "male vocalists": 5.0,
+ "soul": 2.0,
+ "pop": 6.0,
+ "hip hop": 37.0,
+ "rap": 72.0,
+ "seen live": 8.0,
+ "Hip-Hop": 100.0,
+ "rnb": 50.0,
+ "00s": 1.0
+ }
+ },
+ {
+ "description": "Kanye West",
+ "features": {
+ "indie": 2.0,
+ "male vocalists": 3.0,
+ "soul": 4.0,
+ "american": 6.0,
+ "experimental": 2.0,
+ "pop": 6.0,
+ "rock": 2.0,
+ "electronica": 1.0,
+ "hip hop": 40.0,
+ "electronic": 3.0,
+ "dance": 2.0,
+ "rap": 69.0,
+ "seen live": 22.0,
+ "Hip-Hop": 100.0,
+ "rnb": 31.0,
+ "alternative": 3.0,
+ "00s": 3.0
+ }
+ },
+ {
+ "description": "The Beatles",
+ "features": {
+ "indie": 3.0,
+ "favorites": 2.0,
+ "male vocalists": 2.0,
+ "singer-songwriter": 2.0,
+ "Psychedelic Rock": 6.0,
+ "british": 58.0,
+ "experimental": 2.0,
+ "classic rock": 100.0,
+ "rock": 71.0,
+ "pop": 41.0,
+ "psychedelic": 13.0,
+ "indie rock": 2.0,
+ "oldies": 10.0,
+ "britpop": 4.0,
+ "alternative": 4.0,
+ "Progressive rock": 2.0,
+ "alternative rock": 1.0,
+ "Love": 2.0,
+ "folk": 1.0,
+ "60s": 56.0,
+ "70s": 3.0
+ }
+ },
+ {
+ "description": "Taylor Swift",
+ "features": {
+ "country": 100.0,
+ "singer-songwriter": 42.0,
+ "chillout": 2.0,
+ "american": 8.0,
+ "pop": 71.0,
+ "rock": 2.0,
+ "beautiful": 2.0,
+ "synthpop": 2.0,
+ "seen live": 4.0,
+ "acoustic": 31.0,
+ "female vocalist": 3.0,
+ "00s": 3.0,
+ "female vocalists": 60.0,
+ "Love": 3.0,
+ "folk": 2.0
+ }
+ },
+ {
+ "description": "Radiohead",
+ "features": {
+ "indie": 59.0,
+ "favorites": 2.0,
+ "male vocalists": 2.0,
+ "emo": 1.0,
+ "chillout": 2.0,
+ "british": 22.0,
+ "experimental": 14.0,
+ "ambient": 2.0,
+ "classic rock": 1.0,
+ "rock": 73.0,
+ "electronica": 5.0,
+ "psychedelic": 3.0,
+ "pop": 2.0,
+ "beautiful": 1.0,
+ "post-rock": 3.0,
+ "indie rock": 15.0,
+ "electronic": 48.0,
+ "trip-hop": 1.0,
+ "britpop": 17.0,
+ "indie pop": 1.0,
+ "seen live": 27.0,
+ "alternative": 100.0,
+ "Progressive rock": 6.0,
+ "alternative rock": 81.0,
+ "00s": 2.0,
+ "idm": 1.0,
+ "90s": 5.0
+ }
+ },
+ {
+ "description": "Beyoncé",
+ "features": {
+ "singer-songwriter": 3.0,
+ "jazz": 2.0,
+ "american": 9.0,
+ "beautiful": 2.0,
+ "dance": 13.0,
+ "rap": 2.0,
+ "rnb": 100.0,
+ "female vocalists": 70.0,
+ "Love": 3.0,
+ "soul": 56.0,
+ "pop": 91.0,
+ "hip hop": 5.0,
+ "electronic": 2.0,
+ "seen live": 6.0,
+ "Hip-Hop": 45.0,
+ "alternative": 1.0,
+ "female vocalist": 4.0,
+ "00s": 3.0,
+ "funk": 2.0
+ }
+ },
+ {
+ "description": "Shawn Mendes",
+ "features": {
+ "male vocalists": 28.0,
+ "singer-songwriter": 13.0,
+ "british": 7.0,
+ "rnb": 7.0,
+ "acoustic": 37.0,
+ "metal": 4.0,
+ "folk": 37.0,
+ "indie": 10.0,
+ "soul": 4.0,
+ "pop": 100.0,
+ "seen live": 13.0,
+ "alternative": 4.0,
+ "alternative rock": 4.0
+ }
+ },
+ {
+ "description": "David Bowie",
+ "features": {
+ "singer-songwriter": 11.0,
+ "male vocalists": 4.0,
+ "Psychedelic Rock": 2.0,
+ "british": 23.0,
+ "experimental": 9.0,
+ "classic rock": 86.0,
+ "psychedelic": 3.0,
+ "electronica": 2.0,
+ "indie rock": 2.0,
+ "dance": 2.0,
+ "post-punk": 2.0,
+ "punk": 2.0,
+ "hard rock": 3.0,
+ "folk": 2.0,
+ "70s": 15.0,
+ "90s": 4.0,
+ "indie": 6.0,
+ "favorites": 2.0,
+ "soul": 2.0,
+ "ambient": 2.0,
+ "rock": 100.0,
+ "pop": 19.0,
+ "new wave": 7.0,
+ "electronic": 4.0,
+ "oldies": 2.0,
+ "britpop": 2.0,
+ "seen live": 11.0,
+ "alternative": 43.0,
+ "Progressive rock": 4.0,
+ "alternative rock": 6.0,
+ "industrial": 2.0,
+ "00s": 2.0,
+ "60s": 3.0,
+ "80s": 48.0,
+ "funk": 2.0
+ }
+ },
+ {
+ "description": "Kendrick Lamar",
+ "features": {
+ "male vocalists": 2.0,
+ "american": 7.0,
+ "hip hop": 17.0,
+ "rap": 70.0,
+ "seen live": 38.0,
+ "Hip-Hop": 100.0,
+ "funk": 2.0
+ }
+ },
+ {
+ "description": "Rihanna",
+ "features": {
+ "american": 4.0,
+ "beautiful": 2.0,
+ "dance": 56.0,
+ "rap": 2.0,
+ "rnb": 89.0,
+ "House": 1.0,
+ "female vocalists": 63.0,
+ "Love": 2.0,
+ "soul": 4.0,
+ "pop": 100.0,
+ "rock": 2.0,
+ "hip hop": 6.0,
+ "electronic": 3.0,
+ "seen live": 7.0,
+ "Hip-Hop": 41.0,
+ "alternative": 1.0,
+ "female vocalist": 4.0,
+ "00s": 3.0,
+ "reggae": 6.0
+ }
+ },
+ {
+ "description": "Arctic Monkeys",
+ "features": {
+ "indie": 98.0,
+ "male vocalists": 2.0,
+ "Psychedelic Rock": 1.0,
+ "british": 75.0,
+ "rock": 63.0,
+ "pop": 2.0,
+ "punk rock": 1.0,
+ "indie rock": 100.0,
+ "britpop": 16.0,
+ "indie pop": 2.0,
+ "seen live": 45.0,
+ "alternative": 60.0,
+ "post-punk": 3.0,
+ "alternative rock": 19.0,
+ "00s": 3.0,
+ "punk": 2.0
+ }
+ },
+ {
+ "description": "Tyler, the Creator",
+ "features": {
+ "experimental": 4.0,
+ "american": 4.0,
+ "hip hop": 12.0,
+ "rap": 55.0,
+ "seen live": 15.0,
+ "Hip-Hop": 100.0,
+ "alternative": 2.0
+ }
+ },
+ {
+ "description": "Lana Del Rey",
+ "features": {
+ "singer-songwriter": 10.0,
+ "jazz": 2.0,
+ "american": 14.0,
+ "beautiful": 2.0,
+ "indie rock": 2.0,
+ "female vocalists": 100.0,
+ "folk": 2.0,
+ "indie": 93.0,
+ "soul": 3.0,
+ "chillout": 3.0,
+ "blues": 2.0,
+ "pop": 80.0,
+ "rock": 2.0,
+ "electronic": 2.0,
+ "trip-hop": 10.0,
+ "indie pop": 88.0,
+ "seen live": 12.0,
+ "alternative": 67.0,
+ "female vocalist": 4.0,
+ "alternative rock": 2.0
+ }
+ },
+ {
+ "description": "Katy Perry",
+ "features": {
+ "indie": 25.0,
+ "singer-songwriter": 4.0,
+ "american": 9.0,
+ "pop": 100.0,
+ "rock": 24.0,
+ "beautiful": 2.0,
+ "indie rock": 1.0,
+ "electronic": 4.0,
+ "dance": 9.0,
+ "indie pop": 1.0,
+ "seen live": 7.0,
+ "rnb": 1.0,
+ "alternative": 3.0,
+ "female vocalist": 5.0,
+ "00s": 3.0,
+ "female vocalists": 62.0,
+ "Love": 2.0
+ }
+ },
+ {
+ "description": "Lady Gaga",
+ "features": {
+ "singer-songwriter": 4.0,
+ "american": 7.0,
+ "pop": 100.0,
+ "electronica": 3.0,
+ "rock": 2.0,
+ "electro": 2.0,
+ "synthpop": 2.0,
+ "electronic": 53.0,
+ "dance": 73.0,
+ "seen live": 8.0,
+ "techno": 1.0,
+ "rnb": 1.0,
+ "alternative": 1.0,
+ "female vocalist": 21.0,
+ "00s": 3.0,
+ "female vocalists": 43.0,
+ "Love": 2.0
+ }
+ },
+ {
+ "description": "Tame Impala",
+ "features": {
+ "indie": 21.0,
+ "male vocalists": 1.0,
+ "Psychedelic Rock": 100.0,
+ "experimental": 4.0,
+ "psychedelic": 92.0,
+ "rock": 41.0,
+ "indie rock": 60.0,
+ "electronic": 3.0,
+ "indie pop": 2.0,
+ "seen live": 53.0,
+ "alternative": 7.0,
+ "Progressive rock": 3.0,
+ "alternative rock": 3.0,
+ "00s": 3.0
+ }
+ },
+ {
+ "description": "Calvin Harris",
+ "features": {
+ "indie": 8.0,
+ "male vocalists": 3.0,
+ "singer-songwriter": 1.0,
+ "british": 15.0,
+ "electronica": 34.0,
+ "pop": 8.0,
+ "electro": 53.0,
+ "synthpop": 4.0,
+ "electronic": 100.0,
+ "dance": 75.0,
+ "indie pop": 2.0,
+ "seen live": 36.0,
+ "techno": 1.0,
+ "alternative": 1.0,
+ "House": 9.0,
+ "00s": 3.0,
+ "80s": 1.0
+ }
+ },
+ {
+ "description": "Red Hot Chili Peppers",
+ "features": {
+ "indie": 5.0,
+ "favorites": 1.0,
+ "male vocalists": 1.0,
+ "american": 5.0,
+ "classic rock": 3.0,
+ "rock": 100.0,
+ "pop": 2.0,
+ "punk rock": 2.0,
+ "indie rock": 2.0,
+ "seen live": 23.0,
+ "Grunge": 2.0,
+ "alternative": 54.0,
+ "metal": 2.0,
+ "alternative rock": 59.0,
+ "00s": 2.0,
+ "punk": 6.0,
+ "hard rock": 2.0,
+ "80s": 2.0,
+ "funk": 50.0,
+ "90s": 5.0
+ }
+ },
+ {
+ "description": "Coldplay",
+ "features": {
+ "indie": 55.0,
+ "favorites": 2.0,
+ "male vocalists": 3.0,
+ "singer-songwriter": 2.0,
+ "emo": 2.0,
+ "Mellow": 3.0,
+ "chillout": 3.0,
+ "british": 28.0,
+ "classic rock": 2.0,
+ "rock": 100.0,
+ "pop": 18.0,
+ "piano": 3.0,
+ "indie rock": 11.0,
+ "electronic": 2.0,
+ "britpop": 81.0,
+ "indie pop": 2.0,
+ "seen live": 22.0,
+ "alternative": 87.0,
+ "acoustic": 2.0,
+ "alternative rock": 68.0,
+ "00s": 2.0,
+ "Love": 2.0,
+ "90s": 2.0
+ }
+ },
+ {
+ "description": "Miley Cyrus",
+ "features": {
+ "country": 3.0,
+ "singer-songwriter": 1.0,
+ "american": 12.0,
+ "beautiful": 2.0,
+ "grindcore": 1.0,
+ "dance": 42.0,
+ "rnb": 4.0,
+ "Soundtrack": 2.0,
+ "female vocalists": 55.0,
+ "Love": 2.0,
+ "death metal": 2.0,
+ "pop": 100.0,
+ "rock": 8.0,
+ "electronic": 5.0,
+ "seen live": 3.0,
+ "Hip-Hop": 4.0,
+ "alternative": 2.0,
+ "female vocalist": 4.0,
+ "00s": 2.0
+ }
+ },
+ {
+ "description": "Mark Ronson",
+ "features": {
+ "singer-songwriter": 1.0,
+ "jazz": 7.0,
+ "british": 68.0,
+ "experimental": 2.0,
+ "american": 1.0,
+ "cover": 4.0,
+ "electronica": 2.0,
+ "dance": 5.0,
+ "rap": 4.0,
+ "rnb": 2.0,
+ "House": 1.0,
+ "indie": 3.0,
+ "soul": 17.0,
+ "chillout": 2.0,
+ "pop": 68.0,
+ "rock": 3.0,
+ "electro": 2.0,
+ "hip hop": 11.0,
+ "electronic": 9.0,
+ "trip-hop": 1.0,
+ "britpop": 4.0,
+ "seen live": 31.0,
+ "Hip-Hop": 57.0,
+ "alternative": 9.0,
+ "00s": 7.0,
+ "funk": 100.0
+ }
+ },
+ {
+ "description": "Imagine Dragons",
+ "features": {
+ "indie": 100.0,
+ "male vocalists": 6.0,
+ "american": 12.0,
+ "rock": 64.0,
+ "pop": 10.0,
+ "indie rock": 96.0,
+ "electronic": 4.0,
+ "new wave": 2.0,
+ "indie pop": 57.0,
+ "seen live": 29.0,
+ "alternative": 86.0,
+ "alternative rock": 21.0,
+ "folk": 2.0
+ }
+ },
+ {
+ "description": "The Weeknd",
+ "features": {
+ "indie": 6.0,
+ "male vocalists": 5.0,
+ "soul": 9.0,
+ "chillout": 2.0,
+ "Mellow": 2.0,
+ "experimental": 6.0,
+ "ambient": 3.0,
+ "pop": 10.0,
+ "electronica": 5.0,
+ "electro": 1.0,
+ "downtempo": 7.0,
+ "hip hop": 2.0,
+ "electronic": 78.0,
+ "trip-hop": 3.0,
+ "rap": 2.0,
+ "indie pop": 2.0,
+ "seen live": 16.0,
+ "Hip-Hop": 8.0,
+ "rnb": 100.0,
+ "alternative": 5.0
+ }
+ },
+ {
+ "description": "Eminem",
+ "features": {
+ "male vocalists": 3.0,
+ "singer-songwriter": 2.0,
+ "emo": 1.0,
+ "american": 6.0,
+ "pop": 16.0,
+ "rock": 3.0,
+ "hip hop": 33.0,
+ "dance": 1.0,
+ "rap": 100.0,
+ "seen live": 4.0,
+ "Hip-Hop": 79.0,
+ "alternative": 3.0,
+ "00s": 2.0,
+ "90s": 2.0
+ }
+ },
+ {
+ "description": "Nirvana",
+ "features": {
+ "indie": 4.0,
+ "favorites": 1.0,
+ "male vocalists": 2.0,
+ "Grunge": 100.0,
+ "alternative": 35.0,
+ "metal": 3.0,
+ "american": 4.0,
+ "classic rock": 2.0,
+ "rock": 52.0,
+ "alternative rock": 34.0,
+ "punk rock": 3.0,
+ "punk": 7.0,
+ "indie rock": 2.0,
+ "hard rock": 3.0,
+ "90s": 28.0
+ }
+ },
+ {
+ "description": "Fleetwood Mac",
+ "features": {
+ "favorites": 2.0,
+ "male vocalists": 2.0,
+ "singer-songwriter": 2.0,
+ "british": 10.0,
+ "american": 3.0,
+ "classic rock": 100.0,
+ "blues": 41.0,
+ "rock": 67.0,
+ "pop": 33.0,
+ "oldies": 3.0,
+ "seen live": 7.0,
+ "alternative": 1.0,
+ "acoustic": 1.0,
+ "female vocalist": 2.0,
+ "guitar": 1.0,
+ "Progressive rock": 2.0,
+ "female vocalists": 10.0,
+ "folk": 2.0,
+ "60s": 5.0,
+ "70s": 48.0,
+ "80s": 12.0,
+ "90s": 2.0
+ }
+ },
+ {
+ "description": "Childish Gambino",
+ "features": {
+ "indie": 35.0,
+ "male vocalists": 1.0,
+ "soul": 5.0,
+ "american": 8.0,
+ "experimental": 3.0,
+ "pop": 3.0,
+ "psychedelic": 2.0,
+ "hip hop": 31.0,
+ "electronic": 2.0,
+ "dance": 1.0,
+ "rap": 70.0,
+ "seen live": 37.0,
+ "Hip-Hop": 100.0,
+ "rnb": 6.0,
+ "alternative": 4.0,
+ "funk": 8.0
+ }
+ },
+ {
+ "description": "Pink Floyd",
+ "features": {
+ "favorites": 1.0,
+ "indie": 1.0,
+ "Psychedelic Rock": 72.0,
+ "british": 13.0,
+ "experimental": 6.0,
+ "ambient": 1.0,
+ "classic rock": 79.0,
+ "rock": 53.0,
+ "psychedelic": 44.0,
+ "oldies": 1.0,
+ "seen live": 3.0,
+ "alternative": 6.0,
+ "Progressive rock": 100.0,
+ "alternative rock": 2.0,
+ "hard rock": 2.0,
+ "60s": 5.0,
+ "70s": 8.0,
+ "80s": 3.0
+ }
+ },
+ {
+ "description": "The Killers",
+ "features": {
+ "indie": 100.0,
+ "favorites": 2.0,
+ "male vocalists": 3.0,
+ "emo": 2.0,
+ "american": 9.0,
+ "british": 2.0,
+ "classic rock": 1.0,
+ "rock": 93.0,
+ "pop": 10.0,
+ "electronica": 1.0,
+ "punk rock": 1.0,
+ "indie rock": 90.0,
+ "new wave": 4.0,
+ "electronic": 3.0,
+ "dance": 1.0,
+ "britpop": 4.0,
+ "indie pop": 4.0,
+ "seen live": 39.0,
+ "alternative": 75.0,
+ "post-punk": 3.0,
+ "alternative rock": 64.0,
+ "00s": 3.0,
+ "punk": 4.0
+ }
+ },
+ {
+ "description": "The Rolling Stones",
+ "features": {
+ "indie": 2.0,
+ "male vocalists": 1.0,
+ "Psychedelic Rock": 2.0,
+ "british": 43.0,
+ "classic rock": 100.0,
+ "blues": 31.0,
+ "rock": 80.0,
+ "psychedelic": 3.0,
+ "pop": 2.0,
+ "oldies": 3.0,
+ "seen live": 14.0,
+ "alternative": 4.0,
+ "guitar": 1.0,
+ "hard rock": 8.0,
+ "60s": 41.0,
+ "70s": 8.0,
+ "80s": 4.0,
+ "90s": 2.0
+ }
+ },
+ {
+ "description": "Gorillaz",
+ "features": {
+ "indie": 46.0,
+ "favorites": 1.0,
+ "chillout": 3.0,
+ "british": 12.0,
+ "experimental": 6.0,
+ "rock": 58.0,
+ "electronica": 13.0,
+ "pop": 13.0,
+ "electro": 2.0,
+ "downtempo": 1.0,
+ "punk rock": 1.0,
+ "indie rock": 4.0,
+ "hip hop": 7.0,
+ "electronic": 88.0,
+ "trip-hop": 20.0,
+ "dance": 4.0,
+ "britpop": 4.0,
+ "rap": 4.0,
+ "indie pop": 2.0,
+ "seen live": 5.0,
+ "Hip-Hop": 63.0,
+ "alternative": 100.0,
+ "metal": 2.0,
+ "alternative rock": 11.0,
+ "00s": 3.0,
+ "punk": 2.0,
+ "funk": 3.0
+ }
+ },
+ {
+ "description": "Frank Ocean",
+ "features": {
+ "indie": 3.0,
+ "male vocalists": 6.0,
+ "singer-songwriter": 4.0,
+ "soul": 93.0,
+ "american": 8.0,
+ "experimental": 2.0,
+ "pop": 6.0,
+ "electronica": 1.0,
+ "hip hop": 8.0,
+ "electronic": 3.0,
+ "rap": 4.0,
+ "seen live": 14.0,
+ "Hip-Hop": 82.0,
+ "rnb": 100.0,
+ "alternative": 3.0,
+ "funk": 2.0
+ }
+ },
+ {
+ "description": "Panic! at the Disco",
+ "features": {
+ "male vocalists": 3.0,
+ "american": 8.0,
+ "electronica": 2.0,
+ "punk rock": 6.0,
+ "indie rock": 21.0,
+ "dance": 20.0,
+ "metal": 2.0,
+ "screamo": 2.0,
+ "punk": 21.0,
+ "Love": 2.0,
+ "indie": 68.0,
+ "favorites": 4.0,
+ "emo": 97.0,
+ "rock": 100.0,
+ "pop": 9.0,
+ "electronic": 4.0,
+ "indie pop": 2.0,
+ "seen live": 51.0,
+ "hardcore": 2.0,
+ "techno": 1.0,
+ "alternative": 99.0,
+ "alternative rock": 21.0,
+ "00s": 2.0
+ }
+ },
+ {
+ "description": "The Cure",
+ "features": {
+ "indie": 14.0,
+ "favorites": 2.0,
+ "male vocalists": 2.0,
+ "emo": 1.0,
+ "british": 16.0,
+ "classic rock": 3.0,
+ "rock": 52.0,
+ "pop": 6.0,
+ "psychedelic": 1.0,
+ "synthpop": 1.0,
+ "punk rock": 1.0,
+ "indie rock": 3.0,
+ "new wave": 100.0,
+ "electronic": 2.0,
+ "britpop": 2.0,
+ "indie pop": 1.0,
+ "seen live": 24.0,
+ "alternative": 68.0,
+ "Gothic": 10.0,
+ "post-punk": 100.0,
+ "alternative rock": 23.0,
+ "punk": 4.0,
+ "Love": 2.0,
+ "70s": 2.0,
+ "80s": 65.0,
+ "90s": 3.0
+ }
+ },
+ {
+ "description": "Madonna",
+ "features": {
+ "singer-songwriter": 3.0,
+ "american": 8.0,
+ "pop": 100.0,
+ "rock": 5.0,
+ "electronica": 3.0,
+ "electronic": 29.0,
+ "dance": 50.0,
+ "seen live": 9.0,
+ "rnb": 1.0,
+ "alternative": 2.0,
+ "female vocalist": 4.0,
+ "00s": 4.0,
+ "female vocalists": 50.0,
+ "Love": 1.0,
+ "80s": 35.0,
+ "90s": 8.0
+ }
+ },
+ {
+ "description": "The Chainsmokers",
+ "features": {
+ "male vocalists": 6.0,
+ "american": 28.0,
+ "electronica": 3.0,
+ "dance": 33.0,
+ "House": 63.0,
+ "pop": 79.0,
+ "electro": 6.0,
+ "electronic": 100.0,
+ "seen live": 17.0,
+ "techno": 3.0,
+ "alternative": 3.0
+ }
+ },
+ {
+ "description": "Linkin Park",
+ "features": {
+ "favorites": 2.0,
+ "indie": 1.0,
+ "male vocalists": 3.0,
+ "emo": 3.0,
+ "american": 6.0,
+ "rock": 100.0,
+ "pop": 3.0,
+ "punk rock": 2.0,
+ "electronic": 4.0,
+ "rap": 3.0,
+ "seen live": 21.0,
+ "Hip-Hop": 2.0,
+ "alternative": 67.0,
+ "metal": 37.0,
+ "Nu Metal": 92.0,
+ "alternative rock": 74.0,
+ "00s": 2.0,
+ "punk": 4.0,
+ "hard rock": 5.0
+ }
+ },
+ {
+ "description": "Lorde",
+ "features": {
+ "indie": 72.0,
+ "singer-songwriter": 7.0,
+ "soul": 1.0,
+ "experimental": 2.0,
+ "pop": 100.0,
+ "electronica": 5.0,
+ "rock": 1.0,
+ "synthpop": 7.0,
+ "electronic": 81.0,
+ "trip-hop": 2.0,
+ "indie pop": 92.0,
+ "seen live": 20.0,
+ "alternative": 17.0,
+ "female vocalist": 4.0,
+ "female vocalists": 28.0,
+ "folk": 2.0
+ }
+ },
+ {
+ "description": "The Black Keys",
+ "features": {
+ "indie": 50.0,
+ "male vocalists": 2.0,
+ "soul": 2.0,
+ "Psychedelic Rock": 2.0,
+ "american": 8.0,
+ "blues": 68.0,
+ "classic rock": 2.0,
+ "rock": 63.0,
+ "psychedelic": 2.0,
+ "indie rock": 48.0,
+ "seen live": 37.0,
+ "alternative": 11.0,
+ "alternative rock": 7.0,
+ "00s": 3.0,
+ "hard rock": 2.0
+ }
+ },
+ {
+ "description": "Daft Punk",
+ "features": {
+ "indie": 2.0,
+ "chillout": 2.0,
+ "experimental": 2.0,
+ "electronica": 37.0,
+ "rock": 3.0,
+ "pop": 2.0,
+ "electro": 11.0,
+ "synthpop": 1.0,
+ "electronic": 100.0,
+ "dance": 54.0,
+ "french": 19.0,
+ "seen live": 7.0,
+ "techno": 36.0,
+ "alternative": 6.0,
+ "House": 54.0,
+ "trance": 2.0,
+ "00s": 2.0,
+ "Soundtrack": 2.0,
+ "funk": 4.0,
+ "90s": 3.0
+ }
+ },
+ {
+ "description": "Led Zeppelin",
+ "features": {
+ "favorites": 1.0,
+ "indie": 1.0,
+ "Psychedelic Rock": 3.0,
+ "british": 9.0,
+ "classic rock": 100.0,
+ "blues": 9.0,
+ "rock": 64.0,
+ "psychedelic": 4.0,
+ "heavy metal": 8.0,
+ "oldies": 1.0,
+ "metal": 4.0,
+ "alternative": 4.0,
+ "guitar": 2.0,
+ "Progressive rock": 33.0,
+ "alternative rock": 1.0,
+ "hard rock": 63.0,
+ "folk": 1.0,
+ "60s": 3.0,
+ "70s": 36.0,
+ "80s": 1.0
+ }
+ },
+ {
+ "description": "Marshmello",
+ "features": {
+ "emo": 10.0,
+ "pop": 28.0,
+ "electronic": 91.0,
+ "dance": 100.0,
+ "rap": 28.0,
+ "seen live": 19.0,
+ "rnb": 19.0,
+ "House": 10.0
+ }
+ },
+ {
+ "description": "Khalid",
+ "features": {
+ "indie": 7.0,
+ "male vocalists": 7.0,
+ "soul": 88.0,
+ "american": 19.0,
+ "pop": 100.0,
+ "synthpop": 7.0,
+ "electronic": 13.0,
+ "seen live": 25.0,
+ "rnb": 75.0,
+ "alternative": 63.0,
+ "folk": 7.0
+ }
+ },
+ {
+ "description": "David Guetta",
+ "features": {
+ "chillout": 1.0,
+ "electronica": 7.0,
+ "pop": 6.0,
+ "electro": 6.0,
+ "electronic": 76.0,
+ "dance": 82.0,
+ "french": 11.0,
+ "seen live": 9.0,
+ "techno": 39.0,
+ "House": 100.0,
+ "trance": 4.0,
+ "00s": 2.0
+ }
+ },
+ {
+ "description": "Billie Eilish",
+ "features": {
+ "singer-songwriter": 3.0,
+ "american": 23.0,
+ "british": 3.0,
+ "experimental": 3.0,
+ "rnb": 3.0,
+ "female vocalists": 49.0,
+ "indie": 58.0,
+ "chillout": 12.0,
+ "ambient": 3.0,
+ "pop": 100.0,
+ "synthpop": 3.0,
+ "electronic": 58.0,
+ "indie pop": 83.0,
+ "seen live": 18.0,
+ "alternative": 40.0,
+ "female vocalist": 9.0
+ }
+ },
+ {
+ "description": "Ed Sheeran",
+ "features": {
+ "indie": 12.0,
+ "singer-songwriter": 78.0,
+ "male vocalists": 13.0,
+ "soul": 3.0,
+ "chillout": 2.0,
+ "Mellow": 2.0,
+ "british": 82.0,
+ "pop": 23.0,
+ "rock": 4.0,
+ "rap": 2.0,
+ "indie pop": 3.0,
+ "seen live": 21.0,
+ "Hip-Hop": 4.0,
+ "acoustic": 100.0,
+ "alternative": 8.0,
+ "guitar": 2.0,
+ "00s": 2.0,
+ "Soundtrack": 1.0,
+ "Love": 2.0,
+ "folk": 12.0
+ }
+ },
+ {
+ "description": "Lil Nas X",
+ "features": {
+ "country": 54.0,
+ "male vocalists": 8.0,
+ "american": 24.0,
+ "rock": 16.0,
+ "pop": 8.0,
+ "hip hop": 47.0,
+ "rap": 62.0,
+ "Hip-Hop": 100.0,
+ "alternative": 24.0
+ }
+ },
+ {
+ "description": "Queen",
+ "features": {
+ "favorites": 2.0,
+ "male vocalists": 3.0,
+ "british": 17.0,
+ "classic rock": 100.0,
+ "rock": 81.0,
+ "pop": 7.0,
+ "heavy metal": 2.0,
+ "oldies": 2.0,
+ "seen live": 2.0,
+ "alternative": 4.0,
+ "metal": 2.0,
+ "Progressive rock": 6.0,
+ "alternative rock": 1.0,
+ "hard rock": 38.0,
+ "70s": 8.0,
+ "80s": 41.0,
+ "90s": 2.0
+ }
+ },
+ {
+ "description": "Ariana Grande",
+ "features": {
+ "american": 13.0,
+ "beautiful": 2.0,
+ "dance": 11.0,
+ "House": 2.0,
+ "Soundtrack": 3.0,
+ "chillout": 3.0,
+ "pop": 100.0,
+ "rock": 2.0,
+ "electronic": 3.0,
+ "seen live": 3.0,
+ "female vocalist": 3.0,
+ "singer-songwriter": 5.0,
+ "rnb": 51.0,
+ "female vocalists": 54.0,
+ "soul": 10.0,
+ "synthpop": 2.0,
+ "hip hop": 3.0,
+ "Hip-Hop": 3.0
+ }
+ },
+ {
+ "description": "Post Malone",
+ "features": {
+ "american": 8.0,
+ "rap": 72.0,
+ "rnb": 14.0,
+ "pop": 8.0,
+ "synthpop": 3.0,
+ "hip hop": 35.0,
+ "seen live": 19.0,
+ "Hip-Hop": 100.0
+ }
+ },
+ {
+ "description": "Drake",
+ "features": {
+ "indie": 4.0,
+ "male vocalists": 5.0,
+ "soul": 2.0,
+ "pop": 6.0,
+ "hip hop": 37.0,
+ "rap": 72.0,
+ "seen live": 8.0,
+ "Hip-Hop": 100.0,
+ "rnb": 50.0,
+ "00s": 1.0
+ }
+ },
+ {
+ "description": "Kanye West",
+ "features": {
+ "indie": 2.0,
+ "male vocalists": 3.0,
+ "soul": 4.0,
+ "american": 6.0,
+ "experimental": 2.0,
+ "pop": 6.0,
+ "rock": 2.0,
+ "electronica": 1.0,
+ "hip hop": 40.0,
+ "electronic": 3.0,
+ "dance": 2.0,
+ "rap": 69.0,
+ "seen live": 22.0,
+ "Hip-Hop": 100.0,
+ "rnb": 31.0,
+ "alternative": 3.0,
+ "00s": 3.0
+ }
+ },
+ {
+ "description": "The Beatles",
+ "features": {
+ "indie": 3.0,
+ "favorites": 2.0,
+ "male vocalists": 2.0,
+ "singer-songwriter": 2.0,
+ "Psychedelic Rock": 6.0,
+ "british": 58.0,
+ "experimental": 2.0,
+ "classic rock": 100.0,
+ "rock": 71.0,
+ "pop": 41.0,
+ "psychedelic": 13.0,
+ "indie rock": 2.0,
+ "oldies": 10.0,
+ "britpop": 4.0,
+ "alternative": 4.0,
+ "Progressive rock": 2.0,
+ "alternative rock": 1.0,
+ "Love": 2.0,
+ "folk": 1.0,
+ "60s": 56.0,
+ "70s": 3.0
+ }
+ },
+ {
+ "description": "Taylor Swift",
+ "features": {
+ "country": 100.0,
+ "singer-songwriter": 42.0,
+ "chillout": 2.0,
+ "american": 8.0,
+ "pop": 71.0,
+ "rock": 2.0,
+ "beautiful": 2.0,
+ "synthpop": 2.0,
+ "seen live": 4.0,
+ "acoustic": 31.0,
+ "female vocalist": 3.0,
+ "00s": 3.0,
+ "female vocalists": 60.0,
+ "Love": 3.0,
+ "folk": 2.0
+ }
+ },
+ {
+ "description": "Radiohead",
+ "features": {
+ "indie": 59.0,
+ "favorites": 2.0,
+ "male vocalists": 2.0,
+ "emo": 1.0,
+ "chillout": 2.0,
+ "british": 22.0,
+ "experimental": 14.0,
+ "ambient": 2.0,
+ "classic rock": 1.0,
+ "rock": 73.0,
+ "electronica": 5.0,
+ "psychedelic": 3.0,
+ "pop": 2.0,
+ "beautiful": 1.0,
+ "post-rock": 3.0,
+ "indie rock": 15.0,
+ "electronic": 48.0,
+ "trip-hop": 1.0,
+ "britpop": 17.0,
+ "indie pop": 1.0,
+ "seen live": 27.0,
+ "alternative": 100.0,
+ "Progressive rock": 6.0,
+ "alternative rock": 81.0,
+ "00s": 2.0,
+ "idm": 1.0,
+ "90s": 5.0
+ }
+ },
+ {
+ "description": "Beyoncé",
+ "features": {
+ "singer-songwriter": 3.0,
+ "jazz": 2.0,
+ "american": 9.0,
+ "beautiful": 2.0,
+ "dance": 13.0,
+ "rap": 2.0,
+ "rnb": 100.0,
+ "female vocalists": 70.0,
+ "Love": 3.0,
+ "soul": 56.0,
+ "pop": 91.0,
+ "hip hop": 5.0,
+ "electronic": 2.0,
+ "seen live": 6.0,
+ "Hip-Hop": 45.0,
+ "alternative": 1.0,
+ "female vocalist": 4.0,
+ "00s": 3.0,
+ "funk": 2.0
+ }
+ },
+ {
+ "description": "Shawn Mendes",
+ "features": {
+ "male vocalists": 28.0,
+ "singer-songwriter": 13.0,
+ "british": 7.0,
+ "rnb": 7.0,
+ "acoustic": 37.0,
+ "metal": 4.0,
+ "folk": 37.0,
+ "indie": 10.0,
+ "soul": 4.0,
+ "pop": 100.0,
+ "seen live": 13.0,
+ "alternative": 4.0,
+ "alternative rock": 4.0
+ }
+ },
+ {
+ "description": "David Bowie",
+ "features": {
+ "singer-songwriter": 11.0,
+ "male vocalists": 4.0,
+ "Psychedelic Rock": 2.0,
+ "british": 23.0,
+ "experimental": 9.0,
+ "classic rock": 86.0,
+ "psychedelic": 3.0,
+ "electronica": 2.0,
+ "indie rock": 2.0,
+ "dance": 2.0,
+ "post-punk": 2.0,
+ "punk": 2.0,
+ "hard rock": 3.0,
+ "folk": 2.0,
+ "70s": 15.0,
+ "90s": 4.0,
+ "indie": 6.0,
+ "favorites": 2.0,
+ "soul": 2.0,
+ "ambient": 2.0,
+ "rock": 100.0,
+ "pop": 19.0,
+ "new wave": 7.0,
+ "electronic": 4.0,
+ "oldies": 2.0,
+ "britpop": 2.0,
+ "seen live": 11.0,
+ "alternative": 43.0,
+ "Progressive rock": 4.0,
+ "alternative rock": 6.0,
+ "industrial": 2.0,
+ "00s": 2.0,
+ "60s": 3.0,
+ "80s": 48.0,
+ "funk": 2.0
+ }
+ },
+ {
+ "description": "Kendrick Lamar",
+ "features": {
+ "male vocalists": 2.0,
+ "american": 7.0,
+ "hip hop": 17.0,
+ "rap": 70.0,
+ "seen live": 38.0,
+ "Hip-Hop": 100.0,
+ "funk": 2.0
+ }
+ },
+ {
+ "description": "Rihanna",
+ "features": {
+ "american": 4.0,
+ "beautiful": 2.0,
+ "dance": 56.0,
+ "rap": 2.0,
+ "rnb": 89.0,
+ "House": 1.0,
+ "female vocalists": 63.0,
+ "Love": 2.0,
+ "soul": 4.0,
+ "pop": 100.0,
+ "rock": 2.0,
+ "hip hop": 6.0,
+ "electronic": 3.0,
+ "seen live": 7.0,
+ "Hip-Hop": 41.0,
+ "alternative": 1.0,
+ "female vocalist": 4.0,
+ "00s": 3.0,
+ "reggae": 6.0
+ }
+ },
+ {
+ "description": "Arctic Monkeys",
+ "features": {
+ "indie": 98.0,
+ "male vocalists": 2.0,
+ "Psychedelic Rock": 1.0,
+ "british": 75.0,
+ "rock": 63.0,
+ "pop": 2.0,
+ "punk rock": 1.0,
+ "indie rock": 100.0,
+ "britpop": 16.0,
+ "indie pop": 2.0,
+ "seen live": 45.0,
+ "alternative": 60.0,
+ "post-punk": 3.0,
+ "alternative rock": 19.0,
+ "00s": 3.0,
+ "punk": 2.0
+ }
+ },
+ {
+ "description": "Tyler, the Creator",
+ "features": {
+ "experimental": 4.0,
+ "american": 4.0,
+ "hip hop": 12.0,
+ "rap": 55.0,
+ "seen live": 15.0,
+ "Hip-Hop": 100.0,
+ "alternative": 2.0
+ }
+ },
+ {
+ "description": "Lana Del Rey",
+ "features": {
+ "singer-songwriter": 10.0,
+ "jazz": 2.0,
+ "american": 14.0,
+ "beautiful": 2.0,
+ "indie rock": 2.0,
+ "female vocalists": 100.0,
+ "folk": 2.0,
+ "indie": 93.0,
+ "soul": 3.0,
+ "chillout": 3.0,
+ "blues": 2.0,
+ "pop": 80.0,
+ "rock": 2.0,
+ "electronic": 2.0,
+ "trip-hop": 10.0,
+ "indie pop": 88.0,
+ "seen live": 12.0,
+ "alternative": 67.0,
+ "female vocalist": 4.0,
+ "alternative rock": 2.0
+ }
+ },
+ {
+ "description": "Katy Perry",
+ "features": {
+ "indie": 25.0,
+ "singer-songwriter": 4.0,
+ "american": 9.0,
+ "pop": 100.0,
+ "rock": 24.0,
+ "beautiful": 2.0,
+ "indie rock": 1.0,
+ "electronic": 4.0,
+ "dance": 9.0,
+ "indie pop": 1.0,
+ "seen live": 7.0,
+ "rnb": 1.0,
+ "alternative": 3.0,
+ "female vocalist": 5.0,
+ "00s": 3.0,
+ "female vocalists": 62.0,
+ "Love": 2.0
+ }
+ },
+ {
+ "description": "Lady Gaga",
+ "features": {
+ "singer-songwriter": 4.0,
+ "american": 7.0,
+ "pop": 100.0,
+ "electronica": 3.0,
+ "rock": 2.0,
+ "electro": 2.0,
+ "synthpop": 2.0,
+ "electronic": 53.0,
+ "dance": 73.0,
+ "seen live": 8.0,
+ "techno": 1.0,
+ "rnb": 1.0,
+ "alternative": 1.0,
+ "female vocalist": 21.0,
+ "00s": 3.0,
+ "female vocalists": 43.0,
+ "Love": 2.0
+ }
+ },
+ {
+ "description": "Tame Impala",
+ "features": {
+ "indie": 21.0,
+ "male vocalists": 1.0,
+ "Psychedelic Rock": 100.0,
+ "experimental": 4.0,
+ "psychedelic": 92.0,
+ "rock": 41.0,
+ "indie rock": 60.0,
+ "electronic": 3.0,
+ "indie pop": 2.0,
+ "seen live": 53.0,
+ "alternative": 7.0,
+ "Progressive rock": 3.0,
+ "alternative rock": 3.0,
+ "00s": 3.0
+ }
+ },
+ {
+ "description": "Calvin Harris",
+ "features": {
+ "indie": 8.0,
+ "male vocalists": 3.0,
+ "singer-songwriter": 1.0,
+ "british": 15.0,
+ "electronica": 34.0,
+ "pop": 8.0,
+ "electro": 53.0,
+ "synthpop": 4.0,
+ "electronic": 100.0,
+ "dance": 75.0,
+ "indie pop": 2.0,
+ "seen live": 36.0,
+ "techno": 1.0,
+ "alternative": 1.0,
+ "House": 9.0,
+ "00s": 3.0,
+ "80s": 1.0
+ }
+ },
+ {
+ "description": "Red Hot Chili Peppers",
+ "features": {
+ "indie": 5.0,
+ "favorites": 1.0,
+ "male vocalists": 1.0,
+ "american": 5.0,
+ "classic rock": 3.0,
+ "rock": 100.0,
+ "pop": 2.0,
+ "punk rock": 2.0,
+ "indie rock": 2.0,
+ "seen live": 23.0,
+ "Grunge": 2.0,
+ "alternative": 54.0,
+ "metal": 2.0,
+ "alternative rock": 59.0,
+ "00s": 2.0,
+ "punk": 6.0,
+ "hard rock": 2.0,
+ "80s": 2.0,
+ "funk": 50.0,
+ "90s": 5.0
+ }
+ },
+ {
+ "description": "Coldplay",
+ "features": {
+ "indie": 55.0,
+ "favorites": 2.0,
+ "male vocalists": 3.0,
+ "singer-songwriter": 2.0,
+ "emo": 2.0,
+ "Mellow": 3.0,
+ "chillout": 3.0,
+ "british": 28.0,
+ "classic rock": 2.0,
+ "rock": 100.0,
+ "pop": 18.0,
+ "piano": 3.0,
+ "indie rock": 11.0,
+ "electronic": 2.0,
+ "britpop": 81.0,
+ "indie pop": 2.0,
+ "seen live": 22.0,
+ "alternative": 87.0,
+ "acoustic": 2.0,
+ "alternative rock": 68.0,
+ "00s": 2.0,
+ "Love": 2.0,
+ "90s": 2.0
+ }
+ },
+ {
+ "description": "Miley Cyrus",
+ "features": {
+ "country": 3.0,
+ "singer-songwriter": 1.0,
+ "american": 12.0,
+ "beautiful": 2.0,
+ "grindcore": 1.0,
+ "dance": 42.0,
+ "rnb": 4.0,
+ "Soundtrack": 2.0,
+ "female vocalists": 55.0,
+ "Love": 2.0,
+ "death metal": 2.0,
+ "pop": 100.0,
+ "rock": 8.0,
+ "electronic": 5.0,
+ "seen live": 3.0,
+ "Hip-Hop": 4.0,
+ "alternative": 2.0,
+ "female vocalist": 4.0,
+ "00s": 2.0
+ }
+ },
+ {
+ "description": "Mark Ronson",
+ "features": {
+ "singer-songwriter": 1.0,
+ "jazz": 7.0,
+ "british": 68.0,
+ "experimental": 2.0,
+ "american": 1.0,
+ "cover": 4.0,
+ "electronica": 2.0,
+ "dance": 5.0,
+ "rap": 4.0,
+ "rnb": 2.0,
+ "House": 1.0,
+ "indie": 3.0,
+ "soul": 17.0,
+ "chillout": 2.0,
+ "pop": 68.0,
+ "rock": 3.0,
+ "electro": 2.0,
+ "hip hop": 11.0,
+ "electronic": 9.0,
+ "trip-hop": 1.0,
+ "britpop": 4.0,
+ "seen live": 31.0,
+ "Hip-Hop": 57.0,
+ "alternative": 9.0,
+ "00s": 7.0,
+ "funk": 100.0
+ }
+ },
+ {
+ "description": "Imagine Dragons",
+ "features": {
+ "indie": 100.0,
+ "male vocalists": 6.0,
+ "american": 12.0,
+ "rock": 64.0,
+ "pop": 10.0,
+ "indie rock": 96.0,
+ "electronic": 4.0,
+ "new wave": 2.0,
+ "indie pop": 57.0,
+ "seen live": 29.0,
+ "alternative": 86.0,
+ "alternative rock": 21.0,
+ "folk": 2.0
+ }
+ },
+ {
+ "description": "The Weeknd",
+ "features": {
+ "indie": 6.0,
+ "male vocalists": 5.0,
+ "soul": 9.0,
+ "chillout": 2.0,
+ "Mellow": 2.0,
+ "experimental": 6.0,
+ "ambient": 3.0,
+ "pop": 10.0,
+ "electronica": 5.0,
+ "electro": 1.0,
+ "downtempo": 7.0,
+ "hip hop": 2.0,
+ "electronic": 78.0,
+ "trip-hop": 3.0,
+ "rap": 2.0,
+ "indie pop": 2.0,
+ "seen live": 16.0,
+ "Hip-Hop": 8.0,
+ "rnb": 100.0,
+ "alternative": 5.0
+ }
+ },
+ {
+ "description": "Eminem",
+ "features": {
+ "male vocalists": 3.0,
+ "singer-songwriter": 2.0,
+ "emo": 1.0,
+ "american": 6.0,
+ "pop": 16.0,
+ "rock": 3.0,
+ "hip hop": 33.0,
+ "dance": 1.0,
+ "rap": 100.0,
+ "seen live": 4.0,
+ "Hip-Hop": 79.0,
+ "alternative": 3.0,
+ "00s": 2.0,
+ "90s": 2.0
+ }
+ },
+ {
+ "description": "Nirvana",
+ "features": {
+ "indie": 4.0,
+ "favorites": 1.0,
+ "male vocalists": 2.0,
+ "Grunge": 100.0,
+ "alternative": 35.0,
+ "metal": 3.0,
+ "american": 4.0,
+ "classic rock": 2.0,
+ "rock": 52.0,
+ "alternative rock": 34.0,
+ "punk rock": 3.0,
+ "punk": 7.0,
+ "indie rock": 2.0,
+ "hard rock": 3.0,
+ "90s": 28.0
+ }
+ },
+ {
+ "description": "Fleetwood Mac",
+ "features": {
+ "favorites": 2.0,
+ "male vocalists": 2.0,
+ "singer-songwriter": 2.0,
+ "british": 10.0,
+ "american": 3.0,
+ "classic rock": 100.0,
+ "blues": 41.0,
+ "rock": 67.0,
+ "pop": 33.0,
+ "oldies": 3.0,
+ "seen live": 7.0,
+ "alternative": 1.0,
+ "acoustic": 1.0,
+ "female vocalist": 2.0,
+ "guitar": 1.0,
+ "Progressive rock": 2.0,
+ "female vocalists": 10.0,
+ "folk": 2.0,
+ "60s": 5.0,
+ "70s": 48.0,
+ "80s": 12.0,
+ "90s": 2.0
+ }
+ },
+ {
+ "description": "Childish Gambino",
+ "features": {
+ "indie": 35.0,
+ "male vocalists": 1.0,
+ "soul": 5.0,
+ "american": 8.0,
+ "experimental": 3.0,
+ "pop": 3.0,
+ "psychedelic": 2.0,
+ "hip hop": 31.0,
+ "electronic": 2.0,
+ "dance": 1.0,
+ "rap": 70.0,
+ "seen live": 37.0,
+ "Hip-Hop": 100.0,
+ "rnb": 6.0,
+ "alternative": 4.0,
+ "funk": 8.0
+ }
+ },
+ {
+ "description": "Pink Floyd",
+ "features": {
+ "favorites": 1.0,
+ "indie": 1.0,
+ "Psychedelic Rock": 72.0,
+ "british": 13.0,
+ "experimental": 6.0,
+ "ambient": 1.0,
+ "classic rock": 79.0,
+ "rock": 53.0,
+ "psychedelic": 44.0,
+ "oldies": 1.0,
+ "seen live": 3.0,
+ "alternative": 6.0,
+ "Progressive rock": 100.0,
+ "alternative rock": 2.0,
+ "hard rock": 2.0,
+ "60s": 5.0,
+ "70s": 8.0,
+ "80s": 3.0
+ }
+ },
+ {
+ "description": "The Killers",
+ "features": {
+ "indie": 100.0,
+ "favorites": 2.0,
+ "male vocalists": 3.0,
+ "emo": 2.0,
+ "american": 9.0,
+ "british": 2.0,
+ "classic rock": 1.0,
+ "rock": 93.0,
+ "pop": 10.0,
+ "electronica": 1.0,
+ "punk rock": 1.0,
+ "indie rock": 90.0,
+ "new wave": 4.0,
+ "electronic": 3.0,
+ "dance": 1.0,
+ "britpop": 4.0,
+ "indie pop": 4.0,
+ "seen live": 39.0,
+ "alternative": 75.0,
+ "post-punk": 3.0,
+ "alternative rock": 64.0,
+ "00s": 3.0,
+ "punk": 4.0
+ }
+ },
+ {
+ "description": "The Rolling Stones",
+ "features": {
+ "indie": 2.0,
+ "male vocalists": 1.0,
+ "Psychedelic Rock": 2.0,
+ "british": 43.0,
+ "classic rock": 100.0,
+ "blues": 31.0,
+ "rock": 80.0,
+ "psychedelic": 3.0,
+ "pop": 2.0,
+ "oldies": 3.0,
+ "seen live": 14.0,
+ "alternative": 4.0,
+ "guitar": 1.0,
+ "hard rock": 8.0,
+ "60s": 41.0,
+ "70s": 8.0,
+ "80s": 4.0,
+ "90s": 2.0
+ }
+ },
+ {
+ "description": "Gorillaz",
+ "features": {
+ "indie": 46.0,
+ "favorites": 1.0,
+ "chillout": 3.0,
+ "british": 12.0,
+ "experimental": 6.0,
+ "rock": 58.0,
+ "electronica": 13.0,
+ "pop": 13.0,
+ "electro": 2.0,
+ "downtempo": 1.0,
+ "punk rock": 1.0,
+ "indie rock": 4.0,
+ "hip hop": 7.0,
+ "electronic": 88.0,
+ "trip-hop": 20.0,
+ "dance": 4.0,
+ "britpop": 4.0,
+ "rap": 4.0,
+ "indie pop": 2.0,
+ "seen live": 5.0,
+ "Hip-Hop": 63.0,
+ "alternative": 100.0,
+ "metal": 2.0,
+ "alternative rock": 11.0,
+ "00s": 3.0,
+ "punk": 2.0,
+ "funk": 3.0
+ }
+ },
+ {
+ "description": "Frank Ocean",
+ "features": {
+ "indie": 3.0,
+ "male vocalists": 6.0,
+ "singer-songwriter": 4.0,
+ "soul": 93.0,
+ "american": 8.0,
+ "experimental": 2.0,
+ "pop": 6.0,
+ "electronica": 1.0,
+ "hip hop": 8.0,
+ "electronic": 3.0,
+ "rap": 4.0,
+ "seen live": 14.0,
+ "Hip-Hop": 82.0,
+ "rnb": 100.0,
+ "alternative": 3.0,
+ "funk": 2.0
+ }
+ },
+ {
+ "description": "Panic! at the Disco",
+ "features": {
+ "male vocalists": 3.0,
+ "american": 8.0,
+ "electronica": 2.0,
+ "punk rock": 6.0,
+ "indie rock": 21.0,
+ "dance": 20.0,
+ "metal": 2.0,
+ "screamo": 2.0,
+ "punk": 21.0,
+ "Love": 2.0,
+ "indie": 68.0,
+ "favorites": 4.0,
+ "emo": 97.0,
+ "rock": 100.0,
+ "pop": 9.0,
+ "electronic": 4.0,
+ "indie pop": 2.0,
+ "seen live": 51.0,
+ "hardcore": 2.0,
+ "techno": 1.0,
+ "alternative": 99.0,
+ "alternative rock": 21.0,
+ "00s": 2.0
+ }
+ },
+ {
+ "description": "The Cure",
+ "features": {
+ "indie": 14.0,
+ "favorites": 2.0,
+ "male vocalists": 2.0,
+ "emo": 1.0,
+ "british": 16.0,
+ "classic rock": 3.0,
+ "rock": 52.0,
+ "pop": 6.0,
+ "psychedelic": 1.0,
+ "synthpop": 1.0,
+ "punk rock": 1.0,
+ "indie rock": 3.0,
+ "new wave": 100.0,
+ "electronic": 2.0,
+ "britpop": 2.0,
+ "indie pop": 1.0,
+ "seen live": 24.0,
+ "alternative": 68.0,
+ "Gothic": 10.0,
+ "post-punk": 100.0,
+ "alternative rock": 23.0,
+ "punk": 4.0,
+ "Love": 2.0,
+ "70s": 2.0,
+ "80s": 65.0,
+ "90s": 3.0
+ }
+ },
+ {
+ "description": "Madonna",
+ "features": {
+ "singer-songwriter": 3.0,
+ "american": 8.0,
+ "pop": 100.0,
+ "rock": 5.0,
+ "electronica": 3.0,
+ "electronic": 29.0,
+ "dance": 50.0,
+ "seen live": 9.0,
+ "rnb": 1.0,
+ "alternative": 2.0,
+ "female vocalist": 4.0,
+ "00s": 4.0,
+ "female vocalists": 50.0,
+ "Love": 1.0,
+ "80s": 35.0,
+ "90s": 8.0
+ }
+ },
+ {
+ "description": "The Chainsmokers",
+ "features": {
+ "male vocalists": 6.0,
+ "american": 28.0,
+ "electronica": 3.0,
+ "dance": 33.0,
+ "House": 63.0,
+ "pop": 79.0,
+ "electro": 6.0,
+ "electronic": 100.0,
+ "seen live": 17.0,
+ "techno": 3.0,
+ "alternative": 3.0
+ }
+ },
+ {
+ "description": "Linkin Park",
+ "features": {
+ "favorites": 2.0,
+ "indie": 1.0,
+ "male vocalists": 3.0,
+ "emo": 3.0,
+ "american": 6.0,
+ "rock": 100.0,
+ "pop": 3.0,
+ "punk rock": 2.0,
+ "electronic": 4.0,
+ "rap": 3.0,
+ "seen live": 21.0,
+ "Hip-Hop": 2.0,
+ "alternative": 67.0,
+ "metal": 37.0,
+ "Nu Metal": 92.0,
+ "alternative rock": 74.0,
+ "00s": 2.0,
+ "punk": 4.0,
+ "hard rock": 5.0
+ }
+ },
+ {
+ "description": "Lorde",
+ "features": {
+ "indie": 72.0,
+ "singer-songwriter": 7.0,
+ "soul": 1.0,
+ "experimental": 2.0,
+ "pop": 100.0,
+ "electronica": 5.0,
+ "rock": 1.0,
+ "synthpop": 7.0,
+ "electronic": 81.0,
+ "trip-hop": 2.0,
+ "indie pop": 92.0,
+ "seen live": 20.0,
+ "alternative": 17.0,
+ "female vocalist": 4.0,
+ "female vocalists": 28.0,
+ "folk": 2.0
+ }
+ },
+ {
+ "description": "The Black Keys",
+ "features": {
+ "indie": 50.0,
+ "male vocalists": 2.0,
+ "soul": 2.0,
+ "Psychedelic Rock": 2.0,
+ "american": 8.0,
+ "blues": 68.0,
+ "classic rock": 2.0,
+ "rock": 63.0,
+ "psychedelic": 2.0,
+ "indie rock": 48.0,
+ "seen live": 37.0,
+ "alternative": 11.0,
+ "alternative rock": 7.0,
+ "00s": 3.0,
+ "hard rock": 2.0
+ }
+ },
+ {
+ "description": "Daft Punk",
+ "features": {
+ "indie": 2.0,
+ "chillout": 2.0,
+ "experimental": 2.0,
+ "electronica": 37.0,
+ "rock": 3.0,
+ "pop": 2.0,
+ "electro": 11.0,
+ "synthpop": 1.0,
+ "electronic": 100.0,
+ "dance": 54.0,
+ "french": 19.0,
+ "seen live": 7.0,
+ "techno": 36.0,
+ "alternative": 6.0,
+ "House": 54.0,
+ "trance": 2.0,
+ "00s": 2.0,
+ "Soundtrack": 2.0,
+ "funk": 4.0,
+ "90s": 3.0
+ }
+ },
+ {
+ "description": "Led Zeppelin",
+ "features": {
+ "favorites": 1.0,
+ "indie": 1.0,
+ "Psychedelic Rock": 3.0,
+ "british": 9.0,
+ "classic rock": 100.0,
+ "blues": 9.0,
+ "rock": 64.0,
+ "psychedelic": 4.0,
+ "heavy metal": 8.0,
+ "oldies": 1.0,
+ "metal": 4.0,
+ "alternative": 4.0,
+ "guitar": 2.0,
+ "Progressive rock": 33.0,
+ "alternative rock": 1.0,
+ "hard rock": 63.0,
+ "folk": 1.0,
+ "60s": 3.0,
+ "70s": 36.0,
+ "80s": 1.0
+ }
+ },
+ {
+ "description": "Marshmello",
+ "features": {
+ "emo": 10.0,
+ "pop": 28.0,
+ "electronic": 91.0,
+ "dance": 100.0,
+ "rap": 28.0,
+ "seen live": 19.0,
+ "rnb": 19.0,
+ "House": 10.0
+ }
+ },
+ {
+ "description": "Khalid",
+ "features": {
+ "indie": 7.0,
+ "male vocalists": 7.0,
+ "soul": 88.0,
+ "american": 19.0,
+ "pop": 100.0,
+ "synthpop": 7.0,
+ "electronic": 13.0,
+ "seen live": 25.0,
+ "rnb": 75.0,
+ "alternative": 63.0,
+ "folk": 7.0
+ }
+ },
+ {
+ "description": "David Guetta",
+ "features": {
+ "chillout": 1.0,
+ "electronica": 7.0,
+ "pop": 6.0,
+ "electro": 6.0,
+ "electronic": 76.0,
+ "dance": 82.0,
+ "french": 11.0,
+ "seen live": 9.0,
+ "techno": 39.0,
+ "House": 100.0,
+ "trance": 4.0,
+ "00s": 2.0
+ }
+ },
+ {
+ "description": "Lizzo",
+ "features": {
+ "american": 16.0,
+ "dance": 8.0,
+ "rap": 47.0,
+ "rnb": 54.0,
+ "female vocalists": 24.0,
+ "pop": 16.0,
+ "hip hop": 47.0,
+ "seen live": 93.0,
+ "Hip-Hop": 100.0,
+ "alternative": 8.0
+ }
+ },
+ {
+ "description": "Sia",
+ "features": {
+ "indie": 70.0,
+ "favorites": 1.0,
+ "singer-songwriter": 18.0,
+ "soul": 3.0,
+ "jazz": 2.0,
+ "chillout": 76.0,
+ "Mellow": 4.0,
+ "ambient": 2.0,
+ "pop": 20.0,
+ "electronica": 4.0,
+ "rock": 2.0,
+ "beautiful": 4.0,
+ "piano": 1.0,
+ "downtempo": 57.0,
+ "indie rock": 1.0,
+ "electronic": 16.0,
+ "trip-hop": 57.0,
+ "dance": 2.0,
+ "indie pop": 8.0,
+ "lounge": 2.0,
+ "seen live": 7.0,
+ "alternative": 9.0,
+ "acoustic": 2.0,
+ "female vocalist": 7.0,
+ "alternative rock": 1.0,
+ "00s": 2.0,
+ "female vocalists": 100.0,
+ "Love": 2.0
+ }
+ },
+ {
+ "description": "Elton John",
+ "features": {
+ "favorites": 2.0,
+ "indie": 1.0,
+ "singer-songwriter": 64.0,
+ "male vocalists": 9.0,
+ "soul": 2.0,
+ "british": 28.0,
+ "classic rock": 87.0,
+ "blues": 1.0,
+ "pop": 100.0,
+ "rock": 59.0,
+ "piano": 54.0,
+ "oldies": 4.0,
+ "britpop": 2.0,
+ "seen live": 19.0,
+ "alternative": 2.0,
+ "Soundtrack": 3.0,
+ "00s": 2.0,
+ "Love": 2.0,
+ "60s": 2.0,
+ "70s": 16.0,
+ "80s": 16.0,
+ "90s": 5.0
+ }
+ },
+ {
+ "description": "Twenty One Pilots",
+ "features": {
+ "american": 15.0,
+ "experimental": 6.0,
+ "electronica": 1.0,
+ "piano": 1.0,
+ "indie rock": 12.0,
+ "rap": 48.0,
+ "pop": 17.0,
+ "rock": 11.0,
+ "electronic": 100.0,
+ "noise": 1.0,
+ "indie pop": 71.0,
+ "seen live": 50.0,
+ "male vocalists": 4.0,
+ "punk": 1.0,
+ "Love": 1.0,
+ "indie": 84.0,
+ "emo": 5.0,
+ "electro": 1.0,
+ "synthpop": 1.0,
+ "hip hop": 7.0,
+ "Hip-Hop": 27.0,
+ "alternative": 84.0,
+ "alternative rock": 23.0
+ }
+ },
+ {
+ "description": "Avicii",
+ "features": {
+ "swedish": 20.0,
+ "seen live": 14.0,
+ "techno": 2.0,
+ "House": 100.0,
+ "trance": 3.0,
+ "electronica": 6.0,
+ "pop": 3.0,
+ "electro": 5.0,
+ "electronic": 83.0,
+ "dance": 66.0
+ }
+ },
+ {
+ "description": "Ellie Goulding",
+ "features": {
+ "indie": 85.0,
+ "singer-songwriter": 11.0,
+ "chillout": 3.0,
+ "british": 89.0,
+ "pop": 36.0,
+ "electronica": 3.0,
+ "rock": 1.0,
+ "electro": 2.0,
+ "synthpop": 9.0,
+ "electronic": 98.0,
+ "dance": 5.0,
+ "britpop": 2.0,
+ "indie pop": 78.0,
+ "seen live": 27.0,
+ "alternative": 6.0,
+ "acoustic": 2.0,
+ "female vocalist": 5.0,
+ "00s": 2.0,
+ "female vocalists": 100.0,
+ "folk": 4.0
+ }
+ },
+ {
+ "description": "Muse",
+ "features": {
+ "indie": 43.0,
+ "favorites": 2.0,
+ "male vocalists": 3.0,
+ "emo": 2.0,
+ "british": 21.0,
+ "experimental": 3.0,
+ "rock": 83.0,
+ "pop": 2.0,
+ "piano": 1.0,
+ "indie rock": 12.0,
+ "electronic": 4.0,
+ "britpop": 10.0,
+ "seen live": 46.0,
+ "alternative": 70.0,
+ "metal": 1.0,
+ "Progressive rock": 57.0,
+ "alternative rock": 100.0,
+ "00s": 2.0,
+ "hard rock": 2.0,
+ "90s": 2.0
+ }
+ },
+ {
+ "description": "Green Day",
+ "features": {
+ "indie": 3.0,
+ "favorites": 2.0,
+ "male vocalists": 2.0,
+ "emo": 4.0,
+ "american": 7.0,
+ "classic rock": 2.0,
+ "rock": 75.0,
+ "pop": 6.0,
+ "punk rock": 100.0,
+ "indie rock": 2.0,
+ "seen live": 26.0,
+ "Grunge": 1.0,
+ "alternative": 47.0,
+ "metal": 2.0,
+ "alternative rock": 17.0,
+ "00s": 2.0,
+ "punk": 69.0,
+ "hard rock": 2.0,
+ "90s": 5.0
+ }
+ },
+ {
+ "description": "Metallica",
+ "features": {
+ "seen live": 31.0,
+ "emo": 2.0,
+ "metal": 91.0,
+ "alternative": 2.0,
+ "american": 6.0,
+ "Power metal": 1.0,
+ "classic rock": 6.0,
+ "rock": 34.0,
+ "alternative rock": 2.0,
+ "hard rock": 41.0,
+ "heavy metal": 74.0,
+ "80s": 4.0,
+ "thrash metal": 100.0,
+ "90s": 3.0,
+ "Progressive metal": 2.0
+ }
+ },
+ {
+ "description": "Sam Smith",
+ "features": {
+ "indie": 3.0,
+ "male vocalists": 9.0,
+ "singer-songwriter": 8.0,
+ "soul": 100.0,
+ "british": 55.0,
+ "pop": 87.0,
+ "electronic": 45.0,
+ "dance": 32.0,
+ "seen live": 30.0,
+ "rnb": 7.0,
+ "alternative": 3.0,
+ "folk": 2.0
+ }
+ },
+ {
+ "description": "Kygo",
+ "features": {
+ "electronica": 7.0,
+ "downtempo": 49.0,
+ "dance": 5.0,
+ "House": 74.0,
+ "chillout": 65.0,
+ "pop": 5.0,
+ "synthpop": 3.0,
+ "electronic": 100.0,
+ "seen live": 36.0
+ }
+ },
+ {
+ "description": "Halsey",
+ "features": {
+ "american": 34.0,
+ "classic rock": 2.0,
+ "beautiful": 4.0,
+ "black metal": 4.0,
+ "pop": 100.0,
+ "electronic": 38.0,
+ "noise": 2.0,
+ "heavy metal": 2.0,
+ "indie pop": 44.0,
+ "seen live": 32.0,
+ "female vocalist": 4.0,
+ "singer-songwriter": 8.0,
+ "british": 4.0,
+ "rnb": 4.0,
+ "trance": 2.0,
+ "female vocalists": 44.0,
+ "indie": 68.0,
+ "soul": 4.0,
+ "emo": 4.0,
+ "synthpop": 12.0,
+ "alternative": 32.0
+ }
+ },
+ {
+ "description": "Michael Jackson",
+ "features": {
+ "male vocalists": 5.0,
+ "singer-songwriter": 2.0,
+ "soul": 36.0,
+ "american": 6.0,
+ "classic rock": 2.0,
+ "pop": 100.0,
+ "rock": 8.0,
+ "electronic": 1.0,
+ "dance": 41.0,
+ "seen live": 3.0,
+ "rnb": 7.0,
+ "70s": 3.0,
+ "80s": 50.0,
+ "funk": 34.0,
+ "90s": 5.0
+ }
+ },
+ {
+ "description": "Maroon 5",
+ "features": {
+ "indie": 12.0,
+ "favorites": 3.0,
+ "male vocalists": 9.0,
+ "singer-songwriter": 2.0,
+ "soul": 3.0,
+ "emo": 2.0,
+ "jazz": 2.0,
+ "american": 11.0,
+ "classic rock": 2.0,
+ "rock": 100.0,
+ "pop": 96.0,
+ "punk rock": 2.0,
+ "indie rock": 3.0,
+ "electronic": 1.0,
+ "dance": 2.0,
+ "britpop": 1.0,
+ "indie pop": 2.0,
+ "seen live": 16.0,
+ "alternative": 69.0,
+ "acoustic": 2.0,
+ "metal": 1.0,
+ "alternative rock": 52.0,
+ "00s": 4.0,
+ "punk": 3.0,
+ "Love": 2.0,
+ "funk": 5.0
+ }
+ },
+ {
+ "description": "Charli XCX",
+ "features": {
+ "singer-songwriter": 5.0,
+ "british": 26.0,
+ "experimental": 2.0,
+ "electronica": 5.0,
+ "dance": 4.0,
+ "female vocalists": 56.0,
+ "indie": 7.0,
+ "chillout": 11.0,
+ "pop": 98.0,
+ "rock": 2.0,
+ "electro": 4.0,
+ "synthpop": 100.0,
+ "electronic": 99.0,
+ "new wave": 4.0,
+ "britpop": 2.0,
+ "indie pop": 7.0,
+ "seen live": 63.0,
+ "Hip-Hop": 2.0,
+ "alternative": 5.0,
+ "female vocalist": 2.0,
+ "00s": 2.0
+ }
+ },
+ {
+ "description": "Foo Fighters",
+ "features": {
+ "indie": 7.0,
+ "favorites": 2.0,
+ "male vocalists": 2.0,
+ "emo": 1.0,
+ "american": 5.0,
+ "classic rock": 1.0,
+ "rock": 100.0,
+ "pop": 2.0,
+ "punk rock": 2.0,
+ "indie rock": 3.0,
+ "seen live": 35.0,
+ "Grunge": 48.0,
+ "alternative": 47.0,
+ "metal": 2.0,
+ "alternative rock": 69.0,
+ "00s": 2.0,
+ "punk": 5.0,
+ "hard rock": 30.0,
+ "90s": 4.0
+ }
+ },
+ {
+ "description": "Florence + the Machine",
+ "features": {
+ "indie": 100.0,
+ "singer-songwriter": 7.0,
+ "soul": 5.0,
+ "british": 72.0,
+ "experimental": 2.0,
+ "pop": 10.0,
+ "rock": 5.0,
+ "beautiful": 1.0,
+ "indie rock": 8.0,
+ "electronic": 2.0,
+ "britpop": 2.0,
+ "indie pop": 61.0,
+ "seen live": 29.0,
+ "alternative": 72.0,
+ "female vocalist": 3.0,
+ "alternative rock": 5.0,
+ "00s": 2.0,
+ "female vocalists": 86.0,
+ "Love": 2.0,
+ "folk": 6.0
+ }
+ },
+ {
+ "description": "blink-182",
+ "features": {
+ "indie": 3.0,
+ "favorites": 2.0,
+ "male vocalists": 2.0,
+ "emo": 8.0,
+ "american": 6.0,
+ "rock": 56.0,
+ "pop": 5.0,
+ "punk rock": 100.0,
+ "indie rock": 2.0,
+ "seen live": 23.0,
+ "hardcore": 2.0,
+ "alternative": 42.0,
+ "metal": 2.0,
+ "alternative rock": 10.0,
+ "00s": 1.0,
+ "punk": 72.0,
+ "hard rock": 1.0,
+ "90s": 4.0
+ }
+ },
+ {
+ "description": "Vampire Weekend",
+ "features": {
+ "indie": 100.0,
+ "male vocalists": 3.0,
+ "american": 11.0,
+ "experimental": 6.0,
+ "rock": 37.0,
+ "pop": 9.0,
+ "indie rock": 82.0,
+ "electronic": 2.0,
+ "indie pop": 63.0,
+ "seen live": 58.0,
+ "alternative": 57.0,
+ "alternative rock": 7.0,
+ "00s": 4.0,
+ "folk": 1.0
+ }
+ },
+ {
+ "description": "Martin Garrix",
+ "features": {
+ "german": 2.0,
+ "experimental": 2.0,
+ "american": 2.0,
+ "electronica": 5.0,
+ "dance": 57.0,
+ "House": 100.0,
+ "Soundtrack": 2.0,
+ "black metal": 2.0,
+ "Power metal": 2.0,
+ "pop": 30.0,
+ "electronic": 80.0,
+ "noise": 2.0,
+ "seen live": 38.0,
+ "Grunge": 2.0,
+ "techno": 2.0,
+ "trance": 3.0,
+ "electro": 5.0
+ }
+ },
+ {
+ "description": "Paramore",
+ "features": {
+ "indie": 9.0,
+ "favorites": 2.0,
+ "emo": 35.0,
+ "american": 7.0,
+ "rock": 100.0,
+ "pop": 6.0,
+ "punk rock": 8.0,
+ "indie rock": 4.0,
+ "seen live": 34.0,
+ "alternative": 79.0,
+ "female vocalist": 6.0,
+ "alternative rock": 22.0,
+ "screamo": 1.0,
+ "00s": 2.0,
+ "female vocalists": 72.0,
+ "punk": 13.0,
+ "Love": 2.0
+ }
+ },
+ {
+ "description": "Britney Spears",
+ "features": {
+ "singer-songwriter": 2.0,
+ "american": 9.0,
+ "electronica": 1.0,
+ "beautiful": 2.0,
+ "dance": 44.0,
+ "rnb": 3.0,
+ "House": 1.0,
+ "trance": 1.0,
+ "female vocalists": 44.0,
+ "Love": 2.0,
+ "black metal": 2.0,
+ "90s": 6.0,
+ "indie": 1.0,
+ "death metal": 1.0,
+ "emo": 2.0,
+ "pop": 100.0,
+ "rock": 3.0,
+ "electro": 2.0,
+ "electronic": 5.0,
+ "seen live": 7.0,
+ "Hip-Hop": 2.0,
+ "alternative": 2.0,
+ "female vocalist": 4.0,
+ "00s": 4.0
+ }
+ },
+ {
+ "description": "Travi$ Scott",
+ "features": {
+ "male vocalists": 2.0,
+ "american": 10.0,
+ "experimental": 4.0,
+ "electronica": 2.0,
+ "rap": 62.0,
+ "rnb": 4.0,
+ "soul": 4.0,
+ "electro": 4.0,
+ "hip hop": 20.0,
+ "seen live": 24.0,
+ "Hip-Hop": 100.0,
+ "alternative": 2.0
+ }
+ },
+ {
+ "description": "Oasis",
+ "features": {
+ "indie": 45.0,
+ "favorites": 1.0,
+ "male vocalists": 2.0,
+ "british": 61.0,
+ "classic rock": 3.0,
+ "rock": 89.0,
+ "pop": 7.0,
+ "indie rock": 12.0,
+ "britpop": 100.0,
+ "seen live": 21.0,
+ "alternative": 53.0,
+ "alternative rock": 20.0,
+ "00s": 2.0,
+ "punk": 1.0,
+ "hard rock": 2.0,
+ "90s": 10.0
+ }
+ },
+ {
+ "description": "A$AP Rocky",
+ "features": {
+ "male vocalists": 3.0,
+ "american": 9.0,
+ "experimental": 2.0,
+ "hip hop": 12.0,
+ "rap": 61.0,
+ "seen live": 30.0,
+ "Hip-Hop": 100.0
+ }
+ },
+ {
+ "description": "Nicki Minaj",
+ "features": {
+ "singer-songwriter": 2.0,
+ "american": 7.0,
+ "dance": 9.0,
+ "rap": 84.0,
+ "rnb": 51.0,
+ "female vocalists": 25.0,
+ "Love": 2.0,
+ "chillout": 1.0,
+ "pop": 44.0,
+ "hip hop": 39.0,
+ "electronic": 3.0,
+ "seen live": 4.0,
+ "Hip-Hop": 100.0,
+ "alternative": 2.0,
+ "female vocalist": 3.0
+ }
+ },
+ {
+ "description": "The Smiths",
+ "features": {
+ "indie": 100.0,
+ "favorites": 3.0,
+ "male vocalists": 3.0,
+ "singer-songwriter": 2.0,
+ "emo": 2.0,
+ "british": 32.0,
+ "classic rock": 4.0,
+ "rock": 32.0,
+ "pop": 10.0,
+ "indie rock": 22.0,
+ "new wave": 85.0,
+ "electronic": 1.0,
+ "britpop": 10.0,
+ "indie pop": 10.0,
+ "seen live": 2.0,
+ "alternative": 77.0,
+ "post-punk": 85.0,
+ "alternative rock": 21.0,
+ "punk": 2.0,
+ "Love": 2.0,
+ "80s": 96.0
+ }
+ },
+ {
+ "description": "The Strokes",
+ "features": {
+ "indie": 97.0,
+ "favorites": 2.0,
+ "male vocalists": 2.0,
+ "emo": 1.0,
+ "american": 10.0,
+ "british": 2.0,
+ "classic rock": 2.0,
+ "rock": 98.0,
+ "pop": 2.0,
+ "punk rock": 2.0,
+ "indie rock": 100.0,
+ "new wave": 1.0,
+ "britpop": 2.0,
+ "indie pop": 3.0,
+ "seen live": 42.0,
+ "alternative": 71.0,
+ "post-punk": 5.0,
+ "alternative rock": 59.0,
+ "00s": 4.0,
+ "punk": 4.0,
+ "Love": 1.0
+ }
+ },
+ {
+ "description": "MGMT",
+ "features": {
+ "indie": 90.0,
+ "male vocalists": 2.0,
+ "Psychedelic Rock": 4.0,
+ "american": 8.0,
+ "experimental": 5.0,
+ "psychedelic": 68.0,
+ "pop": 7.0,
+ "rock": 5.0,
+ "electronica": 5.0,
+ "electro": 3.0,
+ "synthpop": 10.0,
+ "indie rock": 10.0,
+ "electronic": 100.0,
+ "dance": 3.0,
+ "indie pop": 51.0,
+ "seen live": 35.0,
+ "alternative": 53.0,
+ "post-punk": 1.0,
+ "alternative rock": 3.0,
+ "00s": 3.0
+ }
+ },
+ {
+ "description": "Fall Out Boy",
+ "features": {
+ "indie": 14.0,
+ "favorites": 3.0,
+ "male vocalists": 3.0,
+ "emo": 86.0,
+ "american": 7.0,
+ "rock": 91.0,
+ "pop": 9.0,
+ "punk rock": 28.0,
+ "indie rock": 4.0,
+ "dance": 1.0,
+ "seen live": 39.0,
+ "hardcore": 3.0,
+ "alternative": 69.0,
+ "metal": 2.0,
+ "alternative rock": 16.0,
+ "screamo": 3.0,
+ "00s": 2.0,
+ "punk": 55.0,
+ "Love": 2.0
+ }
+ },
+ {
+ "description": "Dua Lipa",
+ "features": {
+ "beautiful": 8.0,
+ "dance": 11.0,
+ "pop": 100.0,
+ "electronic": 11.0,
+ "indie pop": 16.0,
+ "seen live": 28.0,
+ "british": 29.0,
+ "japanese": 6.0,
+ "rnb": 12.0,
+ "female vocalists": 31.0,
+ "indie": 9.0,
+ "emo": 9.0,
+ "synthpop": 35.0
+ }
+ },
+ {
+ "description": "Jonas Brothers",
+ "features": {
+ "american": 9.0,
+ "grindcore": 3.0,
+ "punk rock": 2.0,
+ "dance": 2.0,
+ "metal": 3.0,
+ "House": 2.0,
+ "Soundtrack": 2.0,
+ "black metal": 3.0,
+ "Power metal": 2.0,
+ "pop": 100.0,
+ "rock": 56.0,
+ "Melodic Death Metal": 3.0,
+ "seen live": 17.0,
+ "00s": 4.0,
+ "male vocalists": 10.0,
+ "female vocalists": 2.0,
+ "punk": 3.0,
+ "Love": 3.0,
+ "indie": 2.0,
+ "death metal": 4.0,
+ "emo": 3.0,
+ "metalcore": 2.0,
+ "hardcore": 2.0,
+ "alternative": 2.0,
+ "alternative rock": 2.0
+ }
+ },
+ {
+ "description": "Foster the People",
+ "features": {
+ "indie": 100.0,
+ "male vocalists": 4.0,
+ "american": 36.0,
+ "pop": 10.0,
+ "rock": 7.0,
+ "indie rock": 12.0,
+ "electronic": 8.0,
+ "dance": 2.0,
+ "indie pop": 83.0,
+ "seen live": 23.0,
+ "alternative": 65.0,
+ "alternative rock": 7.0,
+ "00s": 1.0
+ }
+ },
+ {
+ "description": "Bon Iver",
+ "features": {
+ "indie": 75.0,
+ "singer-songwriter": 70.0,
+ "male vocalists": 4.0,
+ "Mellow": 3.0,
+ "chillout": 2.0,
+ "american": 7.0,
+ "experimental": 2.0,
+ "ambient": 2.0,
+ "rock": 2.0,
+ "beautiful": 3.0,
+ "indie rock": 5.0,
+ "indie pop": 2.0,
+ "seen live": 36.0,
+ "acoustic": 59.0,
+ "alternative": 6.0,
+ "00s": 2.0,
+ "folk": 100.0
+ }
+ },
+ {
+ "description": "Major Lazer",
+ "features": {
+ "indie": 2.0,
+ "american": 4.0,
+ "experimental": 3.0,
+ "british": 2.0,
+ "electronica": 5.0,
+ "pop": 2.0,
+ "electro": 37.0,
+ "hip hop": 2.0,
+ "electronic": 85.0,
+ "dance": 12.0,
+ "seen live": 100.0,
+ "Hip-Hop": 4.0,
+ "alternative": 2.0,
+ "House": 3.0,
+ "00s": 2.0,
+ "funk": 4.0,
+ "reggae": 66.0
+ }
+ },
+ {
+ "description": "Two Door Cinema Club",
+ "features": {
+ "indie": 100.0,
+ "male vocalists": 2.0,
+ "british": 63.0,
+ "rock": 6.0,
+ "pop": 3.0,
+ "electronica": 2.0,
+ "synthpop": 2.0,
+ "indie rock": 19.0,
+ "electronic": 68.0,
+ "dance": 2.0,
+ "britpop": 2.0,
+ "indie pop": 10.0,
+ "seen live": 47.0,
+ "alternative": 53.0,
+ "alternative rock": 4.0,
+ "00s": 2.0
+ }
+ },
+ {
+ "description": "OneRepublic",
+ "features": {
+ "indie": 63.0,
+ "favorites": 2.0,
+ "male vocalists": 8.0,
+ "emo": 1.0,
+ "american": 12.0,
+ "rock": 100.0,
+ "pop": 21.0,
+ "piano": 2.0,
+ "indie rock": 5.0,
+ "indie pop": 2.0,
+ "seen live": 9.0,
+ "alternative": 94.0,
+ "acoustic": 1.0,
+ "alternative rock": 80.0,
+ "00s": 3.0,
+ "Love": 2.0
+ }
+ },
+ {
+ "description": "Cage the Elephant",
+ "features": {
+ "indie": 59.0,
+ "male vocalists": 2.0,
+ "american": 11.0,
+ "rock": 68.0,
+ "punk rock": 44.0,
+ "indie rock": 100.0,
+ "seen live": 41.0,
+ "alternative": 73.0,
+ "alternative rock": 26.0,
+ "00s": 4.0,
+ "punk": 7.0
+ }
+ },
+ {
+ "description": "Arcade Fire",
+ "features": {
+ "indie": 100.0,
+ "male vocalists": 2.0,
+ "experimental": 6.0,
+ "rock": 48.0,
+ "pop": 2.0,
+ "beautiful": 1.0,
+ "post-rock": 2.0,
+ "indie rock": 99.0,
+ "new wave": 1.0,
+ "indie pop": 4.0,
+ "seen live": 59.0,
+ "alternative": 76.0,
+ "post-punk": 3.0,
+ "alternative rock": 16.0,
+ "00s": 5.0,
+ "female vocalists": 2.0,
+ "folk": 6.0
+ }
+ },
+ {
+ "description": "Bruno Mars",
+ "features": {
+ "indie": 3.0,
+ "male vocalists": 47.0,
+ "singer-songwriter": 7.0,
+ "soul": 10.0,
+ "american": 8.0,
+ "blues": 2.0,
+ "pop": 100.0,
+ "rock": 2.0,
+ "hip hop": 2.0,
+ "dance": 2.0,
+ "seen live": 6.0,
+ "Hip-Hop": 3.0,
+ "rnb": 63.0,
+ "alternative": 2.0,
+ "acoustic": 1.0,
+ "folk": 1.0,
+ "funk": 4.0,
+ "reggae": 4.0
+ }
+ },
+ {
+ "description": "Carly Rae Jepsen",
+ "features": {
+ "singer-songwriter": 5.0,
+ "soul": 2.0,
+ "chillout": 2.0,
+ "pop": 100.0,
+ "synthpop": 9.0,
+ "electronic": 4.0,
+ "dance": 11.0,
+ "seen live": 6.0,
+ "acoustic": 21.0,
+ "female vocalist": 4.0,
+ "00s": 1.0,
+ "female vocalists": 46.0,
+ "folk": 26.0
+ }
+ },
+ {
+ "description": "Weezer",
+ "features": {
+ "indie": 50.0,
+ "favorites": 3.0,
+ "male vocalists": 2.0,
+ "emo": 15.0,
+ "american": 8.0,
+ "classic rock": 2.0,
+ "rock": 100.0,
+ "pop": 6.0,
+ "punk rock": 5.0,
+ "indie rock": 41.0,
+ "britpop": 1.0,
+ "indie pop": 3.0,
+ "seen live": 37.0,
+ "hardcore": 1.0,
+ "Grunge": 2.0,
+ "alternative": 86.0,
+ "metal": 2.0,
+ "alternative rock": 73.0,
+ "00s": 2.0,
+ "punk": 11.0,
+ "Love": 1.0,
+ "hard rock": 2.0,
+ "90s": 9.0
+ }
+ },
+ {
+ "description": "P!nk",
+ "features": {
+ "indie": 2.0,
+ "favorites": 2.0,
+ "singer-songwriter": 5.0,
+ "soul": 2.0,
+ "american": 11.0,
+ "pop": 100.0,
+ "rock": 59.0,
+ "electronic": 2.0,
+ "dance": 7.0,
+ "seen live": 14.0,
+ "Hip-Hop": 2.0,
+ "rnb": 5.0,
+ "alternative": 4.0,
+ "female vocalist": 4.0,
+ "alternative rock": 2.0,
+ "00s": 4.0,
+ "female vocalists": 74.0,
+ "punk": 3.0
+ }
+ },
+ {
+ "description": "System of a Down",
+ "features": {
+ "indie": 2.0,
+ "favorites": 1.0,
+ "male vocalists": 1.0,
+ "emo": 2.0,
+ "american": 5.0,
+ "experimental": 3.0,
+ "rock": 65.0,
+ "punk rock": 2.0,
+ "metalcore": 1.0,
+ "heavy metal": 6.0,
+ "Progressive metal": 3.0,
+ "thrash metal": 2.0,
+ "seen live": 18.0,
+ "hardcore": 3.0,
+ "metal": 100.0,
+ "alternative": 54.0,
+ "Progressive rock": 2.0,
+ "Nu Metal": 62.0,
+ "alternative rock": 13.0,
+ "industrial": 1.0,
+ "00s": 2.0,
+ "punk": 3.0,
+ "hard rock": 20.0,
+ "90s": 2.0
+ }
+ },
+ {
+ "description": "Adele",
+ "features": {
+ "indie": 40.0,
+ "singer-songwriter": 50.0,
+ "soul": 100.0,
+ "jazz": 8.0,
+ "chillout": 2.0,
+ "Mellow": 2.0,
+ "british": 66.0,
+ "blues": 3.0,
+ "pop": 19.0,
+ "rock": 2.0,
+ "beautiful": 1.0,
+ "piano": 2.0,
+ "britpop": 3.0,
+ "indie pop": 3.0,
+ "seen live": 2.0,
+ "rnb": 2.0,
+ "acoustic": 4.0,
+ "alternative": 4.0,
+ "female vocalist": 4.0,
+ "00s": 2.0,
+ "female vocalists": 81.0,
+ "Love": 2.0,
+ "folk": 2.0
+ }
+ },
+ {
+ "description": "xxxtentacion",
+ "features": {
+ "american": 11.0,
+ "experimental": 7.0,
+ "punk rock": 4.0,
+ "rap": 80.0,
+ "folk": 4.0,
+ "rock": 7.0,
+ "pop": 4.0,
+ "heavy metal": 4.0,
+ "seen live": 4.0,
+ "rnb": 7.0,
+ "emo": 49.0,
+ "soul": 4.0,
+ "hip hop": 32.0,
+ "new wave": 4.0,
+ "hardcore": 4.0,
+ "Hip-Hop": 100.0,
+ "alternative": 11.0,
+ "alternative rock": 7.0
+ }
+ },
+ {
+ "description": "The 1975",
+ "features": {
+ "male vocalists": 5.0,
+ "british": 69.0,
+ "experimental": 2.0,
+ "indie rock": 98.0,
+ "Love": 1.0,
+ "indie": 100.0,
+ "emo": 1.0,
+ "ambient": 1.0,
+ "rock": 21.0,
+ "pop": 9.0,
+ "post-rock": 1.0,
+ "synthpop": 5.0,
+ "new wave": 4.0,
+ "electronic": 3.0,
+ "britpop": 1.0,
+ "indie pop": 17.0,
+ "seen live": 73.0,
+ "alternative": 69.0,
+ "alternative rock": 20.0,
+ "00s": 1.0,
+ "funk": 1.0
+ }
+ },
+ {
+ "description": "Depeche Mode",
+ "features": {
+ "indie": 3.0,
+ "favorites": 1.0,
+ "male vocalists": 2.0,
+ "british": 13.0,
+ "ambient": 1.0,
+ "experimental": 1.0,
+ "classic rock": 2.0,
+ "rock": 16.0,
+ "electronica": 11.0,
+ "pop": 9.0,
+ "electro": 3.0,
+ "synthpop": 56.0,
+ "electronic": 100.0,
+ "new wave": 87.0,
+ "dance": 3.0,
+ "britpop": 2.0,
+ "seen live": 22.0,
+ "techno": 1.0,
+ "alternative": 20.0,
+ "Gothic": 2.0,
+ "post-punk": 4.0,
+ "alternative rock": 5.0,
+ "industrial": 3.0,
+ "00s": 2.0,
+ "80s": 59.0,
+ "90s": 4.0
+ }
+ },
+ {
+ "description": "The White Stripes",
+ "features": {
+ "indie": 59.0,
+ "favorites": 2.0,
+ "male vocalists": 1.0,
+ "singer-songwriter": 1.0,
+ "american": 8.0,
+ "experimental": 1.0,
+ "blues": 13.0,
+ "classic rock": 3.0,
+ "rock": 100.0,
+ "pop": 2.0,
+ "punk rock": 2.0,
+ "indie rock": 47.0,
+ "seen live": 25.0,
+ "Grunge": 1.0,
+ "alternative": 74.0,
+ "metal": 1.0,
+ "guitar": 2.0,
+ "Progressive rock": 1.0,
+ "alternative rock": 65.0,
+ "00s": 3.0,
+ "punk": 5.0,
+ "hard rock": 3.0,
+ "folk": 2.0,
+ "90s": 2.0
+ }
+ },
+ {
+ "description": "The Clash",
+ "features": {
+ "indie": 5.0,
+ "british": 43.0,
+ "classic rock": 29.0,
+ "rock": 43.0,
+ "pop": 1.0,
+ "punk rock": 71.0,
+ "indie rock": 2.0,
+ "new wave": 4.0,
+ "alternative": 9.0,
+ "post-punk": 3.0,
+ "alternative rock": 2.0,
+ "punk": 100.0,
+ "hard rock": 1.0,
+ "70s": 8.0,
+ "80s": 13.0,
+ "reggae": 6.0
+ }
+ }
+]
\ No newline at end of file
diff --git a/algorithms-miscellaneous-3/src/main/resources/kmeans/lastfm.json b/algorithms-miscellaneous-3/src/main/resources/kmeans/lastfm.json
new file mode 100644
index 0000000000..4d684b2fc0
--- /dev/null
+++ b/algorithms-miscellaneous-3/src/main/resources/kmeans/lastfm.json
@@ -0,0 +1,490 @@
+{
+ "children": [
+ {
+ "children": [
+ {
+ "name": "Radiohead"
+ },
+ {
+ "name": "Red Hot Chili Peppers"
+ },
+ {
+ "name": "Coldplay"
+ },
+ {
+ "name": "Nirvana"
+ },
+ {
+ "name": "Panic! at the Disco"
+ },
+ {
+ "name": "The Cure"
+ },
+ {
+ "name": "Linkin Park"
+ },
+ {
+ "name": "Radiohead"
+ },
+ {
+ "name": "Red Hot Chili Peppers"
+ },
+ {
+ "name": "Coldplay"
+ },
+ {
+ "name": "Nirvana"
+ },
+ {
+ "name": "Panic! at the Disco"
+ },
+ {
+ "name": "The Cure"
+ },
+ {
+ "name": "Linkin Park"
+ },
+ {
+ "name": "Muse"
+ },
+ {
+ "name": "Maroon 5"
+ },
+ {
+ "name": "Foo Fighters"
+ },
+ {
+ "name": "Paramore"
+ },
+ {
+ "name": "Oasis"
+ },
+ {
+ "name": "Fall Out Boy"
+ },
+ {
+ "name": "OneRepublic"
+ },
+ {
+ "name": "Weezer"
+ },
+ {
+ "name": "System of a Down"
+ },
+ {
+ "name": "The White Stripes"
+ }
+ ],
+ "name": "rock, alternative"
+ },
+ {
+ "children": [
+ {
+ "name": "Lil Nas X"
+ },
+ {
+ "name": "Post Malone"
+ },
+ {
+ "name": "Drake"
+ },
+ {
+ "name": "Kanye West"
+ },
+ {
+ "name": "Kendrick Lamar"
+ },
+ {
+ "name": "Tyler, the Creator"
+ },
+ {
+ "name": "Eminem"
+ },
+ {
+ "name": "Childish Gambino"
+ },
+ {
+ "name": "Frank Ocean"
+ },
+ {
+ "name": "Lil Nas X"
+ },
+ {
+ "name": "Post Malone"
+ },
+ {
+ "name": "Drake"
+ },
+ {
+ "name": "Kanye West"
+ },
+ {
+ "name": "Kendrick Lamar"
+ },
+ {
+ "name": "Tyler, the Creator"
+ },
+ {
+ "name": "Eminem"
+ },
+ {
+ "name": "Childish Gambino"
+ },
+ {
+ "name": "Frank Ocean"
+ },
+ {
+ "name": "Lizzo"
+ },
+ {
+ "name": "Travi$ Scott"
+ },
+ {
+ "name": "A$AP Rocky"
+ },
+ {
+ "name": "Nicki Minaj"
+ },
+ {
+ "name": "xxxtentacion"
+ }
+ ],
+ "name": "Hip-Hop, rap"
+ },
+ {
+ "children": [
+ {
+ "name": "Arctic Monkeys"
+ },
+ {
+ "name": "Imagine Dragons"
+ },
+ {
+ "name": "The Killers"
+ },
+ {
+ "name": "Gorillaz"
+ },
+ {
+ "name": "The Black Keys"
+ },
+ {
+ "name": "Arctic Monkeys"
+ },
+ {
+ "name": "Imagine Dragons"
+ },
+ {
+ "name": "The Killers"
+ },
+ {
+ "name": "Gorillaz"
+ },
+ {
+ "name": "The Black Keys"
+ },
+ {
+ "name": "Twenty One Pilots"
+ },
+ {
+ "name": "Ellie Goulding"
+ },
+ {
+ "name": "Florence + the Machine"
+ },
+ {
+ "name": "Vampire Weekend"
+ },
+ {
+ "name": "The Smiths"
+ },
+ {
+ "name": "The Strokes"
+ },
+ {
+ "name": "MGMT"
+ },
+ {
+ "name": "Foster the People"
+ },
+ {
+ "name": "Two Door Cinema Club"
+ },
+ {
+ "name": "Cage the Elephant"
+ },
+ {
+ "name": "Arcade Fire"
+ },
+ {
+ "name": "The 1975"
+ }
+ ],
+ "name": "indie, alternative"
+ },
+ {
+ "children": [
+ {
+ "name": "Ed Sheeran"
+ },
+ {
+ "name": "Tame Impala"
+ },
+ {
+ "name": "Ed Sheeran"
+ },
+ {
+ "name": "Tame Impala"
+ },
+ {
+ "name": "Green Day"
+ },
+ {
+ "name": "Metallica"
+ },
+ {
+ "name": "blink-182"
+ },
+ {
+ "name": "Bon Iver"
+ },
+ {
+ "name": "The Clash"
+ }
+ ],
+ "name": "rock, punk rock"
+ },
+ {
+ "children": [
+ {
+ "name": "Calvin Harris"
+ },
+ {
+ "name": "The Weeknd"
+ },
+ {
+ "name": "The Chainsmokers"
+ },
+ {
+ "name": "Daft Punk"
+ },
+ {
+ "name": "Marshmello"
+ },
+ {
+ "name": "David Guetta"
+ },
+ {
+ "name": "Calvin Harris"
+ },
+ {
+ "name": "The Weeknd"
+ },
+ {
+ "name": "The Chainsmokers"
+ },
+ {
+ "name": "Daft Punk"
+ },
+ {
+ "name": "Marshmello"
+ },
+ {
+ "name": "David Guetta"
+ },
+ {
+ "name": "Avicii"
+ },
+ {
+ "name": "Kygo"
+ },
+ {
+ "name": "Martin Garrix"
+ },
+ {
+ "name": "Major Lazer"
+ },
+ {
+ "name": "Depeche Mode"
+ }
+ ],
+ "name": "electronic, dance"
+ },
+ {
+ "children": [
+ {
+ "name": "Queen"
+ },
+ {
+ "name": "The Beatles"
+ },
+ {
+ "name": "David Bowie"
+ },
+ {
+ "name": "Fleetwood Mac"
+ },
+ {
+ "name": "Pink Floyd"
+ },
+ {
+ "name": "The Rolling Stones"
+ },
+ {
+ "name": "Led Zeppelin"
+ },
+ {
+ "name": "Queen"
+ },
+ {
+ "name": "The Beatles"
+ },
+ {
+ "name": "David Bowie"
+ },
+ {
+ "name": "Fleetwood Mac"
+ },
+ {
+ "name": "Pink Floyd"
+ },
+ {
+ "name": "The Rolling Stones"
+ },
+ {
+ "name": "Led Zeppelin"
+ },
+ {
+ "name": "Elton John"
+ }
+ ],
+ "name": "classic rock, rock"
+ },
+ {
+ "children": [
+ {
+ "name": "Billie Eilish"
+ },
+ {
+ "name": "Ariana Grande"
+ },
+ {
+ "name": "Taylor Swift"
+ },
+ {
+ "name": "Beyoncé"
+ },
+ {
+ "name": "Shawn Mendes"
+ },
+ {
+ "name": "Rihanna"
+ },
+ {
+ "name": "Lana Del Rey"
+ },
+ {
+ "name": "Katy Perry"
+ },
+ {
+ "name": "Lady Gaga"
+ },
+ {
+ "name": "Miley Cyrus"
+ },
+ {
+ "name": "Mark Ronson"
+ },
+ {
+ "name": "Madonna"
+ },
+ {
+ "name": "Lorde"
+ },
+ {
+ "name": "Khalid"
+ },
+ {
+ "name": "Billie Eilish"
+ },
+ {
+ "name": "Ariana Grande"
+ },
+ {
+ "name": "Taylor Swift"
+ },
+ {
+ "name": "Beyoncé"
+ },
+ {
+ "name": "Shawn Mendes"
+ },
+ {
+ "name": "Rihanna"
+ },
+ {
+ "name": "Lana Del Rey"
+ },
+ {
+ "name": "Katy Perry"
+ },
+ {
+ "name": "Lady Gaga"
+ },
+ {
+ "name": "Miley Cyrus"
+ },
+ {
+ "name": "Mark Ronson"
+ },
+ {
+ "name": "Madonna"
+ },
+ {
+ "name": "Lorde"
+ },
+ {
+ "name": "Khalid"
+ },
+ {
+ "name": "Sia"
+ },
+ {
+ "name": "Sam Smith"
+ },
+ {
+ "name": "Halsey"
+ },
+ {
+ "name": "Michael Jackson"
+ },
+ {
+ "name": "Charli XCX"
+ },
+ {
+ "name": "Britney Spears"
+ },
+ {
+ "name": "Dua Lipa"
+ },
+ {
+ "name": "Jonas Brothers"
+ },
+ {
+ "name": "Bruno Mars"
+ },
+ {
+ "name": "Carly Rae Jepsen"
+ },
+ {
+ "name": "P!nk"
+ },
+ {
+ "name": "Adele"
+ }
+ ],
+ "name": "pop, female vocalists"
+ }
+ ],
+ "name": "Musicians"
+}
diff --git a/algorithms-miscellaneous-3/src/main/resources/kmeans/radial.html b/algorithms-miscellaneous-3/src/main/resources/kmeans/radial.html
new file mode 100644
index 0000000000..e7e7403871
--- /dev/null
+++ b/algorithms-miscellaneous-3/src/main/resources/kmeans/radial.html
@@ -0,0 +1,68 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/algorithms-miscellaneous-3/src/test/java/com/baeldung/algorithms/breadthfirstsearch/BreadthFirstSearchAlgorithmUnitTest.java b/algorithms-miscellaneous-3/src/test/java/com/baeldung/algorithms/breadthfirstsearch/BreadthFirstSearchAlgorithmUnitTest.java
new file mode 100644
index 0000000000..aa22fc5353
--- /dev/null
+++ b/algorithms-miscellaneous-3/src/test/java/com/baeldung/algorithms/breadthfirstsearch/BreadthFirstSearchAlgorithmUnitTest.java
@@ -0,0 +1,86 @@
+package com.baeldung.algorithms.breadthfirstsearch;
+
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+class BreadthFirstSearchAlgorithmUnitTest {
+
+ private Tree root;
+ private Tree rootFirstChild;
+ private Tree depthMostChild;
+ private Tree rootSecondChild;
+
+ private Node start;
+ private Node firstNeighbor;
+ private Node firstNeighborNeighbor;
+ private Node secondNeighbor;
+
+ @Test
+ void givenTree_whenSearchTen_thenRoot() {
+ initTree();
+ assertThat(BreadthFirstSearchAlgorithm.search(10, root)).isPresent().contains(root);
+ }
+
+ @Test
+ void givenTree_whenSearchThree_thenDepthMostValue() {
+ initTree();
+ assertThat(BreadthFirstSearchAlgorithm.search(3, root)).isPresent().contains(depthMostChild);
+ }
+
+ @Test
+ void givenTree_whenSearchFour_thenRootSecondChild() {
+ initTree();
+ assertThat(BreadthFirstSearchAlgorithm.search(4, root)).isPresent().contains(rootSecondChild);
+ }
+
+ @Test
+ void givenTree_whenSearchFive_thenNotFound() {
+ initTree();
+ assertThat(BreadthFirstSearchAlgorithm.search(5, root)).isEmpty();
+ }
+
+ private void initTree() {
+ root = Tree.of(10);
+ rootFirstChild = root.addChild(2);
+ depthMostChild = rootFirstChild.addChild(3);
+ rootSecondChild = root.addChild(4);
+ }
+
+ @Test
+ void givenNode_whenSearchTen_thenStart() {
+ initNode();
+ assertThat(BreadthFirstSearchAlgorithm.search(10, firstNeighborNeighbor)).isPresent().contains(start);
+ }
+
+ @Test
+ void givenNode_whenSearchThree_thenNeighborNeighbor() {
+ initNode();
+ assertThat(BreadthFirstSearchAlgorithm.search(3, firstNeighborNeighbor)).isPresent().contains(firstNeighborNeighbor);
+ }
+
+ @Test
+ void givenNode_whenSearchFour_thenSecondNeighbor() {
+ initNode();
+ assertThat(BreadthFirstSearchAlgorithm.search(4, firstNeighborNeighbor)).isPresent().contains(secondNeighbor);
+ }
+
+ @Test
+ void givenNode_whenSearchFive_thenNotFound() {
+ initNode();
+ assertThat(BreadthFirstSearchAlgorithm.search(5, firstNeighborNeighbor)).isEmpty();
+ }
+
+ private void initNode() {
+ start = new Node<>(10);
+ firstNeighbor = new Node<>(2);
+ start.connect(firstNeighbor);
+
+ firstNeighborNeighbor = new Node<>(3);
+ firstNeighbor.connect(firstNeighborNeighbor);
+ firstNeighborNeighbor.connect(start);
+
+ secondNeighbor = new Node<>(4);
+ start.connect(secondNeighbor);
+ }
+}
\ No newline at end of file
diff --git a/algorithms-miscellaneous-3/src/test/java/com/baeldung/algorithms/checksortedlist/SortedListCheckerUnitTest.java b/algorithms-miscellaneous-3/src/test/java/com/baeldung/algorithms/checksortedlist/SortedListCheckerUnitTest.java
new file mode 100644
index 0000000000..44c4388e6c
--- /dev/null
+++ b/algorithms-miscellaneous-3/src/test/java/com/baeldung/algorithms/checksortedlist/SortedListCheckerUnitTest.java
@@ -0,0 +1,106 @@
+package com.baeldung.algorithms.checksortedlist;
+
+import static com.baeldung.algorithms.checksortedlist.SortedListChecker.checkIfSortedUsingComparators;
+import static com.baeldung.algorithms.checksortedlist.SortedListChecker.checkIfSortedUsingIterativeApproach;
+import static com.baeldung.algorithms.checksortedlist.SortedListChecker.checkIfSortedUsingOrderingClass;
+import static com.baeldung.algorithms.checksortedlist.SortedListChecker.checkIfSortedUsingRecursion;
+import static java.util.Arrays.asList;
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+import org.junit.Before;
+import org.junit.Test;
+
+public class SortedListCheckerUnitTest {
+
+ private List sortedListOfString;
+ private List unsortedListOfString;
+ private List singletonList;
+
+ private List employeesSortedByName;
+ private List employeesNotSortedByName;
+
+ @Before
+ public void setUp() {
+ sortedListOfString = asList("Canada", "HK", "LA", "NJ", "NY");
+ unsortedListOfString = asList("LA", "HK", "NJ", "NY", "Canada");
+ singletonList = Collections.singletonList("NY");
+
+ employeesSortedByName = asList(new Employee(1L, "John"), new Employee(2L, "Kevin"), new Employee(3L, "Mike"));
+ employeesNotSortedByName = asList(new Employee(1L, "Kevin"), new Employee(2L, "John"), new Employee(3L, "Mike"));
+ }
+
+ @Test
+ public void givenSortedList_whenUsingIterativeApproach_thenReturnTrue() {
+ assertThat(checkIfSortedUsingIterativeApproach(sortedListOfString)).isTrue();
+ }
+
+ @Test
+ public void givenSingleElementList_whenUsingIterativeApproach_thenReturnTrue() {
+ assertThat(checkIfSortedUsingIterativeApproach(singletonList)).isTrue();
+ }
+
+ @Test
+ public void givenUnsortedList_whenUsingIterativeApproach_thenReturnFalse() {
+ assertThat(checkIfSortedUsingIterativeApproach(unsortedListOfString)).isFalse();
+ }
+
+ @Test
+ public void givenSortedListOfEmployees_whenUsingIterativeApproach_thenReturnTrue() {
+ assertThat(checkIfSortedUsingIterativeApproach(employeesSortedByName, Comparator.comparing(Employee::getName))).isTrue();
+ }
+
+ @Test
+ public void givenUnsortedListOfEmployees_whenUsingIterativeApproach_thenReturnFalse() {
+ assertThat(checkIfSortedUsingIterativeApproach(employeesNotSortedByName, Comparator.comparing(Employee::getName))).isFalse();
+ }
+
+ @Test
+ public void givenSortedList_whenUsingRecursion_thenReturnTrue() {
+ assertThat(checkIfSortedUsingRecursion(sortedListOfString)).isTrue();
+ }
+
+ @Test
+ public void givenSingleElementList_whenUsingRecursion_thenReturnTrue() {
+ assertThat(checkIfSortedUsingRecursion(singletonList)).isTrue();
+ }
+
+ @Test
+ public void givenUnsortedList_whenUsingRecursion_thenReturnFalse() {
+ assertThat(checkIfSortedUsingRecursion(unsortedListOfString)).isFalse();
+ }
+
+ @Test
+ public void givenSortedList_whenUsingGuavaOrdering_thenReturnTrue() {
+ assertThat(checkIfSortedUsingOrderingClass(sortedListOfString)).isTrue();
+ }
+
+ @Test
+ public void givenUnsortedList_whenUsingGuavaOrdering_thenReturnFalse() {
+ assertThat(checkIfSortedUsingOrderingClass(unsortedListOfString)).isFalse();
+ }
+
+ @Test
+ public void givenSortedListOfEmployees_whenUsingGuavaOrdering_thenReturnTrue() {
+ assertThat(checkIfSortedUsingOrderingClass(employeesSortedByName, Comparator.comparing(Employee::getName))).isTrue();
+ }
+
+ @Test
+ public void givenUnsortedListOfEmployees_whenUsingGuavaOrdering_thenReturnFalse() {
+ assertThat(checkIfSortedUsingOrderingClass(employeesNotSortedByName, Comparator.comparing(Employee::getName))).isFalse();
+ }
+
+ @Test
+ public void givenSortedList_whenUsingGuavaComparators_thenReturnTrue() {
+ assertThat(checkIfSortedUsingComparators(sortedListOfString)).isTrue();
+ }
+
+ @Test
+ public void givenUnsortedList_whenUsingGuavaComparators_thenReturnFalse() {
+ assertThat(checkIfSortedUsingComparators(unsortedListOfString)).isFalse();
+ }
+
+}
diff --git a/algorithms-miscellaneous-3/src/test/java/com/baeldung/algorithms/interpolationsearch/InterpolationSearchUnitTest.java b/algorithms-miscellaneous-3/src/test/java/com/baeldung/algorithms/interpolationsearch/InterpolationSearchUnitTest.java
new file mode 100644
index 0000000000..8ad962055e
--- /dev/null
+++ b/algorithms-miscellaneous-3/src/test/java/com/baeldung/algorithms/interpolationsearch/InterpolationSearchUnitTest.java
@@ -0,0 +1,29 @@
+package com.baeldung.algorithms.interpolationsearch;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import org.junit.Before;
+import org.junit.Test;
+
+public class InterpolationSearchUnitTest {
+
+ private int[] myData;
+
+ @Before
+ public void setUp() {
+ myData = new int[]{13,21,34,55,69,73,84,101};
+ }
+
+ @Test
+ public void givenSortedArray_whenLookingFor84_thenReturn6() {
+ int pos = InterpolationSearch.interpolationSearch(myData, 84);
+ assertEquals(6, pos);
+ }
+
+ @Test
+ public void givenSortedArray_whenLookingFor19_thenReturnMinusOne() {
+ int pos = InterpolationSearch.interpolationSearch(myData, 19);
+ assertEquals(-1, pos);
+ }
+
+}
diff --git a/algorithms-miscellaneous-3/src/test/java/com/baeldung/algorithms/printtriangles/PrintTriangleExamplesUnitTest.java b/algorithms-miscellaneous-3/src/test/java/com/baeldung/algorithms/printtriangles/PrintTriangleExamplesUnitTest.java
new file mode 100644
index 0000000000..97e99290c9
--- /dev/null
+++ b/algorithms-miscellaneous-3/src/test/java/com/baeldung/algorithms/printtriangles/PrintTriangleExamplesUnitTest.java
@@ -0,0 +1,101 @@
+package com.baeldung.algorithms.printtriangles;
+
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.assertEquals;
+
+@RunWith(JUnitParamsRunner.class)
+public class PrintTriangleExamplesUnitTest {
+
+ private static Object[][] rightTriangles() {
+ String expected0 = "";
+
+ String expected2 = "*" + System.lineSeparator()
+ + "**" + System.lineSeparator();
+
+ String expected5 = "*" + System.lineSeparator()
+ + "**" + System.lineSeparator()
+ + "***" + System.lineSeparator()
+ + "****" + System.lineSeparator()
+ + "*****" + System.lineSeparator();
+
+ String expected7 = "*" + System.lineSeparator()
+ + "**" + System.lineSeparator()
+ + "***" + System.lineSeparator()
+ + "****" + System.lineSeparator()
+ + "*****" + System.lineSeparator()
+ + "******" + System.lineSeparator()
+ + "*******" + System.lineSeparator();
+
+ return new Object[][] {
+ { 0, expected0 },
+ { 2, expected2 },
+ { 5, expected5 },
+ { 7, expected7 }
+ };
+ }
+
+ @Test
+ @Parameters(method = "rightTriangles")
+ public void whenPrintARightTriangleIsCalled_ThenTheCorrectStringIsReturned(int nrOfRows, String expected) {
+ String actual = PrintTriangleExamples.printARightTriangle(nrOfRows);
+
+ assertEquals(expected, actual);
+ }
+
+ private static Object[][] isoscelesTriangles() {
+ String expected0 = "";
+
+ String expected2 = " *" + System.lineSeparator()
+ + "***" + System.lineSeparator();
+
+ String expected5 = " *" + System.lineSeparator()
+ + " ***" + System.lineSeparator()
+ + " *****" + System.lineSeparator()
+ + " *******" + System.lineSeparator()
+ + "*********" + System.lineSeparator();
+
+ String expected7 = " *" + System.lineSeparator()
+ + " ***" + System.lineSeparator()
+ + " *****" + System.lineSeparator()
+ + " *******" + System.lineSeparator()
+ + " *********" + System.lineSeparator()
+ + " ***********" + System.lineSeparator()
+ + "*************" + System.lineSeparator();
+
+ return new Object[][] {
+ { 0, expected0 },
+ { 2, expected2 },
+ { 5, expected5 },
+ { 7, expected7 }
+ };
+ }
+
+ @Test
+ @Parameters(method = "isoscelesTriangles")
+ public void whenPrintAnIsoscelesTriangleIsCalled_ThenTheCorrectStringIsReturned(int nrOfRows, String expected) {
+ String actual = PrintTriangleExamples.printAnIsoscelesTriangle(nrOfRows);
+
+ assertEquals(expected, actual);
+ }
+
+ @Test
+ @Parameters(method = "isoscelesTriangles")
+ public void whenPrintAnIsoscelesTriangleUsingStringUtilsIsCalled_ThenTheCorrectStringIsReturned(int nrOfRows, String expected) {
+ String actual = PrintTriangleExamples.printAnIsoscelesTriangleUsingStringUtils(nrOfRows);
+
+ assertEquals(expected, actual);
+ }
+
+ @Test
+ @Parameters(method = "isoscelesTriangles")
+ public void whenPrintAnIsoscelesTriangleUsingSubstringIsCalled_ThenTheCorrectStringIsReturned(int nrOfRows, String expected) {
+ String actual = PrintTriangleExamples.printAnIsoscelesTriangleUsingSubstring(nrOfRows);
+
+ assertEquals(expected, actual);
+ }
+
+}
diff --git a/core-java-modules/core-java-8/src/test/java/com/baeldung/counter/CounterStatistics.java b/algorithms-miscellaneous-3/src/test/java/com/baeldung/counter/CounterStatistics.java
similarity index 100%
rename from core-java-modules/core-java-8/src/test/java/com/baeldung/counter/CounterStatistics.java
rename to algorithms-miscellaneous-3/src/test/java/com/baeldung/counter/CounterStatistics.java
diff --git a/core-java-modules/core-java-8/src/test/java/com/baeldung/counter/CounterUnitTest.java b/algorithms-miscellaneous-3/src/test/java/com/baeldung/counter/CounterUnitTest.java
similarity index 100%
rename from core-java-modules/core-java-8/src/test/java/com/baeldung/counter/CounterUnitTest.java
rename to algorithms-miscellaneous-3/src/test/java/com/baeldung/counter/CounterUnitTest.java
diff --git a/core-java-modules/core-java-8/src/test/java/com/baeldung/counter/CounterUtil.java b/algorithms-miscellaneous-3/src/test/java/com/baeldung/counter/CounterUtil.java
similarity index 100%
rename from core-java-modules/core-java-8/src/test/java/com/baeldung/counter/CounterUtil.java
rename to algorithms-miscellaneous-3/src/test/java/com/baeldung/counter/CounterUtil.java
diff --git a/algorithms-miscellaneous-4/README.md b/algorithms-miscellaneous-4/README.md
new file mode 100644
index 0000000000..6aad9a43e4
--- /dev/null
+++ b/algorithms-miscellaneous-4/README.md
@@ -0,0 +1,15 @@
+## Algorithms - Miscellaneous
+
+This module contains articles about algorithms. Some classes of algorithms, e.g., [sorting](/../algorithms-sorting) and
+[genetic algorithms](/../algorithms-genetic), have their own dedicated modules.
+
+### Relevant articles:
+
+- [Multi-Swarm Optimization Algorithm in Java](https://www.baeldung.com/java-multi-swarm-algorithm)
+- [String Search Algorithms for Large Texts](https://www.baeldung.com/java-full-text-search-algorithms)
+- [Check If a String Contains All The Letters of The Alphabet](https://www.baeldung.com/java-string-contains-all-letters)
+- [Find the Middle Element of a Linked List](https://www.baeldung.com/java-linked-list-middle-element)
+- [Find Substrings That Are Palindromes in Java](https://www.baeldung.com/java-palindrome-substrings)
+- [Find the Longest Substring without Repeating Characters](https://www.baeldung.com/java-longest-substring-without-repeated-characters)
+- [Permutations of an Array in Java](https://www.baeldung.com/java-array-permutations)
+- More articles: [[<-- prev]](/../algorithms-miscellaneous-3) [[next -->]](/../algorithms-miscellaneous-5)
diff --git a/algorithms-miscellaneous-4/pom.xml b/algorithms-miscellaneous-4/pom.xml
new file mode 100644
index 0000000000..8fd8f807ba
--- /dev/null
+++ b/algorithms-miscellaneous-4/pom.xml
@@ -0,0 +1,51 @@
+
+ 4.0.0
+ algorithms-miscellaneous-4
+ 0.0.1-SNAPSHOT
+ algorithms-miscellaneous-4
+
+
+ com.baeldung
+ parent-modules
+ 1.0.0-SNAPSHOT
+
+
+
+
+ com.google.guava
+ guava
+ ${guava.version}
+
+
+ org.projectlombok
+ lombok
+ ${lombok.version}
+ provided
+
+
+ org.assertj
+ assertj-core
+ ${org.assertj.core.version}
+ test
+
+
+
+
+
+
+
+ org.codehaus.mojo
+ exec-maven-plugin
+ ${exec-maven-plugin.version}
+
+
+
+
+
+
+ 3.9.0
+ 27.0.1-jre
+
+
+
\ No newline at end of file
diff --git a/algorithms-miscellaneous-1/src/main/java/com/baeldung/algorithms/middleelementlookup/MiddleElementLookup.java b/algorithms-miscellaneous-4/src/main/java/com/baeldung/algorithms/middleelementlookup/MiddleElementLookup.java
similarity index 100%
rename from algorithms-miscellaneous-1/src/main/java/com/baeldung/algorithms/middleelementlookup/MiddleElementLookup.java
rename to algorithms-miscellaneous-4/src/main/java/com/baeldung/algorithms/middleelementlookup/MiddleElementLookup.java
diff --git a/algorithms-miscellaneous-1/src/main/java/com/baeldung/algorithms/middleelementlookup/Node.java b/algorithms-miscellaneous-4/src/main/java/com/baeldung/algorithms/middleelementlookup/Node.java
similarity index 100%
rename from algorithms-miscellaneous-1/src/main/java/com/baeldung/algorithms/middleelementlookup/Node.java
rename to algorithms-miscellaneous-4/src/main/java/com/baeldung/algorithms/middleelementlookup/Node.java
diff --git a/algorithms-miscellaneous-1/src/main/java/com/baeldung/algorithms/multiswarm/Constants.java b/algorithms-miscellaneous-4/src/main/java/com/baeldung/algorithms/multiswarm/Constants.java
similarity index 100%
rename from algorithms-miscellaneous-1/src/main/java/com/baeldung/algorithms/multiswarm/Constants.java
rename to algorithms-miscellaneous-4/src/main/java/com/baeldung/algorithms/multiswarm/Constants.java
diff --git a/algorithms-miscellaneous-1/src/main/java/com/baeldung/algorithms/multiswarm/FitnessFunction.java b/algorithms-miscellaneous-4/src/main/java/com/baeldung/algorithms/multiswarm/FitnessFunction.java
similarity index 100%
rename from algorithms-miscellaneous-1/src/main/java/com/baeldung/algorithms/multiswarm/FitnessFunction.java
rename to algorithms-miscellaneous-4/src/main/java/com/baeldung/algorithms/multiswarm/FitnessFunction.java
diff --git a/algorithms-miscellaneous-1/src/main/java/com/baeldung/algorithms/multiswarm/Multiswarm.java b/algorithms-miscellaneous-4/src/main/java/com/baeldung/algorithms/multiswarm/Multiswarm.java
similarity index 100%
rename from algorithms-miscellaneous-1/src/main/java/com/baeldung/algorithms/multiswarm/Multiswarm.java
rename to algorithms-miscellaneous-4/src/main/java/com/baeldung/algorithms/multiswarm/Multiswarm.java
diff --git a/algorithms-miscellaneous-1/src/main/java/com/baeldung/algorithms/multiswarm/Particle.java b/algorithms-miscellaneous-4/src/main/java/com/baeldung/algorithms/multiswarm/Particle.java
similarity index 100%
rename from algorithms-miscellaneous-1/src/main/java/com/baeldung/algorithms/multiswarm/Particle.java
rename to algorithms-miscellaneous-4/src/main/java/com/baeldung/algorithms/multiswarm/Particle.java
diff --git a/algorithms-miscellaneous-1/src/main/java/com/baeldung/algorithms/multiswarm/Swarm.java b/algorithms-miscellaneous-4/src/main/java/com/baeldung/algorithms/multiswarm/Swarm.java
similarity index 100%
rename from algorithms-miscellaneous-1/src/main/java/com/baeldung/algorithms/multiswarm/Swarm.java
rename to algorithms-miscellaneous-4/src/main/java/com/baeldung/algorithms/multiswarm/Swarm.java
diff --git a/algorithms-miscellaneous-1/src/main/java/com/baeldung/algorithms/permutation/Permutation.java b/algorithms-miscellaneous-4/src/main/java/com/baeldung/algorithms/permutation/Permutation.java
similarity index 100%
rename from algorithms-miscellaneous-1/src/main/java/com/baeldung/algorithms/permutation/Permutation.java
rename to algorithms-miscellaneous-4/src/main/java/com/baeldung/algorithms/permutation/Permutation.java
diff --git a/algorithms-miscellaneous-1/src/main/java/com/baeldung/algorithms/string/EnglishAlphabetLetters.java b/algorithms-miscellaneous-4/src/main/java/com/baeldung/algorithms/string/EnglishAlphabetLetters.java
similarity index 100%
rename from algorithms-miscellaneous-1/src/main/java/com/baeldung/algorithms/string/EnglishAlphabetLetters.java
rename to algorithms-miscellaneous-4/src/main/java/com/baeldung/algorithms/string/EnglishAlphabetLetters.java
diff --git a/algorithms-miscellaneous-1/src/main/java/com/baeldung/algorithms/string/LongestSubstringNonRepeatingCharacters.java b/algorithms-miscellaneous-4/src/main/java/com/baeldung/algorithms/string/LongestSubstringNonRepeatingCharacters.java
similarity index 100%
rename from algorithms-miscellaneous-1/src/main/java/com/baeldung/algorithms/string/LongestSubstringNonRepeatingCharacters.java
rename to algorithms-miscellaneous-4/src/main/java/com/baeldung/algorithms/string/LongestSubstringNonRepeatingCharacters.java
diff --git a/algorithms-miscellaneous-1/src/main/java/com/baeldung/algorithms/string/SubstringPalindrome.java b/algorithms-miscellaneous-4/src/main/java/com/baeldung/algorithms/string/SubstringPalindrome.java
similarity index 100%
rename from algorithms-miscellaneous-1/src/main/java/com/baeldung/algorithms/string/SubstringPalindrome.java
rename to algorithms-miscellaneous-4/src/main/java/com/baeldung/algorithms/string/SubstringPalindrome.java
diff --git a/algorithms-miscellaneous-1/src/main/java/com/baeldung/algorithms/string/search/StringSearchAlgorithms.java b/algorithms-miscellaneous-4/src/main/java/com/baeldung/algorithms/string/search/StringSearchAlgorithms.java
similarity index 100%
rename from algorithms-miscellaneous-1/src/main/java/com/baeldung/algorithms/string/search/StringSearchAlgorithms.java
rename to algorithms-miscellaneous-4/src/main/java/com/baeldung/algorithms/string/search/StringSearchAlgorithms.java
diff --git a/JGit/src/main/resources/logback.xml b/algorithms-miscellaneous-4/src/main/resources/logback.xml
similarity index 100%
rename from JGit/src/main/resources/logback.xml
rename to algorithms-miscellaneous-4/src/main/resources/logback.xml
diff --git a/algorithms-miscellaneous-1/src/test/java/com/baeldung/algorithms/MiddleElementLookupUnitTest.java b/algorithms-miscellaneous-4/src/test/java/com/baeldung/algorithms/MiddleElementLookupUnitTest.java
similarity index 100%
rename from algorithms-miscellaneous-1/src/test/java/com/baeldung/algorithms/MiddleElementLookupUnitTest.java
rename to algorithms-miscellaneous-4/src/test/java/com/baeldung/algorithms/MiddleElementLookupUnitTest.java
diff --git a/algorithms-miscellaneous-1/src/test/java/com/baeldung/algorithms/StringSearchAlgorithmsUnitTest.java b/algorithms-miscellaneous-4/src/test/java/com/baeldung/algorithms/StringSearchAlgorithmsUnitTest.java
similarity index 100%
rename from algorithms-miscellaneous-1/src/test/java/com/baeldung/algorithms/StringSearchAlgorithmsUnitTest.java
rename to algorithms-miscellaneous-4/src/test/java/com/baeldung/algorithms/StringSearchAlgorithmsUnitTest.java
diff --git a/algorithms-miscellaneous-1/src/test/java/com/baeldung/algorithms/multiswarm/LolFitnessFunction.java b/algorithms-miscellaneous-4/src/test/java/com/baeldung/algorithms/multiswarm/LolFitnessFunction.java
similarity index 100%
rename from algorithms-miscellaneous-1/src/test/java/com/baeldung/algorithms/multiswarm/LolFitnessFunction.java
rename to algorithms-miscellaneous-4/src/test/java/com/baeldung/algorithms/multiswarm/LolFitnessFunction.java
diff --git a/algorithms-miscellaneous-1/src/test/java/com/baeldung/algorithms/multiswarm/MultiswarmUnitTest.java b/algorithms-miscellaneous-4/src/test/java/com/baeldung/algorithms/multiswarm/MultiswarmUnitTest.java
similarity index 100%
rename from algorithms-miscellaneous-1/src/test/java/com/baeldung/algorithms/multiswarm/MultiswarmUnitTest.java
rename to algorithms-miscellaneous-4/src/test/java/com/baeldung/algorithms/multiswarm/MultiswarmUnitTest.java
diff --git a/algorithms-miscellaneous-1/src/test/java/com/baeldung/algorithms/string/EnglishAlphabetLettersUnitTest.java b/algorithms-miscellaneous-4/src/test/java/com/baeldung/algorithms/string/EnglishAlphabetLettersUnitTest.java
similarity index 100%
rename from algorithms-miscellaneous-1/src/test/java/com/baeldung/algorithms/string/EnglishAlphabetLettersUnitTest.java
rename to algorithms-miscellaneous-4/src/test/java/com/baeldung/algorithms/string/EnglishAlphabetLettersUnitTest.java
diff --git a/algorithms-miscellaneous-1/src/test/java/com/baeldung/algorithms/string/LongestSubstringNonRepeatingCharactersUnitTest.java b/algorithms-miscellaneous-4/src/test/java/com/baeldung/algorithms/string/LongestSubstringNonRepeatingCharactersUnitTest.java
similarity index 100%
rename from algorithms-miscellaneous-1/src/test/java/com/baeldung/algorithms/string/LongestSubstringNonRepeatingCharactersUnitTest.java
rename to algorithms-miscellaneous-4/src/test/java/com/baeldung/algorithms/string/LongestSubstringNonRepeatingCharactersUnitTest.java
diff --git a/algorithms-miscellaneous-1/src/test/java/com/baeldung/algorithms/string/SubstringPalindromeUnitTest.java b/algorithms-miscellaneous-4/src/test/java/com/baeldung/algorithms/string/SubstringPalindromeUnitTest.java
similarity index 100%
rename from algorithms-miscellaneous-1/src/test/java/com/baeldung/algorithms/string/SubstringPalindromeUnitTest.java
rename to algorithms-miscellaneous-4/src/test/java/com/baeldung/algorithms/string/SubstringPalindromeUnitTest.java
diff --git a/algorithms-miscellaneous-1/src/test/java/com/baeldung/algorithms/support/MayFailRule.java b/algorithms-miscellaneous-4/src/test/java/com/baeldung/algorithms/support/MayFailRule.java
similarity index 100%
rename from algorithms-miscellaneous-1/src/test/java/com/baeldung/algorithms/support/MayFailRule.java
rename to algorithms-miscellaneous-4/src/test/java/com/baeldung/algorithms/support/MayFailRule.java
diff --git a/algorithms-miscellaneous-5/README.md b/algorithms-miscellaneous-5/README.md
new file mode 100644
index 0000000000..7a8f2e0a36
--- /dev/null
+++ b/algorithms-miscellaneous-5/README.md
@@ -0,0 +1,11 @@
+## Algorithms - Miscellaneous
+
+This module contains articles about algorithms. Some classes of algorithms, e.g., [sorting](/../algorithms-sorting) and
+[genetic algorithms](/../algorithms-genetic), have their own dedicated modules.
+
+### Relevant articles:
+
+- [Converting Between Byte Arrays and Hexadecimal Strings in Java](https://www.baeldung.com/java-byte-arrays-hex-strings)
+- [Reversing a Binary Tree in Java](https://www.baeldung.com/java-reversing-a-binary-tree)
+- [Find If Two Numbers Are Relatively Prime in Java](https://www.baeldung.com/java-two-relatively-prime-numbers)
+- More articles: [[<-- prev]](/../algorithms-miscellaneous-4)
diff --git a/algorithms-miscellaneous-5/pom.xml b/algorithms-miscellaneous-5/pom.xml
new file mode 100644
index 0000000000..d17f93e6e0
--- /dev/null
+++ b/algorithms-miscellaneous-5/pom.xml
@@ -0,0 +1,57 @@
+
+ 4.0.0
+ algorithms-miscellaneous-5
+ 0.0.1-SNAPSHOT
+ algorithms-miscellaneous-5
+
+
+ com.baeldung
+ parent-modules
+ 1.0.0-SNAPSHOT
+
+
+
+
+ commons-codec
+ commons-codec
+ ${commons-codec.version}
+
+
+ org.projectlombok
+ lombok
+ ${lombok.version}
+ provided
+
+
+ pl.allegro.finance
+ tradukisto
+ ${tradukisto.version}
+
+
+ org.assertj
+ assertj-core
+ ${org.assertj.core.version}
+ test
+
+
+
+
+
+
+
+ org.codehaus.mojo
+ exec-maven-plugin
+ ${exec-maven-plugin.version}
+
+
+
+
+
+
+ 1.0.1
+ 3.9.0
+ 1.11
+
+
+
\ No newline at end of file
diff --git a/algorithms-miscellaneous-2/src/main/java/com/baeldung/algorithms/conversion/HexStringConverter.java b/algorithms-miscellaneous-5/src/main/java/com/baeldung/algorithms/conversion/HexStringConverter.java
similarity index 100%
rename from algorithms-miscellaneous-2/src/main/java/com/baeldung/algorithms/conversion/HexStringConverter.java
rename to algorithms-miscellaneous-5/src/main/java/com/baeldung/algorithms/conversion/HexStringConverter.java
diff --git a/algorithms-miscellaneous-5/src/main/java/com/baeldung/algorithms/knapsack/Knapsack.java b/algorithms-miscellaneous-5/src/main/java/com/baeldung/algorithms/knapsack/Knapsack.java
new file mode 100644
index 0000000000..61c3f05a95
--- /dev/null
+++ b/algorithms-miscellaneous-5/src/main/java/com/baeldung/algorithms/knapsack/Knapsack.java
@@ -0,0 +1,36 @@
+package com.baeldung.algorithms.knapsack;
+
+public class Knapsack {
+
+ public int knapsackRec(int[] w, int[] v, int n, int W) {
+ if (n <= 0) {
+ return 0;
+ } else if (w[n - 1] > W) {
+ return knapsackRec(w, v, n - 1, W);
+ } else {
+ return Math.max(knapsackRec(w, v, n - 1, W), v[n - 1] + knapsackRec(w, v, n - 1, W - w[n - 1]));
+ }
+ }
+
+ public int knapsackDP(int[] w, int[] v, int n, int W) {
+ if (n <= 0 || W <= 0) {
+ return 0;
+ }
+
+ int[][] m = new int[n + 1][W + 1];
+ for (int j = 0; j <= W; j++) {
+ m[0][j] = 0;
+ }
+
+ for (int i = 1; i <= n; i++) {
+ for (int j = 1; j <= W; j++) {
+ if (w[i - 1] > j) {
+ m[i][j] = m[i - 1][j];
+ } else {
+ m[i][j] = Math.max(m[i - 1][j], m[i - 1][j - w[i - 1]] + v[i - 1]);
+ }
+ }
+ }
+ return m[n][W];
+ }
+}
diff --git a/algorithms-miscellaneous-2/src/main/java/com/baeldung/algorithms/relativelyprime/RelativelyPrime.java b/algorithms-miscellaneous-5/src/main/java/com/baeldung/algorithms/relativelyprime/RelativelyPrime.java
similarity index 100%
rename from algorithms-miscellaneous-2/src/main/java/com/baeldung/algorithms/relativelyprime/RelativelyPrime.java
rename to algorithms-miscellaneous-5/src/main/java/com/baeldung/algorithms/relativelyprime/RelativelyPrime.java
diff --git a/algorithms-miscellaneous-2/src/main/java/com/baeldung/algorithms/reversingtree/TreeNode.java b/algorithms-miscellaneous-5/src/main/java/com/baeldung/algorithms/reversingtree/TreeNode.java
similarity index 100%
rename from algorithms-miscellaneous-2/src/main/java/com/baeldung/algorithms/reversingtree/TreeNode.java
rename to algorithms-miscellaneous-5/src/main/java/com/baeldung/algorithms/reversingtree/TreeNode.java
diff --git a/algorithms-miscellaneous-2/src/main/java/com/baeldung/algorithms/reversingtree/TreeReverser.java b/algorithms-miscellaneous-5/src/main/java/com/baeldung/algorithms/reversingtree/TreeReverser.java
similarity index 100%
rename from algorithms-miscellaneous-2/src/main/java/com/baeldung/algorithms/reversingtree/TreeReverser.java
rename to algorithms-miscellaneous-5/src/main/java/com/baeldung/algorithms/reversingtree/TreeReverser.java
diff --git a/Twitter4J/src/main/resources/logback.xml b/algorithms-miscellaneous-5/src/main/resources/logback.xml
similarity index 100%
rename from Twitter4J/src/main/resources/logback.xml
rename to algorithms-miscellaneous-5/src/main/resources/logback.xml
diff --git a/algorithms-miscellaneous-2/src/test/java/com/baeldung/algorithms/conversion/ByteArrayConverterUnitTest.java b/algorithms-miscellaneous-5/src/test/java/com/baeldung/algorithms/conversion/ByteArrayConverterUnitTest.java
similarity index 100%
rename from algorithms-miscellaneous-2/src/test/java/com/baeldung/algorithms/conversion/ByteArrayConverterUnitTest.java
rename to algorithms-miscellaneous-5/src/test/java/com/baeldung/algorithms/conversion/ByteArrayConverterUnitTest.java
diff --git a/algorithms-miscellaneous-5/src/test/java/com/baeldung/algorithms/knapsack/KnapsackUnitTest.java b/algorithms-miscellaneous-5/src/test/java/com/baeldung/algorithms/knapsack/KnapsackUnitTest.java
new file mode 100644
index 0000000000..b168e6b1eb
--- /dev/null
+++ b/algorithms-miscellaneous-5/src/test/java/com/baeldung/algorithms/knapsack/KnapsackUnitTest.java
@@ -0,0 +1,44 @@
+package com.baeldung.algorithms.knapsack;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import org.junit.jupiter.api.Test;
+
+public class KnapsackUnitTest {
+
+ @Test
+ public void givenWeightsandValues_whenCalculateMax_thenOutputCorrectResult() {
+ final int[] w = new int[] { 23, 26, 20, 18, 32, 27, 29, 26, 30, 27 };
+ final int[] v = new int[] { 505, 352, 458, 220, 354, 414, 498, 545, 473, 543 };
+ final int n = 10;
+ final int W = 67;
+ final Knapsack knapsack = new Knapsack();
+
+ assertEquals(1270, knapsack.knapsackRec(w, v, n, W));
+ assertEquals(1270, knapsack.knapsackDP(w, v, n, W));
+ }
+
+ @Test
+ public void givenZeroItems_whenCalculateMax_thenOutputZero() {
+ final int[] w = new int[] {};
+ final int[] v = new int[] {};
+ final int n = 0;
+ final int W = 67;
+ final Knapsack knapsack = new Knapsack();
+
+ assertEquals(0, knapsack.knapsackRec(w, v, n, W));
+ assertEquals(0, knapsack.knapsackDP(w, v, n, W));
+ }
+
+ @Test
+ public void givenZeroWeightLimit_whenCalculateMax_thenOutputZero() {
+ final int[] w = new int[] { 23, 26, 20, 18, 32, 27, 29, 26, 30, 27 };
+ final int[] v = new int[] { 505, 352, 458, 220, 354, 414, 498, 545, 473, 543 };
+ final int n = 10;
+ final int W = 0;
+ final Knapsack knapsack = new Knapsack();
+
+ assertEquals(0, knapsack.knapsackRec(w, v, n, W));
+ assertEquals(0, knapsack.knapsackDP(w, v, n, W));
+ }
+}
diff --git a/algorithms-miscellaneous-2/src/test/java/com/baeldung/algorithms/relativelyprime/RelativelyPrimeUnitTest.java b/algorithms-miscellaneous-5/src/test/java/com/baeldung/algorithms/relativelyprime/RelativelyPrimeUnitTest.java
similarity index 100%
rename from algorithms-miscellaneous-2/src/test/java/com/baeldung/algorithms/relativelyprime/RelativelyPrimeUnitTest.java
rename to algorithms-miscellaneous-5/src/test/java/com/baeldung/algorithms/relativelyprime/RelativelyPrimeUnitTest.java
diff --git a/algorithms-miscellaneous-2/src/test/java/com/baeldung/algorithms/reversingtree/TreeReverserUnitTest.java b/algorithms-miscellaneous-5/src/test/java/com/baeldung/algorithms/reversingtree/TreeReverserUnitTest.java
similarity index 100%
rename from algorithms-miscellaneous-2/src/test/java/com/baeldung/algorithms/reversingtree/TreeReverserUnitTest.java
rename to algorithms-miscellaneous-5/src/test/java/com/baeldung/algorithms/reversingtree/TreeReverserUnitTest.java
diff --git a/algorithms-sorting/README.md b/algorithms-sorting/README.md
index 36460293b0..3f27cfea49 100644
--- a/algorithms-sorting/README.md
+++ b/algorithms-sorting/README.md
@@ -1,7 +1,19 @@
-## Relevant articles:
+## Algorithms - Sorting
-- [Bubble Sort in Java](http://www.baeldung.com/java-bubble-sort)
+This module contains articles about sorting algorithms.
+
+### Relevant articles:
+
+- [Bubble Sort in Java](https://www.baeldung.com/java-bubble-sort)
- [Merge Sort in Java](https://www.baeldung.com/java-merge-sort)
- [Quicksort Algorithm Implementation in Java](https://www.baeldung.com/java-quicksort)
- [Insertion Sort in Java](https://www.baeldung.com/java-insertion-sort)
- [Heap Sort in Java](https://www.baeldung.com/java-heap-sort)
+- [Shell Sort in Java](https://www.baeldung.com/java-shell-sort)
+- [Counting Sort in Java](https://www.baeldung.com/java-counting-sort)
+- [Sorting Strings by Contained Numbers in Java](https://www.baeldung.com/java-sort-strings-contained-numbers)
+- [How an In-Place Sorting Algorithm Works](https://www.baeldung.com/java-in-place-sorting)
+- [Selection Sort in Java](https://www.baeldung.com/java-selection-sort)
+- [Sorting Strings by Contained Numbers in Java](https://www.baeldung.com/java-sort-strings-contained-numbers)
+- [Radix Sort in Java](https://www.baeldung.com/java-radix-sort)
+- [Sorting a String Alphabetically in Java](https://www.baeldung.com/java-sort-string-alphabetically)
diff --git a/algorithms-sorting/pom.xml b/algorithms-sorting/pom.xml
index b25adf05a8..5bb19a8069 100644
--- a/algorithms-sorting/pom.xml
+++ b/algorithms-sorting/pom.xml
@@ -28,6 +28,12 @@
${lombok.version}
provided
+
+ org.junit.jupiter
+ junit-jupiter-api
+ ${junit-jupiter-api.version}
+ test
+
org.assertj
assertj-core
@@ -52,6 +58,7 @@
3.6.1
3.9.0
1.11
+ 5.3.1
\ No newline at end of file
diff --git a/algorithms-sorting/src/main/java/com/baeldung/algorithms/bucketsort/IntegerBucketSorter.java b/algorithms-sorting/src/main/java/com/baeldung/algorithms/bucketsort/IntegerBucketSorter.java
new file mode 100644
index 0000000000..002d4997f4
--- /dev/null
+++ b/algorithms-sorting/src/main/java/com/baeldung/algorithms/bucketsort/IntegerBucketSorter.java
@@ -0,0 +1,68 @@
+package com.baeldung.bucketsort;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.LinkedList;
+import java.util.List;
+
+public class IntegerBucketSorter implements Sorter {
+
+ private final Comparator comparator;
+
+ public IntegerBucketSorter(Comparator comparator) {
+ this.comparator = comparator;
+ }
+
+ public IntegerBucketSorter() {
+ comparator = Comparator.naturalOrder();
+ }
+
+ public List sort(List arrayToSort) {
+
+ List> buckets = splitIntoUnsortedBuckets(arrayToSort);
+
+ for(List bucket : buckets){
+ bucket.sort(comparator);
+ }
+
+ return concatenateSortedBuckets(buckets);
+ }
+
+ private List concatenateSortedBuckets(List> buckets){
+ List sortedArray = new LinkedList<>();
+ for(List bucket : buckets){
+ sortedArray.addAll(bucket);
+ }
+ return sortedArray;
+ }
+
+ private List> splitIntoUnsortedBuckets(List initialList){
+
+ final int max = findMax(initialList);
+ final int numberOfBuckets = (int) Math.sqrt(initialList.size());
+
+ List> buckets = new ArrayList<>();
+ for(int i = 0; i < numberOfBuckets; i++) buckets.add(new ArrayList<>());
+
+ //distribute the data
+ for (int i : initialList) {
+ buckets.get(hash(i, max, numberOfBuckets)).add(i);
+ }
+ return buckets;
+
+ }
+
+ private int findMax(List input){
+ int m = Integer.MIN_VALUE;
+ for (int i : input){
+ m = Math.max(i, m);
+ }
+ return m;
+ }
+
+ private static int hash(int i, int max, int numberOfBuckets) {
+ return (int) ((double) i / max * (numberOfBuckets - 1));
+ }
+
+
+}
diff --git a/algorithms-sorting/src/main/java/com/baeldung/algorithms/bucketsort/Sorter.java b/algorithms-sorting/src/main/java/com/baeldung/algorithms/bucketsort/Sorter.java
new file mode 100644
index 0000000000..b86f60324f
--- /dev/null
+++ b/algorithms-sorting/src/main/java/com/baeldung/algorithms/bucketsort/Sorter.java
@@ -0,0 +1,8 @@
+package com.baeldung.bucketsort;
+
+import java.util.List;
+
+public interface Sorter {
+
+ List sort(List arrayToSort);
+}
diff --git a/algorithms-sorting/src/main/java/com/baeldung/algorithms/counting/CountingSort.java b/algorithms-sorting/src/main/java/com/baeldung/algorithms/counting/CountingSort.java
new file mode 100644
index 0000000000..823f372849
--- /dev/null
+++ b/algorithms-sorting/src/main/java/com/baeldung/algorithms/counting/CountingSort.java
@@ -0,0 +1,48 @@
+package com.baeldung.algorithms.counting;
+
+import java.util.Arrays;
+import java.util.stream.IntStream;
+
+public class CountingSort {
+
+ public static int[] sort(int[] input, int k) {
+ verifyPreconditions(input, k);
+ if (input.length == 0) return input;
+
+ int[] c = countElements(input, k);
+ int[] sorted = new int[input.length];
+ for (int i = input.length - 1; i >= 0; i--) {
+ int current = input[i];
+ sorted[c[current] - 1] = current;
+ c[current] -= 1;
+ }
+
+ return sorted;
+ }
+
+ static int[] countElements(int[] input, int k) {
+ int[] c = new int[k + 1];
+ Arrays.fill(c, 0);
+ for (int i : input) {
+ c[i] += 1;
+ }
+
+ for (int i = 1; i < c.length; i++) {
+ c[i] += c[i - 1];
+ }
+ return c;
+ }
+
+ private static void verifyPreconditions(int[] input, int k) {
+ if (input == null) {
+ throw new IllegalArgumentException("Input is required");
+ }
+
+ int min = IntStream.of(input).min().getAsInt();
+ int max = IntStream.of(input).max().getAsInt();
+
+ if (min < 0 || max > k) {
+ throw new IllegalArgumentException("The input numbers should be between zero and " + k);
+ }
+ }
+}
diff --git a/algorithms-sorting/src/main/java/com/baeldung/algorithms/inoutsort/InOutSort.java b/algorithms-sorting/src/main/java/com/baeldung/algorithms/inoutsort/InOutSort.java
new file mode 100644
index 0000000000..5ba225cead
--- /dev/null
+++ b/algorithms-sorting/src/main/java/com/baeldung/algorithms/inoutsort/InOutSort.java
@@ -0,0 +1,25 @@
+package com.baeldung.algorithms.inoutsort;
+
+public class InOutSort {
+
+ public static int[] reverseInPlace(int A[]) {
+ int n = A.length;
+ for (int i = 0; i < n / 2; i++) {
+ int temp = A[i];
+ A[i] = A[n - 1 - i];
+ A[n - 1 - i] = temp;
+ }
+
+ return A;
+ }
+
+ public static int[] reverseOutOfPlace(int A[]) {
+ int n = A.length;
+ int[] B = new int[n];
+ for (int i = 0; i < n; i++) {
+ B[n - i - 1] = A[i];
+ }
+
+ return B;
+ }
+}
diff --git a/algorithms-sorting/src/main/java/com/baeldung/algorithms/radixsort/RadixSort.java b/algorithms-sorting/src/main/java/com/baeldung/algorithms/radixsort/RadixSort.java
new file mode 100644
index 0000000000..723a2f5a80
--- /dev/null
+++ b/algorithms-sorting/src/main/java/com/baeldung/algorithms/radixsort/RadixSort.java
@@ -0,0 +1,53 @@
+package com.baeldung.algorithms.radixsort;
+
+import java.util.Arrays;
+
+public class RadixSort {
+
+ public static void sort(int numbers[]) {
+ int maximumNumber = findMaximumNumberIn(numbers);
+
+ int numberOfDigits = calculateNumberOfDigitsIn(maximumNumber);
+
+ int placeValue = 1;
+
+ while (numberOfDigits-- > 0) {
+ applyCountingSortOn(numbers, placeValue);
+ placeValue *= 10;
+ }
+ }
+
+ private static void applyCountingSortOn(int[] numbers, int placeValue) {
+ int range = 10; // radix or the base
+
+ int length = numbers.length;
+ int[] frequency = new int[range];
+ int[] sortedValues = new int[length];
+
+ for (int i = 0; i < length; i++) {
+ int digit = (numbers[i] / placeValue) % range;
+ frequency[digit]++;
+ }
+
+ for (int i = 1; i < range; i++) {
+ frequency[i] += frequency[i - 1];
+ }
+
+ for (int i = length - 1; i >= 0; i--) {
+ int digit = (numbers[i] / placeValue) % range;
+ sortedValues[frequency[digit] - 1] = numbers[i];
+ frequency[digit]--;
+ }
+
+ System.arraycopy(sortedValues, 0, numbers, 0, length);
+ }
+
+ private static int calculateNumberOfDigitsIn(int number) {
+ return (int) Math.log10(number) + 1; // valid only if number > 0
+ }
+
+ private static int findMaximumNumberIn(int[] arr) {
+ return Arrays.stream(arr).max().getAsInt();
+ }
+
+}
diff --git a/algorithms-sorting/src/main/java/com/baeldung/algorithms/selectionsort/SelectionSort.java b/algorithms-sorting/src/main/java/com/baeldung/algorithms/selectionsort/SelectionSort.java
new file mode 100644
index 0000000000..17e95edf06
--- /dev/null
+++ b/algorithms-sorting/src/main/java/com/baeldung/algorithms/selectionsort/SelectionSort.java
@@ -0,0 +1,38 @@
+package com.baeldung.algorithms.selectionsort;
+
+public class SelectionSort {
+
+ public static void sortAscending(final int[] arr) {
+ for (int i = 0; i < arr.length - 1; i++) {
+ int minElementIndex = i;
+ for (int j = i + 1; j < arr.length; j++) {
+ if (arr[minElementIndex] > arr[j]) {
+ minElementIndex = j;
+ }
+ }
+
+ if (minElementIndex != i) {
+ int temp = arr[i];
+ arr[i] = arr[minElementIndex];
+ arr[minElementIndex] = temp;
+ }
+ }
+ }
+
+ public static void sortDescending(final int[] arr) {
+ for (int i = 0; i < arr.length - 1; i++) {
+ int maxElementIndex = i;
+ for (int j = i + 1; j < arr.length; j++) {
+ if (arr[maxElementIndex] < arr[j]) {
+ maxElementIndex = j;
+ }
+ }
+
+ if (maxElementIndex != i) {
+ int temp = arr[i];
+ arr[i] = arr[maxElementIndex];
+ arr[maxElementIndex] = temp;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/algorithms-sorting/src/main/java/com/baeldung/algorithms/shellsort/ShellSort.java b/algorithms-sorting/src/main/java/com/baeldung/algorithms/shellsort/ShellSort.java
new file mode 100644
index 0000000000..c130e2d866
--- /dev/null
+++ b/algorithms-sorting/src/main/java/com/baeldung/algorithms/shellsort/ShellSort.java
@@ -0,0 +1,20 @@
+package com.baeldung.algorithms.shellsort;
+
+public class ShellSort {
+
+ public static void sort(int arrayToSort[]) {
+ int n = arrayToSort.length;
+
+ for (int gap = n / 2; gap > 0; gap /= 2) {
+ for (int i = gap; i < n; i++) {
+ int key = arrayToSort[i];
+ int j = i;
+ while (j >= gap && arrayToSort[j - gap] > key) {
+ arrayToSort[j] = arrayToSort[j - gap];
+ j -= gap;
+ }
+ arrayToSort[j] = key;
+ }
+ }
+ }
+}
diff --git a/algorithms-sorting/src/main/java/com/baeldung/algorithms/sort/bynumber/NaturalOrderComparators.java b/algorithms-sorting/src/main/java/com/baeldung/algorithms/sort/bynumber/NaturalOrderComparators.java
new file mode 100644
index 0000000000..b177bd60fc
--- /dev/null
+++ b/algorithms-sorting/src/main/java/com/baeldung/algorithms/sort/bynumber/NaturalOrderComparators.java
@@ -0,0 +1,30 @@
+package com.baeldung.algorithms.sort.bynumber;
+
+import java.util.Comparator;
+
+public final class NaturalOrderComparators {
+
+ private static final String DIGIT_AND_DECIMAL_REGEX = "[^\\d.]";
+
+ private NaturalOrderComparators() {
+ throw new AssertionError("Let's keep this static");
+ }
+
+ public static Comparator createNaturalOrderRegexComparator() {
+ return Comparator.comparingDouble(NaturalOrderComparators::parseStringToNumber);
+ }
+
+ private static double parseStringToNumber(String input){
+
+ final String digitsOnly = input.replaceAll(DIGIT_AND_DECIMAL_REGEX, "");
+
+ if("".equals(digitsOnly)) return 0;
+
+ try{
+ return Double.parseDouble(digitsOnly);
+ }catch (NumberFormatException nfe){
+ return 0;
+ }
+ }
+
+}
diff --git a/java-strings/src/main/java/com/baeldung/string/sorting/AnagramValidator.java b/algorithms-sorting/src/main/java/com/baeldung/algorithms/stringsort/AnagramValidator.java
similarity index 93%
rename from java-strings/src/main/java/com/baeldung/string/sorting/AnagramValidator.java
rename to algorithms-sorting/src/main/java/com/baeldung/algorithms/stringsort/AnagramValidator.java
index c4f684383d..67b5e5facc 100644
--- a/java-strings/src/main/java/com/baeldung/string/sorting/AnagramValidator.java
+++ b/algorithms-sorting/src/main/java/com/baeldung/algorithms/stringsort/AnagramValidator.java
@@ -1,4 +1,4 @@
-package com.baeldung.string.sorting;
+package com.baeldung.algorithms.stringsort;
import java.util.Arrays;
diff --git a/algorithms-sorting/src/test/java/com/baeldung/algorithms/bucketsort/IntegerBucketSorterUnitTest.java b/algorithms-sorting/src/test/java/com/baeldung/algorithms/bucketsort/IntegerBucketSorterUnitTest.java
new file mode 100644
index 0000000000..2773d8a68f
--- /dev/null
+++ b/algorithms-sorting/src/test/java/com/baeldung/algorithms/bucketsort/IntegerBucketSorterUnitTest.java
@@ -0,0 +1,33 @@
+package com.baeldung.bucketsort;
+
+import com.baeldung.bucketsort.IntegerBucketSorter;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+
+public class IntegerBucketSorterUnitTest {
+
+ private IntegerBucketSorter sorter;
+
+ @Before
+ public void setUp() throws Exception {
+ sorter = new IntegerBucketSorter();
+ }
+
+ @Test
+ public void givenUnsortedList_whenSortedUsingBucketSorter_checkSortingCorrect() {
+
+ List unsorted = Arrays.asList(80,50,60,30,20,10,70,0,40,500,600,602,200,15);
+ List expected = Arrays.asList(0,10,15,20,30,40,50,60,70,80,200,500,600,602);
+
+ List actual = sorter.sort(unsorted);
+
+ assertEquals(expected, actual);
+
+
+ }
+}
\ No newline at end of file
diff --git a/algorithms-sorting/src/test/java/com/baeldung/algorithms/counting/CountingSortUnitTest.java b/algorithms-sorting/src/test/java/com/baeldung/algorithms/counting/CountingSortUnitTest.java
new file mode 100644
index 0000000000..ec6cf0784e
--- /dev/null
+++ b/algorithms-sorting/src/test/java/com/baeldung/algorithms/counting/CountingSortUnitTest.java
@@ -0,0 +1,32 @@
+package com.baeldung.algorithms.counting;
+
+import java.util.Arrays;
+
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+
+import org.junit.jupiter.api.Test;
+
+class CountingSortUnitTest {
+
+ @Test
+ void countElements_GivenAnArray_ShouldCalculateTheFrequencyArrayAsExpected() {
+ int k = 5;
+ int[] input = { 4, 3, 2, 5, 4, 3, 5, 1, 0, 2, 5 };
+
+ int[] c = CountingSort.countElements(input, k);
+ int[] expected = { 1, 2, 4, 6, 8, 11 };
+ assertArrayEquals(expected, c);
+ }
+
+ @Test
+ void sort_GivenAnArray_ShouldSortTheInputAsExpected() {
+ int k = 5;
+ int[] input = { 4, 3, 2, 5, 4, 3, 5, 1, 0, 2, 5 };
+
+ int[] sorted = CountingSort.sort(input, k);
+
+ // Our sorting algorithm and Java's should return the same result
+ Arrays.sort(input);
+ assertArrayEquals(input, sorted);
+ }
+}
\ No newline at end of file
diff --git a/algorithms-sorting/src/test/java/com/baeldung/algorithms/inoutsort/InOutSortUnitTest.java b/algorithms-sorting/src/test/java/com/baeldung/algorithms/inoutsort/InOutSortUnitTest.java
new file mode 100644
index 0000000000..321b905f68
--- /dev/null
+++ b/algorithms-sorting/src/test/java/com/baeldung/algorithms/inoutsort/InOutSortUnitTest.java
@@ -0,0 +1,23 @@
+package com.baeldung.algorithms.inoutsort;
+
+import static org.junit.Assert.*;
+import static org.junit.Assert.assertArrayEquals;
+
+import org.junit.Test;
+
+public class InOutSortUnitTest {
+
+ @Test
+ public void givenArray_whenInPlaceSort_thenReversed() {
+ int[] input = {1, 2, 3, 4, 5, 6, 7};
+ int[] expected = {7, 6, 5, 4, 3, 2, 1};
+ assertArrayEquals("the two arrays are not equal", expected, InOutSort.reverseInPlace(input));
+ }
+
+ @Test
+ public void givenArray_whenOutOfPlaceSort_thenReversed() {
+ int[] input = {1, 2, 3, 4, 5, 6, 7};
+ int[] expected = {7, 6, 5, 4, 3, 2, 1};
+ assertArrayEquals("the two arrays are not equal", expected, InOutSort.reverseOutOfPlace(input));
+ }
+}
diff --git a/algorithms-sorting/src/test/java/com/baeldung/algorithms/radixsort/RadixSortUnitTest.java b/algorithms-sorting/src/test/java/com/baeldung/algorithms/radixsort/RadixSortUnitTest.java
new file mode 100644
index 0000000000..0f6c751ade
--- /dev/null
+++ b/algorithms-sorting/src/test/java/com/baeldung/algorithms/radixsort/RadixSortUnitTest.java
@@ -0,0 +1,16 @@
+package com.baeldung.algorithms.radixsort;
+
+import static org.junit.Assert.assertArrayEquals;
+
+import org.junit.Test;
+
+public class RadixSortUnitTest {
+
+ @Test
+ public void givenUnsortedArray_whenRadixSort_thenArraySorted() {
+ int[] numbers = { 387, 468, 134, 123, 68, 221, 769, 37, 7 };
+ RadixSort.sort(numbers);
+ int[] numbersSorted = { 7, 37, 68, 123, 134, 221, 387, 468, 769 };
+ assertArrayEquals(numbersSorted, numbers);
+ }
+}
diff --git a/algorithms-sorting/src/test/java/com/baeldung/algorithms/selectionsort/SelectionSortUnitTest.java b/algorithms-sorting/src/test/java/com/baeldung/algorithms/selectionsort/SelectionSortUnitTest.java
new file mode 100644
index 0000000000..85efd1d3da
--- /dev/null
+++ b/algorithms-sorting/src/test/java/com/baeldung/algorithms/selectionsort/SelectionSortUnitTest.java
@@ -0,0 +1,25 @@
+package com.baeldung.algorithms.selectionsort;
+
+import static org.junit.Assert.*;
+import static org.junit.Assert.assertArrayEquals;
+
+import org.junit.Test;
+
+public class SelectionSortUnitTest {
+
+ @Test
+ public void givenUnsortedArray_whenSelectionSort_SortAscending_thenSortedAsc() {
+ int[] input = { 5, 4, 1, 6, 2 };
+ SelectionSort.sortAscending(input);
+ int[] expected = {1, 2, 4, 5, 6};
+ assertArrayEquals("the two arrays are not equal", expected, input);
+ }
+
+ @Test
+ public void givenUnsortedArray_whenSelectionSort_SortDescending_thenSortedDesc() {
+ int[] input = { 5, 4, 1, 6, 2 };
+ SelectionSort.sortDescending(input);
+ int[] expected = {6, 5, 4, 2, 1};
+ assertArrayEquals("the two arrays are not equal", expected, input);
+ }
+}
diff --git a/algorithms-sorting/src/test/java/com/baeldung/algorithms/shellsort/ShellSortUnitTest.java b/algorithms-sorting/src/test/java/com/baeldung/algorithms/shellsort/ShellSortUnitTest.java
new file mode 100644
index 0000000000..91a27c41d0
--- /dev/null
+++ b/algorithms-sorting/src/test/java/com/baeldung/algorithms/shellsort/ShellSortUnitTest.java
@@ -0,0 +1,17 @@
+package com.baeldung.algorithms.shellsort;
+
+import static org.junit.Assert.*;
+import static org.junit.Assert.assertArrayEquals;
+
+import org.junit.Test;
+
+public class ShellSortUnitTest {
+
+ @Test
+ public void givenUnsortedArray_whenShellSort_thenSortedAsc() {
+ int[] input = {41, 15, 82, 5, 65, 19, 32, 43, 8};
+ ShellSort.sort(input);
+ int[] expected = {5, 8, 15, 19, 32, 41, 43, 65, 82};
+ assertArrayEquals("the two arrays are not equal", expected, input);
+ }
+}
diff --git a/algorithms-sorting/src/test/java/com/baeldung/algorithms/sort/bynumber/NaturalOrderComparatorsUnitTest.java b/algorithms-sorting/src/test/java/com/baeldung/algorithms/sort/bynumber/NaturalOrderComparatorsUnitTest.java
new file mode 100644
index 0000000000..2f05f62147
--- /dev/null
+++ b/algorithms-sorting/src/test/java/com/baeldung/algorithms/sort/bynumber/NaturalOrderComparatorsUnitTest.java
@@ -0,0 +1,79 @@
+package com.baeldung.algorithms.sort.bynumber;
+
+import com.baeldung.algorithms.sort.bynumber.NaturalOrderComparators;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import static org.junit.Assert.*;
+
+public class NaturalOrderComparatorsUnitTest {
+
+ @Test
+ public void givenSimpleStringsContainingIntsAndDoubles_whenSortedByRegex_checkSortingCorrect() {
+
+ List testStrings = Arrays.asList("a1", "b3", "c4", "d2.2", "d2.4", "d2.3d");
+
+ testStrings.sort(NaturalOrderComparators.createNaturalOrderRegexComparator());
+
+ List expected = Arrays.asList("a1", "d2.2", "d2.3d", "d2.4", "b3", "c4");
+
+ assertEquals(expected, testStrings);
+
+
+ }
+
+ @Test
+ public void givenSimpleStringsContainingIntsAndDoublesWithAnInvalidNumber_whenSortedByRegex_checkSortingCorrect() {
+
+ List testStrings = Arrays.asList("a1", "b3", "c4", "d2.2", "d2.4", "d2.3.3d");
+
+ testStrings.sort(NaturalOrderComparators.createNaturalOrderRegexComparator());
+
+ List expected = Arrays.asList("d2.3.3d", "a1", "d2.2", "d2.4", "b3", "c4");
+
+ assertEquals(expected, testStrings);
+
+
+ }
+
+ @Test
+ public void givenAllForseenProblems_whenSortedByRegex_checkSortingCorrect() {
+
+ List testStrings = Arrays.asList("a1", "b3", "c4", "d2.2", "d2.f4", "d2.3.3d");
+
+ testStrings.sort(NaturalOrderComparators.createNaturalOrderRegexComparator());
+
+ List expected = Arrays.asList("d2.3.3d", "a1", "d2.2", "d2.f4", "b3", "c4");
+
+ assertEquals(expected, testStrings);
+
+
+ }
+
+ @Test
+ public void givenComplexStringsContainingSeparatedNumbers_whenSortedByRegex_checkNumbersCondensedAndSorted() {
+
+ List testStrings = Arrays.asList("a1b2c5", "b3ght3.2", "something65.thensomething5"); //125, 33.2, 65.5
+
+ List expected = Arrays.asList("b3ght3.2", "something65.thensomething5", "a1b2c5" );
+
+ testStrings.sort(NaturalOrderComparators.createNaturalOrderRegexComparator());
+
+ assertEquals(expected, testStrings);
+
+ }
+
+ @Test
+ public void givenStringsNotContainingNumbers_whenSortedByRegex_checkOrderNotChanged() {
+
+ List testStrings = Arrays.asList("a", "c", "d", "e");
+ List expected = new ArrayList<>(testStrings);
+
+ testStrings.sort(NaturalOrderComparators.createNaturalOrderRegexComparator());
+
+ assertEquals(expected, testStrings);
+ }
+}
\ No newline at end of file
diff --git a/java-strings/src/test/java/com/baeldung/string/sorting/AnagramValidatorUnitTest.java b/algorithms-sorting/src/test/java/com/baeldung/algorithms/stringsort/AnagramValidatorUnitTest.java
similarity index 86%
rename from java-strings/src/test/java/com/baeldung/string/sorting/AnagramValidatorUnitTest.java
rename to algorithms-sorting/src/test/java/com/baeldung/algorithms/stringsort/AnagramValidatorUnitTest.java
index 07d31c7187..25fc274dd8 100644
--- a/java-strings/src/test/java/com/baeldung/string/sorting/AnagramValidatorUnitTest.java
+++ b/algorithms-sorting/src/test/java/com/baeldung/algorithms/stringsort/AnagramValidatorUnitTest.java
@@ -1,12 +1,10 @@
-package com.baeldung.string.sorting;
+package com.baeldung.algorithms.stringsort;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-import static org.junit.jupiter.api.Assertions.assertFalse;
-
import org.junit.jupiter.api.Test;
-import com.baeldung.string.sorting.AnagramValidator;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
class AnagramValidatorUnitTest {
diff --git a/java-strings/src/test/java/com/baeldung/string/sorting/SortStringUnitTest.java b/algorithms-sorting/src/test/java/com/baeldung/algorithms/stringsort/SortStringUnitTest.java
similarity index 94%
rename from java-strings/src/test/java/com/baeldung/string/sorting/SortStringUnitTest.java
rename to algorithms-sorting/src/test/java/com/baeldung/algorithms/stringsort/SortStringUnitTest.java
index 90d1dad554..226de64f07 100644
--- a/java-strings/src/test/java/com/baeldung/string/sorting/SortStringUnitTest.java
+++ b/algorithms-sorting/src/test/java/com/baeldung/algorithms/stringsort/SortStringUnitTest.java
@@ -1,9 +1,9 @@
-package com.baeldung.string.sorting;
-
-import java.util.Arrays;
+package com.baeldung.algorithms.stringsort;
import org.junit.jupiter.api.Test;
+import java.util.Arrays;
+
import static org.assertj.core.api.Assertions.assertThat;
class SortStringUnitTest {
diff --git a/animal-sniffer-mvn-plugin/README.md b/animal-sniffer-mvn-plugin/README.md
index 4c7c381da4..52e319b399 100644
--- a/animal-sniffer-mvn-plugin/README.md
+++ b/animal-sniffer-mvn-plugin/README.md
@@ -1,3 +1,7 @@
-## Relevant articles:
+## Animal Sniffer Maven Plugin
-[Introduction to Animal Sniffer Maven Plugin](http://www.baeldung.com/maven-animal-sniffer)
+This module contains articles about the Animal Sniffer Maven Plugin
+
+### Relevant articles:
+
+[Introduction to Animal Sniffer Maven Plugin](https://www.baeldung.com/maven-animal-sniffer)
diff --git a/animal-sniffer-mvn-plugin/pom.xml b/animal-sniffer-mvn-plugin/pom.xml
index 55e37e2ec4..fd8f6d3d8c 100644
--- a/animal-sniffer-mvn-plugin/pom.xml
+++ b/animal-sniffer-mvn-plugin/pom.xml
@@ -1,7 +1,6 @@
4.0.0
- com.baeldung
animal-sniffer-mvn-plugin
1.0-SNAPSHOT
animal-sniffer-mvn-plugin
diff --git a/annotations/README.md b/annotations/README.md
new file mode 100644
index 0000000000..ec4005fc5e
--- /dev/null
+++ b/annotations/README.md
@@ -0,0 +1,7 @@
+## Annotations
+
+This module contains articles about Java annotations
+
+### Relevant Articles:
+
+- [Java Annotation Processing and Creating a Builder](https://www.baeldung.com/java-annotation-processing-builder)
diff --git a/annotations/readme.md b/annotations/readme.md
deleted file mode 100644
index 2b052803e6..0000000000
--- a/annotations/readme.md
+++ /dev/null
@@ -1,2 +0,0 @@
-### Relevant Articles:
-- [Java Annotation Processing and Creating a Builder](http://www.baeldung.com/java-annotation-processing-builder)
diff --git a/antlr/README.md b/antlr/README.md
index 419d9ddfbb..1f394125c6 100644
--- a/antlr/README.md
+++ b/antlr/README.md
@@ -1,3 +1,7 @@
+## ANTLR
+
+This module contains articles about ANTLR
+
### Relevant Articles:
-- [Java with ANTLR](http://www.baeldung.com/java-antlr)
+- [Java with ANTLR](https://www.baeldung.com/java-antlr)
diff --git a/apache-avro/README.md b/apache-avro/README.md
index 32d84ecc76..b338e8e565 100644
--- a/apache-avro/README.md
+++ b/apache-avro/README.md
@@ -1,2 +1,6 @@
+## Apache Avro
+
+This module contains articles about Apache Avro
+
### Relevant Articles:
-- [Guide to Apache Avro](http://www.baeldung.com/java-apache-avro)
+- [Guide to Apache Avro](https://www.baeldung.com/java-apache-avro)
diff --git a/apache-avro/pom.xml b/apache-avro/pom.xml
index 5d170f0a81..6baae9c541 100644
--- a/apache-avro/pom.xml
+++ b/apache-avro/pom.xml
@@ -14,12 +14,6 @@
-
- junit
- junit
- 4.10
- test
-
org.slf4j
slf4j-simple
@@ -46,15 +40,6 @@
-
- org.apache.maven.plugins
- maven-compiler-plugin
- ${compiler-plugin.version}
-
- ${java.version}
- ${java.version}
-
-
org.apache.avro
avro-maven-plugin
@@ -79,8 +64,6 @@
- UTF-8
- 3.5
1.8.2
1.7.25
diff --git a/apache-bval/README.md b/apache-bval/README.md
index 80ea149993..e856810378 100644
--- a/apache-bval/README.md
+++ b/apache-bval/README.md
@@ -1,2 +1,7 @@
+## Apache BVal
+
+This module contains articles about Apache BVal
+
### Relevant Articles:
-- [Intro to Apache BVal](http://www.baeldung.com/apache-bval)
+
+- [Intro to Apache BVal](https://www.baeldung.com/apache-bval)
diff --git a/apache-curator/README.md b/apache-curator/README.md
index 9bda573292..4fef6a138e 100644
--- a/apache-curator/README.md
+++ b/apache-curator/README.md
@@ -1,3 +1,7 @@
+## Apache Curator
+
+This module contains articles about Apache Curator
+
### Relevant Articles:
-- [Introduction to Apache Curator](http://www.baeldung.com/apache-curator)
+- [Introduction to Apache Curator](https://www.baeldung.com/apache-curator)
diff --git a/apache-cxf/README.md b/apache-cxf/README.md
index d03999dce3..f825b85bb3 100644
--- a/apache-cxf/README.md
+++ b/apache-cxf/README.md
@@ -1,6 +1,10 @@
+## Apache CXF
+
+This module contains articles about Apache CXF
+
## Relevant Articles:
-- [Introduction to Apache CXF Aegis Data Binding](http://www.baeldung.com/aegis-data-binding-in-apache-cxf)
-- [Apache CXF Support for RESTful Web Services](http://www.baeldung.com/apache-cxf-rest-api)
-- [A Guide to Apache CXF with Spring](http://www.baeldung.com/apache-cxf-with-spring)
-- [Introduction to Apache CXF](http://www.baeldung.com/introduction-to-apache-cxf)
+- [Introduction to Apache CXF Aegis Data Binding](https://www.baeldung.com/aegis-data-binding-in-apache-cxf)
+- [Apache CXF Support for RESTful Web Services](https://www.baeldung.com/apache-cxf-rest-api)
+- [A Guide to Apache CXF with Spring](https://www.baeldung.com/apache-cxf-with-spring)
+- [Introduction to Apache CXF](https://www.baeldung.com/introduction-to-apache-cxf)
- [Server-Sent Events (SSE) In JAX-RS](https://www.baeldung.com/java-ee-jax-rs-sse)
diff --git a/apache-fop/README.md b/apache-fop/README.md
index 1e734a5f36..2adc593026 100644
--- a/apache-fop/README.md
+++ b/apache-fop/README.md
@@ -1,5 +1,5 @@
-=========
+## Apache FOP
-## Core Java Cookbooks and Examples
+This module contains articles about Apache FOP
### Relevant Articles:
diff --git a/apache-fop/pom.xml b/apache-fop/pom.xml
index 879eb4f111..150756d51d 100644
--- a/apache-fop/pom.xml
+++ b/apache-fop/pom.xml
@@ -1,7 +1,6 @@
4.0.0
- com.baeldung
apache-fop
0.1-SNAPSHOT
apache-fop
diff --git a/apache-fop/src/test/resources/input.xml b/apache-fop/src/test/resources/input.xml
deleted file mode 100644
index a8266f3f5c..0000000000
--- a/apache-fop/src/test/resources/input.xml
+++ /dev/null
@@ -1,1674 +0,0 @@
-
-
-
- Bootstrap a Web Application with Spring 4
-
-
-
-
-
- Return to Content
-
-
-
-
-
- Contents
-
-
- Table of Contents
-
-
- 1. Overview
-
-
- 2. The Maven pom.xml
-
-
- 3. The Java based Web Configuration
-
-
- 4. Conclusion
-
-
- If you're new here, you may want to get my "REST APIs with Spring" eBook. Thanks for visiting!
-
-
-
-
- Table of Contents
-
-
- 1. Overview
-
-
- 2. The Maven pom.xml
-
-
- Â Â Â 2.1. Justification of the cglib dependency
-
-
- Â Â Â 2.2. The cglib dependency in Spring 3.2 and beyond
-
-
- 3. The Java based web configuration
-
-
- Â Â 3.1. The web.xml
-
-
- 4. Conclusion
-
-
-
- 1. Overview
- The tutorial illustrates how to Bootstrap a Web Application with Spring and also discusses how to make the jump from XML to Java without having to completely migrate the entire XML configuration.
-
-
- 2. The Maven pom.xml
- <project xmlns="http://maven.apache.org/POM/4.0.0"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="
- http://maven.apache.org/POM/4.0.0
- http://maven.apache.org/xsd/maven-4.0.0.xsd">
- <modelVersion>4.0.0</modelVersion>
- <groupId>org</groupId>
- <artifactId>rest</artifactId>
- <version>0.0.1-SNAPSHOT</version>
- <packaging>war</packaging>
-
- <dependencies>
-
- <dependency>
- <groupId>org.springframework</groupId>
- <artifactId>spring-webmvc</artifactId>
- <version>${spring.version}</version>
- <exclusions>
- <exclusion>
- <artifactId>commons-logging</artifactId>
- <groupId>commons-logging</groupId>
- </exclusion>
- </exclusions>
- </dependency>
-
- </dependencies>
-
- <build>
- <finalName>rest</finalName>
-
- <plugins>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-compiler-plugin</artifactId>
- <version>3.1</version>
- <configuration>
- <source>1.6</source>
- <target>1.6</target>
- <encoding>UTF-8</encoding>
- </configuration>
- </plugin>
- </plugins>
- </build>
-
- <properties>
- <spring.version>4.0.5.RELEASE</spring.version>
- </properties>
-
-</project>
-
- 2.1. The cglib dependency before Spring 3.2
- You may wonder why cglib is a dependency – it turns out there is a valid reason to include it – the entire configuration cannot function without it. If removed, Spring will throw:
- Caused by: java.lang.IllegalStateException: CGLIB is required to process @Configuration classes. Either add CGLIB to the classpath or remove the following @Configuration bean definitions
- The reason this happens is explained by the way Spring deals with @Configuration classes. These classes are effectively beans, and because of this they need to be aware of the Context, and respect scope and other bean semantics. This is achieved by dynamically creating a cglib proxy with this awareness for each @Configuration class, hence the cglib dependency.
- Also, because of this, there are a few restrictions for Configuration annotated classes:
-
-
- Configuration classes should not be final
-
-
- They should have a constructor with no arguments
-
-
-
-
- 2.2. The cglib dependency in Spring 3.2 and beyond
- Starting with Spring 3.2, it is no longer necessary to add cglib as an explicit dependency . This is because Spring is in now inlining cglib – which will ensure that all class based proxying functionality will work out of the box with Spring 3.2.
- The new cglib code is placed under the Spring package: org.springframework.cglib (replacing the original net.sf.cglib ). The reason for the package change is to avoid conflicts with any cglib versions already existing on the classpath.
- Also, the new cglib 3.0 is now used, upgraded from the older 2.2 dependency (see this JIRA issue for more details).
- Finally, now that Spring 4.0 is out in the wild, changes like this one (removing the cglib dependency) are to be expected with Java 8 just around the corner – you can watch this Spring Jira to keep track of the Spring support, and the Java 8 Resources page to keep tabs on the that.
-
-
-
- 3. The Java based Web Configuration
- @Configuration
-@ImportResource( { "classpath*:/rest_config.xml" } )
-@ComponentScan( basePackages = "org.rest" )
-@PropertySource({ "classpath:rest.properties", "classpath:web.properties" })
-public class AppConfig{
-
- @Bean
-Â Â public static PropertySourcesPlaceholderConfigurer properties() {
-Â Â return new PropertySourcesPlaceholderConfigurer();
-Â Â }
-}
- First, the @Configuration annotation – this is the main artifact used by the Java based Spring configuration; it is itself meta-annotated with @Component , which makes the annotated classes standard beans and as such, also candidates for component scanning. The main purpose of @Configuration classes is to be sources of bean definitions for the Spring IoC Container. For a more detailed description, see the official docs.
- Then, @ImportResource is used to import the existing XML based Spring configuration. This may be configuration which is still being migrated from XML to Java, or simply legacy configuration that you wish to keep. Either way, importing it into the Container is essential for a successful migration, allowing small steps without to much risk. The equivalent XML annotation that is replaced is:
- <import resource=”classpath*:/rest_config.xml” />
- Moving on to @ComponentScan – this configures the component scanning directive, effectively replacing the XML:
- <context:component-scan base-package="org.rest" />
- As of Spring 3.1, the @Configuration are excluded from classpath scanning by default – see this JIRA issue. Before Spring 3.1 though, these classes should have been excluded explicitly:
- excludeFilters = { @ComponentScan.Filter( Configuration.class ) }
- The @Configuration classes should not be autodiscovered because they are already specified and used by the Container – allowing them to be rediscovered and introduced into the Spring context will result in the following error:
- Caused by: org.springframework.context.annotation.ConflictingBeanDefinitionException: Annotation-specified bean name ‘webConfig’ for bean class [org.rest.spring.AppConfig] conflicts with existing, non-compatible bean definition of same name and class [org.rest.spring.AppConfig]
- And finally, using the @Bean annotation to configure the properties support –Â PropertySourcesPlaceholderConfigurer is initialized in a @Bean annotated method, indicating it will produce a Spring bean managed by the Container. This new configuration has replaced the following XML:
- <context:property-placeholder
-location="classpath:persistence.properties, classpath:web.properties"
-ignore-unresolvable="true"/>
- For a more in depth discussion on why it was necessary to manually register the PropertySourcesPlaceholderConfigurer bean, see the Properties with Spring Tutorial.
-
- 3.1. The web.xml
- <?xml version="1.0" encoding="UTF-8"?>
-<web-app xmlns="
- http://java.sun.com/xml/ns/javaee"
-Â Â Â xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
-Â Â Â xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-Â Â Â xsi:schemaLocation="
- http://java.sun.com/xml/ns/javaee
- http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
-Â Â Â id="rest" version="3.0">
-
- <context-param>
- <param-name>contextClass</param-name>
- <param-value>
- org.springframework.web.context.support.AnnotationConfigWebApplicationContext
- </param-value>
- </context-param>
- <context-param>
- <param-name>contextConfigLocation</param-name>
- <param-value>org.rest.spring.root</param-value>
- </context-param>
- <listener>
- <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
- </listener>
-
- <servlet>
- <servlet-name>rest</servlet-name>
- <servlet-class>
- org.springframework.web.servlet.DispatcherServlet
- </servlet-class>
- <init-param>
- <param-name>contextClass</param-name>
- <param-value>
- org.springframework.web.context.support.AnnotationConfigWebApplicationContext
- </param-value>
- </init-param>
- <init-param>
- <param-name>contextConfigLocation</param-name>
- <param-value>org.rest.spring.rest</param-value>
- </init-param>
- <load-on-startup>1</load-on-startup>
- </servlet>
- <servlet-mapping>
- <servlet-name>rest</servlet-name>
- <url-pattern>/api/*</url-pattern>
- </servlet-mapping>
-
- <welcome-file-list>
- <welcome-file />
- </welcome-file-list>
-
-</web-app>
- First, the root context is defined and configured to use AnnotationConfigWebApplicationContext instead of the default XmlWebApplicationContext . The newer AnnotationConfigWebApplicationContext accepts @Configuration annotated classes as input for the Container configuration and is needed in order to set up the Java based context. Unlike XmlWebApplicationContext , it assumes no default configuration class locations, so the “contextConfigLocation” init-param for the Servlet must be set. This will point to the java package where the @Configuration classes are located; the fully qualified name(s) of the classes are also supported.
- Next, the DispatcherServlet is configured to use the same kind of context, with the only difference that it’s loading configuration classes out of a different package.
- Other than this, the web.xml doesn’t really change from a XML to a Java based configuration.
-
-
-
- 4. Conclusion
- The presented approach allows for a smooth migration of the Spring configuration from XML to Java, mixing the old and the new. This is important for older projects, which may have a lot of XML based configuration that cannot be migrated all at once.
- This way, in a migration, the XML beans can be ported in small increments.
- In the next article on REST with Spring, I cover setting up MVC in the project, configuration of the HTTP status codes, payload marshalling and content negotiation.
- The implementation of this Bootstrap a Spring Web App Tutorial can be downloaded as a working sample project.
- This is an Eclipse based project, so it should be easy to import and run as it is.
-
- Â
-
-
-
-
-
-
-
-
- java, Spring
-
-
-
-
-
-
-
-
-
-
- © 2014 Baeldung. All Rights Reserved.
-
-
-
-
-
-
-
-
-
-
-
-
-
- Build a REST API with Spring 4 and Java Config
-
-
-
-
-
- Return to Content
-
-
-
-
-
- Contents
-
-
- Table of Contents
-
-
- 1. Overview
-
-
- 2. Understanding REST in Spring
-
-
- 3. The Java configuration
-
-
- 4. Testing the Spring context
-
-
- 5. The Controller
-
-
- 6. Mapping the HTTP response codes
-
-
- 7. Additional Maven dependencies
-
-
- 8. Conclusion
-
-
- If you're new here, you may want to get my "REST APIs with Spring" eBook. Thanks for visiting!
-
-
- Â
-
-
- Table of Contents
-
-
- 1. Overview
-
-
- 2. Understanding REST in Spring
-
-
- 3. The Java configuration
-
-
- 4. Testing the Spring context
-
-
- 5. The Controller
-
-
- 6. Mapping the HTTP response codes
-
-
- Â Â Â 6.1. Unmapped requests
-
-
- Â Â Â 6.2. Valid, mapped requests
-
-
- Â Â Â 6.3. Client error
-
-
- Â Â Â 6.4. Using @ExceptionHandler
-
-
- 7. Additional Maven dependencies
-
-
- 8. Conclusion
-
-
-
- 1. Overview
- This article shows how to set up REST in Spring – the Controller and HTTP response codes, configuration of payload marshalling and content negotiation.
-
-
- 2. Understanding REST in Spring
- The Spring framework supports 2 ways of creating RESTful services:
-
-
- using MVC with ModelAndView
-
-
- using HTTP message converters
-
-
- The ModelAndView approach is older and much better documented, but also more verbose and configuration heavy. It tries to shoehorn the REST paradigm into the old model, which is not without problems. The Spring team understood this and provided first-class REST support starting with Spring 3.0 .
- The new approach, based on HttpMessageConverter and annotations , is much more lightweight and easy to implement. Configuration is minimal and it provides sensible defaults for what you would expect from a RESTful service. It is however newer and a a bit on the light side concerning documentation; what’s , the reference doesn’t go out of it’s way to make the distinction and the tradeoffs between the two approaches as clear as they should be. Nevertheless, this is the way RESTful services should be build after Spring 3.0.
-
-
- 3. The Java configuration
-
- @Configuration
-@EnableWebMvc
-public class WebConfig{
- //
-}
- The new @EnableWebMvc annotation does a number of useful things – specifically, in the case of REST, it detect the existence of Jackson and JAXB 2 on the classpath and automatically creates and registers default JSON and XML converters . The functionality of the annotation is equivalent to the XML version:
- <mvc:annotation-driven />
- This is a shortcut, and though it may be useful in many situations, it’s not perfect. When more complex configuration is needed, remove the annotation and extend WebMvcConfigurationSupport directly.
-
-
- 4. Testing the Spring context
- Starting with Spring 3.1 , we get first-class testing support for @Configuration classes:
- @RunWith( SpringJUnit4ClassRunner.class )
-@ContextConfiguration( classes = { ApplicationConfig.class, PersistenceConfig.class },
- loader = AnnotationConfigContextLoader.class )
-public class SpringTest{
-
- @Test
- public void whenSpringContextIsInstantiated_thenNoExceptions(){
- // When
- }
-}
- The Java configuration classes are simply specified with the @ContextConfiguration annotation and the new AnnotationConfigContextLoader loads the bean definitions from the @Configuration classes.
- Notice that the WebConfig configuration class was not included in the test because it needs to run in a Servlet context, which is not provided.
-
-
- 5. The Controller
- The @Controller is the central artifact in the entire Web Tier of the RESTful API. For the purpose of this post, the controller is modeling a simple REST resource – Foo :
- @Controller
-@RequestMapping( value = "/foos" )
-class FooController{
-
- @Autowired
- IFooService service;
-
- @RequestMapping( method = RequestMethod.GET )
- @ResponseBody
- public List< Foo > findAll(){
- return service.findAll();
- }
-
- @RequestMapping( value = "/{id}", method = RequestMethod.GET )
- @ResponseBody
- public Foo findOne( @PathVariable( "id" ) Long id ){
- return RestPreconditions.checkFound( service.findOne( id ) );
- }
-
- @RequestMapping( method = RequestMethod.POST )
- @ResponseStatus( HttpStatus.CREATED )
- @ResponseBody
- public Long create( @RequestBody Foo resource ){
- Preconditions.checkNotNull( resource );
- return service.create( resource );
- }
-
- @RequestMapping( value = "/{id}", method = RequestMethod.PUT )
- @ResponseStatus( HttpStatus.OK )
- public void update( @PathVariable( "id" ) Long id, @RequestBody Foo resource ){
- Preconditions.checkNotNull( resource );
- RestPreconditions.checkNotNull( service.getById( resource.getId() ) );
- service.update( resource );
- }
-
- @RequestMapping( value = "/{id}", method = RequestMethod.DELETE )
- @ResponseStatus( HttpStatus.OK )
- public void delete( @PathVariable( "id" ) Long id ){
- service.deleteById( id );
- }
-
-}
- You may have noticed I’m using a very simple, guava style RestPreconditions utility:
- public class RestPreconditions {
- public static <T> T checkFound(final T resource) {
- if (resource == null) {
- throw new MyResourceNotFoundException();
- }
- return resource;
- }
-}
- The Controller implementation is non-public – this is because it doesn’t need to be. Usually the controller is the last in the chain of dependencies – it receives HTTP requests from the Spring front controller (the DispathcerServlet ) and simply delegate them forward to a service layer. If there is no use case where the controller has to be injected or manipulated through a direct reference, then I prefer not to declare it as public.
- The request mappings are straightforward – as with any controller, the actual value of the mapping as well as the HTTP method are used to determine the target method for the request. @ RequestBody will bind the parameters of the method to the body of the HTTP request, whereas @ResponseBody does the same for the response and return type. They also ensure that the resource will be marshalled and unmarshalled using the correct HTTP converter. Content negotiation will take place to choose which one of the active converters will be used, based mostly on the Accept header, although other HTTP headers may be used to determine the representation as well.
-
-
- 6. Mapping the HTTP response codes
- The status codes of the HTTP response are one of the most important parts of the REST service, and the subject can quickly become very complex. Getting these right can be what makes or breaks the service.
-
- 6.1. Unmapped requests
- If Spring MVC receives a request which doesn’t have a mapping, it considers the request not to be allowed and returns a 405 METHOD NOT ALLOWED back to the client. It is also good practice to include the Allow HTTP header when returning a 405 to the client, in order to specify which operations are allowed. This is the standard behavior of Spring MVC and does not require any additional configuration.
-
-
- 6.2. Valid, mapped requests
- For any request that does have a mapping, Spring MVC considers the request valid and responds with 200 OK if no other status code is specified otherwise. It is because of this that controller declares different @ResponseStatus for the create , update and delete actions but not for get , which should indeed return the default 200 OK.
-
-
- 6.3. Client error
- In case of a client error , custom exceptions are defined and mapped to the appropriate error codes. Simply throwing these exceptions from any of the layers of the web tier will ensure Spring maps the corresponding status code on the HTTP response.
- @ResponseStatus( value = HttpStatus.BAD_REQUEST )
-public class BadRequestException extends RuntimeException{
- //
-}
-@ResponseStatus( value = HttpStatus.NOT_FOUND )
-public class ResourceNotFoundException extends RuntimeException{
- //
-}
- These exceptions are part of the REST API and, as such, should only be used in the appropriate layers corresponding to REST; if for instance a DAO/DAL layer exist, it should not use the exceptions directly. Note also that these are not checked exceptions but runtime exceptions – in line with Spring practices and idioms.
-
-
- 6.4. Using @ExceptionHandler
- Another option to map custom exceptions on specific status codes is to use the @ExceptionHandler annotation in the controller. The problem with that approach is that the annotation only applies to the controller in which it is defined, not to the entire Spring Container, which means that it needs to be declared in each controller individually. This quickly becomes cumbersome, especially in more complex applications which many controllers. There are a few JIRA issues opened with Spring at this time to handle this and other related limitations: SPR-8124, SPR-7278, SPR-8406.
-
-
-
- 7. Additional Maven dependencies
- In addition to the spring-webmvc dependency required for the standard web application, we’ll need to set up content marshalling and unmarshalling for the REST API:
- <dependencies>
- <dependency>
-Â Â <groupId>com.fasterxml.jackson.core</groupId>
- Â Â <artifactId>jackson-databind</artifactId>
-Â Â <version>${jackson.version}</version>
- </dependency>
- <dependency>
- <groupId>javax.xml.bind</groupId>
- <artifactId>jaxb-api</artifactId>
- <version>${jaxb-api.version}</version>
- <scope>runtime</scope>
- </dependency>
-</dependencies>
-
-<properties>
- <jackson.version>2.4.0</jackson.version>
- <jaxb-api.version>2.2.11</jaxb-api.version>
-</properties>
- These are the libraries used to convert the representation of the REST resource to either JSON or XML .
-
-
- 8. Conclusion
- This tutorial illustrated how to implement and configure a REST Service using Spring 4 and Java based configuration, discussing HTTP response codes, basic Content Negotiation and marshaling.
- In the next articles of the series I will focus on Discoverability of the API, advanced content negotiation and working with additional representations of a Resource.
- The implementation of this Spring REST API Tutorial can be downloaded as a working sample project.
- This is an Eclipse based project, so it should be easy to import and run as it is.
-
-
-
-
-
-
-
-
-
- java, REST, Spring, testing
-
-
-
-
-
-
-
-
-
-
- © 2014 Baeldung. All Rights Reserved.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Spring Security for a REST API
-
-
-
-
-
- Return to Content
-
-
-
-
-
- Contents
-
-
- Table of Contents
-
-
- 1. Overview
-
-
- 2. Spring Security in the web.xml
-
-
- 3. The Security Configuration
-
-
- 4. Maven and other trouble
-
-
- 5. Conclusion
-
-
-
-
-
-
- Table of Contents
-
-
- 1. Overview
-
-
- 2. Introducing Spring Security in the web.xml
-
-
- 3. The Security Configuration
-
-
- Â Â Â Â 3.1. The basics
-
-
- Â Â Â Â 3.2. The Entry Point
-
-
- Â Â Â Â 3.3. The Login
-
-
- Â Â Â 3.4. Authentication should return 200 instead of 301
-
-
- Â Â Â 3.5. Failed Authentication should return 401 instead of 302
-
-
- Â Â Â Â 3.6. The Authentication Manager and Provider
-
-
-    3.7. Finally – Authentication against the running REST Service
-
-
- 4. Maven and other trouble
-
-
- 5. Conclusion
-
-
-
- 1. Overview
- This tutorial shows how to Secure a REST Service using Spring and Spring Security 3.1 with Java based configuration. The article will focus on how to set up the Security Configuration specifically for the REST API using a Login and Cookie approach.
-
-
- 2. Spring Security in the web.xml
- The architecture of Spring Security is based entirely on Servlet Filters and, as such, comes before Spring MVC in regards to the processing of HTTP requests. Keeping this in mind, to begin with, a filter needs to be declared in the web.xml of the application:
- <filter>
- <filter-name>springSecurityFilterChain</filter-name>
- <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
-</filter>
-<filter-mapping>
- <filter-name>springSecurityFilterChain</filter-name>
- <url-pattern>/*</url-pattern>
-</filter-mapping>
- The filter must necessarily be named ‘springSecurityFilterChain’ Â to match the default bean created by Spring Security in the container.
- Note that the defined filter is not the actual class implementing the security logic but a DelegatingFilterProxy with the purpose of delegating the Filter’s methods to an internal bean. This is done so that the target bean can still benefit from the Spring context lifecycle and flexibility.
- The URL pattern used to configure the Filter is /* even though the entire web service is mapped to /api/* so that the security configuration has the option to secure other possible mappings as well, if required.
-
-
- 3. The Security Configuration
- <?xml version="1.0" encoding="UTF-8"?>
-<beans:beans
- xmlns="http://www.springframework.org/schema/security"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xmlns:beans="http://www.springframework.org/schema/beans"
- xmlns:sec="http://www.springframework.org/schema/security"
- xsi:schemaLocation="
- http://www.springframework.org/schema/security
- http://www.springframework.org/schema/security/spring-security-3.2.xsd
- http://www.springframework.org/schema/beans
- http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">
-
- <http entry-point-ref="restAuthenticationEntryPoint">
- <intercept-url pattern="/api/admin/**" access="ROLE_ADMIN"/>
-
- <form-login
- authentication-success-handler-ref="mySuccessHandler"
- authentication-failure-handler-ref="myFailureHandler"
- />
-
- <logout />
- </http>
-
- <beans:bean id="mySuccessHandler"
- class="org.rest.security.MySavedRequestAwareAuthenticationSuccessHandler"/>
- <beans:bean id="myFailureHandler"
- class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler"/>
-
- <authentication-manager alias="authenticationManager">
- <authentication-provider>
- <user-service>
- <user name="temporary" password="temporary" authorities="ROLE_ADMIN"/>
- <user name="user" password="user" authorities="ROLE_USER"/>
- </user-service>
- </authentication-provider>
- </authentication-manager>
-
-</beans:beans>
- Most of the configuration is done using the security namespace – for this to be enabled, the schema locations must be defined and pointed to the correct 3.1 or 3.2 XSD versions. The namespace is designed so that it expresses the common uses of Spring Security while still providing hooks raw beans to accommodate more advanced scenarios. >> Signup for my upcoming Video Course on Building a REST API with Spring 4
-
- 3.1. The <http> element
- The <http> element is the main container element for HTTP security configuration. In the current implementation, it only secured a single mapping: /api/admin/** . Note that the mapping is relative to the root context of the web application, not to the rest Servlet; this is because the entire security configuration lives in the root Spring context and not in the child context of the Servlet.
-
-
- 3.2. The Entry Point
- In a standard web application, the authentication process may be automatically triggered when the client tries to access a secured resource without being authenticated – this is usually done by redirecting to a login page so that the user can enter credentials. However, for a REST Web Service this behavior doesn’t make much sense – Authentication should only be done by a request to the correct URI and all other requests should simply fail with a 401 UNAUTHORIZED status code if the user is not authenticated.
- Spring Security handles this automatic triggering of the authentication process with the concept of an Entry Point – this is a required part of the configuration, and can be injected via the entry-point-ref attribute of the <http> element. Keeping in mind that this functionality doesn’t make sense in the context of the REST Service, the new custom entry point is defined to simply return 401 whenever it is triggered:
- @Component( "restAuthenticationEntryPoint" )
-public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint{
-
- @Override
- public void commence( HttpServletRequest request, HttpServletResponse response,
- AuthenticationException authException ) throws IOException{
- response.sendError( HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized" );
- }
-}
- A quick sidenote here is that the 401 is sent without the WWW-Authenticate header, as required by the HTTP Spec – we can of course set the value manually if we need to.
-
-
- 3.3. The Login Form for REST
- There are multiple ways to do Authentication for a REST API – one of the default Spring Security provides is Form Login – which uses an authentication processing filter – org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter .
- The <form-login> element will create this filter and will also allow us to set our custom authentication success handler on it. This can also be done manually by using the <custom-filter> element to register a filter at the position FORM_LOGIN_FILTER – but the namespace support is flexible enough.
- Note that for a standard web application, the auto-config attribute of the <http> element is shorthand syntax for some useful security configuration. While this may be appropriate for some very simple configurations, it doesn’t fit and should not be used for a REST API.
-
-
- 3.4. Authentication should return 200 instead of 301
- By default, form login will answer a successful authentication request with a 301 MOVED PERMANENTLY status code; this makes sense in the context of an actual login form which needs to redirect after login. For a RESTful web service however, the desired response for a successful authentication should be 200 OK .
- This is done by injecting a custom authentication success handler in the form login filter, to replace the default one. The new handler implements the exact same login as the default org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler with one notable difference – the redirect logic is removed:
- public class MySavedRequestAwareAuthenticationSuccessHandler
- extends SimpleUrlAuthenticationSuccessHandler {
-
- private RequestCache requestCache = new HttpSessionRequestCache();
-
- @Override
- public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
- Authentication authentication) throws ServletException, IOException {
- SavedRequest savedRequest = requestCache.getRequest(request, response);
-
- if (savedRequest == null) {
- clearAuthenticationAttributes(request);
- return;
- }
- String targetUrlParam = getTargetUrlParameter();
- if (isAlwaysUseDefaultTargetUrl() ||
- (targetUrlParam != null &&
- StringUtils.hasText(request.getParameter(targetUrlParam)))) {
- requestCache.removeRequest(request, response);
- clearAuthenticationAttributes(request);
- return;
- }
-
- clearAuthenticationAttributes(request);
- }
-
- public void setRequestCache(RequestCache requestCache) {
- this.requestCache = requestCache;
- }
-}
-
-
- 3.5. Failed Authentication should return 401 instead of 302
- Similarly – we configured the authentication failure handler – same way we did with the success handler.
- Luckily – in this case, we don’t need to actually define a new class for this handler – the standard implementation – SimpleUrlAuthenticationFailureHandler – does just fine.
- The only difference is that – now that we’re defining this explicitly in our XML config – it’s not going to get a default defaultFailureUrl from Spring – and so it won’t redirect.
-
-
- 3.6. The Authentication Manager and Provider
- The authentication process uses an in-memory provider to perform authentication – this is meant to simplify the configuration as a production implementation of these artifacts is outside the scope of this post.
-
-
- 3.7 Finally – Authentication against the running REST Service
- Now let’s see how we can authenticate against the REST API – the URL for login is /j_spring_security_check – and a simple curl command performing login would be:
- curl -i -X POST -d j_username=user -d j_password=userPass
-http://localhost:8080/spring-security-rest/j_spring_security_check
- This request will return the Cookie which will then be used by any subsequent request against the REST Service.
- We can use curl to authentication and store the cookie it receives in a file :
- curl -i -X POST -d j_username=user -d j_password=userPass -c /opt/cookies.txt
-http://localhost:8080/spring-security-rest/j_spring_security_check
- Then we can use the cookie from the file to do further authenticated requests:
- curl -i --header "Accept:application/json" -X GET -b /opt/cookies.txt
-http://localhost:8080/spring-security-rest/api/foos
- This authenticated request will correctly result in a 200 OK :
- HTTP/1.1 200 OK
-Server: Apache-Coyote/1.1
-Content-Type: application/json;charset=UTF-8
-Transfer-Encoding: chunked
-Date: Wed, 24 Jul 2013 20:31:13 GMT
-
-[{"id":0,"name":"JbidXc"}]
-
-
-
- 4. Maven and other trouble
- The Spring core dependencies necessary for a web application and for the REST Service have been discussed in detail. For security, we’ll need to add: spring-security-web and spring-security-config – all of these have also been covered in the Maven for Spring Security tutorial.
- It’s worth paying close attention to the way Maven will resolve the older Spring dependencies – the resolution strategy will start causing problems once the security artifacts are added to the pom. To address this problem, some of the core dependencies will need to be overridden in order to keep them at the right version.
-
-
- 5. Conclusion
- This post covered the basic security configuration and implementation for a RESTful Service using Spring Security 3.1 , discussing the web.xml , the security configuration, the HTTP status codes for the authentication process and the Maven resolution of the security artifacts.
- The implementation of this Spring Security REST Tutorial can be downloaded as a working sample project.This is an Eclipse based project, so it should be easy to import and run as it is.
-
-
-
-
-
-
-
-
-
- REST, security, Spring
-
-
-
-
-
-
-
-
-
-
- © 2014 Baeldung. All Rights Reserved.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Spring Security Basic Authentication
-
-
-
-
-
- Return to Content
-
-
-
-
-
- Contents
-
-
- 1. Overview
-
-
- 2. The Spring Security Configuration
-
-
- 3. Consuming The Secured Application
-
-
- 4. Further Configuration – The Entry Point
-
-
- 5. The Maven Dependencies
-
-
- 6. Conclusion
-
-
- If you're new here, you may want to get my "REST APIs with Spring" eBook. Thanks for visiting!
-
-
-
-
- 1. Overview
- This tutorial shows how to set up, configure and customize Basic Authentication with Spring . We’re going to built on top of the simple Spring MVC example, and secure the UI of the MVC application with the Basic Auth mechanism provided by Spring Security.
-
- 2. The Spring Security Configuration
- The Configuration for Spring Security is still XML:
- <?xml version="1.0" encoding="UTF-8"?>
-<beans:beans xmlns="http://www.springframework.org/schema/security"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xmlns:beans="http://www.springframework.org/schema/beans"
- xsi:schemaLocation="
- http://www.springframework.org/schema/security
- http://www.springframework.org/schema/security/spring-security-3.1.xsd
- http://www.springframework.org/schema/beans
- http://www.springframework.org/schema/beans/spring-beans-3.2.xsd">
-
- <http use-expressions="true">
- <intercept-url pattern="/**" access="isAuthenticated()" />
-
- <http-basic />
- </http>
-
- <authentication-manager>
- <authentication-provider>
- <user-service>
- <user name="user1" password="user1Pass" authorities="ROLE_USER" />
- </user-service>
- </authentication-provider>
- </authentication-manager>
-
-</beans:beans>
- This is one of the last pieces of configuration in Spring that still need XML – Java Configuration for Spring Security is still a work in progress.
- What is relevant here is the <http-basic> element inside the main <http> element of the configuration – this is enough to enable Basic Authentication for the entire application. The Authentication Manager is not the focus of this tutorial, so we are using an in memory manager with the user and password defined in plaintext.
- The web.xml of the web application enabling Spring Security has already been discussed in the Spring Logout tutorial.
-
-
- 3. Consuming The Secured Application
- The curl command is our go to tool for consuming the secured application.
- First, let’s try to request the /homepage.html without providing any security credentials:
- curl -i http://localhost:8080/spring-security-mvc-basic-auth/homepage.html
- We get back the expected 401 Unauthorized and the Authentication Challenge:
- HTTP/1.1 401 Unauthorized
-Server: Apache-Coyote/1.1
-Set-Cookie: JSESSIONID=E5A8D3C16B65A0A007CFAACAEEE6916B; Path=/spring-security-mvc-basic-auth/; HttpOnly
-WWW-Authenticate: Basic realm="Spring Security Application"
-Content-Type: text/html;charset=utf-8
-Content-Length: 1061
-Date: Wed, 29 May 2013 15:14:08 GMT
- The browser would interpret this challenge and prompt us for credentials with a simple dialog, but since we’re using curl , this isn’t the case.
- Now, let’s request the same resource – the homepage – but provide the credentials to access it as well:
- curl -i --user user1:user1Pass http://localhost:8080/spring-security-mvc-basic-auth/homepage.html
- Now, the response from the server is 200 OK along with a Cookie :
- HTTP/1.1 200 OK
-Server: Apache-Coyote/1.1
-Set-Cookie: JSESSIONID=301225C7AE7C74B0892887389996785D; Path=/spring-security-mvc-basic-auth/; HttpOnly
-Content-Type: text/html;charset=ISO-8859-1
-Content-Language: en-US
-Content-Length: 90
-Date: Wed, 29 May 2013 15:19:38 GMT
- From the browser, the application can be consumed normally – the only difference is that a login page is no longer a hard requirement since all browsers support Basic Authentication and use a dialog to prompt the user for credentials.
-
-
- 4. Further Configuration – The Entry Point
- By default, the BasicAuthenticationEntryPoint provisioned by Spring Security returns a full html page for a 401 Unauthorized response back to the client. This html representation of the error renders well in a browser, but it not well suited for other scenarios, such as a REST API where a json representation may be preferred.
- The namespace is flexible enough for this new requirement as well – to address this – the entry point can be overridden:
- <http-basic entry-point-ref="myBasicAuthenticationEntryPoint" />
- The new entry point is defined as a standard bean:
- @Component
-public class MyBasicAuthenticationEntryPoint extends BasicAuthenticationEntryPoint {
-
- @Override
- public void commence
- (HttpServletRequest request, HttpServletResponse response, AuthenticationException authEx)
- throws IOException, ServletException {
- response.addHeader("WWW-Authenticate", "Basic realm=\"" + getRealmName() + "\"");
- response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
- PrintWriter writer = response.getWriter();
- writer.println("HTTP Status 401 - " + authEx.getMessage());
- }
-
- @Override
- public void afterPropertiesSet() throws Exception {
- setRealmName("Baeldung");
- super.afterPropertiesSet();
- }
-}
- By writing directly to the HTTP Response we now have full control over the format of the response body.
-
-
- 5. The Maven Dependencies
- The Maven dependencies for Spring Security have been discussed before in the Spring Security with Maven article – we will need both spring-security-web and spring-security-config available at runtime.
-
-
- 6. Conclusion
- In this example we secured an MVC application with Spring Security and Basic Authentication. We discussed the XML configuration and we consumed the application with simple curl commands. Finally took control of the exact error message format – moving from the standard HTML error page to a custom text or json format.
- The implementation of this Spring tutorial can be found in the github project – this is an Eclipse based project, so it should be easy to import and run as it is. When the project runs locally, the sample html can be accessed at:
- http://localhost:8080/spring-security-mvc-basic-auth/homepage.html
-
-
-
-
-
-
-
-
-
- security, Spring
-
-
-
-
-
-
-
-
-
-
- © 2014 Baeldung. All Rights Reserved.
-
-
-
-
-
-
-
-
-
-
-
-
-
- Spring Security Digest Authentication
-
-
-
-
-
- Return to Content
-
-
-
-
-
- Contents
-
-
- 1. Overview
-
-
- 2. The Security XML Configuration
-
-
- 3. Consuming the Secured Application
-
-
- 4. The Maven Dependencies
-
-
- 5. Conclusion
-
-
- If you're new here, you may want to get my "REST APIs with Spring" eBook. Thanks for visiting!
-
-
-
-
- 1. Overview
- This tutorial shows how to set up, configure and customize Digest Authentication with Spring. Similar to the previous article covering Basic Authentication, we’re going to built on top of the Spring MVC tutorial, and secure the application with the Digest Auth mechanism provided by Spring Security.
-
- 2. The Security XML Configuration
- First thing to understand about the configuration is that, while Spring Security does have full out of the box support for the Digest authentication mechanism, this support is not as well integrated into the namespace as Basic Authentication was.
- In this case, we need to manually define the raw beans that are going to make up the security configuration – the DigestAuthenticationFilter and the DigestAuthenticationEntryPoint :
- <?xml version="1.0" encoding="UTF-8"?>
-<beans:beans xmlns="http://www.springframework.org/schema/security"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xmlns:beans="http://www.springframework.org/schema/beans"
- xsi:schemaLocation="
- http://www.springframework.org/schema/security
- http://www.springframework.org/schema/security/spring-security-3.1.xsd
- http://www.springframework.org/schema/beans
- http://www.springframework.org/schema/beans/spring-beans-3.2.xsd">
-
-Â Â Â <beans:bean id="digestFilter"
- class="org.springframework.security.web.authentication.www.DigestAuthenticationFilter">
-Â Â Â Â Â Â Â <beans:property name="userDetailsService" ref="userService" />
-Â Â Â Â Â Â Â <beans:property name="authenticationEntryPoint" ref="digestEntryPoint" />
-Â Â Â </beans:bean>
-Â Â Â <beans:bean id="digestEntryPoint"Â
- class="org.springframework.security.web.authentication.www.DigestAuthenticationEntryPoint">
-Â Â Â Â Â Â Â <beans:property name="realmName" value="Contacts Realm via Digest Authentication" />
-Â Â Â Â Â Â Â <beans:property name="key" value="acegi" />
-Â Â Â </beans:bean>
-
- <!-- the security namespace configuration -->
- <http use-expressions="true" entry-point-ref="digestEntryPoint">
- <intercept-url pattern="/**" access="isAuthenticated()" />
-
- <custom-filter ref="digestFilter" after="BASIC_AUTH_FILTER" />
- </http>
-
- <authentication-manager>
- <authentication-provider>
- <user-service id="userService">
- <user name="user1" password="user1Pass" authorities="ROLE_USER" />
- </user-service>
- </authentication-provider>
- </authentication-manager>
-
-</beans:beans>
- Next, we need to integrate these beans into the overall security configuration – and in this case, the namespace is still flexible enough to allow us to do that.
- The first part of this is pointing to the custom entry point bean, via the entry-point-ref attribute of the main <http> element.
- The second part is adding the newly defined digest filter into the security filter chain . Since this filter is functionally equivalent to the BasicAuthenticationFilter , we are using the same relative position in the chain – this is specified by the BASIC_AUTH_FILTER alias in the overall Spring Security Standard Filters.
- Finally, notice that the Digest Filter is configured to point to the user service bean – and here, the namespace is again very useful as it allows us to specify a bean name for the default user service created by the <user-service> element:
- <user-service id="userService">
-
-
- 3. Consuming the Secured Application
- We’re going to be using the curl command to consume the secured application and understand how a client can interact with it.
- Let’s start by requesting the homepage – without providing security credentials in the request:
- curl -i http://localhost/spring-security-mvc-digest-auth/homepage.html
- As expected, we get back a response with a 401 Unauthorized status code:
- HTTP/1.1 401 Unauthorized
-Server: Apache-Coyote/1.1
-Set-Cookie: JSESSIONID=CF0233C...; Path=/spring-security-mvc-digest-auth/; HttpOnly
-WWW-Authenticate: Digest realm="Contacts Realm via Digest Authentication", qop="auth",
- nonce="MTM3MzYzODE2NTg3OTo3MmYxN2JkOWYxZTc4MzdmMzBiN2Q0YmY0ZTU0N2RkZg=="
-Content-Type: text/html;charset=utf-8
-Content-Length: 1061
-Date: Fri, 12 Jul 2013 14:04:25 GMT
- If this request were sent by the browser, the authentication challenge would prompt the user for credentials using a simple user/password dialog.
- Let’s now provide the correct credentials and send the request again:
- curl -i --digest --user
- user1:user1Pass http://localhost/spring-security-mvc-digest-auth/homepage.html
- Notice that we are enabling Digest Authentication for the curl command via the –digest flag.
- The first response from the server will be the same – the 401 Unauthorized – but the challenge will now be interpreted and acted upon by a second request – which will succeed with a 200 OK :
- HTTP/1.1 401 Unauthorized
-Server: Apache-Coyote/1.1
-Set-Cookie: JSESSIONID=A961E0D...; Path=/spring-security-mvc-digest-auth/; HttpOnly
-WWW-Authenticate: Digest realm="Contacts Realm via Digest Authentication", qop="auth",
- nonce="MTM3MzYzODgyOTczMTo3YjM4OWQzMGU0YTgwZDg0YmYwZjRlZWJjMDQzZWZkOA=="
-Content-Type: text/html;charset=utf-8
-Content-Length: 1061
-Date: Fri, 12 Jul 2013 14:15:29 GMT
-
-HTTP/1.1 200 OK
-Server: Apache-Coyote/1.1
-Set-Cookie: JSESSIONID=55F996B...; Path=/spring-security-mvc-digest-auth/; HttpOnly
-Content-Type: text/html;charset=ISO-8859-1
-Content-Language: en-US
-Content-Length: 90
-Date: Fri, 12 Jul 2013 14:15:29 GMT
-
-<html>
-<head></head>
-
-<body>
- <h1>This is the homepage</h1>
-</body>
-</html>
- A final note on this interaction is that a client can preemptively send the correct Authorization header with the first request, and thus entirely avoid the server security challenge and the second request.
-
-
- 4. The Maven Dependencies
- The security dependencies are discussed in depth in the Spring Security Maven tutorial. In short, we will need to define spring-security-web and spring-security-config as dependencies in our pom.xml .
-
-
- 5. Conclusion
- In this tutorial we introduce security into a simple Spring MVC project by leveraging the Digest Authentication support in the framework.
- The implementation of these examples can be found in the github project – this is an Eclipse based project, so it should be easy to import and run as it is.
- When the project runs locally, the homepage html can be accessed at (or, with minimal Tomcat configuration, on port 80):
- http://localhost:8080/spring-security-mvc-digest-auth/homepage.html
- Finally, there is no reason an application needs to choose between Basic and Digest authentication – both can be set up simultaneously on the same URI structure , in such a way that the client can pick between the two mechanisms when consuming the web application.
-
-
-
-
-
-
-
-
-
- security, Spring
-
-
-
-
-
-
-
-
-
-
- © 2014 Baeldung. All Rights Reserved.
-
-
-
-
-
-
-
-
-
-
-
-
-
- Basic and Digest Authentication for a REST Service with Spring Security
-
-
-
-
-
- Return to Content
-
-
-
-
-
- Contents
-
-
- Table of Contents
-
-
- 1. Overview
-
-
- 2. Configuration of Basic Authentication
-
-
- 3. Configuration of Digest Authentication
-
-
- 4. Supporting both authentication protocols in the same RESTful service
-
-
- 5. Testing both scenarios
-
-
- 6. Conclusion
-
-
- If you're new here, you may want to get my "REST APIs with Spring" eBook. Thanks for visiting!
-
-
-
-
- Table of Contents
-
-
- 1. Overview
-
-
- 2. Configuration of Basic Authentication
-
-
- Â Â Â 2.1. Satisfying the stateless constraint – getting rid of sessions
-
-
- 3. Configuration of Digest Authentication
-
-
- 4. Supporting both authentication protocols in the same RESTful service
-
-
- Â Â Â 4.1. Anonymous request
-
-
- Â Â Â 4.2. Request with authentication credentials
-
-
- 5. Testing both scenarios
-
-
- 6. Conclusion
-
-
-
- 1. Overview
- This article discusses how to set up both Basic and Digest Authentication on the same URI structure of a REST API . In a previous article, we discussed another method of securing the REST Service – form based authentication, so Basic and Digest authentication is the natural alternative, as well as the more RESTful one.
-
-
- 2. Configuration of Basic Authentication
- The main reason that form based authentication is not ideal for a RESTful Service is that Spring Security will make use of Sessions – this is of course state on the server, so the statelessness constraints in REST is practically ignored.
- We’ll start by setting up Basic Authentication – first we remove the old custom entry point and filter from the main <http> security element:
- <http create-session="stateless">
- <intercept-url pattern="/api/admin/**" access="ROLE_ADMIN" />
-
- <http-basic />
-</http>
- Note how support for basic authentication has been added with a single configuration line – <http-basic /> – which handles the creation and wiring of both the BasicAuthenticationFilter and the BasicAuthenticationEntryPoint .
-
- 2.1. Satisfying the stateless constraint – getting rid of sessions
- One of the main constraints of the RESTful architectural style is that the client-server communication is fully stateless , as the original dissertation reads:
-
- Â Â Â 5.1.3 Stateless
- We next add a constraint to the client-server interaction: communication must be stateless in nature, as in the client-stateless-server (CSS) style of Section 3.4.3 (Figure 5-3), such that each request from client to server must contain all of the information necessary to understand the request, and cannot take advantage of any stored context on the server. Session state is therefore kept entirely on the client .
-
- The concept of Session on the server is one with a long history in Spring Security, and removing it entirely has been difficult until now, especially when configuration was done by using the namespace. However, Spring Security 3.1 augments the namespace configuration with a new stateless option for session creation, which effectively guarantees that no session will be created or used by Spring. What this new option does is completely removes all session related filters from the security filter chain, ensuring that authentication is performed for each request.
-
-
-
- 3. Configuration of Digest Authentication
- Starting with the previous configuration, the filter and entry point necessary to set up digest authentication will be defined as beans. Then, the digest entry point will override the one created by <http-basic> behind the scenes. Finally, the custom digest filter will be introduced in the security filter chain using the after semantics of the security namespace to position it directly after the basic authentication filter.
- <http create-session="stateless" entry-point-ref="digestEntryPoint">
- <intercept-url pattern="/api/admin/**" access="ROLE_ADMIN" />
-
- <http-basic />
- <custom-filter ref="digestFilter" after="BASIC_AUTH_FILTER" />
-</http>
-
-<beans:bean id="digestFilter" class=
- "org.springframework.security.web.authentication.www.DigestAuthenticationFilter">
- <beans:property name="userDetailsService" ref="userService" />
- <beans:property name="authenticationEntryPoint" ref="digestEntryPoint" />
-</beans:bean>
-
-<beans:bean id="digestEntryPoint" class=
- "org.springframework.security.web.authentication.www.DigestAuthenticationEntryPoint">
- <beans:property name="realmName" value="Contacts Realm via Digest Authentication"/>
- <beans:property name="key" value="acegi" />
-</beans:bean>
-
-<authentication-manager>
- <authentication-provider>
- <user-service id="userService">
- <user name="eparaschiv" password="eparaschiv" authorities="ROLE_ADMIN" />
- <user name="user" password="user" authorities="ROLE_USER" />
- </user-service>
- </authentication-provider>
-</authentication-manager>
- Unfortunately there is no support in the security namespace to automatically configure the digest authentication the way basic authentication can be configured with <http-basic> . Because of that, the necessary beans had to be defined and wired manually into the security configuration.
-
-
- 4. Supporting both authentication protocols in the same RESTful service
- Basic or Digest authentication alone can be easily implemented in Spring Security 3.x; it is supporting both of them for the same RESTful web service, on the same URI mappings that introduces a new level of complexity into the configuration and testing of the service.
-
- 4.1. Anonymous request
- With both basic and digest filters in the security chain, the way a anonymous request – a request containing no authentication credentials (Authorization HTTP header) – is processed by Spring Security is – the two authentication filters will find no credentials and will continue execution of the filter chain. Then, seeing how the request wasn’t authenticated, an AccessDeniedException is thrown and caught in the ExceptionTranslationFilter , which commences the digest entry point, prompting the client for credentials.
- The responsibilities of both the basic and digest filters are very narrow – they will continue to execute the security filter chain if they are unable to identify the type of authentication credentials in the request. It is because of this that Spring Security can have the flexibility to be configured with support for multiple authentication protocols on the same URI.
- When a request is made containing the correct authentication credentials – either basic or digest – that protocol will be rightly used. However, for an anonymous request, the client will get prompted only for digest authentication credentials. This is because the digest entry point is configured as the main and single entry point of the Spring Security chain; as such digest authentication can be considered the default .
-
-
- 4.2. Request with authentication credentials
- A request with credentials for Basic authentication will be identified by the Authorization header starting with the prefix “Basic” . When processing such a request, the credentials will be decoded in the basic authentication filter and the request will be authorized. Similarly, a request with credentials for Digest authentication will use the prefix “Digest” Â for it’s Authorization header.
-
-
-
- 5. Testing both scenarios
- The tests will consume the REST service by creating a new resource after authenticating with either basic or digest:
- @Test
-public void givenAuthenticatedByBasicAuth_whenAResourceIsCreated_then201IsReceived(){
- // Given
- // When
- Response response = given()
- .auth().preemptive().basic( ADMIN_USERNAME, ADMIN_PASSWORD )
- .contentType( HttpConstants.MIME_JSON ).body( new Foo( randomAlphabetic( 6 ) ) )
- .post( paths.getFooURL() );
-
- // Then
- assertThat( response.getStatusCode(), is( 201 ) );
-}
-@Test
-public void givenAuthenticatedByDigestAuth_whenAResourceIsCreated_then201IsReceived(){
- // Given
- // When
- Response response = given()
- .auth().digest( ADMIN_USERNAME, ADMIN_PASSWORD )
- .contentType( HttpConstants.MIME_JSON ).body( new Foo( randomAlphabetic( 6 ) ) )
- .post( paths.getFooURL() );
-
- // Then
- assertThat( response.getStatusCode(), is( 201 ) );
-}
- Note that the test using basic authentication adds credentials to the request preemptively , regardless if the server has challenged for authentication or not. This is to ensure that the server doesn’t need to challenge the client for credentials, because if it did, the challenge would be for Digest credentials, since that is the default.
-
-
- 6. Conclusion
- This article covered the configuration and implementation of both Basic and Digest authentication for a RESTful service, using mostly Spring Security 3.0 namespace support as well as some new features added by Spring Security 3.1.
- For the full implementation, check out the github project.
-
-
-
-
-
-
-
-
-
- REST, security, Spring
-
-
-
-
-
-
-
-
-
-
- © 2014 Baeldung. All Rights Reserved.
-
-
-
-
-
-
-
-
-
-
diff --git a/apache-fop/src/test/resources/output_herold.pdf b/apache-fop/src/test/resources/output_herold.pdf
deleted file mode 100644
index 1d23de7b61..0000000000
Binary files a/apache-fop/src/test/resources/output_herold.pdf and /dev/null differ
diff --git a/apache-fop/src/test/resources/output_html2fo.pdf b/apache-fop/src/test/resources/output_html2fo.pdf
deleted file mode 100644
index 7c2b4a0c51..0000000000
Binary files a/apache-fop/src/test/resources/output_html2fo.pdf and /dev/null differ
diff --git a/apache-fop/src/test/resources/output_jtidy.pdf b/apache-fop/src/test/resources/output_jtidy.pdf
deleted file mode 100644
index 1d9456122c..0000000000
Binary files a/apache-fop/src/test/resources/output_jtidy.pdf and /dev/null differ
diff --git a/apache-geode/README.md b/apache-geode/README.md
index 2f04418825..86629f7a82 100644
--- a/apache-geode/README.md
+++ b/apache-geode/README.md
@@ -1,3 +1,7 @@
+## Apache Geode
+
+This module contains articles about Apache Geode
+
### Relevant Articles:
- [A Quick Guide to Apache Geode](https://www.baeldung.com/apache-geode)
diff --git a/apache-geode/pom.xml b/apache-geode/pom.xml
index 15c7e04d29..195dfadd20 100644
--- a/apache-geode/pom.xml
+++ b/apache-geode/pom.xml
@@ -19,25 +19,7 @@
geode-core
${geode.core}
-
- junit
- junit
- ${junit.version}
-
-
-
-
-
- org.apache.maven.plugins
- maven-compiler-plugin
-
- ${java.version}
- ${java.version}
-
-
-
-
1.6.0
diff --git a/apache-meecrowave/README.md b/apache-meecrowave/README.md
index 42b93a383e..d360af13af 100644
--- a/apache-meecrowave/README.md
+++ b/apache-meecrowave/README.md
@@ -1,3 +1,7 @@
+## Apache Meecrowave
+
+This module contains articles about Apache Meecrowave
+
### Relevant Articles:
-================================
-- [Building a Microservice with Apache Meecrowave](http://www.baeldung.com/apache-meecrowave)
+
+- [Building a Microservice with Apache Meecrowave](https://www.baeldung.com/apache-meecrowave)
\ No newline at end of file
diff --git a/apache-meecrowave/pom.xml b/apache-meecrowave/pom.xml
index 4eb1094f94..51c6514992 100644
--- a/apache-meecrowave/pom.xml
+++ b/apache-meecrowave/pom.xml
@@ -1,7 +1,6 @@
4.0.0
- com.baeldung
apache-meecrowave
0.0.1
apache-meecrowave
@@ -38,13 +37,6 @@
${meecrowave-junit.version}
test
-
-
- junit
- junit
- ${junit.version}
- test
-
diff --git a/apache-olingo/README.md b/apache-olingo/README.md
index bfbdc97700..2f4e86d5a2 100644
--- a/apache-olingo/README.md
+++ b/apache-olingo/README.md
@@ -1,3 +1,8 @@
-## Relevant articles:
+## Apache Olingo
+
+This module contains articles about Apache Olingo
+
+### Relevant articles:
- [OData Protocol Guide](https://www.baeldung.com/odata)
+- [Intro to OData with Olingo](https://www.baeldung.com/olingo)
diff --git a/apache-olingo/olingo2/pom.xml b/apache-olingo/olingo2/pom.xml
index 1efd4ea602..95b123efbc 100644
--- a/apache-olingo/olingo2/pom.xml
+++ b/apache-olingo/olingo2/pom.xml
@@ -3,37 +3,27 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4.0.0
-
- org.springframework.boot
- spring-boot-starter-parent
- 2.1.3.RELEASE
-
-
org.baeldung.examples.olingo2
- olingo2-sample
- 0.0.1-SNAPSHOT
- olingo2-sample
+ olingo2
+ olingo2
Sample Olingo 2 Project
-
- 1.8
- 2.0.11
-
+
+ parent-boot-2
+ com.baeldung
+ 0.0.1-SNAPSHOT
+ ../../parent-boot-2
+
-
org.springframework.boot
spring-boot-starter-jersey
-
-
org.springframework.boot
spring-boot-starter-data-jpa
-
-
com.h2database
h2
@@ -90,4 +80,8 @@
+
+ 2.0.11
+
+
diff --git a/apache-olingo/olingo2/src/test/java/org/baeldung/examples/olingo2/Olingo2SampleApplicationTests.java b/apache-olingo/olingo2/src/test/java/org/baeldung/examples/olingo2/Olingo2SampleApplicationTests.java
deleted file mode 100644
index 687f6ab1ff..0000000000
--- a/apache-olingo/olingo2/src/test/java/org/baeldung/examples/olingo2/Olingo2SampleApplicationTests.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package org.baeldung.examples.olingo2;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.springframework.boot.test.context.SpringBootTest;
-import org.springframework.test.context.junit4.SpringRunner;
-
-@RunWith(SpringRunner.class)
-@SpringBootTest
-public class Olingo2SampleApplicationTests {
-
- @Test
- public void contextLoads() {
- }
-
-}
diff --git a/apache-olingo/olingo2/src/test/java/org/baeldung/examples/olingo2/Olingo2SampleApplicationUnitTest.java b/apache-olingo/olingo2/src/test/java/org/baeldung/examples/olingo2/Olingo2SampleApplicationUnitTest.java
new file mode 100644
index 0000000000..7cb685e3e9
--- /dev/null
+++ b/apache-olingo/olingo2/src/test/java/org/baeldung/examples/olingo2/Olingo2SampleApplicationUnitTest.java
@@ -0,0 +1,16 @@
+package org.baeldung.examples.olingo2;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit4.SpringRunner;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest
+public class Olingo2SampleApplicationUnitTest {
+
+ @Test
+ public void contextLoads() {
+ }
+
+}
diff --git a/apache-opennlp/README.md b/apache-opennlp/README.md
index 2e9fa0e384..4b1fa36540 100644
--- a/apache-opennlp/README.md
+++ b/apache-opennlp/README.md
@@ -1,3 +1,7 @@
+## Apache OpenNLP
+
+This module contains articles about Apache OpenNLP
+
### Relevant Articles
-- [Intro to Apache OpenNLP](http://www.baeldung.com/apache-open-nlp)
+- [Intro to Apache OpenNLP](https://www.baeldung.com/apache-open-nlp)
diff --git a/apache-poi/README.md b/apache-poi/README.md
index 862981991d..b7b8bf5f6a 100644
--- a/apache-poi/README.md
+++ b/apache-poi/README.md
@@ -1,4 +1,8 @@
+## Apache POI
+
+This module contains articles about Apache POI
+
### Relevant Articles:
-- [Microsoft Word Processing in Java with Apache POI](http://www.baeldung.com/java-microsoft-word-with-apache-poi)
-- [Working with Microsoft Excel in Java](http://www.baeldung.com/java-microsoft-excel)
-- [Creating a MS PowerPoint Presentation in Java](http://www.baeldung.com/apache-poi-slideshow)
+- [Microsoft Word Processing in Java with Apache POI](https://www.baeldung.com/java-microsoft-word-with-apache-poi)
+- [Working with Microsoft Excel in Java](https://www.baeldung.com/java-microsoft-excel)
+- [Creating a MS PowerPoint Presentation in Java](https://www.baeldung.com/apache-poi-slideshow)
diff --git a/apache-pulsar/README.md b/apache-pulsar/README.md
index 2970bc3d88..c44849a490 100644
--- a/apache-pulsar/README.md
+++ b/apache-pulsar/README.md
@@ -1,3 +1,7 @@
+## Apache Pulsar
+
+This module contains articles about Apache Pulsar
+
### Relevant Articles:
- [Introduction to Apache Pulsar](https://www.baeldung.com/apache-pulsar)
diff --git a/apache-pulsar/pom.xml b/apache-pulsar/pom.xml
index 11df6d0b87..fdd9ae1e95 100644
--- a/apache-pulsar/pom.xml
+++ b/apache-pulsar/pom.xml
@@ -7,6 +7,13 @@
0.0.1
apache-pulsar
+
+ com.baeldung
+ parent-modules
+ 1.0.0-SNAPSHOT
+ ..
+
+
org.apache.pulsar
@@ -17,8 +24,6 @@
- 1.8
- 1.8
- 2.1.1-incubating
+ 2.1.1-incubating
diff --git a/apache-pulsar/src/main/java/com/baeldung/ConsumerTest.java b/apache-pulsar/src/main/java/com/baeldung/ConsumerTest.java
deleted file mode 100755
index 72dc10b542..0000000000
--- a/apache-pulsar/src/main/java/com/baeldung/ConsumerTest.java
+++ /dev/null
@@ -1,48 +0,0 @@
-package com.baeldung;
-
-import java.io.IOException;
-
-import org.apache.pulsar.client.api.Consumer;
-import org.apache.pulsar.client.api.Message;
-import org.apache.pulsar.client.api.PulsarClient;
-import org.apache.pulsar.client.api.SubscriptionType;
-
-public class ConsumerTest {
-
- private static final String SERVICE_URL = "pulsar://localhost:6650";
- private static final String TOPIC_NAME = "test-topic";
- private static final String SUBSCRIPTION_NAME = "test-subscription";
-
- public static void main(String[] args) throws IOException {
- // Create a Pulsar client instance. A single instance can be shared across many
- // producers and consumer within the same application
- PulsarClient client = PulsarClient.builder()
- .serviceUrl(SERVICE_URL)
- .build();
-
- //Configure consumer specific settings.
- Consumer consumer = client.newConsumer()
- .topic(TOPIC_NAME)
- // Allow multiple consumers to attach to the same subscription
- // and get messages dispatched as a queue
- .subscriptionType(SubscriptionType.Shared)
- .subscriptionName(SUBSCRIPTION_NAME)
- .subscribe();
-
-
- // Once the consumer is created, it can be used for the entire application lifecycle
- System.out.println("Created consumer for the topic "+ TOPIC_NAME);
-
- do {
- // Wait until a message is available
- Message msg = consumer.receive();
-
- // Extract the message as a printable string and then log
- String content = new String(msg.getData());
- System.out.println("Received message '"+content+"' with ID "+msg.getMessageId());
-
- // Acknowledge processing of the message so that it can be deleted
- consumer.acknowledge(msg);
- } while (true);
- }
-}
diff --git a/apache-pulsar/src/main/java/com/baeldung/ConsumerUnitTest.java b/apache-pulsar/src/main/java/com/baeldung/ConsumerUnitTest.java
new file mode 100644
index 0000000000..82a0028837
--- /dev/null
+++ b/apache-pulsar/src/main/java/com/baeldung/ConsumerUnitTest.java
@@ -0,0 +1,48 @@
+package com.baeldung;
+
+import java.io.IOException;
+
+import org.apache.pulsar.client.api.Consumer;
+import org.apache.pulsar.client.api.Message;
+import org.apache.pulsar.client.api.PulsarClient;
+import org.apache.pulsar.client.api.SubscriptionType;
+
+public class ConsumerUnitTest {
+
+ private static final String SERVICE_URL = "pulsar://localhost:6650";
+ private static final String TOPIC_NAME = "test-topic";
+ private static final String SUBSCRIPTION_NAME = "test-subscription";
+
+ public static void main(String[] args) throws IOException {
+ // Create a Pulsar client instance. A single instance can be shared across many
+ // producers and consumer within the same application
+ PulsarClient client = PulsarClient.builder()
+ .serviceUrl(SERVICE_URL)
+ .build();
+
+ //Configure consumer specific settings.
+ Consumer consumer = client.newConsumer()
+ .topic(TOPIC_NAME)
+ // Allow multiple consumers to attach to the same subscription
+ // and get messages dispatched as a queue
+ .subscriptionType(SubscriptionType.Shared)
+ .subscriptionName(SUBSCRIPTION_NAME)
+ .subscribe();
+
+
+ // Once the consumer is created, it can be used for the entire application lifecycle
+ System.out.println("Created consumer for the topic "+ TOPIC_NAME);
+
+ do {
+ // Wait until a message is available
+ Message msg = consumer.receive();
+
+ // Extract the message as a printable string and then log
+ String content = new String(msg.getData());
+ System.out.println("Received message '"+content+"' with ID "+msg.getMessageId());
+
+ // Acknowledge processing of the message so that it can be deleted
+ consumer.acknowledge(msg);
+ } while (true);
+ }
+}
diff --git a/apache-pulsar/src/main/java/com/baeldung/ProducerTest.java b/apache-pulsar/src/main/java/com/baeldung/ProducerTest.java
deleted file mode 100755
index 08ee0e89b9..0000000000
--- a/apache-pulsar/src/main/java/com/baeldung/ProducerTest.java
+++ /dev/null
@@ -1,58 +0,0 @@
-package com.baeldung;
-
-import org.apache.pulsar.client.api.CompressionType;
-import org.apache.pulsar.client.api.Message;
-import org.apache.pulsar.client.api.MessageBuilder;
-import org.apache.pulsar.client.api.MessageId;
-import org.apache.pulsar.client.api.Producer;
-import org.apache.pulsar.client.api.PulsarClient;
-import org.apache.pulsar.client.api.PulsarClientException;
-
-import java.io.IOException;
-import java.util.stream.IntStream;
-
-public class ProducerTest {
-
- private static final String SERVICE_URL = "pulsar://localhost:6650";
- private static final String TOPIC_NAME = "test-topic";
-
- public static void main(String[] args) throws IOException {
- // Create a Pulsar client instance. A single instance can be shared across many
- // producers and consumer within the same application
- PulsarClient client = PulsarClient.builder()
- .serviceUrl(SERVICE_URL)
- .build();
-
- // Configure producer specific settings
- Producer producer = client.newProducer()
- // Set the topic
- .topic(TOPIC_NAME)
- // Enable compression
- .compressionType(CompressionType.LZ4)
- .create();
-
- // Once the producer is created, it can be used for the entire application life-cycle
- System.out.println("Created producer for the topic "+TOPIC_NAME);
-
- // Send 5 test messages
- IntStream.range(1, 5).forEach(i -> {
- String content = String.format("hi-pulsar-%d", i);
-
- // Build a message object
- Message msg = MessageBuilder.create()
- .setContent(content.getBytes())
- .build();
-
- // Send each message and log message content and ID when successfully received
- try {
- MessageId msgId = producer.send(msg);
-
- System.out.println("Published message '"+content+"' with the ID "+msgId);
- } catch (PulsarClientException e) {
- System.out.println(e.getMessage());
- }
- });
-
- client.close();
- }
-}
diff --git a/apache-pulsar/src/main/java/com/baeldung/ProducerUnitTest.java b/apache-pulsar/src/main/java/com/baeldung/ProducerUnitTest.java
new file mode 100644
index 0000000000..10a4b46c4d
--- /dev/null
+++ b/apache-pulsar/src/main/java/com/baeldung/ProducerUnitTest.java
@@ -0,0 +1,58 @@
+package com.baeldung;
+
+import org.apache.pulsar.client.api.CompressionType;
+import org.apache.pulsar.client.api.Message;
+import org.apache.pulsar.client.api.MessageBuilder;
+import org.apache.pulsar.client.api.MessageId;
+import org.apache.pulsar.client.api.Producer;
+import org.apache.pulsar.client.api.PulsarClient;
+import org.apache.pulsar.client.api.PulsarClientException;
+
+import java.io.IOException;
+import java.util.stream.IntStream;
+
+public class ProducerUnitTest {
+
+ private static final String SERVICE_URL = "pulsar://localhost:6650";
+ private static final String TOPIC_NAME = "test-topic";
+
+ public static void main(String[] args) throws IOException {
+ // Create a Pulsar client instance. A single instance can be shared across many
+ // producers and consumer within the same application
+ PulsarClient client = PulsarClient.builder()
+ .serviceUrl(SERVICE_URL)
+ .build();
+
+ // Configure producer specific settings
+ Producer producer = client.newProducer()
+ // Set the topic
+ .topic(TOPIC_NAME)
+ // Enable compression
+ .compressionType(CompressionType.LZ4)
+ .create();
+
+ // Once the producer is created, it can be used for the entire application life-cycle
+ System.out.println("Created producer for the topic "+TOPIC_NAME);
+
+ // Send 5 test messages
+ IntStream.range(1, 5).forEach(i -> {
+ String content = String.format("hi-pulsar-%d", i);
+
+ // Build a message object
+ Message msg = MessageBuilder.create()
+ .setContent(content.getBytes())
+ .build();
+
+ // Send each message and log message content and ID when successfully received
+ try {
+ MessageId msgId = producer.send(msg);
+
+ System.out.println("Published message '"+content+"' with the ID "+msgId);
+ } catch (PulsarClientException e) {
+ System.out.println(e.getMessage());
+ }
+ });
+
+ client.close();
+ }
+}
diff --git a/apache-pulsar/src/main/java/com/baeldung/subscriptions/ExclusiveSubscriptionTest.java b/apache-pulsar/src/main/java/com/baeldung/subscriptions/ExclusiveSubscriptionTest.java
deleted file mode 100644
index efb898eaf4..0000000000
--- a/apache-pulsar/src/main/java/com/baeldung/subscriptions/ExclusiveSubscriptionTest.java
+++ /dev/null
@@ -1,59 +0,0 @@
-package com.baeldung.subscriptions;
-
-import org.apache.pulsar.client.api.ConsumerBuilder;
-import org.apache.pulsar.client.api.Message;
-import org.apache.pulsar.client.api.MessageBuilder;
-import org.apache.pulsar.client.api.Producer;
-import org.apache.pulsar.client.api.PulsarClient;
-import org.apache.pulsar.client.api.PulsarClientException;
-import org.apache.pulsar.client.api.SubscriptionType;
-
-import java.util.stream.IntStream;
-
-public class ExclusiveSubscriptionTest {
- private static final String SERVICE_URL = "pulsar://localhost:6650";
- private static final String TOPIC_NAME = "test-topic";
- private static final String SUBSCRIPTION_NAME = "test-subscription";
- private static final SubscriptionType SUBSCRIPTION_TYPE = SubscriptionType.Exclusive;
-
- public static void main(String[] args) throws PulsarClientException {
- PulsarClient client = PulsarClient.builder()
- .serviceUrl(SERVICE_URL)
- .build();
-
- Producer producer = client.newProducer()
- .topic(TOPIC_NAME)
- .create();
-
- ConsumerBuilder consumer1 = client.newConsumer()
- .topic(TOPIC_NAME)
- .subscriptionName(SUBSCRIPTION_NAME)
- .subscriptionType(SUBSCRIPTION_TYPE);
-
- ConsumerBuilder consumer2 = client.newConsumer()
- .topic(TOPIC_NAME)
- .subscriptionName(SUBSCRIPTION_NAME)
- .subscriptionType(SUBSCRIPTION_TYPE);
-
- IntStream.range(0, 999).forEach(i -> {
- Message msg = MessageBuilder.create()
- .setContent(String.format("message-%d", i).getBytes())
- .build();
- try {
- producer.send(msg);
- } catch (PulsarClientException e) {
- System.out.println(e.getMessage());
- }
- });
-
- // Consumer 1 can subscribe to the topic
- consumer1.subscribe();
-
- // Consumer 2 cannot due to the exclusive subscription held by consumer 1
- consumer2.subscribeAsync()
- .handle((consumer, exception) -> {
- System.out.println(exception.getMessage());
- return null;
- });
- }
-}
diff --git a/apache-pulsar/src/main/java/com/baeldung/subscriptions/ExclusiveSubscriptionUnitTest.java b/apache-pulsar/src/main/java/com/baeldung/subscriptions/ExclusiveSubscriptionUnitTest.java
new file mode 100644
index 0000000000..79121347e7
--- /dev/null
+++ b/apache-pulsar/src/main/java/com/baeldung/subscriptions/ExclusiveSubscriptionUnitTest.java
@@ -0,0 +1,59 @@
+package com.baeldung.subscriptions;
+
+import org.apache.pulsar.client.api.ConsumerBuilder;
+import org.apache.pulsar.client.api.Message;
+import org.apache.pulsar.client.api.MessageBuilder;
+import org.apache.pulsar.client.api.Producer;
+import org.apache.pulsar.client.api.PulsarClient;
+import org.apache.pulsar.client.api.PulsarClientException;
+import org.apache.pulsar.client.api.SubscriptionType;
+
+import java.util.stream.IntStream;
+
+public class ExclusiveSubscriptionUnitTest {
+ private static final String SERVICE_URL = "pulsar://localhost:6650";
+ private static final String TOPIC_NAME = "test-topic";
+ private static final String SUBSCRIPTION_NAME = "test-subscription";
+ private static final SubscriptionType SUBSCRIPTION_TYPE = SubscriptionType.Exclusive;
+
+ public static void main(String[] args) throws PulsarClientException {
+ PulsarClient client = PulsarClient.builder()
+ .serviceUrl(SERVICE_URL)
+ .build();
+
+ Producer producer = client.newProducer()
+ .topic(TOPIC_NAME)
+ .create();
+
+ ConsumerBuilder consumer1 = client.newConsumer()
+ .topic(TOPIC_NAME)
+ .subscriptionName(SUBSCRIPTION_NAME)
+ .subscriptionType(SUBSCRIPTION_TYPE);
+
+ ConsumerBuilder consumer2 = client.newConsumer()
+ .topic(TOPIC_NAME)
+ .subscriptionName(SUBSCRIPTION_NAME)
+ .subscriptionType(SUBSCRIPTION_TYPE);
+
+ IntStream.range(0, 999).forEach(i -> {
+ Message msg = MessageBuilder.create()
+ .setContent(String.format("message-%d", i).getBytes())
+ .build();
+ try {
+ producer.send(msg);
+ } catch (PulsarClientException e) {
+ System.out.println(e.getMessage());
+ }
+ });
+
+ // Consumer 1 can subscribe to the topic
+ consumer1.subscribe();
+
+ // Consumer 2 cannot due to the exclusive subscription held by consumer 1
+ consumer2.subscribeAsync()
+ .handle((consumer, exception) -> {
+ System.out.println(exception.getMessage());
+ return null;
+ });
+ }
+}
diff --git a/apache-pulsar/src/main/java/com/baeldung/subscriptions/FailoverSubscriptionTest.java b/apache-pulsar/src/main/java/com/baeldung/subscriptions/FailoverSubscriptionTest.java
deleted file mode 100644
index 545661e0c3..0000000000
--- a/apache-pulsar/src/main/java/com/baeldung/subscriptions/FailoverSubscriptionTest.java
+++ /dev/null
@@ -1,76 +0,0 @@
-package com.baeldung.subscriptions;
-
-import org.apache.pulsar.client.api.Consumer;
-import org.apache.pulsar.client.api.ConsumerBuilder;
-import org.apache.pulsar.client.api.Message;
-import org.apache.pulsar.client.api.MessageBuilder;
-import org.apache.pulsar.client.api.Producer;
-import org.apache.pulsar.client.api.PulsarClient;
-import org.apache.pulsar.client.api.PulsarClientException;
-import org.apache.pulsar.client.api.SubscriptionType;
-
-import java.util.stream.IntStream;
-
-public class FailoverSubscriptionTest {
- private static final String SERVICE_URL = "pulsar://localhost:6650";
- private static final String TOPIC_NAME = "failover-subscription-test-topic";
- private static final String SUBSCRIPTION_NAME = "test-subscription";
- private static final SubscriptionType SUBSCRIPTION_TYPE = SubscriptionType.Failover;
- private static final int NUM_MSGS = 10;
-
- public static void main(String[] args) throws PulsarClientException {
- PulsarClient client = PulsarClient.builder()
- .serviceUrl(SERVICE_URL)
- .build();
-
- Producer producer = client.newProducer()
- .topic(TOPIC_NAME)
- .create();
-
- ConsumerBuilder consumerBuilder = client.newConsumer()
- .topic(TOPIC_NAME)
- .subscriptionName(SUBSCRIPTION_NAME)
- .subscriptionType(SUBSCRIPTION_TYPE);
-
- Consumer mainConsumer = consumerBuilder
- .consumerName("consumer-a")
- .messageListener((consumer, msg) -> {
- System.out.println("Message received by main consumer");
-
- try {
- consumer.acknowledge(msg);
- } catch (PulsarClientException e) {
- System.out.println(e.getMessage());
- }
- })
- .subscribe();
-
- Consumer failoverConsumer = consumerBuilder
- .consumerName("consumer-b")
- .messageListener((consumer, msg) -> {
- System.out.println("Message received by failover consumer");
-
- try {
- consumer.acknowledge(msg);
- } catch (PulsarClientException e) {
- System.out.println(e.getMessage());
- }
- })
- .subscribe();
-
- IntStream.range(0, NUM_MSGS).forEach(i -> {
- Message msg = MessageBuilder.create()
- .setContent(String.format("message-%d", i).getBytes())
- .build();
- try {
- producer.send(msg);
-
- Thread.sleep(100);
-
- if (i > 5) mainConsumer.close();
- } catch (InterruptedException | PulsarClientException e) {
- System.out.println(e.getMessage());
- }
- });
- }
-}
diff --git a/apache-pulsar/src/main/java/com/baeldung/subscriptions/FailoverSubscriptionUnitTest.java b/apache-pulsar/src/main/java/com/baeldung/subscriptions/FailoverSubscriptionUnitTest.java
new file mode 100644
index 0000000000..1d13b4b83a
--- /dev/null
+++ b/apache-pulsar/src/main/java/com/baeldung/subscriptions/FailoverSubscriptionUnitTest.java
@@ -0,0 +1,76 @@
+package com.baeldung.subscriptions;
+
+import org.apache.pulsar.client.api.Consumer;
+import org.apache.pulsar.client.api.ConsumerBuilder;
+import org.apache.pulsar.client.api.Message;
+import org.apache.pulsar.client.api.MessageBuilder;
+import org.apache.pulsar.client.api.Producer;
+import org.apache.pulsar.client.api.PulsarClient;
+import org.apache.pulsar.client.api.PulsarClientException;
+import org.apache.pulsar.client.api.SubscriptionType;
+
+import java.util.stream.IntStream;
+
+public class FailoverSubscriptionUnitTest {
+ private static final String SERVICE_URL = "pulsar://localhost:6650";
+ private static final String TOPIC_NAME = "failover-subscription-test-topic";
+ private static final String SUBSCRIPTION_NAME = "test-subscription";
+ private static final SubscriptionType SUBSCRIPTION_TYPE = SubscriptionType.Failover;
+ private static final int NUM_MSGS = 10;
+
+ public static void main(String[] args) throws PulsarClientException {
+ PulsarClient client = PulsarClient.builder()
+ .serviceUrl(SERVICE_URL)
+ .build();
+
+ Producer producer = client.newProducer()
+ .topic(TOPIC_NAME)
+ .create();
+
+ ConsumerBuilder consumerBuilder = client.newConsumer()
+ .topic(TOPIC_NAME)
+ .subscriptionName(SUBSCRIPTION_NAME)
+ .subscriptionType(SUBSCRIPTION_TYPE);
+
+ Consumer mainConsumer = consumerBuilder
+ .consumerName("consumer-a")
+ .messageListener((consumer, msg) -> {
+ System.out.println("Message received by main consumer");
+
+ try {
+ consumer.acknowledge(msg);
+ } catch (PulsarClientException e) {
+ System.out.println(e.getMessage());
+ }
+ })
+ .subscribe();
+
+ Consumer failoverConsumer = consumerBuilder
+ .consumerName("consumer-b")
+ .messageListener((consumer, msg) -> {
+ System.out.println("Message received by failover consumer");
+
+ try {
+ consumer.acknowledge(msg);
+ } catch (PulsarClientException e) {
+ System.out.println(e.getMessage());
+ }
+ })
+ .subscribe();
+
+ IntStream.range(0, NUM_MSGS).forEach(i -> {
+ Message msg = MessageBuilder.create()
+ .setContent(String.format("message-%d", i).getBytes())
+ .build();
+ try {
+ producer.send(msg);
+
+ Thread.sleep(100);
+
+ if (i > 5) mainConsumer.close();
+ } catch (InterruptedException | PulsarClientException e) {
+ System.out.println(e.getMessage());
+ }
+ });
+ }
+}
diff --git a/apache-shiro/README.md b/apache-shiro/README.md
index bc3480b266..ed63c569da 100644
--- a/apache-shiro/README.md
+++ b/apache-shiro/README.md
@@ -1,2 +1,9 @@
-### Relevant articles
-- [Introduction to Apache Shiro](http://www.baeldung.com/apache-shiro)
+## Apache Shiro
+
+This module contains articles about Apache Shiro
+
+### Relevant articles:
+
+- [Introduction to Apache Shiro](https://www.baeldung.com/apache-shiro)
+- [Permissions-Based Access Control with Apache Shiro](https://www.baeldung.com/apache-shiro-access-control)
+
diff --git a/apache-shiro/pom.xml b/apache-shiro/pom.xml
index 644d70b30a..7f0d07589c 100644
--- a/apache-shiro/pom.xml
+++ b/apache-shiro/pom.xml
@@ -38,17 +38,6 @@
jcl-over-slf4j
runtime
-
- org.slf4j
- slf4j-log4j12
- runtime
-
-
- log4j
- log4j
- ${log4j-version}
- runtime
-
@@ -56,4 +45,4 @@
1.2.17
-
\ No newline at end of file
+
diff --git a/apache-shiro/src/main/java/com/baeldung/controllers/ShiroSpringController.java b/apache-shiro/src/main/java/com/baeldung/controllers/ShiroSpringController.java
index e6e72b2579..2713786d71 100644
--- a/apache-shiro/src/main/java/com/baeldung/controllers/ShiroSpringController.java
+++ b/apache-shiro/src/main/java/com/baeldung/controllers/ShiroSpringController.java
@@ -18,22 +18,17 @@ import javax.servlet.http.HttpServletRequest;
@Controller
public class ShiroSpringController {
-
-
@GetMapping("/")
public String index() {
return "index";
}
-
@RequestMapping( value = "/login", method = {RequestMethod.GET, RequestMethod.POST})
public String login(HttpServletRequest req, UserCredentials cred, RedirectAttributes attr) {
if(req.getMethod().equals(RequestMethod.GET.toString())) {
return "login";
- }
- else {
-
+ } else {
Subject subject = SecurityUtils.getSubject();
if(!subject.isAuthenticated()) {
diff --git a/apache-shiro/src/main/java/com/baeldung/shiro/permissions/custom/Main.java b/apache-shiro/src/main/java/com/baeldung/shiro/permissions/custom/Main.java
new file mode 100644
index 0000000000..a373122d6c
--- /dev/null
+++ b/apache-shiro/src/main/java/com/baeldung/shiro/permissions/custom/Main.java
@@ -0,0 +1,68 @@
+package com.baeldung.shiro.permissions.custom;
+
+import com.baeldung.MyCustomRealm;
+import org.apache.shiro.SecurityUtils;
+import org.apache.shiro.authc.*;
+import org.apache.shiro.config.Ini;
+import org.apache.shiro.mgt.DefaultSecurityManager;
+import org.apache.shiro.mgt.SecurityManager;
+import org.apache.shiro.realm.Realm;
+import org.apache.shiro.realm.text.IniRealm;
+import org.apache.shiro.session.Session;
+import org.apache.shiro.subject.Subject;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class Main {
+
+ private static final transient Logger log = LoggerFactory.getLogger(Main.class);
+
+ public static void main(String[] args) {
+
+ IniRealm realm = new IniRealm();
+ Ini ini = Ini.fromResourcePath(Main.class.getResource("/com/baeldung/shiro/permissions/custom/shiro.ini").getPath());
+ realm.setIni(ini);
+ realm.setPermissionResolver(new PathPermissionResolver());
+ realm.init();
+ SecurityManager securityManager = new DefaultSecurityManager(realm);
+
+ SecurityUtils.setSecurityManager(securityManager);
+ Subject currentUser = SecurityUtils.getSubject();
+
+ if (!currentUser.isAuthenticated()) {
+ UsernamePasswordToken token = new UsernamePasswordToken("paul.reader", "password4");
+ token.setRememberMe(true);
+ try {
+ currentUser.login(token);
+ } catch (UnknownAccountException uae) {
+ log.error("Username Not Found!", uae);
+ } catch (IncorrectCredentialsException ice) {
+ log.error("Invalid Credentials!", ice);
+ } catch (LockedAccountException lae) {
+ log.error("Your Account is Locked!", lae);
+ } catch (AuthenticationException ae) {
+ log.error("Unexpected Error!", ae);
+ }
+ }
+
+ log.info("User [" + currentUser.getPrincipal() + "] logged in successfully.");
+
+ if (currentUser.hasRole("admin")) {
+ log.info("Welcome Admin");
+ } else if(currentUser.hasRole("editor")) {
+ log.info("Welcome, Editor!");
+ } else if(currentUser.hasRole("author")) {
+ log.info("Welcome, Author");
+ } else {
+ log.info("Welcome, Guest");
+ }
+
+ if(currentUser.isPermitted("/articles/drafts/new-article")) {
+ log.info("You can access articles");
+ } else {
+ log.info("You cannot access articles!");
+ }
+ currentUser.logout();
+ }
+
+}
diff --git a/apache-shiro/src/main/java/com/baeldung/shiro/permissions/custom/PathPermission.java b/apache-shiro/src/main/java/com/baeldung/shiro/permissions/custom/PathPermission.java
new file mode 100644
index 0000000000..f7dfbda06a
--- /dev/null
+++ b/apache-shiro/src/main/java/com/baeldung/shiro/permissions/custom/PathPermission.java
@@ -0,0 +1,22 @@
+package com.baeldung.shiro.permissions.custom;
+
+import org.apache.shiro.authz.Permission;
+
+import java.nio.file.Path;
+
+public class PathPermission implements Permission {
+
+ private final Path path;
+
+ public PathPermission(Path path) {
+ this.path = path;
+ }
+
+ @Override
+ public boolean implies(Permission p) {
+ if(p instanceof PathPermission) {
+ return ((PathPermission) p).path.startsWith(path);
+ }
+ return false;
+ }
+}
diff --git a/apache-shiro/src/main/java/com/baeldung/shiro/permissions/custom/PathPermissionResolver.java b/apache-shiro/src/main/java/com/baeldung/shiro/permissions/custom/PathPermissionResolver.java
new file mode 100644
index 0000000000..4b60d2fbd4
--- /dev/null
+++ b/apache-shiro/src/main/java/com/baeldung/shiro/permissions/custom/PathPermissionResolver.java
@@ -0,0 +1,13 @@
+package com.baeldung.shiro.permissions.custom;
+
+import org.apache.shiro.authz.Permission;
+import org.apache.shiro.authz.permission.PermissionResolver;
+
+import java.nio.file.Paths;
+
+public class PathPermissionResolver implements PermissionResolver {
+ @Override
+ public Permission resolvePermission(String permissionString) {
+ return new PathPermission(Paths.get(permissionString));
+ }
+}
diff --git a/apache-shiro/src/main/resources/com/baeldung/shiro/permissions/custom/shiro.ini b/apache-shiro/src/main/resources/com/baeldung/shiro/permissions/custom/shiro.ini
new file mode 100644
index 0000000000..732ff8b60d
--- /dev/null
+++ b/apache-shiro/src/main/resources/com/baeldung/shiro/permissions/custom/shiro.ini
@@ -0,0 +1,10 @@
+[users]
+jane.admin = password, admin
+john.editor = password2, editor
+zoe.author = password3, author
+paul.reader = password4
+
+[roles]
+admin = /
+editor = /articles
+author = /articles/drafts
diff --git a/apache-solrj/README.md b/apache-solrj/README.md
index 7a32becb64..803db393e9 100644
--- a/apache-solrj/README.md
+++ b/apache-solrj/README.md
@@ -1,4 +1,7 @@
-## Apache Solrj Tutorials Project
+## Apache Solrj
-### Relevant Articles
-- [Guide to Solr in Java with Apache Solrj](http://www.baeldung.com/apache-solrj)
+This module contains articles about Apache Solrj
+
+### Relevant Articles:
+
+- [Guide to Solr in Java with Apache Solrj](https://www.baeldung.com/apache-solrj)
\ No newline at end of file
diff --git a/apache-solrj/pom.xml b/apache-solrj/pom.xml
index 1227fdca46..b19ceb1e48 100644
--- a/apache-solrj/pom.xml
+++ b/apache-solrj/pom.xml
@@ -1,7 +1,6 @@
4.0.0
- com.baeldung
apache-solrj
0.0.1-SNAPSHOT
apache-solrj
diff --git a/apache-spark/README.md b/apache-spark/README.md
index a4dce212b4..52313d66bf 100644
--- a/apache-spark/README.md
+++ b/apache-spark/README.md
@@ -1,4 +1,10 @@
-### Relevant articles
+## Apache Spark
-- [Introduction to Apache Spark](http://www.baeldung.com/apache-spark)
+This module contains articles about Apache Spark
+
+### Relevant articles:
+
+- [Introduction to Apache Spark](https://www.baeldung.com/apache-spark)
- [Building a Data Pipeline with Kafka, Spark Streaming and Cassandra](https://www.baeldung.com/kafka-spark-data-pipeline)
+- [Machine Learning with Spark MLlib](https://www.baeldung.com/spark-mlib-machine-learning)
+
diff --git a/apache-spark/data/iris.data b/apache-spark/data/iris.data
new file mode 100644
index 0000000000..396653cc98
--- /dev/null
+++ b/apache-spark/data/iris.data
@@ -0,0 +1,150 @@
+5.1,3.5,1.4,0.2,Iris-setosa
+4.9,3.0,1.4,0.2,Iris-setosa
+4.7,3.2,1.3,0.2,Iris-setosa
+4.6,3.1,1.5,0.2,Iris-setosa
+5.0,3.6,1.4,0.2,Iris-setosa
+5.4,3.9,1.7,0.4,Iris-setosa
+4.6,3.4,1.4,0.3,Iris-setosa
+5.0,3.4,1.5,0.2,Iris-setosa
+4.4,2.9,1.4,0.2,Iris-setosa
+4.9,3.1,1.5,0.1,Iris-setosa
+5.4,3.7,1.5,0.2,Iris-setosa
+4.8,3.4,1.6,0.2,Iris-setosa
+4.8,3.0,1.4,0.1,Iris-setosa
+4.3,3.0,1.1,0.1,Iris-setosa
+5.8,4.0,1.2,0.2,Iris-setosa
+5.7,4.4,1.5,0.4,Iris-setosa
+5.4,3.9,1.3,0.4,Iris-setosa
+5.1,3.5,1.4,0.3,Iris-setosa
+5.7,3.8,1.7,0.3,Iris-setosa
+5.1,3.8,1.5,0.3,Iris-setosa
+5.4,3.4,1.7,0.2,Iris-setosa
+5.1,3.7,1.5,0.4,Iris-setosa
+4.6,3.6,1.0,0.2,Iris-setosa
+5.1,3.3,1.7,0.5,Iris-setosa
+4.8,3.4,1.9,0.2,Iris-setosa
+5.0,3.0,1.6,0.2,Iris-setosa
+5.0,3.4,1.6,0.4,Iris-setosa
+5.2,3.5,1.5,0.2,Iris-setosa
+5.2,3.4,1.4,0.2,Iris-setosa
+4.7,3.2,1.6,0.2,Iris-setosa
+4.8,3.1,1.6,0.2,Iris-setosa
+5.4,3.4,1.5,0.4,Iris-setosa
+5.2,4.1,1.5,0.1,Iris-setosa
+5.5,4.2,1.4,0.2,Iris-setosa
+4.9,3.1,1.5,0.1,Iris-setosa
+5.0,3.2,1.2,0.2,Iris-setosa
+5.5,3.5,1.3,0.2,Iris-setosa
+4.9,3.1,1.5,0.1,Iris-setosa
+4.4,3.0,1.3,0.2,Iris-setosa
+5.1,3.4,1.5,0.2,Iris-setosa
+5.0,3.5,1.3,0.3,Iris-setosa
+4.5,2.3,1.3,0.3,Iris-setosa
+4.4,3.2,1.3,0.2,Iris-setosa
+5.0,3.5,1.6,0.6,Iris-setosa
+5.1,3.8,1.9,0.4,Iris-setosa
+4.8,3.0,1.4,0.3,Iris-setosa
+5.1,3.8,1.6,0.2,Iris-setosa
+4.6,3.2,1.4,0.2,Iris-setosa
+5.3,3.7,1.5,0.2,Iris-setosa
+5.0,3.3,1.4,0.2,Iris-setosa
+7.0,3.2,4.7,1.4,Iris-versicolor
+6.4,3.2,4.5,1.5,Iris-versicolor
+6.9,3.1,4.9,1.5,Iris-versicolor
+5.5,2.3,4.0,1.3,Iris-versicolor
+6.5,2.8,4.6,1.5,Iris-versicolor
+5.7,2.8,4.5,1.3,Iris-versicolor
+6.3,3.3,4.7,1.6,Iris-versicolor
+4.9,2.4,3.3,1.0,Iris-versicolor
+6.6,2.9,4.6,1.3,Iris-versicolor
+5.2,2.7,3.9,1.4,Iris-versicolor
+5.0,2.0,3.5,1.0,Iris-versicolor
+5.9,3.0,4.2,1.5,Iris-versicolor
+6.0,2.2,4.0,1.0,Iris-versicolor
+6.1,2.9,4.7,1.4,Iris-versicolor
+5.6,2.9,3.6,1.3,Iris-versicolor
+6.7,3.1,4.4,1.4,Iris-versicolor
+5.6,3.0,4.5,1.5,Iris-versicolor
+5.8,2.7,4.1,1.0,Iris-versicolor
+6.2,2.2,4.5,1.5,Iris-versicolor
+5.6,2.5,3.9,1.1,Iris-versicolor
+5.9,3.2,4.8,1.8,Iris-versicolor
+6.1,2.8,4.0,1.3,Iris-versicolor
+6.3,2.5,4.9,1.5,Iris-versicolor
+6.1,2.8,4.7,1.2,Iris-versicolor
+6.4,2.9,4.3,1.3,Iris-versicolor
+6.6,3.0,4.4,1.4,Iris-versicolor
+6.8,2.8,4.8,1.4,Iris-versicolor
+6.7,3.0,5.0,1.7,Iris-versicolor
+6.0,2.9,4.5,1.5,Iris-versicolor
+5.7,2.6,3.5,1.0,Iris-versicolor
+5.5,2.4,3.8,1.1,Iris-versicolor
+5.5,2.4,3.7,1.0,Iris-versicolor
+5.8,2.7,3.9,1.2,Iris-versicolor
+6.0,2.7,5.1,1.6,Iris-versicolor
+5.4,3.0,4.5,1.5,Iris-versicolor
+6.0,3.4,4.5,1.6,Iris-versicolor
+6.7,3.1,4.7,1.5,Iris-versicolor
+6.3,2.3,4.4,1.3,Iris-versicolor
+5.6,3.0,4.1,1.3,Iris-versicolor
+5.5,2.5,4.0,1.3,Iris-versicolor
+5.5,2.6,4.4,1.2,Iris-versicolor
+6.1,3.0,4.6,1.4,Iris-versicolor
+5.8,2.6,4.0,1.2,Iris-versicolor
+5.0,2.3,3.3,1.0,Iris-versicolor
+5.6,2.7,4.2,1.3,Iris-versicolor
+5.7,3.0,4.2,1.2,Iris-versicolor
+5.7,2.9,4.2,1.3,Iris-versicolor
+6.2,2.9,4.3,1.3,Iris-versicolor
+5.1,2.5,3.0,1.1,Iris-versicolor
+5.7,2.8,4.1,1.3,Iris-versicolor
+6.3,3.3,6.0,2.5,Iris-virginica
+5.8,2.7,5.1,1.9,Iris-virginica
+7.1,3.0,5.9,2.1,Iris-virginica
+6.3,2.9,5.6,1.8,Iris-virginica
+6.5,3.0,5.8,2.2,Iris-virginica
+7.6,3.0,6.6,2.1,Iris-virginica
+4.9,2.5,4.5,1.7,Iris-virginica
+7.3,2.9,6.3,1.8,Iris-virginica
+6.7,2.5,5.8,1.8,Iris-virginica
+7.2,3.6,6.1,2.5,Iris-virginica
+6.5,3.2,5.1,2.0,Iris-virginica
+6.4,2.7,5.3,1.9,Iris-virginica
+6.8,3.0,5.5,2.1,Iris-virginica
+5.7,2.5,5.0,2.0,Iris-virginica
+5.8,2.8,5.1,2.4,Iris-virginica
+6.4,3.2,5.3,2.3,Iris-virginica
+6.5,3.0,5.5,1.8,Iris-virginica
+7.7,3.8,6.7,2.2,Iris-virginica
+7.7,2.6,6.9,2.3,Iris-virginica
+6.0,2.2,5.0,1.5,Iris-virginica
+6.9,3.2,5.7,2.3,Iris-virginica
+5.6,2.8,4.9,2.0,Iris-virginica
+7.7,2.8,6.7,2.0,Iris-virginica
+6.3,2.7,4.9,1.8,Iris-virginica
+6.7,3.3,5.7,2.1,Iris-virginica
+7.2,3.2,6.0,1.8,Iris-virginica
+6.2,2.8,4.8,1.8,Iris-virginica
+6.1,3.0,4.9,1.8,Iris-virginica
+6.4,2.8,5.6,2.1,Iris-virginica
+7.2,3.0,5.8,1.6,Iris-virginica
+7.4,2.8,6.1,1.9,Iris-virginica
+7.9,3.8,6.4,2.0,Iris-virginica
+6.4,2.8,5.6,2.2,Iris-virginica
+6.3,2.8,5.1,1.5,Iris-virginica
+6.1,2.6,5.6,1.4,Iris-virginica
+7.7,3.0,6.1,2.3,Iris-virginica
+6.3,3.4,5.6,2.4,Iris-virginica
+6.4,3.1,5.5,1.8,Iris-virginica
+6.0,3.0,4.8,1.8,Iris-virginica
+6.9,3.1,5.4,2.1,Iris-virginica
+6.7,3.1,5.6,2.4,Iris-virginica
+6.9,3.1,5.1,2.3,Iris-virginica
+5.8,2.7,5.1,1.9,Iris-virginica
+6.8,3.2,5.9,2.3,Iris-virginica
+6.7,3.3,5.7,2.5,Iris-virginica
+6.7,3.0,5.2,2.3,Iris-virginica
+6.3,2.5,5.0,1.9,Iris-virginica
+6.5,3.0,5.2,2.0,Iris-virginica
+6.2,3.4,5.4,2.3,Iris-virginica
+5.9,3.0,5.1,1.8,Iris-virginica
\ No newline at end of file
diff --git a/apache-spark/model/logistic-regression/data/._SUCCESS.crc b/apache-spark/model/logistic-regression/data/._SUCCESS.crc
new file mode 100644
index 0000000000..3b7b044936
Binary files /dev/null and b/apache-spark/model/logistic-regression/data/._SUCCESS.crc differ
diff --git a/apache-spark/model/logistic-regression/data/.part-00000-f3a3ee61-f200-41ff-80d9-8e1edf399601-c000.snappy.parquet.crc b/apache-spark/model/logistic-regression/data/.part-00000-f3a3ee61-f200-41ff-80d9-8e1edf399601-c000.snappy.parquet.crc
new file mode 100644
index 0000000000..46311024cf
Binary files /dev/null and b/apache-spark/model/logistic-regression/data/.part-00000-f3a3ee61-f200-41ff-80d9-8e1edf399601-c000.snappy.parquet.crc differ
diff --git a/core-java-modules/core-java-lang/src/test/resources/correctFileNameWithoutProperExtension b/apache-spark/model/logistic-regression/data/_SUCCESS
similarity index 100%
rename from core-java-modules/core-java-lang/src/test/resources/correctFileNameWithoutProperExtension
rename to apache-spark/model/logistic-regression/data/_SUCCESS
diff --git a/apache-spark/model/logistic-regression/data/part-00000-f3a3ee61-f200-41ff-80d9-8e1edf399601-c000.snappy.parquet b/apache-spark/model/logistic-regression/data/part-00000-f3a3ee61-f200-41ff-80d9-8e1edf399601-c000.snappy.parquet
new file mode 100644
index 0000000000..07f442ef34
Binary files /dev/null and b/apache-spark/model/logistic-regression/data/part-00000-f3a3ee61-f200-41ff-80d9-8e1edf399601-c000.snappy.parquet differ
diff --git a/apache-spark/model/logistic-regression/metadata/._SUCCESS.crc b/apache-spark/model/logistic-regression/metadata/._SUCCESS.crc
new file mode 100644
index 0000000000..3b7b044936
Binary files /dev/null and b/apache-spark/model/logistic-regression/metadata/._SUCCESS.crc differ
diff --git a/apache-spark/model/logistic-regression/metadata/.part-00000.crc b/apache-spark/model/logistic-regression/metadata/.part-00000.crc
new file mode 100644
index 0000000000..35d5043fbe
Binary files /dev/null and b/apache-spark/model/logistic-regression/metadata/.part-00000.crc differ
diff --git a/libraries-data/src/main/resources/META-INF/BenchmarkList b/apache-spark/model/logistic-regression/metadata/_SUCCESS
similarity index 100%
rename from libraries-data/src/main/resources/META-INF/BenchmarkList
rename to apache-spark/model/logistic-regression/metadata/_SUCCESS
diff --git a/apache-spark/model/logistic-regression/metadata/part-00000 b/apache-spark/model/logistic-regression/metadata/part-00000
new file mode 100644
index 0000000000..e3f27fec10
--- /dev/null
+++ b/apache-spark/model/logistic-regression/metadata/part-00000
@@ -0,0 +1 @@
+{"class":"org.apache.spark.mllib.classification.LogisticRegressionModel","version":"1.0","numFeatures":4,"numClasses":3}
diff --git a/apache-spark/pom.xml b/apache-spark/pom.xml
index f0f002a7e9..41fd51d9fd 100644
--- a/apache-spark/pom.xml
+++ b/apache-spark/pom.xml
@@ -1,31 +1,31 @@
-
- 4.0.0
- com.baeldung
- apache-spark
- 1.0-SNAPSHOT
- apache-spark
- jar
- http://maven.apache.org
+
+ 4.0.0
+ apache-spark
+ 1.0-SNAPSHOT
+ apache-spark
+ jar
+ http://maven.apache.org
-
- com.baeldung
- parent-modules
- 1.0.0-SNAPSHOT
-
+
+ com.baeldung
+ parent-modules
+ 1.0.0-SNAPSHOT
+
-
-
- org.apache.spark
- spark-core_2.11
- ${org.apache.spark.spark-core.version}
- provided
-
+
- org.apache.spark
- spark-sql_2.11
- ${org.apache.spark.spark-sql.version}
- provided
+ org.apache.spark
+ spark-core_2.11
+ ${org.apache.spark.spark-core.version}
+ provided
+
+
+ org.apache.spark
+ spark-sql_2.11
+ ${org.apache.spark.spark-sql.version}
+ provided
org.apache.spark
@@ -33,6 +33,12 @@
${org.apache.spark.spark-streaming.version}
provided
+
+ org.apache.spark
+ spark-mllib_2.11
+ ${org.apache.spark.spark-mllib.version}
+ provided
+
org.apache.spark
spark-streaming-kafka-0-10_2.11
@@ -48,44 +54,37 @@
spark-cassandra-connector-java_2.11
${com.datastax.spark.spark-cassandra-connector-java.version}
-
+
+
-
-
- org.apache.maven.plugins
- maven-compiler-plugin
- ${maven-compiler-plugin.version}
-
- ${java.version}
- ${java.version}
-
-
-
- maven-assembly-plugin
-
-
- package
-
- single
-
-
-
-
-
- jar-with-dependencies
-
-
-
-
-
-
- 2.3.0
- 2.3.0
- 2.3.0
- 2.3.0
- 2.3.0
- 1.5.2
- 3.2
-
+
+
+ maven-assembly-plugin
+
+
+ package
+
+ single
+
+
+
+
+
+ jar-with-dependencies
+
+
+
+
+
+
+
+ 2.3.0
+ 2.3.0
+ 2.3.0
+ 2.3.0
+ 2.3.0
+ 2.3.0
+ 1.5.2
+
diff --git a/apache-spark/src/main/java/com/baeldung/ml/MachineLearningApp.java b/apache-spark/src/main/java/com/baeldung/ml/MachineLearningApp.java
new file mode 100644
index 0000000000..6094683031
--- /dev/null
+++ b/apache-spark/src/main/java/com/baeldung/ml/MachineLearningApp.java
@@ -0,0 +1,111 @@
+package com.baeldung.ml;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.log4j.Level;
+import org.apache.log4j.Logger;
+import org.apache.spark.SparkConf;
+import org.apache.spark.api.java.JavaPairRDD;
+import org.apache.spark.api.java.JavaRDD;
+import org.apache.spark.api.java.JavaSparkContext;
+import org.apache.spark.mllib.classification.LogisticRegressionModel;
+import org.apache.spark.mllib.classification.LogisticRegressionWithLBFGS;
+import org.apache.spark.mllib.evaluation.MulticlassMetrics;
+import org.apache.spark.mllib.linalg.Matrix;
+import org.apache.spark.mllib.linalg.Vector;
+import org.apache.spark.mllib.linalg.Vectors;
+import org.apache.spark.mllib.regression.LabeledPoint;
+import org.apache.spark.mllib.stat.MultivariateStatisticalSummary;
+import org.apache.spark.mllib.stat.Statistics;
+
+import scala.Tuple2;
+
+public class MachineLearningApp {
+
+ public static void main(String[] args) {
+
+ // 1. Setting the Spark Context
+ SparkConf conf = new SparkConf().setAppName("Main")
+ .setMaster("local[2]")
+ .set("spark.executor.memory", "3g")
+ .set("spark.driver.memory", "3g");
+ JavaSparkContext sc = new JavaSparkContext(conf);
+ Logger.getLogger("org")
+ .setLevel(Level.OFF);
+ Logger.getLogger("akka")
+ .setLevel(Level.OFF);
+
+ // 2. Loading the Data-set
+ String dataFile = "data\\iris.data";
+ JavaRDD data = sc.textFile(dataFile);
+
+ // 3. Exploratory Data Analysis
+ // 3.1. Creating Vector of Input Data
+ JavaRDD inputData = data.map(line -> {
+ String[] parts = line.split(",");
+ double[] v = new double[parts.length - 1];
+ for (int i = 0; i < parts.length - 1; i++) {
+ v[i] = Double.parseDouble(parts[i]);
+ }
+ return Vectors.dense(v);
+ });
+ // 3.2. Performing Statistical Analysis
+ MultivariateStatisticalSummary summary = Statistics.colStats(inputData.rdd());
+ System.out.println("Summary Mean:");
+ System.out.println(summary.mean());
+ System.out.println("Summary Variance:");
+ System.out.println(summary.variance());
+ System.out.println("Summary Non-zero:");
+ System.out.println(summary.numNonzeros());
+ // 3.3. Performing Correlation Analysis
+ Matrix correlMatrix = Statistics.corr(inputData.rdd(), "pearson");
+ System.out.println("Correlation Matrix:");
+ System.out.println(correlMatrix.toString());
+
+ // 4. Data Preparation
+ // 4.1. Creating Map for Textual Output Labels
+ Map map = new HashMap();
+ map.put("Iris-setosa", 0);
+ map.put("Iris-versicolor", 1);
+ map.put("Iris-virginica", 2);
+ // 4.2. Creating LabeledPoint of Input and Output Data
+ JavaRDD parsedData = data.map(line -> {
+ String[] parts = line.split(",");
+ double[] v = new double[parts.length - 1];
+ for (int i = 0; i < parts.length - 1; i++) {
+ v[i] = Double.parseDouble(parts[i]);
+ }
+ return new LabeledPoint(map.get(parts[parts.length - 1]), Vectors.dense(v));
+ });
+
+ // 5. Data Splitting into 80% Training and 20% Test Sets
+ JavaRDD[] splits = parsedData.randomSplit(new double[] { 0.8, 0.2 }, 11L);
+ JavaRDD trainingData = splits[0].cache();
+ JavaRDD testData = splits[1];
+
+ // 6. Modeling
+ // 6.1. Model Training
+ LogisticRegressionModel model = new LogisticRegressionWithLBFGS().setNumClasses(3)
+ .run(trainingData.rdd());
+ // 6.2. Model Evaluation
+ JavaPairRDD predictionAndLabels = testData.mapToPair(p -> new Tuple2<>(model.predict(p.features()), p.label()));
+ MulticlassMetrics metrics = new MulticlassMetrics(predictionAndLabels.rdd());
+ double accuracy = metrics.accuracy();
+ System.out.println("Model Accuracy on Test Data: " + accuracy);
+
+ // 7. Model Saving and Loading
+ // 7.1. Model Saving
+ model.save(sc.sc(), "model\\logistic-regression");
+ // 7.2. Model Loading
+ LogisticRegressionModel sameModel = LogisticRegressionModel.load(sc.sc(), "model\\logistic-regression");
+ // 7.3. Prediction on New Data
+ Vector newData = Vectors.dense(new double[] { 1, 1, 1, 1 });
+ double prediction = sameModel.predict(newData);
+ System.out.println("Model Prediction on New Data = " + prediction);
+
+ // 8. Clean-up
+ sc.close();
+ }
+
+}
diff --git a/apache-thrift/README.md b/apache-thrift/README.md
index d8b9195dcc..4508939de6 100644
--- a/apache-thrift/README.md
+++ b/apache-thrift/README.md
@@ -1,3 +1,7 @@
-## Relevant articles:
+## Apache Thrift
-- [Working with Apache Thrift](http://www.baeldung.com/apache-thrift)
+This module contains articles about Apache Thrift
+
+### Relevant articles:
+
+- [Working with Apache Thrift](https://www.baeldung.com/apache-thrift)
diff --git a/apache-tika/README.md b/apache-tika/README.md
index b92a7bebf1..690e55edc3 100644
--- a/apache-tika/README.md
+++ b/apache-tika/README.md
@@ -1,3 +1,7 @@
-## Relevant articles:
+## Apache Tika
-- [Content Analysis with Apache Tika](http://www.baeldung.com/apache-tika)
+This module contains articles about Apache Tika
+
+### Relevant articles:
+
+- [Content Analysis with Apache Tika](https://www.baeldung.com/apache-tika)
diff --git a/apache-velocity/README.md b/apache-velocity/README.md
index 53c67f847e..d539d79efc 100644
--- a/apache-velocity/README.md
+++ b/apache-velocity/README.md
@@ -1,3 +1,7 @@
-## Relevant articles:
+## Apache Velocity
-- [Introduction to Apache Velocity](http://www.baeldung.com/apache-velocity)
+This module contains articles about Apache Velocity
+
+### Relevant articles:
+
+- [Introduction to Apache Velocity](https://www.baeldung.com/apache-velocity)
diff --git a/apache-velocity/pom.xml b/apache-velocity/pom.xml
index 24ab0b861d..b370b11cc1 100644
--- a/apache-velocity/pom.xml
+++ b/apache-velocity/pom.xml
@@ -1,7 +1,6 @@
4.0.0
- com.baeldung
0.1-SNAPSHOT
apache-velocity
apache-velocity
diff --git a/apache-zookeeper/README.md b/apache-zookeeper/README.md
index 6bddcfd5a8..cda1cd6d73 100644
--- a/apache-zookeeper/README.md
+++ b/apache-zookeeper/README.md
@@ -1,3 +1,7 @@
-## Relevant articles:
+## Apache Zookeeper
-- [Getting Started with Java and Zookeeper](http://www.baeldung.com/java-zookeeper)
+This module contains articles about Apache Zookeeper
+
+### Relevant articles:
+
+- [Getting Started with Java and Zookeeper](https://www.baeldung.com/java-zookeeper)
diff --git a/asciidoctor/README.md b/asciidoctor/README.md
index 2124907e87..87b1ec833c 100644
--- a/asciidoctor/README.md
+++ b/asciidoctor/README.md
@@ -1,4 +1,8 @@
-### Relevant articles
+## Asciidoctor
-- [Generating a Book with Asciidoctor](http://www.baeldung.com/asciidoctor-book)
-- [Introduction to Asciidoctor in Java](http://www.baeldung.com/asciidoctor)
+This module contains articles about Asciidoctor
+
+### Relevant articles:
+
+- [Generating a Book with Asciidoctor](https://www.baeldung.com/asciidoctor-book)
+- [Introduction to Asciidoctor in Java](https://www.baeldung.com/asciidoctor)
diff --git a/asm/README.md b/asm/README.md
index 50d9c34324..e10cdc45bd 100644
--- a/asm/README.md
+++ b/asm/README.md
@@ -1,3 +1,7 @@
+## ASM
+
+This module contains articles about ASM
+
### Relevant Articles:
-- [A Guide to Java Bytecode Manipulation with ASM](http://www.baeldung.com/java-asm)
+- [A Guide to Java Bytecode Manipulation with ASM](https://www.baeldung.com/java-asm)
diff --git a/atomix/README.md b/atomix/README.md
index fb22eec8dc..217fced82a 100644
--- a/atomix/README.md
+++ b/atomix/README.md
@@ -1,3 +1,7 @@
-## Relevant articles:
+## Atomix
-- [Introduction to Atomix](http://www.baeldung.com/atomix)
+This module contains articles about Atomix
+
+### Relevant articles:
+
+- [Introduction to Atomix](https://www.baeldung.com/atomix)
diff --git a/autovalue/README.md b/autovalue/README.md
deleted file mode 100644
index c6a08359ef..0000000000
--- a/autovalue/README.md
+++ /dev/null
@@ -1,3 +0,0 @@
-### Relevant Articles:
-- [Introduction to AutoValue](http://www.baeldung.com/introduction-to-autovalue)
-- [Introduction to AutoFactory](http://www.baeldung.com/autofactory)
diff --git a/autovalue/pom.xml b/autovalue/pom.xml
deleted file mode 100644
index a10e8ef055..0000000000
--- a/autovalue/pom.xml
+++ /dev/null
@@ -1,53 +0,0 @@
-
- 4.0.0
- com.baeldung
- autovalue
- 1.0
- autovalue
-
-
- com.baeldung
- parent-modules
- 1.0.0-SNAPSHOT
-
-
-
-
- com.google.auto.value
- auto-value
- ${auto-value.version}
-
-
- com.google.auto.factory
- auto-factory
- ${auto-factory.version}
-
-
- com.google.guava
- guava
-
-
-
-
- com.google.auto.service
- auto-service
- ${auto-service.version}
- true
-
-
-
- com.google.inject
- guice
- ${guice.version}
-
-
-
-
- 1.3
- 1.0-beta5
- 1.0-rc5
- 4.2.0
-
-
-
diff --git a/aws-lambda/README.md b/aws-lambda/README.md
index 921b699bdd..2fbdaace10 100644
--- a/aws-lambda/README.md
+++ b/aws-lambda/README.md
@@ -1,3 +1,7 @@
+## AWS Lambda
+
+This module contains articles about AWS Lambda
+
### Relevant Articles:
-- [Using AWS Lambda with API Gateway](http://www.baeldung.com/aws-lambda-api-gateway)
-- [Introduction to AWS Serverless Application Model](http://www.baeldung.com/aws-serverless)
+- [Using AWS Lambda with API Gateway](https://www.baeldung.com/aws-lambda-api-gateway)
+- [Introduction to AWS Serverless Application Model](https://www.baeldung.com/aws-serverless)
diff --git a/aws-lambda/pom.xml b/aws-lambda/pom.xml
index c799718e61..1f3c31f87c 100644
--- a/aws-lambda/pom.xml
+++ b/aws-lambda/pom.xml
@@ -2,7 +2,6 @@
4.0.0
- com.baeldung
aws-lambda
0.1.0-SNAPSHOT
aws-lambda
@@ -88,7 +87,6 @@
1.1.1
- 20180130
2.5
1.3.0
1.2.0
diff --git a/aws/README.md b/aws/README.md
index d14ea8a75e..9e4ca8b699 100644
--- a/aws/README.md
+++ b/aws/README.md
@@ -1,11 +1,15 @@
+## AWS
+
+This module contains articles about AWS
+
### Relevant articles
-- [AWS Lambda Using DynamoDB With Java](http://www.baeldung.com/aws-lambda-dynamodb-java)
-- [AWS S3 with Java](http://www.baeldung.com/aws-s3-java)
-- [AWS Lambda With Java](http://www.baeldung.com/java-aws-lambda)
-- [Managing EC2 Instances in Java](http://www.baeldung.com/ec2-java)
-- [Multipart Uploads in Amazon S3 with Java](http://www.baeldung.com/aws-s3-multipart-upload)
-- [Integration Testing with a Local DynamoDB Instance](http://www.baeldung.com/dynamodb-local-integration-tests)
-- [Using the JetS3t Java Client With Amazon S3](http://www.baeldung.com/jets3t-amazon-s3)
-- [Managing Amazon SQS Queues in Java](http://www.baeldung.com/aws-queues-java)
+- [AWS Lambda Using DynamoDB With Java](https://www.baeldung.com/aws-lambda-dynamodb-java)
+- [AWS S3 with Java](https://www.baeldung.com/aws-s3-java)
+- [AWS Lambda With Java](https://www.baeldung.com/java-aws-lambda)
+- [Managing EC2 Instances in Java](https://www.baeldung.com/ec2-java)
+- [Multipart Uploads in Amazon S3 with Java](https://www.baeldung.com/aws-s3-multipart-upload)
+- [Integration Testing with a Local DynamoDB Instance](https://www.baeldung.com/dynamodb-local-integration-tests)
+- [Using the JetS3t Java Client With Amazon S3](https://www.baeldung.com/jets3t-amazon-s3)
+- [Managing Amazon SQS Queues in Java](https://www.baeldung.com/aws-queues-java)
- [Guide to AWS Aurora RDS with Java](https://www.baeldung.com/aws-aurora-rds-java)
diff --git a/aws/pom.xml b/aws/pom.xml
index 560f9145f9..45a30f96ba 100644
--- a/aws/pom.xml
+++ b/aws/pom.xml
@@ -1,7 +1,6 @@
4.0.0
- com.baeldung
aws
0.1.0-SNAPSHOT
aws
@@ -112,7 +111,6 @@
- 2.5
1.3.0
1.1.0
2.8.0
diff --git a/axon/README.md b/axon/README.md
index f1ae5d00d8..069102fc3a 100644
--- a/axon/README.md
+++ b/axon/README.md
@@ -1,3 +1,7 @@
+## Axon
+
+This module contains articles about Axon
+
### Relevant articles
-- [A Guide to the Axon Framework](http://www.baeldung.com/axon-cqrs-event-sourcing)
+- [A Guide to the Axon Framework](https://www.baeldung.com/axon-cqrs-event-sourcing)
diff --git a/axon/pom.xml b/axon/pom.xml
index 2b9ac1fcdd..21e21f0ef2 100644
--- a/axon/pom.xml
+++ b/axon/pom.xml
@@ -18,12 +18,6 @@
org.axonframework
axon-spring-boot-starter
${axon.version}
-
-
- org.axonframework
- axon-server-connector
-
-
@@ -36,7 +30,6 @@
org.springframework.boot
spring-boot-autoconfigure
- ${spring-boot.version}
compile
@@ -58,7 +51,7 @@
- 4.0.3
+ 4.1.2
\ No newline at end of file
diff --git a/axon/src/main/java/com/baeldung/axon/commandmodel/OrderAggregate.java b/axon/src/main/java/com/baeldung/axon/commandmodel/OrderAggregate.java
index b37b2fdd66..4ef02e6b54 100644
--- a/axon/src/main/java/com/baeldung/axon/commandmodel/OrderAggregate.java
+++ b/axon/src/main/java/com/baeldung/axon/commandmodel/OrderAggregate.java
@@ -13,6 +13,7 @@ import com.baeldung.axon.coreapi.commands.ShipOrderCommand;
import com.baeldung.axon.coreapi.events.OrderConfirmedEvent;
import com.baeldung.axon.coreapi.events.OrderPlacedEvent;
import com.baeldung.axon.coreapi.events.OrderShippedEvent;
+import com.baeldung.axon.coreapi.exceptions.UnconfirmedOrderException;
@Aggregate
public class OrderAggregate {
@@ -34,7 +35,7 @@ public class OrderAggregate {
@CommandHandler
public void handle(ShipOrderCommand command) {
if (!orderConfirmed) {
- throw new IllegalStateException("Cannot ship an order which has not been confirmed yet.");
+ throw new UnconfirmedOrderException();
}
apply(new OrderShippedEvent(orderId));
@@ -43,12 +44,12 @@ public class OrderAggregate {
@EventSourcingHandler
public void on(OrderPlacedEvent event) {
this.orderId = event.getOrderId();
- orderConfirmed = false;
+ this.orderConfirmed = false;
}
@EventSourcingHandler
public void on(OrderConfirmedEvent event) {
- orderConfirmed = true;
+ this.orderConfirmed = true;
}
protected OrderAggregate() {
diff --git a/axon/src/main/java/com/baeldung/axon/coreapi/exceptions/UnconfirmedOrderException.java b/axon/src/main/java/com/baeldung/axon/coreapi/exceptions/UnconfirmedOrderException.java
new file mode 100644
index 0000000000..1873bc6893
--- /dev/null
+++ b/axon/src/main/java/com/baeldung/axon/coreapi/exceptions/UnconfirmedOrderException.java
@@ -0,0 +1,8 @@
+package com.baeldung.axon.coreapi.exceptions;
+
+public class UnconfirmedOrderException extends IllegalStateException {
+
+ public UnconfirmedOrderException() {
+ super("Cannot ship an order which has not been confirmed yet.");
+ }
+}
diff --git a/axon/src/main/java/com/baeldung/axon/querymodel/OrderedProductsEventHandler.java b/axon/src/main/java/com/baeldung/axon/querymodel/OrderedProductsEventHandler.java
index d4cf3d999b..a37f0111ed 100644
--- a/axon/src/main/java/com/baeldung/axon/querymodel/OrderedProductsEventHandler.java
+++ b/axon/src/main/java/com/baeldung/axon/querymodel/OrderedProductsEventHandler.java
@@ -5,6 +5,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import org.axonframework.config.ProcessingGroup;
import org.axonframework.eventhandling.EventHandler;
import org.axonframework.queryhandling.QueryHandler;
import org.springframework.stereotype.Service;
@@ -16,6 +17,7 @@ import com.baeldung.axon.coreapi.queries.FindAllOrderedProductsQuery;
import com.baeldung.axon.coreapi.queries.OrderedProduct;
@Service
+@ProcessingGroup("ordered-products")
public class OrderedProductsEventHandler {
private final Map orderedProducts = new HashMap<>();
diff --git a/axon/src/main/resources/application.properties b/axon/src/main/resources/application.properties
new file mode 100644
index 0000000000..7c51eb8e1e
--- /dev/null
+++ b/axon/src/main/resources/application.properties
@@ -0,0 +1 @@
+spring.application.name=Order Management Service
\ No newline at end of file
diff --git a/axon/src/test/java/com/baeldung/axon/commandmodel/OrderAggregateUnitTest.java b/axon/src/test/java/com/baeldung/axon/commandmodel/OrderAggregateUnitTest.java
index 9beedbaa19..aaefe49fb1 100644
--- a/axon/src/test/java/com/baeldung/axon/commandmodel/OrderAggregateUnitTest.java
+++ b/axon/src/test/java/com/baeldung/axon/commandmodel/OrderAggregateUnitTest.java
@@ -2,6 +2,7 @@ package com.baeldung.axon.commandmodel;
import java.util.UUID;
+import com.baeldung.axon.coreapi.exceptions.UnconfirmedOrderException;
import org.axonframework.test.aggregate.AggregateTestFixture;
import org.axonframework.test.aggregate.FixtureConfiguration;
import org.junit.*;
@@ -41,12 +42,12 @@ public class OrderAggregateUnitTest {
}
@Test
- public void givenOrderPlacedEvent_whenShipOrderCommand_thenShouldThrowIllegalStateException() {
+ public void givenOrderPlacedEvent_whenShipOrderCommand_thenShouldThrowUnconfirmedOrderException() {
String orderId = UUID.randomUUID().toString();
String product = "Deluxe Chair";
fixture.given(new OrderPlacedEvent(orderId, product))
.when(new ShipOrderCommand(orderId))
- .expectException(IllegalStateException.class);
+ .expectException(UnconfirmedOrderException.class);
}
@Test
diff --git a/azure/README.md b/azure/README.md
index ae8c443660..4da8481502 100644
--- a/azure/README.md
+++ b/azure/README.md
@@ -1,4 +1,8 @@
+## Azure
+
+This module contains articles about Azure
+
### Relevant Articles:
-- [Deploy a Spring Boot App to Azure](http://www.baeldung.com/spring-boot-azure)
+- [Deploy a Spring Boot App to Azure](https://www.baeldung.com/spring-boot-azure)
diff --git a/azure/pom.xml b/azure/pom.xml
index 270b3e4829..3d9c296748 100644
--- a/azure/pom.xml
+++ b/azure/pom.xml
@@ -2,7 +2,6 @@
4.0.0
- com.baeldung
azure
0.1
azure
diff --git a/bazel/README.md b/bazel/README.md
new file mode 100644
index 0000000000..af4f901f49
--- /dev/null
+++ b/bazel/README.md
@@ -0,0 +1,7 @@
+## Bazel
+
+This module contains articles about Bazel
+
+### Relevant Articles:
+
+- [Building Java Applications with Bazel](https://www.baeldung.com/bazel-build-tool)
diff --git a/bazel/WORKSPACE b/bazel/WORKSPACE
new file mode 100644
index 0000000000..415aa398f9
--- /dev/null
+++ b/bazel/WORKSPACE
@@ -0,0 +1,31 @@
+
+load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_jar")
+load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
+
+
+RULES_JVM_EXTERNAL_TAG = "2.0.1"
+RULES_JVM_EXTERNAL_SHA = "55e8d3951647ae3dffde22b4f7f8dee11b3f70f3f89424713debd7076197eaca"
+
+http_archive(
+ name = "rules_jvm_external",
+ strip_prefix = "rules_jvm_external-%s" % RULES_JVM_EXTERNAL_TAG,
+ sha256 = RULES_JVM_EXTERNAL_SHA,
+ url = "https://github.com/bazelbuild/rules_jvm_external/archive/%s.zip" % RULES_JVM_EXTERNAL_TAG,
+)
+
+load("@rules_jvm_external//:defs.bzl", "maven_install")
+
+maven_install(
+ artifacts = [
+ "org.apache.commons:commons-lang3:3.9"
+ ],
+ repositories = [
+ "https://repo1.maven.org/maven2",
+ ]
+)
+
+http_jar (
+ name = "apache-commons-lang",
+ url = "https://repo1.maven.org/maven2/org/apache/commons/commons-lang3/3.9/commons-lang3-3.9.jar"
+)
+
diff --git a/bazel/bazelapp/BUILD b/bazel/bazelapp/BUILD
new file mode 100644
index 0000000000..1de95aada5
--- /dev/null
+++ b/bazel/bazelapp/BUILD
@@ -0,0 +1,7 @@
+
+java_binary (
+ name = "BazelApp",
+ srcs = glob(["src/main/java/com/baeldung/*.java"]),
+ main_class = "com.baeldung.BazelApp",
+ deps = ["//bazelgreeting:greeter", "@maven//:org_apache_commons_commons_lang3"]
+)
diff --git a/bazel/bazelapp/pom.xml b/bazel/bazelapp/pom.xml
new file mode 100644
index 0000000000..e56cc06ef4
--- /dev/null
+++ b/bazel/bazelapp/pom.xml
@@ -0,0 +1,32 @@
+
+
+ 4.0.0
+ bazelapp
+ bazelapp
+
+
+ bazel
+ com.baeldung
+ 1.0.0-SNAPSHOT
+
+
+
+
+ com.baeldung
+ bazelgreeting
+ ${bazelgreeting.version}
+
+
+ org.apache.commons
+ commons-lang3
+ ${commons-lang3.version}
+
+
+
+
+ 1.0.0-SNAPSHOT
+
+
+
\ No newline at end of file
diff --git a/bazel/bazelapp/src/main/java/com/baeldung/BazelApp.java b/bazel/bazelapp/src/main/java/com/baeldung/BazelApp.java
new file mode 100644
index 0000000000..e92d85018b
--- /dev/null
+++ b/bazel/bazelapp/src/main/java/com/baeldung/BazelApp.java
@@ -0,0 +1,15 @@
+package com.baeldung;
+
+import com.baeldung.Greetings;
+import org.apache.commons.lang3.StringUtils;
+
+public class BazelApp {
+
+ public static void main(String ... args) {
+ Greetings greetings = new Greetings();
+
+ System.out.println(greetings.greet("Bazel"));
+
+ System.out.println(StringUtils.lowerCase("Bazel"));
+ }
+}
diff --git a/bazel/bazelgreeting/BUILD b/bazel/bazelgreeting/BUILD
new file mode 100644
index 0000000000..6cd8bc13f7
--- /dev/null
+++ b/bazel/bazelgreeting/BUILD
@@ -0,0 +1,6 @@
+
+java_library (
+ name = "greeter",
+ srcs = glob(["src/main/java/com/baeldung/*.java"]),
+ visibility = ["//bazelapp:__pkg__"]
+)
diff --git a/bazel/bazelgreeting/pom.xml b/bazel/bazelgreeting/pom.xml
new file mode 100644
index 0000000000..e688a55bd5
--- /dev/null
+++ b/bazel/bazelgreeting/pom.xml
@@ -0,0 +1,16 @@
+
+
+ 4.0.0
+ bazelgreeting
+ bazelgreeting
+
+
+ bazel
+ com.baeldung
+ 1.0.0-SNAPSHOT
+
+
+
+
\ No newline at end of file
diff --git a/bazel/bazelgreeting/src/main/java/com/baeldung/Greetings.java b/bazel/bazelgreeting/src/main/java/com/baeldung/Greetings.java
new file mode 100644
index 0000000000..0e8fae7fbe
--- /dev/null
+++ b/bazel/bazelgreeting/src/main/java/com/baeldung/Greetings.java
@@ -0,0 +1,8 @@
+package com.baeldung;
+
+public class Greetings {
+
+ public String greet(String name) {
+ return "Hello ".concat(name);
+ }
+}
diff --git a/bazel/pom.xml b/bazel/pom.xml
new file mode 100644
index 0000000000..cb784ec6e6
--- /dev/null
+++ b/bazel/pom.xml
@@ -0,0 +1,22 @@
+
+
+ 4.0.0
+ bazel
+ pom
+ bazel
+
+
+ parent-modules
+ com.baeldung
+ 1.0.0-SNAPSHOT
+
+
+
+ bazelgreeting
+ bazelapp
+
+
+
+
\ No newline at end of file
diff --git a/blade/README.md b/blade/README.md
index 1f2a00ed3f..202494330f 100644
--- a/blade/README.md
+++ b/blade/README.md
@@ -1,5 +1,5 @@
### Relevant Articles:
-- [Blade – A Complete Guidebook](http://www.baeldung.com/blade)
+- [Blade – A Complete Guidebook](https://www.baeldung.com/blade)
Run Integration Tests with `mvn integration-test`
diff --git a/blade/pom.xml b/blade/pom.xml
index 37615bed01..6f8a78852d 100644
--- a/blade/pom.xml
+++ b/blade/pom.xml
@@ -3,19 +3,14 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
4.0.0
-
- com.baeldung
blade
- 1.0.0-SNAPSHOT
blade
-
-
-
-
-
-
-
+
+ com.baeldung
+ parent-modules
+ 1.0.0-SNAPSHOT
+
@@ -45,12 +40,6 @@
-
- junit
- junit
- ${junit.version}
- test
-
org.assertj
assertj-core
@@ -79,20 +68,6 @@
sample-blade-app
-
-
- org.apache.maven.plugins
- maven-surefire-plugin
- ${maven-surefire-plugin.version}
-
- 3
- true
-
- **/*LiveTest.java
-
-
-
-
org.apache.maven.plugins
maven-failsafe-plugin
@@ -172,34 +147,19 @@
-
- org.apache.maven.plugins
- maven-compiler-plugin
- ${maven-compiler-plugin.version}
-
- ${maven.compiler.source}
- ${maven.compiler.target}
- UTF-8
-
-
- 1.8
- 1.8
2.0.14.RELEASE
4.2.1
3.8.1
1.18.4
- 4.12
4.5.6
4.5.6
4.4.10
3.11.1
3.0.0-M3
0.7
- 2.21.0
- 3.7.0
diff --git a/bootique/README.md b/bootique/README.md
index 2ef898fcf7..0d4076201e 100644
--- a/bootique/README.md
+++ b/bootique/README.md
@@ -1,2 +1,6 @@
+## Bootique
+
+This module contains articles about Bootique
+
### Relevant Articles:
-- [Introduction to Bootique](http://www.baeldung.com/bootique)
+- [Introduction to Bootique](https://www.baeldung.com/bootique)
diff --git a/bootique/pom.xml b/bootique/pom.xml
index 4ae8703074..7601321b99 100644
--- a/bootique/pom.xml
+++ b/bootique/pom.xml
@@ -42,12 +42,6 @@
bootique-test
test
-
- junit
- junit
- ${junit.version}
- test
-
@@ -65,7 +59,6 @@
com.baeldung.bootique.App
0.23
-
2.4.3
diff --git a/cas/README.md b/cas/README.md
new file mode 100644
index 0000000000..16775a8a02
--- /dev/null
+++ b/cas/README.md
@@ -0,0 +1,7 @@
+## CAS
+
+This module contains articles about the Central Authentication Service (CAS)
+
+### Relevant Articles:
+
+- [CAS SSO With Spring Security](baeldung.com/spring-security-cas-sso)
\ No newline at end of file
diff --git a/cas/cas-server/pom.xml b/cas/cas-server/pom.xml
index 98f5f10493..8a1e602114 100644
--- a/cas/cas-server/pom.xml
+++ b/cas/cas-server/pom.xml
@@ -198,7 +198,6 @@
0.0.4
2.6
- 3.3
0.3.0
1.1.0
diff --git a/cdi/README.md b/cdi/README.md
index bfb635be9e..13169698a2 100644
--- a/cdi/README.md
+++ b/cdi/README.md
@@ -1,5 +1,9 @@
+## CDI
+
+This module contains articles about Contexts and Dependency Injection (CDI)
+
### Relevant Articles:
-- [CDI Interceptor vs Spring AspectJ](http://www.baeldung.com/cdi-interceptor-vs-spring-aspectj)
-- [An Introduction to CDI (Contexts and Dependency Injection) in Java](http://www.baeldung.com/java-ee-cdi)
+- [CDI Interceptor vs Spring AspectJ](https://www.baeldung.com/cdi-interceptor-vs-spring-aspectj)
+- [An Introduction to CDI (Contexts and Dependency Injection) in Java](https://www.baeldung.com/java-ee-cdi)
- [Introduction to the Event Notification Model in CDI 2.0](https://www.baeldung.com/cdi-event-notification)
diff --git a/cdi/pom.xml b/cdi/pom.xml
index c98ad57495..809622a5db 100644
--- a/cdi/pom.xml
+++ b/cdi/pom.xml
@@ -36,12 +36,6 @@
${assertj-core.version}
test
-
- junit
- junit
- ${junit.version}
- test
-
org.springframework
spring-context
diff --git a/cdi/src/main/java/com/baeldung/cdi2observers/application/BootstrappingApplication.java b/cdi/src/main/java/com/baeldung/cdi2observers/application/BootstrappingApplication.java
index 4896408502..dc0bdeb951 100644
--- a/cdi/src/main/java/com/baeldung/cdi2observers/application/BootstrappingApplication.java
+++ b/cdi/src/main/java/com/baeldung/cdi2observers/application/BootstrappingApplication.java
@@ -1,15 +1,16 @@
-package com.baeldung.cdi.cdi2observers.application;
-
-import com.baeldung.cdi.cdi2observers.events.ExampleEvent;
-import javax.enterprise.inject.se.SeContainer;
-import javax.enterprise.inject.se.SeContainerInitializer;
-
-public class BootstrappingApplication {
-
- public static void main(String... args) {
- SeContainerInitializer containerInitializer = SeContainerInitializer.newInstance();
- try (SeContainer container = containerInitializer.initialize()) {
- container.getBeanManager().fireEvent(new ExampleEvent("Welcome to Baeldung!"));
- }
- }
-}
+package com.baeldung.cdi2observers.application;
+
+import com.baeldung.cdi2observers.events.ExampleEvent;
+
+import javax.enterprise.inject.se.SeContainer;
+import javax.enterprise.inject.se.SeContainerInitializer;
+
+public class BootstrappingApplication {
+
+ public static void main(String... args) {
+ SeContainerInitializer containerInitializer = SeContainerInitializer.newInstance();
+ try (SeContainer container = containerInitializer.initialize()) {
+ container.getBeanManager().fireEvent(new ExampleEvent("Welcome to Baeldung!"));
+ }
+ }
+}
diff --git a/cdi/src/main/java/com/baeldung/cdi2observers/events/ExampleEvent.java b/cdi/src/main/java/com/baeldung/cdi2observers/events/ExampleEvent.java
index a2329d2ef1..9adfad4d39 100644
--- a/cdi/src/main/java/com/baeldung/cdi2observers/events/ExampleEvent.java
+++ b/cdi/src/main/java/com/baeldung/cdi2observers/events/ExampleEvent.java
@@ -1,14 +1,14 @@
-package com.baeldung.cdi.cdi2observers.events;
-
-public class ExampleEvent {
-
- private final String eventMessage;
-
- public ExampleEvent(String eventMessage) {
- this.eventMessage = eventMessage;
- }
-
- public String getEventMessage() {
- return eventMessage;
- }
-}
+package com.baeldung.cdi2observers.events;
+
+public class ExampleEvent {
+
+ private final String eventMessage;
+
+ public ExampleEvent(String eventMessage) {
+ this.eventMessage = eventMessage;
+ }
+
+ public String getEventMessage() {
+ return eventMessage;
+ }
+}
diff --git a/cdi/src/main/java/com/baeldung/cdi2observers/events/ExampleEventSource.java b/cdi/src/main/java/com/baeldung/cdi2observers/events/ExampleEventSource.java
index f37030778a..5a0aa0b5e3 100644
--- a/cdi/src/main/java/com/baeldung/cdi2observers/events/ExampleEventSource.java
+++ b/cdi/src/main/java/com/baeldung/cdi2observers/events/ExampleEventSource.java
@@ -1,14 +1,14 @@
-package com.baeldung.cdi.cdi2observers.events;
-
-import javax.enterprise.event.Event;
-import javax.inject.Inject;
-
-public class ExampleEventSource {
-
- @Inject
- Event exampleEvent;
-
- public void fireEvent() {
- exampleEvent.fireAsync(new ExampleEvent("Welcome to Baeldung!"));
- }
-}
+package com.baeldung.cdi2observers.events;
+
+import javax.enterprise.event.Event;
+import javax.inject.Inject;
+
+public class ExampleEventSource {
+
+ @Inject
+ Event exampleEvent;
+
+ public void fireEvent() {
+ exampleEvent.fireAsync(new ExampleEvent("Welcome to Baeldung!"));
+ }
+}
diff --git a/cdi/src/main/java/com/baeldung/cdi2observers/observers/AnotherExampleEventObserver.java b/cdi/src/main/java/com/baeldung/cdi2observers/observers/AnotherExampleEventObserver.java
index 34520c2b3d..3af48af13f 100644
--- a/cdi/src/main/java/com/baeldung/cdi2observers/observers/AnotherExampleEventObserver.java
+++ b/cdi/src/main/java/com/baeldung/cdi2observers/observers/AnotherExampleEventObserver.java
@@ -1,12 +1,13 @@
-package com.baeldung.cdi.cdi2observers.observers;
-
-import com.baeldung.cdi.cdi2observers.events.ExampleEvent;
-import javax.annotation.Priority;
-import javax.enterprise.event.Observes;
-
-public class AnotherExampleEventObserver {
-
- public String onEvent(@Observes @Priority(2) ExampleEvent event) {
- return event.getEventMessage();
- }
-}
+package com.baeldung.cdi2observers.observers;
+
+import com.baeldung.cdi2observers.events.ExampleEvent;
+
+import javax.annotation.Priority;
+import javax.enterprise.event.Observes;
+
+public class AnotherExampleEventObserver {
+
+ public String onEvent(@Observes @Priority(2) ExampleEvent event) {
+ return event.getEventMessage();
+ }
+}
diff --git a/cdi/src/main/java/com/baeldung/cdi2observers/observers/ExampleEventObserver.java b/cdi/src/main/java/com/baeldung/cdi2observers/observers/ExampleEventObserver.java
index b3522b2ad0..33fdc43bbb 100644
--- a/cdi/src/main/java/com/baeldung/cdi2observers/observers/ExampleEventObserver.java
+++ b/cdi/src/main/java/com/baeldung/cdi2observers/observers/ExampleEventObserver.java
@@ -1,13 +1,13 @@
-package com.baeldung.cdi.cdi2observers.observers;
-
-import com.baeldung.cdi.cdi2observers.events.ExampleEvent;
-import com.baeldung.cdi.cdi2observers.services.TextService;
-import javax.annotation.Priority;
-import javax.enterprise.event.Observes;
-
-public class ExampleEventObserver {
-
- public String onEvent(@Observes @Priority(1) ExampleEvent event, TextService textService) {
- return textService.parseText(event.getEventMessage());
- }
-}
+package com.baeldung.cdi2observers.observers;
+
+import com.baeldung.cdi2observers.events.ExampleEvent;
+import com.baeldung.cdi2observers.services.TextService;
+import javax.annotation.Priority;
+import javax.enterprise.event.Observes;
+
+public class ExampleEventObserver {
+
+ public String onEvent(@Observes @Priority(1) ExampleEvent event, TextService textService) {
+ return textService.parseText(event.getEventMessage());
+ }
+}
diff --git a/cdi/src/main/java/com/baeldung/cdi2observers/services/TextService.java b/cdi/src/main/java/com/baeldung/cdi2observers/services/TextService.java
index 47788a0657..eabe031223 100644
--- a/cdi/src/main/java/com/baeldung/cdi2observers/services/TextService.java
+++ b/cdi/src/main/java/com/baeldung/cdi2observers/services/TextService.java
@@ -1,8 +1,8 @@
-package com.baeldung.cdi.cdi2observers.services;
-
-public class TextService {
-
- public String parseText(String text) {
- return text.toUpperCase();
- }
-}
+package com.baeldung.cdi2observers.services;
+
+public class TextService {
+
+ public String parseText(String text) {
+ return text.toUpperCase();
+ }
+}
diff --git a/cdi/src/main/resources/META-INF/beans.xml b/cdi/src/main/resources/META-INF/beans.xml
index d41b35e7d9..144e9e567f 100644
--- a/cdi/src/main/resources/META-INF/beans.xml
+++ b/cdi/src/main/resources/META-INF/beans.xml
@@ -1,7 +1,8 @@
-
+
+ xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
+ bean-discovery-mode="all">
com.baeldung.interceptor.AuditedInterceptor
diff --git a/cdi/src/test/java/com/baeldung/test/cdi2observers/tests/TextServiceUnitTest.java b/cdi/src/test/java/com/baeldung/test/cdi2observers/tests/TextServiceUnitTest.java
index deecf13f9a..1b976144aa 100644
--- a/cdi/src/test/java/com/baeldung/test/cdi2observers/tests/TextServiceUnitTest.java
+++ b/cdi/src/test/java/com/baeldung/test/cdi2observers/tests/TextServiceUnitTest.java
@@ -1,14 +1,15 @@
-package com.baeldung.cdi.cdi2observers.tests;
-
-import com.baeldung.cdi.cdi2observers.services.TextService;
-import static org.assertj.core.api.Assertions.assertThat;
-import org.junit.Test;
-
-public class TextServiceUnitTest {
-
- @Test
- public void givenTextServiceInstance_whenCalledparseText_thenCorrect() {
- TextService textService = new TextService();
- assertThat(textService.parseText("Baeldung")).isEqualTo("BAELDUNG");
- }
-}
+package com.baeldung.test.cdi2observers.tests;
+
+import com.baeldung.cdi2observers.services.TextService;
+import org.junit.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class TextServiceUnitTest {
+
+ @Test
+ public void givenTextServiceInstance_whenCalledparseText_thenCorrect() {
+ TextService textService = new TextService();
+ assertThat(textService.parseText("Baeldung")).isEqualTo("BAELDUNG");
+ }
+}
diff --git a/cdi/src/test/java/com/baeldung/test/dependencyinjection/TimeLoggerFactoryUnitTest.java b/cdi/src/test/java/com/baeldung/test/dependencyinjection/TimeLoggerFactoryUnitTest.java
index caf2ed32b5..b22f189373 100644
--- a/cdi/src/test/java/com/baeldung/test/dependencyinjection/TimeLoggerFactoryUnitTest.java
+++ b/cdi/src/test/java/com/baeldung/test/dependencyinjection/TimeLoggerFactoryUnitTest.java
@@ -6,7 +6,6 @@ import static org.assertj.core.api.Assertions.assertThat;
import org.junit.Test;
public class TimeLoggerFactoryUnitTest {
-
@Test
public void givenTimeLoggerFactory_whenCalledgetTimeLogger_thenOneAssertion() {
TimeLoggerFactory timeLoggerFactory = new TimeLoggerFactory();
diff --git a/checker-plugin/README.md b/checker-plugin/README.md
index f4534b09e8..59dc2878a2 100644
--- a/checker-plugin/README.md
+++ b/checker-plugin/README.md
@@ -1,3 +1,7 @@
+## Checker Plugin
+
+This module contains articles about the Checker Plugin
+
### Relevant articles
-- [The Checker Framework – Pluggable Type Systems for Java](http://www.baeldung.com/checker-framework)
+- [The Checker Framework – Pluggable Type Systems for Java](https://www.baeldung.com/checker-framework)
diff --git a/checker-plugin/pom.xml b/checker-plugin/pom.xml
index 08408366a4..63def4dbdc 100644
--- a/checker-plugin/pom.xml
+++ b/checker-plugin/pom.xml
@@ -1,7 +1,6 @@
4.0.0
- com.baeldung
checker-plugin
1.0-SNAPSHOT
checker-plugin
diff --git a/cloud-foundry-uaa/README.md b/cloud-foundry-uaa/README.md
index b2f382cad1..f7707a04e2 100644
--- a/cloud-foundry-uaa/README.md
+++ b/cloud-foundry-uaa/README.md
@@ -1,3 +1,7 @@
-### Revelant Articles
+## Cloud Foundry UAA
+
+This module contains articles about Cloud Foundry UAA
+
+### Relevant Articles:
- [A Quick Guide To Using Cloud Foundry UAA](https://www.baeldung.com/cloud-foundry-uaa)
diff --git a/cloud-foundry-uaa/cf-uaa-oauth2-client/pom.xml b/cloud-foundry-uaa/cf-uaa-oauth2-client/pom.xml
index c6de00dbe9..fe1b919905 100644
--- a/cloud-foundry-uaa/cf-uaa-oauth2-client/pom.xml
+++ b/cloud-foundry-uaa/cf-uaa-oauth2-client/pom.xml
@@ -4,7 +4,6 @@
4.0.0
com.example
cf-uaa-oauth2-client
- 0.0.1-SNAPSHOT
uaa-client-webapp
Demo project for Spring Boot
diff --git a/cloud-foundry-uaa/cf-uaa-oauth2-resource-server/pom.xml b/cloud-foundry-uaa/cf-uaa-oauth2-resource-server/pom.xml
index 56fb23e9d8..7cd47bb7b3 100644
--- a/cloud-foundry-uaa/cf-uaa-oauth2-resource-server/pom.xml
+++ b/cloud-foundry-uaa/cf-uaa-oauth2-resource-server/pom.xml
@@ -4,7 +4,6 @@
4.0.0
com.baeldung.cfuaa
cf-uaa-oauth2-resource-server
- 0.0.1-SNAPSHOT
cf-uaa-oauth2-resource-server
Demo project for Spring Boot
diff --git a/code-generation/README.md b/code-generation/README.md
new file mode 100644
index 0000000000..289a336f99
--- /dev/null
+++ b/code-generation/README.md
@@ -0,0 +1,9 @@
+## Code Generation
+
+This module contains articles about automatic code generation
+
+### Relevant Articles:
+
+- [Introduction to AutoValue](https://www.baeldung.com/introduction-to-autovalue)
+- [Introduction to AutoFactory](https://www.baeldung.com/autofactory)
+- [Google AutoService](https://www.baeldung.com/google-autoservice)
diff --git a/code-generation/pom.xml b/code-generation/pom.xml
new file mode 100644
index 0000000000..b52e558b53
--- /dev/null
+++ b/code-generation/pom.xml
@@ -0,0 +1,52 @@
+
+ 4.0.0
+ code-generation
+ 1.0
+ code-generation
+
+
+ com.baeldung
+ parent-modules
+ 1.0.0-SNAPSHOT
+
+
+
+
+ com.google.auto.value
+ auto-value
+ ${auto-value.version}
+
+
+ com.google.auto.factory
+ auto-factory
+ ${auto-factory.version}
+
+
+ com.google.guava
+ guava
+
+
+
+
+ com.google.auto.service
+ auto-service
+ ${auto-service.version}
+ true
+
+
+
+ com.google.inject
+ guice
+ ${guice.version}
+
+
+
+
+ 1.3
+ 1.0-beta5
+ 1.0-rc5
+ 4.2.0
+
+
+
diff --git a/autovalue/src/main/java/com/baeldung/autofactory/App.java b/code-generation/src/main/java/com/baeldung/autofactory/App.java
similarity index 100%
rename from autovalue/src/main/java/com/baeldung/autofactory/App.java
rename to code-generation/src/main/java/com/baeldung/autofactory/App.java
diff --git a/autovalue/src/main/java/com/baeldung/autofactory/CustomStorage.java b/code-generation/src/main/java/com/baeldung/autofactory/CustomStorage.java
similarity index 100%
rename from autovalue/src/main/java/com/baeldung/autofactory/CustomStorage.java
rename to code-generation/src/main/java/com/baeldung/autofactory/CustomStorage.java
diff --git a/autovalue/src/main/java/com/baeldung/autofactory/custom/AbstractFactory.java b/code-generation/src/main/java/com/baeldung/autofactory/custom/AbstractFactory.java
similarity index 100%
rename from autovalue/src/main/java/com/baeldung/autofactory/custom/AbstractFactory.java
rename to code-generation/src/main/java/com/baeldung/autofactory/custom/AbstractFactory.java
diff --git a/autovalue/src/main/java/com/baeldung/autofactory/custom/CustomPhone.java b/code-generation/src/main/java/com/baeldung/autofactory/custom/CustomPhone.java
similarity index 100%
rename from autovalue/src/main/java/com/baeldung/autofactory/custom/CustomPhone.java
rename to code-generation/src/main/java/com/baeldung/autofactory/custom/CustomPhone.java
diff --git a/autovalue/src/main/java/com/baeldung/autofactory/custom/SmartPhone.java b/code-generation/src/main/java/com/baeldung/autofactory/custom/SmartPhone.java
similarity index 100%
rename from autovalue/src/main/java/com/baeldung/autofactory/custom/SmartPhone.java
rename to code-generation/src/main/java/com/baeldung/autofactory/custom/SmartPhone.java
diff --git a/autovalue/src/main/java/com/baeldung/autofactory/model/Camera.java b/code-generation/src/main/java/com/baeldung/autofactory/model/Camera.java
similarity index 100%
rename from autovalue/src/main/java/com/baeldung/autofactory/model/Camera.java
rename to code-generation/src/main/java/com/baeldung/autofactory/model/Camera.java
diff --git a/autovalue/src/main/java/com/baeldung/autofactory/model/ClassicPhone.java b/code-generation/src/main/java/com/baeldung/autofactory/model/ClassicPhone.java
similarity index 100%
rename from autovalue/src/main/java/com/baeldung/autofactory/model/ClassicPhone.java
rename to code-generation/src/main/java/com/baeldung/autofactory/model/ClassicPhone.java
diff --git a/autovalue/src/main/java/com/baeldung/autofactory/model/Phone.java b/code-generation/src/main/java/com/baeldung/autofactory/model/Phone.java
similarity index 100%
rename from autovalue/src/main/java/com/baeldung/autofactory/model/Phone.java
rename to code-generation/src/main/java/com/baeldung/autofactory/model/Phone.java
diff --git a/autovalue/src/main/java/com/baeldung/autofactory/modules/SonyCameraModule.java b/code-generation/src/main/java/com/baeldung/autofactory/modules/SonyCameraModule.java
similarity index 100%
rename from autovalue/src/main/java/com/baeldung/autofactory/modules/SonyCameraModule.java
rename to code-generation/src/main/java/com/baeldung/autofactory/modules/SonyCameraModule.java
diff --git a/autovalue/src/main/java/com/baeldung/autofactory/provided/IntermediateAssembler.java b/code-generation/src/main/java/com/baeldung/autofactory/provided/IntermediateAssembler.java
similarity index 100%
rename from autovalue/src/main/java/com/baeldung/autofactory/provided/IntermediateAssembler.java
rename to code-generation/src/main/java/com/baeldung/autofactory/provided/IntermediateAssembler.java
diff --git a/autovalue/src/main/java/com/baeldung/autofactory/provider/SonyCameraProvider.java b/code-generation/src/main/java/com/baeldung/autofactory/provider/SonyCameraProvider.java
similarity index 100%
rename from autovalue/src/main/java/com/baeldung/autofactory/provider/SonyCameraProvider.java
rename to code-generation/src/main/java/com/baeldung/autofactory/provider/SonyCameraProvider.java
diff --git a/autovalue/src/main/java/com/baeldung/autoservice/BingTranslationServiceProvider.java b/code-generation/src/main/java/com/baeldung/autoservice/BingTranslationServiceProvider.java
similarity index 100%
rename from autovalue/src/main/java/com/baeldung/autoservice/BingTranslationServiceProvider.java
rename to code-generation/src/main/java/com/baeldung/autoservice/BingTranslationServiceProvider.java
diff --git a/autovalue/src/main/java/com/baeldung/autoservice/GoogleTranslationServiceProvider.java b/code-generation/src/main/java/com/baeldung/autoservice/GoogleTranslationServiceProvider.java
similarity index 100%
rename from autovalue/src/main/java/com/baeldung/autoservice/GoogleTranslationServiceProvider.java
rename to code-generation/src/main/java/com/baeldung/autoservice/GoogleTranslationServiceProvider.java
diff --git a/autovalue/src/main/java/com/baeldung/autoservice/TranslationService.java b/code-generation/src/main/java/com/baeldung/autoservice/TranslationService.java
similarity index 100%
rename from autovalue/src/main/java/com/baeldung/autoservice/TranslationService.java
rename to code-generation/src/main/java/com/baeldung/autoservice/TranslationService.java
diff --git a/autovalue/src/main/java/com/baeldung/autovalue/AutoValueMoney.java b/code-generation/src/main/java/com/baeldung/autovalue/AutoValueMoney.java
similarity index 100%
rename from autovalue/src/main/java/com/baeldung/autovalue/AutoValueMoney.java
rename to code-generation/src/main/java/com/baeldung/autovalue/AutoValueMoney.java
diff --git a/autovalue/src/main/java/com/baeldung/autovalue/AutoValueMoneyWithBuilder.java b/code-generation/src/main/java/com/baeldung/autovalue/AutoValueMoneyWithBuilder.java
similarity index 100%
rename from autovalue/src/main/java/com/baeldung/autovalue/AutoValueMoneyWithBuilder.java
rename to code-generation/src/main/java/com/baeldung/autovalue/AutoValueMoneyWithBuilder.java
diff --git a/autovalue/src/main/java/com/baeldung/autovalue/Foo.java b/code-generation/src/main/java/com/baeldung/autovalue/Foo.java
similarity index 100%
rename from autovalue/src/main/java/com/baeldung/autovalue/Foo.java
rename to code-generation/src/main/java/com/baeldung/autovalue/Foo.java
diff --git a/autovalue/src/main/java/com/baeldung/autovalue/ImmutableMoney.java b/code-generation/src/main/java/com/baeldung/autovalue/ImmutableMoney.java
similarity index 100%
rename from autovalue/src/main/java/com/baeldung/autovalue/ImmutableMoney.java
rename to code-generation/src/main/java/com/baeldung/autovalue/ImmutableMoney.java
diff --git a/autovalue/src/main/java/com/baeldung/autovalue/MutableMoney.java b/code-generation/src/main/java/com/baeldung/autovalue/MutableMoney.java
similarity index 100%
rename from autovalue/src/main/java/com/baeldung/autovalue/MutableMoney.java
rename to code-generation/src/main/java/com/baeldung/autovalue/MutableMoney.java
diff --git a/autovalue/src/main/resources/logback.xml b/code-generation/src/main/resources/logback.xml
similarity index 100%
rename from autovalue/src/main/resources/logback.xml
rename to code-generation/src/main/resources/logback.xml
diff --git a/autovalue/src/test/java/com/baeldung/autoservice/TranslationServiceUnitTest.java b/code-generation/src/test/java/com/baeldung/autoservice/TranslationServiceUnitTest.java
similarity index 100%
rename from autovalue/src/test/java/com/baeldung/autoservice/TranslationServiceUnitTest.java
rename to code-generation/src/test/java/com/baeldung/autoservice/TranslationServiceUnitTest.java
diff --git a/autovalue/src/test/java/com/baeldung/autovalue/MoneyUnitTest.java b/code-generation/src/test/java/com/baeldung/autovalue/MoneyUnitTest.java
similarity index 100%
rename from autovalue/src/test/java/com/baeldung/autovalue/MoneyUnitTest.java
rename to code-generation/src/test/java/com/baeldung/autovalue/MoneyUnitTest.java
diff --git a/core-groovy-2/README.md b/core-groovy-2/README.md
index 1d35669cd7..f1984f18f4 100644
--- a/core-groovy-2/README.md
+++ b/core-groovy-2/README.md
@@ -1,8 +1,15 @@
-# Groovy
+# Core Groovy 2
+
+This module contains articles about core Groovy concepts
## Relevant articles:
-- [String Matching in Groovy](http://www.baeldung.com/)
- [Template Engines in Groovy](https://www.baeldung.com/groovy-template-engines)
- [Groovy def Keyword](https://www.baeldung.com/groovy-def-keyword)
-- [Pattern Matching in Strings in Groovy](https://www.baeldung.com/groovy-pattern-matching)
\ No newline at end of file
+- [Pattern Matching in Strings in Groovy](https://www.baeldung.com/groovy-pattern-matching)
+- [Working with XML in Groovy](https://www.baeldung.com/groovy-xml)
+- [Integrating Groovy into Java Applications](https://www.baeldung.com/groovy-java-applications)
+- [Concatenate Strings with Groovy](https://www.baeldung.com/groovy-concatenate-strings)
+- [Metaprogramming in Groovy](https://www.baeldung.com/groovy-metaprogramming)
+- [A Quick Guide to Working with Web Services in Groovy](https://www.baeldung.com/groovy-web-services)
+- [[<-- Prev]](/core-groovy)
diff --git a/core-groovy-2/pom.xml b/core-groovy-2/pom.xml
index b945546c8a..08526bac9a 100644
--- a/core-groovy-2/pom.xml
+++ b/core-groovy-2/pom.xml
@@ -48,6 +48,11 @@
${spock-core.version}
test
+
+ com.github.groovy-wslite
+ groovy-wslite
+ ${groovy-wslite.version}
+
@@ -157,31 +162,27 @@
-
- bintray
- Groovy Bintray
- https://dl.bintray.com/groovy/maven
-
-
- never
-
-
- false
-
-
-
-
+
+ bintray
+ Groovy Bintray
+ https://dl.bintray.com/groovy/maven
+
+
+ never
+
+
+ false
+
+
+
1.0.0
2.4.0
1.1-groovy-2.4
- 3.9
- 1.8
- 3.8.1
+ 1.1.3
1.2.3
2.5.7
- UTF-8
diff --git a/core-groovy-2/src/main/groovy/com/baeldung/category/BaeldungCategory.groovy b/core-groovy-2/src/main/groovy/com/baeldung/category/BaeldungCategory.groovy
new file mode 100644
index 0000000000..479c39699f
--- /dev/null
+++ b/core-groovy-2/src/main/groovy/com/baeldung/category/BaeldungCategory.groovy
@@ -0,0 +1,17 @@
+package com.baeldung.category;
+
+class BaeldungCategory {
+
+ public static String capitalize(String self) {
+ String capitalizedStr = self;
+ if (self.size() > 0) {
+ capitalizedStr = self.substring(0, 1).toUpperCase() + self.substring(1);
+ }
+ return capitalizedStr
+ }
+
+ public static double toThePower(Number self, Number exponent) {
+ return Math.pow(self, exponent);
+ }
+
+}
diff --git a/core-groovy-2/src/main/groovy/com/baeldung/category/NumberCategory.groovy b/core-groovy-2/src/main/groovy/com/baeldung/category/NumberCategory.groovy
new file mode 100644
index 0000000000..ccf2ed519b
--- /dev/null
+++ b/core-groovy-2/src/main/groovy/com/baeldung/category/NumberCategory.groovy
@@ -0,0 +1,17 @@
+package com.baeldung.category;
+
+import groovy.lang.Category
+
+@Category(Number)
+class NumberCategory {
+
+ public Number cube() {
+ return this*this*this
+ }
+
+ public int divideWithRoundUp(BigDecimal divisor, boolean isRoundUp) {
+ def mathRound = isRoundUp ? BigDecimal.ROUND_UP : BigDecimal.ROUND_DOWN
+ return (int)new BigDecimal(this).divide(divisor, 0, mathRound)
+ }
+
+}
diff --git a/core-groovy-2/src/main/groovy/com/baeldung/concatenate/Wonder.groovy b/core-groovy-2/src/main/groovy/com/baeldung/concatenate/Wonder.groovy
new file mode 100644
index 0000000000..1d7527726e
--- /dev/null
+++ b/core-groovy-2/src/main/groovy/com/baeldung/concatenate/Wonder.groovy
@@ -0,0 +1,52 @@
+package com.baeldung.concatenate
+
+class Wonder {
+
+ String numOfWonder = 'seven'
+
+ String operator_plus() {
+ return 'The ' + numOfWonder + ' wonders of the world'
+ }
+
+ String operator_left() {
+ return 'The ' << numOfWonder << ' wonders of ' << 'the world'
+ }
+
+ String interpolation_one() {
+ return "The $numOfWonder wonders of the world"
+
+ }
+
+ String interpolation_two() {
+ return "The ${numOfWonder} wonders of the world"
+ }
+
+ String multilineString() {
+ return """
+ There are $numOfWonder wonders of the world.
+ Can you name them all?
+ 1. The Great Pyramid of Giza
+ 2. Hanging Gardens of Babylon
+ 3. Colossus of Rhode
+ 4. Lighthouse of Alexendra
+ 5. Temple of Artemis
+ 6. Status of Zeus at Olympia
+ 7. Mausoleum at Halicarnassus
+ """
+ }
+
+ String method_concat() {
+ return 'The '.concat(numOfWonder).concat(' wonders of the world')
+
+ }
+
+ String method_builder() {
+ return new StringBuilder()
+ .append('The ').append(numOfWonder).append(' wonders of the world')
+ }
+
+ String method_buffer() {
+ return new StringBuffer()
+ .append('The ').append(numOfWonder).append(' wonders of the world')
+ }
+}
\ No newline at end of file
diff --git a/core-groovy-2/src/main/groovy/com/baeldung/metaprogramming/Employee.groovy b/core-groovy-2/src/main/groovy/com/baeldung/metaprogramming/Employee.groovy
new file mode 100644
index 0000000000..f49d0f906b
--- /dev/null
+++ b/core-groovy-2/src/main/groovy/com/baeldung/metaprogramming/Employee.groovy
@@ -0,0 +1,45 @@
+package com.baeldung.metaprogramming
+
+import groovy.transform.AutoClone
+import groovy.transform.Canonical
+import groovy.transform.EqualsAndHashCode
+import groovy.transform.ToString
+import groovy.transform.TupleConstructor
+import groovy.util.logging.*
+
+@Canonical
+@TupleConstructor
+@EqualsAndHashCode
+@ToString(includePackage=false, excludes=['id'])
+@Log
+@AutoClone
+class Employee {
+
+ long id
+ String firstName
+ String lastName
+ int age
+
+ //method to catch missing property's getter
+ def propertyMissing(String propertyName) {
+ log.info "$propertyName is not available"
+ "property '$propertyName' is not available"
+ }
+
+ //method to catch missing property's setter
+ def propertyMissing(String propertyName, propertyValue) {
+ println "property '$propertyName' is not available"
+ log.info "$propertyName is not available"
+ "property '$propertyName' is not available"
+ }
+
+ def methodMissing(String methodName, def methodArgs) {
+ log.info "$methodName is not defined"
+ "method '$methodName' is not defined"
+ }
+
+ def logEmp() {
+ log.info "Employee: $lastName, $firstName is of $age years age"
+ }
+
+}
\ No newline at end of file
diff --git a/core-groovy-2/src/main/groovy/com/baeldung/metaprogramming/extension/BasicExtensions.groovy b/core-groovy-2/src/main/groovy/com/baeldung/metaprogramming/extension/BasicExtensions.groovy
new file mode 100644
index 0000000000..65591cae8d
--- /dev/null
+++ b/core-groovy-2/src/main/groovy/com/baeldung/metaprogramming/extension/BasicExtensions.groovy
@@ -0,0 +1,30 @@
+package com.baeldung.metaprogramming.extension
+
+import com.baeldung.metaprogramming.Employee
+
+class BasicExtensions {
+
+ static int getYearOfBirth(Employee self) {
+ return (new Date().getYear() + 1900) - self.age;
+ }
+
+ static String capitalize(String self) {
+ return self.substring(0, 1).toUpperCase() + self.substring(1)
+ }
+
+ static void printCounter(Integer self) {
+ while (self>0) {
+ println self
+ self--
+ }
+ }
+
+ static Long square(Long self) {
+ return self*self
+ }
+
+ static BigDecimal cube(BigDecimal self) {
+ return self*self*self
+ }
+
+}
\ No newline at end of file
diff --git a/core-groovy-2/src/main/groovy/com/baeldung/metaprogramming/extension/StaticEmployeeExtension.groovy b/core-groovy-2/src/main/groovy/com/baeldung/metaprogramming/extension/StaticEmployeeExtension.groovy
new file mode 100644
index 0000000000..ab2aac38ea
--- /dev/null
+++ b/core-groovy-2/src/main/groovy/com/baeldung/metaprogramming/extension/StaticEmployeeExtension.groovy
@@ -0,0 +1,10 @@
+package com.baeldung.metaprogramming.extension
+
+import com.baeldung.metaprogramming.Employee
+
+class StaticEmployeeExtension {
+
+ static Employee getDefaultObj(Employee self) {
+ return new Employee(firstName: "firstName", lastName: "lastName", age: 20)
+ }
+}
\ No newline at end of file
diff --git a/core-groovy-2/src/main/resources/META-INF/services/org.codehaus.groovy.runtime.ExtensionModule b/core-groovy-2/src/main/resources/META-INF/services/org.codehaus.groovy.runtime.ExtensionModule
new file mode 100644
index 0000000000..967108b846
--- /dev/null
+++ b/core-groovy-2/src/main/resources/META-INF/services/org.codehaus.groovy.runtime.ExtensionModule
@@ -0,0 +1,4 @@
+moduleName=core-groovy-2
+moduleVersion=1.0-SNAPSHOT
+extensionClasses=com.baeldung.metaprogramming.extension.BasicExtensions
+staticExtensionClasses=com.baeldung.metaprogramming.extension.StaticEmployeeExtension
diff --git a/core-groovy-2/src/test/groovy/com/baeldung/category/CategoryUnitTest.groovy b/core-groovy-2/src/test/groovy/com/baeldung/category/CategoryUnitTest.groovy
new file mode 100644
index 0000000000..a1f67b1e2e
--- /dev/null
+++ b/core-groovy-2/src/test/groovy/com/baeldung/category/CategoryUnitTest.groovy
@@ -0,0 +1,101 @@
+package com.baeldung.category
+
+import groovy.time.*
+import java.text.SimpleDateFormat
+import groovy.xml.*
+import groovy.xml.dom.*
+import com.baeldung.category.BaeldungCategory
+import com.baeldung.category.NumberCategory
+
+class CategoryUnitTest extends GroovyTestCase {
+
+ void test_whenUsingTimeCategory_thenOperationOnDate() {
+ def jan_1_2019 = new Date("01/01/2019")
+ use (TimeCategory) {
+ assert jan_1_2019 + 10.seconds == new Date("01/01/2019 00:00:10")
+
+ assert jan_1_2019 + 20.minutes == new Date("01/01/2019 00:20:00")
+
+ assert jan_1_2019 + 2.hours == new Date("01/01/2019 02:00:00")
+
+ assert jan_1_2019 - 1.day == new Date("12/31/2018")
+
+ assert jan_1_2019 + 2.weeks == new Date("01/15/2019")
+
+ assert jan_1_2019 - 2.months == new Date("11/01/2018")
+
+ assert jan_1_2019 + 3.years == new Date("01/01/2022")
+ }
+ }
+
+ void test_whenUsingTimeCategory_thenOperationOnNumber() {
+ SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy")
+ use (TimeCategory) {
+ assert sdf.format(5.days.from.now) == sdf.format(new Date() + 5.days)
+
+ sdf = new SimpleDateFormat("dd/MM/yyyy hh:mm:ss")
+ assert sdf.format(10.minutes.from.now) == sdf.format(new Date() + 10.minutes)
+ assert sdf.format(2.hours.ago) == sdf.format(new Date() - 2.hours)
+ }
+ }
+
+ void test_whenUsingDOMCategory_thenOperationOnXML() {
+
+ def baeldungArticlesText = """
+
+
+ An Intro to the Java Debug Interface (JDI)
+ A quick and practical overview of Java Debug Interface.
+
+
+ A Quick Guide to Working with Web Services in Groovy
+ Learn how to work with Web Services in Groovy.
+
+
+"""
+ def baeldungArticlesDom = DOMBuilder.newInstance().parseText(baeldungArticlesText)
+
+ def root = baeldungArticlesDom.documentElement
+
+ use (DOMCategory) {
+ assert root.article.size() == 2
+
+ def articles = root.article
+
+ assert articles[0].title.text() == "An Intro to the Java Debug Interface (JDI)"
+ assert articles[1].desc.text() == "Learn how to work with Web Services in Groovy."
+
+ def articleNode3 = root.appendNode(new QName("article"), ["core-java": "false"])
+
+ articleNode3.appendNode("title", "Metaprogramming in Groovy")
+ articleNode3.appendNode("desc", "Explore the concept of runtime and compile-time metaprogramming in Groovy")
+
+ assert root.article.size() == 3
+
+ assert root.article[2].title.text() == "Metaprogramming in Groovy"
+ }
+ }
+
+ void test_whenUsingBaeldungCategory_thenCapitalizeString() {
+ use (BaeldungCategory) {
+ assert "norman".capitalize() == "Norman"
+ }
+ }
+
+ void test_whenUsingBaeldungCategory_thenOperationsOnNumber() {
+ use (BaeldungCategory) {
+ assert 50.toThePower(2) == 2500
+ assert 2.4.toThePower(4) == 33.1776
+ }
+ }
+
+ void test_whenUsingNumberCategory_thenOperationsOnNumber() {
+ use (NumberCategory) {
+ assert 3.cube() == 27
+ assert 25.divideWithRoundUp(6, true) == 5
+ assert 120.23.divideWithRoundUp(6.1, true) == 20
+ assert 150.9.divideWithRoundUp(12.1, false) == 12
+ }
+ }
+
+}
diff --git a/core-groovy-2/src/test/groovy/com/baeldung/concatenate/WonderUnitTest.groovy b/core-groovy-2/src/test/groovy/com/baeldung/concatenate/WonderUnitTest.groovy
new file mode 100644
index 0000000000..5fc74abd69
--- /dev/null
+++ b/core-groovy-2/src/test/groovy/com/baeldung/concatenate/WonderUnitTest.groovy
@@ -0,0 +1,69 @@
+package com.baeldung.concatenate
+
+import org.junit.Before
+import org.junit.Test
+
+import static org.junit.Assert.*
+
+class WonderUnitTest {
+
+ static final String EXPECTED_SINGLE_LINE = "The seven wonders of the world"
+
+ Wonder wonder
+
+ @Before
+ void before() {
+ wonder = new Wonder()
+ }
+
+ @Test
+ void whenUsingOperatorPlus_thenConcatCorrectly() {
+ assertEquals(EXPECTED_SINGLE_LINE, wonder.operator_plus())
+ }
+
+ @Test
+ void whenUsingOperatorLeft_thenConcatCorrectly() {
+ assertEquals(EXPECTED_SINGLE_LINE, wonder.operator_left())
+ }
+
+ @Test
+ void whenUsingInterpolationOne_thenConcatCorrectly() {
+ assertEquals(EXPECTED_SINGLE_LINE, wonder.interpolation_one())
+ }
+
+ @Test
+ void whenUsingInterpolationTwo_thenConcatCorrectly() {
+ assertEquals(EXPECTED_SINGLE_LINE, wonder.interpolation_two())
+ }
+
+ @Test
+ void whenUsingMultiline_thenConcatCorrectly() {
+ def expectedMultiline = """
+ There are seven wonders of the world.
+ Can you name them all?
+ 1. The Great Pyramid of Giza
+ 2. Hanging Gardens of Babylon
+ 3. Colossus of Rhode
+ 4. Lighthouse of Alexendra
+ 5. Temple of Artemis
+ 6. Status of Zeus at Olympia
+ 7. Mausoleum at Halicarnassus
+ """
+ assertEquals(expectedMultiline, wonder.multilineString())
+ }
+
+ @Test
+ void whenUsingMethodConcat_thenConcatCorrectly() {
+ assertEquals(EXPECTED_SINGLE_LINE, wonder.method_concat())
+ }
+
+ @Test
+ void whenUsingMethodBuilder_thenConcatCorrectly() {
+ assertEquals(EXPECTED_SINGLE_LINE, wonder.method_builder())
+ }
+
+ @Test
+ void whenUsingMethodBuffer_thenConcatCorrectly() {
+ assertEquals(EXPECTED_SINGLE_LINE, wonder.method_buffer())
+ }
+}
diff --git a/core-groovy-2/src/test/groovy/com/baeldung/metaprogramming/MetaprogrammingUnitTest.groovy b/core-groovy-2/src/test/groovy/com/baeldung/metaprogramming/MetaprogrammingUnitTest.groovy
new file mode 100644
index 0000000000..8066b10f9b
--- /dev/null
+++ b/core-groovy-2/src/test/groovy/com/baeldung/metaprogramming/MetaprogrammingUnitTest.groovy
@@ -0,0 +1,118 @@
+package com.baeldung.metaprogramming
+
+import groovy.time.TimeCategory
+
+class MetaprogrammingUnitTest extends GroovyTestCase {
+
+ Employee emp = new Employee(firstName: "Norman", lastName: "Lewis")
+
+ void testPropertyMissing() {
+ assert emp.address == "property 'address' is not available"
+ }
+
+ void testMethodMissing() {
+ Employee emp = new Employee()
+ try {
+ emp.getFullName()
+ } catch(MissingMethodException e) {
+ println "method is not defined"
+ }
+ assert emp.getFullName() == "method 'getFullName' is not defined"
+ }
+
+ void testMetaClassProperty() {
+ Employee.metaClass.address = ""
+ emp = new Employee(firstName: "Norman", lastName: "Lewis", address: "US")
+ assert emp.address == "US"
+ }
+
+ void testMetaClassMethod() {
+ emp.metaClass.getFullName = {
+ "$lastName, $firstName"
+ }
+ assert emp.getFullName() == "Lewis, Norman"
+ }
+
+ void testMetaClassConstructor() {
+ try {
+ Employee emp = new Employee("Norman")
+ } catch(GroovyRuntimeException e) {
+ assert e.message == "Could not find matching constructor for: com.baeldung.metaprogramming.Employee(String)"
+ }
+
+ Employee.metaClass.constructor = { String firstName ->
+ new Employee(firstName: firstName)
+ }
+
+ Employee norman = new Employee("Norman")
+ assert norman.firstName == "Norman"
+ assert norman.lastName == null
+ }
+
+ void testJavaMetaClass() {
+ String.metaClass.capitalize = { String str ->
+ str.substring(0, 1).toUpperCase() + str.substring(1);
+ }
+ assert "norman".capitalize() == "Norman"
+ }
+
+ void testEmployeeExtension() {
+ Employee emp = new Employee(age: 28)
+ assert emp.getYearOfBirth() == 1991
+ }
+
+ void testJavaClassesExtensions() {
+ 5.printCounter()
+
+ assert 40l.square() == 1600l
+
+ assert (2.98).cube() == 26.463592
+ }
+
+ void testStaticEmployeeExtension() {
+ assert Employee.getDefaultObj().firstName == "firstName"
+ assert Employee.getDefaultObj().lastName == "lastName"
+ assert Employee.getDefaultObj().age == 20
+ }
+
+ void testToStringAnnotation() {
+ Employee employee = new Employee()
+ employee.id = 1
+ employee.firstName = "norman"
+ employee.lastName = "lewis"
+ employee.age = 28
+
+ assert employee.toString() == "Employee(norman, lewis, 28)"
+ }
+
+ void testTupleConstructorAnnotation() {
+ Employee norman = new Employee(1, "norman", "lewis", 28)
+ assert norman.toString() == "Employee(norman, lewis, 28)"
+
+ Employee snape = new Employee(2, "snape")
+ assert snape.toString() == "Employee(snape, null, 0)"
+
+ }
+
+ void testEqualsAndHashCodeAnnotation() {
+ Employee norman = new Employee(1, "norman", "lewis", 28)
+ Employee normanCopy = new Employee(1, "norman", "lewis", 28)
+ assert norman.equals(normanCopy)
+ assert norman.hashCode() == normanCopy.hashCode()
+ }
+
+ void testAutoCloneAnnotation() {
+ try {
+ Employee norman = new Employee(1, "norman", "lewis", 28)
+ def normanCopy = norman.clone()
+ assert norman == normanCopy
+ } catch(CloneNotSupportedException e) {
+ e.printStackTrace()
+ }
+ }
+
+ void testLoggingAnnotation() {
+ Employee employee = new Employee(1, "Norman", "Lewis", 28)
+ employee.logEmp()
+ }
+}
\ No newline at end of file
diff --git a/core-groovy-2/src/test/groovy/com/baeldung/webservice/WebserviceUnitTest.groovy b/core-groovy-2/src/test/groovy/com/baeldung/webservice/WebserviceUnitTest.groovy
new file mode 100644
index 0000000000..302959d0d9
--- /dev/null
+++ b/core-groovy-2/src/test/groovy/com/baeldung/webservice/WebserviceUnitTest.groovy
@@ -0,0 +1,152 @@
+package com.baeldung.webservice
+
+import groovy.json.JsonSlurper
+import wslite.rest.ContentType
+import wslite.rest.RESTClient
+import wslite.rest.RESTClientException
+import wslite.soap.SOAPClient
+import wslite.soap.SOAPMessageBuilder
+import wslite.http.auth.HTTPBasicAuthorization
+import org.junit.Test
+
+class WebserviceUnitTest extends GroovyTestCase {
+
+ JsonSlurper jsonSlurper = new JsonSlurper()
+
+ static RESTClient client = new RESTClient("https://postman-echo.com")
+
+ static {
+ client.defaultAcceptHeader = ContentType.JSON
+ client.httpClient.sslTrustAllCerts = true
+ }
+
+ void test_whenSendingGet_thenRespose200() {
+ def postmanGet = new URL('https://postman-echo.com/get')
+ def getConnection = postmanGet.openConnection()
+ getConnection.requestMethod = 'GET'
+ assert getConnection.responseCode == 200
+ if (getConnection.responseCode == 200) {
+ assert jsonSlurper.parseText(getConnection.content.text)?.headers?.host == "postman-echo.com"
+ }
+ }
+
+ void test_whenSendingPost_thenRespose200() {
+ def postmanPost = new URL('https://postman-echo.com/post')
+ def query = "q=This is post request form parameter."
+ def postConnection = postmanPost.openConnection()
+ postConnection.requestMethod = 'POST'
+ assert postConnection.responseCode == 200
+ }
+
+ void test_whenSendingPostWithParams_thenRespose200() {
+ def postmanPost = new URL('https://postman-echo.com/post')
+ def form = "param1=This is request parameter."
+ def postConnection = postmanPost.openConnection()
+ postConnection.requestMethod = 'POST'
+ postConnection.doOutput = true
+ def text
+ postConnection.with {
+ outputStream.withWriter { outputStreamWriter ->
+ outputStreamWriter << form
+ }
+ text = content.text
+ }
+ assert postConnection.responseCode == 200
+ assert jsonSlurper.parseText(text)?.json.param1 == "This is request parameter."
+ }
+
+ void test_whenReadingRSS_thenReceiveFeed() {
+ def rssFeed = new XmlParser().parse("https://news.google.com/rss?hl=en-US&gl=US&ceid=US:en")
+ def stories = []
+ (0..4).each {
+ def item = rssFeed.channel.item.get(it)
+ stories << item.title.text()
+ }
+ assert stories.size() == 5
+ }
+
+ void test_whenReadingAtom_thenReceiveFeed() {
+ def atomFeed = new XmlParser().parse("https://news.google.com/atom?hl=en-US&gl=US&ceid=US:en")
+ def stories = []
+ (0..4).each {
+ def entry = atomFeed.entry.get(it)
+ stories << entry.title.text()
+ }
+ assert stories.size() == 5
+ }
+
+ void test_whenConsumingSoap_thenReceiveResponse() {
+ def url = "http://www.dataaccess.com/webservicesserver/numberconversion.wso"
+ def soapClient = new SOAPClient(url)
+ def message = new SOAPMessageBuilder().build({
+ body {
+ NumberToWords(xmlns: "http://www.dataaccess.com/webservicesserver/") {
+ ubiNum(1234)
+ }
+ }
+ })
+ def response = soapClient.send(message.toString());
+ def words = response.NumberToWordsResponse
+ assert words == "one thousand two hundred and thirty four "
+ }
+
+ void test_whenConsumingRestGet_thenReceiveResponse() {
+ def path = "/get"
+ def response
+ try {
+ response = client.get(path: path)
+ assert response.statusCode == 200
+ assert response.json?.headers?.host == "postman-echo.com"
+ } catch (RESTClientException e) {
+ assert e?.response?.statusCode != 200
+ }
+ }
+
+ void test_whenConsumingRestPost_thenReceiveResponse() {
+ def path = "/post"
+ def params = ["foo":1,"bar":2]
+ def response
+ try {
+ response = client.post(path: path) {
+ type ContentType.JSON
+ json params
+ }
+ assert response.json?.data == params
+ } catch (RESTClientException e) {
+ e.printStackTrace()
+ assert e?.response?.statusCode != 200
+ }
+ }
+
+ void test_whenBasicAuthentication_thenReceive200() {
+ def path = "/basic-auth"
+ def response
+ try {
+ client.authorization = new HTTPBasicAuthorization("postman", "password")
+ response = client.get(path: path)
+ assert response.statusCode == 200
+ assert response.json?.authenticated == true
+ } catch (RESTClientException e) {
+ e.printStackTrace()
+ assert e?.response?.statusCode != 200
+ }
+ }
+
+ void test_whenOAuth_thenReceive200() {
+ RESTClient oAuthClient = new RESTClient("https://postman-echo.com")
+ oAuthClient.defaultAcceptHeader = ContentType.JSON
+ oAuthClient.httpClient.sslTrustAllCerts = true
+
+ def path = "/oauth1"
+ def params = [oauth_consumer_key: "RKCGzna7bv9YD57c", oauth_signature_method: "HMAC-SHA1", oauth_timestamp:1567089944, oauth_nonce: "URT7v4", oauth_version: 1.0, oauth_signature: 'RGgR/ktDmclkM0ISWaFzebtlO0A=']
+ def response
+ try {
+ response = oAuthClient.get(path: path, query: params)
+ assert response.statusCode == 200
+ assert response.statusMessage == "OK"
+ assert response.json.status == "pass"
+ } catch (RESTClientException e) {
+ assert e?.response?.statusCode != 200
+ }
+ }
+}
\ No newline at end of file
diff --git a/core-groovy-collections/README.md b/core-groovy-collections/README.md
index aeba8933be..aae8be508e 100644
--- a/core-groovy-collections/README.md
+++ b/core-groovy-collections/README.md
@@ -1,6 +1,10 @@
-# Groovy
+## Core Groovy Collections
+
+This module contains articles about Groovy core collections
## Relevant articles:
- [Maps in Groovy](https://www.baeldung.com/groovy-maps)
-
+- [Finding Elements in Collections in Groovy](https://www.baeldung.com/groovy-collections-find-elements)
+- [Lists in Groovy](https://www.baeldung.com/groovy-lists)
+- [A Quick Guide to Iterating a Map in Groovy](https://www.baeldung.com/groovy-map-iterating)
diff --git a/core-groovy-collections/src/test/groovy/com/baeldung/find/ListFindUnitTest.groovy b/core-groovy-collections/src/test/groovy/com/baeldung/find/ListFindUnitTest.groovy
new file mode 100644
index 0000000000..82a2138be4
--- /dev/null
+++ b/core-groovy-collections/src/test/groovy/com/baeldung/find/ListFindUnitTest.groovy
@@ -0,0 +1,58 @@
+package com.baeldung.find
+
+import com.baeldung.find.Person
+import org.junit.Test
+
+import static org.junit.Assert.*
+
+class ListFindUnitTest {
+
+ private final personList = [
+ new Person("Regina", "Fitzpatrick", 25),
+ new Person("Abagail", "Ballard", 26),
+ new Person("Lucian", "Walter", 30),
+ ]
+
+ @Test
+ void whenListContainsElement_thenCheckReturnsTrue() {
+ def list = ['a', 'b', 'c']
+
+ assertTrue(list.indexOf('a') > -1)
+ assertTrue(list.contains('a'))
+ }
+
+ @Test
+ void whenListContainsElement_thenCheckWithMembershipOperatorReturnsTrue() {
+ def list = ['a', 'b', 'c']
+
+ assertTrue('a' in list)
+ }
+
+ @Test
+ void givenListOfPerson_whenUsingStreamMatching_thenShouldEvaluateList() {
+ assertTrue(personList.stream().anyMatch {it.age > 20})
+ assertFalse(personList.stream().allMatch {it.age < 30})
+ }
+
+ @Test
+ void givenListOfPerson_whenUsingCollectionMatching_thenShouldEvaluateList() {
+ assertTrue(personList.any {it.age > 20})
+ assertFalse(personList.every {it.age < 30})
+ }
+
+ @Test
+ void givenListOfPerson_whenUsingStreamFind_thenShouldReturnMatchingElements() {
+ assertTrue(personList.stream().filter {it.age > 20}.findAny().isPresent())
+ assertFalse(personList.stream().filter {it.age > 30}.findAny().isPresent())
+ assertTrue(personList.stream().filter {it.age > 20}.findAll().size() == 3)
+ assertTrue(personList.stream().filter {it.age > 30}.findAll().isEmpty())
+ }
+
+ @Test
+ void givenListOfPerson_whenUsingCollectionFind_thenShouldReturnMatchingElements() {
+ assertNotNull(personList.find {it.age > 20})
+ assertNull(personList.find {it.age > 30})
+ assertTrue(personList.findAll {it.age > 20}.size() == 3)
+ assertTrue(personList.findAll {it.age > 30}.isEmpty())
+ }
+}
diff --git a/core-groovy-collections/src/test/groovy/com/baeldung/find/MapFindUnitTest.groovy b/core-groovy-collections/src/test/groovy/com/baeldung/find/MapFindUnitTest.groovy
new file mode 100644
index 0000000000..16e231182b
--- /dev/null
+++ b/core-groovy-collections/src/test/groovy/com/baeldung/find/MapFindUnitTest.groovy
@@ -0,0 +1,76 @@
+package com.baeldung.find
+
+import com.baeldung.find.Person
+import org.junit.Test
+
+import static org.junit.Assert.*
+
+class MapFindUnitTest {
+
+ private final personMap = [
+ Regina : new Person("Regina", "Fitzpatrick", 25),
+ Abagail: new Person("Abagail", "Ballard", 26),
+ Lucian : new Person("Lucian", "Walter", 30)
+ ]
+
+ @Test
+ void whenMapContainsKeyElement_thenCheckReturnsTrue() {
+ def map = [a: 'd', b: 'e', c: 'f']
+
+ assertTrue(map.containsKey('a'))
+ assertFalse(map.containsKey('e'))
+ assertTrue(map.containsValue('e'))
+ }
+
+ @Test
+ void whenMapContainsKeyElement_thenCheckByMembershipReturnsTrue() {
+ def map = [a: 'd', b: 'e', c: 'f']
+
+ assertTrue('a' in map)
+ assertFalse('f' in map)
+ }
+
+ @Test
+ void whenMapContainsFalseBooleanValues_thenCheckReturnsFalse() {
+ def map = [a: true, b: false, c: null]
+
+ assertTrue(map.containsKey('b'))
+ assertTrue('a' in map)
+ assertFalse('b' in map)
+ assertFalse('c' in map)
+ }
+
+ @Test
+ void givenMapOfPerson_whenUsingStreamMatching_thenShouldEvaluateMap() {
+ assertTrue(personMap.keySet().stream().anyMatch {it == "Regina"})
+ assertFalse(personMap.keySet().stream().allMatch {it == "Albert"})
+ assertFalse(personMap.values().stream().allMatch {it.age < 30})
+ assertTrue(personMap.entrySet().stream().anyMatch {it.key == "Abagail" && it.value.lastname == "Ballard"})
+ }
+
+ @Test
+ void givenMapOfPerson_whenUsingCollectionMatching_thenShouldEvaluateMap() {
+ assertTrue(personMap.keySet().any {it == "Regina"})
+ assertFalse(personMap.keySet().every {it == "Albert"})
+ assertFalse(personMap.values().every {it.age < 30})
+ assertTrue(personMap.any {firstname, person -> firstname == "Abagail" && person.lastname == "Ballard"})
+ }
+
+ @Test
+ void givenMapOfPerson_whenUsingCollectionFind_thenShouldReturnElements() {
+ assertNotNull(personMap.find {it.key == "Abagail" && it.value.lastname == "Ballard"})
+ assertTrue(personMap.findAll {it.value.age > 20}.size() == 3)
+ }
+
+ @Test
+ void givenMapOfPerson_whenUsingStreamFind_thenShouldReturnElements() {
+ assertTrue(
+ personMap.entrySet().stream()
+ .filter {it.key == "Abagail" && it.value.lastname == "Ballard"}
+ .findAny().isPresent())
+ assertTrue(
+ personMap.entrySet().stream()
+ .filter {it.value.age > 20}
+ .findAll().size() == 3)
+ }
+}
diff --git a/core-groovy/src/main/groovy/com/baeldung/Person.groovy b/core-groovy-collections/src/test/groovy/com/baeldung/find/Person.groovy
similarity index 96%
rename from core-groovy/src/main/groovy/com/baeldung/Person.groovy
rename to core-groovy-collections/src/test/groovy/com/baeldung/find/Person.groovy
index 6a009aeee0..e65826363a 100644
--- a/core-groovy/src/main/groovy/com/baeldung/Person.groovy
+++ b/core-groovy-collections/src/test/groovy/com/baeldung/find/Person.groovy
@@ -1,4 +1,4 @@
-package com.baeldung
+package com.baeldung.find
class Person {
private String firstname
diff --git a/core-groovy-collections/src/test/groovy/com/baeldung/find/SetFindUnitTest.groovy b/core-groovy-collections/src/test/groovy/com/baeldung/find/SetFindUnitTest.groovy
new file mode 100644
index 0000000000..d2d03d5427
--- /dev/null
+++ b/core-groovy-collections/src/test/groovy/com/baeldung/find/SetFindUnitTest.groovy
@@ -0,0 +1,16 @@
+package com.baeldung.find
+
+import org.junit.Test
+
+import static org.junit.Assert.assertTrue
+
+class SetFindUnitTest {
+
+ @Test
+ void whenSetContainsElement_thenCheckReturnsTrue() {
+ def set = ['a', 'b', 'c'] as Set
+
+ assertTrue(set.contains('a'))
+ assertTrue('a' in set)
+ }
+}
\ No newline at end of file
diff --git a/core-groovy-collections/src/test/groovy/com/baeldung/iteratemap/IterateMapUnitTest.groovy b/core-groovy-collections/src/test/groovy/com/baeldung/iteratemap/IterateMapUnitTest.groovy
new file mode 100644
index 0000000000..970203ce85
--- /dev/null
+++ b/core-groovy-collections/src/test/groovy/com/baeldung/iteratemap/IterateMapUnitTest.groovy
@@ -0,0 +1,87 @@
+package com.baeldung.iteratemap
+
+import com.baeldung.find.Person
+import org.junit.Test
+
+import static org.junit.Assert.*
+
+class IterateMapUnitTest {
+
+ @Test
+ void whenUsingEach_thenMapIsIterated() {
+ def map = [
+ 'FF0000' : 'Red',
+ '00FF00' : 'Lime',
+ '0000FF' : 'Blue',
+ 'FFFF00' : 'Yellow'
+ ]
+
+ map.each { println "Hex Code: $it.key = Color Name: $it.value" }
+ }
+
+ @Test
+ void whenUsingEachWithEntry_thenMapIsIterated() {
+ def map = [
+ 'E6E6FA' : 'Lavender',
+ 'D8BFD8' : 'Thistle',
+ 'DDA0DD' : 'Plum',
+ ]
+
+ map.each { entry -> println "Hex Code: $entry.key = Color Name: $entry.value" }
+ }
+
+ @Test
+ void whenUsingEachWithKeyAndValue_thenMapIsIterated() {
+ def map = [
+ '000000' : 'Black',
+ 'FFFFFF' : 'White',
+ '808080' : 'Gray'
+ ]
+
+ map.each { key, val ->
+ println "Hex Code: $key = Color Name $val"
+ }
+ }
+
+ @Test
+ void whenUsingEachWithIndexAndEntry_thenMapIsIterated() {
+ def map = [
+ '800080' : 'Purple',
+ '4B0082' : 'Indigo',
+ '6A5ACD' : 'Slate Blue'
+ ]
+
+ map.eachWithIndex { entry, index ->
+ def indent = ((index == 0 || index % 2 == 0) ? " " : "")
+ println "$indent Hex Code: $entry.key = Color Name: $entry.value"
+ }
+ }
+
+ @Test
+ void whenUsingEachWithIndexAndKeyAndValue_thenMapIsIterated() {
+ def map = [
+ 'FFA07A' : 'Light Salmon',
+ 'FF7F50' : 'Coral',
+ 'FF6347' : 'Tomato',
+ 'FF4500' : 'Orange Red'
+ ]
+
+ map.eachWithIndex { key, val, index ->
+ def indent = ((index == 0 || index % 2 == 0) ? " " : "")
+ println "$indent Hex Code: $key = Color Name: $val"
+ }
+ }
+
+ @Test
+ void whenUsingForLoop_thenMapIsIterated() {
+ def map = [
+ '2E8B57' : 'Seagreen',
+ '228B22' : 'Forest Green',
+ '008000' : 'Green'
+ ]
+
+ for (entry in map) {
+ println "Hex Code: $entry.key = Color Name: $entry.value"
+ }
+ }
+}
diff --git a/core-groovy-collections/src/test/groovy/com/baeldung/lists/ListUnitTest.groovy b/core-groovy-collections/src/test/groovy/com/baeldung/lists/ListUnitTest.groovy
new file mode 100644
index 0000000000..e4c0a0c177
--- /dev/null
+++ b/core-groovy-collections/src/test/groovy/com/baeldung/lists/ListUnitTest.groovy
@@ -0,0 +1,173 @@
+package com.baeldung.lists
+
+import static groovy.test.GroovyAssert.*
+import org.junit.Test
+
+class ListUnitTest {
+
+ @Test
+ void testCreateList() {
+
+ def list = [1, 2, 3]
+ assertNotNull(list)
+
+ def listMix = ['A', "b", 1, true]
+ assertTrue(listMix == ['A', "b", 1, true])
+
+ def linkedList = [1, 2, 3] as LinkedList
+ assertTrue(linkedList instanceof LinkedList)
+
+ ArrayList arrList = [1, 2, 3]
+ assertTrue(arrList.class == ArrayList)
+
+ def copyList = new ArrayList(arrList)
+ assertTrue(copyList == arrList)
+
+ def cloneList = arrList.clone()
+ assertTrue(cloneList == arrList)
+ }
+
+ @Test
+ void testCreateEmptyList() {
+
+ def emptyList = []
+ assertTrue(emptyList.size() == 0)
+ }
+
+ @Test
+ void testCompareTwoLists() {
+
+ def list1 = [5, 6.0, 'p']
+ def list2 = [5, 6.0, 'p']
+ assertTrue(list1 == list2)
+ }
+
+ @Test
+ void testGetItemsFromList(){
+
+ def list = ["Hello", "World"]
+
+ assertTrue(list.get(1) == "World")
+ assertTrue(list[1] == "World")
+ assertTrue(list[-1] == "World")
+ assertTrue(list.getAt(1) == "World")
+ assertTrue(list.getAt(-2) == "Hello")
+ }
+
+ @Test
+ void testAddItemsToList() {
+
+ def list = []
+
+ list << 1
+ list.add("Apple")
+ assertTrue(list == [1, "Apple"])
+
+ list[2] = "Box"
+ list[4] = true
+ assertTrue(list == [1, "Apple", "Box", null, true])
+
+ list.add(1, 6.0)
+ assertTrue(list == [1, 6.0, "Apple", "Box", null, true])
+
+ def list2 = [1, 2]
+ list += list2
+ list += 12
+ assertTrue(list == [1, 6.0, "Apple", "Box", null, true, 1, 2, 12])
+ }
+
+ @Test
+ void testUpdateItemsInList() {
+
+ def list =[1, "Apple", 80, "App"]
+ list[1] = "Box"
+ list.set(2,90)
+ assertTrue(list == [1, "Box", 90, "App"])
+ }
+
+ @Test
+ void testRemoveItemsFromList(){
+
+ def list = [1, 2, 3, 4, 5, 5, 6, 6, 7]
+
+ list.remove(3)
+ assertTrue(list == [1, 2, 3, 5, 5, 6, 6, 7])
+
+ list.removeElement(5)
+ assertTrue(list == [1, 2, 3, 5, 6, 6, 7])
+
+ assertTrue(list - 6 == [1, 2, 3, 5, 7])
+ }
+
+ @Test
+ void testIteratingOnAList(){
+
+ def list = [1, "App", 3, 4]
+ list.each{ println it * 2}
+
+ list.eachWithIndex{ it, i -> println "$i : $it" }
+ }
+
+ @Test
+ void testCollectingToAnotherList(){
+
+ def list = ["Kay", "Henry", "Justin", "Tom"]
+ assertTrue(list.collect{"Hi " + it} == ["Hi Kay", "Hi Henry", "Hi Justin", "Hi Tom"])
+ }
+
+ @Test
+ void testJoinItemsInAList(){
+ assertTrue(["One", "Two", "Three"].join(",") == "One,Two,Three")
+ }
+
+ @Test
+ void testFilteringOnLists(){
+ def filterList = [2, 1, 3, 4, 5, 6, 76]
+
+ assertTrue(filterList.find{it > 3} == 4)
+
+ assertTrue(filterList.findAll{it > 3} == [4, 5, 6, 76])
+
+ assertTrue(filterList.findAll{ it instanceof Number} == [2, 1, 3, 4, 5, 6, 76])
+
+ assertTrue(filterList.grep( Number )== [2, 1, 3, 4, 5, 6, 76])
+
+ assertTrue(filterList.grep{ it> 6 }== [76])
+
+ def conditionList = [2, 1, 3, 4, 5, 6, 76]
+
+ assertFalse(conditionList.every{ it < 6})
+
+ assertTrue(conditionList.any{ it%2 == 0})
+
+ }
+
+ @Test
+ void testGetUniqueItemsInAList(){
+ assertTrue([1, 3, 3, 4].toUnique() == [1, 3, 4])
+
+ def uniqueList = [1, 3, 3, 4]
+ uniqueList.unique()
+ assertTrue(uniqueList == [1, 3, 4])
+
+ assertTrue(["A", "B", "Ba", "Bat", "Cat"].toUnique{ it.size()} == ["A", "Ba", "Bat"])
+ }
+
+ @Test
+ void testSorting(){
+
+ assertTrue([1, 2, 1, 0].sort() == [0, 1, 1, 2])
+ Comparator mc = {a,b -> a == b? 0: a < b? 1 : -1}
+
+ def list = [1, 2, 1, 0]
+ list.sort(mc)
+ assertTrue(list == [2, 1, 1, 0])
+
+ def strList = ["na", "ppp", "as"]
+ assertTrue(strList.max() == "ppp")
+
+ Comparator minc = {a,b -> a == b? 0: a < b? -1 : 1}
+ def numberList = [3, 2, 0, 7]
+ assertTrue(numberList.min(minc) == 0)
+ }
+}
\ No newline at end of file
diff --git a/core-groovy-collections/src/test/groovy/com/baeldung/map/MapTest.groovy b/core-groovy-collections/src/test/groovy/com/baeldung/map/MapTest.groovy
deleted file mode 100644
index c6105eb1c4..0000000000
--- a/core-groovy-collections/src/test/groovy/com/baeldung/map/MapTest.groovy
+++ /dev/null
@@ -1,148 +0,0 @@
-package com.baeldung.map;
-
-import static groovy.test.GroovyAssert.*
-import org.junit.Test
-
-class MapTest{
-
- @Test
- void createMap() {
-
- def emptyMap = [:]
- assertNotNull(emptyMap)
-
- assertTrue(emptyMap instanceof java.util.LinkedHashMap)
-
- def map = [name:"Jerry", age: 42, city: "New York"]
- assertTrue(map.size() == 3)
- }
-
- @Test
- void addItemsToMap() {
-
- def map = [name:"Jerry"]
-
- map["age"] = 42
-
- map.city = "New York"
-
- def hobbyLiteral = "hobby"
- def hobbyMap = [(hobbyLiteral): "Singing"]
- map.putAll(hobbyMap)
-
- assertTrue(map == [name:"Jerry", age: 42, city: "New York", hobby:"Singing"])
- assertTrue(hobbyMap.hobby == "Singing")
- assertTrue(hobbyMap[hobbyLiteral] == "Singing")
-
- map.plus([1:20]) // returns new map
-
- map << [2:30]
-
- }
-
- @Test
- void getItemsFromMap() {
-
- def map = [name:"Jerry", age: 42, city: "New York", hobby:"Singing"]
-
- assertTrue(map["name"] == "Jerry")
-
- assertTrue(map.name == "Jerry")
-
- def propertyAge = "age"
- assertTrue(map[propertyAge] == 42)
- }
-
- @Test
- void removeItemsFromMap() {
-
- def map = [1:20, a:30, 2:42, 4:34, ba:67, 6:39, 7:49]
-
- def minusMap = map.minus([2:42, 4:34]);
- assertTrue(minusMap == [1:20, a:30, ba:67, 6:39, 7:49])
-
- minusMap.removeAll{it -> it.key instanceof String}
- assertTrue( minusMap == [ 1:20, 6:39, 7:49])
-
- minusMap.retainAll{it -> it.value %2 == 0}
- assertTrue( minusMap == [1:20])
- }
-
- @Test
- void iteratingOnMaps(){
- def map = [name:"Jerry", age: 42, city: "New York", hobby:"Singing"]
-
- map.each{ entry -> println "$entry.key: $entry.value" }
-
- map.eachWithIndex{ entry, i -> println "$i $entry.key: $entry.value" }
-
- map.eachWithIndex{ key, value, i -> println "$i $key: $value" }
- }
-
- @Test
- void filteringAndSearchingMaps(){
- def map = [name:"Jerry", age: 42, city: "New York", hobby:"Singing"]
-
- assertTrue(map.find{ it.value == "New York"}.key == "city")
-
- assertTrue(map.findAll{ it.value == "New York"} == [city : "New York"])
-
- map.grep{it.value == "New York"}.each{ it -> assertTrue(it.key == "city" && it.value == "New York")}
-
- assertTrue(map.every{it -> it.value instanceof String} == false)
-
- assertTrue(map.any{it -> it.value instanceof String} == true)
- }
-
- @Test
- void collect(){
-
- def map = [1: [name:"Jerry", age: 42, city: "New York"],
- 2: [name:"Long", age: 25, city: "New York"],
- 3: [name:"Dustin", age: 29, city: "New York"],
- 4: [name:"Dustin", age: 34, city: "New York"]]
-
- def names = map.collect{entry -> entry.value.name} // returns only list
- assertTrue(names == ["Jerry", "Long", "Dustin", "Dustin"])
-
- def uniqueNames = map.collect([] as HashSet){entry -> entry.value.name}
- assertTrue(uniqueNames == ["Jerry", "Long", "Dustin"] as Set)
-
- def idNames = map.collectEntries{key, value -> [key, value.name]}
- assertTrue(idNames == [1:"Jerry", 2: "Long", 3:"Dustin", 4: "Dustin"])
-
- def below30Names = map.findAll{it.value.age < 30}.collect{key, value -> value.name}
- assertTrue(below30Names == ["Long", "Dustin"])
-
-
- }
-
- @Test
- void group(){
- def map = [1:20, 2: 40, 3: 11, 4: 93]
-
- def subMap = map.groupBy{it.value % 2}
- println subMap
- assertTrue(subMap == [0:[1:20, 2:40 ], 1:[3:11, 4:93]])
-
- def keySubMap = map.subMap([1, 2])
- assertTrue(keySubMap == [1:20, 2:40])
-
- }
-
- @Test
- void sorting(){
- def map = [ab:20, a: 40, cb: 11, ba: 93]
-
- def naturallyOrderedMap = map.sort()
- assertTrue([a:40, ab:20, ba:93, cb:11] == naturallyOrderedMap)
-
- def compSortedMap = map.sort({ k1, k2 -> k1 <=> k2 } as Comparator)
- assertTrue([a:40, ab:20, ba:93, cb:11] == compSortedMap)
-
- def cloSortedMap = map.sort({ it1, it2 -> it1.value <=> it1.value })
- assertTrue([cb:11, ab:20, a:40, ba:93] == cloSortedMap)
-
- }
-
-}
diff --git a/core-groovy-collections/src/test/groovy/com/baeldung/maps/MapTest.groovy b/core-groovy-collections/src/test/groovy/com/baeldung/maps/MapTest.groovy
new file mode 100644
index 0000000000..deb552c420
--- /dev/null
+++ b/core-groovy-collections/src/test/groovy/com/baeldung/maps/MapTest.groovy
@@ -0,0 +1,148 @@
+package com.baeldung.maps;
+
+import static groovy.test.GroovyAssert.*
+import org.junit.Test
+
+class MapTest{
+
+ @Test
+ void createMap() {
+
+ def emptyMap = [:]
+ assertNotNull(emptyMap)
+
+ assertTrue(emptyMap instanceof java.util.LinkedHashMap)
+
+ def map = [name:"Jerry", age: 42, city: "New York"]
+ assertTrue(map.size() == 3)
+ }
+
+ @Test
+ void addItemsToMap() {
+
+ def map = [name:"Jerry"]
+
+ map["age"] = 42
+
+ map.city = "New York"
+
+ def hobbyLiteral = "hobby"
+ def hobbyMap = [(hobbyLiteral): "Singing"]
+ map.putAll(hobbyMap)
+
+ assertTrue(map == [name:"Jerry", age: 42, city: "New York", hobby:"Singing"])
+ assertTrue(hobbyMap.hobby == "Singing")
+ assertTrue(hobbyMap[hobbyLiteral] == "Singing")
+
+ map.plus([1:20]) // returns new map
+
+ map << [2:30]
+
+ }
+
+ @Test
+ void getItemsFromMap() {
+
+ def map = [name:"Jerry", age: 42, city: "New York", hobby:"Singing"]
+
+ assertTrue(map["name"] == "Jerry")
+
+ assertTrue(map.name == "Jerry")
+
+ def propertyAge = "age"
+ assertTrue(map[propertyAge] == 42)
+ }
+
+ @Test
+ void removeItemsFromMap() {
+
+ def map = [1:20, a:30, 2:42, 4:34, ba:67, 6:39, 7:49]
+
+ def minusMap = map.minus([2:42, 4:34]);
+ assertTrue(minusMap == [1:20, a:30, ba:67, 6:39, 7:49])
+
+ minusMap.removeAll{it -> it.key instanceof String}
+ assertTrue( minusMap == [ 1:20, 6:39, 7:49])
+
+ minusMap.retainAll{it -> it.value %2 == 0}
+ assertTrue( minusMap == [1:20])
+ }
+
+ @Test
+ void iteratingOnMaps(){
+ def map = [name:"Jerry", age: 42, city: "New York", hobby:"Singing"]
+
+ map.each{ entry -> println "$entry.key: $entry.value" }
+
+ map.eachWithIndex{ entry, i -> println "$i $entry.key: $entry.value" }
+
+ map.eachWithIndex{ key, value, i -> println "$i $key: $value" }
+ }
+
+ @Test
+ void filteringAndSearchingMaps(){
+ def map = [name:"Jerry", age: 42, city: "New York", hobby:"Singing"]
+
+ assertTrue(map.find{ it.value == "New York"}.key == "city")
+
+ assertTrue(map.findAll{ it.value == "New York"} == [city : "New York"])
+
+ map.grep{it.value == "New York"}.each{ it -> assertTrue(it.key == "city" && it.value == "New York")}
+
+ assertTrue(map.every{it -> it.value instanceof String} == false)
+
+ assertTrue(map.any{it -> it.value instanceof String} == true)
+ }
+
+ @Test
+ void collect(){
+
+ def map = [1: [name:"Jerry", age: 42, city: "New York"],
+ 2: [name:"Long", age: 25, city: "New York"],
+ 3: [name:"Dustin", age: 29, city: "New York"],
+ 4: [name:"Dustin", age: 34, city: "New York"]]
+
+ def names = map.collect{entry -> entry.value.name} // returns only list
+ assertTrue(names == ["Jerry", "Long", "Dustin", "Dustin"])
+
+ def uniqueNames = map.collect([] as HashSet){entry -> entry.value.name}
+ assertTrue(uniqueNames == ["Jerry", "Long", "Dustin"] as Set)
+
+ def idNames = map.collectEntries{key, value -> [key, value.name]}
+ assertTrue(idNames == [1:"Jerry", 2: "Long", 3:"Dustin", 4: "Dustin"])
+
+ def below30Names = map.findAll{it.value.age < 30}.collect{key, value -> value.name}
+ assertTrue(below30Names == ["Long", "Dustin"])
+
+
+ }
+
+ @Test
+ void group(){
+ def map = [1:20, 2: 40, 3: 11, 4: 93]
+
+ def subMap = map.groupBy{it.value % 2}
+ println subMap
+ assertTrue(subMap == [0:[1:20, 2:40 ], 1:[3:11, 4:93]])
+
+ def keySubMap = map.subMap([1, 2])
+ assertTrue(keySubMap == [1:20, 2:40])
+
+ }
+
+ @Test
+ void sorting(){
+ def map = [ab:20, a: 40, cb: 11, ba: 93]
+
+ def naturallyOrderedMap = map.sort()
+ assertTrue([a:40, ab:20, ba:93, cb:11] == naturallyOrderedMap)
+
+ def compSortedMap = map.sort({ k1, k2 -> k1 <=> k2 } as Comparator)
+ assertTrue([a:40, ab:20, ba:93, cb:11] == compSortedMap)
+
+ def cloSortedMap = map.sort({ it1, it2 -> it1.value <=> it1.value })
+ assertTrue([cb:11, ab:20, a:40, ba:93] == cloSortedMap)
+
+ }
+
+}
diff --git a/core-groovy/README.md b/core-groovy/README.md
index 321c37be8d..25a0aece3a 100644
--- a/core-groovy/README.md
+++ b/core-groovy/README.md
@@ -1,15 +1,15 @@
-# Groovy
+# Core Groovy
+
+This module contains articles about core Groovy concepts
## Relevant articles:
-- [JDBC with Groovy](http://www.baeldung.com/jdbc-groovy)
-- [Working with JSON in Groovy](http://www.baeldung.com/groovy-json)
+- [JDBC with Groovy](https://www.baeldung.com/jdbc-groovy)
+- [Working with JSON in Groovy](https://www.baeldung.com/groovy-json)
- [Reading a File in Groovy](https://www.baeldung.com/groovy-file-read)
- [Types of Strings in Groovy](https://www.baeldung.com/groovy-strings)
-- [A Quick Guide to Iterating a Map in Groovy](https://www.baeldung.com/groovy-map-iterating)
- [An Introduction to Traits in Groovy](https://www.baeldung.com/groovy-traits)
- [Closures in Groovy](https://www.baeldung.com/groovy-closures)
-- [Finding Elements in Collections in Groovy](https://www.baeldung.com/groovy-collections-find-elements)
-- [Lists in Groovy](https://www.baeldung.com/groovy-lists)
- [Converting a String to a Date in Groovy](https://www.baeldung.com/groovy-string-to-date)
-- [Guide to I/O in Groovy](https://www.baeldung.com/groovy-io)
\ No newline at end of file
+- [Guide to I/O in Groovy](https://www.baeldung.com/groovy-io)
+- [[More -->]](/core-groovy-2)
\ No newline at end of file
diff --git a/core-groovy/pom.xml b/core-groovy/pom.xml
index 029e5460ab..80d6f8d2d5 100644
--- a/core-groovy/pom.xml
+++ b/core-groovy/pom.xml
@@ -109,9 +109,6 @@
1.0.0
-
-
-
2.5.6
2.5.6
2.5.6
diff --git a/core-groovy/src/test/groovy/com/baeldung/lists/ListTest.groovy b/core-groovy/src/test/groovy/com/baeldung/lists/ListTest.groovy
deleted file mode 100644
index 7771028132..0000000000
--- a/core-groovy/src/test/groovy/com/baeldung/lists/ListTest.groovy
+++ /dev/null
@@ -1,173 +0,0 @@
-package com.baeldung.groovy.lists
-
-import static groovy.test.GroovyAssert.*
-import org.junit.Test
-
-class ListTest{
-
- @Test
- void testCreateList() {
-
- def list = [1, 2, 3]
- assertNotNull(list)
-
- def listMix = ['A', "b", 1, true]
- assertTrue(listMix == ['A', "b", 1, true])
-
- def linkedList = [1, 2, 3] as LinkedList
- assertTrue(linkedList instanceof LinkedList)
-
- ArrayList arrList = [1, 2, 3]
- assertTrue(arrList.class == ArrayList)
-
- def copyList = new ArrayList(arrList)
- assertTrue(copyList == arrList)
-
- def cloneList = arrList.clone()
- assertTrue(cloneList == arrList)
- }
-
- @Test
- void testCreateEmptyList() {
-
- def emptyList = []
- assertTrue(emptyList.size() == 0)
- }
-
- @Test
- void testCompareTwoLists() {
-
- def list1 = [5, 6.0, 'p']
- def list2 = [5, 6.0, 'p']
- assertTrue(list1 == list2)
- }
-
- @Test
- void testGetItemsFromList(){
-
- def list = ["Hello", "World"]
-
- assertTrue(list.get(1) == "World")
- assertTrue(list[1] == "World")
- assertTrue(list[-1] == "World")
- assertTrue(list.getAt(1) == "World")
- assertTrue(list.getAt(-2) == "Hello")
- }
-
- @Test
- void testAddItemsToList() {
-
- def list = []
-
- list << 1
- list.add("Apple")
- assertTrue(list == [1, "Apple"])
-
- list[2] = "Box"
- list[4] = true
- assertTrue(list == [1, "Apple", "Box", null, true])
-
- list.add(1, 6.0)
- assertTrue(list == [1, 6.0, "Apple", "Box", null, true])
-
- def list2 = [1, 2]
- list += list2
- list += 12
- assertTrue(list == [1, 6.0, "Apple", "Box", null, true, 1, 2, 12])
- }
-
- @Test
- void testUpdateItemsInList() {
-
- def list =[1, "Apple", 80, "App"]
- list[1] = "Box"
- list.set(2,90)
- assertTrue(list == [1, "Box", 90, "App"])
- }
-
- @Test
- void testRemoveItemsFromList(){
-
- def list = [1, 2, 3, 4, 5, 5, 6, 6, 7]
-
- list.remove(3)
- assertTrue(list == [1, 2, 3, 5, 5, 6, 6, 7])
-
- list.removeElement(5)
- assertTrue(list == [1, 2, 3, 5, 6, 6, 7])
-
- assertTrue(list - 6 == [1, 2, 3, 5, 7])
- }
-
- @Test
- void testIteratingOnAList(){
-
- def list = [1, "App", 3, 4]
- list.each{ println it * 2}
-
- list.eachWithIndex{ it, i -> println "$i : $it" }
- }
-
- @Test
- void testCollectingToAnotherList(){
-
- def list = ["Kay", "Henry", "Justin", "Tom"]
- assertTrue(list.collect{"Hi " + it} == ["Hi Kay", "Hi Henry", "Hi Justin", "Hi Tom"])
- }
-
- @Test
- void testJoinItemsInAList(){
- assertTrue(["One", "Two", "Three"].join(",") == "One,Two,Three")
- }
-
- @Test
- void testFilteringOnLists(){
- def filterList = [2, 1, 3, 4, 5, 6, 76]
-
- assertTrue(filterList.find{it > 3} == 4)
-
- assertTrue(filterList.findAll{it > 3} == [4, 5, 6, 76])
-
- assertTrue(filterList.findAll{ it instanceof Number} == [2, 1, 3, 4, 5, 6, 76])
-
- assertTrue(filterList.grep( Number )== [2, 1, 3, 4, 5, 6, 76])
-
- assertTrue(filterList.grep{ it> 6 }== [76])
-
- def conditionList = [2, 1, 3, 4, 5, 6, 76]
-
- assertFalse(conditionList.every{ it < 6})
-
- assertTrue(conditionList.any{ it%2 == 0})
-
- }
-
- @Test
- void testGetUniqueItemsInAList(){
- assertTrue([1, 3, 3, 4].toUnique() == [1, 3, 4])
-
- def uniqueList = [1, 3, 3, 4]
- uniqueList.unique()
- assertTrue(uniqueList == [1, 3, 4])
-
- assertTrue(["A", "B", "Ba", "Bat", "Cat"].toUnique{ it.size()} == ["A", "Ba", "Bat"])
- }
-
- @Test
- void testSorting(){
-
- assertTrue([1, 2, 1, 0].sort() == [0, 1, 1, 2])
- Comparator mc = {a,b -> a == b? 0: a < b? 1 : -1}
-
- def list = [1, 2, 1, 0]
- list.sort(mc)
- assertTrue(list == [2, 1, 1, 0])
-
- def strList = ["na", "ppp", "as"]
- assertTrue(strList.max() == "ppp")
-
- Comparator minc = {a,b -> a == b? 0: a < b? -1 : 1}
- def numberList = [3, 2, 0, 7]
- assertTrue(numberList.min(minc) == 0)
- }
-}
\ No newline at end of file
diff --git a/core-groovy/src/test/groovy/com/baeldung/lists/ListUnitTest.groovy b/core-groovy/src/test/groovy/com/baeldung/lists/ListUnitTest.groovy
deleted file mode 100644
index 9617c099ce..0000000000
--- a/core-groovy/src/test/groovy/com/baeldung/lists/ListUnitTest.groovy
+++ /dev/null
@@ -1,58 +0,0 @@
-package com.baeldung.lists
-
-import com.baeldung.Person
-import org.junit.Test
-
-import static org.junit.Assert.*
-
-class ListUnitTest {
-
- private final personList = [
- new Person("Regina", "Fitzpatrick", 25),
- new Person("Abagail", "Ballard", 26),
- new Person("Lucian", "Walter", 30),
- ]
-
- @Test
- void whenListContainsElement_thenCheckReturnsTrue() {
- def list = ['a', 'b', 'c']
-
- assertTrue(list.indexOf('a') > -1)
- assertTrue(list.contains('a'))
- }
-
- @Test
- void whenListContainsElement_thenCheckWithMembershipOperatorReturnsTrue() {
- def list = ['a', 'b', 'c']
-
- assertTrue('a' in list)
- }
-
- @Test
- void givenListOfPerson_whenUsingStreamMatching_thenShouldEvaluateList() {
- assertTrue(personList.stream().anyMatch {it.age > 20})
- assertFalse(personList.stream().allMatch {it.age < 30})
- }
-
- @Test
- void givenListOfPerson_whenUsingCollectionMatching_thenShouldEvaluateList() {
- assertTrue(personList.any {it.age > 20})
- assertFalse(personList.every {it.age < 30})
- }
-
- @Test
- void givenListOfPerson_whenUsingStreamFind_thenShouldReturnMatchingElements() {
- assertTrue(personList.stream().filter {it.age > 20}.findAny().isPresent())
- assertFalse(personList.stream().filter {it.age > 30}.findAny().isPresent())
- assertTrue(personList.stream().filter {it.age > 20}.findAll().size() == 3)
- assertTrue(personList.stream().filter {it.age > 30}.findAll().isEmpty())
- }
-
- @Test
- void givenListOfPerson_whenUsingCollectionFind_thenShouldReturnMatchingElements() {
- assertNotNull(personList.find {it.age > 20})
- assertNull(personList.find {it.age > 30})
- assertTrue(personList.findAll {it.age > 20}.size() == 3)
- assertTrue(personList.findAll {it.age > 30}.isEmpty())
- }
-}
diff --git a/core-groovy/src/test/groovy/com/baeldung/map/MapTest.groovy b/core-groovy/src/test/groovy/com/baeldung/map/MapTest.groovy
deleted file mode 100644
index f1d528207f..0000000000
--- a/core-groovy/src/test/groovy/com/baeldung/map/MapTest.groovy
+++ /dev/null
@@ -1,148 +0,0 @@
-package com.baeldung.groovy.map;
-
-import static groovy.test.GroovyAssert.*
-import org.junit.Test
-
-class MapTest{
-
- @Test
- void createMap() {
-
- def emptyMap = [:]
- assertNotNull(emptyMap)
-
- assertTrue(emptyMap instanceof java.util.LinkedHashMap)
-
- def map = [name:"Jerry", age: 42, city: "New York"]
- assertTrue(map.size() == 3)
- }
-
- @Test
- void addItemsToMap() {
-
- def map = [name:"Jerry"]
-
- map["age"] = 42
-
- map.city = "New York"
-
- def hobbyLiteral = "hobby"
- def hobbyMap = [(hobbyLiteral): "Singing"]
- map.putAll(hobbyMap)
-
- assertTrue(map == [name:"Jerry", age: 42, city: "New York", hobby:"Singing"])
- assertTrue(hobbyMap.hobby == "Singing")
- assertTrue(hobbyMap[hobbyLiteral] == "Singing")
-
- map.plus([1:20]) // returns new map
-
- map << [2:30]
-
- }
-
- @Test
- void getItemsFromMap() {
-
- def map = [name:"Jerry", age: 42, city: "New York", hobby:"Singing"]
-
- assertTrue(map["name"] == "Jerry")
-
- assertTrue(map.name == "Jerry")
-
- def propertyAge = "age"
- assertTrue(map[propertyAge] == 42)
- }
-
- @Test
- void removeItemsFromMap() {
-
- def map = [1:20, a:30, 2:42, 4:34, ba:67, 6:39, 7:49]
-
- def minusMap = map.minus([2:42, 4:34]);
- assertTrue(minusMap == [1:20, a:30, ba:67, 6:39, 7:49])
-
- minusMap.removeAll{it -> it.key instanceof String}
- assertTrue( minusMap == [ 1:20, 6:39, 7:49])
-
- minusMap.retainAll{it -> it.value %2 == 0}
- assertTrue( minusMap == [1:20])
- }
-
- @Test
- void iteratingOnMaps(){
- def map = [name:"Jerry", age: 42, city: "New York", hobby:"Singing"]
-
- map.each{ entry -> println "$entry.key: $entry.value" }
-
- map.eachWithIndex{ entry, i -> println "$i $entry.key: $entry.value" }
-
- map.eachWithIndex{ key, value, i -> println "$i $key: $value" }
- }
-
- @Test
- void filteringAndSearchingMaps(){
- def map = [name:"Jerry", age: 42, city: "New York", hobby:"Singing"]
-
- assertTrue(map.find{ it.value == "New York"}.key == "city")
-
- assertTrue(map.findAll{ it.value == "New York"} == [city : "New York"])
-
- map.grep{it.value == "New York"}.each{ it -> assertTrue(it.key == "city" && it.value == "New York")}
-
- assertTrue(map.every{it -> it.value instanceof String} == false)
-
- assertTrue(map.any{it -> it.value instanceof String} == true)
- }
-
- @Test
- void collect(){
-
- def map = [1: [name:"Jerry", age: 42, city: "New York"],
- 2: [name:"Long", age: 25, city: "New York"],
- 3: [name:"Dustin", age: 29, city: "New York"],
- 4: [name:"Dustin", age: 34, city: "New York"]]
-
- def names = map.collect{entry -> entry.value.name} // returns only list
- assertTrue(names == ["Jerry", "Long", "Dustin", "Dustin"])
-
- def uniqueNames = map.collect([] as HashSet){entry -> entry.value.name}
- assertTrue(uniqueNames == ["Jerry", "Long", "Dustin"] as Set)
-
- def idNames = map.collectEntries{key, value -> [key, value.name]}
- assertTrue(idNames == [1:"Jerry", 2: "Long", 3:"Dustin", 4: "Dustin"])
-
- def below30Names = map.findAll{it.value.age < 30}.collect{key, value -> value.name}
- assertTrue(below30Names == ["Long", "Dustin"])
-
-
- }
-
- @Test
- void group(){
- def map = [1:20, 2: 40, 3: 11, 4: 93]
-
- def subMap = map.groupBy{it.value % 2}
- println subMap
- assertTrue(subMap == [0:[1:20, 2:40 ], 1:[3:11, 4:93]])
-
- def keySubMap = map.subMap([1, 2])
- assertTrue(keySubMap == [1:20, 2:40])
-
- }
-
- @Test
- void sorting(){
- def map = [ab:20, a: 40, cb: 11, ba: 93]
-
- def naturallyOrderedMap = map.sort()
- assertTrue([a:40, ab:20, ba:93, cb:11] == naturallyOrderedMap)
-
- def compSortedMap = map.sort({ k1, k2 -> k1 <=> k2 } as Comparator)
- assertTrue([a:40, ab:20, ba:93, cb:11] == compSortedMap)
-
- def cloSortedMap = map.sort({ it1, it2 -> it1.value <=> it1.value })
- assertTrue([cb:11, ab:20, a:40, ba:93] == cloSortedMap)
-
- }
-
-}
diff --git a/core-groovy/src/test/groovy/com/baeldung/map/MapUnitTest.groovy b/core-groovy/src/test/groovy/com/baeldung/map/MapUnitTest.groovy
deleted file mode 100644
index 0d6bbed04b..0000000000
--- a/core-groovy/src/test/groovy/com/baeldung/map/MapUnitTest.groovy
+++ /dev/null
@@ -1,154 +0,0 @@
-package com.baeldung.map
-
-import com.baeldung.Person
-import org.junit.Test
-
-import static org.junit.Assert.*
-
-class MapUnitTest {
-
- private final personMap = [
- Regina : new Person("Regina", "Fitzpatrick", 25),
- Abagail: new Person("Abagail", "Ballard", 26),
- Lucian : new Person("Lucian", "Walter", 30)
- ]
-
- @Test
- void whenUsingEach_thenMapIsIterated() {
- def map = [
- 'FF0000' : 'Red',
- '00FF00' : 'Lime',
- '0000FF' : 'Blue',
- 'FFFF00' : 'Yellow'
- ]
-
- map.each { println "Hex Code: $it.key = Color Name: $it.value" }
- }
-
- @Test
- void whenUsingEachWithEntry_thenMapIsIterated() {
- def map = [
- 'E6E6FA' : 'Lavender',
- 'D8BFD8' : 'Thistle',
- 'DDA0DD' : 'Plum',
- ]
-
- map.each { entry -> println "Hex Code: $entry.key = Color Name: $entry.value" }
- }
-
- @Test
- void whenUsingEachWithKeyAndValue_thenMapIsIterated() {
- def map = [
- '000000' : 'Black',
- 'FFFFFF' : 'White',
- '808080' : 'Gray'
- ]
-
- map.each { key, val ->
- println "Hex Code: $key = Color Name $val"
- }
- }
-
- @Test
- void whenUsingEachWithIndexAndEntry_thenMapIsIterated() {
- def map = [
- '800080' : 'Purple',
- '4B0082' : 'Indigo',
- '6A5ACD' : 'Slate Blue'
- ]
-
- map.eachWithIndex { entry, index ->
- def indent = ((index == 0 || index % 2 == 0) ? " " : "")
- println "$indent Hex Code: $entry.key = Color Name: $entry.value"
- }
- }
-
- @Test
- void whenUsingEachWithIndexAndKeyAndValue_thenMapIsIterated() {
- def map = [
- 'FFA07A' : 'Light Salmon',
- 'FF7F50' : 'Coral',
- 'FF6347' : 'Tomato',
- 'FF4500' : 'Orange Red'
- ]
-
- map.eachWithIndex { key, val, index ->
- def indent = ((index == 0 || index % 2 == 0) ? " " : "")
- println "$indent Hex Code: $key = Color Name: $val"
- }
- }
-
- @Test
- void whenUsingForLoop_thenMapIsIterated() {
- def map = [
- '2E8B57' : 'Seagreen',
- '228B22' : 'Forest Green',
- '008000' : 'Green'
- ]
-
- for (entry in map) {
- println "Hex Code: $entry.key = Color Name: $entry.value"
- }
- }
-
- @Test
- void whenMapContainsKeyElement_thenCheckReturnsTrue() {
- def map = [a: 'd', b: 'e', c: 'f']
-
- assertTrue(map.containsKey('a'))
- assertFalse(map.containsKey('e'))
- assertTrue(map.containsValue('e'))
- }
-
- @Test
- void whenMapContainsKeyElement_thenCheckByMembershipReturnsTrue() {
- def map = [a: 'd', b: 'e', c: 'f']
-
- assertTrue('a' in map)
- assertFalse('f' in map)
- }
-
- @Test
- void whenMapContainsFalseBooleanValues_thenCheckReturnsFalse() {
- def map = [a: true, b: false, c: null]
-
- assertTrue(map.containsKey('b'))
- assertTrue('a' in map)
- assertFalse('b' in map)
- assertFalse('c' in map)
- }
-
- @Test
- void givenMapOfPerson_whenUsingStreamMatching_thenShouldEvaluateMap() {
- assertTrue(personMap.keySet().stream().anyMatch {it == "Regina"})
- assertFalse(personMap.keySet().stream().allMatch {it == "Albert"})
- assertFalse(personMap.values().stream().allMatch {it.age < 30})
- assertTrue(personMap.entrySet().stream().anyMatch {it.key == "Abagail" && it.value.lastname == "Ballard"})
- }
-
- @Test
- void givenMapOfPerson_whenUsingCollectionMatching_thenShouldEvaluateMap() {
- assertTrue(personMap.keySet().any {it == "Regina"})
- assertFalse(personMap.keySet().every {it == "Albert"})
- assertFalse(personMap.values().every {it.age < 30})
- assertTrue(personMap.any {firstname, person -> firstname == "Abagail" && person.lastname == "Ballard"})
- }
-
- @Test
- void givenMapOfPerson_whenUsingCollectionFind_thenShouldReturnElements() {
- assertNotNull(personMap.find {it.key == "Abagail" && it.value.lastname == "Ballard"})
- assertTrue(personMap.findAll {it.value.age > 20}.size() == 3)
- }
-
- @Test
- void givenMapOfPerson_whenUsingStreamFind_thenShouldReturnElements() {
- assertTrue(
- personMap.entrySet().stream()
- .filter {it.key == "Abagail" && it.value.lastname == "Ballard"}
- .findAny().isPresent())
- assertTrue(
- personMap.entrySet().stream()
- .filter {it.value.age > 20}
- .findAll().size() == 3)
- }
-}
diff --git a/core-groovy/src/test/groovy/com/baeldung/set/SetUnitTest.groovy b/core-groovy/src/test/groovy/com/baeldung/set/SetUnitTest.groovy
deleted file mode 100644
index 1248c9ac91..0000000000
--- a/core-groovy/src/test/groovy/com/baeldung/set/SetUnitTest.groovy
+++ /dev/null
@@ -1,16 +0,0 @@
-package com.baeldung.set
-
-import org.junit.Test
-
-import static org.junit.Assert.assertTrue
-
-class SetUnitTest {
-
- @Test
- void whenSetContainsElement_thenCheckReturnsTrue() {
- def set = ['a', 'b', 'c'] as Set
-
- assertTrue(set.contains('a'))
- assertTrue('a' in set)
- }
-}
\ No newline at end of file
diff --git a/core-java-arrays/README.MD b/core-java-arrays/README.MD
deleted file mode 100644
index 9ee6998784..0000000000
--- a/core-java-arrays/README.MD
+++ /dev/null
@@ -1,3 +0,0 @@
-## Relevant Articles
-
-- [Extending an Array’s Length](https://www.baeldung.com/java-array-add-element-at-the-end)
diff --git a/core-java-modules/README.md b/core-java-modules/README.md
index 7a7d0a7a1b..55dacca916 100644
--- a/core-java-modules/README.md
+++ b/core-java-modules/README.md
@@ -1,5 +1,10 @@
+## Core Java Modules
+
+This module contains modules about core Java
+
## Relevant articles:
- [Multi-Module Maven Application with Java Modules](https://www.baeldung.com/maven-multi-module-project-java-jpms)
- [Guide to Java FileChannel](https://www.baeldung.com/java-filechannel)
- [Understanding the NumberFormatException in Java](https://www.baeldung.com/java-number-format-exception)
+- [Will an Error Be Caught by Catch Block in Java?](https://www.baeldung.com/java-error-catch)
diff --git a/core-java-modules/core-java-10/README.md b/core-java-modules/core-java-10/README.md
index f0a25712a7..2b57ec9064 100644
--- a/core-java-modules/core-java-10/README.md
+++ b/core-java-modules/core-java-10/README.md
@@ -1,3 +1,6 @@
+## Core Java 10
+
+This module contains articles about Java 10 core features
### Relevant Articles:
@@ -5,3 +8,4 @@
- [Guide to Java 10](http://www.baeldung.com/java-10-overview)
- [Copy a List to Another List in Java](http://www.baeldung.com/java-copy-list-to-another)
- [Deep Dive Into the New Java JIT Compiler – Graal](https://www.baeldung.com/graal-java-jit-compiler)
+- [Copying Sets in Java](https://www.baeldung.com/java-copy-sets)
diff --git a/core-java-modules/core-java-11/README.md b/core-java-modules/core-java-11/README.md
index 11c7d9d388..514f24a4ae 100644
--- a/core-java-modules/core-java-11/README.md
+++ b/core-java-modules/core-java-11/README.md
@@ -1,3 +1,7 @@
+## Core Java 11
+
+This module contains articles about Java 11 core features
+
### Relevant articles
- [Java 11 Single File Source Code](https://www.baeldung.com/java-single-file-source-code)
diff --git a/core-java-modules/core-java-11/src/main/java/com/baeldung/predicate/not/README.md b/core-java-modules/core-java-11/src/main/java/com/baeldung/predicate/not/README.md
new file mode 100644
index 0000000000..6f8f95970e
--- /dev/null
+++ b/core-java-modules/core-java-11/src/main/java/com/baeldung/predicate/not/README.md
@@ -0,0 +1,3 @@
+## Relevant articles:
+
+- [Negate a Predicate Method Reference with Java 11](https://www.baeldung.com/java-negate-predicate-method-reference)
diff --git a/core-java-modules/core-java-12/README.md b/core-java-modules/core-java-12/README.md
new file mode 100644
index 0000000000..6c603e4dea
--- /dev/null
+++ b/core-java-modules/core-java-12/README.md
@@ -0,0 +1,4 @@
+## Relevant Articles:
+
+
+- [String API Updates in Java 12](https://www.baeldung.com/java12-string-api)
diff --git a/core-java-modules/core-java-8-2/README.md b/core-java-modules/core-java-8-2/README.md
index d53b731878..9201add1d7 100644
--- a/core-java-modules/core-java-8-2/README.md
+++ b/core-java-modules/core-java-8-2/README.md
@@ -1,8 +1,11 @@
-=========
+## Core Java 8 (part 2)
-## Core Java 8 Cookbooks and Examples (part 2)
+This module contains articles about Java 8 core features
### Relevant Articles:
- [Anonymous Classes in Java](http://www.baeldung.com/)
- [How to Delay Code Execution in Java](https://www.baeldung.com/java-delay-code-execution)
-- [Run JAR Application With Command Line Arguments](https://www.baeldung.com/java-run-jar-with-arguments)
+- [Run a Java Application from the Command Line](https://www.baeldung.com/java-run-jar-with-arguments)
+- [Java 8 Stream skip() vs limit()](https://www.baeldung.com/java-stream-skip-vs-limit)
+- [Guide to Java BiFunction Interface](https://www.baeldung.com/java-bifunction-interface)
+- [[<-- Prev]](/core-java-modules/core-java-8)
\ No newline at end of file
diff --git a/core-java-modules/core-java-8-2/pom.xml b/core-java-modules/core-java-8-2/pom.xml
index cc184de529..269631e0dd 100644
--- a/core-java-modules/core-java-8-2/pom.xml
+++ b/core-java-modules/core-java-8-2/pom.xml
@@ -3,13 +3,11 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4.0.0
- com.baeldung
core-java-8-2
0.1.0-SNAPSHOT
core-java-8-2
jar
-
com.baeldung
parent-java
@@ -17,33 +15,22 @@
../../parent-java
-
- UTF-8
- 1.8
- 1.8
- 64.2
-
-
com.ibm.icu
icu4j
${icu.version}
+
+ org.assertj
+ assertj-core
+ ${assertj.version}
+ test
+
-
-
-
- org.apache.maven.plugins
- maven-compiler-plugin
- ${maven-compiler-plugin.version}
-
- ${maven.compiler.source}
- ${maven.compiler.target}
-
-
-
-
-
+
+ 64.2
+ 3.12.2
+
diff --git a/core-java-modules/core-java-8-2/src/test/java/com/baeldung/bifunction/BiFunctionalInterfacesUnitTest.java b/core-java-modules/core-java-8-2/src/test/java/com/baeldung/bifunction/BiFunctionalInterfacesUnitTest.java
new file mode 100644
index 0000000000..ea63409c88
--- /dev/null
+++ b/core-java-modules/core-java-8-2/src/test/java/com/baeldung/bifunction/BiFunctionalInterfacesUnitTest.java
@@ -0,0 +1,164 @@
+package com.baeldung.bifunction;
+
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.List;
+import java.util.function.BiFunction;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class BiFunctionalInterfacesUnitTest {
+ @Test
+ public void givenStreamValues_whenMappedToNewValues() {
+ List mapped = Stream.of("hello", "world")
+ .map(word -> word + "!")
+ .collect(Collectors.toList());
+
+ assertThat(mapped).containsExactly("hello!", "world!");
+ }
+
+ @Test
+ public void givenStreamValues_whenReducedWithPrefixingOperation() {
+ String result = Stream.of("hello", "world")
+ .reduce("", (a, b) -> b + "-" + a);
+
+ assertThat(result).isEqualTo("world-hello-");
+ }
+
+ @Test
+ public void givenStreamValues_whenReducedWithPrefixingLambda_thenHasNoTrailingDash() {
+ String result = Stream.of("hello", "world")
+ .reduce("", (a, b) -> combineWithoutTrailingDash(a, b));
+
+ assertThat(result).isEqualTo("world-hello");
+ }
+
+ private String combineWithoutTrailingDash(String a, String b) {
+ if (a.isEmpty()) {
+ return b;
+ }
+ return b + "-" + a;
+ }
+
+ @Test
+ public void givenStreamValues_whenReducedWithPrefixingMethodReference_thenHasNoTrailingDash() {
+ String result = Stream.of("hello", "world")
+ .reduce("", this::combineWithoutTrailingDash);
+
+ assertThat(result).isEqualTo("world-hello");
+ }
+
+ @Test
+ public void givenTwoLists_whenCombined() {
+ List list1 = Arrays.asList("a", "b", "c");
+ List list2 = Arrays.asList(1, 2, 3);
+
+ List result = new ArrayList<>();
+ for (int i=0; i < list1.size(); i++) {
+ result.add(list1.get(i) + list2.get(i));
+ }
+
+ assertThat(result).containsExactly("a1", "b2", "c3");
+ }
+
+ @Test
+ public void givenTwoLists_whenCombinedWithGeneralPurposeCombiner() {
+ List list1 = Arrays.asList("a", "b", "c");
+ List list2 = Arrays.asList(1, 2, 3);
+
+ List result = listCombiner(list1, list2, (a, b) -> a + b);
+
+ assertThat(result).containsExactly("a1", "b2", "c3");
+ }
+
+ private static List listCombiner(List list1,
+ List list2,
+ BiFunction combiner) {
+ List result = new ArrayList<>();
+ for (int i = 0; i < list1.size(); i++) {
+ result.add(combiner.apply(list1.get(i), list2.get(i)));
+ }
+ return result;
+ }
+
+ @Test
+ public void givenTwoLists_whenComparedWithCombiningFunction() {
+ List list1 = Arrays.asList(1.0d, 2.1d, 3.3d);
+ List list2 = Arrays.asList(0.1f, 0.2f, 4f);
+
+ // algorithm to determine if the value in list1 > value in list 2
+ List result = listCombiner(list1, list2, (a, b) -> a > b);
+
+ assertThat(result).containsExactly(true, true, false);
+ }
+
+ @Test
+ public void givenTwoLists_whenComparedWithCombiningFunctionByMethodReference() {
+ List list1 = Arrays.asList(1.0d, 2.1d, 3.3d);
+ List list2 = Arrays.asList(0.1f, 0.2f, 4f);
+
+ // algorithm to determine if the value in list1 > value in list 2
+ List result = listCombiner(list1, list2, this::firstIsGreaterThanSecond);
+
+ assertThat(result).containsExactly(true, true, false);
+ }
+
+ private boolean firstIsGreaterThanSecond(Double a, Float b) {
+ return a > b;
+ }
+
+ @Test
+ public void givenTwoLists_whenComparedForEqualityByCombiningFunction() {
+ List list1 = Arrays.asList(0.1f, 0.2f, 4f);
+ List list2 = Arrays.asList(0.1f, 0.2f, 4f);
+
+ List result = listCombiner(list1, list2, (a, b) -> a.equals(b));
+
+ assertThat(result).containsExactly(true, true, true);
+ }
+
+ @Test
+ public void givenTwoLists_whenComparedForEqualityByCombiningFunctionWithMethodReference() {
+ List list1 = Arrays.asList(0.1f, 0.2f, 4f);
+ List list2 = Arrays.asList(0.1f, 0.2f, 4f);
+
+ List result = listCombiner(list1, list2, Float::equals);
+
+ assertThat(result).containsExactly(true, true, true);
+ }
+
+ @Test
+ public void givenTwoLists_whenComparedWithCombiningFunctionWithCompareTo() {
+ List list1 = Arrays.asList(1.0d, 2.1d, 3.3d);
+ List list2 = Arrays.asList(0.1d, 0.2d, 4d);
+
+ List result = listCombiner(list1, list2, Double::compareTo);
+
+ assertThat(result).containsExactly(1, 1, -1);
+ }
+
+ /**
+ * Allows you to to pass in a lambda or method reference and then
+ * get access to the BiFunction it is meant to become
+ */
+ private static BiFunction asBiFunction(BiFunction function) {
+ return function;
+ }
+
+ @Test
+ public void givenTwoLists_whenComparedWithCombiningFunctionWithComposedBiFunction() {
+ List list1 = Arrays.asList(1.0d, 2.1d, 3.3d);
+ List list2 = Arrays.asList(0.1d, 0.2d, 4d);
+
+ List result = listCombiner(list1, list2,
+ asBiFunction(Double::compareTo)
+ .andThen(i -> i > 0));
+
+ assertThat(result).containsExactly(true, true, false);
+ }
+}
diff --git a/core-java-modules/core-java-8/README.md b/core-java-modules/core-java-8/README.md
index d11d2debce..72bdafe5fa 100644
--- a/core-java-modules/core-java-8/README.md
+++ b/core-java-modules/core-java-8/README.md
@@ -1,42 +1,15 @@
-=========
+## Core Java 8
-## Core Java 8 Cookbooks and Examples
+This module contains articles about Java 8 core features
### Relevant Articles:
-- [Guide to Java 8’s Collectors](http://www.baeldung.com/java-8-collectors)
-- [Functional Interfaces in Java 8](http://www.baeldung.com/java-8-functional-interfaces)
-- [Java 8 – Powerful Comparison with Lambdas](http://www.baeldung.com/java-8-sort-lambda)
-- [New Features in Java 8](http://www.baeldung.com/java-8-new-features)
-- [Lambda Expressions and Functional Interfaces: Tips and Best Practices](http://www.baeldung.com/java-8-lambda-expressions-tips)
-- [The Double Colon Operator in Java 8](http://www.baeldung.com/java-8-double-colon-operator)
-- [Guide to Java 8 groupingBy Collector](http://www.baeldung.com/java-groupingby-collector)
-- [Strategy Design Pattern in Java 8](http://www.baeldung.com/java-strategy-pattern)
-- [Exceptions in Java 8 Lambda Expressions](http://www.baeldung.com/java-lambda-exceptions)
-- [Guide to Java 8 Comparator.comparing()](http://www.baeldung.com/java-8-comparator-comparing)
-- [Guide To Java 8 Optional](http://www.baeldung.com/java-optional)
-- [Guide to the Java 8 forEach](http://www.baeldung.com/foreach-java)
-- [The Difference Between map() and flatMap()](http://www.baeldung.com/java-difference-map-and-flatmap)
-- [Static and Default Methods in Interfaces in Java](http://www.baeldung.com/java-static-default-methods)
-- [Efficient Word Frequency Calculator in Java](http://www.baeldung.com/java-word-frequency)
-- [Introduction to Spliterator in Java](http://www.baeldung.com/java-spliterator)
-- [Java 8 Math New Methods](http://www.baeldung.com/java-8-math)
-- [Overview of Java Built-in Annotations](http://www.baeldung.com/java-default-annotations)
-- [Finding Min/Max in an Array with Java](http://www.baeldung.com/java-array-min-max)
-- [Internationalization and Localization in Java 8](http://www.baeldung.com/java-8-localization)
-- [Java Optional – orElse() vs orElseGet()](http://www.baeldung.com/java-optional-or-else-vs-or-else-get)
-- [Method Parameter Reflection in Java](http://www.baeldung.com/java-parameter-reflection)
-- [Java 8 Unsigned Arithmetic Support](http://www.baeldung.com/java-unsigned-arithmetic)
-- [Generalized Target-Type Inference in Java](http://www.baeldung.com/java-generalized-target-type-inference)
-- [Overriding System Time for Testing in Java](http://www.baeldung.com/java-override-system-time)
-- [Set the Time Zone of a Date in Java](https://www.baeldung.com/java-set-date-time-zone)
-- [An Overview of Regular Expressions Performance in Java](https://www.baeldung.com/java-regex-performance)
-- [Java Primitives versus Objects](https://www.baeldung.com/java-primitives-vs-objects)
-- [How to Use if/else Logic in Java 8 Streams](https://www.baeldung.com/java-8-streams-if-else-logic)
-- [How to Replace Many if Statements in Java](https://www.baeldung.com/java-replace-if-statements)
-- [Java @Override Annotation](https://www.baeldung.com/java-override)
-- [Java @SuppressWarnings Annotation](https://www.baeldung.com/java-suppresswarnings)
-- [Java @SafeVarargs Annotation](https://www.baeldung.com/java-safevarargs)
-- [Java @Deprecated Annotation](https://www.baeldung.com/java-deprecated)
-- [Java 8 Predicate Chain](https://www.baeldung.com/java-predicate-chain)
-- [Method References in Java](https://www.baeldung.com/java-method-references)
-- [Creating a Custom Annotation in Java](https://www.baeldung.com/java-custom-annotation)
+- [New Features in Java 8](https://www.baeldung.com/java-8-new-features)
+- [Guide to Java 8 groupingBy Collector](https://www.baeldung.com/java-groupingby-collector)
+- [Strategy Design Pattern in Java 8](https://www.baeldung.com/java-strategy-pattern)
+- [Guide to Java 8 Comparator.comparing()](https://www.baeldung.com/java-8-comparator-comparing)
+- [Guide to the Java 8 forEach](https://www.baeldung.com/foreach-java)
+- [Introduction to Spliterator in Java](https://www.baeldung.com/java-spliterator)
+- [Finding Min/Max in an Array with Java](https://www.baeldung.com/java-array-min-max)
+- [Internationalization and Localization in Java 8](https://www.baeldung.com/java-8-localization)
+- [Generalized Target-Type Inference in Java](https://www.baeldung.com/java-generalized-target-type-inference)
+- [[More -->]](/core-java-modules/core-java-8-2)
diff --git a/core-java-modules/core-java-8/pom.xml b/core-java-modules/core-java-8/pom.xml
index c09c970e07..074359ae54 100644
--- a/core-java-modules/core-java-8/pom.xml
+++ b/core-java-modules/core-java-8/pom.xml
@@ -1,7 +1,6 @@
4.0.0
- com.baeldung
core-java-8
0.1.0-SNAPSHOT
core-java-8
@@ -20,31 +19,11 @@
commons-collections4
${commons-collections4.version}
-
- commons-io
- commons-io
- ${commons-io.version}
-
-
- org.apache.commons
- commons-lang3
- ${commons-lang3.version}
-
-
- org.apache.commons
- commons-math3
- ${commons-math3.version}
-
log4j
log4j
${log4j.version}
-
- commons-codec
- commons-codec
- ${commons-codec.version}
-
org.projectlombok
lombok
@@ -58,70 +37,6 @@
${assertj.version}
test
-
- com.jayway.awaitility
- awaitility
- ${avaitility.version}
- test
-
-
- org.openjdk.jmh
- jmh-core
- ${jmh-core.version}
-
-
- org.openjdk.jmh
- jmh-generator-annprocess
- ${jmh-generator.version}
-
-
- org.openjdk.jmh
- jmh-generator-bytecode
- ${jmh-generator.version}
-
-
- com.codepoetics
- protonpack
- ${protonpack.version}
-
-
- io.vavr
- vavr
- ${vavr.version}
-
-
- joda-time
- joda-time
- ${joda.version}
-
-
- org.aspectj
- aspectjrt
- ${asspectj.version}
-
-
- org.aspectj
- aspectjweaver
- ${asspectj.version}
-
-
- org.powermock
- powermock-module-junit4
- ${powermock.version}
- test
-
-
- org.powermock
- powermock-api-mockito2
- ${powermock.version}
- test
-
-
- org.jmockit
- jmockit
- ${jmockit.version}
- test
-
@@ -134,16 +49,6 @@
-
- org.apache.maven.plugins
- maven-compiler-plugin
- ${maven-compiler-plugin.version}
-
- 1.8
- 1.8
- -parameters
-
-
org.springframework.boot
spring-boot-maven-plugin
@@ -160,40 +65,15 @@
-
- maven-surefire-plugin
- ${maven-surefire-plugin.version}
-
-
- -javaagent:${settings.localRepository}/org/jmockit/jmockit/${jmockit.version}/jmockit-${jmockit.version}.jar
-
- true
-
-
- 3.5
- 3.6.1
4.1
- 4.01
- 1.10
- 0.9.0
- 1.13
- 2.10
3.6.1
- 1.8.9
- 2.0.0-RC.4
- 1.44
- 1.7.0
- 1.19
- 1.19
- 2.0.4.RELEASE
- 3.8.0
- 2.22.1
+ 2.0.4.RELEASE
diff --git a/core-java-modules/core-java-8/src/main/java/com/baeldung/java_8_features/groupingby/Tuple.java b/core-java-modules/core-java-8/src/main/java/com/baeldung/java_8_features/groupingby/Tuple.java
new file mode 100644
index 0000000000..82a84bb2d6
--- /dev/null
+++ b/core-java-modules/core-java-8/src/main/java/com/baeldung/java_8_features/groupingby/Tuple.java
@@ -0,0 +1,41 @@
+package com.baeldung.java_8_features.groupingby;
+
+import java.util.Objects;
+
+public class Tuple {
+ private final BlogPostType type;
+ private final String author;
+
+ public Tuple(BlogPostType type, String author) {
+ this.type = type;
+ this.author = author;
+ }
+
+ public BlogPostType getType() {
+ return type;
+ }
+
+ public String getAuthor() {
+ return author;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o)
+ return true;
+ if (o == null || getClass() != o.getClass())
+ return false;
+ Tuple tuple = (Tuple) o;
+ return type == tuple.type && author.equals(tuple.author);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(type, author);
+ }
+
+ @Override
+ public String toString() {
+ return "Tuple{" + "type=" + type + ", author='" + author + '\'' + '}';
+ }
+}
diff --git a/core-java-modules/core-java-8/src/main/java/com/baeldung/optional/PersonRepository.java b/core-java-modules/core-java-8/src/main/java/com/baeldung/optional/PersonRepository.java
deleted file mode 100644
index 46018faf80..0000000000
--- a/core-java-modules/core-java-8/src/main/java/com/baeldung/optional/PersonRepository.java
+++ /dev/null
@@ -1,9 +0,0 @@
-package com.baeldung.optional;
-
-public class PersonRepository {
-
- public String findNameById(String id) {
- return id == null ? null : "Name";
- }
-
-}
diff --git a/core-java-modules/core-java-8/src/test/java/com/baeldung/java8/JavaTryWithResourcesLongRunningUnitTest.java b/core-java-modules/core-java-8/src/test/java/com/baeldung/java8/JavaTryWithResourcesLongRunningUnitTest.java
deleted file mode 100644
index 32879aed0c..0000000000
--- a/core-java-modules/core-java-8/src/test/java/com/baeldung/java8/JavaTryWithResourcesLongRunningUnitTest.java
+++ /dev/null
@@ -1,92 +0,0 @@
-package com.baeldung.java8;
-
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.util.Date;
-import java.util.Scanner;
-
-import org.junit.Assert;
-import org.junit.Test;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class JavaTryWithResourcesLongRunningUnitTest {
-
- private static final Logger LOG = LoggerFactory.getLogger(JavaTryWithResourcesLongRunningUnitTest.class);
-
- private static final String TEST_STRING_HELLO_WORLD = "Hello World";
- private Date resource1Date, resource2Date;
-
- // tests
-
- /* Example for using Try_with_resources */
- @Test
- public void whenWritingToStringWriter_thenCorrectlyWritten() {
- final StringWriter sw = new StringWriter();
- try (PrintWriter pw = new PrintWriter(sw, true)) {
- pw.print(TEST_STRING_HELLO_WORLD);
- }
-
- Assert.assertEquals(sw.getBuffer()
- .toString(), TEST_STRING_HELLO_WORLD);
- }
-
- /* Example for using multiple resources */
- @Test
- public void givenStringToScanner_whenWritingToStringWriter_thenCorrectlyWritten() {
-
- final StringWriter sw = new StringWriter();
- try (Scanner sc = new Scanner(TEST_STRING_HELLO_WORLD); PrintWriter pw = new PrintWriter(sw, true)) {
- while (sc.hasNext()) {
- pw.print(sc.nextLine());
- }
- }
-
- Assert.assertEquals(sw.getBuffer()
- .toString(), TEST_STRING_HELLO_WORLD);
- }
-
- /* Example to show order in which the resources are closed */
- @Test
- public void whenFirstAutoClosableResourceIsinitializedFirst_thenFirstAutoClosableResourceIsReleasedFirst() throws Exception {
- try (AutoCloseableResourcesFirst af = new AutoCloseableResourcesFirst(); AutoCloseableResourcesSecond as = new AutoCloseableResourcesSecond()) {
- af.doSomething();
- as.doSomething();
- }
- Assert.assertTrue(resource1Date.after(resource2Date));
- }
-
- class AutoCloseableResourcesFirst implements AutoCloseable {
- public AutoCloseableResourcesFirst() {
- LOG.debug("Constructor -> AutoCloseableResources_First");
- }
-
- public void doSomething() {
- LOG.debug("Something -> AutoCloseableResources_First");
- }
-
- @Override
- public void close() throws Exception {
- LOG.debug("Closed AutoCloseableResources_First");
- resource1Date = new Date();
- }
- }
-
- class AutoCloseableResourcesSecond implements AutoCloseable {
- public AutoCloseableResourcesSecond() {
- LOG.debug("Constructor -> AutoCloseableResources_Second");
- }
-
- public void doSomething() {
- LOG.debug("Something -> AutoCloseableResources_Second");
- }
-
- @Override
- public void close() throws Exception {
- LOG.debug("Closed AutoCloseableResources_Second");
- resource2Date = new Date();
- Thread.sleep(10000);
- }
- }
-
-}
\ No newline at end of file
diff --git a/core-java-modules/core-java-8/src/test/java/com/baeldung/java8/Java8GroupingByCollectorUnitTest.java b/core-java-modules/core-java-8/src/test/java/com/baeldung/java_8_features/groupingby/Java8GroupingByCollectorUnitTest.java
similarity index 81%
rename from core-java-modules/core-java-8/src/test/java/com/baeldung/java8/Java8GroupingByCollectorUnitTest.java
rename to core-java-modules/core-java-8/src/test/java/com/baeldung/java_8_features/groupingby/Java8GroupingByCollectorUnitTest.java
index eea019da2c..1da705294e 100644
--- a/core-java-modules/core-java-8/src/test/java/com/baeldung/java8/Java8GroupingByCollectorUnitTest.java
+++ b/core-java-modules/core-java-8/src/test/java/com/baeldung/java_8_features/groupingby/Java8GroupingByCollectorUnitTest.java
@@ -1,15 +1,32 @@
-package com.baeldung.java8;
-
-import com.baeldung.java_8_features.groupingby.BlogPost;
-import com.baeldung.java_8_features.groupingby.BlogPostType;
-import org.junit.Test;
-
-import java.util.*;
-import java.util.concurrent.ConcurrentMap;
+package com.baeldung.java_8_features.groupingby;
import static java.util.Comparator.comparingInt;
-import static java.util.stream.Collectors.*;
-import static org.junit.Assert.*;
+import static java.util.stream.Collectors.averagingInt;
+import static java.util.stream.Collectors.counting;
+import static java.util.stream.Collectors.groupingBy;
+import static java.util.stream.Collectors.groupingByConcurrent;
+import static java.util.stream.Collectors.joining;
+import static java.util.stream.Collectors.mapping;
+import static java.util.stream.Collectors.maxBy;
+import static java.util.stream.Collectors.summarizingInt;
+import static java.util.stream.Collectors.summingInt;
+import static java.util.stream.Collectors.toList;
+import static java.util.stream.Collectors.toSet;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Arrays;
+import java.util.EnumMap;
+import java.util.IntSummaryStatistics;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.ConcurrentMap;
+
+import org.junit.Test;
public class Java8GroupingByCollectorUnitTest {
@@ -180,4 +197,19 @@ public class Java8GroupingByCollectorUnitTest {
assertEquals(15, newsLikeStatistics.getMin());
}
+ @Test
+ public void givenAListOfPosts_whenGroupedByComplexMapKeyType_thenGetAMapBetweenTupleAndList() {
+ Map> postsPerTypeAndAuthor = posts.stream()
+ .collect(groupingBy(post -> new Tuple(post.getType(), post.getAuthor())));
+
+ List result = postsPerTypeAndAuthor.get(new Tuple(BlogPostType.GUIDE, "Author 1"));
+
+ assertThat(result.size()).isEqualTo(1);
+
+ BlogPost blogPost = result.get(0);
+
+ assertThat(blogPost.getTitle()).isEqualTo("Programming guide");
+ assertThat(blogPost.getType()).isEqualTo(BlogPostType.GUIDE);
+ assertThat(blogPost.getAuthor()).isEqualTo("Author 1");
+ }
}
diff --git a/core-java-modules/core-java-8/src/test/java/com/baeldung/optional/PersonRepositoryUnitTest.java b/core-java-modules/core-java-8/src/test/java/com/baeldung/optional/PersonRepositoryUnitTest.java
deleted file mode 100644
index 4efa625ccd..0000000000
--- a/core-java-modules/core-java-8/src/test/java/com/baeldung/optional/PersonRepositoryUnitTest.java
+++ /dev/null
@@ -1,43 +0,0 @@
-package com.baeldung.optional;
-
-import org.junit.Test;
-
-import java.util.Optional;
-
-import static org.junit.jupiter.api.Assertions.assertAll;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertThrows;
-
-public class PersonRepositoryUnitTest {
-
- PersonRepository personRepository = new PersonRepository();
-
- @Test
- public void whenIdIsNull_thenExceptionIsThrown() {
- assertThrows(IllegalArgumentException.class,
- () ->
- Optional
- .ofNullable(personRepository.findNameById(null))
- .orElseThrow(IllegalArgumentException::new));
- }
-
- @Test
- public void whenIdIsNonNull_thenNoExceptionIsThrown() {
- assertAll(
- () ->
- Optional
- .ofNullable(personRepository.findNameById("id"))
- .orElseThrow(RuntimeException::new));
- }
-
- @Test
- public void whenIdNonNull_thenReturnsNameUpperCase() {
- String name = Optional
- .ofNullable(personRepository.findNameById("id"))
- .map(String::toUpperCase)
- .orElseThrow(RuntimeException::new);
-
- assertEquals("NAME", name);
- }
-
-}
\ No newline at end of file
diff --git a/core-java-modules/core-java-9/README.md b/core-java-modules/core-java-9/README.md
index 8b52ce79b4..f4939ae1e1 100644
--- a/core-java-modules/core-java-9/README.md
+++ b/core-java-modules/core-java-9/README.md
@@ -1,8 +1,6 @@
-=========
+## Core Java 9
-## Core Java 9 Examples
-
-[Java 9 New Features](http://www.baeldung.com/new-java-9)
+This module contains articles about Java 9 core features
### Relevant Articles:
diff --git a/core-java-modules/core-java-annotations/README.md b/core-java-modules/core-java-annotations/README.md
new file mode 100644
index 0000000000..a125e8abd5
--- /dev/null
+++ b/core-java-modules/core-java-annotations/README.md
@@ -0,0 +1,11 @@
+=========
+
+## Core Java 8 Cookbooks and Examples
+
+### Relevant Articles:
+- [Java @Override Annotation](https://www.baeldung.com/java-override)
+- [Java @SuppressWarnings Annotation](https://www.baeldung.com/java-suppresswarnings)
+- [Java @SafeVarargs Annotation](https://www.baeldung.com/java-safevarargs)
+- [Java @Deprecated Annotation](https://www.baeldung.com/java-deprecated)
+- [Overview of Java Built-in Annotations](https://www.baeldung.com/java-default-annotations)
+- [Creating a Custom Annotation in Java](https://www.baeldung.com/java-custom-annotation)
\ No newline at end of file
diff --git a/core-java-modules/core-java-annotations/pom.xml b/core-java-modules/core-java-annotations/pom.xml
new file mode 100644
index 0000000000..92b5afbd47
--- /dev/null
+++ b/core-java-modules/core-java-annotations/pom.xml
@@ -0,0 +1,26 @@
+
+ 4.0.0
+ core-java-annotations
+ 0.1.0-SNAPSHOT
+ core-java-annotations
+ jar
+
+
+ com.baeldung
+ parent-java
+ 0.0.1-SNAPSHOT
+ ../../parent-java
+
+
+
+ core-java-annotations
+
+
+ src/main/resources
+ true
+
+
+
+
+
diff --git a/core-java-modules/core-java-8/src/main/java/com/baeldung/annotations/ClassWithAnnotation.java b/core-java-modules/core-java-annotations/src/main/java/com/baeldung/annotations/ClassWithAnnotation.java
similarity index 100%
rename from core-java-modules/core-java-8/src/main/java/com/baeldung/annotations/ClassWithAnnotation.java
rename to core-java-modules/core-java-annotations/src/main/java/com/baeldung/annotations/ClassWithAnnotation.java
diff --git a/core-java-modules/core-java-8/src/main/java/com/baeldung/annotations/ClassWithDeprecatedMethod.java b/core-java-modules/core-java-annotations/src/main/java/com/baeldung/annotations/ClassWithDeprecatedMethod.java
similarity index 100%
rename from core-java-modules/core-java-8/src/main/java/com/baeldung/annotations/ClassWithDeprecatedMethod.java
rename to core-java-modules/core-java-annotations/src/main/java/com/baeldung/annotations/ClassWithDeprecatedMethod.java
diff --git a/core-java-modules/core-java-8/src/main/java/com/baeldung/annotations/ClassWithSafeVarargs.java b/core-java-modules/core-java-annotations/src/main/java/com/baeldung/annotations/ClassWithSafeVarargs.java
similarity index 100%
rename from core-java-modules/core-java-8/src/main/java/com/baeldung/annotations/ClassWithSafeVarargs.java
rename to core-java-modules/core-java-annotations/src/main/java/com/baeldung/annotations/ClassWithSafeVarargs.java
diff --git a/core-java-modules/core-java-8/src/main/java/com/baeldung/annotations/ClassWithSuppressWarnings.java b/core-java-modules/core-java-annotations/src/main/java/com/baeldung/annotations/ClassWithSuppressWarnings.java
similarity index 100%
rename from core-java-modules/core-java-8/src/main/java/com/baeldung/annotations/ClassWithSuppressWarnings.java
rename to core-java-modules/core-java-annotations/src/main/java/com/baeldung/annotations/ClassWithSuppressWarnings.java
diff --git a/core-java-modules/core-java-8/src/main/java/com/baeldung/annotations/IntConsumer.java b/core-java-modules/core-java-annotations/src/main/java/com/baeldung/annotations/IntConsumer.java
similarity index 100%
rename from core-java-modules/core-java-8/src/main/java/com/baeldung/annotations/IntConsumer.java
rename to core-java-modules/core-java-annotations/src/main/java/com/baeldung/annotations/IntConsumer.java
diff --git a/core-java-modules/core-java-8/src/main/java/com/baeldung/annotations/Interval.java b/core-java-modules/core-java-annotations/src/main/java/com/baeldung/annotations/Interval.java
similarity index 100%
rename from core-java-modules/core-java-8/src/main/java/com/baeldung/annotations/Interval.java
rename to core-java-modules/core-java-annotations/src/main/java/com/baeldung/annotations/Interval.java
diff --git a/core-java-modules/core-java-8/src/main/java/com/baeldung/annotations/IntervalUsage.java b/core-java-modules/core-java-annotations/src/main/java/com/baeldung/annotations/IntervalUsage.java
similarity index 100%
rename from core-java-modules/core-java-8/src/main/java/com/baeldung/annotations/IntervalUsage.java
rename to core-java-modules/core-java-annotations/src/main/java/com/baeldung/annotations/IntervalUsage.java
diff --git a/core-java-modules/core-java-8/src/main/java/com/baeldung/annotations/Intervals.java b/core-java-modules/core-java-annotations/src/main/java/com/baeldung/annotations/Intervals.java
similarity index 100%
rename from core-java-modules/core-java-8/src/main/java/com/baeldung/annotations/Intervals.java
rename to core-java-modules/core-java-annotations/src/main/java/com/baeldung/annotations/Intervals.java
diff --git a/core-java-modules/core-java-8/src/main/java/com/baeldung/annotations/MyAnnotation.java b/core-java-modules/core-java-annotations/src/main/java/com/baeldung/annotations/MyAnnotation.java
similarity index 100%
rename from core-java-modules/core-java-8/src/main/java/com/baeldung/annotations/MyAnnotation.java
rename to core-java-modules/core-java-annotations/src/main/java/com/baeldung/annotations/MyAnnotation.java
diff --git a/core-java-modules/core-java-8/src/main/java/com/baeldung/annotations/MyAnnotationTarget.java b/core-java-modules/core-java-annotations/src/main/java/com/baeldung/annotations/MyAnnotationTarget.java
similarity index 100%
rename from core-java-modules/core-java-8/src/main/java/com/baeldung/annotations/MyAnnotationTarget.java
rename to core-java-modules/core-java-annotations/src/main/java/com/baeldung/annotations/MyAnnotationTarget.java
diff --git a/core-java-modules/core-java-8/src/main/java/com/baeldung/annotations/MyOperation.java b/core-java-modules/core-java-annotations/src/main/java/com/baeldung/annotations/MyOperation.java
similarity index 100%
rename from core-java-modules/core-java-8/src/main/java/com/baeldung/annotations/MyOperation.java
rename to core-java-modules/core-java-annotations/src/main/java/com/baeldung/annotations/MyOperation.java
diff --git a/core-java-modules/core-java-8/src/main/java/com/baeldung/annotations/MyOperationImpl.java b/core-java-modules/core-java-annotations/src/main/java/com/baeldung/annotations/MyOperationImpl.java
similarity index 100%
rename from core-java-modules/core-java-8/src/main/java/com/baeldung/annotations/MyOperationImpl.java
rename to core-java-modules/core-java-annotations/src/main/java/com/baeldung/annotations/MyOperationImpl.java
diff --git a/core-java-modules/core-java-8/src/main/java/com/baeldung/customannotations/Init.java b/core-java-modules/core-java-annotations/src/main/java/com/baeldung/customannotations/Init.java
similarity index 100%
rename from core-java-modules/core-java-8/src/main/java/com/baeldung/customannotations/Init.java
rename to core-java-modules/core-java-annotations/src/main/java/com/baeldung/customannotations/Init.java
diff --git a/core-java-modules/core-java-8/src/main/java/com/baeldung/customannotations/JsonElement.java b/core-java-modules/core-java-annotations/src/main/java/com/baeldung/customannotations/JsonElement.java
similarity index 100%
rename from core-java-modules/core-java-8/src/main/java/com/baeldung/customannotations/JsonElement.java
rename to core-java-modules/core-java-annotations/src/main/java/com/baeldung/customannotations/JsonElement.java
diff --git a/core-java-modules/core-java-8/src/main/java/com/baeldung/customannotations/JsonSerializable.java b/core-java-modules/core-java-annotations/src/main/java/com/baeldung/customannotations/JsonSerializable.java
similarity index 100%
rename from core-java-modules/core-java-8/src/main/java/com/baeldung/customannotations/JsonSerializable.java
rename to core-java-modules/core-java-annotations/src/main/java/com/baeldung/customannotations/JsonSerializable.java
diff --git a/core-java-modules/core-java-8/src/main/java/com/baeldung/customannotations/JsonSerializationException.java b/core-java-modules/core-java-annotations/src/main/java/com/baeldung/customannotations/JsonSerializationException.java
similarity index 100%
rename from core-java-modules/core-java-8/src/main/java/com/baeldung/customannotations/JsonSerializationException.java
rename to core-java-modules/core-java-annotations/src/main/java/com/baeldung/customannotations/JsonSerializationException.java
diff --git a/core-java-modules/core-java-8/src/main/java/com/baeldung/customannotations/ObjectToJsonConverter.java b/core-java-modules/core-java-annotations/src/main/java/com/baeldung/customannotations/ObjectToJsonConverter.java
similarity index 100%
rename from core-java-modules/core-java-8/src/main/java/com/baeldung/customannotations/ObjectToJsonConverter.java
rename to core-java-modules/core-java-annotations/src/main/java/com/baeldung/customannotations/ObjectToJsonConverter.java
diff --git a/core-java-modules/core-java-8/src/main/java/com/baeldung/customannotations/Person.java b/core-java-modules/core-java-annotations/src/main/java/com/baeldung/customannotations/Person.java
similarity index 100%
rename from core-java-modules/core-java-8/src/main/java/com/baeldung/customannotations/Person.java
rename to core-java-modules/core-java-annotations/src/main/java/com/baeldung/customannotations/Person.java
diff --git a/java-dates/src/main/resources/logback.xml b/core-java-modules/core-java-annotations/src/main/resources/logback.xml
similarity index 100%
rename from java-dates/src/main/resources/logback.xml
rename to core-java-modules/core-java-annotations/src/main/resources/logback.xml
diff --git a/core-java-modules/core-java-8/src/test/java/com/baeldung/customannotations/JsonSerializerUnitTest.java b/core-java-modules/core-java-annotations/src/test/java/com/baeldung/customannotations/JsonSerializerUnitTest.java
similarity index 100%
rename from core-java-modules/core-java-8/src/test/java/com/baeldung/customannotations/JsonSerializerUnitTest.java
rename to core-java-modules/core-java-annotations/src/test/java/com/baeldung/customannotations/JsonSerializerUnitTest.java
diff --git a/core-java-modules/core-java-arrays-2/README.md b/core-java-modules/core-java-arrays-2/README.md
new file mode 100644
index 0000000000..5fa2f2bf4c
--- /dev/null
+++ b/core-java-modules/core-java-arrays-2/README.md
@@ -0,0 +1,15 @@
+## Core Java Arrays (Part 2)
+
+This module contains articles about Java arrays
+
+## Relevant Articles
+
+- [Extending an Array’s Length](https://www.baeldung.com/java-array-add-element-at-the-end)
+- [Looping Diagonally Through a 2d Java Array](https://www.baeldung.com/java-loop-diagonal-array)
+- [Converting Between Stream and Array in Java](https://www.baeldung.com/java-stream-to-array)
+- [Convert a Float to a Byte Array in Java](https://www.baeldung.com/java-convert-float-to-byte-array)
+- [Array Operations in Java](https://www.baeldung.com/java-common-array-operations)
+- [Intersection Between two Integer Arrays](https://www.baeldung.com/java-array-intersection)
+- [Removing an Element from an Array in Java](https://www.baeldung.com/java-array-remove-element)
+- [Removing the First Element of an Array](https://www.baeldung.com/java-array-remove-first-element)
+- [[<-- Prev]](/core-java-modules/core-java-arrays)
diff --git a/core-java-modules/core-java-arrays-2/pom.xml b/core-java-modules/core-java-arrays-2/pom.xml
new file mode 100644
index 0000000000..a1da169f2f
--- /dev/null
+++ b/core-java-modules/core-java-arrays-2/pom.xml
@@ -0,0 +1,49 @@
+
+ 4.0.0
+ core-java-arrays-2
+ 0.1.0-SNAPSHOT
+ core-java-arrays-2
+ jar
+
+
+ com.baeldung
+ parent-java
+ 0.0.1-SNAPSHOT
+ ../../parent-java
+
+
+
+
+ org.apache.commons
+ commons-lang3
+ ${commons-lang3.version}
+
+
+
+ org.assertj
+ assertj-core
+ ${assertj-core.version}
+ test
+
+
+
+
+ core-java-arrays-2
+
+
+ src/main/resources
+ true
+
+
+
+
+
+
+
+ 3.9
+
+ 3.10.0
+
+
+
diff --git a/core-java-modules/core-java-arrays/src/main/java/com/baeldung/array/AddElementToEndOfArray.java b/core-java-modules/core-java-arrays-2/src/main/java/com/baeldung/array/AddElementToEndOfArray.java
similarity index 100%
rename from core-java-modules/core-java-arrays/src/main/java/com/baeldung/array/AddElementToEndOfArray.java
rename to core-java-modules/core-java-arrays-2/src/main/java/com/baeldung/array/AddElementToEndOfArray.java
diff --git a/core-java-modules/core-java-arrays-2/src/main/java/com/baeldung/array/RemoveElementFromAnArray.java b/core-java-modules/core-java-arrays-2/src/main/java/com/baeldung/array/RemoveElementFromAnArray.java
new file mode 100644
index 0000000000..62a1a0ee58
--- /dev/null
+++ b/core-java-modules/core-java-arrays-2/src/main/java/com/baeldung/array/RemoveElementFromAnArray.java
@@ -0,0 +1,27 @@
+package com.baeldung.array;
+
+import org.apache.commons.lang3.ArrayUtils;
+
+public class RemoveElementFromAnArray {
+
+ public int[] removeAnElementWithAGivenIndex(int[] array, int index) {
+ return ArrayUtils.remove(array, index);
+ }
+
+ public int[] removeAllElementsWithGivenIndices(int[] array, int... indicies) {
+ return ArrayUtils.removeAll(array, indicies);
+ }
+
+ public int[] removeFirstOccurrenceOfGivenElement(int[] array, int element) {
+ return ArrayUtils.removeElement(array, element);
+ }
+
+ public int[] removeAllGivenElements(int[] array, int... elements) {
+ return ArrayUtils.removeElements(array, elements);
+ }
+
+ public int[] removeAllOccurrencesOfAGivenElement(int[] array, int element) {
+ return ArrayUtils.removeAllOccurences(array, element);
+ }
+
+}
diff --git a/core-java-modules/core-java-arrays/src/main/java/com/baeldung/array/conversions/FloatToByteArray.java b/core-java-modules/core-java-arrays-2/src/main/java/com/baeldung/array/conversions/FloatToByteArray.java
similarity index 100%
rename from core-java-modules/core-java-arrays/src/main/java/com/baeldung/array/conversions/FloatToByteArray.java
rename to core-java-modules/core-java-arrays-2/src/main/java/com/baeldung/array/conversions/FloatToByteArray.java
diff --git a/core-java-arrays/src/main/java/com/baeldung/array/conversions/StreamArrayConversion.java b/core-java-modules/core-java-arrays-2/src/main/java/com/baeldung/array/conversions/StreamArrayConversion.java
similarity index 100%
rename from core-java-arrays/src/main/java/com/baeldung/array/conversions/StreamArrayConversion.java
rename to core-java-modules/core-java-arrays-2/src/main/java/com/baeldung/array/conversions/StreamArrayConversion.java
diff --git a/core-java-modules/core-java-arrays-2/src/main/java/com/baeldung/array/looping/LoopDiagonally.java b/core-java-modules/core-java-arrays-2/src/main/java/com/baeldung/array/looping/LoopDiagonally.java
new file mode 100644
index 0000000000..71e2840f45
--- /dev/null
+++ b/core-java-modules/core-java-arrays-2/src/main/java/com/baeldung/array/looping/LoopDiagonally.java
@@ -0,0 +1,45 @@
+package com.baeldung.array.looping;
+
+public class LoopDiagonally {
+
+
+ public String loopDiagonally(String[][] twoDArray) {
+
+ int length = twoDArray.length;
+ int diagonalLines = (length + length) - 1;
+ int itemsInDiagonal = 0;
+ int midPoint = (diagonalLines / 2) + 1;
+ StringBuilder output = new StringBuilder();
+
+ for (int i = 1; i <= diagonalLines; i++) {
+
+ StringBuilder items = new StringBuilder();
+ int rowIndex;
+ int columnIndex;
+
+ if (i <= midPoint) {
+ itemsInDiagonal++;
+ for (int j = 0; j < itemsInDiagonal; j++) {
+ rowIndex = (i - j) - 1;
+ columnIndex = j;
+ items.append(twoDArray[rowIndex][columnIndex]);
+ }
+ } else {
+ itemsInDiagonal--;
+ for (int j = 0; j < itemsInDiagonal; j++) {
+ rowIndex = (length - 1) - j;
+ columnIndex = (i - length) + j;
+ items.append(twoDArray[rowIndex][columnIndex]);
+ }
+ }
+
+ if (i != diagonalLines) {
+ output.append(items).append(" ");
+ } else {
+ output.append(items);
+ }
+ }
+
+ return output.toString();
+ }
+}
diff --git a/core-java-modules/core-java-arrays/src/main/java/com/baeldung/array/operations/ArrayOperations.java b/core-java-modules/core-java-arrays-2/src/main/java/com/baeldung/array/operations/ArrayOperations.java
similarity index 83%
rename from core-java-modules/core-java-arrays/src/main/java/com/baeldung/array/operations/ArrayOperations.java
rename to core-java-modules/core-java-arrays-2/src/main/java/com/baeldung/array/operations/ArrayOperations.java
index d8cc0afd61..4513dbf899 100644
--- a/core-java-modules/core-java-arrays/src/main/java/com/baeldung/array/operations/ArrayOperations.java
+++ b/core-java-modules/core-java-arrays-2/src/main/java/com/baeldung/array/operations/ArrayOperations.java
@@ -1,19 +1,14 @@
package com.baeldung.array.operations;
+import org.apache.commons.lang3.ArrayUtils;
+
import java.lang.reflect.Array;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.LinkedHashSet;
-import java.util.LinkedList;
-import java.util.Random;
-import java.util.Set;
+import java.util.*;
import java.util.function.Function;
import java.util.function.IntPredicate;
import java.util.function.Predicate;
import java.util.stream.Stream;
-import org.apache.commons.lang3.ArrayUtils;
-
public class ArrayOperations {
// Get the first and last item of an array
@@ -197,15 +192,48 @@ public class ArrayOperations {
return array[new Random().nextInt(array.length)];
}
- public static Integer[] intersectionSimple(final Integer[] a, final Integer[] b){
- return Stream.of(a).filter(Arrays.asList(b)::contains).toArray(Integer[]::new);
+ public static Integer[] intersectionSimple(final Integer[] a, final Integer[] b) {
+ return Stream.of(a)
+ .filter(Arrays.asList(b)::contains)
+ .toArray(Integer[]::new);
}
- public static Integer[] intersectionSet(final Integer[] a, final Integer[] b){
- return Stream.of(a).filter(Arrays.asList(b)::contains).distinct().toArray(Integer[]::new);
+ public static Integer[] intersectionSet(final Integer[] a, final Integer[] b) {
+ return Stream.of(a)
+ .filter(Arrays.asList(b)::contains)
+ .distinct()
+ .toArray(Integer[]::new);
}
- public static Integer[] intersectionMultiSet(final Integer[] a, final Integer[] b){
- return Stream.of(a).filter(new LinkedList<>(Arrays.asList(b))::remove).toArray(Integer[]::new);
+ public static Integer[] intersectionMultiSet(final Integer[] a, final Integer[] b) {
+ return Stream.of(a)
+ .filter(new LinkedList<>(Arrays.asList(b))::remove)
+ .toArray(Integer[]::new);
+ }
+
+ public static Integer[] addElementUsingPureJava(Integer[] srcArray, int elementToAdd) {
+ Integer[] destArray = new Integer[srcArray.length + 1];
+
+ for (int i = 0; i < srcArray.length; i++) {
+ destArray[i] = srcArray[i];
+ }
+
+ destArray[destArray.length - 1] = elementToAdd;
+ return destArray;
+ }
+
+ public static int[] insertAnElementAtAGivenIndex(final int[] srcArray, int index, int newElement) {
+ int[] destArray = new int[srcArray.length + 1];
+ int j = 0;
+ for (int i = 0; i < destArray.length - 1; i++) {
+
+ if (i == index) {
+ destArray[i] = newElement;
+ } else {
+ destArray[i] = srcArray[j];
+ j++;
+ }
+ }
+ return destArray;
}
}
diff --git a/core-java-modules/core-java-arrays-2/src/main/java/com/baeldung/arraylist/operations/ArrayListOperations.java b/core-java-modules/core-java-arrays-2/src/main/java/com/baeldung/arraylist/operations/ArrayListOperations.java
new file mode 100644
index 0000000000..b2aed553da
--- /dev/null
+++ b/core-java-modules/core-java-arrays-2/src/main/java/com/baeldung/arraylist/operations/ArrayListOperations.java
@@ -0,0 +1,23 @@
+package com.baeldung.arraylist.operations;
+
+import java.util.ArrayList;
+
+public class ArrayListOperations {
+
+ public static Integer getAnIntegerElement(ArrayList anArrayList, int index) {
+ return anArrayList.get(index);
+ }
+
+ public static void modifyAnIntegerElement(ArrayList anArrayList, int index, Integer newElement) {
+ anArrayList.set(index, newElement);
+ }
+
+ public static void appendAnIntegerElement(ArrayList anArrayList, Integer newElement) {
+ anArrayList.add(newElement);
+ }
+
+ public static void insertAnIntegerElementAtIndex(ArrayList anArrayList, int index, Integer newElement) {
+ anArrayList.add(index, newElement);
+ }
+
+}
diff --git a/core-java-modules/core-java-arrays/src/test/java/com/baeldung/array/AddElementToEndOfArrayUnitTest.java b/core-java-modules/core-java-arrays-2/src/test/java/com/baeldung/array/AddElementToEndOfArrayUnitTest.java
similarity index 100%
rename from core-java-modules/core-java-arrays/src/test/java/com/baeldung/array/AddElementToEndOfArrayUnitTest.java
rename to core-java-modules/core-java-arrays-2/src/test/java/com/baeldung/array/AddElementToEndOfArrayUnitTest.java
diff --git a/core-java-modules/core-java-arrays-2/src/test/java/com/baeldung/array/RemoveElementFromAnArrayUnitTest.java b/core-java-modules/core-java-arrays-2/src/test/java/com/baeldung/array/RemoveElementFromAnArrayUnitTest.java
new file mode 100644
index 0000000000..567b870ef1
--- /dev/null
+++ b/core-java-modules/core-java-arrays-2/src/test/java/com/baeldung/array/RemoveElementFromAnArrayUnitTest.java
@@ -0,0 +1,82 @@
+package com.baeldung.array;
+
+import org.apache.commons.lang3.ArrayUtils;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class RemoveElementFromAnArrayUnitTest {
+
+ private final RemoveElementFromAnArray sut = new RemoveElementFromAnArray();
+ private final int[] inputArray = new int[] { 40, 10, 20, 30, 40, 50 };
+
+ @Test
+ void testRemoveAnElementWithAGivenIndex() {
+ int index = 2;
+ int[] modifiedArray = sut.removeAnElementWithAGivenIndex(inputArray, index);
+
+ assertFalse(ArrayUtils.contains(modifiedArray, inputArray[index]));
+ }
+
+ @Test
+ void testRemoveAllElementsWithGivenIndices() {
+ int first = 0;
+ int last = inputArray.length - 1;
+ int[] modifiedArray = sut.removeAllElementsWithGivenIndices(inputArray, first, last);
+
+ assertFalse(ArrayUtils.contains(modifiedArray, inputArray[first]) && ArrayUtils.contains(modifiedArray, inputArray[last]));
+ }
+
+ @Test
+ void testRemoveElement_WhenArrayIsNull_ThrowsIndexOutOfBoundEx() {
+ int index = 2;
+
+ assertThrows(IndexOutOfBoundsException.class, () -> {
+ sut.removeAnElementWithAGivenIndex(null, index);
+ });
+
+ assertThrows(IndexOutOfBoundsException.class, () -> {
+ sut.removeAllElementsWithGivenIndices(null, index);
+ });
+ }
+
+ @Test
+ void testRemoveFirstOccurrenceOfGivenElement() {
+ int element = 40;
+ int[] modifiedArray = sut.removeFirstOccurrenceOfGivenElement(inputArray, element);
+
+ int indexInInputArray = ArrayUtils.indexOf(inputArray, element);
+ int indexInModifiedArray = ArrayUtils.indexOf(modifiedArray, element);
+ assertFalse(indexInInputArray == indexInModifiedArray);
+ }
+
+ @Test
+ void testRemoveAllGivenElements() {
+ int duplicateElement = 40;
+ int[] elements = new int[] { duplicateElement, 10, 50 };
+ int[] modifiedArray = sut.removeAllGivenElements(inputArray, elements);
+
+ assertTrue(ArrayUtils.contains(modifiedArray, duplicateElement));
+ assertFalse(ArrayUtils.contains(modifiedArray, elements[1]));
+ assertFalse(ArrayUtils.contains(modifiedArray, elements[2]));
+ }
+
+ @Test
+ void testRemoveAllOccurrencesOfAGivenElement() {
+ int element = 40;
+ int[] modifiedArray = sut.removeAllOccurrencesOfAGivenElement(inputArray, element);
+
+ assertFalse(ArrayUtils.contains(modifiedArray, element));
+ }
+
+ @Test
+ void testRemoveElement_WhenArrayIsNull_ReturnsNull() {
+ int element = 20;
+
+ assertEquals(null, sut.removeFirstOccurrenceOfGivenElement(null, element));
+ assertEquals(null, sut.removeAllGivenElements(null, element));
+ assertEquals(null, sut.removeAllOccurrencesOfAGivenElement(null, element));
+
+ }
+
+}
diff --git a/core-java-modules/core-java-arrays/src/test/java/com/baeldung/array/conversions/FloatToByteArrayUnitTest.java b/core-java-modules/core-java-arrays-2/src/test/java/com/baeldung/array/conversions/FloatToByteArrayUnitTest.java
similarity index 80%
rename from core-java-modules/core-java-arrays/src/test/java/com/baeldung/array/conversions/FloatToByteArrayUnitTest.java
rename to core-java-modules/core-java-arrays-2/src/test/java/com/baeldung/array/conversions/FloatToByteArrayUnitTest.java
index a2cd273f21..7656783052 100644
--- a/core-java-modules/core-java-arrays/src/test/java/com/baeldung/array/conversions/FloatToByteArrayUnitTest.java
+++ b/core-java-modules/core-java-arrays-2/src/test/java/com/baeldung/array/conversions/FloatToByteArrayUnitTest.java
@@ -1,12 +1,10 @@
package com.baeldung.array.conversions;
-import static com.baeldung.array.conversions.FloatToByteArray.byteArrayToFloat;
-import static com.baeldung.array.conversions.FloatToByteArray.byteArrayToFloatWithByteBuffer;
-import static com.baeldung.array.conversions.FloatToByteArray.floatToByteArray;
-import static com.baeldung.array.conversions.FloatToByteArray.floatToByteArrayWithByteBuffer;
+import org.junit.Test;
+
+import static com.baeldung.array.conversions.FloatToByteArray.*;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
-import org.junit.Test;
public class FloatToByteArrayUnitTest {
diff --git a/core-java-arrays/src/test/java/com/baeldung/array/conversions/StreamArrayConversionUnitTest.java b/core-java-modules/core-java-arrays-2/src/test/java/com/baeldung/array/conversions/StreamArrayConversionUnitTest.java
similarity index 100%
rename from core-java-arrays/src/test/java/com/baeldung/array/conversions/StreamArrayConversionUnitTest.java
rename to core-java-modules/core-java-arrays-2/src/test/java/com/baeldung/array/conversions/StreamArrayConversionUnitTest.java
diff --git a/core-java-modules/core-java-arrays-2/src/test/java/com/baeldung/array/looping/LoopDiagonallyUnitTest.java b/core-java-modules/core-java-arrays-2/src/test/java/com/baeldung/array/looping/LoopDiagonallyUnitTest.java
new file mode 100644
index 0000000000..5f670f4a59
--- /dev/null
+++ b/core-java-modules/core-java-arrays-2/src/test/java/com/baeldung/array/looping/LoopDiagonallyUnitTest.java
@@ -0,0 +1,20 @@
+package com.baeldung.array.looping;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+public class LoopDiagonallyUnitTest {
+
+ @Test
+ public void twoArrayIsLoopedDiagonallyAsExpected() {
+
+ LoopDiagonally loopDiagonally = new LoopDiagonally();
+ String[][] twoDArray = {{"a", "b", "c"},
+ {"d", "e", "f"},
+ {"g", "h", "i"}};
+
+ String output = loopDiagonally.loopDiagonally(twoDArray);
+ assertEquals("a db gec hf i", output);
+ }
+}
\ No newline at end of file
diff --git a/core-java-modules/core-java-arrays/src/test/java/com/baeldung/array/operations/ArrayOperationsUnitTest.java b/core-java-modules/core-java-arrays-2/src/test/java/com/baeldung/array/operations/ArrayOperationsUnitTest.java
similarity index 90%
rename from core-java-modules/core-java-arrays/src/test/java/com/baeldung/array/operations/ArrayOperationsUnitTest.java
rename to core-java-modules/core-java-arrays-2/src/test/java/com/baeldung/array/operations/ArrayOperationsUnitTest.java
index a9c6d97d9f..ea9bf2c5a9 100644
--- a/core-java-modules/core-java-arrays/src/test/java/com/baeldung/array/operations/ArrayOperationsUnitTest.java
+++ b/core-java-modules/core-java-arrays-2/src/test/java/com/baeldung/array/operations/ArrayOperationsUnitTest.java
@@ -1,12 +1,11 @@
package com.baeldung.array.operations;
-import static org.assertj.core.api.Assertions.assertThat;
-
import java.util.Arrays;
-
import org.assertj.core.api.Condition;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.Assert.assertArrayEquals;
public class ArrayOperationsUnitTest {
@@ -262,8 +261,7 @@ public class ArrayOperationsUnitTest {
@Test
public void whenMapIntArrayToString_thenReturnArray() {
- String[] expectedArray = new String[] { "Value: 3", "Value: 5", "Value: 2", "Value: 5", "Value: 14",
- "Value: 4" };
+ String[] expectedArray = new String[] { "Value: 3", "Value: 5", "Value: 2", "Value: 5", "Value: 14", "Value: 4" };
String[] output = ArrayOperations.mapIntArrayToString(defaultIntArray);
assertThat(output).containsExactly(expectedArray);
@@ -313,13 +311,10 @@ public class ArrayOperationsUnitTest {
int[] output5 = ArrayOperations.shuffleIntArray(defaultIntArray);
int[] output6 = ArrayOperations.shuffleIntArray(defaultIntArray);
- Condition atLeastOneArraysIsNotEqual = new Condition(
- "at least one output should be different (order-wise)") {
+ Condition atLeastOneArraysIsNotEqual = new Condition("at least one output should be different (order-wise)") {
@Override
public boolean matches(int[] value) {
- return !Arrays.equals(value, output) || !Arrays.equals(value, output2) || !Arrays.equals(value, output3)
- || !Arrays.equals(value, output4) || !Arrays.equals(value, output5)
- || !Arrays.equals(value, output6);
+ return !Arrays.equals(value, output) || !Arrays.equals(value, output2) || !Arrays.equals(value, output3) || !Arrays.equals(value, output4) || !Arrays.equals(value, output5) || !Arrays.equals(value, output6);
}
};
@@ -335,13 +330,10 @@ public class ArrayOperationsUnitTest {
Integer[] output5 = ArrayOperations.shuffleObjectArray(defaultObjectArray);
Integer[] output6 = ArrayOperations.shuffleObjectArray(defaultObjectArray);
- Condition atLeastOneArraysIsNotEqual = new Condition(
- "at least one output should be different (order-wise)") {
+ Condition atLeastOneArraysIsNotEqual = new Condition("at least one output should be different (order-wise)") {
@Override
public boolean matches(Integer[] value) {
- return !Arrays.equals(value, output) || !Arrays.equals(value, output2) || !Arrays.equals(value, output3)
- || !Arrays.equals(value, output4) || !Arrays.equals(value, output5)
- || !Arrays.equals(value, output6);
+ return !Arrays.equals(value, output) || !Arrays.equals(value, output2) || !Arrays.equals(value, output3) || !Arrays.equals(value, output4) || !Arrays.equals(value, output5) || !Arrays.equals(value, output6);
}
};
@@ -362,4 +354,27 @@ public class ArrayOperationsUnitTest {
assertThat(defaultObjectArray).contains(output);
}
+
+ @Test
+ public void givenSourceArrayAndElement_whenAddElementUsingPureJavaIsInvoked_thenNewElementMustBeAdded() {
+ Integer[] sourceArray = { 1, 2, 3, 4 };
+ int elementToAdd = 5;
+
+ Integer[] destArray = ArrayOperations.addElementUsingPureJava(sourceArray, elementToAdd);
+
+ Integer[] expectedArray = { 1, 2, 3, 4, 5 };
+ assertArrayEquals(expectedArray, destArray);
+ }
+
+ @Test
+ public void whenInsertAnElementAtAGivenIndexCalled_thenShiftTheFollowingElementsAndInsertTheElementInArray() {
+ int[] expectedArray = { 1, 4, 2, 3, 0 };
+ int[] anArray = new int[4];
+ anArray[0] = 1;
+ anArray[1] = 2;
+ anArray[2] = 3;
+ int[] outputArray = ArrayOperations.insertAnElementAtAGivenIndex(anArray, 1, 4);
+
+ assertThat(outputArray).containsExactly(expectedArray);
+ }
}
diff --git a/core-java-modules/core-java-arrays/src/test/java/com/baeldung/array/operations/IntersectionUnitTest.java b/core-java-modules/core-java-arrays-2/src/test/java/com/baeldung/array/operations/IntersectionUnitTest.java
similarity index 91%
rename from core-java-modules/core-java-arrays/src/test/java/com/baeldung/array/operations/IntersectionUnitTest.java
rename to core-java-modules/core-java-arrays-2/src/test/java/com/baeldung/array/operations/IntersectionUnitTest.java
index 3c61060ea8..1560cc5701 100644
--- a/core-java-modules/core-java-arrays/src/test/java/com/baeldung/array/operations/IntersectionUnitTest.java
+++ b/core-java-modules/core-java-arrays-2/src/test/java/com/baeldung/array/operations/IntersectionUnitTest.java
@@ -2,9 +2,7 @@ package com.baeldung.array.operations;
import org.junit.jupiter.api.Test;
-import static com.baeldung.array.operations.ArrayOperations.intersectionMultiSet;
-import static com.baeldung.array.operations.ArrayOperations.intersectionSet;
-import static com.baeldung.array.operations.ArrayOperations.intersectionSimple;
+import static com.baeldung.array.operations.ArrayOperations.*;
import static org.assertj.core.api.Assertions.assertThat;
class IntersectionUnitTest {
diff --git a/core-java-modules/core-java-collections-list/src/test/java/com/baeldung/list/removefirst/RemoveFirstElementUnitTest.java b/core-java-modules/core-java-arrays-2/src/test/java/com/baeldung/array/removefirst/RemoveFirstElementUnitTest.java
similarity index 97%
rename from core-java-modules/core-java-collections-list/src/test/java/com/baeldung/list/removefirst/RemoveFirstElementUnitTest.java
rename to core-java-modules/core-java-arrays-2/src/test/java/com/baeldung/array/removefirst/RemoveFirstElementUnitTest.java
index f2b1bd9d88..83a97973f3 100644
--- a/core-java-modules/core-java-collections-list/src/test/java/com/baeldung/list/removefirst/RemoveFirstElementUnitTest.java
+++ b/core-java-modules/core-java-arrays-2/src/test/java/com/baeldung/array/removefirst/RemoveFirstElementUnitTest.java
@@ -1,4 +1,4 @@
-package com.baeldung.list.removefirst;
+package com.baeldung.array.removefirst;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;
diff --git a/core-java-modules/core-java-arrays-2/src/test/java/com/baeldung/arraylist/operations/ArrayListOperationsUnitTest.java b/core-java-modules/core-java-arrays-2/src/test/java/com/baeldung/arraylist/operations/ArrayListOperationsUnitTest.java
new file mode 100644
index 0000000000..1ec7645d8f
--- /dev/null
+++ b/core-java-modules/core-java-arrays-2/src/test/java/com/baeldung/arraylist/operations/ArrayListOperationsUnitTest.java
@@ -0,0 +1,50 @@
+package com.baeldung.arraylist.operations;
+
+import java.util.ArrayList;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class ArrayListOperationsUnitTest {
+
+ private ArrayList anArrayList;
+
+ @BeforeEach
+ public void setupDefaults() {
+ anArrayList = new ArrayList<>();
+ anArrayList.add(2);
+ anArrayList.add(3);
+ anArrayList.add(4);
+ }
+
+ @Test
+ public void whenGetAnIntegerElementCalled_thenReturnTheIntegerElement() {
+ Integer output = ArrayListOperations.getAnIntegerElement(anArrayList, 1);
+
+ assertThat(output).isEqualTo(3);
+ }
+
+ @Test
+ public void whenModifyAnIntegerElementCalled_thenModifyTheIntegerElement() {
+ ArrayListOperations.modifyAnIntegerElement(anArrayList, 2, 5);
+ Integer output = ArrayListOperations.getAnIntegerElement(anArrayList, 2);
+
+ assertThat(output).isEqualTo(5);
+ }
+
+ @Test
+ public void whenAppendAnIntegerElementCalled_thenTheIntegerElementIsAppendedToArrayList() {
+ ArrayListOperations.appendAnIntegerElement(anArrayList, 6);
+ Integer output = ArrayListOperations.getAnIntegerElement(anArrayList, anArrayList.size() - 1);
+
+ assertThat(output).isEqualTo(6);
+ }
+
+ @Test
+ public void whenInsertAnIntegerAtIndexCalled_thenTheIntegerElementIsInseredToArrayList() {
+ ArrayListOperations.insertAnIntegerElementAtIndex(anArrayList, 1, 10);
+ Integer output = ArrayListOperations.getAnIntegerElement(anArrayList, 1);
+
+ assertThat(output).isEqualTo(10);
+ }
+}
\ No newline at end of file
diff --git a/core-java-modules/core-java-arrays/README.md b/core-java-modules/core-java-arrays/README.md
index b5f71cc253..42fe3f83a1 100644
--- a/core-java-modules/core-java-arrays/README.md
+++ b/core-java-modules/core-java-arrays/README.md
@@ -1,18 +1,17 @@
-=========
+## Core Java Arrays
-## Core Java Arrays Cookbooks and Examples
+This module contains articles about Java arrays
### Relevant Articles:
-- [How to Copy an Array in Java](http://www.baeldung.com/java-array-copy)
-- [Check if a Java Array Contains a Value](http://www.baeldung.com/java-array-contains-value)
-- [Initializing Arrays in Java](http://www.baeldung.com/java-initialize-array)
-- [Guide to the java.util.Arrays Class](http://www.baeldung.com/java-util-arrays)
-- [Jagged Arrays In Java](http://www.baeldung.com/java-jagged-arrays)
-- [Find Sum and Average in a Java Array](http://www.baeldung.com/java-array-sum-average)
+- [How to Copy an Array in Java](https://www.baeldung.com/java-array-copy)
+- [Check if a Java Array Contains a Value](https://www.baeldung.com/java-array-contains-value)
+- [Initializing Arrays in Java](https://www.baeldung.com/java-initialize-array)
+- [Guide to the java.util.Arrays Class](https://www.baeldung.com/java-util-arrays)
+- [Multi-Dimensional Arrays In Java](https://www.baeldung.com/java-jagged-arrays)
+- [Find Sum and Average in a Java Array](https://www.baeldung.com/java-array-sum-average)
- [Arrays in Java: A Reference Guide](https://www.baeldung.com/java-arrays-guide)
-- [How to Invert an Array in Java](http://www.baeldung.com/java-invert-array)
-- [Array Operations in Java](http://www.baeldung.com/java-common-array-operations)
-- [Intersection Between two Integer Arrays](https://www.baeldung.com/java-array-intersection)
+- [Read and Write User Input in Java](https://www.baeldung.com/java-console-input-output)
+- [How to Reverse an Array in Java](http://www.baeldung.com/java-invert-array)
- [Sorting Arrays in Java](https://www.baeldung.com/java-sorting-arrays)
-- [Convert a Float to a Byte Array in Java](https://www.baeldung.com/java-convert-float-to-byte-array)
-- [Converting Between Stream and Array in Java](https://www.baeldung.com/java-stream-to-array)
+- [Checking If an Array Is Sorted in Java](https://www.baeldung.com/java-check-sorted-array)
+- [[More -->]](/core-java-modules/core-java-arrays-2)
diff --git a/core-java-modules/core-java-arrays/pom.xml b/core-java-modules/core-java-arrays/pom.xml
index b713c196b5..ca4e262ff9 100644
--- a/core-java-modules/core-java-arrays/pom.xml
+++ b/core-java-modules/core-java-arrays/pom.xml
@@ -1,7 +1,6 @@
4.0.0
- com.baeldung
core-java-arrays
0.1.0-SNAPSHOT
core-java-arrays
@@ -20,16 +19,6 @@
commons-lang3
${commons-lang3.version}
-
- log4j
- log4j
- ${log4j.version}
-
-
- org.slf4j
- log4j-over-slf4j
- ${org.slf4j.version}
-
org.assertj
@@ -47,11 +36,6 @@
jmh-generator-annprocess
${jmh-generator-annprocess.version}
-
- org.springframework
- spring-web
- ${springframework.spring-web.version}
-
@@ -64,21 +48,6 @@
-
- org.apache.maven.plugins
- maven-surefire-plugin
-
-
- **/*LiveTest.java
- **/*IntegrationTest.java
- **/*IntTest.java
- **/*LongRunningUnitTest.java
- **/*ManualTest.java
-
- true
-
-
-
org.apache.maven.plugins
maven-dependency-plugin
@@ -389,7 +358,7 @@
- 3.8.1
+ 3.9
1.19
1.19
@@ -398,8 +367,6 @@
3.10.0
- 2.21.0
- 4.3.4.RELEASE
3.0.0-M1
3.0.2
1.4.4
diff --git a/core-java-modules/core-java-arrays/src/main/java/com/baeldung/array/JaggedArray.java b/core-java-modules/core-java-arrays/src/main/java/com/baeldung/array/JaggedArray.java
deleted file mode 100644
index 36cfc88b95..0000000000
--- a/core-java-modules/core-java-arrays/src/main/java/com/baeldung/array/JaggedArray.java
+++ /dev/null
@@ -1,49 +0,0 @@
-package com.baeldung.array;
-
-import java.util.Arrays;
-import java.util.Scanner;
-
-public class JaggedArray {
-
- int[][] shortHandFormInitialization() {
- int[][] jaggedArr = { { 1, 2 }, { 3, 4, 5 }, { 6, 7, 8, 9 } };
- return jaggedArr;
- }
-
- int[][] declarationAndThenInitialization() {
- int[][] jaggedArr = new int[3][];
- jaggedArr[0] = new int[] { 1, 2 };
- jaggedArr[1] = new int[] { 3, 4, 5 };
- jaggedArr[2] = new int[] { 6, 7, 8, 9 };
- return jaggedArr;
- }
-
- int[][] declarationAndThenInitializationUsingUserInputs() {
- int[][] jaggedArr = new int[3][];
- jaggedArr[0] = new int[2];
- jaggedArr[1] = new int[3];
- jaggedArr[2] = new int[4];
- initializeElements(jaggedArr);
- return jaggedArr;
- }
-
- void initializeElements(int[][] jaggedArr) {
- Scanner sc = new Scanner(System.in);
- for (int outer = 0; outer < jaggedArr.length; outer++) {
- for (int inner = 0; inner < jaggedArr[outer].length; inner++) {
- jaggedArr[outer][inner] = sc.nextInt();
- }
- }
- }
-
- void printElements(int[][] jaggedArr) {
- for (int index = 0; index < jaggedArr.length; index++) {
- System.out.println(Arrays.toString(jaggedArr[index]));
- }
- }
-
- int[] getElementAtGivenIndex(int[][] jaggedArr, int index) {
- return jaggedArr[index];
- }
-
-}
diff --git a/core-java-modules/core-java-arrays/src/main/java/com/baeldung/array/MultiDimensionalArray.java b/core-java-modules/core-java-arrays/src/main/java/com/baeldung/array/MultiDimensionalArray.java
new file mode 100644
index 0000000000..4e01b99a14
--- /dev/null
+++ b/core-java-modules/core-java-arrays/src/main/java/com/baeldung/array/MultiDimensionalArray.java
@@ -0,0 +1,83 @@
+package com.baeldung.array;
+
+import java.util.Arrays;
+import java.util.Scanner;
+
+public class MultiDimensionalArray {
+
+ int[][] shortHandFormInitialization() {
+ int[][] multiDimensionalArray = { { 1, 2 }, { 3, 4, 5 }, { 6, 7, 8, 9 } };
+ return multiDimensionalArray;
+ }
+
+ int[][] declarationAndThenInitialization() {
+ int[][] multiDimensionalArray = new int[3][];
+ multiDimensionalArray[0] = new int[] { 1, 2 };
+ multiDimensionalArray[1] = new int[] { 3, 4, 5 };
+ multiDimensionalArray[2] = new int[] { 6, 7, 8, 9 };
+ return multiDimensionalArray;
+ }
+
+ int[][] declarationAndThenInitializationUsingUserInputs() {
+ int[][] multiDimensionalArray = new int[3][];
+ multiDimensionalArray[0] = new int[2];
+ multiDimensionalArray[1] = new int[3];
+ multiDimensionalArray[2] = new int[4];
+ initializeElements(multiDimensionalArray);
+ return multiDimensionalArray;
+ }
+
+ void initializeElements(int[][] multiDimensionalArray) {
+ Scanner sc = new Scanner(System.in);
+ for (int outer = 0; outer < multiDimensionalArray.length; outer++) {
+ for (int inner = 0; inner < multiDimensionalArray[outer].length; inner++) {
+ multiDimensionalArray[outer][inner] = sc.nextInt();
+ }
+ }
+ }
+
+ void initialize2DArray(int[][] multiDimensionalArray) {
+ for (int[] array : multiDimensionalArray) {
+ Arrays.fill(array, 7);
+ }
+ }
+
+ void printElements(int[][] multiDimensionalArray) {
+ for (int index = 0; index < multiDimensionalArray.length; index++) {
+ System.out.println(Arrays.toString(multiDimensionalArray[index]));
+ }
+ }
+
+ int[] getElementAtGivenIndex(int[][] multiDimensionalArray, int index) {
+ return multiDimensionalArray[index];
+ }
+
+ int[] findLengthOfElements(int[][] multiDimensionalArray) {
+ int[] arrayOfLengths = new int[multiDimensionalArray.length];
+ for (int i = 0; i < multiDimensionalArray.length; i++) {
+ arrayOfLengths[i] = multiDimensionalArray[i].length;
+ }
+ return arrayOfLengths;
+ }
+
+ Integer[] findLengthOfElements(Integer[][] multiDimensionalArray) {
+ return Arrays.stream(multiDimensionalArray)
+ .map(array -> array.length)
+ .toArray(Integer[]::new);
+ }
+
+ int[][] copy2DArray(int[][] arrayOfArrays) {
+ int[][] copied2DArray = new int[arrayOfArrays.length][];
+ for (int i = 0; i < arrayOfArrays.length; i++) {
+ int[] array = arrayOfArrays[i];
+ copied2DArray[i] = Arrays.copyOf(array, array.length);
+ }
+ return copied2DArray;
+ }
+
+ Integer[][] copy2DArray(Integer[][] arrayOfArrays) {
+ return Arrays.stream(arrayOfArrays)
+ .map(array -> Arrays.copyOf(array, array.length))
+ .toArray(Integer[][]::new);
+ }
+}
diff --git a/core-java-modules/core-java-arrays/src/main/java/com/baeldung/array/SortedArrayChecker.java b/core-java-modules/core-java-arrays/src/main/java/com/baeldung/array/SortedArrayChecker.java
new file mode 100644
index 0000000000..78a9a8f4d1
--- /dev/null
+++ b/core-java-modules/core-java-arrays/src/main/java/com/baeldung/array/SortedArrayChecker.java
@@ -0,0 +1,65 @@
+package com.baeldung.array;
+
+import com.baeldung.arraycopy.model.Employee;
+
+import java.util.Comparator;
+
+public class SortedArrayChecker {
+
+ boolean isSorted(int[] array, int length) {
+ if (array == null || length < 2)
+ return true;
+
+ if (array[length - 2] > array[length - 1])
+ return false;
+
+ return isSorted(array, length - 1);
+ }
+
+ boolean isSorted(int[] array) {
+ for (int i = 0; i < array.length - 1; i++) {
+ if (array[i] > array[i + 1])
+ return false;
+ }
+
+ return true;
+ }
+
+ boolean isSorted(Comparable[] array, int length) {
+ if (array == null || length < 2)
+ return true;
+
+ if (array[length - 2].compareTo(array[length - 1]) > 0)
+ return false;
+
+ return isSorted(array, length - 1);
+ }
+
+ boolean isSorted(Comparable[] array) {
+ for (int i = 0; i < array.length - 1; ++i) {
+ if (array[i].compareTo(array[i + 1]) > 0)
+ return false;
+ }
+
+ return true;
+ }
+
+ boolean isSorted(Object[] array, Comparator comparator) {
+ for (int i = 0; i < array.length - 1; ++i) {
+ if (comparator.compare(array[i], (array[i + 1])) > 0)
+ return false;
+ }
+
+ return true;
+ }
+
+ boolean isSorted(Object[] array, Comparator comparator, int length) {
+ if (array == null || length < 2)
+ return true;
+
+ if (comparator.compare(array[length - 2], array[length - 1]) > 0)
+ return false;
+
+ return isSorted(array, comparator, length - 1);
+ }
+}
diff --git a/core-java-modules/core-java-arrays/src/main/java/com/baeldung/arraycopy/model/Employee.java b/core-java-modules/core-java-arrays/src/main/java/com/baeldung/arraycopy/model/Employee.java
index a7592574d9..eba39d5716 100644
--- a/core-java-modules/core-java-arrays/src/main/java/com/baeldung/arraycopy/model/Employee.java
+++ b/core-java-modules/core-java-arrays/src/main/java/com/baeldung/arraycopy/model/Employee.java
@@ -6,6 +6,7 @@ public class Employee implements Serializable {
private static final long serialVersionUID = -2454619097207585825L;
private int id;
private String name;
+ private int age;
public Employee() {
}
@@ -15,10 +16,24 @@ public class Employee implements Serializable {
this.name = name;
}
+ public Employee(int id, String name, int age) {
+ this.id = id;
+ this.name = name;
+ this.age = age;
+ }
+
+ public int getAge() {
+ return age;
+ }
+
public int getId() {
return id;
}
+ public void setAge(int age) {
+ this.age = age;
+ }
+
public void setId(int id) {
this.id = id;
}
diff --git a/core-java-modules/core-java-arrays/src/test/java/com/baeldung/array/JaggedArrayUnitTest.java b/core-java-modules/core-java-arrays/src/test/java/com/baeldung/array/JaggedArrayUnitTest.java
deleted file mode 100644
index a4dd7e25c3..0000000000
--- a/core-java-modules/core-java-arrays/src/test/java/com/baeldung/array/JaggedArrayUnitTest.java
+++ /dev/null
@@ -1,53 +0,0 @@
-package com.baeldung.array;
-
-import static org.junit.Assert.assertArrayEquals;
-import static org.junit.Assert.assertEquals;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.InputStream;
-import java.io.PrintStream;
-
-import org.junit.Test;
-
-public class JaggedArrayUnitTest {
-
- private JaggedArray obj = new JaggedArray();
-
- @Test
- public void whenInitializedUsingShortHandForm_thenCorrect() {
- assertArrayEquals(new int[][] { { 1, 2 }, { 3, 4, 5 }, { 6, 7, 8, 9 } }, obj.shortHandFormInitialization());
- }
-
- @Test
- public void whenInitializedWithDeclarationAndThenInitalization_thenCorrect() {
- assertArrayEquals(new int[][] { { 1, 2 }, { 3, 4, 5 }, { 6, 7, 8, 9 } }, obj.declarationAndThenInitialization());
- }
-
- @Test
- public void whenInitializedWithDeclarationAndThenInitalizationUsingUserInputs_thenCorrect() {
- InputStream is = new ByteArrayInputStream("1 2 3 4 5 6 7 8 9".getBytes());
- System.setIn(is);
- assertArrayEquals(new int[][] { { 1, 2 }, { 3, 4, 5 }, { 6, 7, 8, 9 } }, obj.declarationAndThenInitializationUsingUserInputs());
- System.setIn(System.in);
- }
-
- @Test
- public void givenJaggedArrayAndAnIndex_thenReturnArrayAtGivenIndex() {
- int[][] jaggedArr = { { 1, 2 }, { 3, 4, 5 }, { 6, 7, 8, 9 } };
- assertArrayEquals(new int[] { 1, 2 }, obj.getElementAtGivenIndex(jaggedArr, 0));
- assertArrayEquals(new int[] { 3, 4, 5 }, obj.getElementAtGivenIndex(jaggedArr, 1));
- assertArrayEquals(new int[] { 6, 7, 8, 9 }, obj.getElementAtGivenIndex(jaggedArr, 2));
- }
-
- @Test
- public void givenJaggedArray_whenUsingArraysAPI_thenVerifyPrintedElements() {
- int[][] jaggedArr = { { 1, 2 }, { 3, 4, 5 }, { 6, 7, 8, 9 } };
- ByteArrayOutputStream outContent = new ByteArrayOutputStream();
- System.setOut(new PrintStream(outContent));
- obj.printElements(jaggedArr);
- assertEquals("[1, 2][3, 4, 5][6, 7, 8, 9]", outContent.toString().replace("\r", "").replace("\n", ""));
- System.setOut(System.out);
- }
-
-}
diff --git a/core-java-modules/core-java-arrays/src/test/java/com/baeldung/array/MultiDimensionalArrayUnitTest.java b/core-java-modules/core-java-arrays/src/test/java/com/baeldung/array/MultiDimensionalArrayUnitTest.java
new file mode 100644
index 0000000000..8980eaa9dc
--- /dev/null
+++ b/core-java-modules/core-java-arrays/src/test/java/com/baeldung/array/MultiDimensionalArrayUnitTest.java
@@ -0,0 +1,86 @@
+package com.baeldung.array;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.io.PrintStream;
+
+import org.junit.Test;
+
+public class MultiDimensionalArrayUnitTest {
+
+ private MultiDimensionalArray obj = new MultiDimensionalArray();
+
+ @Test
+ public void whenInitializedUsingShortHandForm_thenCorrect() {
+ assertArrayEquals(new int[][] { { 1, 2 }, { 3, 4, 5 }, { 6, 7, 8, 9 } }, obj.shortHandFormInitialization());
+ }
+
+ @Test
+ public void whenInitializedWithDeclarationAndThenInitalization_thenCorrect() {
+ assertArrayEquals(new int[][] { { 1, 2 }, { 3, 4, 5 }, { 6, 7, 8, 9 } }, obj.declarationAndThenInitialization());
+ }
+
+ @Test
+ public void whenInitializedWithDeclarationAndThenInitalizationUsingUserInputs_thenCorrect() {
+ InputStream is = new ByteArrayInputStream("1 2 3 4 5 6 7 8 9".getBytes());
+ System.setIn(is);
+ assertArrayEquals(new int[][] { { 1, 2 }, { 3, 4, 5 }, { 6, 7, 8, 9 } }, obj.declarationAndThenInitializationUsingUserInputs());
+ System.setIn(System.in);
+ }
+
+ @Test
+ public void givenMultiDimensionalArrayAndAnIndex_thenReturnArrayAtGivenIndex() {
+ int[][] multiDimensionalArr = { { 1, 2 }, { 3, 4, 5 }, { 6, 7, 8, 9 } };
+ assertArrayEquals(new int[] { 1, 2 }, obj.getElementAtGivenIndex(multiDimensionalArr, 0));
+ assertArrayEquals(new int[] { 3, 4, 5 }, obj.getElementAtGivenIndex(multiDimensionalArr, 1));
+ assertArrayEquals(new int[] { 6, 7, 8, 9 }, obj.getElementAtGivenIndex(multiDimensionalArr, 2));
+ }
+
+ @Test
+ public void givenMultiDimensionalArray_whenUsingArraysAPI_thenVerifyPrintedElements() {
+ int[][] multiDimensionalArr = { { 1, 2 }, { 3, 4, 5 }, { 6, 7, 8, 9 } };
+ ByteArrayOutputStream outContent = new ByteArrayOutputStream();
+ System.setOut(new PrintStream(outContent));
+ obj.printElements(multiDimensionalArr);
+ assertEquals("[1, 2][3, 4, 5][6, 7, 8, 9]", outContent.toString().replace("\r", "").replace("\n", ""));
+ System.setOut(System.out);
+ }
+
+ @Test
+ public void givenMultiDimensionalArray_whenUsingArraysFill_thenVerifyInitialize2DArray() {
+ int[][] multiDimensionalArr = new int[3][];
+ multiDimensionalArr[0] = new int[2];
+ multiDimensionalArr[1] = new int[3];
+ multiDimensionalArr[2] = new int[4];
+ obj.initialize2DArray(multiDimensionalArr);
+ assertArrayEquals(new int[][] {{7,7}, {7,7,7}, {7,7,7,7}}, multiDimensionalArr);
+ }
+
+ @Test
+ public void givenMultiDimensionalArray_whenUsingIteration_thenVerifyFindLengthOfElements() {
+ int[][] multiDimensionalArr = { { 1, 2 }, { 3, 4, 5 }, { 6, 7, 8, 9 } };
+ assertArrayEquals(new int[]{2,3,4}, obj.findLengthOfElements(multiDimensionalArr));
+ }
+
+ @Test
+ public void givenMultiDimensionalArray_whenUsingArraysStream_thenVerifyFindLengthOfElements() {
+ Integer[][] multiDimensionalArr = { { 1, 2 }, { 3, 4, 5 }, { 6, 7, 8, 9 } };
+ assertArrayEquals(new Integer[]{2,3,4}, obj.findLengthOfElements(multiDimensionalArr));
+ }
+
+ @Test
+ public void givenMultiDimensionalArray_whenUsingArraysCopyOf_thenVerifyCopy2DArray() {
+ int[][] multiDimensionalArr = { { 1, 2 }, { 3, 4, 5 }, { 6, 7, 8, 9 } };
+ assertArrayEquals(multiDimensionalArr, obj.copy2DArray(multiDimensionalArr));
+ }
+
+ @Test
+ public void givenMultiDimensionalArray_whenUsingArraysStream_thenVerifyCopy2DArray() {
+ Integer[][] multiDimensionalArr = { { 1, 2 }, { 3, 4, 5 }, { 6, 7, 8, 9 } };
+ assertArrayEquals(multiDimensionalArr, obj.copy2DArray(multiDimensionalArr));
+ }
+}
diff --git a/core-java-modules/core-java-arrays/src/test/java/com/baeldung/array/SortedArrayCheckerUnitTest.java b/core-java-modules/core-java-arrays/src/test/java/com/baeldung/array/SortedArrayCheckerUnitTest.java
new file mode 100644
index 0000000000..7971e0eab7
--- /dev/null
+++ b/core-java-modules/core-java-arrays/src/test/java/com/baeldung/array/SortedArrayCheckerUnitTest.java
@@ -0,0 +1,79 @@
+package com.baeldung.array;
+
+import com.baeldung.arraycopy.model.Employee;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Comparator;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class SortedArrayCheckerUnitTest {
+ private static final int[] INTEGER_SORTED = {1, 3, 5, 7, 9};
+ private static final int[] INTEGER_NOT_SORTED = {1, 3, 11, 7};
+
+ private static final String[] STRING_SORTED = {"abc", "cde", "fgh"};
+ private static final String[] STRING_NOT_SORTED = {"abc", "fgh", "cde", "ijk"};
+
+ private static final Employee[] EMPLOYEES_SORTED_BY_NAME = {
+ new Employee(1, "Carlos", 26),
+ new Employee(2, "Daniel", 31),
+ new Employee(3, "Marta", 27)};
+
+ private static final Employee[] EMPLOYEES_NOT_SORTED_BY_NAME = {
+ new Employee(1, "Daniel", 31),
+ new Employee(2, "Carlos", 26),
+ new Employee(3, "Marta", 27)};
+
+ private static final Employee[] EMPLOYEES_SORTED_BY_AGE = {
+ new Employee(1, "Carlos", 26),
+ new Employee(2, "Marta", 27),
+ new Employee(3, "Daniel", 31)};
+
+ private static final Employee[] EMPLOYEES_NOT_SORTED_BY_AGE = {
+ new Employee(1, "Marta", 27),
+ new Employee(2, "Carlos", 26),
+ new Employee(3, "Daniel", 31)};
+
+ private SortedArrayChecker sortedArrayChecker;
+
+ @Before
+ public void setup() {
+ sortedArrayChecker = new SortedArrayChecker();
+ }
+
+ @Test
+ public void givenIntegerArray_thenReturnIfItIsSortedOrNot() {
+ assertThat(sortedArrayChecker.isSorted(INTEGER_SORTED)).isEqualTo(true);
+ assertThat(sortedArrayChecker.isSorted(INTEGER_NOT_SORTED)).isEqualTo(false);
+
+ assertThat(sortedArrayChecker.isSorted(INTEGER_SORTED, INTEGER_SORTED.length)).isEqualTo(true);
+ assertThat(sortedArrayChecker.isSorted(INTEGER_NOT_SORTED, INTEGER_NOT_SORTED.length)).isEqualTo(false);
+ }
+
+ @Test
+ public void givenStringArray_thenReturnIfItIsSortedOrNot() {
+ assertThat(sortedArrayChecker.isSorted(STRING_SORTED)).isEqualTo(true);
+ assertThat(sortedArrayChecker.isSorted(STRING_NOT_SORTED)).isEqualTo(false);
+
+ assertThat(sortedArrayChecker.isSorted(STRING_SORTED, STRING_SORTED.length)).isEqualTo(true);
+ assertThat(sortedArrayChecker.isSorted(STRING_NOT_SORTED, STRING_NOT_SORTED.length)).isEqualTo(false);
+ }
+
+ @Test
+ public void givenEmployeeArray_thenReturnIfItIsSortedOrNot() {
+ assertThat(sortedArrayChecker.isSorted(EMPLOYEES_SORTED_BY_NAME, Comparator.comparing(Employee::getName))).isEqualTo(true);
+ assertThat(sortedArrayChecker.isSorted(EMPLOYEES_NOT_SORTED_BY_NAME, Comparator.comparing(Employee::getName))).isEqualTo(false);
+
+ assertThat(sortedArrayChecker.isSorted(EMPLOYEES_SORTED_BY_AGE, Comparator.comparingInt(Employee::getAge))).isEqualTo(true);
+ assertThat(sortedArrayChecker.isSorted(EMPLOYEES_NOT_SORTED_BY_AGE, Comparator.comparingInt(Employee::getAge))).isEqualTo(false);
+
+ assertThat(sortedArrayChecker
+ .isSorted(EMPLOYEES_SORTED_BY_AGE, Comparator.comparingInt(Employee::getAge), EMPLOYEES_SORTED_BY_AGE.length))
+ .isEqualTo(true);
+ assertThat(sortedArrayChecker
+ .isSorted(EMPLOYEES_NOT_SORTED_BY_AGE, Comparator.comparingInt(Employee::getAge), EMPLOYEES_NOT_SORTED_BY_AGE.length))
+ .isEqualTo(false);
+ }
+
+}
\ No newline at end of file
diff --git a/core-java-modules/core-java-collections-2/README.md b/core-java-modules/core-java-collections-2/README.md
new file mode 100644
index 0000000000..13ca191edb
--- /dev/null
+++ b/core-java-modules/core-java-collections-2/README.md
@@ -0,0 +1,15 @@
+=========
+
+## Core Java Collections Cookbooks and Examples
+
+### Relevant Articles:
+- [Removing Elements from Java Collections](https://www.baeldung.com/java-collection-remove-elements)
+- [How to Filter a Collection in Java](https://www.baeldung.com/java-collection-filtering)
+- [Join and Split Arrays and Collections in Java](https://www.baeldung.com/java-join-and-split)
+- [Java – Combine Multiple Collections](https://www.baeldung.com/java-combine-multiple-collections)
+- [Combining Different Types of Collections in Java](https://www.baeldung.com/java-combine-collections)
+- [Shuffling Collections In Java](https://www.baeldung.com/java-shuffle-collection)
+- [Sorting in Java](https://www.baeldung.com/java-sorting)
+- [Getting the Size of an Iterable in Java](https://www.baeldung.com/java-iterable-size)
+- [Java Null-Safe Streams from Collections](https://www.baeldung.com/java-null-safe-streams-from-collections)
+
diff --git a/core-java-modules/core-java-collections-2/pom.xml b/core-java-modules/core-java-collections-2/pom.xml
new file mode 100644
index 0000000000..217c1d9258
--- /dev/null
+++ b/core-java-modules/core-java-collections-2/pom.xml
@@ -0,0 +1,57 @@
+
+ 4.0.0
+ core-java-collections-2
+ core-java-collections-2
+ jar
+
+
+ com.baeldung
+ parent-java
+ 0.0.1-SNAPSHOT
+ ../../parent-java
+
+
+
+
+ org.eclipse.collections
+ eclipse-collections
+ ${eclipse.collections.version}
+
+
+ org.apache.commons
+ commons-collections4
+ ${commons-collections4.version}
+
+
+ org.apache.commons
+ commons-exec
+ ${commons-exec.version}
+
+
+ org.apache.commons
+ commons-lang3
+ ${commons-lang3.version}
+
+
+ org.assertj
+ assertj-core
+ ${assertj.version}
+ test
+
+
+ org.junit.platform
+ junit-platform-runner
+ ${junit.platform.version}
+ test
+
+
+
+
+ 7.1.0
+ 4.1
+ 3.11.1
+ 1.2.0
+ 1.3
+
+
diff --git a/core-java-modules/core-java-collections/src/main/java/com/baeldung/combiningcollections/CombiningArrays.java b/core-java-modules/core-java-collections-2/src/main/java/com/baeldung/collections/combiningcollections/CombiningArrays.java
similarity index 95%
rename from core-java-modules/core-java-collections/src/main/java/com/baeldung/combiningcollections/CombiningArrays.java
rename to core-java-modules/core-java-collections-2/src/main/java/com/baeldung/collections/combiningcollections/CombiningArrays.java
index 2ad48033c0..5f63123f6a 100644
--- a/core-java-modules/core-java-collections/src/main/java/com/baeldung/combiningcollections/CombiningArrays.java
+++ b/core-java-modules/core-java-collections-2/src/main/java/com/baeldung/collections/combiningcollections/CombiningArrays.java
@@ -1,4 +1,4 @@
-package com.baeldung.combiningcollections;
+package com.baeldung.collections.combiningcollections;
import java.util.Arrays;
import java.util.stream.Stream;
diff --git a/core-java-modules/core-java-collections/src/main/java/com/baeldung/combiningcollections/CombiningLists.java b/core-java-modules/core-java-collections-2/src/main/java/com/baeldung/collections/combiningcollections/CombiningLists.java
similarity index 96%
rename from core-java-modules/core-java-collections/src/main/java/com/baeldung/combiningcollections/CombiningLists.java
rename to core-java-modules/core-java-collections-2/src/main/java/com/baeldung/collections/combiningcollections/CombiningLists.java
index 3fdf672758..a45e9cdfe8 100644
--- a/core-java-modules/core-java-collections/src/main/java/com/baeldung/combiningcollections/CombiningLists.java
+++ b/core-java-modules/core-java-collections-2/src/main/java/com/baeldung/collections/combiningcollections/CombiningLists.java
@@ -1,4 +1,4 @@
-package com.baeldung.combiningcollections;
+package com.baeldung.collections.combiningcollections;
import java.util.ArrayList;
import java.util.Collection;
diff --git a/core-java-modules/core-java-collections/src/main/java/com/baeldung/combiningcollections/CombiningMaps.java b/core-java-modules/core-java-collections-2/src/main/java/com/baeldung/collections/combiningcollections/CombiningMaps.java
similarity index 96%
rename from core-java-modules/core-java-collections/src/main/java/com/baeldung/combiningcollections/CombiningMaps.java
rename to core-java-modules/core-java-collections-2/src/main/java/com/baeldung/collections/combiningcollections/CombiningMaps.java
index d8bbd01ed3..d4d21d0dad 100644
--- a/core-java-modules/core-java-collections/src/main/java/com/baeldung/combiningcollections/CombiningMaps.java
+++ b/core-java-modules/core-java-collections-2/src/main/java/com/baeldung/collections/combiningcollections/CombiningMaps.java
@@ -1,4 +1,4 @@
-package com.baeldung.combiningcollections;
+package com.baeldung.collections.combiningcollections;
import java.util.Collection;
import java.util.HashMap;
diff --git a/core-java-modules/core-java-collections/src/main/java/com/baeldung/combiningcollections/CombiningSets.java b/core-java-modules/core-java-collections-2/src/main/java/com/baeldung/collections/combiningcollections/CombiningSets.java
similarity index 96%
rename from core-java-modules/core-java-collections/src/main/java/com/baeldung/combiningcollections/CombiningSets.java
rename to core-java-modules/core-java-collections-2/src/main/java/com/baeldung/collections/combiningcollections/CombiningSets.java
index 5f531c1d43..27a5681eee 100644
--- a/core-java-modules/core-java-collections/src/main/java/com/baeldung/combiningcollections/CombiningSets.java
+++ b/core-java-modules/core-java-collections-2/src/main/java/com/baeldung/collections/combiningcollections/CombiningSets.java
@@ -1,4 +1,4 @@
-package com.baeldung.combiningcollections;
+package com.baeldung.collections.combiningcollections;
import java.util.Collection;
import java.util.HashSet;
diff --git a/core-java-modules/core-java-collections/src/main/java/com/baeldung/java/filtering/CollectionUtilsCollectionFilter.java b/core-java-modules/core-java-collections-2/src/main/java/com/baeldung/collections/filtering/CollectionUtilsCollectionFilter.java
similarity index 91%
rename from core-java-modules/core-java-collections/src/main/java/com/baeldung/java/filtering/CollectionUtilsCollectionFilter.java
rename to core-java-modules/core-java-collections-2/src/main/java/com/baeldung/collections/filtering/CollectionUtilsCollectionFilter.java
index 58f9f6af54..de5158e147 100644
--- a/core-java-modules/core-java-collections/src/main/java/com/baeldung/java/filtering/CollectionUtilsCollectionFilter.java
+++ b/core-java-modules/core-java-collections-2/src/main/java/com/baeldung/collections/filtering/CollectionUtilsCollectionFilter.java
@@ -1,4 +1,4 @@
-package com.baeldung.java.filtering;
+package com.baeldung.collections.filtering;
import java.util.Collection;
diff --git a/core-java-modules/core-java-collections/src/main/java/com/baeldung/java/filtering/EclipseCollectionsCollectionFilter.java b/core-java-modules/core-java-collections-2/src/main/java/com/baeldung/collections/filtering/EclipseCollectionsCollectionFilter.java
similarity index 96%
rename from core-java-modules/core-java-collections/src/main/java/com/baeldung/java/filtering/EclipseCollectionsCollectionFilter.java
rename to core-java-modules/core-java-collections-2/src/main/java/com/baeldung/collections/filtering/EclipseCollectionsCollectionFilter.java
index 981d6ca241..a7b78b1f9b 100644
--- a/core-java-modules/core-java-collections/src/main/java/com/baeldung/java/filtering/EclipseCollectionsCollectionFilter.java
+++ b/core-java-modules/core-java-collections-2/src/main/java/com/baeldung/collections/filtering/EclipseCollectionsCollectionFilter.java
@@ -1,4 +1,4 @@
-package com.baeldung.java.filtering;
+package com.baeldung.collections.filtering;
import java.util.Collection;
diff --git a/core-java-modules/core-java-collections/src/main/java/com/baeldung/java/filtering/GuavaCollectionFilter.java b/core-java-modules/core-java-collections-2/src/main/java/com/baeldung/collections/filtering/GuavaCollectionFilter.java
similarity index 91%
rename from core-java-modules/core-java-collections/src/main/java/com/baeldung/java/filtering/GuavaCollectionFilter.java
rename to core-java-modules/core-java-collections-2/src/main/java/com/baeldung/collections/filtering/GuavaCollectionFilter.java
index 88338fd6d4..0a2a782c33 100644
--- a/core-java-modules/core-java-collections/src/main/java/com/baeldung/java/filtering/GuavaCollectionFilter.java
+++ b/core-java-modules/core-java-collections-2/src/main/java/com/baeldung/collections/filtering/GuavaCollectionFilter.java
@@ -1,4 +1,4 @@
-package com.baeldung.java.filtering;
+package com.baeldung.collections.filtering;
import java.util.Collection;
diff --git a/core-java-modules/core-java-collections/src/main/java/com/baeldung/java/filtering/StreamsCollectionFilter.java b/core-java-modules/core-java-collections-2/src/main/java/com/baeldung/collections/filtering/StreamsCollectionFilter.java
similarity index 95%
rename from core-java-modules/core-java-collections/src/main/java/com/baeldung/java/filtering/StreamsCollectionFilter.java
rename to core-java-modules/core-java-collections-2/src/main/java/com/baeldung/collections/filtering/StreamsCollectionFilter.java
index f074f74199..a9fb8481e5 100644
--- a/core-java-modules/core-java-collections/src/main/java/com/baeldung/java/filtering/StreamsCollectionFilter.java
+++ b/core-java-modules/core-java-collections-2/src/main/java/com/baeldung/collections/filtering/StreamsCollectionFilter.java
@@ -1,4 +1,4 @@
-package com.baeldung.java.filtering;
+package com.baeldung.collections.filtering;
import java.util.Collection;
import java.util.function.Predicate;
diff --git a/core-java-modules/core-java-collections/src/main/java/com/baeldung/java/iterable/IterableSize.java b/core-java-modules/core-java-collections-2/src/main/java/com/baeldung/collections/iterablesize/IterableSize.java
similarity index 97%
rename from core-java-modules/core-java-collections/src/main/java/com/baeldung/java/iterable/IterableSize.java
rename to core-java-modules/core-java-collections-2/src/main/java/com/baeldung/collections/iterablesize/IterableSize.java
index 03864f16f2..b96e2bb571 100644
--- a/core-java-modules/core-java-collections/src/main/java/com/baeldung/java/iterable/IterableSize.java
+++ b/core-java-modules/core-java-collections-2/src/main/java/com/baeldung/collections/iterablesize/IterableSize.java
@@ -1,4 +1,4 @@
-package com.baeldung.java.iterable;
+package com.baeldung.collections.iterablesize;
import java.util.Collection;
import java.util.stream.StreamSupport;
diff --git a/core-java-modules/core-java-collections/src/main/java/com/baeldung/nullsafecollectionstreams/NullSafeCollectionStreamsUsingCommonsEmptyIfNull.java b/core-java-modules/core-java-collections-2/src/main/java/com/baeldung/collections/nullsafecollectionstreams/NullSafeCollectionStreamsUsingCommonsEmptyIfNull.java
similarity index 92%
rename from core-java-modules/core-java-collections/src/main/java/com/baeldung/nullsafecollectionstreams/NullSafeCollectionStreamsUsingCommonsEmptyIfNull.java
rename to core-java-modules/core-java-collections-2/src/main/java/com/baeldung/collections/nullsafecollectionstreams/NullSafeCollectionStreamsUsingCommonsEmptyIfNull.java
index 2405c26aac..ce5cbb39d6 100644
--- a/core-java-modules/core-java-collections/src/main/java/com/baeldung/nullsafecollectionstreams/NullSafeCollectionStreamsUsingCommonsEmptyIfNull.java
+++ b/core-java-modules/core-java-collections-2/src/main/java/com/baeldung/collections/nullsafecollectionstreams/NullSafeCollectionStreamsUsingCommonsEmptyIfNull.java
@@ -1,4 +1,4 @@
-package com.baeldung.nullsafecollectionstreams;
+package com.baeldung.collections.nullsafecollectionstreams;
import java.util.Collection;
import java.util.stream.Stream;
diff --git a/core-java-modules/core-java-collections/src/main/java/com/baeldung/nullsafecollectionstreams/NullSafeCollectionStreamsUsingJava8OptionalContainer.java b/core-java-modules/core-java-collections-2/src/main/java/com/baeldung/collections/nullsafecollectionstreams/NullSafeCollectionStreamsUsingJava8OptionalContainer.java
similarity index 92%
rename from core-java-modules/core-java-collections/src/main/java/com/baeldung/nullsafecollectionstreams/NullSafeCollectionStreamsUsingJava8OptionalContainer.java
rename to core-java-modules/core-java-collections-2/src/main/java/com/baeldung/collections/nullsafecollectionstreams/NullSafeCollectionStreamsUsingJava8OptionalContainer.java
index da767d4563..68d51c2d87 100644
--- a/core-java-modules/core-java-collections/src/main/java/com/baeldung/nullsafecollectionstreams/NullSafeCollectionStreamsUsingJava8OptionalContainer.java
+++ b/core-java-modules/core-java-collections-2/src/main/java/com/baeldung/collections/nullsafecollectionstreams/NullSafeCollectionStreamsUsingJava8OptionalContainer.java
@@ -1,4 +1,4 @@
-package com.baeldung.nullsafecollectionstreams;
+package com.baeldung.collections.nullsafecollectionstreams;
import java.util.Collection;
import java.util.Optional;
diff --git a/core-java-modules/core-java-collections/src/main/java/com/baeldung/nullsafecollectionstreams/NullSafeCollectionStreamsUsingNullDereferenceCheck.java b/core-java-modules/core-java-collections-2/src/main/java/com/baeldung/collections/nullsafecollectionstreams/NullSafeCollectionStreamsUsingNullDereferenceCheck.java
similarity index 91%
rename from core-java-modules/core-java-collections/src/main/java/com/baeldung/nullsafecollectionstreams/NullSafeCollectionStreamsUsingNullDereferenceCheck.java
rename to core-java-modules/core-java-collections-2/src/main/java/com/baeldung/collections/nullsafecollectionstreams/NullSafeCollectionStreamsUsingNullDereferenceCheck.java
index 0c10f1cebc..6c606ebedd 100644
--- a/core-java-modules/core-java-collections/src/main/java/com/baeldung/nullsafecollectionstreams/NullSafeCollectionStreamsUsingNullDereferenceCheck.java
+++ b/core-java-modules/core-java-collections-2/src/main/java/com/baeldung/collections/nullsafecollectionstreams/NullSafeCollectionStreamsUsingNullDereferenceCheck.java
@@ -1,4 +1,4 @@
-package com.baeldung.nullsafecollectionstreams;
+package com.baeldung.collections.nullsafecollectionstreams;
import java.util.Collection;
import java.util.stream.Stream;
diff --git a/core-java-modules/core-java-collections/src/main/java/com/baeldung/removal/CollectionRemoveIf.java b/core-java-modules/core-java-collections-2/src/main/java/com/baeldung/collections/removal/CollectionRemoveIf.java
similarity index 91%
rename from core-java-modules/core-java-collections/src/main/java/com/baeldung/removal/CollectionRemoveIf.java
rename to core-java-modules/core-java-collections-2/src/main/java/com/baeldung/collections/removal/CollectionRemoveIf.java
index 2f5e91596f..4089382376 100644
--- a/core-java-modules/core-java-collections/src/main/java/com/baeldung/removal/CollectionRemoveIf.java
+++ b/core-java-modules/core-java-collections-2/src/main/java/com/baeldung/collections/removal/CollectionRemoveIf.java
@@ -1,4 +1,4 @@
-package com.baeldung.removal;
+package com.baeldung.collections.removal;
import java.util.ArrayList;
import java.util.Collection;
diff --git a/core-java-modules/core-java-collections-2/src/main/java/com/baeldung/collections/removal/Iterators.java b/core-java-modules/core-java-collections-2/src/main/java/com/baeldung/collections/removal/Iterators.java
new file mode 100644
index 0000000000..d551b04eae
--- /dev/null
+++ b/core-java-modules/core-java-collections-2/src/main/java/com/baeldung/collections/removal/Iterators.java
@@ -0,0 +1,28 @@
+package com.baeldung.collections.removal;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+
+public class Iterators {
+
+ public static void main(String args[]) {
+ Collection names = new ArrayList<>();
+ names.add("John");
+ names.add("Ana");
+ names.add("Mary");
+ names.add("Anthony");
+ names.add("Mark");
+
+ Iterator i = names.iterator();
+
+ while (i.hasNext()) {
+ String e = i.next();
+ if (e.startsWith("A")) {
+ i.remove();
+ }
+ }
+
+ System.out.println(String.join(",", names));
+ }
+}
diff --git a/core-java-modules/core-java-collections/src/main/java/com/baeldung/removal/StreamFilterAndCollector.java b/core-java-modules/core-java-collections-2/src/main/java/com/baeldung/collections/removal/StreamFilterAndCollector.java
similarity index 93%
rename from core-java-modules/core-java-collections/src/main/java/com/baeldung/removal/StreamFilterAndCollector.java
rename to core-java-modules/core-java-collections-2/src/main/java/com/baeldung/collections/removal/StreamFilterAndCollector.java
index bf6db68bae..e0dc75f428 100644
--- a/core-java-modules/core-java-collections/src/main/java/com/baeldung/removal/StreamFilterAndCollector.java
+++ b/core-java-modules/core-java-collections-2/src/main/java/com/baeldung/collections/removal/StreamFilterAndCollector.java
@@ -1,4 +1,4 @@
-package com.baeldung.removal;
+package com.baeldung.collections.removal;
import java.util.ArrayList;
import java.util.Collection;
diff --git a/core-java-modules/core-java-collections/src/main/java/com/baeldung/removal/StreamPartitioningBy.java b/core-java-modules/core-java-collections-2/src/main/java/com/baeldung/collections/removal/StreamPartitioningBy.java
similarity index 95%
rename from core-java-modules/core-java-collections/src/main/java/com/baeldung/removal/StreamPartitioningBy.java
rename to core-java-modules/core-java-collections-2/src/main/java/com/baeldung/collections/removal/StreamPartitioningBy.java
index c77e996616..c01c334f01 100644
--- a/core-java-modules/core-java-collections/src/main/java/com/baeldung/removal/StreamPartitioningBy.java
+++ b/core-java-modules/core-java-collections-2/src/main/java/com/baeldung/collections/removal/StreamPartitioningBy.java
@@ -1,4 +1,4 @@
-package com.baeldung.removal;
+package com.baeldung.collections.removal;
import java.util.ArrayList;
import java.util.Collection;
diff --git a/core-java-modules/core-java-collections/src/test/java/com/baeldung/combiningcollections/CombiningArraysUnitTest.java b/core-java-modules/core-java-collections-2/src/test/java/com/baeldung/collections/combiningcollections/CombiningArraysUnitTest.java
similarity index 96%
rename from core-java-modules/core-java-collections/src/test/java/com/baeldung/combiningcollections/CombiningArraysUnitTest.java
rename to core-java-modules/core-java-collections-2/src/test/java/com/baeldung/collections/combiningcollections/CombiningArraysUnitTest.java
index 3b80d773ad..312f5582ba 100644
--- a/core-java-modules/core-java-collections/src/test/java/com/baeldung/combiningcollections/CombiningArraysUnitTest.java
+++ b/core-java-modules/core-java-collections-2/src/test/java/com/baeldung/collections/combiningcollections/CombiningArraysUnitTest.java
@@ -1,4 +1,4 @@
-package com.baeldung.combiningcollections;
+package com.baeldung.collections.combiningcollections;
import static org.junit.Assert.*;
import org.junit.Test;
diff --git a/core-java-modules/core-java-collections/src/test/java/com/baeldung/combiningcollections/CombiningListsUnitTest.java b/core-java-modules/core-java-collections-2/src/test/java/com/baeldung/collections/combiningcollections/CombiningListsUnitTest.java
similarity index 96%
rename from core-java-modules/core-java-collections/src/test/java/com/baeldung/combiningcollections/CombiningListsUnitTest.java
rename to core-java-modules/core-java-collections-2/src/test/java/com/baeldung/collections/combiningcollections/CombiningListsUnitTest.java
index c5851d7daf..5443e56e5f 100644
--- a/core-java-modules/core-java-collections/src/test/java/com/baeldung/combiningcollections/CombiningListsUnitTest.java
+++ b/core-java-modules/core-java-collections-2/src/test/java/com/baeldung/collections/combiningcollections/CombiningListsUnitTest.java
@@ -1,4 +1,4 @@
-package com.baeldung.combiningcollections;
+package com.baeldung.collections.combiningcollections;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
diff --git a/core-java-modules/core-java-collections/src/test/java/com/baeldung/combiningcollections/CombiningMapsUnitTest.java b/core-java-modules/core-java-collections-2/src/test/java/com/baeldung/collections/combiningcollections/CombiningMapsUnitTest.java
similarity index 96%
rename from core-java-modules/core-java-collections/src/test/java/com/baeldung/combiningcollections/CombiningMapsUnitTest.java
rename to core-java-modules/core-java-collections-2/src/test/java/com/baeldung/collections/combiningcollections/CombiningMapsUnitTest.java
index 3fa9cc7dc4..644f178f80 100644
--- a/core-java-modules/core-java-collections/src/test/java/com/baeldung/combiningcollections/CombiningMapsUnitTest.java
+++ b/core-java-modules/core-java-collections-2/src/test/java/com/baeldung/collections/combiningcollections/CombiningMapsUnitTest.java
@@ -1,4 +1,4 @@
-package com.baeldung.combiningcollections;
+package com.baeldung.collections.combiningcollections;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
diff --git a/core-java-modules/core-java-collections/src/test/java/com/baeldung/combiningcollections/CombiningSetsUnitTest.java b/core-java-modules/core-java-collections-2/src/test/java/com/baeldung/collections/combiningcollections/CombiningSetsUnitTest.java
similarity index 96%
rename from core-java-modules/core-java-collections/src/test/java/com/baeldung/combiningcollections/CombiningSetsUnitTest.java
rename to core-java-modules/core-java-collections-2/src/test/java/com/baeldung/collections/combiningcollections/CombiningSetsUnitTest.java
index 330827bdc2..c9976eb6de 100644
--- a/core-java-modules/core-java-collections/src/test/java/com/baeldung/combiningcollections/CombiningSetsUnitTest.java
+++ b/core-java-modules/core-java-collections-2/src/test/java/com/baeldung/collections/combiningcollections/CombiningSetsUnitTest.java
@@ -1,5 +1,5 @@
-package com.baeldung.combiningcollections;
+package com.baeldung.collections.combiningcollections;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
diff --git a/core-java-modules/core-java-collections/src/test/java/com/baeldung/java/filtering/CollectionFiltersUnitTest.java b/core-java-modules/core-java-collections-2/src/test/java/com/baeldung/collections/filtering/CollectionFiltersUnitTest.java
similarity index 98%
rename from core-java-modules/core-java-collections/src/test/java/com/baeldung/java/filtering/CollectionFiltersUnitTest.java
rename to core-java-modules/core-java-collections-2/src/test/java/com/baeldung/collections/filtering/CollectionFiltersUnitTest.java
index b30805d471..db387818b8 100644
--- a/core-java-modules/core-java-collections/src/test/java/com/baeldung/java/filtering/CollectionFiltersUnitTest.java
+++ b/core-java-modules/core-java-collections-2/src/test/java/com/baeldung/collections/filtering/CollectionFiltersUnitTest.java
@@ -1,4 +1,4 @@
-package com.baeldung.java.filtering;
+package com.baeldung.collections.filtering;
import static org.assertj.core.api.Assertions.assertThat;
diff --git a/core-java-modules/core-java-collections/src/test/java/com/baeldung/java/iterable/IterableSizeUnitTest.java b/core-java-modules/core-java-collections-2/src/test/java/com/baeldung/collections/iterablesize/IterableSizeUnitTest.java
similarity index 96%
rename from core-java-modules/core-java-collections/src/test/java/com/baeldung/java/iterable/IterableSizeUnitTest.java
rename to core-java-modules/core-java-collections-2/src/test/java/com/baeldung/collections/iterablesize/IterableSizeUnitTest.java
index 4bc413dee0..35702a74b3 100644
--- a/core-java-modules/core-java-collections/src/test/java/com/baeldung/java/iterable/IterableSizeUnitTest.java
+++ b/core-java-modules/core-java-collections-2/src/test/java/com/baeldung/collections/iterablesize/IterableSizeUnitTest.java
@@ -1,4 +1,4 @@
-package com.baeldung.java.iterable;
+package com.baeldung.collections.iterablesize;
import static org.junit.Assert.assertEquals;
diff --git a/core-java-modules/core-java-collections/src/test/java/org/baeldung/java/collections/CollectionsJoinAndSplitJUnitTest.java b/core-java-modules/core-java-collections-2/src/test/java/com/baeldung/collections/joinsplit/CollectionsJoinAndSplitJUnitTest.java
similarity index 97%
rename from core-java-modules/core-java-collections/src/test/java/org/baeldung/java/collections/CollectionsJoinAndSplitJUnitTest.java
rename to core-java-modules/core-java-collections-2/src/test/java/com/baeldung/collections/joinsplit/CollectionsJoinAndSplitJUnitTest.java
index c288cf499d..1904fd1587 100644
--- a/core-java-modules/core-java-collections/src/test/java/org/baeldung/java/collections/CollectionsJoinAndSplitJUnitTest.java
+++ b/core-java-modules/core-java-collections-2/src/test/java/com/baeldung/collections/joinsplit/CollectionsJoinAndSplitJUnitTest.java
@@ -1,4 +1,4 @@
-package org.baeldung.java.collections;
+package com.baeldung.collections.joinsplit;
import java.util.ArrayList;
import java.util.Collections;
diff --git a/core-java-modules/core-java-collections/src/test/java/org/baeldung/java/collections/JoinSplitCollectionsUnitTest.java b/core-java-modules/core-java-collections-2/src/test/java/com/baeldung/collections/joinsplit/JoinSplitCollectionsUnitTest.java
similarity index 99%
rename from core-java-modules/core-java-collections/src/test/java/org/baeldung/java/collections/JoinSplitCollectionsUnitTest.java
rename to core-java-modules/core-java-collections-2/src/test/java/com/baeldung/collections/joinsplit/JoinSplitCollectionsUnitTest.java
index c594529f41..1fbe210ad4 100644
--- a/core-java-modules/core-java-collections/src/test/java/org/baeldung/java/collections/JoinSplitCollectionsUnitTest.java
+++ b/core-java-modules/core-java-collections-2/src/test/java/com/baeldung/collections/joinsplit/JoinSplitCollectionsUnitTest.java
@@ -1,4 +1,4 @@
-package org.baeldung.java.collections;
+package com.baeldung.collections.joinsplit;
import org.junit.Test;
diff --git a/core-java-modules/core-java-collections-2/src/test/java/com/baeldung/collections/multiplecollections/CombineMultipleCollectionsUnitTest.java b/core-java-modules/core-java-collections-2/src/test/java/com/baeldung/collections/multiplecollections/CombineMultipleCollectionsUnitTest.java
new file mode 100644
index 0000000000..ebef8d6875
--- /dev/null
+++ b/core-java-modules/core-java-collections-2/src/test/java/com/baeldung/collections/multiplecollections/CombineMultipleCollectionsUnitTest.java
@@ -0,0 +1,135 @@
+package com.baeldung.collections.multiplecollections;
+
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.collections4.IterableUtils;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.util.*;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import static java.util.Arrays.asList;
+
+public class CombineMultipleCollectionsUnitTest {
+
+ @Test
+ public void givenUsingJava8_whenConcatenatingUsingConcat_thenCorrect() {
+ Collection collectionA = asList("S", "T");
+ Collection collectionB = asList("U", "V");
+ Collection collectionC = asList("W", "X");
+
+ Stream combinedStream = Stream.concat(Stream.concat(collectionA.stream(), collectionB.stream()), collectionC.stream());
+ Collection collectionCombined = combinedStream.collect(Collectors.toList());
+
+ Assert.assertEquals(asList("S", "T", "U", "V", "W", "X"), collectionCombined);
+ }
+
+ @Test
+ public void givenUsingJava8_whenConcatenatingUsingflatMap_thenCorrect() {
+ Collection collectionA = asList("S", "T");
+ Collection collectionB = asList("U", "V");
+
+ Stream combinedStream = Stream.of(collectionA, collectionB).flatMap(Collection::stream);
+ Collection collectionCombined = combinedStream.collect(Collectors.toList());
+
+ Assert.assertEquals(asList("S", "T", "U", "V"), collectionCombined);
+ }
+
+ @Test
+ public void givenUsingGuava_whenConcatenatingUsingIterables_thenCorrect() {
+ Collection collectionA = asList("S", "T");
+ Collection collectionB = asList("U", "V");
+
+ Iterable combinedIterables = Iterables.unmodifiableIterable(Iterables.concat(collectionA, collectionB));
+ Collection collectionCombined = Lists.newArrayList(combinedIterables);
+
+ Assert.assertEquals(asList("S", "T", "U", "V"), collectionCombined);
+ }
+
+ @Test
+ public void givenUsingJava7_whenConcatenatingUsingIterables_thenCorrect() {
+ Collection collectionA = asList("S", "T");
+ Collection collectionB = asList("U", "V");
+
+ Iterable combinedIterables = concat(collectionA, collectionB);
+ Collection collectionCombined = makeListFromIterable(combinedIterables);
+ Assert.assertEquals(Arrays.asList("S", "T", "U", "V"), collectionCombined);
+ }
+
+ public static Iterable concat(Iterable extends E> i1, Iterable extends E> i2) {
+ return new Iterable() {
+ public Iterator iterator() {
+ return new Iterator() {
+ Iterator extends E> listIterator = i1.iterator();
+ Boolean checkedHasNext;
+ E nextValue;
+ private boolean startTheSecond;
+
+ void theNext() {
+ if (listIterator.hasNext()) {
+ checkedHasNext = true;
+ nextValue = listIterator.next();
+ } else if (startTheSecond)
+ checkedHasNext = false;
+ else {
+ startTheSecond = true;
+ listIterator = i2.iterator();
+ theNext();
+ }
+ }
+
+ public boolean hasNext() {
+ if (checkedHasNext == null)
+ theNext();
+ return checkedHasNext;
+ }
+
+ public E next() {
+ if (!hasNext())
+ throw new NoSuchElementException();
+ checkedHasNext = null;
+ return nextValue;
+ }
+
+ public void remove() {
+ listIterator.remove();
+ }
+ };
+ }
+ };
+ }
+
+ public static List makeListFromIterable(Iterable iter) {
+ List list = new ArrayList<>();
+ for (E item : iter) {
+ list.add(item);
+ }
+ return list;
+ }
+
+ @Test
+ public void givenUsingApacheCommons_whenConcatenatingUsingUnion_thenCorrect() {
+ Collection collectionA = asList("S", "T");
+ Collection collectionB = asList("U", "V");
+
+ Iterable combinedIterables = CollectionUtils.union(collectionA, collectionB);
+ Collection collectionCombined = Lists.newArrayList(combinedIterables);
+
+ Assert.assertEquals(asList("S", "T", "U", "V"), collectionCombined);
+ }
+
+ @Test
+ public void givenUsingApacheCommons_whenConcatenatingUsingChainedIterable_thenCorrect() {
+ Collection collectionA = asList("S", "T");
+ Collection collectionB = asList("U", "V");
+
+ Iterable combinedIterables = IterableUtils.chainedIterable(collectionA, collectionB);
+ Collection collectionCombined = Lists.newArrayList(combinedIterables);
+
+ Assert.assertEquals(asList("S", "T", "U", "V"), collectionCombined);
+ }
+}
diff --git a/core-java-modules/core-java-collections/src/test/java/com/baeldung/nullsafecollectionstreams/NullSafeCollectionStreamsUsingCommonsEmptyIfNullUnitTest.java b/core-java-modules/core-java-collections-2/src/test/java/com/baeldung/collections/nullsafecollectionstreams/NullSafeCollectionStreamsUsingCommonsEmptyIfNullUnitTest.java
similarity index 95%
rename from core-java-modules/core-java-collections/src/test/java/com/baeldung/nullsafecollectionstreams/NullSafeCollectionStreamsUsingCommonsEmptyIfNullUnitTest.java
rename to core-java-modules/core-java-collections-2/src/test/java/com/baeldung/collections/nullsafecollectionstreams/NullSafeCollectionStreamsUsingCommonsEmptyIfNullUnitTest.java
index 875045946d..42cda7926c 100644
--- a/core-java-modules/core-java-collections/src/test/java/com/baeldung/nullsafecollectionstreams/NullSafeCollectionStreamsUsingCommonsEmptyIfNullUnitTest.java
+++ b/core-java-modules/core-java-collections-2/src/test/java/com/baeldung/collections/nullsafecollectionstreams/NullSafeCollectionStreamsUsingCommonsEmptyIfNullUnitTest.java
@@ -1,4 +1,4 @@
-package com.baeldung.nullsafecollectionstreams;
+package com.baeldung.collections.nullsafecollectionstreams;
import java.util.Arrays;
import java.util.Collection;
diff --git a/core-java-modules/core-java-collections/src/test/java/com/baeldung/nullsafecollectionstreams/NullSafeCollectionStreamsUsingJava8OptionalContainerUnitTest.java b/core-java-modules/core-java-collections-2/src/test/java/com/baeldung/collections/nullsafecollectionstreams/NullSafeCollectionStreamsUsingJava8OptionalContainerUnitTest.java
similarity index 95%
rename from core-java-modules/core-java-collections/src/test/java/com/baeldung/nullsafecollectionstreams/NullSafeCollectionStreamsUsingJava8OptionalContainerUnitTest.java
rename to core-java-modules/core-java-collections-2/src/test/java/com/baeldung/collections/nullsafecollectionstreams/NullSafeCollectionStreamsUsingJava8OptionalContainerUnitTest.java
index 402f1a6a19..666d5e7d04 100644
--- a/core-java-modules/core-java-collections/src/test/java/com/baeldung/nullsafecollectionstreams/NullSafeCollectionStreamsUsingJava8OptionalContainerUnitTest.java
+++ b/core-java-modules/core-java-collections-2/src/test/java/com/baeldung/collections/nullsafecollectionstreams/NullSafeCollectionStreamsUsingJava8OptionalContainerUnitTest.java
@@ -1,4 +1,4 @@
-package com.baeldung.nullsafecollectionstreams;
+package com.baeldung.collections.nullsafecollectionstreams;
import java.util.Arrays;
import java.util.Collection;
diff --git a/core-java-modules/core-java-collections/src/test/java/com/baeldung/nullsafecollectionstreams/NullSafeCollectionStreamsUsingNullDereferenceCheckUnitTest.java b/core-java-modules/core-java-collections-2/src/test/java/com/baeldung/collections/nullsafecollectionstreams/NullSafeCollectionStreamsUsingNullDereferenceCheckUnitTest.java
similarity index 95%
rename from core-java-modules/core-java-collections/src/test/java/com/baeldung/nullsafecollectionstreams/NullSafeCollectionStreamsUsingNullDereferenceCheckUnitTest.java
rename to core-java-modules/core-java-collections-2/src/test/java/com/baeldung/collections/nullsafecollectionstreams/NullSafeCollectionStreamsUsingNullDereferenceCheckUnitTest.java
index bb6152371d..2e8eeb35ad 100644
--- a/core-java-modules/core-java-collections/src/test/java/com/baeldung/nullsafecollectionstreams/NullSafeCollectionStreamsUsingNullDereferenceCheckUnitTest.java
+++ b/core-java-modules/core-java-collections-2/src/test/java/com/baeldung/collections/nullsafecollectionstreams/NullSafeCollectionStreamsUsingNullDereferenceCheckUnitTest.java
@@ -1,4 +1,4 @@
-package com.baeldung.nullsafecollectionstreams;
+package com.baeldung.collections.nullsafecollectionstreams;
import java.util.Arrays;
import java.util.Collection;
diff --git a/core-java-modules/core-java-collections/src/test/java/com/baeldung/removal/RemovalUnitTest.java b/core-java-modules/core-java-collections-2/src/test/java/com/baeldung/collections/removal/RemovalUnitTest.java
similarity index 98%
rename from core-java-modules/core-java-collections/src/test/java/com/baeldung/removal/RemovalUnitTest.java
rename to core-java-modules/core-java-collections-2/src/test/java/com/baeldung/collections/removal/RemovalUnitTest.java
index 1b379f32de..998dbe6cca 100644
--- a/core-java-modules/core-java-collections/src/test/java/com/baeldung/removal/RemovalUnitTest.java
+++ b/core-java-modules/core-java-collections-2/src/test/java/com/baeldung/collections/removal/RemovalUnitTest.java
@@ -1,4 +1,4 @@
-package com.baeldung.removal;
+package com.baeldung.collections.removal;
import org.junit.Before;
import org.junit.Test;
diff --git a/core-java-modules/core-java-collections/src/test/java/com/baeldung/shufflingcollections/ShufflingCollectionsUnitTest.java b/core-java-modules/core-java-collections-2/src/test/java/com/baeldung/collections/shufflingcollections/ShufflingCollectionsUnitTest.java
similarity index 97%
rename from core-java-modules/core-java-collections/src/test/java/com/baeldung/shufflingcollections/ShufflingCollectionsUnitTest.java
rename to core-java-modules/core-java-collections-2/src/test/java/com/baeldung/collections/shufflingcollections/ShufflingCollectionsUnitTest.java
index d013907c9a..041e67ba7f 100644
--- a/core-java-modules/core-java-collections/src/test/java/com/baeldung/shufflingcollections/ShufflingCollectionsUnitTest.java
+++ b/core-java-modules/core-java-collections-2/src/test/java/com/baeldung/collections/shufflingcollections/ShufflingCollectionsUnitTest.java
@@ -1,4 +1,4 @@
-package com.baeldung.shufflingcollections;
+package com.baeldung.collections.shufflingcollections;
import org.junit.Test;
diff --git a/core-java-modules/core-java-collections-2/src/test/java/com/baeldung/collections/sorting/Employee.java b/core-java-modules/core-java-collections-2/src/test/java/com/baeldung/collections/sorting/Employee.java
new file mode 100644
index 0000000000..e838dbea18
--- /dev/null
+++ b/core-java-modules/core-java-collections-2/src/test/java/com/baeldung/collections/sorting/Employee.java
@@ -0,0 +1,62 @@
+package com.baeldung.collections.sorting;
+
+public class Employee implements Comparable {
+
+ private String name;
+ private int age;
+ private double salary;
+
+ public Employee(String name, int age, double salary) {
+ this.name = name;
+ this.age = age;
+ this.salary = salary;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public int getAge() {
+ return age;
+ }
+
+ public void setAge(int age) {
+ this.age = age;
+ }
+
+ public double getSalary() {
+ return salary;
+ }
+
+ public void setSalary(double salary) {
+ this.salary = salary;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return ((Employee) obj).getName()
+ .equals(getName());
+ }
+
+ @Override
+ public int compareTo(Object o) {
+ Employee e = (Employee) o;
+ return getName().compareTo(e.getName());
+ }
+
+ @Override
+ public String toString() {
+ return new StringBuffer().append("(")
+ .append(getName())
+ .append(getAge())
+ .append(",")
+ .append(getSalary())
+ .append(")")
+ .toString();
+ }
+
+}
diff --git a/core-java-modules/core-java-collections/src/test/java/org/baeldung/java/sorting/JavaSortingUnitTest.java b/core-java-modules/core-java-collections-2/src/test/java/com/baeldung/collections/sorting/JavaSortingUnitTest.java
similarity index 99%
rename from core-java-modules/core-java-collections/src/test/java/org/baeldung/java/sorting/JavaSortingUnitTest.java
rename to core-java-modules/core-java-collections-2/src/test/java/com/baeldung/collections/sorting/JavaSortingUnitTest.java
index ca9c9b4b5d..2505adcea7 100644
--- a/core-java-modules/core-java-collections/src/test/java/org/baeldung/java/sorting/JavaSortingUnitTest.java
+++ b/core-java-modules/core-java-collections-2/src/test/java/com/baeldung/collections/sorting/JavaSortingUnitTest.java
@@ -1,4 +1,4 @@
-package org.baeldung.java.sorting;
+package com.baeldung.collections.sorting;
import com.google.common.primitives.Ints;
import org.apache.commons.lang3.ArrayUtils;
diff --git a/core-java-modules/core-java-collections-3/README.md b/core-java-modules/core-java-collections-3/README.md
new file mode 100644
index 0000000000..9218384640
--- /dev/null
+++ b/core-java-modules/core-java-collections-3/README.md
@@ -0,0 +1,11 @@
+=========
+
+## Core Java Collections Cookbooks and Examples
+
+### Relevant Articles:
+- [Time Comparison of Arrays.sort(Object[]) and Arrays.sort(int[])](https://www.baeldung.com/arrays-sortobject-vs-sortint)
+- [Java ArrayList vs Vector](https://www.baeldung.com/java-arraylist-vs-vector)
+- [Differences Between HashMap and Hashtable](https://www.baeldung.com/hashmap-hashtable-differences)
+- [Differences Between Collection.clear() and Collection.removeAll()](https://www.baeldung.com/java-collection-clear-vs-removeall)
+- [Performance of contains() in a HashSet vs ArrayList](https://www.baeldung.com/java-hashset-arraylist-contains-performance)
+- [Fail-Safe Iterator vs Fail-Fast Iterator](https://www.baeldung.com/java-fail-safe-vs-fail-fast-iterator)
diff --git a/core-java-modules/core-java-collections-3/pom.xml b/core-java-modules/core-java-collections-3/pom.xml
new file mode 100644
index 0000000000..84c7865e68
--- /dev/null
+++ b/core-java-modules/core-java-collections-3/pom.xml
@@ -0,0 +1,34 @@
+
+ 4.0.0
+ core-java-collections-3
+ 0.1.0-SNAPSHOT
+ core-java-collections-3
+ jar
+
+
+ com.baeldung
+ parent-java
+ 0.0.1-SNAPSHOT
+ ../../parent-java
+
+
+
+
+ org.openjdk.jmh
+ jmh-core
+ ${openjdk.jmh.version}
+
+
+ org.assertj
+ assertj-core
+ ${assertj.version}
+ test
+
+
+
+
+ 1.19
+ 3.11.1
+
+
diff --git a/core-java-modules/core-java-collections/src/main/java/com/baeldung/performance/ArrayListBenchmark.java b/core-java-modules/core-java-collections-3/src/main/java/com/baeldung/collections/arraylistvsvector/ArrayListBenchmark.java
similarity index 98%
rename from core-java-modules/core-java-collections/src/main/java/com/baeldung/performance/ArrayListBenchmark.java
rename to core-java-modules/core-java-collections-3/src/main/java/com/baeldung/collections/arraylistvsvector/ArrayListBenchmark.java
index 331ae8d908..7fcadf019c 100644
--- a/core-java-modules/core-java-collections/src/main/java/com/baeldung/performance/ArrayListBenchmark.java
+++ b/core-java-modules/core-java-collections-3/src/main/java/com/baeldung/collections/arraylistvsvector/ArrayListBenchmark.java
@@ -1,4 +1,4 @@
-package com.baeldung.performance;
+package com.baeldung.collections.arraylistvsvector;
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.runner.Runner;
diff --git a/core-java-modules/core-java-collections-3/src/main/java/com/baeldung/collections/arraylistvsvector/Employee.java b/core-java-modules/core-java-collections-3/src/main/java/com/baeldung/collections/arraylistvsvector/Employee.java
new file mode 100644
index 0000000000..02f25a7558
--- /dev/null
+++ b/core-java-modules/core-java-collections-3/src/main/java/com/baeldung/collections/arraylistvsvector/Employee.java
@@ -0,0 +1,55 @@
+package com.baeldung.collections.arraylistvsvector;
+
+public class Employee {
+
+ private Long id;
+ private String name;
+
+ public Employee(Long id, String name) {
+ this.name = name;
+ this.id = id;
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ Employee employee = (Employee) o;
+
+ if (!id.equals(employee.id)) return false;
+ return name.equals(employee.name);
+
+ }
+
+ @Override
+ public int hashCode() {
+ int result = id.hashCode();
+ result = 31 * result + name.hashCode();
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "Employee{" +
+ "id=" + id +
+ ", name='" + name + '\'' +
+ '}';
+ }
+}
diff --git a/core-java-modules/core-java-collections/src/main/java/com/baeldung/java/list/VectorExample.java b/core-java-modules/core-java-collections-3/src/main/java/com/baeldung/collections/arraylistvsvector/VectorExample.java
similarity index 92%
rename from core-java-modules/core-java-collections/src/main/java/com/baeldung/java/list/VectorExample.java
rename to core-java-modules/core-java-collections-3/src/main/java/com/baeldung/collections/arraylistvsvector/VectorExample.java
index 7debc07911..e82e47cdbb 100644
--- a/core-java-modules/core-java-collections/src/main/java/com/baeldung/java/list/VectorExample.java
+++ b/core-java-modules/core-java-collections-3/src/main/java/com/baeldung/collections/arraylistvsvector/VectorExample.java
@@ -1,4 +1,4 @@
-package com.baeldung.java.list;
+package com.baeldung.collections.arraylistvsvector;
import java.util.Enumeration;
import java.util.Iterator;
diff --git a/core-java-modules/core-java-collections/src/main/java/com/baeldung/performance/CollectionsBenchmark.java b/core-java-modules/core-java-collections-3/src/main/java/com/baeldung/collections/containsperformance/CollectionsBenchmark.java
similarity index 96%
rename from core-java-modules/core-java-collections/src/main/java/com/baeldung/performance/CollectionsBenchmark.java
rename to core-java-modules/core-java-collections-3/src/main/java/com/baeldung/collections/containsperformance/CollectionsBenchmark.java
index 921e1608ea..76edd10e92 100644
--- a/core-java-modules/core-java-collections/src/main/java/com/baeldung/performance/CollectionsBenchmark.java
+++ b/core-java-modules/core-java-collections-3/src/main/java/com/baeldung/collections/containsperformance/CollectionsBenchmark.java
@@ -1,4 +1,4 @@
-package com.baeldung.performance;
+package com.baeldung.collections.containsperformance;
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.runner.Runner;
diff --git a/core-java-modules/core-java-collections-3/src/main/java/com/baeldung/collections/containsperformance/Employee.java b/core-java-modules/core-java-collections-3/src/main/java/com/baeldung/collections/containsperformance/Employee.java
new file mode 100644
index 0000000000..6c60f8772c
--- /dev/null
+++ b/core-java-modules/core-java-collections-3/src/main/java/com/baeldung/collections/containsperformance/Employee.java
@@ -0,0 +1,55 @@
+package com.baeldung.collections.containsperformance;
+
+public class Employee {
+
+ private Long id;
+ private String name;
+
+ public Employee(Long id, String name) {
+ this.name = name;
+ this.id = id;
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ Employee employee = (Employee) o;
+
+ if (!id.equals(employee.id)) return false;
+ return name.equals(employee.name);
+
+ }
+
+ @Override
+ public int hashCode() {
+ int result = id.hashCode();
+ result = 31 * result + name.hashCode();
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "Employee{" +
+ "id=" + id +
+ ", name='" + name + '\'' +
+ '}';
+ }
+}
diff --git a/core-java-modules/core-java-collections-3/src/main/java/com/baeldung/collections/iterators/Iterators.java b/core-java-modules/core-java-collections-3/src/main/java/com/baeldung/collections/iterators/Iterators.java
new file mode 100644
index 0000000000..23e6bbda77
--- /dev/null
+++ b/core-java-modules/core-java-collections-3/src/main/java/com/baeldung/collections/iterators/Iterators.java
@@ -0,0 +1,80 @@
+package com.baeldung.collections.iterators;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Source code https://github.com/eugenp/tutorials
+ *
+ * @author Santosh Thakur
+ */
+
+public class Iterators {
+
+ public static int failFast1() {
+ ArrayList numbers = new ArrayList<>();
+
+ numbers.add(10);
+ numbers.add(20);
+ numbers.add(30);
+ numbers.add(40);
+
+ Iterator iterator = numbers.iterator();
+ while (iterator.hasNext()) {
+ Integer number = iterator.next();
+ numbers.add(50);
+ }
+
+ return numbers.size();
+ }
+
+ public static int failFast2() {
+ ArrayList numbers = new ArrayList<>();
+ numbers.add(10);
+ numbers.add(20);
+ numbers.add(30);
+ numbers.add(40);
+
+ Iterator iterator = numbers.iterator();
+ while (iterator.hasNext()) {
+ if (iterator.next() == 30) {
+ // will not throw Exception
+ iterator.remove();
+ }
+ }
+
+ System.out.println("using iterator's remove method = " + numbers);
+
+ iterator = numbers.iterator();
+ while (iterator.hasNext()) {
+ if (iterator.next() == 40) {
+ // will throw Exception on
+ // next call of next() method
+ numbers.remove(2);
+ }
+ }
+
+ return numbers.size();
+ }
+
+ public static int failSafe1() {
+ ConcurrentHashMap map = new ConcurrentHashMap<>();
+
+ map.put("First", 10);
+ map.put("Second", 20);
+ map.put("Third", 30);
+ map.put("Fourth", 40);
+
+ Iterator iterator = map.keySet()
+ .iterator();
+
+ while (iterator.hasNext()) {
+ String key = iterator.next();
+ map.put("Fifth", 50);
+ }
+
+ return map.size();
+ }
+
+}
diff --git a/core-java-modules/core-java-collections/src/main/java/com/baeldung/performance/ArraySortBenchmark.java b/core-java-modules/core-java-collections-3/src/main/java/com/baeldung/collections/sortingcomparison/ArraySortBenchmark.java
similarity index 97%
rename from core-java-modules/core-java-collections/src/main/java/com/baeldung/performance/ArraySortBenchmark.java
rename to core-java-modules/core-java-collections-3/src/main/java/com/baeldung/collections/sortingcomparison/ArraySortBenchmark.java
index b93f8e9cc2..1cd56aa29d 100644
--- a/core-java-modules/core-java-collections/src/main/java/com/baeldung/performance/ArraySortBenchmark.java
+++ b/core-java-modules/core-java-collections-3/src/main/java/com/baeldung/collections/sortingcomparison/ArraySortBenchmark.java
@@ -1,4 +1,4 @@
-package com.baeldung.performance;
+package com.baeldung.collections.sortingcomparison;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;
diff --git a/core-java-modules/core-java-collections/src/main/java/com/baeldung/java/sort/CollectionsSortCompare.java b/core-java-modules/core-java-collections-3/src/main/java/com/baeldung/collections/sortingcomparison/CollectionsSortCompare.java
similarity index 94%
rename from core-java-modules/core-java-collections/src/main/java/com/baeldung/java/sort/CollectionsSortCompare.java
rename to core-java-modules/core-java-collections-3/src/main/java/com/baeldung/collections/sortingcomparison/CollectionsSortCompare.java
index 1eff522877..abe7a12a00 100644
--- a/core-java-modules/core-java-collections/src/main/java/com/baeldung/java/sort/CollectionsSortCompare.java
+++ b/core-java-modules/core-java-collections-3/src/main/java/com/baeldung/collections/sortingcomparison/CollectionsSortCompare.java
@@ -1,4 +1,4 @@
-package com.baeldung.java.sort;
+package com.baeldung.collections.sortingcomparison;
import java.util.ArrayList;
import java.util.Arrays;
diff --git a/core-java-modules/core-java-collections-list/src/test/java/com/baeldung/collection/ClearVsRemoveAllUnitTest.java b/core-java-modules/core-java-collections-3/src/test/java/com/baeldung/collections/clearvsremoveall/ClearVsRemoveAllUnitTest.java
similarity index 95%
rename from core-java-modules/core-java-collections-list/src/test/java/com/baeldung/collection/ClearVsRemoveAllUnitTest.java
rename to core-java-modules/core-java-collections-3/src/test/java/com/baeldung/collections/clearvsremoveall/ClearVsRemoveAllUnitTest.java
index 8b0a7ef0db..9cd9c6aa50 100644
--- a/core-java-modules/core-java-collections-list/src/test/java/com/baeldung/collection/ClearVsRemoveAllUnitTest.java
+++ b/core-java-modules/core-java-collections-3/src/test/java/com/baeldung/collections/clearvsremoveall/ClearVsRemoveAllUnitTest.java
@@ -1,4 +1,4 @@
-package com.baeldung.collection;
+package com.baeldung.collections.clearvsremoveall;
import org.junit.jupiter.api.Test;
diff --git a/core-java-modules/core-java-collections/src/test/java/com/baeldung/hashmapvshashtable/HashmapVsHashtableDifferenceUnitTest.java b/core-java-modules/core-java-collections-3/src/test/java/com/baeldung/collections/hashmapvshashtable/HashmapVsHashtableDifferenceUnitTest.java
similarity index 98%
rename from core-java-modules/core-java-collections/src/test/java/com/baeldung/hashmapvshashtable/HashmapVsHashtableDifferenceUnitTest.java
rename to core-java-modules/core-java-collections-3/src/test/java/com/baeldung/collections/hashmapvshashtable/HashmapVsHashtableDifferenceUnitTest.java
index 5218332d60..b00a7fd953 100644
--- a/core-java-modules/core-java-collections/src/test/java/com/baeldung/hashmapvshashtable/HashmapVsHashtableDifferenceUnitTest.java
+++ b/core-java-modules/core-java-collections-3/src/test/java/com/baeldung/collections/hashmapvshashtable/HashmapVsHashtableDifferenceUnitTest.java
@@ -1,4 +1,4 @@
-package com.baeldung.hashmapvshashtable;
+package com.baeldung.collections.hashmapvshashtable;
import static org.junit.Assert.assertEquals;
diff --git a/core-java-modules/core-java-collections/src/test/java/com/baeldung/iterators/IteratorsUnitTest.java b/core-java-modules/core-java-collections-3/src/test/java/com/baeldung/collections/iterators/IteratorsUnitTest.java
similarity index 77%
rename from core-java-modules/core-java-collections/src/test/java/com/baeldung/iterators/IteratorsUnitTest.java
rename to core-java-modules/core-java-collections-3/src/test/java/com/baeldung/collections/iterators/IteratorsUnitTest.java
index 36e1f4a83c..95cf590857 100644
--- a/core-java-modules/core-java-collections/src/test/java/com/baeldung/iterators/IteratorsUnitTest.java
+++ b/core-java-modules/core-java-collections-3/src/test/java/com/baeldung/collections/iterators/IteratorsUnitTest.java
@@ -1,8 +1,8 @@
-package com.baeldung.iterators;
+package com.baeldung.collections.iterators;
-import static com.baeldung.iterators.Iterators.failFast1;
-import static com.baeldung.iterators.Iterators.failFast2;
-import static com.baeldung.iterators.Iterators.failSafe1;
+import static com.baeldung.collections.iterators.Iterators.failFast1;
+import static com.baeldung.collections.iterators.Iterators.failFast2;
+import static com.baeldung.collections.iterators.Iterators.failSafe1;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
diff --git a/core-java-modules/core-java-collections-array-list/README.md b/core-java-modules/core-java-collections-array-list/README.md
index 3d1cdd5085..302ea82130 100644
--- a/core-java-modules/core-java-collections-array-list/README.md
+++ b/core-java-modules/core-java-collections-array-list/README.md
@@ -1,10 +1,12 @@
-=========
+## Core Java Collections ArrayList
-## Core Java Collections Array List Cookbooks and Examples
+This module contains articles about the Java ArrayList collection
### Relevant Articles:
- [Immutable ArrayList in Java](http://www.baeldung.com/java-immutable-list)
- [Guide to the Java ArrayList](http://www.baeldung.com/java-arraylist)
- [Add Multiple Items to an Java ArrayList](http://www.baeldung.com/java-add-items-array-list)
- [ClassCastException: Arrays$ArrayList cannot be cast to ArrayList](https://www.baeldung.com/java-classcastexception-arrays-arraylist)
-- [Multi Dimensional ArrayList in Java](https://www.baeldung.com/java-multi-dimensional-arraylist)
\ No newline at end of file
+- [Multi Dimensional ArrayList in Java](https://www.baeldung.com/java-multi-dimensional-arraylist)
+- [Removing an Element From an ArrayList](https://www.baeldung.com/java-arraylist-remove-element)
+
diff --git a/core-java-modules/core-java-collections-array-list/pom.xml b/core-java-modules/core-java-collections-array-list/pom.xml
index 95a5f3ea36..249103152a 100644
--- a/core-java-modules/core-java-collections-array-list/pom.xml
+++ b/core-java-modules/core-java-collections-array-list/pom.xml
@@ -19,28 +19,16 @@