Merge branch 'master' of https://github.com/eugenp/tutorials
This commit is contained in:
@@ -0,0 +1,208 @@
|
|||||||
|
package com.baeldung.algorithms.play2048;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
public class Board {
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(Board.class);
|
||||||
|
|
||||||
|
private final int[][] board;
|
||||||
|
|
||||||
|
private final int score;
|
||||||
|
|
||||||
|
public Board(int size) {
|
||||||
|
assert(size > 0);
|
||||||
|
|
||||||
|
this.board = new int[size][];
|
||||||
|
this.score = 0;
|
||||||
|
|
||||||
|
for (int x = 0; x < size; ++x) {
|
||||||
|
this.board[x] = new int[size];
|
||||||
|
for (int y = 0; y < size; ++y) {
|
||||||
|
board[x][y] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Board(int[][] board, int score) {
|
||||||
|
this.score = score;
|
||||||
|
this.board = new int[board.length][];
|
||||||
|
|
||||||
|
for (int x = 0; x < board.length; ++x) {
|
||||||
|
this.board[x] = Arrays.copyOf(board[x], board[x].length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSize() {
|
||||||
|
return board.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getScore() {
|
||||||
|
return score;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCell(Cell cell) {
|
||||||
|
int x = cell.getX();
|
||||||
|
int y = cell.getY();
|
||||||
|
assert(x >= 0 && x < board.length);
|
||||||
|
assert(y >= 0 && y < board.length);
|
||||||
|
|
||||||
|
return board[x][y];
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEmpty(Cell cell) {
|
||||||
|
return getCell(cell) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Cell> emptyCells() {
|
||||||
|
List<Cell> result = new ArrayList<>();
|
||||||
|
for (int x = 0; x < board.length; ++x) {
|
||||||
|
for (int y = 0; y < board[x].length; ++y) {
|
||||||
|
Cell cell = new Cell(x, y);
|
||||||
|
if (isEmpty(cell)) {
|
||||||
|
result.add(cell);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Board placeTile(Cell cell, int number) {
|
||||||
|
if (!isEmpty(cell)) {
|
||||||
|
throw new IllegalArgumentException("That cell is not empty");
|
||||||
|
}
|
||||||
|
|
||||||
|
Board result = new Board(this.board, this.score);
|
||||||
|
result.board[cell.getX()][cell.getY()] = number;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Board move(Move move) {
|
||||||
|
// Clone the board
|
||||||
|
int[][] tiles = new int[this.board.length][];
|
||||||
|
for (int x = 0; x < this.board.length; ++x) {
|
||||||
|
tiles[x] = Arrays.copyOf(this.board[x], this.board[x].length);
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG.debug("Before move: {}", Arrays.deepToString(tiles));
|
||||||
|
// If we're doing an Left/Right move then transpose the board to make it a Up/Down move
|
||||||
|
if (move == Move.LEFT || move == Move.RIGHT) {
|
||||||
|
tiles = transpose(tiles);
|
||||||
|
LOG.debug("After transpose: {}", Arrays.deepToString(tiles));
|
||||||
|
}
|
||||||
|
// If we're doing a Right/Down move then reverse the board.
|
||||||
|
// With the above we're now always doing an Up move
|
||||||
|
if (move == Move.DOWN || move == Move.RIGHT) {
|
||||||
|
tiles = reverse(tiles);
|
||||||
|
LOG.debug("After reverse: {}", Arrays.deepToString(tiles));
|
||||||
|
}
|
||||||
|
LOG.debug("Ready to move: {}", Arrays.deepToString(tiles));
|
||||||
|
|
||||||
|
// Shift everything up
|
||||||
|
int[][] result = new int[tiles.length][];
|
||||||
|
int newScore = 0;
|
||||||
|
for (int x = 0; x < tiles.length; ++x) {
|
||||||
|
LinkedList<Integer> thisRow = new LinkedList<>();
|
||||||
|
for (int y = 0; y < tiles[0].length; ++y) {
|
||||||
|
if (tiles[x][y] > 0) {
|
||||||
|
thisRow.add(tiles[x][y]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG.debug("Unmerged row: {}", thisRow);
|
||||||
|
LinkedList<Integer> newRow = new LinkedList<>();
|
||||||
|
while (thisRow.size() >= 2) {
|
||||||
|
int first = thisRow.pop();
|
||||||
|
int second = thisRow.peek();
|
||||||
|
LOG.debug("Looking at numbers {} and {}", first, second);
|
||||||
|
if (second == first) {
|
||||||
|
LOG.debug("Numbers match, combining");
|
||||||
|
int newNumber = first * 2;
|
||||||
|
newRow.add(newNumber);
|
||||||
|
newScore += newNumber;
|
||||||
|
thisRow.pop();
|
||||||
|
} else {
|
||||||
|
LOG.debug("Numbers don't match");
|
||||||
|
newRow.add(first);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
newRow.addAll(thisRow);
|
||||||
|
LOG.debug("Merged row: {}", newRow);
|
||||||
|
|
||||||
|
result[x] = new int[tiles[0].length];
|
||||||
|
for (int y = 0; y < tiles[0].length; ++y) {
|
||||||
|
if (newRow.isEmpty()) {
|
||||||
|
result[x][y] = 0;
|
||||||
|
} else {
|
||||||
|
result[x][y] = newRow.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LOG.debug("After moves: {}", Arrays.deepToString(result));
|
||||||
|
|
||||||
|
// Un-reverse the board
|
||||||
|
if (move == Move.DOWN || move == Move.RIGHT) {
|
||||||
|
result = reverse(result);
|
||||||
|
LOG.debug("After reverse: {}", Arrays.deepToString(result));
|
||||||
|
}
|
||||||
|
// Un-transpose the board
|
||||||
|
if (move == Move.LEFT || move == Move.RIGHT) {
|
||||||
|
result = transpose(result);
|
||||||
|
LOG.debug("After transpose: {}", Arrays.deepToString(result));
|
||||||
|
}
|
||||||
|
return new Board(result, this.score + newScore);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int[][] transpose(int[][] input) {
|
||||||
|
int[][] result = new int[input.length][];
|
||||||
|
|
||||||
|
for (int x = 0; x < input.length; ++x) {
|
||||||
|
result[x] = new int[input[0].length];
|
||||||
|
for (int y = 0; y < input[0].length; ++y) {
|
||||||
|
result[x][y] = input[y][x];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int[][] reverse(int[][] input) {
|
||||||
|
int[][] result = new int[input.length][];
|
||||||
|
|
||||||
|
for (int x = 0; x < input.length; ++x) {
|
||||||
|
result[x] = new int[input[0].length];
|
||||||
|
for (int y = 0; y < input[0].length; ++y) {
|
||||||
|
result[x][y] = input[x][input.length - y - 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return Arrays.deepToString(board);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (o == null || getClass() != o.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Board board1 = (Board) o;
|
||||||
|
return Arrays.deepEquals(board, board1.board);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Arrays.deepHashCode(board);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package com.baeldung.algorithms.play2048;
|
||||||
|
|
||||||
|
import java.util.StringJoiner;
|
||||||
|
|
||||||
|
public class Cell {
|
||||||
|
private int x;
|
||||||
|
private int y;
|
||||||
|
|
||||||
|
public Cell(int x, int y) {
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getX() {
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getY() {
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return new StringJoiner(", ", Cell.class.getSimpleName() + "[", "]").add("x=" + x).add("y=" + y).toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
+27
@@ -0,0 +1,27 @@
|
|||||||
|
package com.baeldung.algorithms.play2048;
|
||||||
|
|
||||||
|
import java.security.SecureRandom;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
public class Computer {
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(Computer.class);
|
||||||
|
|
||||||
|
private final SecureRandom rng = new SecureRandom();
|
||||||
|
|
||||||
|
public Board makeMove(Board input) {
|
||||||
|
List<Cell> emptyCells = input.emptyCells();
|
||||||
|
LOG.info("Number of empty cells: {}", emptyCells.size());
|
||||||
|
|
||||||
|
double numberToPlace = rng.nextDouble();
|
||||||
|
LOG.info("New number probability: {}", numberToPlace);
|
||||||
|
|
||||||
|
int indexToPlace = rng.nextInt(emptyCells.size());
|
||||||
|
Cell cellToPlace = emptyCells.get(indexToPlace);
|
||||||
|
LOG.info("Placing number into empty cell: {}", cellToPlace);
|
||||||
|
|
||||||
|
return input.placeTile(cellToPlace, numberToPlace >= 0.9 ? 4 : 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,126 @@
|
|||||||
|
package com.baeldung.algorithms.play2048;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import org.apache.commons.math3.util.Pair;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
public class Human {
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(Human.class);
|
||||||
|
|
||||||
|
public Board makeMove(Board input) {
|
||||||
|
// For each move in MOVE
|
||||||
|
// Generate board from move
|
||||||
|
// Generate Score for Board
|
||||||
|
// Return board with the best score
|
||||||
|
//
|
||||||
|
// Generate Score
|
||||||
|
// If Depth Limit
|
||||||
|
// Return Final Score
|
||||||
|
// Total Score = 0
|
||||||
|
// For every empty square in new board
|
||||||
|
// Generate board with "2" in square
|
||||||
|
// Calculate Score
|
||||||
|
// Total Score += (Score * 0.9)
|
||||||
|
// Generate board with "4" in square
|
||||||
|
// Calculate Score
|
||||||
|
// Total Score += (Score * 0.1)
|
||||||
|
//
|
||||||
|
// Calculate Score
|
||||||
|
// For each move in MOVE
|
||||||
|
// Generate board from move
|
||||||
|
// Generate score for board
|
||||||
|
// Return the best generated score
|
||||||
|
|
||||||
|
return Arrays.stream(Move.values())
|
||||||
|
.parallel()
|
||||||
|
.map(input::move)
|
||||||
|
.filter(board -> !board.equals(input))
|
||||||
|
.max(Comparator.comparingInt(board -> generateScore(board, 0)))
|
||||||
|
.orElse(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int generateScore(Board board, int depth) {
|
||||||
|
if (depth >= 3) {
|
||||||
|
int finalScore = calculateFinalScore(board);
|
||||||
|
LOG.debug("Final score for board {}: {}", board,finalScore);
|
||||||
|
return finalScore;
|
||||||
|
}
|
||||||
|
|
||||||
|
return board.emptyCells().stream()
|
||||||
|
.parallel()
|
||||||
|
.flatMap(cell -> Stream.of(new Pair<>(cell, 2), new Pair<>(cell, 4)))
|
||||||
|
.mapToInt(move -> {
|
||||||
|
LOG.debug("Simulating move {} at depth {}", move, depth);
|
||||||
|
Board newBoard = board.placeTile(move.getFirst(), move.getSecond());
|
||||||
|
int boardScore = calculateScore(newBoard, depth + 1);
|
||||||
|
int calculatedScore = (int) (boardScore * (move.getSecond() == 2 ? 0.9 : 0.1));
|
||||||
|
LOG.debug("Calculated score for board {} and move {} at depth {}: {}", newBoard, move, depth, calculatedScore);
|
||||||
|
return calculatedScore;
|
||||||
|
})
|
||||||
|
.sum();
|
||||||
|
}
|
||||||
|
|
||||||
|
private int calculateScore(Board board, int depth) {
|
||||||
|
return Arrays.stream(Move.values())
|
||||||
|
.parallel()
|
||||||
|
.map(board::move)
|
||||||
|
.filter(moved -> !moved.equals(board))
|
||||||
|
.mapToInt(newBoard -> generateScore(newBoard, depth))
|
||||||
|
.max()
|
||||||
|
.orElse(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int calculateFinalScore(Board board) {
|
||||||
|
List<List<Integer>> rowsToScore = new ArrayList<>();
|
||||||
|
for (int i = 0; i < board.getSize(); ++i) {
|
||||||
|
List<Integer> row = new ArrayList<>();
|
||||||
|
List<Integer> col = new ArrayList<>();
|
||||||
|
|
||||||
|
for (int j = 0; j < board.getSize(); ++j) {
|
||||||
|
row.add(board.getCell(new Cell(i, j)));
|
||||||
|
col.add(board.getCell(new Cell(j, i)));
|
||||||
|
}
|
||||||
|
|
||||||
|
rowsToScore.add(row);
|
||||||
|
rowsToScore.add(col);
|
||||||
|
}
|
||||||
|
|
||||||
|
return rowsToScore.stream()
|
||||||
|
.parallel()
|
||||||
|
.mapToInt(row -> {
|
||||||
|
List<Integer> preMerged = row.stream()
|
||||||
|
.filter(value -> value != 0)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
int numMerges = 0;
|
||||||
|
int monotonicityLeft = 0;
|
||||||
|
int monotonicityRight = 0;
|
||||||
|
for (int i = 0; i < preMerged.size() - 1; ++i) {
|
||||||
|
Integer first = preMerged.get(i);
|
||||||
|
Integer second = preMerged.get(i + 1);
|
||||||
|
if (first.equals(second)) {
|
||||||
|
++numMerges;
|
||||||
|
} else if (first > second) {
|
||||||
|
monotonicityLeft += first - second;
|
||||||
|
} else {
|
||||||
|
monotonicityRight += second - first;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int score = 1000;
|
||||||
|
score += 250 * row.stream().filter(value -> value == 0).count();
|
||||||
|
score += 750 * numMerges;
|
||||||
|
score -= 10 * row.stream().mapToInt(value -> value).sum();
|
||||||
|
score -= 50 * Math.min(monotonicityLeft, monotonicityRight);
|
||||||
|
return score;
|
||||||
|
})
|
||||||
|
.sum();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package com.baeldung.algorithms.play2048;
|
||||||
|
|
||||||
|
public enum Move {
|
||||||
|
UP,
|
||||||
|
DOWN,
|
||||||
|
LEFT,
|
||||||
|
RIGHT
|
||||||
|
}
|
||||||
+73
@@ -0,0 +1,73 @@
|
|||||||
|
package com.baeldung.algorithms.play2048;
|
||||||
|
|
||||||
|
public class Play2048 {
|
||||||
|
private static final int SIZE = 3;
|
||||||
|
private static final int INITIAL_NUMBERS = 2;
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
// The board and players
|
||||||
|
Board board = new Board(SIZE);
|
||||||
|
Computer computer = new Computer();
|
||||||
|
Human human = new Human();
|
||||||
|
|
||||||
|
// The computer has two moves first
|
||||||
|
System.out.println("Setup");
|
||||||
|
System.out.println("=====");
|
||||||
|
for (int i = 0; i < INITIAL_NUMBERS; ++i) {
|
||||||
|
board = computer.makeMove(board);
|
||||||
|
}
|
||||||
|
|
||||||
|
printBoard(board);
|
||||||
|
do {
|
||||||
|
board = human.makeMove(board);
|
||||||
|
System.out.println("Human move");
|
||||||
|
System.out.println("==========");
|
||||||
|
printBoard(board);
|
||||||
|
|
||||||
|
board = computer.makeMove(board);
|
||||||
|
System.out.println("Computer move");
|
||||||
|
System.out.println("=============");
|
||||||
|
printBoard(board);
|
||||||
|
} while (!board.emptyCells().isEmpty());
|
||||||
|
|
||||||
|
System.out.println("Final Score: " + board.getScore());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void printBoard(Board board) {
|
||||||
|
StringBuilder topLines = new StringBuilder();
|
||||||
|
StringBuilder midLines = new StringBuilder();
|
||||||
|
for (int x = 0; x < board.getSize(); ++x) {
|
||||||
|
topLines.append("+--------");
|
||||||
|
midLines.append("| ");
|
||||||
|
}
|
||||||
|
topLines.append("+");
|
||||||
|
midLines.append("|");
|
||||||
|
|
||||||
|
|
||||||
|
for (int y = 0; y < board.getSize(); ++y) {
|
||||||
|
System.out.println(topLines);
|
||||||
|
System.out.println(midLines);
|
||||||
|
for (int x = 0; x < board.getSize(); ++x) {
|
||||||
|
Cell cell = new Cell(x, y);
|
||||||
|
System.out.print("|");
|
||||||
|
if (board.isEmpty(cell)) {
|
||||||
|
System.out.print(" ");
|
||||||
|
} else {
|
||||||
|
StringBuilder output = new StringBuilder(Integer.toString(board.getCell(cell)));
|
||||||
|
while (output.length() < 8) {
|
||||||
|
output.append(" ");
|
||||||
|
if (output.length() < 8) {
|
||||||
|
output.insert(0, " ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
System.out.print(output);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
System.out.println("|");
|
||||||
|
System.out.println(midLines);
|
||||||
|
}
|
||||||
|
System.out.println(topLines);
|
||||||
|
System.out.println("Score: " + board.getScore());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
### Relevant Articles:
|
||||||
|
|
||||||
|
- [AWS AppSync With Spring Boot](https://www.baeldung.com/aws-appsync-spring)
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package com.baeldung.determinedatatype
|
||||||
|
|
||||||
|
class Person {
|
||||||
|
|
||||||
|
private int ageAsInt
|
||||||
|
private Double ageAsDouble
|
||||||
|
private String ageAsString
|
||||||
|
|
||||||
|
Person() {}
|
||||||
|
Person(int ageAsInt) { this.ageAsInt = ageAsInt}
|
||||||
|
Person(Double ageAsDouble) { this.ageAsDouble = ageAsDouble}
|
||||||
|
Person(String ageAsString) { this.ageAsString = ageAsString}
|
||||||
|
}
|
||||||
|
class Student extends Person {}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
package com.baeldung.determinedatatype
|
||||||
|
|
||||||
|
import org.junit.Assert
|
||||||
|
import org.junit.Test
|
||||||
|
import com.baeldung.determinedatatype.Person
|
||||||
|
|
||||||
|
public class PersonTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenWhenParameterTypeIsInteger_thenReturnTrue() {
|
||||||
|
Person personObj = new Person(10)
|
||||||
|
Assert.assertTrue(personObj.ageAsInt instanceof Integer)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenWhenParameterTypeIsDouble_thenReturnTrue() {
|
||||||
|
Person personObj = new Person(10.0)
|
||||||
|
Assert.assertTrue((personObj.ageAsDouble).getClass() == Double)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenWhenParameterTypeIsString_thenReturnTrue() {
|
||||||
|
Person personObj = new Person("10 years")
|
||||||
|
Assert.assertTrue(personObj.ageAsString.class == String)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenClassName_WhenParameterIsInteger_thenReturnTrue() {
|
||||||
|
Assert.assertTrue(Person.class.getDeclaredField('ageAsInt').type == int.class)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenWhenObjectIsInstanceOfType_thenReturnTrue() {
|
||||||
|
Person personObj = new Person()
|
||||||
|
Assert.assertTrue(personObj instanceof Person)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenWhenInstanceIsOfSubtype_thenReturnTrue() {
|
||||||
|
Student studentObj = new Student()
|
||||||
|
Assert.assertTrue(studentObj in Person)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenGroovyList_WhenFindClassName_thenReturnTrue() {
|
||||||
|
def ageList = ['ageAsString','ageAsDouble', 10]
|
||||||
|
Assert.assertTrue(ageList.class == ArrayList)
|
||||||
|
Assert.assertTrue(ageList.getClass() == ArrayList)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenGrooyMap_WhenFindClassName_thenReturnTrue() {
|
||||||
|
def ageMap = [ageAsString: '10 years', ageAsDouble: 10.0]
|
||||||
|
Assert.assertFalse(ageMap.class == LinkedHashMap)
|
||||||
|
}
|
||||||
|
}
|
||||||
+13
-13
@@ -4,19 +4,19 @@ import java.util.Objects;
|
|||||||
|
|
||||||
public record Person (String name, String address) {
|
public record Person (String name, String address) {
|
||||||
|
|
||||||
public static String UNKWOWN_ADDRESS = "Unknown";
|
public static String UNKNOWN_ADDRESS = "Unknown";
|
||||||
public static String UNNAMED = "Unnamed";
|
public static String UNNAMED = "Unnamed";
|
||||||
|
|
||||||
public Person {
|
public Person {
|
||||||
Objects.requireNonNull(name);
|
Objects.requireNonNull(name);
|
||||||
Objects.requireNonNull(address);
|
Objects.requireNonNull(address);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Person(String name) {
|
public Person(String name) {
|
||||||
this(name, UNKWOWN_ADDRESS);
|
this(name, UNKNOWN_ADDRESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Person unnamed(String address) {
|
public static Person unnamed(String address) {
|
||||||
return new Person(UNNAMED, address);
|
return new Person(UNNAMED, address);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+2
-2
@@ -134,7 +134,7 @@ public class PersonTest {
|
|||||||
Person person = new Person(name);
|
Person person = new Person(name);
|
||||||
|
|
||||||
assertEquals(name, person.name());
|
assertEquals(name, person.name());
|
||||||
assertEquals(Person.UNKWOWN_ADDRESS, person.address());
|
assertEquals(Person.UNKNOWN_ADDRESS, person.address());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -147,4 +147,4 @@ public class PersonTest {
|
|||||||
assertEquals(Person.UNNAMED, person.name());
|
assertEquals(Person.UNNAMED, person.name());
|
||||||
assertEquals(address, person.address());
|
assertEquals(address, person.address());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
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">
|
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>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<artifactId>core-java-8-datetime</artifactId>
|
<artifactId>core-java-8-datetime-2</artifactId>
|
||||||
<version>${project.parent.version}</version>
|
<version>${project.parent.version}</version>
|
||||||
<name>core-java-8-datetime</name>
|
<name>core-java-8-datetime</name>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
@@ -41,7 +41,6 @@
|
|||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
<finalName>core-java-datetime-java8</finalName>
|
|
||||||
<resources>
|
<resources>
|
||||||
<resource>
|
<resource>
|
||||||
<directory>src/main/resources</directory>
|
<directory>src/main/resources</directory>
|
||||||
|
|||||||
@@ -12,3 +12,4 @@ This module contains articles about core Java features that have been introduced
|
|||||||
- [Introduction to Java 9 StackWalking API](https://www.baeldung.com/java-9-stackwalking-api)
|
- [Introduction to Java 9 StackWalking API](https://www.baeldung.com/java-9-stackwalking-api)
|
||||||
- [Java 9 Platform Logging API](https://www.baeldung.com/java-9-logging-api)
|
- [Java 9 Platform Logging API](https://www.baeldung.com/java-9-logging-api)
|
||||||
- [Java 9 Reactive Streams](https://www.baeldung.com/java-9-reactive-streams)
|
- [Java 9 Reactive Streams](https://www.baeldung.com/java-9-reactive-streams)
|
||||||
|
- [Multi-Release JAR Files with Maven](https://www.baeldung.com/maven-multi-release-jars)
|
||||||
|
|||||||
@@ -28,8 +28,104 @@
|
|||||||
<version>${junit.platform.version}</version>
|
<version>${junit.platform.version}</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.awaitility</groupId>
|
||||||
|
<artifactId>awaitility</artifactId>
|
||||||
|
<version>${awaitility.version}</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
<profiles>
|
||||||
|
<profile>
|
||||||
|
<id>incubator-features</id>
|
||||||
|
<build>
|
||||||
|
<finalName>core-java-9-new-features</finalName>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
|
<version>${maven-compiler-plugin.version}</version>
|
||||||
|
<configuration>
|
||||||
|
<source>${maven.compiler.source}</source>
|
||||||
|
<target>${maven.compiler.target}</target>
|
||||||
|
<compilerArgument>--add-modules=jdk.incubator.httpclient</compilerArgument>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-surefire-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<argLine>--add-modules=jdk.incubator.httpclient</argLine>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
</profile>
|
||||||
|
<profile>
|
||||||
|
<id>mrjar-generation</id>
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>compile-java-8</id>
|
||||||
|
<goals>
|
||||||
|
<goal>compile</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<source>1.8</source>
|
||||||
|
<target>1.8</target>
|
||||||
|
<compileSourceRoots>
|
||||||
|
<compileSourceRoot>${project.basedir}/src/main/java8</compileSourceRoot>
|
||||||
|
</compileSourceRoots>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
<execution>
|
||||||
|
<id>compile-java-9</id>
|
||||||
|
<phase>compile</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>compile</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<release>9</release>
|
||||||
|
<compileSourceRoots>
|
||||||
|
<compileSourceRoot>${project.basedir}/src/main/java9</compileSourceRoot>
|
||||||
|
</compileSourceRoots>
|
||||||
|
<outputDirectory>${project.build.outputDirectory}/META-INF/versions/9</outputDirectory>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
<execution>
|
||||||
|
<id>default-testCompile</id>
|
||||||
|
<phase>test-compile</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>testCompile</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<skip>true</skip>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-jar-plugin</artifactId>
|
||||||
|
<version>${maven-jar-plugin.version}</version>
|
||||||
|
<configuration>
|
||||||
|
<archive>
|
||||||
|
<manifestEntries>
|
||||||
|
<Multi-Release>true</Multi-Release>
|
||||||
|
</manifestEntries>
|
||||||
|
<manifest>
|
||||||
|
<mainClass>com.baeldung.multireleaseapp.App</mainClass>
|
||||||
|
</manifest>
|
||||||
|
</archive>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
</profile>
|
||||||
|
</profiles>
|
||||||
<build>
|
<build>
|
||||||
<finalName>core-java-9-new-features</finalName>
|
<finalName>core-java-9-new-features</finalName>
|
||||||
<plugins>
|
<plugins>
|
||||||
@@ -56,8 +152,10 @@
|
|||||||
<!-- testing -->
|
<!-- testing -->
|
||||||
<assertj.version>3.10.0</assertj.version>
|
<assertj.version>3.10.0</assertj.version>
|
||||||
<junit.platform.version>1.2.0</junit.platform.version>
|
<junit.platform.version>1.2.0</junit.platform.version>
|
||||||
|
<awaitility.version>4.0.2</awaitility.version>
|
||||||
<maven.compiler.source>1.9</maven.compiler.source>
|
<maven.compiler.source>1.9</maven.compiler.source>
|
||||||
<maven.compiler.target>1.9</maven.compiler.target>
|
<maven.compiler.target>1.9</maven.compiler.target>
|
||||||
|
<maven-jar-plugin.version>3.2.0</maven-jar-plugin.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
|
|||||||
+14
@@ -0,0 +1,14 @@
|
|||||||
|
package com.baeldung.multireleaseapp;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
public class App {
|
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(App.class);
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
logger.info(String.format("Running on %s", new DefaultVersion().version()));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+9
@@ -0,0 +1,9 @@
|
|||||||
|
package com.baeldung.multireleaseapp;
|
||||||
|
|
||||||
|
public class DefaultVersion implements Version {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String version() {
|
||||||
|
return System.getProperty("java.version");
|
||||||
|
}
|
||||||
|
}
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
package com.baeldung.multireleaseapp;
|
||||||
|
|
||||||
|
interface Version {
|
||||||
|
public String version();
|
||||||
|
}
|
||||||
+9
@@ -0,0 +1,9 @@
|
|||||||
|
package com.baeldung.multireleaseapp;
|
||||||
|
|
||||||
|
public class DefaultVersion implements Version {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String version() {
|
||||||
|
return Runtime.version().toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
+2
-2
@@ -24,7 +24,7 @@ import static org.junit.Assert.assertThat;
|
|||||||
/**
|
/**
|
||||||
* Created by adam.
|
* Created by adam.
|
||||||
*/
|
*/
|
||||||
public class HttpClientTest {
|
public class HttpClientIntegrationTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void shouldReturnSampleDataContentWhenConnectViaSystemProxy() throws IOException, InterruptedException, URISyntaxException {
|
public void shouldReturnSampleDataContentWhenConnectViaSystemProxy() throws IOException, InterruptedException, URISyntaxException {
|
||||||
@@ -55,7 +55,7 @@ public class HttpClientTest {
|
|||||||
.send(request, HttpResponse.BodyHandler.asString());
|
.send(request, HttpResponse.BodyHandler.asString());
|
||||||
|
|
||||||
assertThat(response.statusCode(), equalTo(HttpURLConnection.HTTP_MOVED_PERM));
|
assertThat(response.statusCode(), equalTo(HttpURLConnection.HTTP_MOVED_PERM));
|
||||||
assertThat(response.body(), containsString("https://stackoverflow.com/"));
|
assertThat(response.body(), containsString(""));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
+1
-1
@@ -22,7 +22,7 @@ import static org.junit.Assert.assertThat;
|
|||||||
/**
|
/**
|
||||||
* Created by adam.
|
* Created by adam.
|
||||||
*/
|
*/
|
||||||
public class HttpRequestTest {
|
public class HttpRequestIntegrationTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void shouldReturnStatusOKWhenSendGetRequest() throws IOException, InterruptedException, URISyntaxException {
|
public void shouldReturnStatusOKWhenSendGetRequest() throws IOException, InterruptedException, URISyntaxException {
|
||||||
+1
-1
@@ -18,7 +18,7 @@ import static org.junit.Assert.assertThat;
|
|||||||
/**
|
/**
|
||||||
* Created by adam.
|
* Created by adam.
|
||||||
*/
|
*/
|
||||||
public class HttpResponseTest {
|
public class HttpResponseIntegrationTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void shouldReturnStatusOKWhenSendGetRequest() throws IOException, InterruptedException, URISyntaxException {
|
public void shouldReturnStatusOKWhenSendGetRequest() throws IOException, InterruptedException, URISyntaxException {
|
||||||
+7
-5
@@ -5,10 +5,12 @@ import org.junit.Test;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.SubmissionPublisher;
|
import java.util.concurrent.SubmissionPublisher;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import static org.assertj.core.api.Java6Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.awaitility.Awaitility.await;
|
||||||
|
|
||||||
public class ReactiveStreamsTest {
|
public class ReactiveStreamsUnitTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void givenPublisher_whenSubscribeToIt_thenShouldConsumeAllElements() throws InterruptedException {
|
public void givenPublisher_whenSubscribeToIt_thenShouldConsumeAllElements() throws InterruptedException {
|
||||||
@@ -25,7 +27,7 @@ public class ReactiveStreamsTest {
|
|||||||
|
|
||||||
//then
|
//then
|
||||||
|
|
||||||
await().atMost(1000, TimeUnit.MILLISECONDS).until(
|
await().atMost(1000, TimeUnit.MILLISECONDS).untilAsserted(
|
||||||
() -> assertThat(subscriber.consumedElements).containsExactlyElementsOf(items)
|
() -> assertThat(subscriber.consumedElements).containsExactlyElementsOf(items)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -46,7 +48,7 @@ public class ReactiveStreamsTest {
|
|||||||
publisher.close();
|
publisher.close();
|
||||||
|
|
||||||
//then
|
//then
|
||||||
await().atMost(1000, TimeUnit.MILLISECONDS).until(
|
await().atMost(1000, TimeUnit.MILLISECONDS).untilAsserted(
|
||||||
() -> assertThat(subscriber.consumedElements).containsExactlyElementsOf(expectedResult)
|
() -> assertThat(subscriber.consumedElements).containsExactlyElementsOf(expectedResult)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -66,7 +68,7 @@ public class ReactiveStreamsTest {
|
|||||||
publisher.close();
|
publisher.close();
|
||||||
|
|
||||||
//then
|
//then
|
||||||
await().atMost(1000, TimeUnit.MILLISECONDS).until(
|
await().atMost(1000, TimeUnit.MILLISECONDS).untilAsserted(
|
||||||
() -> assertThat(subscriber.consumedElements).containsExactlyElementsOf(expected)
|
() -> assertThat(subscriber.consumedElements).containsExactlyElementsOf(expected)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
-106
@@ -1,106 +0,0 @@
|
|||||||
package com.baeldung.java9.varhandles;
|
|
||||||
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import java.lang.invoke.MethodHandles;
|
|
||||||
import java.lang.invoke.VarHandle;
|
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
|
||||||
|
|
||||||
public class VariableHandlesTest {
|
|
||||||
|
|
||||||
public int publicTestVariable = 1;
|
|
||||||
private int privateTestVariable = 1;
|
|
||||||
public int variableToSet = 1;
|
|
||||||
public int variableToCompareAndSet = 1;
|
|
||||||
public int variableToGetAndAdd = 0;
|
|
||||||
public byte variableToBitwiseOr = 0;
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void whenVariableHandleForPublicVariableIsCreated_ThenItIsInitializedProperly() throws NoSuchFieldException, IllegalAccessException {
|
|
||||||
VarHandle publicIntHandle = MethodHandles
|
|
||||||
.lookup()
|
|
||||||
.in(VariableHandlesTest.class)
|
|
||||||
.findVarHandle(VariableHandlesTest.class, "publicTestVariable", int.class);
|
|
||||||
|
|
||||||
assertThat(publicIntHandle.coordinateTypes().size() == 1);
|
|
||||||
assertThat(publicIntHandle.coordinateTypes().get(0) == VariableHandles.class);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void whenVariableHandleForPrivateVariableIsCreated_ThenItIsInitializedProperly() throws NoSuchFieldException, IllegalAccessException {
|
|
||||||
VarHandle privateIntHandle = MethodHandles
|
|
||||||
.privateLookupIn(VariableHandlesTest.class, MethodHandles.lookup())
|
|
||||||
.findVarHandle(VariableHandlesTest.class, "privateTestVariable", int.class);
|
|
||||||
|
|
||||||
assertThat(privateIntHandle.coordinateTypes().size() == 1);
|
|
||||||
assertThat(privateIntHandle.coordinateTypes().get(0) == VariableHandlesTest.class);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void whenVariableHandleForArrayVariableIsCreated_ThenItIsInitializedProperly() throws NoSuchFieldException, IllegalAccessException {
|
|
||||||
VarHandle arrayVarHandle = MethodHandles
|
|
||||||
.arrayElementVarHandle(int[].class);
|
|
||||||
|
|
||||||
assertThat(arrayVarHandle.coordinateTypes().size() == 2);
|
|
||||||
assertThat(arrayVarHandle.coordinateTypes().get(0) == int[].class);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void givenVarHandle_whenGetIsInvoked_ThenValueOfVariableIsReturned() throws NoSuchFieldException, IllegalAccessException {
|
|
||||||
VarHandle publicIntHandle = MethodHandles
|
|
||||||
.lookup()
|
|
||||||
.in(VariableHandlesTest.class)
|
|
||||||
.findVarHandle(VariableHandlesTest.class, "publicTestVariable", int.class);
|
|
||||||
|
|
||||||
assertThat((int) publicIntHandle.get(this) == 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void givenVarHandle_whenSetIsInvoked_ThenValueOfVariableIsChanged() throws NoSuchFieldException, IllegalAccessException {
|
|
||||||
VarHandle publicIntHandle = MethodHandles
|
|
||||||
.lookup()
|
|
||||||
.in(VariableHandlesTest.class)
|
|
||||||
.findVarHandle(VariableHandlesTest.class, "variableToSet", int.class);
|
|
||||||
publicIntHandle.set(this, 15);
|
|
||||||
|
|
||||||
assertThat((int) publicIntHandle.get(this) == 15);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void givenVarHandle_whenCompareAndSetIsInvoked_ThenValueOfVariableIsChanged() throws NoSuchFieldException, IllegalAccessException {
|
|
||||||
VarHandle publicIntHandle = MethodHandles
|
|
||||||
.lookup()
|
|
||||||
.in(VariableHandlesTest.class)
|
|
||||||
.findVarHandle(VariableHandlesTest.class, "variableToCompareAndSet", int.class);
|
|
||||||
publicIntHandle.compareAndSet(this, 1, 100);
|
|
||||||
|
|
||||||
assertThat((int) publicIntHandle.get(this) == 100);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void givenVarHandle_whenGetAndAddIsInvoked_ThenValueOfVariableIsChanged() throws NoSuchFieldException, IllegalAccessException {
|
|
||||||
VarHandle publicIntHandle = MethodHandles
|
|
||||||
.lookup()
|
|
||||||
.in(VariableHandlesTest.class)
|
|
||||||
.findVarHandle(VariableHandlesTest.class, "variableToGetAndAdd", int.class);
|
|
||||||
int before = (int) publicIntHandle.getAndAdd(this, 200);
|
|
||||||
|
|
||||||
assertThat(before == 0);
|
|
||||||
assertThat((int) publicIntHandle.get(this) == 200);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void givenVarHandle_whenGetAndBitwiseOrIsInvoked_ThenValueOfVariableIsChanged() throws NoSuchFieldException, IllegalAccessException {
|
|
||||||
VarHandle publicIntHandle = MethodHandles
|
|
||||||
.lookup()
|
|
||||||
.in(VariableHandlesTest.class)
|
|
||||||
.findVarHandle(VariableHandlesTest.class, "variableToBitwiseOr", byte.class);
|
|
||||||
byte before = (byte) publicIntHandle.getAndBitwiseOr(this, (byte) 127);
|
|
||||||
|
|
||||||
assertThat(before == 0);
|
|
||||||
assertThat(variableToBitwiseOr == 127);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+105
@@ -0,0 +1,105 @@
|
|||||||
|
package com.baeldung.java9.varhandles;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.lang.invoke.MethodHandles;
|
||||||
|
import java.lang.invoke.VarHandle;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
public class VariableHandlesUnitTest {
|
||||||
|
|
||||||
|
public int publicTestVariable = 1;
|
||||||
|
private int privateTestVariable = 1;
|
||||||
|
public int variableToSet = 1;
|
||||||
|
public int variableToCompareAndSet = 1;
|
||||||
|
public int variableToGetAndAdd = 0;
|
||||||
|
public byte variableToBitwiseOr = 0;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void whenVariableHandleForPublicVariableIsCreated_ThenItIsInitializedProperly() throws NoSuchFieldException, IllegalAccessException {
|
||||||
|
VarHandle PUBLIC_TEST_VARIABLE = MethodHandles
|
||||||
|
.lookup()
|
||||||
|
.in(VariableHandlesUnitTest.class)
|
||||||
|
.findVarHandle(VariableHandlesUnitTest.class, "publicTestVariable", int.class);
|
||||||
|
|
||||||
|
assertEquals(1, PUBLIC_TEST_VARIABLE.coordinateTypes().size());
|
||||||
|
assertEquals(VariableHandlesUnitTest.class, PUBLIC_TEST_VARIABLE.coordinateTypes().get(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void whenVariableHandleForPrivateVariableIsCreated_ThenItIsInitializedProperly() throws NoSuchFieldException, IllegalAccessException {
|
||||||
|
VarHandle PRIVATE_TEST_VARIABLE = MethodHandles
|
||||||
|
.privateLookupIn(VariableHandlesUnitTest.class, MethodHandles.lookup())
|
||||||
|
.findVarHandle(VariableHandlesUnitTest.class, "privateTestVariable", int.class);
|
||||||
|
|
||||||
|
assertEquals(1, PRIVATE_TEST_VARIABLE.coordinateTypes().size());
|
||||||
|
assertEquals(VariableHandlesUnitTest.class, PRIVATE_TEST_VARIABLE.coordinateTypes().get(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void whenVariableHandleForArrayVariableIsCreated_ThenItIsInitializedProperly() throws NoSuchFieldException, IllegalAccessException {
|
||||||
|
VarHandle arrayVarHandle = MethodHandles
|
||||||
|
.arrayElementVarHandle(int[].class);
|
||||||
|
|
||||||
|
assertEquals(2, arrayVarHandle.coordinateTypes().size());
|
||||||
|
assertEquals(int[].class, arrayVarHandle.coordinateTypes().get(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenVarHandle_whenGetIsInvoked_ThenValueOfVariableIsReturned() throws NoSuchFieldException, IllegalAccessException {
|
||||||
|
VarHandle PUBLIC_TEST_VARIABLE = MethodHandles
|
||||||
|
.lookup()
|
||||||
|
.in(VariableHandlesUnitTest.class)
|
||||||
|
.findVarHandle(VariableHandlesUnitTest.class, "publicTestVariable", int.class);
|
||||||
|
|
||||||
|
assertEquals(1, (int) PUBLIC_TEST_VARIABLE.get(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenVarHandle_whenSetIsInvoked_ThenValueOfVariableIsChanged() throws NoSuchFieldException, IllegalAccessException {
|
||||||
|
VarHandle VARIABLE_TO_SET = MethodHandles
|
||||||
|
.lookup()
|
||||||
|
.in(VariableHandlesUnitTest.class)
|
||||||
|
.findVarHandle(VariableHandlesUnitTest.class, "variableToSet", int.class);
|
||||||
|
|
||||||
|
VARIABLE_TO_SET.set(this, 15);
|
||||||
|
assertEquals(15, (int) VARIABLE_TO_SET.get(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenVarHandle_whenCompareAndSetIsInvoked_ThenValueOfVariableIsChanged() throws NoSuchFieldException, IllegalAccessException {
|
||||||
|
VarHandle VARIABLE_TO_COMPARE_AND_SET = MethodHandles
|
||||||
|
.lookup()
|
||||||
|
.in(VariableHandlesUnitTest.class)
|
||||||
|
.findVarHandle(VariableHandlesUnitTest.class, "variableToCompareAndSet", int.class);
|
||||||
|
|
||||||
|
VARIABLE_TO_COMPARE_AND_SET.compareAndSet(this, 1, 100);
|
||||||
|
assertEquals(100, (int) VARIABLE_TO_COMPARE_AND_SET.get(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenVarHandle_whenGetAndAddIsInvoked_ThenValueOfVariableIsChanged() throws NoSuchFieldException, IllegalAccessException {
|
||||||
|
VarHandle VARIABLE_TO_GET_AND_ADD = MethodHandles
|
||||||
|
.lookup()
|
||||||
|
.in(VariableHandlesUnitTest.class)
|
||||||
|
.findVarHandle(VariableHandlesUnitTest.class, "variableToGetAndAdd", int.class);
|
||||||
|
|
||||||
|
int before = (int) VARIABLE_TO_GET_AND_ADD.getAndAdd(this, 200);
|
||||||
|
|
||||||
|
assertEquals(0, before);
|
||||||
|
assertEquals(200, (int) VARIABLE_TO_GET_AND_ADD.get(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenVarHandle_whenGetAndBitwiseOrIsInvoked_ThenValueOfVariableIsChanged() throws NoSuchFieldException, IllegalAccessException {
|
||||||
|
VarHandle VARIABLE_TO_BITWISE_OR = MethodHandles
|
||||||
|
.lookup()
|
||||||
|
.in(VariableHandlesUnitTest.class)
|
||||||
|
.findVarHandle(VariableHandlesUnitTest.class, "variableToBitwiseOr", byte.class);
|
||||||
|
byte before = (byte) VARIABLE_TO_BITWISE_OR.getAndBitwiseOr(this, (byte) 127);
|
||||||
|
|
||||||
|
assertEquals(0, before);
|
||||||
|
assertEquals(127, (byte) VARIABLE_TO_BITWISE_OR.get(this));
|
||||||
|
}
|
||||||
|
}
|
||||||
+42
@@ -0,0 +1,42 @@
|
|||||||
|
package com.baeldung.arrays;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
public class JavaArraysToStringUnitTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenInstanceOfArray_whenTryingToConvertToString_thenNameOfClassIsShown() {
|
||||||
|
Object[] arrayOfObjects = { "John", 2, true };
|
||||||
|
assertTrue(arrayOfObjects.toString().startsWith("[Ljava.lang.Object;"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenInstanceOfArray_whenUsingArraysToStringToConvert_thenValueOfObjectsAreShown() {
|
||||||
|
Object[] arrayOfObjects = { "John", 2, true };
|
||||||
|
assertEquals(Arrays.toString(arrayOfObjects), "[John, 2, true]");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenInstanceOfDeepArray_whenUsingArraysDeepToStringToConvert_thenValueOfInnerObjectsAreShown() {
|
||||||
|
Object[] innerArray = { "We", "Are", "Inside" };
|
||||||
|
Object[] arrayOfObjects = { "John", 2, innerArray };
|
||||||
|
assertEquals(Arrays.deepToString(arrayOfObjects), "[John, 2, [We, Are, Inside]]");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenInstanceOfDeepArray_whenUsingStreamsToConvert_thenValueOfObjectsAreShown() {
|
||||||
|
Object[] arrayOfObjects = { "John", 2, true };
|
||||||
|
List<String> listOfString = Stream.of(arrayOfObjects)
|
||||||
|
.map(Object::toString)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
assertEquals(listOfString.toString(), "[John, 2, true]");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,3 +9,4 @@
|
|||||||
- [Differences Between Collection.clear() and Collection.removeAll()](https://www.baeldung.com/java-collection-clear-vs-removeall)
|
- [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)
|
- [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)
|
- [Fail-Safe Iterator vs Fail-Fast Iterator](https://www.baeldung.com/java-fail-safe-vs-fail-fast-iterator)
|
||||||
|
- [Quick Guide to the Java Stack](https://www.baeldung.com/java-stack)
|
||||||
|
|||||||
+66
@@ -0,0 +1,66 @@
|
|||||||
|
package com.baeldung.abaproblem;
|
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
|
public class Account {
|
||||||
|
|
||||||
|
private AtomicInteger balance;
|
||||||
|
private AtomicInteger transactionCount;
|
||||||
|
private ThreadLocal<Integer> currentThreadCASFailureCount;
|
||||||
|
|
||||||
|
public Account() {
|
||||||
|
this.balance = new AtomicInteger(0);
|
||||||
|
this.transactionCount = new AtomicInteger(0);
|
||||||
|
this.currentThreadCASFailureCount = new ThreadLocal<>();
|
||||||
|
this.currentThreadCASFailureCount.set(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getBalance() {
|
||||||
|
return balance.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getTransactionCount() {
|
||||||
|
return transactionCount.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCurrentThreadCASFailureCount() {
|
||||||
|
return currentThreadCASFailureCount.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean withdraw(int amount) {
|
||||||
|
int current = getBalance();
|
||||||
|
maybeWait();
|
||||||
|
boolean result = balance.compareAndSet(current, current - amount);
|
||||||
|
if (result) {
|
||||||
|
transactionCount.incrementAndGet();
|
||||||
|
} else {
|
||||||
|
int currentCASFailureCount = currentThreadCASFailureCount.get();
|
||||||
|
currentThreadCASFailureCount.set(currentCASFailureCount + 1);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void maybeWait() {
|
||||||
|
if ("thread1".equals(Thread.currentThread().getName())) {
|
||||||
|
try {
|
||||||
|
TimeUnit.SECONDS.sleep(2);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean deposit(int amount) {
|
||||||
|
int current = balance.get();
|
||||||
|
boolean result = balance.compareAndSet(current, current + amount);
|
||||||
|
if (result) {
|
||||||
|
transactionCount.incrementAndGet();
|
||||||
|
} else {
|
||||||
|
int currentCASFailureCount = currentThreadCASFailureCount.get();
|
||||||
|
currentThreadCASFailureCount.set(currentCASFailureCount + 1);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+2
-4
@@ -9,13 +9,11 @@ public class StampedAccount {
|
|||||||
private AtomicStampedReference<Integer> account = new AtomicStampedReference<>(0, 0);
|
private AtomicStampedReference<Integer> account = new AtomicStampedReference<>(0, 0);
|
||||||
|
|
||||||
public int getBalance() {
|
public int getBalance() {
|
||||||
return this.account.get(new int[1]);
|
return account.getReference();
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getStamp() {
|
public int getStamp() {
|
||||||
int[] stamps = new int[1];
|
return account.getStamp();
|
||||||
this.account.get(stamps);
|
|
||||||
return stamps[0];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean deposit(int funds) {
|
public boolean deposit(int funds) {
|
||||||
|
|||||||
+98
@@ -0,0 +1,98 @@
|
|||||||
|
package com.baeldung.abaproblem;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNotEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
public class AccountUnitTest {
|
||||||
|
|
||||||
|
private Account account;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
public void setUp() {
|
||||||
|
account = new Account();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void zeroBalanceInitializationTest() {
|
||||||
|
assertEquals(0, account.getBalance());
|
||||||
|
assertEquals(0, account.getTransactionCount());
|
||||||
|
assertEquals(0, account.getCurrentThreadCASFailureCount());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void depositTest() {
|
||||||
|
final int moneyToDeposit = 50;
|
||||||
|
|
||||||
|
assertTrue(account.deposit(moneyToDeposit));
|
||||||
|
|
||||||
|
assertEquals(moneyToDeposit, account.getBalance());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void withdrawTest() throws InterruptedException {
|
||||||
|
final int defaultBalance = 50;
|
||||||
|
final int moneyToWithdraw = 20;
|
||||||
|
|
||||||
|
account.deposit(defaultBalance);
|
||||||
|
|
||||||
|
assertTrue(account.withdraw(moneyToWithdraw));
|
||||||
|
|
||||||
|
assertEquals(defaultBalance - moneyToWithdraw, account.getBalance());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void abaProblemTest() throws InterruptedException {
|
||||||
|
final int defaultBalance = 50;
|
||||||
|
|
||||||
|
final int amountToWithdrawByThread1 = 20;
|
||||||
|
final int amountToWithdrawByThread2 = 10;
|
||||||
|
final int amountToDepositByThread2 = 10;
|
||||||
|
|
||||||
|
assertEquals(0, account.getTransactionCount());
|
||||||
|
assertEquals(0, account.getCurrentThreadCASFailureCount());
|
||||||
|
account.deposit(defaultBalance);
|
||||||
|
assertEquals(1, account.getTransactionCount());
|
||||||
|
|
||||||
|
Thread thread1 = new Thread(() -> {
|
||||||
|
|
||||||
|
// this will take longer due to the name of the thread
|
||||||
|
assertTrue(account.withdraw(amountToWithdrawByThread1));
|
||||||
|
|
||||||
|
// thread 1 fails to capture ABA problem
|
||||||
|
assertNotEquals(1, account.getCurrentThreadCASFailureCount());
|
||||||
|
|
||||||
|
}, "thread1");
|
||||||
|
|
||||||
|
Thread thread2 = new Thread(() -> {
|
||||||
|
|
||||||
|
assertTrue(account.deposit(amountToDepositByThread2));
|
||||||
|
assertEquals(defaultBalance + amountToDepositByThread2, account.getBalance());
|
||||||
|
|
||||||
|
// this will be fast due to the name of the thread
|
||||||
|
assertTrue(account.withdraw(amountToWithdrawByThread2));
|
||||||
|
|
||||||
|
// thread 1 didn't finish yet, so the original value will be in place for it
|
||||||
|
assertEquals(defaultBalance, account.getBalance());
|
||||||
|
|
||||||
|
assertEquals(0, account.getCurrentThreadCASFailureCount());
|
||||||
|
}, "thread2");
|
||||||
|
|
||||||
|
thread1.start();
|
||||||
|
thread2.start();
|
||||||
|
thread1.join();
|
||||||
|
thread2.join();
|
||||||
|
|
||||||
|
// compareAndSet operation succeeds for thread 1
|
||||||
|
assertEquals(defaultBalance - amountToWithdrawByThread1, account.getBalance());
|
||||||
|
|
||||||
|
//but there are other transactions
|
||||||
|
assertNotEquals(2, account.getTransactionCount());
|
||||||
|
|
||||||
|
// thread 2 did two modifications as well
|
||||||
|
assertEquals(4, account.getTransactionCount());
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user