Merge branch 'eugenp:master' into JAVA-26380

This commit is contained in:
anuragkumawat
2023-11-03 18:39:01 +05:30
committed by GitHub
224 changed files with 3737 additions and 222 deletions
@@ -0,0 +1,129 @@
package com.baeldung.algorithms.connect4;
import java.util.ArrayList;
import java.util.List;
public class GameBoard {
private final List<List<Piece>> columns;
private final int rows;
public GameBoard(int columns, int rows) {
this.rows = rows;
this.columns = new ArrayList<>();
for (int i = 0; i < columns; ++i) {
this.columns.add(new ArrayList<>());
}
}
public int getRows() {
return rows;
}
public int getColumns() {
return columns.size();
}
public Piece getCell(int x, int y) {
assert(x >= 0 && x < getColumns());
assert(y >= 0 && y < getRows());
List<Piece> column = columns.get(x);
if (column.size() > y) {
return column.get(y);
} else {
return null;
}
}
public boolean move(int x, Piece player) {
assert(x >= 0 && x < getColumns());
List<Piece> column = columns.get(x);
if (column.size() >= this.rows) {
throw new IllegalArgumentException("That column is full");
}
column.add(player);
return checkWin(x, column.size() - 1, player);
}
private boolean checkWin(int x, int y, Piece player) {
// Vertical line
if (checkLine(x, y, 0, -1, player)) {
return true;
}
for (int offset = 0; offset < 4; ++offset) {
// Horizontal line
if (checkLine(x - 3 + offset, y, 1, 0, player)) {
return true;
}
// Leading diagonal
if (checkLine(x - 3 + offset, y + 3 - offset, 1, -1, player)) {
return true;
}
// Trailing diagonal
if (checkLine(x - 3 + offset, y - 3 + offset, 1, 1, player)) {
return true;
}
}
return false;
}
private boolean checkLine(int x1, int y1, int xDiff, int yDiff, Piece player) {
for (int i = 0; i < 4; ++i) {
int x = x1 + (xDiff * i);
int y = y1 + (yDiff * i);
if (x < 0 || x > columns.size() - 1) {
return false;
}
if (y < 0 || y > rows - 1) {
return false;
}
if (player != getCell(x, y)) {
return false;
}
}
return true;
}
@Override
public String toString() {
StringBuilder result = new StringBuilder();
for (int y = getRows() - 1; y >= 0; --y) {
for (int x = 0; x < getColumns(); ++x) {
Piece piece = getCell(x, y);
result.append("|");
if (piece == null) {
result.append(" ");
} else if (piece == Piece.PLAYER_1) {
result.append("X");
} else if (piece == Piece.PLAYER_2) {
result.append("O");
}
}
result.append("|\n");
for (int i = 0; i < getColumns(); ++i) {
result.append("+-");
}
result.append("+\n");
}
return result.toString();
}
}
@@ -0,0 +1,40 @@
package com.baeldung.algorithms.connect4;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class GameUnitTest {
@Test
public void blankGame() {
GameBoard gameBoard = new GameBoard(8, 6);
System.out.println(gameBoard);
}
@Test
public void playedGame() {
GameBoard gameBoard = new GameBoard(8, 6);
assertFalse(gameBoard.move(3, Piece.PLAYER_1));
assertFalse(gameBoard.move(2, Piece.PLAYER_2));
assertFalse(gameBoard.move(4, Piece.PLAYER_1));
assertFalse(gameBoard.move(3, Piece.PLAYER_2));
assertFalse(gameBoard.move(5, Piece.PLAYER_1));
assertFalse(gameBoard.move(6, Piece.PLAYER_2));
assertFalse(gameBoard.move(5, Piece.PLAYER_1));
assertFalse(gameBoard.move(4, Piece.PLAYER_2));
assertFalse(gameBoard.move(5, Piece.PLAYER_1));
assertFalse(gameBoard.move(5, Piece.PLAYER_2));
assertFalse(gameBoard.move(6, Piece.PLAYER_1));
assertTrue(gameBoard.move(4, Piece.PLAYER_2));
System.out.println(gameBoard);
}
}
@@ -0,0 +1,6 @@
package com.baeldung.algorithms.connect4;
public enum Piece {
PLAYER_1,
PLAYER_2
}
@@ -0,0 +1,113 @@
package com.baeldung.rounddate;
import java.time.DayOfWeek;
import java.time.temporal.ChronoField;
import java.time.temporal.TemporalAdjusters;
import java.util.Calendar;
import java.util.Date;
import java.time.LocalDateTime;
import java.time.ZonedDateTime;
public class RoundDate {
public static Date getDate(int year, int month, int day, int hour, int minute) {
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.YEAR, year);
calendar.set(Calendar.MONTH, month);
calendar.set(Calendar.DAY_OF_MONTH, day);
calendar.set(Calendar.HOUR_OF_DAY, hour);
calendar.set(Calendar.MINUTE, minute);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);
return calendar.getTime();
}
public static Date roundToDay(Date date) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.set(Calendar.HOUR_OF_DAY, 0);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);
return calendar.getTime();
}
public static Date roundToNearestUnit(Date date, int unit) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
switch (unit) {
case Calendar.HOUR:
int minute = calendar.get(Calendar.MINUTE);
if (minute >= 0 && minute < 15) {
calendar.set(Calendar.MINUTE, 0);
} else if (minute >= 15 && minute < 45) {
calendar.set(Calendar.MINUTE, 30);
} else {
calendar.set(Calendar.MINUTE, 0);
calendar.add(Calendar.HOUR_OF_DAY, 1);
}
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);
break;
case Calendar.DAY_OF_MONTH:
int hour = calendar.get(Calendar.HOUR_OF_DAY);
if (hour >= 12) {
calendar.add(Calendar.DAY_OF_MONTH, 1);
}
calendar.set(Calendar.HOUR_OF_DAY, 0);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);
break;
case Calendar.MONTH:
int day = calendar.get(Calendar.DAY_OF_MONTH);
if (day >= 15) {
calendar.add(Calendar.MONTH, 1);
}
calendar.set(Calendar.DAY_OF_MONTH, 1);
calendar.set(Calendar.HOUR_OF_DAY, 0);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);
break;
}
return calendar.getTime();
}
public static LocalDateTime roundToStartOfMonthUsingLocalDateTime(LocalDateTime dateTime) {
return dateTime.withDayOfMonth(1).withHour(0).withMinute(0).withSecond(0).withNano(0);
}
public static LocalDateTime roundToEndOfWeekUsingLocalDateTime(LocalDateTime dateTime) {
return dateTime.with(TemporalAdjusters.next(DayOfWeek.SATURDAY))
.withHour(23)
.withMinute(59)
.withSecond(59)
.withNano(999);
}
public static ZonedDateTime roundToStartOfMonthUsingZonedDateTime(ZonedDateTime dateTime) {
return dateTime.withDayOfMonth(1)
.withHour(0)
.withMinute(0)
.withSecond(0)
.with(ChronoField.MILLI_OF_SECOND, 0)
.with(ChronoField.MICRO_OF_SECOND, 0)
.with(ChronoField.NANO_OF_SECOND, 0);
}
public static ZonedDateTime roundToEndOfWeekUsingZonedDateTime(ZonedDateTime dateTime) {
return dateTime.with(TemporalAdjusters.next(DayOfWeek.SATURDAY))
.withHour(23)
.withMinute(59)
.withSecond(59)
.with(ChronoField.MILLI_OF_SECOND, 999)
.with(ChronoField.MICRO_OF_SECOND, 999)
.with(ChronoField.NANO_OF_SECOND, 999);
}
}
@@ -0,0 +1,63 @@
package com.baeldung.rounddate;
import org.junit.Test;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Calendar;
import java.util.Date;
import static org.junit.Assert.assertEquals;
public class DateRoundingUnitTest {
@Test
public void givenDate_whenRoundToDay_thenBeginningOfDay() {
Date date = RoundDate.getDate(2023, Calendar.JANUARY, 27, 14, 30);
Date result = RoundDate.roundToDay(date);
assertEquals(RoundDate.getDate(2023, Calendar.JANUARY, 27, 0, 0), result);
}
@Test
public void givenDate_whenRoundToNearestUnit_thenNearestUnit() {
Date date = RoundDate.getDate(2023, Calendar.JANUARY, 27, 14, 12);
Date result = RoundDate.roundToNearestUnit(date, Calendar.DAY_OF_MONTH);
Date expected = RoundDate.getDate(2023, Calendar.JANUARY, 28, 0, 0);
assertEquals(expected, result);
}
@Test
public void givenLocalDateTime_whenRoundToStartOfMonth_thenStartOfMonth() {
LocalDateTime dateTime = LocalDateTime.of(2023, 1, 27, 14, 12);
LocalDateTime result = RoundDate.roundToStartOfMonthUsingLocalDateTime(dateTime);
LocalDateTime expected = LocalDateTime.of(2023, 1, 1, 0, 0, 0);
assertEquals(expected, result);
}
@Test
public void givenZonedDateTime_whenRoundToStartOfMonth_thenStartOfMonth() {
ZonedDateTime dateTime = ZonedDateTime.of(2023, 1, 27, 14, 12, 0, 0, ZoneId.systemDefault());
ZonedDateTime result = RoundDate.roundToStartOfMonthUsingZonedDateTime(dateTime);
ZonedDateTime expected = ZonedDateTime.of(2023, 1, 1, 0, 0, 0, 0, ZoneId.systemDefault());
assertEquals(expected, result);
}
@Test
public void givenLocalDateTime_whenRoundToEndOfWeek_thenEndOfWeek() {
LocalDateTime dateTime = LocalDateTime.of(2023, 1, 27, 14, 12);
LocalDateTime result = RoundDate.roundToEndOfWeekUsingLocalDateTime(dateTime);
LocalDateTime expected = LocalDateTime.of(2023, 1, 28, 23, 59, 59, 999);
assertEquals(expected, result);
}
@Test
public void givenZonedDateTime_whenRoundToEndOfWeek_thenEndOfWeek() {
ZonedDateTime dateTime = ZonedDateTime.of(2023, 1, 27, 14, 12, 0, 0, ZoneId.systemDefault());
ZonedDateTime result = RoundDate.roundToEndOfWeekUsingZonedDateTime(dateTime);
ZonedDateTime expected = ZonedDateTime.of(2023, 1, 28, 23, 59, 59, 999, ZoneId.systemDefault());
assertEquals(expected, result);
}
}
@@ -0,0 +1,48 @@
package com.baeldung.spliterator;
import java.util.List;
import java.util.Spliterator;
import java.util.function.Consumer;
public class CustomSpliterator implements Spliterator<Integer> {
private final List<Integer> elements;
private int currentIndex;
public CustomSpliterator(List<Integer> elements) {
this.elements = elements;
this.currentIndex = 0;
}
@Override
public boolean tryAdvance(Consumer<? super Integer> action) {
if (currentIndex < elements.size()) {
action.accept(elements.get(currentIndex));
currentIndex++;
return true;
}
return false;
}
@Override
public Spliterator<Integer> trySplit() {
int currentSize = elements.size() - currentIndex;
if (currentSize < 2) {
return null;
}
int splitIndex = currentIndex + currentSize / 2;
CustomSpliterator newSpliterator = new CustomSpliterator(elements.subList(currentIndex, splitIndex));
currentIndex = splitIndex;
return newSpliterator;
}
@Override
public long estimateSize() {
return elements.size() - currentIndex;
}
@Override
public int characteristics() {
return ORDERED | SIZED | SUBSIZED | NONNULL;
}
}
@@ -1,12 +0,0 @@
package com.baeldung.spliterator;
import java.util.stream.Stream;
public class Executor {
public static int countAutors(Stream<Author> stream) {
RelatedAuthorCounter wordCounter = stream.reduce(new RelatedAuthorCounter(0, true),
RelatedAuthorCounter::accumulate, RelatedAuthorCounter::combine);
return wordCounter.getCounter();
}
}
@@ -1,27 +0,0 @@
package com.baeldung.spliterator;
public class RelatedAuthorCounter {
private final int counter;
private final boolean isRelated;
public RelatedAuthorCounter(int counter, boolean isRelated) {
this.counter = counter;
this.isRelated = isRelated;
}
public RelatedAuthorCounter accumulate(Author author) {
if (author.getRelatedArticleId() == 0) {
return isRelated ? this : new RelatedAuthorCounter(counter, true);
} else {
return isRelated ? new RelatedAuthorCounter(counter + 1, false) : this;
}
}
public RelatedAuthorCounter combine(RelatedAuthorCounter RelatedAuthorCounter) {
return new RelatedAuthorCounter(counter + RelatedAuthorCounter.counter, RelatedAuthorCounter.isRelated);
}
public int getCounter() {
return counter;
}
}
@@ -1,49 +0,0 @@
package com.baeldung.spliterator;
import java.util.List;
import java.util.Spliterator;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
public class RelatedAuthorSpliterator implements Spliterator<Author> {
private final List<Author> list;
AtomicInteger current = new AtomicInteger();
public RelatedAuthorSpliterator(List<Author> list) {
this.list = list;
}
@Override
public boolean tryAdvance(Consumer<? super Author> action) {
action.accept(list.get(current.getAndIncrement()));
return current.get() < list.size();
}
@Override
public Spliterator<Author> trySplit() {
int currentSize = list.size() - current.get();
if (currentSize < 10) {
return null;
}
for (int splitPos = currentSize / 2 + current.intValue(); splitPos < list.size(); splitPos++) {
if (list.get(splitPos).getRelatedArticleId() == 0) {
Spliterator<Author> spliterator = new RelatedAuthorSpliterator(list.subList(current.get(), splitPos));
current.set(splitPos);
return spliterator;
}
}
return null;
}
@Override
public long estimateSize() {
return list.size() - current.get();
}
@Override
public int characteristics() {
return CONCURRENT;
}
}
@@ -6,10 +6,10 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Spliterator;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import org.junit.Before;
import org.junit.Test;
@@ -18,22 +18,50 @@ import lombok.extern.slf4j.Slf4j;
@Slf4j
public class ExecutorUnitTest {
Article article;
Stream<Author> stream;
Spliterator<Author> spliterator;
@Before
public void init() {
article = new Article(Arrays.asList(new Author("Ahmad", 0), new Author("Eugen", 0), new Author("Alice", 1), new Author("Alice", 1), new Author("Mike", 0), new Author("Alice", 1), new Author("Mike", 0), new Author("Alice", 1), new Author("Mike", 0),
new Author("Alice", 1), new Author("Mike", 0), new Author("Mike", 0), new Author("Alice", 1), new Author("Mike", 0), new Author("Alice", 1), new Author("Mike", 0), new Author("Alice", 1), new Author("Mike", 0), new Author("Alice", 1),
new Author("Mike", 0), new Author("Michał", 0), new Author("Loredana", 1)), 0);
spliterator = new RelatedAuthorSpliterator(article.getListOfAuthors());
}
@Test
public void givenAstreamOfAuthors_whenProcessedInParallelWithCustomSpliterator_coubtProducessRightOutput() {
Stream<Author> stream2 = StreamSupport.stream(spliterator, true);
assertThat(Executor.countAutors(stream2.parallel())).isEqualTo(9);
public void givenAStreamOfIntegers_whenProcessedSequentialCustomSpliterator_countProducesRightOutput() {
List<Integer> numbers = new ArrayList<>();
numbers.add(1);
numbers.add(2);
numbers.add(3);
numbers.add(4);
numbers.add(5);
CustomSpliterator customSpliterator = new CustomSpliterator(numbers);
AtomicInteger sum = new AtomicInteger();
customSpliterator.forEachRemaining(sum::addAndGet);
assertThat(sum.get()).isEqualTo(15);
}
@Test
public void givenAStreamOfIntegers_whenProcessedInParallelWithCustomSpliterator_countProducesRightOutput() {
List<Integer> numbers = new ArrayList<>();
numbers.add(1);
numbers.add(2);
numbers.add(3);
numbers.add(4);
numbers.add(5);
CustomSpliterator customSpliterator = new CustomSpliterator(numbers);
// Create a ForkJoinPool for parallel processing
ForkJoinPool forkJoinPool = ForkJoinPool.commonPool();
AtomicInteger sum = new AtomicInteger(0);
// Process elements in parallel using parallelStream
forkJoinPool.submit(() -> customSpliterator.forEachRemaining(sum::addAndGet)).join();
assertThat(sum.get()).isEqualTo(15);
}
@Test
@@ -43,9 +71,7 @@ public class ExecutorUnitTest {
.collect(Collectors.toList());
Spliterator<Article> spliterator = articles.spliterator();
while (spliterator.tryAdvance(article -> article.setName(article.getName()
.concat("- published by Baeldung"))))
;
while(spliterator.tryAdvance(article -> article.setName(article.getName().concat("- published by Baeldung"))));
articles.forEach(article -> assertThat(article.getName()).isEqualTo("Java- published by Baeldung"));
}
@@ -57,5 +57,9 @@
</plugin>
</plugins>
</build>
<properties>
<maven.compiler.release>11</maven.compiler.release>
</properties>
</project>
@@ -0,0 +1,29 @@
package com.baeldung.arraycompare;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.Arrays;
import org.junit.jupiter.api.Test;
class ArraysCompareUnitTest {
@Test
void givenSameContents_whenCompare_thenCorrect() {
String[] array1 = new String[] { "A", "B", "C" };
String[] array2 = new String[] { "A", "B", "C" };
assertThat(Arrays.compare(array1, array2)).isEqualTo(0);
}
@Test
void givenDifferentContents_whenCompare_thenDifferent() {
String[] array1 = new String[] { "A", "B", "C" };
String[] array2 = new String[] { "A", "C", "B", "D" };
assertThat(Arrays.compare(array1, array2)).isLessThan(0);
assertThat(Arrays.compare(array2, array1)).isGreaterThan(0);
assertThat(Arrays.compare(array1, null)).isGreaterThan(0);
}
}
@@ -3,3 +3,4 @@
This module contains articles about Java array fundamentals. They assume no previous background knowledge on working with arrays.
### Relevant Articles:
- [Arrays mismatch() Method in Java](https://www.baeldung.com/java-arrays-mismatch)
@@ -7,4 +7,5 @@
- [Creating Custom Iterator in Java](https://www.baeldung.com/java-creating-custom-iterator)
- [Difference Between Arrays.sort() and Collections.sort()](https://www.baeldung.com/java-arrays-collections-sort-methods)
- [Skipping the First Iteration in Java](https://www.baeldung.com/java-skip-first-iteration)
- [Remove Elements From a Queue Using Loop](https://www.baeldung.com/java-remove-elements-queue)
- More articles: [[<-- prev]](/core-java-modules/core-java-collections-4)
@@ -1,2 +1,3 @@
## Relevant Articles
- [Difference Between putIfAbsent() and computeIfAbsent() in Java’s Map](https://www.baeldung.com/java-map-putifabsent-computeifabsent)
- [How to Write Hashmap to CSV File](https://www.baeldung.com/java-write-hashmap-csv)
@@ -79,5 +79,18 @@
<version>1.5</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<argLine>
--add-opens java.base/java.util=ALL-UNNAMED
</argLine>
</configuration>
</plugin>
</plugins>
</build>
</project>
@@ -0,0 +1,85 @@
package com.baeldung.map.linkedhashmapfirstandlastentry;
import org.junit.jupiter.api.Test;
import java.lang.reflect.Field;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map.Entry;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
public class GetFirstAndLastEntryFromLinkedHashMapUnitTest {
private static final LinkedHashMap<String, String> THE_MAP = new LinkedHashMap<>();
static {
THE_MAP.put("key one", "a1 b1 c1");
THE_MAP.put("key two", "a2 b2 c2");
THE_MAP.put("key three", "a3 b3 c3");
THE_MAP.put("key four", "a4 b4 c4");
}
@Test
void whenIteratingEntrySet_thenGetExpectedResult() {
Entry<String, String> firstEntry = THE_MAP.entrySet().iterator().next();
assertEquals("key one", firstEntry.getKey());
assertEquals("a1 b1 c1", firstEntry.getValue());
Entry<String, String> lastEntry = null;
Iterator<Entry<String, String>> it = THE_MAP.entrySet().iterator();
while (it.hasNext()) {
lastEntry = it.next();
}
assertNotNull(lastEntry);
assertEquals("key four", lastEntry.getKey());
assertEquals("a4 b4 c4", lastEntry.getValue());
}
@Test
void whenConvertingEntriesToArray_thenGetExpectedResult() {
Entry<String, String>[] theArray = new Entry[THE_MAP.size()];
THE_MAP.entrySet().toArray(theArray);
Entry<String, String> firstEntry = theArray[0];
assertEquals("key one", firstEntry.getKey());
assertEquals("a1 b1 c1", firstEntry.getValue());
Entry<String, String> lastEntry = theArray[THE_MAP.size() - 1];
assertEquals("key four", lastEntry.getKey());
assertEquals("a4 b4 c4", lastEntry.getValue());
}
@Test
void whenUsingStreamAPI_thenGetExpectedResult() {
Entry<String, String> firstEntry = THE_MAP.entrySet().stream().findFirst().get();
assertEquals("key one", firstEntry.getKey());
assertEquals("a1 b1 c1", firstEntry.getValue());
Entry<String, String> lastEntry = THE_MAP.entrySet().stream().skip(THE_MAP.size() - 1).findFirst().get();
assertNotNull(lastEntry);
assertEquals("key four", lastEntry.getKey());
assertEquals("a4 b4 c4", lastEntry.getValue());
}
@Test
void whenUsingReflection_thenGetExpectedResult() throws NoSuchFieldException, IllegalAccessException {
Field head = THE_MAP.getClass().getDeclaredField("head");
head.setAccessible(true);
Entry<String, String> firstEntry = (Entry<String, String>) head.get(THE_MAP);
assertEquals("key one", firstEntry.getKey());
assertEquals("a1 b1 c1", firstEntry.getValue());
Field tail = THE_MAP.getClass().getDeclaredField("tail");
tail.setAccessible(true);
Entry<String, String> lastEntry = (Entry<String, String>) tail.get(THE_MAP);
assertEquals("key four", lastEntry.getKey());
assertEquals("a4 b4 c4", lastEntry.getValue());
}
}
@@ -10,4 +10,5 @@ This module contains articles about basic Java concurrency.
- [Returning a Value After Finishing Thread’s Job in Java](https://www.baeldung.com/java-return-value-after-thread-finish)
- [CompletableFuture and ThreadPool in Java](https://www.baeldung.com/java-completablefuture-threadpool)
- [CompletableFuture allOf().join() vs. CompletableFuture.join()](https://www.baeldung.com/java-completablefuture-allof-join)
- [Retry Logic with CompletableFuture](https://www.baeldung.com/java-completablefuture-retry-logic)
- [[<-- Prev]](../core-java-concurrency-basic-2)
@@ -0,0 +1,100 @@
package com.baeldung.concurrent.completablefuturelist;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.stream.Collectors;
public class Application {
ScheduledExecutorService asyncOperationEmulation;
Application initialize() {
asyncOperationEmulation = Executors.newScheduledThreadPool(10);
return this;
}
CompletableFuture<String> asyncOperation(String operationId) {
CompletableFuture<String> cf = new CompletableFuture<>();
asyncOperationEmulation.submit(() -> {
// The following lines simulate an exception happening on the 567th operation
// if (operationId.endsWith("567")) {
// cf.completeExceptionally(new Exception("Error on operation " + operationId));
// return;
// }
try {
Thread.sleep(100);
cf.complete(operationId);
} catch (InterruptedException e) {
System.err.println("Thread interrupted error");
cf.completeExceptionally(e);
}
});
return cf;
}
void startNaive() {
List<CompletableFuture<String>> futures = new ArrayList<>();
for (int i = 1; i <= 1000; i++) {
String operationId = "Naive-Operation-" + i;
futures.add(asyncOperation(operationId));
}
CompletableFuture<List<String>> aggregate = CompletableFuture.completedFuture(new ArrayList<>());
for (CompletableFuture<String> future : futures) {
aggregate = aggregate.thenCompose(list -> {
try {
list.add(future.get());
return CompletableFuture.completedFuture(list);
} catch (Exception e) {
final CompletableFuture<List<String>> excFuture = new CompletableFuture<>();
excFuture.completeExceptionally(e);
return excFuture;
}
});
}
try {
final List<String> results = aggregate.join();
System.out.println("Printing first 10 results");
for (int i = 0; i < 10; i++) {
System.out.println("Finished " + results.get(i));
}
} finally {
close();
}
}
void start() {
List<CompletableFuture<String>> futures = new ArrayList<>();
for (int i = 1; i <= 1000; i++) {
String operationId = "Operation-" + i;
futures.add(asyncOperation(operationId));
}
CompletableFuture<?>[] futuresArray = futures.toArray(new CompletableFuture<?>[0]);
CompletableFuture<List<String>> listFuture = CompletableFuture.allOf(futuresArray).thenApply(v -> futures.stream().map(CompletableFuture::join).collect(Collectors.toList()));
try {
final List<String> results = listFuture.join();
System.out.println("Printing first 10 results");
for (int i = 0; i < 10; i++) {
System.out.println("Finished " + results.get(i));
}
} finally {
close();
}
}
void close() {
asyncOperationEmulation.shutdownNow();
}
public static void main(String[] args) {
new Application().initialize()
// Switch between .startNaive() and .start() to test both implementations
// .startNaive();
.start();
}
}
@@ -12,6 +12,7 @@ This module contains articles about Java Concurrency that are also part of an Eb
- [Guide to the Volatile Keyword in Java](https://www.baeldung.com/java-volatile)
- [A Guide to the Java ExecutorService](https://www.baeldung.com/java-executor-service-tutorial)
- [Guide To CompletableFuture](https://www.baeldung.com/java-completablefuture)
- [How To Manage Timeout for CompletableFuture](https://www.baeldung.com/java-completablefuture-timeout)
### NOTE:
@@ -12,6 +12,15 @@
<version>0.0.1-SNAPSHOT</version>
</parent>
<dependencies>
<dependency>
<groupId>org.wiremock</groupId>
<artifactId>wiremock</artifactId>
<version>3.1.0</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<finalName>core-java-concurrency-simple</finalName>
<resources>
@@ -0,0 +1,182 @@
package com.baeldung.concurrent.completablefuture;
import com.github.tomakehurst.wiremock.WireMockServer;
import com.github.tomakehurst.wiremock.client.WireMock;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.concurrent.*;
import static com.github.tomakehurst.wiremock.client.WireMock.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class CompletableFutureTimeoutUnitTest {
private WireMockServer wireMockServer;
private ScheduledExecutorService executorService;
private static final int DEFAULT_TIMEOUT = 1000; //1 seconds
private static final String DEFAULT_PRODUCT = """
{
"products": [
{
"id": 2,
"title": "iPhone X",
"description": "SIM-Free, Model A19211 6.5-inch Super Retina HD display with OLED technology A12 Bionic chip with ...",
"price": 899,
"discountPercentage": 0.0,
"rating": 4.44,
"stock": 34,
"brand": "Apple",
"category": "smartphones"
},
{
"id": 3,
"title": "Samsung Universe 9",
"description": "Samsung's new variant which goes beyond Galaxy to the Universe",
"price": 1249,
"discountPercentage": 0.0,
"rating": 4.09,
"stock": 36,
"brand": "Samsung",
"category": "smartphones"
}
],
"total": 2
}""";
private static final String PRODUCT_OFFERS = """
{
"products": [
{
"id": 1,
"title": "iPhone 9",
"description": "An apple mobile which is nothing like apple",
"price": 549,
"discountPercentage": 12.96,
"rating": 4.69,
"stock": 94,
"brand": "Apple",
"category": "smartphones"
},
{
"id": 2,
"title": "iPhone X",
"description": "SIM-Free, Model A19211 6.5-inch Super Retina HD display with OLED technology A12 Bionic chip with ...",
"price": 899,
"discountPercentage": 17.94,
"rating": 4.44,
"stock": 34,
"brand": "Apple",
"category": "smartphones"
},
{
"id": 3,
"title": "Samsung Universe 9",
"description": "Samsung's new variant which goes beyond Galaxy to the Universe",
"price": 1249,
"discountPercentage": 15.46,
"rating": 4.09,
"stock": 36,
"brand": "Samsung",
"category": "smartphones"
},
{
"id": 4,
"title": "OPPOF19",
"description": "OPPO F19 is officially announced on April 2021.",
"price": 280,
"discountPercentage": 17.91,
"rating": 4.3,
"stock": 123,
"brand": "OPPO",
"category": "smartphones"
},
{
"id": 5,
"title": "Huawei P30",
"description": "Huawei’s re-badged P30 Pro New Edition was officially unveiled yesterday in Germany and now the device has made its way to the UK.",
"price": 499,
"discountPercentage": 10.58,
"rating": 4.09,
"stock": 32,
"brand": "Huawei",
"category": "smartphones"
}
],
"total": 5
}""";
@BeforeAll
void setUp() {
wireMockServer = new WireMockServer(8080);
wireMockServer.start();
WireMock.configureFor("localhost", 8080);
stubFor(get(urlEqualTo("/api/dummy"))
.willReturn(aResponse()
.withFixedDelay(5000) // must be > DEFAULT_TIMEOUT for a timeout to occur.
.withBody(PRODUCT_OFFERS)));
executorService = Executors.newScheduledThreadPool(1);
}
@AfterAll
void tearDown() {
executorService.shutdown();
wireMockServer.stop();
}
private CompletableFuture<String> fetchProductData() {
return CompletableFuture.supplyAsync(() -> {
try {
URL url = new URL("http://localhost:8080/api/dummy");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
try (BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()))) {
String inputLine;
StringBuffer response = new StringBuffer();
while ((inputLine = in.readLine()) != null) {
response.append(inputLine);
}
return response.toString();
} finally {
connection.disconnect();
}
} catch (IOException e) {
return "";
}
});
}
@Test
void whenorTimeout_thenGetThrow() {
CompletableFuture<String> productDataFuture = fetchProductData();
productDataFuture.orTimeout(DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS);
assertThrows(ExecutionException.class, productDataFuture::get);
}
@Test
void whencompleteOnTimeout_thenReturnValue() throws ExecutionException, InterruptedException {
CompletableFuture<String> productDataFuture = fetchProductData();
productDataFuture.completeOnTimeout(DEFAULT_PRODUCT, DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS);
assertEquals(DEFAULT_PRODUCT, productDataFuture.get());
}
@Test
void whencompleteExceptionally_thenGetThrow() {
CompletableFuture<String> productDataFuture = fetchProductData();
executorService.schedule(() -> productDataFuture
.completeExceptionally(new TimeoutException("Timeout occurred")), DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS);
assertThrows(ExecutionException.class, productDataFuture::get);
}
}
@@ -0,0 +1,61 @@
package com.baeldung.firstdaymonth;
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.time.LocalDate;
import java.time.YearMonth;
import java.time.temporal.TemporalAdjusters;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import org.junit.jupiter.api.Test;
public class FirstDayOfMonthUnitTest {
@Test
void givenMonth_whenUsingCalendar_thenReturnFirstDay() {
Date currentDate = new GregorianCalendar(2023, Calendar.NOVEMBER, 23).getTime();
Date expectedDate = new GregorianCalendar(2023, Calendar.NOVEMBER, 1).getTime();
Calendar cal = Calendar.getInstance();
cal.setTime(currentDate);
cal.set(Calendar.DAY_OF_MONTH, 1);
assertEquals(expectedDate, cal.getTime());
}
@Test
void givenMonth_whenUsingLocalDate_thenReturnFirstDay() {
LocalDate currentDate = LocalDate.of(2023, 9, 6);
LocalDate expectedDate = LocalDate.of(2023, 9, 1);
assertEquals(expectedDate, currentDate.withDayOfMonth(1));
}
@Test
void givenMonth_whenUsingTemporalAdjusters_thenReturnFirstDay() {
LocalDate currentDate = LocalDate.of(2023, 7, 19);
LocalDate expectedDate = LocalDate.of(2023, 7, 1);
assertEquals(expectedDate, currentDate.with(TemporalAdjusters.firstDayOfMonth()));
}
@Test
void givenMonth_whenUsingYearMonth_thenReturnFirstDay() {
YearMonth currentDate = YearMonth.of(2023, 4);
LocalDate expectedDate = LocalDate.of(2023, 4, 1);
assertEquals(expectedDate, currentDate.atDay(1));
}
@Test
void givenMonth_whenUsingJodaTime_thenReturnFirstDay() {
org.joda.time.LocalDate currentDate = org.joda.time.LocalDate.parse("2023-5-10");
org.joda.time.LocalDate expectedDate = org.joda.time.LocalDate.parse("2023-5-1");
assertEquals(expectedDate, currentDate.dayOfMonth()
.withMinimumValue());
}
}
@@ -0,0 +1,23 @@
package com.baeldung.generictype;
/**
* @param <T> The type of the first value in the {@code Pair<T,S>}.
* @param <S> The type of the second value in the {@code Pair<T,S>}.
*/
public class Pair<T, S> {
public T first;
public S second;
/**
* Constructs a new Pair object with the specified values.
*
* @param a The first value.
* @param b The second value.
*/
public Pair(T a, S b) {
first = a;
second = b;
}
}
+16
View File
@@ -35,6 +35,21 @@
<artifactId>simplemagic</artifactId>
<version>${simplemagic.version}</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>${commons-io.version}</version>
</dependency>
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-core</artifactId>
<version>${jmh.version}</version>
</dependency>
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-generator-annprocess</artifactId>
<version>${jmh.version}</version>
</dependency>
</dependencies>
<build>
@@ -62,5 +77,6 @@
<jmime-magic.version>0.1.5</jmime-magic.version>
<jodd-util.version>6.2.1</jodd-util.version>
<simplemagic.version>1.17</simplemagic.version>
<jmh.version>1.37</jmh.version>
</properties>
</project>
@@ -0,0 +1,112 @@
package com.baeldung.zip;
import java.io.*;
import java.util.concurrent.TimeUnit;
import java.util.*;
import java.util.zip.*;
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.infra.Blackhole;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
@State(Scope.Benchmark)
@BenchmarkMode(Mode.AverageTime)
@Warmup(iterations = 1, time = 2, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 3, time = 1, timeUnit = TimeUnit.SECONDS)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@Fork(value = 1)
public class ZipBenchmark {
public static final int NUM_OF_FILES = 10;
public static final int DATA_SIZE = 204800;
@State(Scope.Thread)
public static class SourceState {
public File compressedFile;
@Setup(Level.Trial)
public void setup() throws IOException {
ZipSampleFileStore sampleFileStore = new ZipSampleFileStore(NUM_OF_FILES, DATA_SIZE);
compressedFile = sampleFileStore.getFile();
}
@TearDown(Level.Trial)
public void cleanup() {
if (compressedFile.exists()) {
compressedFile.delete();
}
}
}
@Benchmark
public static void readAllEntriesByZipFile(SourceState sourceState, Blackhole blackhole) throws IOException {
try (ZipFile zipFile = new ZipFile(sourceState.compressedFile)) {
Enumeration<? extends ZipEntry> zipEntries = zipFile.entries();
while (zipEntries.hasMoreElements()) {
ZipEntry zipEntry = zipEntries.nextElement();
try (InputStream inputStream = new BufferedInputStream(zipFile.getInputStream(zipEntry))) {
blackhole.consume(ZipSampleFileStore.getString(inputStream));
}
}
}
}
@Benchmark
public static void readAllEntriesByZipInputStream(SourceState sourceState, Blackhole blackhole) throws IOException {
try (
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(sourceState.compressedFile));
ZipInputStream zipInputStream = new ZipInputStream(bis)
) {
ZipEntry entry;
while ((entry = zipInputStream.getNextEntry()) != null) {
blackhole.consume(ZipSampleFileStore.getString(zipInputStream));
}
}
}
@Benchmark
public static void readLastEntryByZipFile(SourceState sourceState, Blackhole blackhole) throws IOException {
try (ZipFile zipFile = new ZipFile(sourceState.compressedFile)) {
ZipEntry zipEntry = zipFile.getEntry(getLastEntryName());
try (InputStream inputStream = new BufferedInputStream(zipFile.getInputStream(zipEntry))) {
blackhole.consume(ZipSampleFileStore.getString(inputStream));
}
}
}
@Benchmark
public static void readLastEntryByZipInputStream(SourceState sourceState, Blackhole blackhole) throws IOException {
try (
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(sourceState.compressedFile));
ZipInputStream zipInputStream = new ZipInputStream(bis)
) {
ZipEntry entry;
while ((entry = zipInputStream.getNextEntry()) != null) {
if (Objects.equals(entry.getName(), getLastEntryName())){
blackhole.consume(ZipSampleFileStore.getString(zipInputStream));
}
}
}
}
private static String getLastEntryName() {
return String.format(ZipSampleFileStore.ENTRY_NAME_PATTERN, NUM_OF_FILES);
}
public static void main(String[] args) throws Exception {
Options options = new OptionsBuilder()
.include(ZipBenchmark.class.getSimpleName()).threads(1)
.shouldFailOnError(true)
.shouldDoGC(true)
.jvmArgs("-server").build();
new Runner(options).run();
}
}
@@ -0,0 +1,70 @@
package com.baeldung.zip;
import java.io.*;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import org.apache.commons.io.IOUtils;
public class ZipSampleFileStore {
public static final String ENTRY_NAME_PATTERN = "str-data-%s.txt";
private static final Charset DEFAULT_ENCODING = StandardCharsets.UTF_8;
private final File file;
private final List<String> dataList;
public ZipSampleFileStore(int numOfFiles, int fileSize) throws IOException {
dataList = new ArrayList<>(numOfFiles);
file = File.createTempFile("zip-sample", "");
try (ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(file))) {
for (int idx=0; idx<=numOfFiles; idx++) {
byte[] data = createRandomStringInByte(fileSize);
dataList.add(new String(data, DEFAULT_ENCODING));
ZipEntry entry = new ZipEntry(String.format(ENTRY_NAME_PATTERN, idx));
zos.putNextEntry(entry);
zos.write(data);
zos.closeEntry();
}
}
}
public static byte[] createRandomStringInByte(int size) {
Random random = new Random();
byte[] data = new byte[size];
for (int n = 0; n < data.length; n++) {
char randomChar;
int choice = random.nextInt(2); // 0 for uppercase, 1 for lowercase
if (choice == 0) {
randomChar = (char) ('A' + random.nextInt(26)); // 'A' to 'Z'
} else {
randomChar = (char) ('a' + random.nextInt(26)); // 'a' to 'z'
}
data[n] = (byte) randomChar;
}
return data;
}
public File getFile() {
return file;
}
public List<String> getDataList() {
return dataList;
}
public static String getString(InputStream inputStream) throws IOException {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
IOUtils.copy(inputStream, byteArrayOutputStream);
return byteArrayOutputStream.toString(DEFAULT_ENCODING);
}
}
@@ -0,0 +1,83 @@
package com.baeldung.zip;
import static org.assertj.core.api.Assertions.assertThat;
import java.io.*;
import java.util.*;
import java.util.zip.*;
import org.junit.*;
public class ZipUnitTest {
private static File compressedFile;
private static List<String> dataList = new ArrayList<>();
@BeforeClass
public static void prepareData() throws IOException {
ZipSampleFileStore sampleFileStore = new ZipSampleFileStore(ZipBenchmark.NUM_OF_FILES, ZipBenchmark.DATA_SIZE);
compressedFile = sampleFileStore.getFile();
dataList = sampleFileStore.getDataList();
}
@Test
public void whenCreateZipFile_thenCompressedSizeShouldBeLessThanOriginal() throws IOException {
byte[] data = ZipSampleFileStore.createRandomStringInByte(10240);
File file = File.createTempFile("zip-temp", "");
try (
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file));
ZipOutputStream zos = new ZipOutputStream(bos)
) {
ZipEntry zipEntry = new ZipEntry("zip-entry.txt");
zos.putNextEntry(zipEntry);
zos.write(data);
zos.closeEntry();
assertThat(file.length()).isLessThan(data.length);
}
finally {
file.delete();
}
}
@Test
public void whenReadAllEntriesViaZipFile_thenDataIsEqualtoTheSource() throws IOException {
try (ZipFile zipFile = new ZipFile(compressedFile)) {
Enumeration<? extends ZipEntry> entries = zipFile.entries();
List<? extends ZipEntry> entryList = Collections.list(entries);
for (int idx=0; idx<entryList.size(); idx++) {
ZipEntry zipEntry = entryList.get(idx);
try (InputStream inputStream = zipFile.getInputStream(zipEntry)) {
String actual = ZipSampleFileStore.getString(inputStream);
assertThat(actual).as("Data for ZIP entry: " + zipEntry.getName()).isEqualTo(dataList.get(idx));
}
}
}
}
@Test
public void whenReadAllEntriesViaZipInputStream_thenDataIsEqualtoTheSource() throws IOException {
int idx = 0;
try (
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(compressedFile));
ZipInputStream zipInputStream = new ZipInputStream(bis)
) {
ZipEntry zipEntry;
while ((zipEntry = zipInputStream.getNextEntry()) != null) {
String actual = ZipSampleFileStore.getString(zipInputStream);
assertThat(actual).as("Data for ZIP entry: " + zipEntry.getName()).isEqualTo(dataList.get(idx++));
}
}
}
@AfterClass
public static void cleanup() {
if (compressedFile.exists()) {
compressedFile.delete();
}
}
}
@@ -0,0 +1,2 @@
I'm going to Alabama
Alabama is a state in the US
@@ -0,0 +1 @@
Dreams from My Father by Barack Obama

Some files were not shown because too many files have changed in this diff Show More