100 Days of Java
- Day 1 - Generating a random number within a specific range.
- Day 2 - Formatting a
LocalDateTime
object. - Day 3 - Scheduling a task to run every 2 seconds.
- Day 4 - Removing items from a List.
- Day 5 - Creating a list with filtered items from other lists.
- Day 6 - Running a task asynchronously.
- Day 7 - Formatting a message using
MessageFormat
. - Day 8 - Creating a thread pool to run tasks simultaneously and reuse threads.
- Day 9 - Creating a valued Enum.
- Day 10 - Using Google’s Jimfs to write tests that use an in-memory file system.
- Day 11 - Sorting a
java.util.Map
by its values. - Day 12 - Using
Callable
andFuture
to run tasks in parallel. - Day 13 - Creating a lazily initialized Singleton.
- Day 14 - Never pass a
double
as argument when constructingBigDecimal
objects. - Day 15 - Builder Pattern
- Day 16 - Joining Strings.
- Day 17 - Splitting Strings.
- Day 18 - Finding the maximum value from a
Collection
. - Day 19 - A
Map
implementation that keeps the elements in the same order that they were inserted. - Day 20 - Reversing a
String
- Day 21 - Using WireMock to mock a web server.
- Day 22 - Mapping and reducing from a
List
. - Day 23 - Loading properties from a file.
- Day 24 - Running operating system commands.
- Day 25 - Pattern Matching for
instanceof
in JDK 16. - Day 26 - Using
Optional
whennull
is a valid return value. - Day 27 - Printing a
List
: one item per line. - Day 28 - Sorting a
List
by a specific attribute. - Day 29 - Using Awaitility to wait for a task to finish.
- Day 30 - Creating multi-line Strings.
- Day 31 - Converting a
Stream
toList
on JDK 16. - Day 32 - Using switch to return a value.
- Day 33 - Start using
Optional#orElseThrow
instead ofOptional#get
. - Day 34 - Printing information about all java processes running on the machine.
- Day 35 - Autoclosing resources (try-with-resources).
- Day 36 - Using
javax.annotation.Nonnull
(JSR 305) to avoidNullPointerException
. - Day 37 - Using
Objects.requireNonNullElse
whenOptional
is not an option. - Day 38 - Supplying a default value in case of timeout when running an async task.
- Day 39 - Understanding the Prefix (i) and Postfix (i) Operators
- Day 40 - Filtering a Stream using
Predicate#not
. - Day 41 - Quiz about
Collections#unmodifiableList
. - Day 42 - Using
jinfo
to update manageable VM flags at runtime. - Day 43 - Indenting a String.
- Day 44 - Formatting Strings with
java.lang.String#formatted
. - Day 45 - Using
java.util.Comparator#comparing
to sort aList
. - Day 46 - Using
Function
to map a List from one type to another. - Day 47 - Creating a
FunctionalInterface
. - Day 48 - Don’t Repeat Yourself: Reusing functions with
Function#andThen
. - Day 49 -
Function#identity
: Creating a function that always returns its input argument. - Day 50 - Creating a
Stream
from a range of Integers. - Day 51 - Using
UnaryOperator
. - Day 52 - Making
IOException
unchecked. - Day 53 - Using
Supplier
to run an expensive object creation only when and if needed - Day 54 - Computing
Map
values if absent. - Day 55 - Creating smart and readable assertions with AssertJ
- Day 56 - Printing colored characters on the console
- Day 57 - Using Picocli to parse command line arguments
- Day 58 - Never write code that depends on
toString()
format - Day 59 - Using a
Predicate
.
A little of Java content every day for a hundred days.
If you have any questions, ask me on my social media.
Day 1 - Generating a random number within a specific range.
import java.security.SecureRandom;
public final class Day001 {
public static final SecureRandom SECURE_RANDOM = new SecureRandom();
public static void main(String[] args) {
System.out.println("Generating a number between 50 and 100...");
System.out.println(randomNumberBetween(50, 100));
}
private static int randomNumberBetween(int minimum, int maximum) {
return SECURE_RANDOM.nextInt(maximum - minimum) + minimum;
}
}
LocalDateTime
object.
Day 2 - Formatting a import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
public final class Day002 {
private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm:ss");
public static void main(String[] args) {
LocalDateTime currentDateTime = LocalDateTime.now();
String formattedDateTime = currentDateTime.format(FORMATTER);
System.out.println(formattedDateTime);
}
}
Day 3 - Scheduling a task to run every 2 seconds.
import java.time.LocalTime;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class Day003 {
private final ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
public static void main(String[] args) throws InterruptedException {
var day003 = new Day003();
day003.printCurrentTimeEvery2Seconds();
Thread.sleep(15_000);
day003.stopPrinting();
}
public void printCurrentTimeEvery2Seconds() {
Runnable task = () -> System.out.println(LocalTime.now());
scheduledExecutorService.scheduleAtFixedRate(task, 0, 2, TimeUnit.SECONDS);
}
public void stopPrinting() {
scheduledExecutorService.shutdown();
}
}
Day 4 - Removing items from a List.
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
public class Day004 {
public static void main(String[] args) {
List<Person> beatles = new ArrayList<>();
beatles.add(new Person("1", "John Lennon", LocalDate.of(1940, 10, 9)));
beatles.add(new Person("2", "Paul McCartney", LocalDate.of(1942, 6, 18)));
beatles.add(new Person("3", "George Harrison", LocalDate.of(1943, 2, 25)));
beatles.add(new Person("4", "Ringo Starr", LocalDate.of(1940, 7, 7)));
removeItemUsingEquals(beatles);
removeItemUsingAnSpecificFilter(beatles);
System.out.println(beatles);
}
private static void removeItemUsingAnSpecificFilter(List<Person> beatles) {
beatles.removeIf(person -> "George Harrison".equals(person.getName()));
}
private static void removeItemUsingEquals(List<Person> beatles) {
var lennon = new Person("1", "John Lennon", LocalDate.of(1940, 10, 9));
beatles.remove(lennon);
}
static class Person {
private final String id;
private final String name;
private final LocalDate dateOfBirth;
Person(String id, String name, LocalDate dateOfBirth) {
this.id = id;
this.name = name;
this.dateOfBirth = dateOfBirth;
}
public String getId() {
return id;
}
public String getName() {
return name;
}
public LocalDate getDateOfBirth() {
return dateOfBirth;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
var person = (Person) o;
return Objects.equals(id, person.id) && Objects.equals(name, person.name) && Objects.equals(dateOfBirth, person.dateOfBirth);
}
@Override
public int hashCode() {
return Objects.hash(id, name, dateOfBirth);
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
'}';
}
}
}
Day 5 - Creating a list with filtered items from other lists.
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class Day005 {
private static final String GUITAR = "Guitar";
private static final String DRUMS = "Drums";
private static final String BASS = "Bass";
private static final String VOCALS = "Vocals";
private static final String KEYBOARDS = "Keyboards";
public static void main(String[] args) {
List<BandMember> pinkFloyd = List.of(
new BandMember("David Gilmour", GUITAR),
new BandMember("Roger Waters", BASS),
new BandMember("Richard Wright", KEYBOARDS),
new BandMember("Nick Mason", DRUMS)
);
List<BandMember> ironMaiden = List.of(
new BandMember("Bruce Dickinson", VOCALS),
new BandMember("Steve Harris", BASS),
new BandMember("Adrian Smith", GUITAR),
new BandMember("Dave Murray", GUITAR),
new BandMember("Nicko McBrain", DRUMS)
);
List<BandMember> blackSabbath = List.of(
new BandMember("Ozzy Osbourne", VOCALS),
new BandMember("Geezer Butler", BASS),
new BandMember("Toni Iommi", GUITAR),
new BandMember("Bill Ward", DRUMS)
);
Stream<BandMember> musicians = Stream.concat(Stream.concat(pinkFloyd.stream(), ironMaiden.stream()), blackSabbath.stream());
List<String> guitarPlayers = musicians.filter(bandMember -> GUITAR.equals(bandMember.instrument))
.map(BandMember::name)
.collect(Collectors.toList());
System.out.println(guitarPlayers);
}
static record BandMember(String name, String instrument) {
}
}
Day 6 - Running a task asynchronously.
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ForkJoinPool;
import java.util.logging.Level;
import java.util.logging.Logger;
import static java.util.concurrent.TimeUnit.SECONDS;
public class Day006 {
private static final Logger LOGGER = Logger.getLogger(Day006.class.getName());
public static void main(String[] args) {
CompletableFuture.runAsync(Day006::task);
LOGGER.info("Message from the main thread. Note that this message is logged before the async task ends.");
LOGGER.info("Waiting for the async task to end.");
boolean isQuiecent = ForkJoinPool.commonPool().awaitQuiescence(5, SECONDS);
if (isQuiecent) {
LOGGER.info("Async task ended.");
} else {
LOGGER.log(Level.SEVERE, "The async task is taking too long to finish. This program will end anyway.");
}
}
private static void task() {
LOGGER.info("Async task starting. This message is logged by the async task thread");
try {
Thread.sleep(1000);
LOGGER.info("Async task is ending. This message is logged by the async task thread");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
LOGGER.log(Level.SEVERE, "The async task thread was interrupted.", e);
}
}
}
MessageFormat
.
Day 7 - Formatting a message using import java.text.MessageFormat;
public class Day007 {
public static void main(String[] args) {
showMessage("Java", "is", "great");
}
private static void showMessage(String param1, String param2, String param3) {
String message = MessageFormat.format("This message contains 3 parameters. The #1 is ''{0}'', the #2 is ''{1}'', and the #3 is ''{2}''.",
param1, param2, param3);
System.out.println(message);
}
}
Day 8 - Creating a thread pool to run tasks simultaneously and reuse threads.
import java.security.SecureRandom;
import java.text.MessageFormat;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.logging.Logger;
public class Day008 {
private static final Logger LOGGER = Logger.getLogger(Day008.class.getName());
private static final SecureRandom RANDOM = new SecureRandom();
public static void main(String[] args) {
LOGGER.info("Creating a thread pool with 5 threads");
ExecutorService executor = Executors.newFixedThreadPool(5);
/*
* Will submit 15 tasks. Note that there's only 5 threads to run all of them in our thread pool.
* So the first 5 tasks will run simultaneously and 10 tasks will wait in the queue until a thread is available.
*/
LOGGER.info("Starting tasks submissions.");
try {
for (var i = 1; i <= 15; i++) {
int taskId = i;
LOGGER.info(() -> MessageFormat.format("Will submit task {0}.", taskId));
executor.submit(() -> task(taskId));
}
} finally {
executor.shutdown();
}
}
private static void task(int taskId) {
LOGGER.info(() -> MessageFormat.format("Running task {0}.", taskId));
simulateLongProcessing();
LOGGER.info(() -> MessageFormat.format("Task {0} has finished.", taskId));
}
private static void simulateLongProcessing() {
try {
Thread.sleep((RANDOM.nextInt(3) + 10) * 1000L);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException(MessageFormat.format("Thread {0} was interrupted.", Thread.currentThread().getName()), e);
}
}
}
Day 9 - Creating a valued Enum.
public class Day009 {
public static void main(String[] args) {
for (Gender gender : Gender.values()) {
System.out.printf("The value of %s is %s%n", gender, gender.getValue());
}
}
public enum Gender {
FEMALE('f'),
MALE('m');
private final char value;
Gender(char value) {
this.value = value;
}
public char getValue() {
return value;
}
}
}
Day 10 - Using Google’s Jimfs to write tests that use an in-memory file system.
import com.google.common.jimfs.Configuration;
import com.google.common.jimfs.Jimfs;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.nio.file.*;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatCode;
class Day010Test {
@Test
void fileDoesNotExist() {
FileSystem fileSystem = Jimfs.newFileSystem(Configuration.unix());
Path directory = fileSystem.getPath("/directory");
Path file = directory.resolve(fileSystem.getPath("myfile.txt"));
assertThatCode(() -> Files.write(file, "thegreatapi.com".getBytes(), StandardOpenOption.WRITE))
.isInstanceOf(NoSuchFileException.class);
}
@Test
void fileExists() throws IOException {
FileSystem fileSystem = Jimfs.newFileSystem(Configuration.unix());
Path directory = fileSystem.getPath("/directory");
Path file = directory.resolve(fileSystem.getPath("myfile.txt"));
Files.createDirectory(directory);
Files.createFile(file);
assertThatCode(() -> Files.write(file, "thegreatapi.com".getBytes(), StandardOpenOption.WRITE))
.doesNotThrowAnyException();
assertThat(Files.readString(file))
.isEqualTo("thegreatapi.com");
}
}
java.util.Map
by its values.
Day 11 - Sorting a import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.TreeMap;
public class Day011 {
public static void main(String[] args) {
Map<String, Integer> unsortedMap = Map.of(
"three", 3,
"one", 1,
"four", 4,
"five", 5,
"two", 2
);
Map<String, Integer> sortedMap = sortByValue(unsortedMap);
System.out.println(sortedMap);
}
private static Map<String, Integer> sortByValue(Map<String, Integer> unsortedMap) {
TreeMap<Integer, String> treeMap = new TreeMap<>();
unsortedMap.forEach((key, value) -> treeMap.put(value, key));
Map<String, Integer> sortedMap = new LinkedHashMap<>();
treeMap.forEach((key, value) -> sortedMap.put(value, key));
return Collections.unmodifiableMap(sortedMap);
}
}
Callable
and Future
to run tasks in parallel.
Day 12 - Using import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.logging.Logger;
public class Day012 {
private static final Logger LOGGER = Logger.getLogger(Day012.class.getName());
public static void main(String[] args) throws InterruptedException {
var executorService = Executors.newSingleThreadExecutor();
try {
Callable<Integer> callable = Day012::doALongCalculation;
Future<Integer> future = executorService.submit(callable);
doOtherThingWhileCalculating();
LOGGER.info("Will get the calculated value. Note that the value will be get immediately");
LOGGER.info("Calculated value: " + future.get());
} catch (ExecutionException e) {
e.printStackTrace();
} finally {
executorService.shutdown();
}
}
private static int doALongCalculation() throws InterruptedException {
Thread.sleep(5000L);
return 42;
}
private static void doOtherThingWhileCalculating() throws InterruptedException {
Thread.sleep(7000L);
}
}
Day 13 - Creating a lazily initialized Singleton.
import java.time.LocalDateTime;
public final class MySingletonClass {
private final LocalDateTime creationDateTime;
private MySingletonClass(LocalDateTime creationDateTime) {
this.creationDateTime = creationDateTime;
}
public LocalDateTime getCreationDateTime() {
return creationDateTime;
}
public static MySingletonClass getInstance() {
return InstanceHolder.INSTANCE;
}
private static final class InstanceHolder {
static final MySingletonClass INSTANCE = new MySingletonClass(LocalDateTime.now());
}
}
double
as argument when constructing BigDecimal
objects.
Day 14 - Never pass a import java.math.BigDecimal;
public class Day014 {
public static void main(String[] args) {
// Prints 1.229999999999999982236431605997495353221893310546875
System.out.println(new BigDecimal(1.23));
// Prints 1.23
System.out.println(new BigDecimal("1.23"));
// Prints 1.23
System.out.println(BigDecimal.valueOf(1.23));
}
}
Day 15 - Builder Pattern
import javax.annotation.Nullable;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
public class Day015 {
public static void main(String[] args) {
Person john = Person.builder()
.name("John")
.children(List.of(
Person.builder()
.name("Amanda")
.petName("Toto")
.build()
))
.build();
System.out.println(john);
}
public static class Person {
private final String name;
private final List<Person> children;
@Nullable
private final String petName;
private Person(Builder builder) {
name = Objects.requireNonNull(builder.name);
children = builder.children != null ? builder.children : List.of();
petName = builder.petName;
}
public String getName() {
return name;
}
public List<Person> getChildren() {
return children;
}
@Nullable
public String getPetName() {
return petName;
}
public static Builder builder() {
return new Builder();
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", children=" + children +
", petName='" + petName + '\'' +
'}';
}
}
public static final class Builder {
private String name;
private List<Person> children;
@Nullable
private String petName;
private Builder() {
}
public Builder name(String name) {
this.name = name;
return this;
}
public Builder children(List<Person> children) {
this.children = Collections.unmodifiableList(children);
return this;
}
public Builder petName(String petName) {
this.petName = petName;
return this;
}
public Person build() {
return new Person(this);
}
}
}
Day 16 - Joining Strings.
public class Day016 {
public static void main(String[] args) {
System.out.println(createSql("id", "name", "coutry", "gender"));
}
private static String createSql(String... columns) {
return new StringBuilder("SELECT ")
.append(String.join(", ", columns))
.append(" FROM PEOPLE")
.toString();
}
}
Day 17 - Splitting Strings.
import java.util.regex.Pattern;
public class Day017 {
private static final Pattern REGEX = Pattern.compile(", ");
public static void main(String[] args) {
System.out.println("Simple split: ");
for (String column : simpleSplit()) {
System.out.println(column);
}
System.out.println("Performant split: ");
for (String column : performantSplit()) {
System.out.println(column);
}
}
private static String[] simpleSplit() {
return "id, name, country, gender".split(", ");
}
// If you will split frequently, prefer this implementation.
private static String[] performantSplit() {
return REGEX.split("id, name, country, gender");
}
}
Collection
.
Day 18 - Finding the maximum value from a import java.util.Collection;
import java.util.List;
import java.util.NoSuchElementException;
public class Day018 {
public static void main(String[] args) {
System.out.println(max(List.of(6, 3, 1, 8, 3, 9, 2, 7)));
}
private static Integer max(Collection<Integer> collection) {
return collection.stream()
.max(Integer::compareTo)
.orElseThrow(NoSuchElementException::new);
}
}
Map
implementation that keeps the elements in the same order that they were inserted.
Day 19 - A import java.util.LinkedHashMap;
import java.util.Map;
public class Day019 {
public static void main(String[] args) {
Map<Integer, String> map = new LinkedHashMap<>();
map.put(5, "five");
map.put(4, "four");
map.put(3, "three");
map.put(2, "two");
map.put(1, "one");
map.forEach((key, value) -> System.out.println(key + ": " + value));
}
}
String
Day 20 - Reversing a public class Day020 {
public static void main(String[] args) {
var original = "moc.ipataergeht";
var reversed = new StringBuilder(original).reverse().toString();
System.out.println(reversed);
}
}
Day 21 - Using WireMock to mock a web server.
import com.github.tomakehurst.wiremock.WireMockServer;
import com.github.tomakehurst.wiremock.core.WireMockConfiguration;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import static com.github.tomakehurst.wiremock.client.WireMock.get;
import static com.github.tomakehurst.wiremock.client.WireMock.ok;
import static org.junit.jupiter.api.Assertions.assertEquals;
class Day021Test {
private WireMockServer server;
@BeforeEach
void setUp() {
server = new WireMockServer(WireMockConfiguration.wireMockConfig().dynamicPort());
server.start();
}
@Test
void test() throws Exception {
mockWebServer();
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("http://localhost:" + server.port() + "/my/resource"))
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
assertEquals("TheGreatAPI.com", response.body());
}
private void mockWebServer() {
server.stubFor(get("/my/resource")
.willReturn(ok()
.withBody("TheGreatAPI.com")));
}
@AfterEach
void tearDown() {
server.shutdownServer();
}
}
List
.
Day 22 - Mapping and reducing from a import java.util.List;
public class Day022 {
public static void main(String[] args) {
List<Order> orders = readOrders();
String bands = orders.stream()
.map(Order::customer)
.map(Customer::band)
.reduce((band1, band2) -> String.join(";", band1, band2))
.orElse("None");
System.out.println(bands);
/* Prints:
Pink Floyd;Black Sabbath;Ozzy Osbourne
*/
}
private static List<Order> readOrders() {
var gilmour = new Customer("David Gilmour", "Pink Floyd");
var iommi = new Customer("Toni Iommi", "Black Sabbath");
var rhoads = new Customer("Randy Rhoads", "Ozzy Osbourne");
var strato = new Product("Fender", "Stratocaster");
var sg = new Product("Gibson", "SG");
var lesPaul = new Product("Gibson", "Les Paul");
var rr = new Product("Jackson", "RR");
return List.of(
new Order(gilmour, List.of(strato)),
new Order(iommi, List.of(sg)),
new Order(rhoads, List.of(lesPaul, rr))
);
}
static record Customer(String name, String band) {
}
static record Product(String brand, String modelName) {
}
static record Order(Customer customer, List<Product> products) {
}
}
Day 23 - Loading properties from a file.
import java.io.IOException;
import java.util.Properties;
public class Day023 {
public static void main(String[] args) throws IOException {
var properties = new Properties();
try (var reader = Day023.class.getClassLoader().getResourceAsStream("config.properties")) {
properties.load(reader);
}
System.out.println(properties);
}
}
Day 24 - Running operating system commands.
package com.thegreatapi.ahundreddaysofjava.day024;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import static java.util.concurrent.TimeUnit.SECONDS;
public class Day024 {
public static void main(String[] args) throws IOException, InterruptedException {
var process = new ProcessBuilder("ls").start();
try (var stdOutReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
var stdErrReader = new BufferedReader(new InputStreamReader(process.getErrorStream()))) {
if (process.waitFor(5, SECONDS)) {
int exitValue = process.exitValue();
if (exitValue == 0) {
stdOutReader.lines().forEach(System.out::println);
} else {
stdErrReader.lines().forEach(System.err::println);
}
} else {
throw new RuntimeException("Timeout");
}
}
}
}
instanceof
in JDK 16.
Day 25 - Pattern Matching for public class Day025 {
public static void main(String[] args) {
Number n = 6;
// Instead of doing:
if (n instanceof Integer) {
Integer i = (Integer) n;
print(i);
}
// Just do:
if (n instanceof Integer i) {
print(i);
}
}
private static void print(Integer i) {
System.out.println(i);
}
}
Optional
when null
is a valid return value.
Day 26 - Using import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.Optional;
public class Day026 {
public static void main(String[] args) {
// Instead of doing:
String nullableValue = getNullableValue();
if (nullableValue != null) {
System.out.println(nullableValue.length());
} else {
System.out.println(0);
}
// Just do:
System.out.println(getOptionalValue().map(String::length).orElse(0));
}
@Nonnull
private static Optional<String> getOptionalValue() {
return Optional.empty();
}
@Nullable
private static String getNullableValue() {
return null;
}
}
List
: one item per line.
Day 27 - Printing a import java.util.List;
public class Day027 {
public static void main(String[] args) {
List<Player> players = createList();
String message = players.stream()
.map(Player::toString)
.reduce((p1, p2) -> p1 + System.lineSeparator() + p2)
.orElse("");
System.out.println(message);
}
private static List<Player> createList() {
Player messi = new Player("Lionel Messi", "Barcelona", "Argentina", 42);
Player cr7 = new Player("Cristiano Ronaldo", "Juventus", "Portugal", 50);
Player neymar = new Player("Neymar Jr.", "PSG", "Brazil", 41);
return List.of(messi, cr7, neymar);
}
private record Player(String name, String club, String coutry, int numberOfGoals) {
}
}
List
by a specific attribute.
Day 28 - Sorting a import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
public class Day028 {
public static void main(String[] args) {
Player messi = new Player("Lionel Messi", "Barcelona", "Argentina", 42);
Player cr7 = new Player("Cristiano Ronaldo", "Juventus", "Portugal", 50);
Player neymar = new Player("Neymar Jr.", "PSG", "Brazil", 41);
List<Player> players = Arrays.asList(messi, cr7, neymar);
players.sort(Comparator.comparing(Player::numberOfGoals).reversed());
System.out.println("Top Scorers:");
players.forEach(System.out::println);
}
private record Player(String name, String club, String coutry, int numberOfGoals) {
}
}
Awaitility to wait for a task to finish.
Day 29 - Usingpackage com.thegreatapi.ahundreddaysofjava.day029;
import org.junit.jupiter.api.Test;
import java.util.concurrent.CompletableFuture;
import static org.awaitility.Awaitility.await;
import static org.junit.jupiter.api.Assertions.assertEquals;
class Day029Test {
@Test
void test() {
Day029 day029 = new Day029();
CompletableFuture.runAsync(day029::startComputingPrimes);
// Await until the already computed primes contain the key 100_000
await().until(() -> day029.getAlreadyComputedPrimes().containsKey(100_000));
assertEquals(1299709, day029.getAlreadyComputedPrimes().get(100_000));
}
}
package com.thegreatapi.ahundreddaysofjava.day029;
import java.util.Collections;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.IntStream;
public class Day029 {
private final Map<Integer, Integer> primes = new ConcurrentHashMap<>();
public void startComputingPrimes() {
var count = 0;
for (var i = 2; i <= Integer.MAX_VALUE; i++) {
if (isPrime(i)) {
primes.put(++count, i);
}
}
}
private static boolean isPrime(int number) {
return IntStream.rangeClosed(2, (int) Math.sqrt(number))
.allMatch(n -> number % n != 0);
}
public Map<Integer, Integer> getAlreadyComputedPrimes() {
return Collections.unmodifiableMap(primes);
}
}
Day 30 - Creating multi-line Strings.
public class Day030 {
public static void main(String[] args) {
// Requires JDK 15 or JDK 13 with Preview Features enabled
var myString = """
This is a
text block of
multiple lines.
""";
System.out.println(myString);
var myIndentedString = """
And this is
a text block with
indentation:
public String getMessage() {
if (LocalTime.now().isAfter(LocalTime.of(12, 0))) {
return "Good afternoon";
} else {
return "Good morning";
}
}
""";
System.out.println(myIndentedString);
}
}
Stream
to List
on JDK 16.
Day 31 - Converting a package com.thegreatapi.ahundreddaysofjava.day031;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class Day031 {
public static void main(String[] args) {
// Instead of doing:
List<String> list = Stream.of("the", "great", "api", ".com")
.collect(Collectors.toList());
// Just do:
List<String> listJdk16 = Stream.of("the", "great", "api", ".com")
.toList();
}
}
Day 32 - Using switch to return a value.
package com.thegreatapi.ahundreddaysofjava.day032;
import java.security.SecureRandom;
public class Day032 {
public static void main(String[] args) {
String result = map(randomNumber());
System.out.println(result);
}
private static String map(int number) {
// Requires JDK 12
return switch (number) {
case 1 -> "one";
case 2 -> "two";
case 3 -> "three";
default -> "unknown";
};
}
private static int randomNumber() {
return new SecureRandom().nextInt(4);
}
}
Optional#orElseThrow
instead of Optional#get
.
Day 33 - Start using package com.thegreatapi.ahundreddaysofjava.day033;
import java.time.LocalTime;
import java.util.Optional;
public class Day033 {
public static void main(String[] args) {
Optional<LocalTime> optionalValue = getOptionalValue();
// Stop using Optional#get.
// It will be deprecated soon, as you can see in https://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-8160606
System.out.println(optionalValue.get());
// Start using Optional#orElseThrow instead of Optional#get
System.out.println(getOptionalValue().orElseThrow());
}
private static Optional<LocalTime> getOptionalValue() {
return Optional.of(LocalTime.now());
}
}
Day 34 - Printing information about all java processes running on the machine.
package com.thegreatapi.ahundreddaysofjava.day034;
import java.io.File;
public class Day034 {
public static final String JAVA_SUFFIX = File.separator + "java";
public static void main(String[] args) {
ProcessHandle.allProcesses()
.filter(Day034::isJavaProcess)
.map(ProcessHandle::info)
.forEach(System.out::println);
}
private static boolean isJavaProcess(ProcessHandle processHandle) {
return processHandle.info()
.command()
.map(command -> command.endsWith(JAVA_SUFFIX))
.orElse(false);
}
}
Day 35 - Autoclosing resources (try-with-resources).
package com.thegreatapi.ahundreddaysofjava.day035;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class Day035 {
public static void main(String[] args) throws IOException {
String path = args[0];
// Instead of doing:
var bufferedReader = new BufferedReader(new FileReader(path));
try {
String line = bufferedReader.readLine();
System.out.println(line);
} finally {
bufferedReader.close();
}
// Just do:
try (var autoClosedBufferedReader = new BufferedReader(new FileReader(path))) {
String line = autoClosedBufferedReader.readLine();
System.out.println(line);
}
}
}
javax.annotation.Nonnull
(JSR 305) to avoid NullPointerException
.
Day 36 - Using package com.thegreatapi.ahundreddaysofjava.day036;
import javax.annotation.Nonnull;
public final class Day036 {
private Day036() {
}
public static void main(String[] args) {
printLenght(null);
}
public static void printLenght(@Nonnull String s) {
System.out.println(s.length());
}
}
Objects.requireNonNullElse
when Optional
is not an option.
Day 37 - Using package com.thegreatapi.ahundreddaysofjava.day037;
import javax.annotation.Nullable;
import java.util.Objects;
public class Day037 {
public static void main(String[] args) {
String s = Objects.requireNonNullElse(doStuff(), "not found");
// Will print 'not found'
System.out.println(s);
}
@Nullable
private static String doStuff() {
return null;
}
}
Day 38 - Supplying a default value in case of timeout when running an async task.
package com.thegreatapi.ahundreddaysofjava.day038;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import static java.util.concurrent.TimeUnit.SECONDS;
public class Day038 {
public static void main(String[] args) throws InterruptedException, ExecutionException {
String webSite = CompletableFuture.supplyAsync(Day038::getWebSite)
.completeOnTimeout("https://twitter.com/helber_belmiro", 5, SECONDS)
.get();
System.out.println(webSite);
}
private static String getWebSite() {
try {
Thread.sleep(10_000);
return "thegreatapi.com";
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException(e);
}
}
}
Day 39 - Understanding the Prefix (i) and Postfix (i) Operators
I did a blog post for that: https://thegreatapi.com/blog/prefix-and-postfix-operators/
Predicate#not
.
Day 40 - Filtering a Stream using package com.thegreatapi.ahundreddaysofjava.day040;
import java.util.function.Predicate;
import java.util.stream.Stream;
import static java.util.function.Predicate.not;
public class Day040 {
public static void main(String[] args) {
// Instead of doing:
printAllThat(word -> !word.isEmpty());
// Just do:
printAllThat(not(String::isEmpty));
}
private static void printAllThat(Predicate<String> filter) {
Stream.of("avocado", "chair", "", "dog", "car")
.filter(filter)
.forEach(System.out::println);
}
}
Collections#unmodifiableList
.
Day 41 - Quiz about Given the following:
package com.thegreatapi.ahundreddaysofjava.day041;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class Day041 {
public static void main(String[] args) {
List<String> originalList = new ArrayList<>();
originalList.add("one");
originalList.add("two");
originalList.add("three");
List<String> copy = Collections.unmodifiableList(originalList);
originalList.remove("two");
System.out.println(String.join(" ", copy));
}
}
What will be printed?
a) one two tree
b) one three
c) Exception at originalList.remove("two");
d) Exception at String.join(" ", copy)
e) Compilation error
jinfo
to update manageable VM flags at runtime.
Day 42 - Using In this article, Vipin Sharma explains how to use the utility jinfo
, which is part of JDK. It’s pretty useful when you need to set HeapDumpOnOutOfMemoryError to investigate a memory leak, for example.
Day 43 - Indenting a String.
package com.thegreatapi.ahundreddaysofjava.day043;
public class Day043 {
public static void main(String[] args) {
var methodCode = """
private static void task() {
LOGGER.info("Async task starting. This message is logged by the async task thread");
try {
Thread.sleep(1000);
LOGGER.info("Async task is ending. This message is logged by the async task thread");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
LOGGER.log(Level.SEVERE, "The async task thread was interrupted.", e);
}
}
""";
var classCode = """
public class MyClass {
%s
}
""";
// Requires JDK 12
String fullCode = classCode.formatted(methodCode.indent(4));
System.out.println(fullCode);
}
}
java.lang.String#formatted
.
Day 44 - Formatting Strings with package com.thegreatapi.ahundreddaysofjava.day044;
public class Day044 {
public static final String NAME = "Helber Belmiro";
public static void main(String[] args) {
String formattedString;
// Instead of doing:
formattedString = String.format("My name is %s", NAME);
// Just do: (Requires JDK 15)
formattedString = "My name is %s".formatted(NAME);
System.out.println(formattedString);
}
}
java.util.Comparator#comparing
to sort a List
.
Day 45 - Using package com.thegreatapi.ahundreddaysofjava.day045;
import java.time.LocalDate;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
public class Day045 {
public static void main(String[] args) {
List<Musician> queen = getMusicians();
// Instead of doing:
queen.sort(new Comparator<Musician>() {
@Override
public int compare(Musician m1, Musician m2) {
return m1.dateOfBirth.compareTo(m2.dateOfBirth);
}
});
System.out.println(queen);
// Just do:
queen.sort(Comparator.comparing(Musician::dateOfBirth));
System.out.println(queen);
}
private static List<Musician> getMusicians() {
Musician roger = new Musician("Roger Taylor", LocalDate.of(1949, 7, 26));
Musician john = new Musician("John Deacon", LocalDate.of(1951, 8, 19));
Musician brian = new Musician("Brian May", LocalDate.of(1947, 7, 19));
Musician freddie = new Musician("Freddie Mercury", LocalDate.of(1946, 9, 5));
return Arrays.asList(roger, john, brian, freddie);
}
record Musician(String name, LocalDate dateOfBirth) {
}
}
Function
to map a List from one type to another.
Day 46 - Using package com.thegreatapi.ahundreddaysofjava.day046;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class Day046 {
public static void main(String[] args) {
List<Musician> queen = getMusicians();
List<Integer> years;
// Instead of doing:
years = new ArrayList<>();
for (Musician musician : queen) {
years.add(musician.dateOfBirth.getYear());
}
System.out.println(years);
// Just do:
years = queen.stream()
.map(musician -> musician.dateOfBirth.getYear())
.collect(Collectors.toList());
System.out.println(years);
}
private static List<Musician> getMusicians() {
Musician roger = new Musician("Roger Taylor", LocalDate.of(1949, 7, 26));
Musician john = new Musician("John Deacon", LocalDate.of(1951, 8, 19));
Musician brian = new Musician("Brian May", LocalDate.of(1947, 7, 19));
Musician freddie = new Musician("Freddie Mercury", LocalDate.of(1946, 9, 5));
return Arrays.asList(roger, john, brian, freddie);
}
record Musician(String name, LocalDate dateOfBirth) {
}
}
FunctionalInterface
.
Day 47 - Creating a package com.thegreatapi.ahundreddaysofjava.day047;
public class Day047 {
@FunctionalInterface
interface Converter {
// Because of the @FunctionalInterface annotation, only one method is allowed in this interface
String convert(Integer number);
}
public static void main(String[] args) {
for (var i = 1; i <= 4; i++) {
System.out.println(i + " in english: " + englishConverter().convert(i));
System.out.println(i + " in portuguese: " + portugueseConverter().convert(i));
System.out.println(i + " in german: " + germanConverter().convert(i));
}
}
private static Converter germanConverter() {
return number -> {
switch (number) {
case 1:
return "eins";
case 2:
return "zwei";
case 3:
return "drei";
case 4:
return "vier";
default:
throw new UnsupportedOperationException();
}
};
}
private static Converter portugueseConverter() {
return number -> {
switch (number) {
case 1:
return "um";
case 2:
return "dois";
case 3:
return "três";
case 4:
return "quatro";
default:
throw new UnsupportedOperationException();
}
};
}
private static Converter englishConverter() {
return number -> {
switch (number) {
case 1:
return "one";
case 2:
return "two";
case 3:
return "three";
case 4:
return "four";
default:
throw new UnsupportedOperationException();
}
};
}
}
Function#andThen
.
Day 48 - Don’t Repeat Yourself: Reusing functions with package com.thegreatapi.ahundreddaysofjava.day048;
import java.util.Comparator;
import java.util.List;
import java.util.function.Function;
import java.util.function.UnaryOperator;
public class Day048 {
public static void main(String[] args) {
System.out.println(lastAlbumWith("Slash").apply(getGunsNRosesAlbums()).title());
System.out.println(lastAlbumWith("Slash", "Izzy Stradlin").apply(getGunsNRosesAlbums()).title());
System.out.println(firstAlbumWith("Matt Sorum").apply(getGunsNRosesAlbums()).title());
/*
Output:
The Spaghetti Incident
Use Your Illusion II
Use Your Illusion I
*/
}
private static Function<List<Album>, Album> firstAlbumWith(String... bandMembers) {
return albumsWith(bandMembers).andThen(sortByYear())
.andThen(firstAlbum());
}
private static Function<List<Album>, Album> lastAlbumWith(String... bandMembers) {
return albumsWith(bandMembers).andThen(sortByYear())
.andThen(lastAlbum());
}
private static Function<List<Album>, Album> lastAlbum() {
return albums -> albums.get(albums.size() - 1);
}
private static Function<List<Album>, Album> firstAlbum() {
return albums -> albums.get(0);
}
private static UnaryOperator<List<Album>> sortByYear() {
return albums -> albums.stream()
.sorted(Comparator.comparing(Album::year))
.toList();
}
private static Function<List<Album>, List<Album>> albumsWith(String... bandMembers) {
if (bandMembers.length < 1) {
throw new IllegalArgumentException("");
} else {
Function<List<Album>, List<Album>> resultFunction = albums -> albums;
for (String bandMember : bandMembers) {
resultFunction = resultFunction.andThen(albumsWith(bandMember));
}
return resultFunction;
}
}
private static UnaryOperator<List<Album>> albumsWith(String bandMember) {
return albums -> albums.stream()
.filter(album -> album.lineup().contains(bandMember))
.toList();
}
private static List<Album> getGunsNRosesAlbums() {
List<String> lineup87to90 = List.of("Axl Rose", "Slash", "Izzy Stradlin", "Duff McKagan", "Steven Adler");
List<String> lineup91 = List.of("Axl Rose", "Slash", "Izzy Stradlin", "Duff McKagan", "Matt Sorum", "Dizzy Reed");
List<String> lineup91to93 = List.of("Axl Rose", "Slash", "Gilby Clarke", "Duff McKagan", "Matt Sorum", "Dizzy Reed");
List<String> lineup2008 = List.of("Axl Rose", "Bumblefoot", "Richard Fortus", "Tommy Stinson", "Frank Ferrer", "Chris Pitman", "Dizzy Reed");
return List.of(
new Album("Appetite for Destruction", lineup87to90, 1987),
new Album("G N' R Lies", lineup87to90, 1988),
new Album("Use Your Illusion I", lineup91, 1991),
new Album("Use Your Illusion II", lineup91, 1991),
new Album("The Spaghetti Incident", lineup91to93, 1993),
new Album("Chinese Democracy", lineup2008, 2008)
);
}
private record Album(String title, List<String> lineup, int year) {
}
}
Function#identity
: Creating a function that always returns its input argument.
Day 49 - package com.thegreatapi.ahundreddaysofjava.day049;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
public class Day049 {
public static void main(String[] args) {
Map<Integer, String> portugueseNumbers = translateToPortuguese(englishNumbers());
System.out.println(portugueseNumbers);
}
private static Map<Integer, String> translateToPortuguese(Map<Integer, String> numbers) {
/*
Instead of doing:
Function<Integer, Integer> keyMapper = number -> number;
*/
// Just do:
Function<Integer, Integer> keyMapper = Function.identity();
Function<Integer, String> valueMapper = number -> switch (number) {
case 1 -> "um";
case 2 -> "dois";
case 3 -> "três";
case 4 -> "quatro";
default -> throw new UnsupportedOperationException("Cannot translate %d".formatted(number));
};
return numbers.keySet()
.stream()
.collect(Collectors.toMap(keyMapper, valueMapper));
}
private static Map<Integer, String> englishNumbers() {
return Map.of(
1, "one",
2, "two",
3, "three",
4, "four"
);
}
}
Stream
from a range of Integers.
Day 50 - Creating a package com.thegreatapi.ahundreddaysofjava.day050;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.IntStream;
public class Day050 {
public static void main(String[] args) {
System.out.println(createPool(10));
System.out.println(enhancedCreatePool(10));
}
// Instead of:
private static List<PooledObject> createPool(int poolSize) {
List<PooledObject> pool = new ArrayList<>(poolSize);
for (var i = 0; i < poolSize; i++) {
pool.add(new PooledObject(String.valueOf(i)));
}
return pool;
}
// Just do:
private static List<PooledObject> enhancedCreatePool(int poolSize) {
return IntStream.range(0, poolSize)
.mapToObj(i -> new PooledObject(String.valueOf(i)))
.toList();
}
private record PooledObject(String id) {
}
}
UnaryOperator
.
Day 51 - Using package com.thegreatapi.ahundreddaysofjava.day051;
import java.util.function.UnaryOperator;
public class Day051 {
public static void main(String[] args) {
// Instead of doing:
// Function<Integer, Integer> multiplyBy2 = i -> i * 2;
// Just do:
UnaryOperator<Integer> multiplyBy2 = i -> i * 2;
System.out.println(multiplyBy2.apply(3));
}
}
IOException
unchecked.
Day 52 - Making package com.thegreatapi.ahundreddaysofjava.day052;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.file.Files;
import java.nio.file.Path;
public class Day052 {
public static void main(String[] args) {
System.out.println(readFile());
}
public static String readFile() {
try {
return Files.readString(Path.of("/test.txt"));
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
}
Supplier
to run an expensive object creation only when and if needed
Day 53 - Using package com.thegreatapi.ahundreddaysofjava.day053;
import java.util.logging.Logger;
public class Day053 {
private static final Logger LOGGER = Logger.getLogger(Day053.class.getName());
public static void main(String[] args) {
// Instead of always running the expensive method
// LOGGER.info(veryExpensiveStringCreation());
// Pass the method reference so that it is called only when and if necessary
LOGGER.info(Day053::veryExpensiveStringCreation);
}
private static String veryExpensiveStringCreation() {
try {
Thread.sleep(10_000);
} catch (InterruptedException e) {
//TODO: handle properly
}
return "thegreatapi.com";
}
}
Map
values if absent.
Day 54 - Computing package com.thegreatapi.ahundreddaysofjava.day054;
import java.util.HashMap;
import java.util.Map;
public class Day054 {
public static void main(String[] args) {
Map<Integer, String> map = new HashMap<>();
map.put(1, "one");
map.put(2, "two");
// Instead of doing:
// String three = map.get(3);
// if (three == null) {
// three = "three";
// map.put(3, three);
// }
// Just do:
String three = map.computeIfAbsent(3, k -> "three");
System.out.println(three);
System.out.println(map);
}
}
AssertJ
Day 55 - Creating smart and readable assertions withpackage com.thegreatapi.ahundreddaysofjava.day055;
public record Day055(String fieldA, Integer fieldB) {
}
package com.thegreatapi.ahundreddaysofjava.day055;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
class Day055Test {
@Test
void testEquals() {
Day055 obj1 = new Day055("thegreatapi.com", 42);
Day055 obj2 = new Day055("thegreatapi.com", 42);
// Intead of using JUnit assertions, like this:
assertEquals(obj1.hashCode(), obj2.hashCode());
// Use AssertJ, like this:
assertThat(obj1).hasSameHashCodeAs(obj2);
}
}
Day 56 - Printing colored characters on the console
package com.thegreatapi.ahundreddaysofjava.day056;
public final class Day056 {
private static final String RESET_COLOR = "\u001B[0m";
public static void main(String[] args) {
var color = Color.valueOf(args[0]);
System.out.println(color.getAnsiColor() + "thegreatapi.com" + RESET_COLOR);
}
@SuppressWarnings("unused")
enum Color {
BLACK("\u001B[30m"),
BLUE("\u001B[34m"),
RED("\u001B[31m"),
YELLOW("\u001B[33m"),
WHITE("\u001B[37m");
private final String ansiColor;
Color(String ansiColor) {
this.ansiColor = ansiColor;
}
public final String getAnsiColor() {
return ansiColor;
}
}
}
Picocli to parse command line arguments
Day 57 - Usingpackage com.thegreatapi.ahundreddaysofjava.day057;
import picocli.CommandLine;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.concurrent.Callable;
@CommandLine.Command(
mixinStandardHelpOptions = true,
description = "Prints the contents of the specified file in the specified color")
public class Day057 implements Callable<Integer> {
private static final String RESET_COLOR = "\u001B[0m";
@CommandLine.Option(names = {"-c", "--collor"}, description = "BLACK, BLUE, RED, YELLOW or WHITE")
private Color color;
@CommandLine.Parameters(index = "0", description = "The file whose checksum to calculate.")
private Path path;
@Override
public Integer call() throws Exception {
print(Files.readString(path));
return 0;
}
private void print(String text) {
System.out.println(color.getAnsiColor() + text + RESET_COLOR);
}
public static void main(String... args) {
int exitCode = new CommandLine(new Day057()).execute(args);
System.exit(exitCode);
}
@SuppressWarnings("unused")
enum Color {
BLACK("\u001B[30m"),
BLUE("\u001B[34m"),
RED("\u001B[31m"),
YELLOW("\u001B[33m"),
WHITE("\u001B[37m");
private final String ansiColor;
Color(String ansiColor) {
this.ansiColor = ansiColor;
}
public final String getAnsiColor() {
return ansiColor;
}
}
}
toString()
format
Day 58 - Never write code that depends on package com.thegreatapi.ahundreddaysofjava.day058;
import java.util.List;
public class Day058 {
static class Bad {
/**
* Never write code that depends on {@link Object#toString()} format.
* The format can change in the future and break your code.
* In this particular case, we don't even know the {@link List} implementation that we're receiving,
* and we don't have any guarantee that the return of {@link List#toString()} would be in the expected format.
*/
public String convertToString(List<String> list) {
return list.toString().replace("[", "").replace("]", "");
}
}
static class Good {
public String convertToString(List<String> list) {
return String.join(", ", list);
}
}
}
Predicate
.
Day 59 - Using a package com.thegreatapi.ahundreddaysofjava.day059;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
public class Day059 {
public static void main(String[] args) {
/*
A Predicate<T> is the same as Function<T, Boolean>.
It consumes a T and returns a Boolean.
*/
Predicate<Integer> isPair = intValue -> intValue % 2 == 0;
List<Integer> numbers = getNumbers();
numbers.stream()
.filter(isPair)
.forEach(System.out::println);
}
private static List<Integer> getNumbers() {
return IntStream.rangeClosed(1, 100).boxed().collect(Collectors.toList());
}
}