Merge branch 'eugenp:master' into JAVA-26380
This commit is contained in:
+129
@@ -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();
|
||||
}
|
||||
}
|
||||
+40
@@ -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);
|
||||
}
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
package com.baeldung.algorithms.connect4;
|
||||
|
||||
public enum Piece {
|
||||
PLAYER_1,
|
||||
PLAYER_2
|
||||
}
|
||||
+113
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
+63
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
+48
@@ -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();
|
||||
}
|
||||
}
|
||||
-27
@@ -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;
|
||||
}
|
||||
}
|
||||
-49
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
+38
-12
@@ -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>
|
||||
+29
@@ -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>
|
||||
|
||||
+85
@@ -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)
|
||||
|
||||
+100
@@ -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>
|
||||
|
||||
+182
@@ -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);
|
||||
}
|
||||
}
|
||||
+61
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
+23
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
+70
@@ -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
Reference in New Issue
Block a user