From 0b0a61e7301a7397322ff7875d9005e55597526e Mon Sep 17 00:00:00 2001 From: Taras Boychuk Date: Mon, 28 Dec 2020 17:56:47 +0200 Subject: [PATCH 01/74] GP-24 Implement CrazyLambdas with comments --- .../bobocode/lambdas/level2/CrazyLambdas.java | 98 +++++++++++++++---- 1 file changed, 79 insertions(+), 19 deletions(-) diff --git a/6-0-functional-programming/src/main/java/com/bobocode/lambdas/level2/CrazyLambdas.java b/6-0-functional-programming/src/main/java/com/bobocode/lambdas/level2/CrazyLambdas.java index 8c938d02..42015f8e 100644 --- a/6-0-functional-programming/src/main/java/com/bobocode/lambdas/level2/CrazyLambdas.java +++ b/6-0-functional-programming/src/main/java/com/bobocode/lambdas/level2/CrazyLambdas.java @@ -4,6 +4,7 @@ import java.math.BigDecimal; import java.util.Map; +import java.util.concurrent.ThreadLocalRandom; import java.util.function.*; /** @@ -21,7 +22,8 @@ public class CrazyLambdas { * @return a string supplier */ public static Supplier helloSupplier() { - throw new ExerciseNotCompletedException(); + // because supplier method has NO PARAMETERS, a lambda starts with empty brackets + return () -> "Hello"; } /** @@ -30,7 +32,9 @@ public static Supplier helloSupplier() { * @return a string predicate */ public static Predicate isEmptyPredicate() { - throw new ExerciseNotCompletedException(); + // have a string parameter we can call isEmpty() and return result, e.g `str -> str.isEmpty()` + // so if we only call a method, it's better to provide a reference to that method instead of lambda expression + return String::isEmpty; } /** @@ -40,7 +44,11 @@ public static Predicate isEmptyPredicate() { * @return function that repeats Strings */ public static BiFunction stringMultiplier() { - throw new ExerciseNotCompletedException(); + // Bi means two parameters (str, n), and we can implement this method using a lambda with two params + // e.g. `(str, n) -> str.repeat(n)`, however in this case it's also better to provide a reference instead. + // BiFunction method `apply` has two params, and String method `repeat` has only one, but when you use a static + // method reference to a non-static method it's first parameter becomes `this` + return String::repeat; } /** @@ -50,7 +58,8 @@ public static BiFunction stringMultiplier() { * @return function that converts adds dollar sign */ public static Function toDollarStringFunction() { - throw new ExerciseNotCompletedException(); + // Function is a classic lambda, where parameter and return types are different + return val -> "$" + val; } /** @@ -62,7 +71,11 @@ public static Function toDollarStringFunction() { * @return a string predicate */ public static Predicate lengthInRangePredicate(int min, int max) { - throw new ExerciseNotCompletedException(); + // A lambda has one string parameter and we need to compare its length with provided min and max values. + // Please note, that `min` and `max` must be "effectively final" if we want to use them in lambda expression. + // Try to uncomment the line below + // min = 1; + return str -> str.length() >= min && str.length() < max; } /** @@ -71,7 +84,8 @@ public static Predicate lengthInRangePredicate(int min, int max) { * @return int supplier */ public static IntSupplier randomIntSupplier() { - throw new ExerciseNotCompletedException(); + // This is a special Supplier for int primitive. Its method has no arguments and supplies an int value. + return () -> ThreadLocalRandom.current().nextInt(); } @@ -81,7 +95,9 @@ public static IntSupplier randomIntSupplier() { * @return int operation */ public static IntUnaryOperator boundedRandomIntSupplier() { - throw new ExerciseNotCompletedException(); + // IntUnaryOperator is just an UnaryOperator for int primitives. Its method accepts int and returns int. + // So a parameter is a bound that should be used when generating a random integer + return bound -> ThreadLocalRandom.current().nextInt(bound); } /** @@ -90,7 +106,8 @@ public static IntUnaryOperator boundedRandomIntSupplier() { * @return square operation */ public static IntUnaryOperator intSquareOperation() { - throw new ExerciseNotCompletedException(); + // a classical example of lambda, we use parameter and return its square + return x -> x * x; } /** @@ -99,7 +116,9 @@ public static IntUnaryOperator intSquareOperation() { * @return binary sum operation */ public static LongBinaryOperator longSumOperation() { - throw new ExerciseNotCompletedException(); + // LongBinaryOperator is a binary operator for long primitive. + // It can be done using lambda with two params like `(a, b) -> a + b` but it's better to use method reference + return Long::sum; } /** @@ -108,7 +127,9 @@ public static LongBinaryOperator longSumOperation() { * @return string to int converter */ public static ToIntFunction stringToIntConverter() { - throw new ExerciseNotCompletedException(); + // ToIntFunction is a special form of Function that returns an int primitive. In this case we also use a simple + // method reference instead of a longer lambda `str -> Integer.parseInt(str)` + return Integer::parseInt; } /** @@ -119,7 +140,11 @@ public static ToIntFunction stringToIntConverter() { * @return a function supplier */ public static Supplier nMultiplyFunctionSupplier(int n) { - throw new ExerciseNotCompletedException(); + // As you can see we have Supplier that supplies IntUnaryOperator, which means we'll need a nested lambda. + // If it looks complex, you can start by implementing an inner lambda which is `x -> n * x`. Then on top of that + // you just need to implement a supplier that supplies that lambda above. + // Or you can start by implementing a supplier like `() -> ...` and then add inner lambda instead of three dots. + return () -> x -> n * x; } /** @@ -128,7 +153,13 @@ public static Supplier nMultiplyFunctionSupplier(int n) { * @return function that composes functions with trim() function */ public static UnaryOperator> composeWithTrimFunction() { - throw new ExerciseNotCompletedException(); + // UnaryOperator has the same parameter and return type. In our case it's a function. So our job is to use + // that function and compose it with another function called `trim` + // As you can see Function provides some additional default methods, and one of them is `compose`. + // So we have one parameter and we'll call compose, like `strFunction -> strFunction.compose(...)` then + // instead of three dots, we need to pass another function(lambda) trim, you can pass `s -> s.trim()`, or just + // use a method reference to `trim` + return strFunction -> strFunction.compose(String::trim); } /** @@ -139,7 +170,16 @@ public static UnaryOperator> composeWithTrimFunction() * @return a thread supplier */ public static Supplier runningThreadSupplier(Runnable runnable) { - throw new ExerciseNotCompletedException(); + // Having a runnable you can create and start a thread. And in this case you need to implement a supplier that + // will supply this running thread. The main point is that THREAD WON'T BE CREATED AND STARTED until + // method `get` of the supplier is called. + // In this case you need to do multiple operations like create thread, call start and return it, so we need to + // use lambda body with curly brackets and return statement + return () -> { + Thread t = new Thread(runnable); + t.start(); + return t; + }; } /** @@ -148,7 +188,10 @@ public static Supplier runningThreadSupplier(Runnable runnable) { * @return a runnable consumer */ public static Consumer newThreadRunnableConsumer() { - throw new ExerciseNotCompletedException(); + // In this case runnable is a parameter of a Consumer method. We use that parameter to create Thread + // and start it. Since consumer does not return any value (void), we call method `start` right within + // lambda expression. (Method `start` also returns `void`) + return runnable -> new Thread(runnable).start(); } /** @@ -158,7 +201,13 @@ public static Consumer newThreadRunnableConsumer() { * @return a function that transforms runnable into a thread supplier */ public static Function> runnableToThreadSupplierFunction() { - throw new ExerciseNotCompletedException(); + // This method is very similar to `runningThreadSupplier`. But in this case we should implement a function + // that accepts a runnable and then does exactly what we did before in `runningThreadSupplier`. + return runnable -> () -> { + Thread t = new Thread(runnable); + t.start(); + return t; + }; } /** @@ -171,7 +220,13 @@ public static Function> runnableToThreadSupplierFunct * @return a binary function that receiver predicate and function and compose them to create a new function */ public static BiFunction functionToConditionalFunction() { - throw new ExerciseNotCompletedException(); + // BiFunction accepts two parameters, so you can start from implementing this part + // `(intFunction, condition) -> ...` then the return type is `IntUnaryOperator`, and in order to implement + // this result `IntUnaryOperator` we need a lambda with parameter e.g. `x`, so we can add it like + // `(intFunction, condition) -> x -> ...`. Now we should check the condition for `x` + // `(intFunction, condition) -> x -> condition.test(x) ? ...` if it's true, we call provided `intFunction` + // and return result, otherwise we just return `x` + return (intFunction, condition) -> x -> condition.test(x) ? intFunction.applyAsInt(x) : x; } /** @@ -182,16 +237,21 @@ public static BiFunction funct * @return a high-order function that fetches a function from a function map by a given name or returns identity() */ public static BiFunction, String, IntUnaryOperator> functionLoader() { - throw new ExerciseNotCompletedException(); + // This BiFunction accepts a map of functions and a function name, so we start form this + // `(functionMap, functionName) -> ...` then using a name we need to extract a function from map and return it + // or return `IntUnaryOperator.identity()` if no function was found. For this use case there is a default method + // of a class `Map` called `getOrDefault` + return (functionMap, functionName) -> functionMap.getOrDefault(functionName, IntUnaryOperator.identity()); } /** - * Returns {@link Supplier} of {@link Supplier} of {@link Supplier} of {@link String} "WELL DONE". + * Returns {@link Supplier} of {@link Supplier} of {@link Supplier} of {@link String} "WELL DONE!". * * @return a supplier instance */ public static Supplier>> trickyWellDoneSupplier() { - throw new ExerciseNotCompletedException(); + // You just need to create a couple of nested lambdas like `() -> () -> ...` + return () -> () -> () -> "WELL DONE!"; } } From 06339fac2a3015c046905982061617abe52b6be2 Mon Sep 17 00:00:00 2001 From: Taras Boychuk Date: Mon, 28 Dec 2020 19:07:16 +0200 Subject: [PATCH 02/74] GP-24 Implement CrazyOptionals and copied impl of CrazyStreams --- .../bobocode/optionals/CrazyOptionals.java | 99 ++++++++++++++++--- .../com/bobocode/streams/CrazyStreams.java | 77 +++++++++++---- 2 files changed, 142 insertions(+), 34 deletions(-) diff --git a/6-0-functional-programming/src/main/java/com/bobocode/optionals/CrazyOptionals.java b/6-0-functional-programming/src/main/java/com/bobocode/optionals/CrazyOptionals.java index 3ec4d4ea..3655a9a2 100644 --- a/6-0-functional-programming/src/main/java/com/bobocode/optionals/CrazyOptionals.java +++ b/6-0-functional-programming/src/main/java/com/bobocode/optionals/CrazyOptionals.java @@ -16,6 +16,8 @@ import java.util.Optional; import java.util.OptionalDouble; +import static java.util.Comparator.comparing; + /** * {@link CrazyOptionals} is an exercise class. Each method represents some operation with a {@link Account} and * should be implemented using Optional API. Every method that is not implemented yet throws @@ -24,7 +26,6 @@ * TODO: remove exception and implement each method of this class using Optional API */ public class CrazyOptionals { - /** * Creates an instance of {@link Optional} using a text parameter * @@ -32,7 +33,9 @@ public class CrazyOptionals { * @return optional object that holds text */ public static Optional optionalOfString(@Nullable String text) { - throw new ExerciseNotCompletedException(); + // Since `text` can be null we should use `Optional.ofNullable()`. It will wrap `text` with `Optional` if it's + // not null or use `Optional.empty()` if it's is `null` + return Optional.ofNullable(text); } /** @@ -42,7 +45,14 @@ public static Optional optionalOfString(@Nullable String text) { * @param amount money to deposit */ public static void deposit(AccountProvider accountProvider, BigDecimal amount) { - throw new ExerciseNotCompletedException(); + // One of the ideas behind Optional API is to provide a declarative "if" statements. E.g. typically when we need + // to check if account is not null and then perform some logic, we would write an "if" statement and then + // do some logic inside. + // But class `Optional` provides a special method for this use case. E.g. if you have an optional account, + // you can call `ifPresent` method and provide a `Consumer` of that account which will be used only + // if optional is not empty + accountProvider.getAccount() + .ifPresent(account -> account.setBalance(account.getBalance().add(amount))); } /** @@ -52,7 +62,9 @@ public static void deposit(AccountProvider accountProvider, BigDecimal amount) { * @return optional object that holds account */ public static Optional optionalOfAccount(@Nonnull Account account) { - throw new ExerciseNotCompletedException(); + // Since account must not be null, we use `Optional.of()` that will throw `NullPointerException` on creation + // if passed object is null + return Optional.of(account); } /** @@ -64,7 +76,10 @@ public static Optional optionalOfAccount(@Nonnull Account account) { * @return account from provider or defaultAccount */ public static Account getAccount(AccountProvider accountProvider, Account defaultAccount) { - throw new ExerciseNotCompletedException(); + // As you can see Optional API is a bunch of useful methods for different use cases inside `Optional` class. + // If you need to provide a default value that will be used if optional is empty, you can use method `orElse`. + return accountProvider.getAccount() + .orElse(defaultAccount); } /** @@ -75,7 +90,11 @@ public static Account getAccount(AccountProvider accountProvider, Account defaul * @param accountService */ public static void processAccount(AccountProvider accountProvider, AccountService accountService) { - throw new ExerciseNotCompletedException(); + // We already saw a declarative "if". Now it's a declarative "if-else". + // A method `ifPresentOrElse` accepts a consumer that will be used if optional is not empty, and a runnable that + // will be called otherwise + accountProvider.getAccount() + .ifPresentOrElse(accountService::processAccount, accountService::processWithNoAccount); } /** @@ -86,7 +105,15 @@ public static void processAccount(AccountProvider accountProvider, AccountServic * @return provided or generated account */ public static Account getOrGenerateAccount(AccountProvider accountProvider) { - throw new ExerciseNotCompletedException(); + // In case you need to provide a default value, but it's computation is an expansive operation + // (e.g. calling other microservice), you SHOULD NOT USE `Optional#orElse()`. Because it FORCES YOU TO COMPUTE + // A DEFAULT VALUE regardless if optional is empty or not. + // For such cases it's better to use `Optional#orElseGet()` that functionally works exactly the same, but + // is based on lazy initialization using `Supplier` interface. Which means that default value + // will not be computed (created) until supplier method `get()` is called. In this case, + // A DEFAULT VALUE WILL BE ONLY COMPUTED WHEN OPTIONAL IS EMPTY + return accountProvider.getAccount() + .orElseGet(Accounts::generateAccount); } /** @@ -96,7 +123,10 @@ public static Account getOrGenerateAccount(AccountProvider accountProvider) { * @return optional balance */ public static Optional retrieveBalance(AccountProvider accountProvider) { - throw new ExerciseNotCompletedException(); + // When you have an optional object, and want to access its field. In that case you can use `Optional#map`. + // Which is a null-safe mapping that transforms an optional object into its optional field. + return accountProvider.getAccount() + .map(Account::getBalance); } /** @@ -107,7 +137,9 @@ public static Optional retrieveBalance(AccountProvider accountProvid * @return provided account */ public static Account getAccount(AccountProvider accountProvider) { - throw new ExerciseNotCompletedException(); + // Un case Optional is empty and you want to throw a custom exception, you can use `orElseThrow` + return accountProvider.getAccount() + .orElseThrow(() -> new AccountNotFoundException("No Account provided!")); } /** @@ -117,7 +149,10 @@ public static Account getAccount(AccountProvider accountProvider) { * @return optional credit balance */ public static Optional retrieveCreditBalance(CreditAccountProvider accountProvider) { - throw new ExerciseNotCompletedException(); + // In case your getter already return Optional, you cannot use `Optional#map` because it will wrap it with + // another `Optional` like `Optional>`. In this case `Optional#flatMap` should be used. + return accountProvider.getAccount() + .flatMap(CreditAccount::getCreditBalance); } @@ -129,7 +164,12 @@ public static Optional retrieveCreditBalance(CreditAccountProvider a * @return optional gmail account */ public static Optional retrieveAccountGmail(AccountProvider accountProvider) { - throw new ExerciseNotCompletedException(); + // Sometimes you need to check if an optional object meets some criteria and you want to do that in + // a null-safe manner. For that purpose you can use `Optional#filter` that will check a provided condition + // only if optional is not empty, and then if condition is true, it will keep the object wrapped with optional, + // or return empty optional otherwise + return accountProvider.getAccount() + .filter(account -> account.getEmail().split("@")[1].equals("gmail.com")); } /** @@ -142,7 +182,12 @@ public static Optional retrieveAccountGmail(AccountProvider accountProv * @return account got from either accountProvider or fallbackProvider */ public static Account getAccountWithFallback(AccountProvider accountProvider, AccountProvider fallbackProvider) { - throw new ExerciseNotCompletedException(); + // In case you have an alternative optional value, you can use `Optional#or`. It will be used only if main + // optional is empty. Then if you want to throw exception if optional is empty, but don't need a custom one, you + // can call `Optional#orElseThrow` + return accountProvider.getAccount() + .or(fallbackProvider::getAccount) + .orElseThrow(); } /** @@ -153,7 +198,10 @@ public static Account getAccountWithFallback(AccountProvider accountProvider, Ac * @return account with the highest balance */ public static Account getAccountWithMaxBalance(List accounts) { - throw new ExerciseNotCompletedException(); + // Optionals are used in Stream API. E.e. `Stream#min` and `Stream#max` return `Optional` + return accounts.stream() + .max(comparing(Account::getBalance)) // returns Optional + .orElseThrow(); // throws NoSuchElementException if optional is empty } /** @@ -163,7 +211,13 @@ public static Account getAccountWithMaxBalance(List accounts) { * @return the lowest balance values */ public static OptionalDouble findMinBalanceValue(List accounts) { - throw new ExerciseNotCompletedException(); + // As well as Stream API, an Optional API provides special classes for primitives. So in case you work with + // stream of primitives and call a method that returns an optional, like `min`, a return type will be + // primitive optional + return accounts.stream() + .map(Account::getBalance) // map all stream accounts to balances + .mapToDouble(BigDecimal::doubleValue) // map all balances to primitive double values (returns DoubleStream) + .min(); // Optional API provides special classes for primitives as well } /** @@ -173,7 +227,12 @@ public static OptionalDouble findMinBalanceValue(List accounts) { * @param accountService */ public static void processAccountWithMaxBalance(List accounts, AccountService accountService) { - throw new ExerciseNotCompletedException(); + // Using Steam API and Optional API allows you to write concise declarative expressions. E.g. find an account + // with max balance and process it if it exists. You use Stream method `max` that returns Optional + // and then use `Optional#ifPreset` to provide a logic we want to execute for found account. + accounts.stream() + .max(comparing(Account::getBalance)) // returns Optional + .ifPresent(accountService::processAccount); // declarative if statement that accepts Consumer } /** @@ -183,7 +242,15 @@ public static void processAccountWithMaxBalance(List accounts, AccountS * @return total credit balance */ public static double calculateTotalCreditBalance(List accounts) { - throw new ExerciseNotCompletedException(); + // If you have a stream of optionals and you want to filter empty ones, you can do the trick and call + // `Stream#flatMap` and pass `Optional#sream`. This logic transforms each optional object into a stream of either + // one of zero elements and then all those streams are flattened into one using `flatMap` which automatically + // filters all empty optional + return accounts.stream() + .map(CreditAccount::getCreditBalance) // transforms each element of stream into Optional + .flatMap(Optional::stream) // uses special Optional#stream() to filter all elements that are empty + .mapToDouble(BigDecimal::doubleValue) // transform BigDecimal into primitive double (returns DoubleStream) + .sum(); // calculates a sum of primitive double } } diff --git a/6-0-functional-programming/src/main/java/com/bobocode/streams/CrazyStreams.java b/6-0-functional-programming/src/main/java/com/bobocode/streams/CrazyStreams.java index 223e06c9..3df8617c 100644 --- a/6-0-functional-programming/src/main/java/com/bobocode/streams/CrazyStreams.java +++ b/6-0-functional-programming/src/main/java/com/bobocode/streams/CrazyStreams.java @@ -1,6 +1,7 @@ package com.bobocode.streams; import com.bobocode.model.Account; +import com.bobocode.model.Sex; import com.bobocode.streams.exception.EntityNotFoundException; import com.bobocode.util.ExerciseNotCompletedException; import lombok.AllArgsConstructor; @@ -8,6 +9,11 @@ import java.math.BigDecimal; import java.time.Month; import java.util.*; +import java.util.stream.Stream; + +import static java.util.Comparator.comparing; +import static java.util.function.Function.identity; +import static java.util.stream.Collectors.*; /** * {@link CrazyStreams} is an exercise class. Each method represent some operation with a collection of accounts that @@ -26,7 +32,8 @@ public class CrazyStreams { * @return account with max balance wrapped with optional */ public Optional findRichestPerson() { - throw new ExerciseNotCompletedException(); + return accounts.stream() + .max(comparing(Account::getBalance)); } /** @@ -36,7 +43,9 @@ public Optional findRichestPerson() { * @return a list of accounts */ public List findAccountsByBirthdayMonth(Month birthdayMonth) { - throw new ExerciseNotCompletedException(); + return accounts.stream() + .filter(a -> a.getBirthday().getMonth().equals(birthdayMonth)) + .collect(toList()); } /** @@ -46,7 +55,8 @@ public List findAccountsByBirthdayMonth(Month birthdayMonth) { * @return a map where key is true or false, and value is list of male, and female accounts */ public Map> partitionMaleAccounts() { - throw new ExerciseNotCompletedException(); + return accounts.stream() + .collect(partitioningBy(a -> a.getSex().equals(Sex.MALE))); } /** @@ -56,7 +66,8 @@ public Map> partitionMaleAccounts() { * @return a map where key is an email domain and value is a list of all account with such email */ public Map> groupAccountsByEmailDomain() { - throw new ExerciseNotCompletedException(); + return accounts.stream() + .collect(groupingBy(a -> a.getEmail().split("@")[1])); } /** @@ -65,7 +76,9 @@ public Map> groupAccountsByEmailDomain() { * @return total number of letters of first and last names of all accounts */ public int getNumOfLettersInFirstAndLastNames() { - throw new ExerciseNotCompletedException(); + return accounts.stream() + .mapToInt(a -> a.getFirstName().length() + a.getLastName().length()) + .sum(); } /** @@ -74,7 +87,9 @@ public int getNumOfLettersInFirstAndLastNames() { * @return total balance of all accounts */ public BigDecimal calculateTotalBalance() { - throw new ExerciseNotCompletedException(); + return accounts.stream() + .map(Account::getBalance) + .reduce(BigDecimal.ZERO, BigDecimal::add); } /** @@ -83,7 +98,10 @@ public BigDecimal calculateTotalBalance() { * @return list of accounts sorted by first and last names */ public List sortByFirstAndLastNames() { - throw new ExerciseNotCompletedException(); + return accounts.stream() + .sorted(comparing(Account::getFirstName) + .thenComparing(Account::getLastName)) + .collect(toList()); } /** @@ -93,7 +111,9 @@ public List sortByFirstAndLastNames() { * @return true if there is an account that has an email with provided domain */ public boolean containsAccountWithEmailDomain(String emailDomain) { - throw new ExerciseNotCompletedException(); + return accounts.stream() + .map(Account::getEmail) + .anyMatch(email -> email.split("@")[1].equals(emailDomain)); } /** @@ -104,7 +124,11 @@ public boolean containsAccountWithEmailDomain(String emailDomain) { * @return account balance */ public BigDecimal getBalanceByEmail(String email) { - throw new ExerciseNotCompletedException(); + return accounts.stream() + .filter(account -> account.getEmail().equals(email)) + .findFirst() + .map(Account::getBalance) + .orElseThrow(() -> new EntityNotFoundException(String.format("Cannot find Account by email=%s", email))); } /** @@ -113,7 +137,8 @@ public BigDecimal getBalanceByEmail(String email) { * @return map of accounts by its ids */ public Map collectAccountsById() { - throw new ExerciseNotCompletedException(); + return accounts.stream() + .collect(toMap(Account::getId, identity())); } /** @@ -124,17 +149,20 @@ public Map collectAccountsById() { * @return map of account by its ids the were created in a particular year */ public Map collectBalancesByEmailForAccountsCreatedOn(int year) { - throw new ExerciseNotCompletedException(); + return accounts.stream() + .filter(account -> account.getCreationDate().getYear() == year) + .collect(toMap(Account::getEmail, Account::getBalance)); } /** * Returns a {@link Map} where key is {@link Account#lastName} and values is a {@link Set} that contains first names * of all accounts with a specific last name. * - * @return a map where key is a last name and value is a set of first names + * @return a map where key is a first name and value is a set of first names */ public Map> groupFirstNamesByLastNames() { - throw new ExerciseNotCompletedException(); + return accounts.stream() + .collect(groupingBy(Account::getLastName, mapping(Account::getFirstName, toSet()))); } /** @@ -144,7 +172,9 @@ public Map> groupFirstNamesByLastNames() { * @return a map where a key is a birthday month and value is comma-separated first names */ public Map groupCommaSeparatedFirstNamesByBirthdayMonth() { - throw new ExerciseNotCompletedException(); + return accounts.stream() + .collect(groupingBy(a -> a.getBirthday().getMonth(), + mapping(Account::getFirstName, joining(", ")))); } /** @@ -154,7 +184,10 @@ public Map groupCommaSeparatedFirstNamesByBirthdayMonth() { * @return a map where key is a creation month and value is total balance of all accounts created in that month */ public Map groupTotalBalanceByCreationMonth() { - throw new ExerciseNotCompletedException(); + return accounts.stream() + .collect(groupingBy(a -> a.getCreationDate().getMonth(), + mapping(Account::getBalance, + reducing(BigDecimal.ZERO, BigDecimal::add)))); } /** @@ -164,7 +197,11 @@ public Map groupTotalBalanceByCreationMonth() { * @return a map where key is a letter and value is its count in all first names */ public Map getCharacterFrequencyInFirstNames() { - throw new ExerciseNotCompletedException(); + return accounts.stream() + .map(Account::getFirstName) + .flatMapToInt(String::chars) + .mapToObj(c -> (char) c) + .collect(groupingBy(identity(), counting())); } /** @@ -174,8 +211,12 @@ public Map getCharacterFrequencyInFirstNames() { * @return a map where key is a letter and value is its count ignoring case in all first and last names */ public Map getCharacterFrequencyIgnoreCaseInFirstAndLastNames() { - throw new ExerciseNotCompletedException(); + return accounts.stream() + .flatMap(a -> Stream.of(a.getFirstName(), a.getLastName())) + .map(String::toLowerCase) + .flatMapToInt(String::chars) + .mapToObj(c -> (char) c) + .collect(groupingBy(identity(), counting())); } - } From 9329d5d7780a0c279a4b2a1b7357a13f4731e045 Mon Sep 17 00:00:00 2001 From: Serhii Date: Tue, 5 Jan 2021 15:59:53 +0200 Subject: [PATCH 03/74] GP-26 add the exercise solution with a tail and extra methods --- .../com/bobocode/linked_list/LinkedList.java | 126 ++++++++++++++++-- 1 file changed, 112 insertions(+), 14 deletions(-) diff --git a/2-0-data-structures-and-algorithms/src/main/java/com/bobocode/linked_list/LinkedList.java b/2-0-data-structures-and-algorithms/src/main/java/com/bobocode/linked_list/LinkedList.java index f0a80833..f44843f4 100644 --- a/2-0-data-structures-and-algorithms/src/main/java/com/bobocode/linked_list/LinkedList.java +++ b/2-0-data-structures-and-algorithms/src/main/java/com/bobocode/linked_list/LinkedList.java @@ -1,7 +1,8 @@ package com.bobocode.linked_list; - -import com.bobocode.util.ExerciseNotCompletedException; +import java.util.NoSuchElementException; +import java.util.Objects; +import java.util.stream.Stream; /** * {@link LinkedList} is a list implementation that is based on singly linked generic nodes. A node is implemented as @@ -10,6 +11,9 @@ * @param generic type parameter */ public class LinkedList implements List { + private Node head; + private Node tail; + private int size; /** * This method creates a list of provided elements @@ -19,7 +23,9 @@ public class LinkedList implements List { * @return a new list of elements the were passed as method parameters */ public static List of(T... elements) { - throw new ExerciseNotCompletedException(); // todo: implement this method + LinkedList linkedList = new LinkedList<>(); + Stream.of(elements).forEach(linkedList::add); + return linkedList; } /** @@ -29,7 +35,7 @@ public static List of(T... elements) { */ @Override public void add(T element) { - throw new ExerciseNotCompletedException(); // todo: implement this method + add(size, element); } /** @@ -41,7 +47,51 @@ public void add(T element) { */ @Override public void add(int index, T element) { - throw new ExerciseNotCompletedException(); // todo: implement this method + Node newNode = Node.valueOf(element); + if (index == 0) { + addAsHead(newNode); + } else if (index == size) { + addAsTail(newNode); + } else { + add(index, newNode); + } + size++; + } + + private void addAsHead(Node newNode) { + newNode.next = head; + head = newNode; + if (head.next == null) { + tail = head; + } + } + + private void addAsTail(Node newNode) { + tail.next = newNode; + tail = newNode; + } + + private void add(int index, Node newNode) { + Node node = findNodeByIndex(index - 1); + newNode.next = node.next; + node.next = newNode; + } + + private Node findNodeByIndex(int index) { + Objects.checkIndex(index, size); + if (index == size - 1) { + return tail; + } else { + return nodeAt(index); + } + } + + private Node nodeAt(int index) { + Node currentNode = head; + for (int i = 0; i < index; i++) { + currentNode = currentNode.next; + } + return currentNode; } /** @@ -53,7 +103,8 @@ public void add(int index, T element) { */ @Override public void set(int index, T element) { - throw new ExerciseNotCompletedException(); // todo: implement this method + Node node = findNodeByIndex(index); + node.value = element; } /** @@ -65,7 +116,8 @@ public void set(int index, T element) { */ @Override public T get(int index) { - throw new ExerciseNotCompletedException(); // todo: implement this method + Node node = findNodeByIndex(index); + return node.value; } /** @@ -76,7 +128,8 @@ public T get(int index) { */ @Override public T getFirst() { - throw new ExerciseNotCompletedException(); // todo: implement this method + checkElementsExist(); + return head.value; } /** @@ -87,7 +140,14 @@ public T getFirst() { */ @Override public T getLast() { - throw new ExerciseNotCompletedException(); // todo: implement this method + checkElementsExist(); + return tail.value; + } + + private void checkElementsExist() { + if (head == null) { + throw new NoSuchElementException(); + } } /** @@ -95,12 +155,29 @@ public T getLast() { * throws {@link IndexOutOfBoundsException} * * @param index element index + * @return an element value */ @Override public void remove(int index) { - throw new ExerciseNotCompletedException(); // todo: implement this method + if (index == 0) { + Objects.checkIndex(index, size); + removeHead(); + } else { + Node previousNode = findNodeByIndex(index - 1); + previousNode.next = previousNode.next.next; + if (index == size - 1) { + tail = previousNode; + } + } + size--; } + private void removeHead() { + head = head.next; + if (head == null) { + tail = null; + } + } /** * Checks if a specific exists in he list @@ -109,7 +186,14 @@ public void remove(int index) { */ @Override public boolean contains(T element) { - throw new ExerciseNotCompletedException(); // todo: implement this method + Node currentNode = head; + while (currentNode != null) { + if (currentNode.value.equals(element)) { + return true; + } + currentNode = currentNode.next; + } + return false; } /** @@ -119,7 +203,7 @@ public boolean contains(T element) { */ @Override public boolean isEmpty() { - throw new ExerciseNotCompletedException(); // todo: implement this method + return head == null; } /** @@ -129,7 +213,7 @@ public boolean isEmpty() { */ @Override public int size() { - throw new ExerciseNotCompletedException(); // todo: implement this method + return size; } /** @@ -137,6 +221,20 @@ public int size() { */ @Override public void clear() { - throw new ExerciseNotCompletedException(); // todo: implement this method + head = tail = null; + size = 0; + } + + static class Node { + private T value; + private Node next; + + private Node(T value) { + this.value = value; + } + + public static Node valueOf(T value) { + return new Node<>(value); + } } } From 760dbfc6437645c3a4417a8500aca1a53c9f30c5 Mon Sep 17 00:00:00 2001 From: Serhii Date: Tue, 5 Jan 2021 20:47:34 +0200 Subject: [PATCH 04/74] GP-31 migrate java oop --- .../bobocode/flight_search/data/FlightDao.java | 9 ++++----- .../factory/FlightServiceFactory.java | 5 +++-- .../flight_search/service/FlightService.java | 17 ++++++++++++----- .../bobocode/flight_search/service/Flights.java | 9 +++++++++ 4 files changed, 28 insertions(+), 12 deletions(-) create mode 100644 4-0-object-oriented-programming/src/main/java/com/bobocode/flight_search/service/Flights.java diff --git a/4-0-object-oriented-programming/src/main/java/com/bobocode/flight_search/data/FlightDao.java b/4-0-object-oriented-programming/src/main/java/com/bobocode/flight_search/data/FlightDao.java index 17ee14cb..f980891c 100644 --- a/4-0-object-oriented-programming/src/main/java/com/bobocode/flight_search/data/FlightDao.java +++ b/4-0-object-oriented-programming/src/main/java/com/bobocode/flight_search/data/FlightDao.java @@ -1,6 +1,6 @@ package com.bobocode.flight_search.data; -import com.bobocode.util.ExerciseNotCompletedException; +import com.bobocode.flight_search.service.Flights; import java.util.HashSet; import java.util.Set; @@ -12,7 +12,7 @@ * todo: 1. Implement a method {@link FlightDao#register(String)} that store new flight number into the set * todo: 2. Implement a method {@link FlightDao#findAll()} that returns a set of all flight numbers */ -public class FlightDao { +public class FlightDao implements Flights { private Set flights = new HashSet<>(); /** @@ -22,7 +22,7 @@ public class FlightDao { * @return {@code true} if a flight number was stored, {@code false} otherwise */ public boolean register(String flightNumber) { - throw new ExerciseNotCompletedException();// todo: implement this method + return flights.add(flightNumber); } /** @@ -31,7 +31,6 @@ public boolean register(String flightNumber) { * @return a set of flight numbers */ public Set findAll() { - throw new ExerciseNotCompletedException();// todo: implement this method + return flights; } - } diff --git a/4-0-object-oriented-programming/src/main/java/com/bobocode/flight_search/factory/FlightServiceFactory.java b/4-0-object-oriented-programming/src/main/java/com/bobocode/flight_search/factory/FlightServiceFactory.java index d7bac6fa..4ac00cdc 100644 --- a/4-0-object-oriented-programming/src/main/java/com/bobocode/flight_search/factory/FlightServiceFactory.java +++ b/4-0-object-oriented-programming/src/main/java/com/bobocode/flight_search/factory/FlightServiceFactory.java @@ -1,7 +1,7 @@ package com.bobocode.flight_search.factory; +import com.bobocode.flight_search.data.FlightDao; import com.bobocode.flight_search.service.FlightService; -import com.bobocode.util.ExerciseNotCompletedException; /** * {@link FlightServiceFactory} is used to create an instance of {@link FlightService} @@ -16,6 +16,7 @@ public class FlightServiceFactory { * @return FlightService */ public FlightService creteFlightService() { - throw new ExerciseNotCompletedException(); + return new FlightService(new FlightDao()); } } + diff --git a/4-0-object-oriented-programming/src/main/java/com/bobocode/flight_search/service/FlightService.java b/4-0-object-oriented-programming/src/main/java/com/bobocode/flight_search/service/FlightService.java index 20a3d79e..86f3f7d9 100644 --- a/4-0-object-oriented-programming/src/main/java/com/bobocode/flight_search/service/FlightService.java +++ b/4-0-object-oriented-programming/src/main/java/com/bobocode/flight_search/service/FlightService.java @@ -1,10 +1,9 @@ package com.bobocode.flight_search.service; -import com.bobocode.flight_search.data.FlightDao; -import com.bobocode.util.ExerciseNotCompletedException; - import java.util.List; +import static java.util.stream.Collectors.toList; + /** * {@link FlightService} provides an API that allows to manage flight numbers *

@@ -13,6 +12,12 @@ */ public class FlightService { + private Flights flights; + + public FlightService(Flights flights) { + this.flights = flights; + } + /** * Adds a new flight number * @@ -20,7 +25,7 @@ public class FlightService { * @return {@code true} if a flight number was added, {@code false} otherwise */ public boolean registerFlight(String flightNumber) { - throw new ExerciseNotCompletedException(); + return flights.register(flightNumber); } /** @@ -30,6 +35,8 @@ public boolean registerFlight(String flightNumber) { * @return a list of found flight numbers */ public List searchFlights(String query) { - throw new ExerciseNotCompletedException(); + return flights.findAll().stream() + .filter(flightNum -> flightNum.toUpperCase().contains(query.toUpperCase())) + .collect(toList()); } } diff --git a/4-0-object-oriented-programming/src/main/java/com/bobocode/flight_search/service/Flights.java b/4-0-object-oriented-programming/src/main/java/com/bobocode/flight_search/service/Flights.java new file mode 100644 index 00000000..b6df8ee3 --- /dev/null +++ b/4-0-object-oriented-programming/src/main/java/com/bobocode/flight_search/service/Flights.java @@ -0,0 +1,9 @@ +package com.bobocode.flight_search.service; + +import java.util.Set; + +public interface Flights { + boolean register(String flightNumber); + + Set findAll(); +} From 49adec34ef0005001df9e97ae8574b3127e65287 Mon Sep 17 00:00:00 2001 From: Serhii Date: Tue, 5 Jan 2021 17:07:33 +0200 Subject: [PATCH 05/74] GP-28 Add File Reader solution to exercise/completed --- .../file_reader/FileReaderException.java | 7 ++++ .../com/bobocode/file_reader/FileReaders.java | 34 ++++++++++++++++++- 2 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 3-0-java-core/src/main/java/com/bobocode/file_reader/FileReaderException.java diff --git a/3-0-java-core/src/main/java/com/bobocode/file_reader/FileReaderException.java b/3-0-java-core/src/main/java/com/bobocode/file_reader/FileReaderException.java new file mode 100644 index 00000000..42c44046 --- /dev/null +++ b/3-0-java-core/src/main/java/com/bobocode/file_reader/FileReaderException.java @@ -0,0 +1,7 @@ +package com.bobocode.file_reader; + +public class FileReaderException extends RuntimeException { + public FileReaderException(String message, Exception e) { + super(message, e); + } +} diff --git a/3-0-java-core/src/main/java/com/bobocode/file_reader/FileReaders.java b/3-0-java-core/src/main/java/com/bobocode/file_reader/FileReaders.java index 685d2d47..5e48f4cf 100644 --- a/3-0-java-core/src/main/java/com/bobocode/file_reader/FileReaders.java +++ b/3-0-java-core/src/main/java/com/bobocode/file_reader/FileReaders.java @@ -2,6 +2,17 @@ import com.bobocode.util.ExerciseNotCompletedException; +import java.io.IOException; +import java.net.URISyntaxException; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Objects; +import java.util.stream.Stream; + +import static java.util.stream.Collectors.joining; + /** * {@link FileReaders} provides an API that allow to read whole file into a {@link String} by file name. */ @@ -14,6 +25,27 @@ public class FileReaders { * @return string that holds whole file content */ public static String readWholeFile(String fileName) { - throw new ExerciseNotCompletedException(); //todo + Path filePath = createPathFromFileName(fileName); + try (Stream fileLinesStream = openFileLinesStream(filePath)) { + return fileLinesStream.collect(joining("\n")); + } + } + + private static Path createPathFromFileName(String fileName) { + Objects.requireNonNull(fileName); + URL fileUrl = FileReaders.class.getClassLoader().getResource(fileName); + try { + return Paths.get(fileUrl.toURI()); + } catch (URISyntaxException e) { + throw new FileReaderException("Invalid file URL",e); + } + } + + private static Stream openFileLinesStream(Path filePath) { + try { + return Files.lines(filePath); + } catch (IOException e) { + throw new FileReaderException("Cannot create stream of file lines!", e); + } } } From b9173969454b27429b4f1d6d9d677111a2139e6c Mon Sep 17 00:00:00 2001 From: Taras Boychuk Date: Wed, 6 Jan 2021 12:09:50 +0200 Subject: [PATCH 06/74] GP-28 fix imports --- .../src/main/java/com/bobocode/file_reader/FileReaders.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/3-0-java-core/src/main/java/com/bobocode/file_reader/FileReaders.java b/3-0-java-core/src/main/java/com/bobocode/file_reader/FileReaders.java index 5e48f4cf..060113fd 100644 --- a/3-0-java-core/src/main/java/com/bobocode/file_reader/FileReaders.java +++ b/3-0-java-core/src/main/java/com/bobocode/file_reader/FileReaders.java @@ -1,7 +1,5 @@ package com.bobocode.file_reader; -import com.bobocode.util.ExerciseNotCompletedException; - import java.io.IOException; import java.net.URISyntaxException; import java.net.URL; @@ -37,7 +35,7 @@ private static Path createPathFromFileName(String fileName) { try { return Paths.get(fileUrl.toURI()); } catch (URISyntaxException e) { - throw new FileReaderException("Invalid file URL",e); + throw new FileReaderException("Invalid file URL", e); } } From 947b71eeaee292b2cb08e16818d17739c9c8a52f Mon Sep 17 00:00:00 2001 From: Taras Boychuk Date: Wed, 6 Jan 2021 14:56:37 +0200 Subject: [PATCH 07/74] GP-25 add new README.md for functional programming basics --- 5-0-functional-programming/README.md | 84 +++++++++++++++++++++++++++- 1 file changed, 83 insertions(+), 1 deletion(-) diff --git a/5-0-functional-programming/README.md b/5-0-functional-programming/README.md index a4406994..bfef3f96 100644 --- a/5-0-functional-programming/README.md +++ b/5-0-functional-programming/README.md @@ -1 +1,83 @@ -# Functional Programming +# Java Functional Programming Basics :muscle: +Learn Java functional programming + +### Into +You know that **Java is an Object-Oriented programming language**, we use objects everywhere (except primitives ๐Ÿ˜‰) and +**functions are not first-class citizens**. So you **cannot store function as variables, pass as arguments or return +as method resultsโ—๏ธ** + +Yes, however... ๐Ÿ™‚ + +Having functions as **first-class citizens is useful!** When building real application quite often **you need to pass +some logic around as function.** But in Java you have to create an object, usually anonymous. + +### Case Study: Search by criteria +Suppose you have a list of accounts, and you need to build a search method that will allow you to find account by +a certain criteria. In java, you will need to create something like this +```java +interface Criteria { + boolean passed(T obj); +} +``` +then you can use that in your search method like this: +```java +public List searchBy(Criteria criteria){ + List foundAccount = new ArrayList<>(); + for (Account a: accounts){ + if (criteria.passed(a)){ + foundAccount.add(a); + } + } + return foundAccounts; +} +``` +Agree? Then if you want to use this method, it will look like this +```java +searchBy(new Criteria() { + @Override + public boolean passed(Account a) { + return a.getEmail().endsWith("@gmail.com"); + } +}); +``` +Most probably you've seen similar examples where you pass a `Runnable` implementation to execute some logic in +a new thread, or you pass `Comparator` implementation to sort a list of objects. That's why having a function +as first-class citizens is not a bad idea. In all cases mentioned **we pass objects, but what we want to pass +is a functionโ—๏ธ** How would passing a function look like? ๐Ÿค” Maybe something like +```java +searchBy(a -> a.getEmail().endsWith("@gmail.com")); +``` +### Summary +The main idea is to keep Java OO language but **enable some functional programming features**. It does two things: +* makes the code more concise +* allows easier parallelization + +Java SE 8+ provides a rich API that enables functional programming features based on +* Functional Interfaces +* Lambdas +* Stream API +* Optional API + +Once you've mastered all those stuff, instead of this +```java +public List findAllGmailAccounts(List accounts) { + List gmailAccounts = new ArrayList<>(); + for (Account account : accounts) { + if (account.getEmail().endsWith("@gmail.com")) { + gmailAccounts.add(account); + } + } + return gmailAccounts; +} +``` +you'll be writing this +```java +public List findAllGmailAccounts(List accounts) { + return accounts.stream() + .filter(a -> a.getEmail().endsWith("@gmail.com")) + .collect(toList()); +} +``` + +โšก๏ธ Nowadays **every Java developer uses some functional programming features in an everyday job**. So make sure you've +check out the rest of the materials in this module and build strong skills using these language features! ๐Ÿ’ช \ No newline at end of file From 47923cb91ec17725045db8d1cb41f05464ac3c06 Mon Sep 17 00:00:00 2001 From: Taras Boychuk Date: Wed, 6 Jan 2021 15:11:01 +0200 Subject: [PATCH 08/74] GP-25 add Lear or Skip section --- 5-0-functional-programming/README.md | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/5-0-functional-programming/README.md b/5-0-functional-programming/README.md index bfef3f96..8c21c5d4 100644 --- a/5-0-functional-programming/README.md +++ b/5-0-functional-programming/README.md @@ -1,7 +1,7 @@ # Java Functional Programming Basics :muscle: Learn Java functional programming -### Into +### Intro You know that **Java is an Object-Oriented programming language**, we use objects everywhere (except primitives ๐Ÿ˜‰) and **functions are not first-class citizens**. So you **cannot store function as variables, pass as arguments or return as method resultsโ—๏ธ** @@ -80,4 +80,25 @@ public List findAllGmailAccounts(List accounts) { ``` โšก๏ธ Nowadays **every Java developer uses some functional programming features in an everyday job**. So make sure you've -check out the rest of the materials in this module and build strong skills using these language features! ๐Ÿ’ช \ No newline at end of file +check out the rest of the materials in this module and build strong skills using these language features! ๐Ÿ’ช + +### Learn or skip ? +Think you're cool enough to skip this topic? ๐Ÿ˜Ž Hand on a sec...โ˜๏ธ Can you easily understand and write lambdas like this? +```java +runnable -> () -> { + Thread t = new Thread(runnable); + t.start(); + return t; +}; +``` +or stream pipelines like this ? ๐Ÿง +```java +accounts.stream() + .flatMap(a -> Stream.of(a.getFirstName(), a.getLastName())) + .map(String::toLowerCase) + .flatMapToInt(String::chars) + .mapToObj(c -> (char) c) + .collect(groupingBy(identity(), counting())); +``` +No worries if you don't! Be patient, **do the exercises in this module**, and you will be able to do not only this ๐Ÿ‘†. +**You will understand and use functional programming techniques far beyond your expectations** ๐Ÿ”ฅ \ No newline at end of file From b563231cdcf584fd7b47ef47db270a079b9b063c Mon Sep 17 00:00:00 2001 From: Serhii Date: Wed, 13 Jan 2021 13:18:45 +0200 Subject: [PATCH 09/74] GP-32 Update Stack(0-2 + 0-6) for completed --- .../java/com/bobocode/stack/LinkedStack.java | 48 +++++++-- .../bobocode/stack/EmptyStackException.java | 5 + .../java/com/bobocode/stack/LinkedStack.java | 101 ++++++++++++++++++ .../java/com/bobocode/stack/StackTest.java | 73 ++++++++++++- 4 files changed, 220 insertions(+), 7 deletions(-) create mode 100644 6-0-test-driven-development/src/main/java/com/bobocode/stack/EmptyStackException.java create mode 100644 6-0-test-driven-development/src/main/java/com/bobocode/stack/LinkedStack.java diff --git a/2-0-data-structures-and-algorithms/src/main/java/com/bobocode/stack/LinkedStack.java b/2-0-data-structures-and-algorithms/src/main/java/com/bobocode/stack/LinkedStack.java index 3f55d096..c4b6f49c 100644 --- a/2-0-data-structures-and-algorithms/src/main/java/com/bobocode/stack/LinkedStack.java +++ b/2-0-data-structures-and-algorithms/src/main/java/com/bobocode/stack/LinkedStack.java @@ -1,7 +1,9 @@ package com.bobocode.stack; import com.bobocode.stack.exception.EmptyStackException; -import com.bobocode.util.ExerciseNotCompletedException; + +import java.util.Objects; +import java.util.stream.Stream; /** * {@link LinkedStack} represents a last-in-first-out (LIFO) stack of objects that is based on singly linked generic nodes. @@ -10,6 +12,21 @@ * @param generic type parameter */ public class LinkedStack implements Stack { + private static class Node { + T element; + Node next; + + public static Node valueOf(T element) { + return new Node<>(element); + } + + private Node(T element) { + this.element = element; + } + } + + private Node head; + private int size = 0; /** * This method creates a stack of provided elements @@ -19,7 +36,9 @@ public class LinkedStack implements Stack { * @return a new stack of elements that were passed as method parameters */ public static LinkedStack of(T... elements) { - throw new ExerciseNotCompletedException(); // todo: implement this method + LinkedStack linkedStack = new LinkedStack<>(); + Stream.of(elements).forEach(linkedStack::push); + return linkedStack; } /** @@ -30,7 +49,13 @@ public static LinkedStack of(T... elements) { */ @Override public void push(T element) { - throw new ExerciseNotCompletedException(); // todo: implement this method + Objects.requireNonNull(element); + Node newNode = Node.valueOf(element); + if (head != null) { + newNode.next = head; + } + head = newNode; + size++; } /** @@ -42,7 +67,18 @@ public void push(T element) { */ @Override public T pop() { - throw new ExerciseNotCompletedException(); // todo: implement this method + if (head != null) { + size--; + return retrieveHead(); + } else { + throw new EmptyStackException(); + } + } + + private T retrieveHead() { + T element = head.element; + this.head = head.next; + return element; } /** @@ -52,7 +88,7 @@ public T pop() { */ @Override public int size() { - throw new ExerciseNotCompletedException(); // todo: implement this method + return size; } /** @@ -62,6 +98,6 @@ public int size() { */ @Override public boolean isEmpty() { - throw new ExerciseNotCompletedException(); // todo: implement this method; + return head == null; } } diff --git a/6-0-test-driven-development/src/main/java/com/bobocode/stack/EmptyStackException.java b/6-0-test-driven-development/src/main/java/com/bobocode/stack/EmptyStackException.java new file mode 100644 index 00000000..68b78a2f --- /dev/null +++ b/6-0-test-driven-development/src/main/java/com/bobocode/stack/EmptyStackException.java @@ -0,0 +1,5 @@ +package com.bobocode.stack; + +public class EmptyStackException extends RuntimeException{ + +} diff --git a/6-0-test-driven-development/src/main/java/com/bobocode/stack/LinkedStack.java b/6-0-test-driven-development/src/main/java/com/bobocode/stack/LinkedStack.java new file mode 100644 index 00000000..a710d330 --- /dev/null +++ b/6-0-test-driven-development/src/main/java/com/bobocode/stack/LinkedStack.java @@ -0,0 +1,101 @@ +package com.bobocode.stack; + +import java.util.Objects; +import java.util.stream.Stream; + +/** + * {@link LinkedStack} represents a last-in-first-out (LIFO) stack of objects that is based on singly linked generic nodes. + * A node is implemented as inner static class {@link Node}. + * + * @param generic type parameter + */ +public class LinkedStack implements Stack { + private static class Node { + T element; + Node next; + + public static Node valueOf(T element) { + return new Node<>(element); + } + + private Node(T element) { + this.element = element; + } + } + + private Node head; + private int size = 0; + + /** + * This method creates a stack of provided elements + * + * @param elements elements to add + * @param generic type + * @return a new stack of elements that were passed as method parameters + */ + public static LinkedStack of(T... elements) { + LinkedStack linkedStack = new LinkedStack<>(); + Stream.of(elements).forEach(linkedStack::push); + return linkedStack; + } + + /** + * The method pushes an element onto the top of this stack. This has exactly the same effect as: + * addElement(item) + * + * @param element elements to add + */ + @Override + public void push(T element) { + Objects.requireNonNull(element); + Node newNode = Node.valueOf(element); + if (head != null) { + newNode.next = head; + } + head = newNode; + size++; + } + + /** + * This method removes the object at the top of this stack + * and returns that object as the value of this function. + * + * @return The object at the top of this stack + * @throws EmptyStackException - if this stack is empty + */ + @Override + public T pop() { + if (head != null) { + size--; + return retrieveHead(); + } else { + throw new EmptyStackException(); + } + } + + private T retrieveHead() { + T element = head.element; + this.head = head.next; + return element; + } + + /** + * Returns the number of elements in the stack + * + * @return number of elements + */ + @Override + public int size() { + return size; + } + + /** + * Checks if a stack is empty + * + * @return {@code true} if a stack is empty, {@code false} otherwise + */ + @Override + public boolean isEmpty() { + return head == null; + } +} diff --git a/6-0-test-driven-development/src/test/java/com/bobocode/stack/StackTest.java b/6-0-test-driven-development/src/test/java/com/bobocode/stack/StackTest.java index ca2774b0..20c37cb7 100644 --- a/6-0-test-driven-development/src/test/java/com/bobocode/stack/StackTest.java +++ b/6-0-test-driven-development/src/test/java/com/bobocode/stack/StackTest.java @@ -1,4 +1,75 @@ package com.bobocode.stack; -public class StackTest { +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; + +class StackTest { + + private Stack intStack = new LinkedStack<>(); + + @Test + void pushAndPopElementOntoEmptyStack() { + intStack.push(243); + + assertThat(intStack.pop()).isEqualTo(243); + } + + @Test + void popElementFromEmptyStack() { + assertThrows(EmptyStackException.class, () -> intStack.pop()); + } + + @Test + void pushElements() { + intStack = LinkedStack.of(23, 35, 72); + + intStack.push(55); + + assertThat(intStack.pop()).isEqualTo(55); + } + + @Test + void popElements() { + intStack = LinkedStack.of(87, 53, 66); + + intStack.pop(); + intStack.push(234); + Integer lastElement = intStack.pop(); + + assertThat(lastElement).isEqualTo(234); + } + + @Test + void size() { + intStack = LinkedStack.of(87, 53, 66); + + int actualSize = intStack.size(); + + assertThat(actualSize).isEqualTo(3); + } + + @Test + void sizeOnEmptyStack() { + int actualSize = intStack.size(); + + assertThat(actualSize).isEqualTo(0); + } + + @Test + void isEmpty() { + intStack = LinkedStack.of(87, 53, 66); + + boolean stackEmpty = intStack.isEmpty(); + + assertThat(stackEmpty).isEqualTo(false); + } + + @Test + void isEmptyOnEmptyStack() { + boolean stackEmpty = intStack.isEmpty(); + + assertThat(stackEmpty).isEqualTo(true); + } } From 4daf746188cd1f2316f28807ab7d97150489ae3c Mon Sep 17 00:00:00 2001 From: Serhii Date: Thu, 14 Jan 2021 11:43:11 +0200 Subject: [PATCH 10/74] GP-32 Fix the tdd StackTest --- .../src/test/java/com/bobocode/stack/StackTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/6-0-test-driven-development/src/test/java/com/bobocode/stack/StackTest.java b/6-0-test-driven-development/src/test/java/com/bobocode/stack/StackTest.java index 20c37cb7..cb6c06f8 100644 --- a/6-0-test-driven-development/src/test/java/com/bobocode/stack/StackTest.java +++ b/6-0-test-driven-development/src/test/java/com/bobocode/stack/StackTest.java @@ -5,7 +5,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertThrows; -class StackTest { +public class StackTest { private Stack intStack = new LinkedStack<>(); From d55d67a1e86598f2c0f2d54047c383f5396e467d Mon Sep 17 00:00:00 2001 From: Serhii Date: Wed, 13 Jan 2021 17:12:28 +0200 Subject: [PATCH 11/74] GP-33 Add ArrayList completed solution --- .../com/bobocode/array_list/ArrayList.java | 87 +++++++++++++++---- 1 file changed, 72 insertions(+), 15 deletions(-) diff --git a/2-0-data-structures-and-algorithms/src/main/java/com/bobocode/array_list/ArrayList.java b/2-0-data-structures-and-algorithms/src/main/java/com/bobocode/array_list/ArrayList.java index 81de865a..e7a3f155 100644 --- a/2-0-data-structures-and-algorithms/src/main/java/com/bobocode/array_list/ArrayList.java +++ b/2-0-data-structures-and-algorithms/src/main/java/com/bobocode/array_list/ArrayList.java @@ -1,7 +1,10 @@ package com.bobocode.array_list; import com.bobocode.linked_list.List; -import com.bobocode.util.ExerciseNotCompletedException; + +import java.util.Arrays; +import java.util.NoSuchElementException; +import java.util.Objects; /** * {@link ArrayList} is an implementation of {@link List} interface. This resizable data structure @@ -9,6 +12,10 @@ */ public class ArrayList implements List { + public static final int DEFAULT_CAPACITY = 5; + private Object[] elementData; + private int size; + /** * This constructor creates an instance of {@link ArrayList} with a specific capacity of an array inside. * @@ -16,7 +23,11 @@ public class ArrayList implements List { * @throws IllegalArgumentException โ€“ if the specified initial capacity is negative or 0. */ public ArrayList(int initCapacity) { - throw new ExerciseNotCompletedException(); // todo: implement this method + if (initCapacity > 0) { + elementData = new Object[initCapacity]; + } else { + throw new IllegalArgumentException(); + } } /** @@ -24,7 +35,7 @@ public ArrayList(int initCapacity) { * A default size of inner array is 5; */ public ArrayList() { - throw new ExerciseNotCompletedException(); // todo: implement this method + elementData = new Object[DEFAULT_CAPACITY]; } /** @@ -34,7 +45,11 @@ public ArrayList() { * @return new instance */ public static List of(T... elements) { - throw new ExerciseNotCompletedException(); // todo: implement this method + List list = new ArrayList<>(elements.length); + for (T element : elements) { + list.add(element); + } + return list; } /** @@ -44,7 +59,15 @@ public static List of(T... elements) { */ @Override public void add(T element) { - throw new ExerciseNotCompletedException(); // todo: implement this method + increaseDataArrayIfFull(); + elementData[size] = element; + size++; + } + + private void increaseDataArrayIfFull() { + if (elementData.length <= size) { + elementData = getTrimmedArrayToSize(elementData.length * 2); + } } /** @@ -55,7 +78,10 @@ public void add(T element) { */ @Override public void add(int index, T element) { - throw new ExerciseNotCompletedException(); // todo: implement this method + increaseDataArrayIfFull(); + System.arraycopy(elementData, index, elementData, index + 1, size - index); + elementData[index] = element; + size++; } /** @@ -66,8 +92,10 @@ public void add(int index, T element) { * @return en element */ @Override + @SuppressWarnings("unchecked") public T get(int index) { - throw new ExerciseNotCompletedException(); // todo: implement this method + Objects.checkIndex(index, size); + return (T) elementData[index]; } /** @@ -77,8 +105,12 @@ public T get(int index) { * @throws java.util.NoSuchElementException if list is empty */ @Override + @SuppressWarnings("unchecked") public T getFirst() { - throw new ExerciseNotCompletedException(); // todo: implement this method + if (isEmpty()) { + throw new NoSuchElementException(); + } + return (T) elementData[0]; } /** @@ -88,8 +120,12 @@ public T getFirst() { * @throws java.util.NoSuchElementException if list is empty */ @Override + @SuppressWarnings("unchecked") public T getLast() { - throw new ExerciseNotCompletedException(); // todo: implement this method + if (isEmpty()) { + throw new NoSuchElementException(); + } + return (T) elementData[size - 1]; } /** @@ -101,7 +137,8 @@ public T getLast() { */ @Override public void set(int index, T element) { - throw new ExerciseNotCompletedException(); // todo: implement this method + Objects.checkIndex(index, size); + elementData[index] = element; } /** @@ -112,7 +149,12 @@ public void set(int index, T element) { */ @Override public void remove(int index) { - throw new ExerciseNotCompletedException(); // todo: implement this method + if (index == size - 1) { + elementData = getTrimmedArrayToSize(size - 1); + } else { + System.arraycopy(elementData, index + 1, elementData, index, size - index - 1); + } + size--; } /** @@ -123,7 +165,16 @@ public void remove(int index) { */ @Override public boolean contains(T element) { - throw new ExerciseNotCompletedException(); // todo: implement this method + if (isEmpty()) { + return false; + } else { + for (Object elem : elementData) { + if (elem.equals(element)) { + return true; + } + } + } + return false; } /** @@ -133,7 +184,12 @@ public boolean contains(T element) { */ @Override public boolean isEmpty() { - throw new ExerciseNotCompletedException(); // todo: implement this method + return size == 0; + } + + @SuppressWarnings("unchecked") + private T[] getTrimmedArrayToSize(int size) { + return (T[]) Arrays.copyOf(elementData, size); } /** @@ -141,7 +197,7 @@ public boolean isEmpty() { */ @Override public int size() { - throw new ExerciseNotCompletedException(); // todo: implement this method + return size; } /** @@ -149,6 +205,7 @@ public int size() { */ @Override public void clear() { - throw new ExerciseNotCompletedException(); // todo: implement this method + elementData = new Object[DEFAULT_CAPACITY]; + size = 0; } } From d574ffd1aae7058a7b292bc5e59e23b31df7a127 Mon Sep 17 00:00:00 2001 From: Maksym Stasiuk Date: Sat, 16 Jan 2021 15:25:45 +0200 Subject: [PATCH 12/74] GP-29: complete FileStats.java --- .../com/bobocode/file_stats/FileStats.java | 76 +++++++++++++++++-- 1 file changed, 71 insertions(+), 5 deletions(-) diff --git a/3-0-java-core/src/main/java/com/bobocode/file_stats/FileStats.java b/3-0-java-core/src/main/java/com/bobocode/file_stats/FileStats.java index b5d3328a..c2983945 100644 --- a/3-0-java-core/src/main/java/com/bobocode/file_stats/FileStats.java +++ b/3-0-java-core/src/main/java/com/bobocode/file_stats/FileStats.java @@ -1,10 +1,28 @@ package com.bobocode.file_stats; +import java.io.IOException; +import java.net.URISyntaxException; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Comparator; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Stream; + +import static java.util.function.Function.identity; +import static java.util.stream.Collectors.counting; +import static java.util.stream.Collectors.groupingBy; + /** * {@link FileStats} provides an API that allow to get character statistic based on text file. All whitespace characters * are ignored. */ public class FileStats { + private final Map characterCountMap; + private final char mostPopularCharacter; + /** * Creates a new immutable {@link FileStats} objects using data from text file received as a parameter. * @@ -12,7 +30,47 @@ public class FileStats { * @return new FileStats object created from text file */ public static FileStats from(String fileName) { - throw new UnsupportedOperationException("It's your job to make it work!"); //todo + return new FileStats(fileName); + } + + private FileStats(String fileName) { + Path filePath = getFilePath(fileName); + characterCountMap = computeCharacterMap(filePath); + mostPopularCharacter = findMostPopularCharacter(characterCountMap); + } + + private Path getFilePath(String fileName) { + Objects.requireNonNull(fileName); + URL fileUrl = getFileUrl(fileName); + try { + return Paths.get(fileUrl.toURI()); + } catch (URISyntaxException e) { + throw new FileStatsException("Wrong file path", e); + } + } + + private URL getFileUrl(String fileName) { + URL fileUrl = getClass().getClassLoader().getResource(fileName); + if (fileUrl == null) { + throw new FileStatsException("Wrong file path"); + } + return fileUrl; + } + + private Map computeCharacterMap(Path filePath) { + try (Stream lines = Files.lines(filePath)) { + return collectCharactersToCountMap(lines); + } catch (IOException e) { + throw new FileStatsException("Cannot read the file", e); + } + } + + private Map collectCharactersToCountMap(Stream linesStream) { + return linesStream + .flatMapToInt(String::chars) + .filter(a -> a != 32) // filter whitespace + .mapToObj(c -> (char) c) + .collect(groupingBy(identity(), counting())); } /** @@ -22,7 +80,7 @@ public static FileStats from(String fileName) { * @return a number that shows how many times this character appeared in a text file */ public int getCharCount(char character) { - throw new UnsupportedOperationException("It's your job to make it work!"); //todo + return characterCountMap.get(character).intValue(); } /** @@ -31,7 +89,15 @@ public int getCharCount(char character) { * @return the most frequently appeared character */ public char getMostPopularCharacter() { - throw new UnsupportedOperationException("It's your job to make it work!"); //todo + return mostPopularCharacter; + } + + private char findMostPopularCharacter(Map characterCountMap) { + return characterCountMap.entrySet() + .stream() + .max(Comparator.comparing(Map.Entry::getValue)) + .get() + .getKey(); } /** @@ -41,6 +107,6 @@ public char getMostPopularCharacter() { * @return {@code true} if this character has appeared in the text, and {@code false} otherwise */ public boolean containsCharacter(char character) { - throw new UnsupportedOperationException("It's your job to make it work!"); //todo + return characterCountMap.containsKey(character); } -} +} \ No newline at end of file From 3a9ca66cfae02c84aba4a6a159dfee671d4f42ac Mon Sep 17 00:00:00 2001 From: Serhii Date: Wed, 20 Jan 2021 17:30:29 +0200 Subject: [PATCH 13/74] GP-41 Update completed methods --- .../main/java/com/bobocode/array_list/ArrayList.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/2-0-data-structures-and-algorithms/src/main/java/com/bobocode/array_list/ArrayList.java b/2-0-data-structures-and-algorithms/src/main/java/com/bobocode/array_list/ArrayList.java index a186e2c5..8c750f0a 100644 --- a/2-0-data-structures-and-algorithms/src/main/java/com/bobocode/array_list/ArrayList.java +++ b/2-0-data-structures-and-algorithms/src/main/java/com/bobocode/array_list/ArrayList.java @@ -149,13 +149,13 @@ public void set(int index, T element) { * @return deleted element */ @Override + @SuppressWarnings("unchecked") public T remove(int index) { - if (index == size - 1) { - elementData = getTrimmedArrayToSize(size - 1); - } else { - System.arraycopy(elementData, index + 1, elementData, index, size - index - 1); - } + Objects.checkIndex(index, size); + T deletedElement = (T) elementData[index]; + System.arraycopy(elementData, index + 1, elementData, index, size - index - 1); size--; + return deletedElement; } /** From 0185b13840028d64576ec7f6e432d7e3f4e7ce06 Mon Sep 17 00:00:00 2001 From: Serhii Date: Wed, 20 Jan 2021 19:26:16 +0200 Subject: [PATCH 14/74] GP-41 Update completed solution --- .../src/main/java/com/bobocode/array_list/ArrayList.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/2-0-data-structures-and-algorithms/src/main/java/com/bobocode/array_list/ArrayList.java b/2-0-data-structures-and-algorithms/src/main/java/com/bobocode/array_list/ArrayList.java index 8c750f0a..416ea8ad 100644 --- a/2-0-data-structures-and-algorithms/src/main/java/com/bobocode/array_list/ArrayList.java +++ b/2-0-data-structures-and-algorithms/src/main/java/com/bobocode/array_list/ArrayList.java @@ -12,7 +12,7 @@ */ public class ArrayList implements List { - public static final int DEFAULT_CAPACITY = 5; + private static final int DEFAULT_CAPACITY = 5; private Object[] elementData; private int size; @@ -65,8 +65,8 @@ public void add(T element) { } private void increaseDataArrayIfFull() { - if (elementData.length <= size) { - elementData = getTrimmedArrayToSize(elementData.length * 2); + if (elementData.length == size) { + elementData = Arrays.copyOf(elementData, size * 2); } } From a8cbabf88bd001134a3ff8d12eccc93608acd943 Mon Sep 17 00:00:00 2001 From: Serhii Date: Thu, 21 Jan 2021 09:59:25 +0200 Subject: [PATCH 15/74] GP-38 Fix test name in tdd completed --- .../src/test/java/com/bobocode/stack/StackTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/6-0-test-driven-development/src/test/java/com/bobocode/stack/StackTest.java b/6-0-test-driven-development/src/test/java/com/bobocode/stack/StackTest.java index cb6c06f8..2c4a0a51 100644 --- a/6-0-test-driven-development/src/test/java/com/bobocode/stack/StackTest.java +++ b/6-0-test-driven-development/src/test/java/com/bobocode/stack/StackTest.java @@ -10,7 +10,7 @@ public class StackTest { private Stack intStack = new LinkedStack<>(); @Test - void pushAndPopElementOntoEmptyStack() { + void pushElementOntoEmptyStack() { intStack.push(243); assertThat(intStack.pop()).isEqualTo(243); From 610d12b9bad57c8bf4505c57b60a9f89c5825bbf Mon Sep 17 00:00:00 2001 From: Serhii Date: Thu, 21 Jan 2021 14:37:05 +0200 Subject: [PATCH 16/74] GP-37 add completed solution for LinkedList in TDD --- .../com/bobocode/linked_list/LinkedList.java | 129 ++++++- .../bobocode/linked_list/LinkedListTest.java | 358 ++++++++++++++++++ 2 files changed, 473 insertions(+), 14 deletions(-) diff --git a/6-0-test-driven-development/src/main/java/com/bobocode/linked_list/LinkedList.java b/6-0-test-driven-development/src/main/java/com/bobocode/linked_list/LinkedList.java index acb14a11..5243abfb 100644 --- a/6-0-test-driven-development/src/main/java/com/bobocode/linked_list/LinkedList.java +++ b/6-0-test-driven-development/src/main/java/com/bobocode/linked_list/LinkedList.java @@ -1,7 +1,8 @@ package com.bobocode.linked_list; - -import com.bobocode.util.ExerciseNotCompletedException; +import java.util.NoSuchElementException; +import java.util.Objects; +import java.util.stream.Stream; /** * {@link LinkedList} is a list implementation that is based on singly linked generic nodes. A node is implemented as @@ -10,6 +11,9 @@ * @param generic type parameter */ public class LinkedList implements List { + private Node head; + private Node tail; + private int size; /** * This method creates a list of provided elements @@ -19,7 +23,9 @@ public class LinkedList implements List { * @return a new list of elements the were passed as method parameters */ public static List of(T... elements) { - throw new ExerciseNotCompletedException(); // todo: implement this method + LinkedList linkedList = new LinkedList<>(); + Stream.of(elements).forEach(linkedList::add); + return linkedList; } /** @@ -29,7 +35,7 @@ public static List of(T... elements) { */ @Override public void add(T element) { - throw new ExerciseNotCompletedException(); // todo: implement this method + add(size, element); } /** @@ -41,7 +47,51 @@ public void add(T element) { */ @Override public void add(int index, T element) { - throw new ExerciseNotCompletedException(); // todo: implement this method + Node newNode = Node.valueOf(element); + if (index == 0) { + addAsHead(newNode); + } else if (index == size) { + addAsTail(newNode); + } else { + add(index, newNode); + } + size++; + } + + private void addAsHead(Node newNode) { + newNode.next = head; + head = newNode; + if (head.next == null) { + tail = head; + } + } + + private void addAsTail(Node newNode) { + tail.next = newNode; + tail = newNode; + } + + private void add(int index, Node newNode) { + Node node = findNodeByIndex(index - 1); + newNode.next = node.next; + node.next = newNode; + } + + private Node findNodeByIndex(int index) { + Objects.checkIndex(index, size); + if (index == size - 1) { + return tail; + } else { + return nodeAt(index); + } + } + + private Node nodeAt(int index) { + Node currentNode = head; + for (int i = 0; i < index; i++) { + currentNode = currentNode.next; + } + return currentNode; } /** @@ -53,7 +103,8 @@ public void add(int index, T element) { */ @Override public void set(int index, T element) { - throw new ExerciseNotCompletedException(); // todo: implement this method + Node node = findNodeByIndex(index); + node.value = element; } /** @@ -65,7 +116,8 @@ public void set(int index, T element) { */ @Override public T get(int index) { - throw new ExerciseNotCompletedException(); // todo: implement this method + Node node = findNodeByIndex(index); + return node.value; } /** @@ -76,7 +128,8 @@ public T get(int index) { */ @Override public T getFirst() { - throw new ExerciseNotCompletedException(); // todo: implement this method + checkElementsExist(); + return head.value; } /** @@ -87,7 +140,14 @@ public T getFirst() { */ @Override public T getLast() { - throw new ExerciseNotCompletedException(); // todo: implement this method + checkElementsExist(); + return tail.value; + } + + private void checkElementsExist() { + if (head == null) { + throw new NoSuchElementException(); + } } /** @@ -99,9 +159,29 @@ public T getLast() { */ @Override public T remove(int index) { - throw new ExerciseNotCompletedException(); // todo: implement this method + T deletedElement = null; + if (index == 0) { + Objects.checkIndex(index, size); + deletedElement = head.value; + removeHead(); + } else { + Node previousNode = findNodeByIndex(index - 1); + deletedElement = previousNode.next.value; + previousNode.next = previousNode.next.next; + if (index == size - 1) { + tail = previousNode; + } + } + size--; + return deletedElement; } + private void removeHead() { + head = head.next; + if (head == null) { + tail = null; + } + } /** * Checks if a specific exists in he list @@ -110,7 +190,14 @@ public T remove(int index) { */ @Override public boolean contains(T element) { - throw new ExerciseNotCompletedException(); // todo: implement this method + Node currentNode = head; + while (currentNode != null) { + if (currentNode.value.equals(element)) { + return true; + } + currentNode = currentNode.next; + } + return false; } /** @@ -120,7 +207,7 @@ public boolean contains(T element) { */ @Override public boolean isEmpty() { - throw new ExerciseNotCompletedException(); // todo: implement this method + return head == null; } /** @@ -130,7 +217,7 @@ public boolean isEmpty() { */ @Override public int size() { - throw new ExerciseNotCompletedException(); // todo: implement this method + return size; } /** @@ -138,6 +225,20 @@ public int size() { */ @Override public void clear() { - throw new ExerciseNotCompletedException(); // todo: implement this method + head = tail = null; + size = 0; + } + + static class Node { + private T value; + private Node next; + + private Node(T value) { + this.value = value; + } + + public static Node valueOf(T value) { + return new Node<>(value); + } } } diff --git a/6-0-test-driven-development/src/test/java/com/bobocode/linked_list/LinkedListTest.java b/6-0-test-driven-development/src/test/java/com/bobocode/linked_list/LinkedListTest.java index 68e7637c..fd3c050c 100644 --- a/6-0-test-driven-development/src/test/java/com/bobocode/linked_list/LinkedListTest.java +++ b/6-0-test-driven-development/src/test/java/com/bobocode/linked_list/LinkedListTest.java @@ -1,4 +1,362 @@ package com.bobocode.linked_list; + +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; + +import java.util.NoSuchElementException; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.assertj.core.api.AssertionsForClassTypes.assertThatExceptionOfType; + +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) public class LinkedListTest { + + private List intList = new LinkedList<>(); + + @Test + @Order(1) + void addIntoEmptyList() { + intList.add(41); + + int element = intList.get(0); + + assertThat(element).isEqualTo(41); + assertThat(intList.size()).isEqualTo(1); + } + + @Test + @Order(2) + void getFirstElementFromSingleElementList() { + intList.add(25); + + int element = intList.get(0); + + assertThat(element).isEqualTo(25); + } + + @Test + @Order(3) + void addElements() { + intList = LinkedList.of(43, 233, 54); + + assertThat(intList.size()).isEqualTo(3); + assertThat(intList.get(0)).isEqualTo(43); + assertThat(intList.get(1)).isEqualTo(233); + assertThat(intList.get(2)).isEqualTo(54); + } + + @Test + @Order(4) + void size() { + intList = LinkedList.of(4, 7, 9, 0, 7); + + int size = intList.size(); + + assertThat(size).isEqualTo(5); + } + + @Test + @Order(5) + void getFirstElement() { + intList = LinkedList.of(31, 32); + + int firstElement = intList.getFirst(); + assertThat(firstElement).isEqualTo(31); + } + + @Test + @Order(6) + void getLastElement() { + intList = LinkedList.of(41, 42); + + int lastElement = intList.getLast(); + + assertThat(lastElement).isEqualTo(42); + } + + @Test + @Order(7) + void getFirstOfEmptyList() { + assertThatExceptionOfType(NoSuchElementException.class) + .isThrownBy(() -> intList.getFirst()); + } + + @Test + @Order(8) + void getLastOfEmptyList() { + assertThatExceptionOfType(NoSuchElementException.class) + .isThrownBy(() -> intList.getLast()); + } + + + @Test + @Order(9) + void getElements() { + intList = LinkedList.of(25, 87, 45); + + int firstElement = intList.get(0); + int secondElement = intList.get(1); + int thirdElement = intList.get(2); + + assertThat(firstElement).isEqualTo(25); + assertThat(secondElement).isEqualTo(87); + assertThat(thirdElement).isEqualTo(45); + + } + + @Test + @Order(10) + void addElementByZeroIndexIntoEmptyList() { + intList.add(0, 45); + + int element = intList.get(0); + + assertThat(element).isEqualTo(45); + assertThat(intList.size()).isEqualTo(1); + } + + @Test + @Order(11) + void addElementByIndexToTheEndOfList() { + intList = LinkedList.of(98, 64, 23, 1, 3, 4); + + int newElementIndex = intList.size(); + intList.add(newElementIndex, 44); + + assertThat(intList.get(newElementIndex)).isEqualTo(44); + assertThat(intList.size()).isEqualTo(7); + } + + @Test + @Order(12) + void addElementToTheHeadOfNonEmptyList() { + intList = LinkedList.of(4, 6, 8, 9, 0, 2); + + intList.add(0, 53); + + assertThat(intList.get(0)).isEqualTo(53); + assertThat(intList.get(1)).isEqualTo(4); + assertThat(intList.size()).isEqualTo(7); + } + + @Test + @Order(13) + void addElementByIndex() { + intList = LinkedList.of(43, 5, 6, 8); + + int newElementIdx = 2; + intList.add(newElementIdx, 66); + + assertThat(intList.get(newElementIdx)).isEqualTo(66); + assertThat(intList.get(0)).isEqualTo(43); + assertThat(intList.get(1)).isEqualTo(5); + assertThat(intList.get(3)).isEqualTo(6); + assertThat(intList.get(4)).isEqualTo(8); + assertThat(intList.size()).isEqualTo(5); + } + + @Test + @Order(14) + void addElementByNegativeIndex() { + assertThatExceptionOfType(IndexOutOfBoundsException.class) + .isThrownBy(() -> intList.add(-1, 66)); + } + + @Test + @Order(15) + void addElementByIndexLargerThanListSize() { + intList = LinkedList.of(4, 6, 11, 9); + + int newElementIdx = 5; + + assertThatExceptionOfType(IndexOutOfBoundsException.class) + .isThrownBy(() -> intList.add(newElementIdx, 88)); + } + + @Test + @Order(16) + void addElementByIndexEqualToSize() { + intList = LinkedList.of(1, 2, 3, 4, 5); // size = 5 + + intList.add(5, 111); + int element = intList.get(5); + + assertThat(element).isEqualTo(111); + assertThat(intList.size()).isEqualTo(6); + } + + @Test + @Order(17) + void setFirstElementOnEmptyTree() { + assertThatExceptionOfType(IndexOutOfBoundsException.class) + .isThrownBy(() -> intList.set(0, 34)); + } + + @Test + @Order(18) + void setElementByIndexEqualToSize() { + intList = LinkedList.of(2, 3, 4); // size = 3 + + assertThatExceptionOfType(IndexOutOfBoundsException.class) + .isThrownBy(() -> intList.set(3, 222)); + } + + @Test + @Order(19) + void setElementByIndex() { + intList = LinkedList.of(34, 78, 9, 8); + + int index = 2; //element = 78 + intList.set(index, 99); + + assertThat(intList.get(index)).isEqualTo(99); + assertThat(intList.get(0)).isEqualTo(34); + assertThat(intList.get(1)).isEqualTo(78); + assertThat(intList.get(3)).isEqualTo(8); + assertThat(intList.size()).isEqualTo(4); + } + + @Test + @Order(20) + void getFirstElementFromEmptyList() { + assertThatExceptionOfType(IndexOutOfBoundsException.class) + .isThrownBy(() -> intList.get(0)); + } + + @Test + @Order(21) + void getElementByNegativeIndex() { + assertThatExceptionOfType(IndexOutOfBoundsException.class) + .isThrownBy(() -> intList.get(-1)); + } + + @Test + @Order(22) + void getElementByIndexEqualsToListSize() { + intList = LinkedList.of(33, 46, 25, 87, 45); + assertThatExceptionOfType(IndexOutOfBoundsException.class) + .isThrownBy(() -> intList.get(5)); + } + + @Test + @Order(23) + void removeElementFromEmptyList() { + assertThatExceptionOfType(IndexOutOfBoundsException.class) + .isThrownBy(() -> intList.remove(234)); + } + + @Test + @Order(24) + void removeFirstElement() { + intList = LinkedList.of(4, 6, 8, 9); + + int deletedElement = intList.remove(0); + + assertThat(intList.get(0)).isEqualTo(6); + assertThat(intList.size()).isEqualTo(3); + assertThat(deletedElement).isEqualTo(4); + } + + @Test + @Order(25) + void removeLastElement() { + intList = LinkedList.of(4, 6, 8, 9); + + int deletedElement = intList.remove(intList.size() - 1); + + assertThat(intList.get(intList.size() - 1)).isEqualTo(8); + assertThat(intList.size()).isEqualTo(3); + assertThat(deletedElement).isEqualTo(9); + } + + @Test + @Order(26) + void removeElement() { + intList = LinkedList.of(1, 2, 3, 4, 5); + + int elementIndex = 2; + int deletedElement = intList.remove(elementIndex); // element = 3 + + assertThat(intList.get(elementIndex)).isEqualTo(4); + assertThat(intList.size()).isEqualTo(4); + assertThat(deletedElement).isEqualTo(3); + } + + @Test + @Order(27) + void containsOnEmptyList() { + boolean contains = intList.contains(34); + + assertThat(contains).isFalse(); + } + + @Test + @Order(28) + void contains() { + intList = LinkedList.of(45, 6, 3, 6); + + boolean containsExistingElement = intList.contains(3); + boolean containsNotExistingElement = intList.contains(54); + + assertThat(containsExistingElement).isTrue(); + assertThat(containsNotExistingElement).isFalse(); + } + + @Test + @Order(29) + void isEmptyOnEmptyList() { + boolean empty = intList.isEmpty(); + + assertThat(empty).isTrue(); + } + + @Test + @Order(30) + void isEmpty() { + intList = LinkedList.of(34, 5, 6); + + boolean empty = intList.isEmpty(); + + assertThat(empty).isFalse(); + } + + @Test + @Order(31) + void sizeOnEmptyList() { + int size = intList.size(); + + assertThat(size).isEqualTo(0); + } + + @Test + @Order(32) + void clearOnEmptyList() { + intList.clear(); + + assertThat(intList.size()).isEqualTo(0); + } + + @Test + @Order(33) + void clearChangesTheSize() { + intList = LinkedList.of(4, 5, 6); + + intList.clear(); + + assertThat(intList.size()).isEqualTo(0); + } + + @Test + @Order(34) + void clearRemovesElements() { + intList = LinkedList.of(4, 5, 6); + + intList.clear(); + + assertThatExceptionOfType(IndexOutOfBoundsException.class) + .isThrownBy(() -> intList.get(0)); + } } From 2cb8c0e745652f03631d677096bdcb797660f8d2 Mon Sep 17 00:00:00 2001 From: Maksym Stasiuk Date: Wed, 20 Jan 2021 01:47:20 +0200 Subject: [PATCH 17/74] GP-40: back-up --- .../com/bobocode/bst/BinarySearchTree.java | 25 +++ .../bobocode/bst/BinarySearchTreeTest.java | 196 ++++++++++++++++++ 2 files changed, 221 insertions(+) create mode 100644 2-0-data-structures-and-algorithms/src/main/java/com/bobocode/bst/BinarySearchTree.java create mode 100644 2-0-data-structures-and-algorithms/src/test/java/com/bobocode/bst/BinarySearchTreeTest.java diff --git a/2-0-data-structures-and-algorithms/src/main/java/com/bobocode/bst/BinarySearchTree.java b/2-0-data-structures-and-algorithms/src/main/java/com/bobocode/bst/BinarySearchTree.java new file mode 100644 index 00000000..ae92ae54 --- /dev/null +++ b/2-0-data-structures-and-algorithms/src/main/java/com/bobocode/bst/BinarySearchTree.java @@ -0,0 +1,25 @@ +package com.bobocode.bst; + +import java.util.function.Consumer; + +/** + * Binary search tree should have an API that allow provides the following functionality: + * - insert an element and return true if it was inserted successfully + * - search an element and return true it element exists + * - get tree size + * - get tree height + * - perform in-order traversal by passing element @{@link Consumer} as a parameter + */ +public interface BinarySearchTree { + + boolean insert(T element); + + boolean search(T element); + + int size(); + + int height(); + + void inOrderTraversal(Consumer consumer); + +} diff --git a/2-0-data-structures-and-algorithms/src/test/java/com/bobocode/bst/BinarySearchTreeTest.java b/2-0-data-structures-and-algorithms/src/test/java/com/bobocode/bst/BinarySearchTreeTest.java new file mode 100644 index 00000000..11a0aacf --- /dev/null +++ b/2-0-data-structures-and-algorithms/src/test/java/com/bobocode/bst/BinarySearchTreeTest.java @@ -0,0 +1,196 @@ +package com.bobocode.bst; + +import com.bobocode.util.ExerciseNotCompletedException; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.mockito.Mockito; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; + +import java.util.ArrayList; +import java.util.List; + +//import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.*; + +/*import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.empty;*/ + +//todo: add Order +//todo: checklist https://bobocode.atlassian.net/l/c/MeYPQPHx +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +class BinarySearchTreeTest { + + @Test + void insertIntoEmptyTree() { + BinarySearchTree bst = createOf(); + boolean inserted = bst.insert(123); + + assertThat(inserted).isTrue(); + } + + @Test + void insertTwoElementsWithSameValue() { + BinarySearchTree bst = createOf(25); + boolean inserted = bst.insert(25); + + assertThat(inserted).isFalse(); + } + + //TODO: rename test and method + @Test + void insertElements() { + BinarySearchTree bst = createOf(10, 9, 11, 8, 12, 7); + //TODO: should insert + //bst.insert() + + assertThat(bst.search(10)).isTrue(); + assertThat(bst.search(9)).isTrue(); + assertThat(bst.search(11)).isTrue(); + assertThat(bst.search(8)).isTrue(); + assertThat(bst.search(12)).isTrue(); + assertThat(bst.search(7)).isTrue(); + } + + //TODO: should be separate test?? + @Test + void searchRootElement() { + BinarySearchTree bst = createOf(44); + + boolean foundExistingElement = bst.search(44); + boolean foundNotExistingElement = bst.search(23423); + + assertThat(foundExistingElement).isTrue(); + assertThat(foundNotExistingElement).isFalse(); + } + + @Test + void searchInEmptyTree() { + BinarySearchTree bst = createOf(); + boolean found = bst.search(55); + + assertThat(found).isFalse(); + } + + @Test + void searchElements() { + BinarySearchTree bst = createOf(234, 54, 12, 544, 21, 10); + + assertThat(bst.search(234)).isTrue(); + assertThat(bst.search(54)).isTrue(); + assertThat(bst.search(12)).isTrue(); + assertThat(bst.search(544)).isTrue(); + assertThat(bst.search(21)).isTrue(); + assertThat(bst.search(10)).isTrue(); + assertThat(bst.search(1000)).isFalse(); + } + + @Test + void sizeOfEmptyTree() { + BinarySearchTree bst = createOf(); + int actualTreeSize = bst.size(); + + assertThat(actualTreeSize).isEqualTo(0); + } + + @Test + void size() { + BinarySearchTree bst = createOf(1, 2, 3, 4, 1); + int actualTreeSize = bst.size(); + + assertThat(actualTreeSize).isEqualTo(4); + } + + @Test + void heightOfEmptyTree() { + BinarySearchTree bst = createOf(); + int actualHeight = bst.height(); + + assertThat(actualHeight).isEqualTo(0); + } + + @Test + void heightOfOneElementTree() { + BinarySearchTree bst = createOf(24); + + int actualHeight = bst.height(); + + //todo: check value, this was initially + assertThat(actualHeight).isEqualTo(0); + } + + /** + * .......10 + * ....../ \ + * .....5 15 + * ..../ \ + * ...1 20 + */ + @Test + void height() { + BinarySearchTree bst = createOf(10, 5, 15, 1, 20); + + int actualHeight = bst.height(); + //todo: change contract, height should be 3 + assertThat(actualHeight).isEqualTo(2); + } + + /** + * ..1 + * ...\ + * ....2 + * .....\ + * ..... 3 + * .......\ + * ........4 + * .........\ + * ..........5 + */ + @Test + void heightOfLikedListTree() { + BinarySearchTree bst = createOf(1, 2, 3, 4, 5); + + int actualHeight = bst.height(); + + //todo: contr. will be changed + assertThat(actualHeight).isEqualTo(4); + } + + @Test + void heightOfSingleElementTree() { + BinarySearchTree bst = createOf(1); + + int actualHeight = bst.height(); + //todo: contr. will be changed + assertThat(actualHeight).isEqualTo(0); + } + + @Test + void inorderTraversalOfEmptyTree() { + BinarySearchTree bst = createOf(); + List treeElementsList = new ArrayList<>(bst.size()); + bst.inOrderTraversal(treeElementsList::add); + + assertThat(treeElementsList).isEmpty(); + } + + @Test + void inorderTraversal() { + BinarySearchTree bst = createOf(324, 23, 14, 1551, 2); + + List treeElementsList = new ArrayList<>(bst.size()); + bst.inOrderTraversal(treeElementsList::add); + + assertThat(bst.size()).isEqualTo(treeElementsList.size()); + assertThat(treeElementsList).contains(2, 14, 23, 324, 1551); + } + + private BinarySearchTree createOf(Integer... elements) { + return Mockito.mock(BinarySearchTree.class, + (InvocationOnMock i) -> {throw new ExerciseNotCompletedException();}); //todo + } +} From 65095248732cf761c03f3c9946a505f096253a05 Mon Sep 17 00:00:00 2001 From: maxstasiuk92 Date: Wed, 20 Jan 2021 19:58:17 +0200 Subject: [PATCH 18/74] back-up --- .../com/bobocode/bst/BinarySearchTree.java | 28 +++++-- .../bobocode/bst/BinarySearchTreeTest.java | 82 ++++++------------- 2 files changed, 44 insertions(+), 66 deletions(-) diff --git a/2-0-data-structures-and-algorithms/src/main/java/com/bobocode/bst/BinarySearchTree.java b/2-0-data-structures-and-algorithms/src/main/java/com/bobocode/bst/BinarySearchTree.java index ae92ae54..9dae6a0a 100644 --- a/2-0-data-structures-and-algorithms/src/main/java/com/bobocode/bst/BinarySearchTree.java +++ b/2-0-data-structures-and-algorithms/src/main/java/com/bobocode/bst/BinarySearchTree.java @@ -2,24 +2,34 @@ import java.util.function.Consumer; -/** - * Binary search tree should have an API that allow provides the following functionality: - * - insert an element and return true if it was inserted successfully - * - search an element and return true it element exists - * - get tree size - * - get tree height - * - perform in-order traversal by passing element @{@link Consumer} as a parameter - */ public interface BinarySearchTree { - + /** + * insert an element + * @return true if element did not exist in the tree and was inserted + */ boolean insert(T element); + //todo: rename + /** + * @return true if element exists in the tree + */ boolean search(T element); + /** + * @return number of elements in the tree + */ int size(); + //todo: rename to depth + /** + * @return 1 + max. number of nodes between root node and any other node(0 - if no elements in the tree; 1 - if there is only root) + */ int height(); + /** + * traverse the tree in element's natural order + * @param consumer accepts ref. to node during traversing + */ void inOrderTraversal(Consumer consumer); } diff --git a/2-0-data-structures-and-algorithms/src/test/java/com/bobocode/bst/BinarySearchTreeTest.java b/2-0-data-structures-and-algorithms/src/test/java/com/bobocode/bst/BinarySearchTreeTest.java index 11a0aacf..f84ff224 100644 --- a/2-0-data-structures-and-algorithms/src/test/java/com/bobocode/bst/BinarySearchTreeTest.java +++ b/2-0-data-structures-and-algorithms/src/test/java/com/bobocode/bst/BinarySearchTreeTest.java @@ -1,10 +1,7 @@ package com.bobocode.bst; import com.bobocode.util.ExerciseNotCompletedException; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.MethodOrderer; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestMethodOrder; +import org.junit.jupiter.api.*; import org.mockito.Mockito; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; @@ -25,68 +22,39 @@ @TestMethodOrder(MethodOrderer.OrderAnnotation.class) class BinarySearchTreeTest { - @Test - void insertIntoEmptyTree() { - BinarySearchTree bst = createOf(); - boolean inserted = bst.insert(123); - - assertThat(inserted).isTrue(); - } - - @Test - void insertTwoElementsWithSameValue() { - BinarySearchTree bst = createOf(25); - boolean inserted = bst.insert(25); - - assertThat(inserted).isFalse(); - } + private static final Integer[] someElements = {10, 9, 11, 8, 12, 7}; - //TODO: rename test and method @Test - void insertElements() { - BinarySearchTree bst = createOf(10, 9, 11, 8, 12, 7); - //TODO: should insert - //bst.insert() - - assertThat(bst.search(10)).isTrue(); - assertThat(bst.search(9)).isTrue(); - assertThat(bst.search(11)).isTrue(); - assertThat(bst.search(8)).isTrue(); - assertThat(bst.search(12)).isTrue(); - assertThat(bst.search(7)).isTrue(); + @Order(1) + void createOfElements() { + BinarySearchTree bst = createOf(someElements); + for (var e: someElements) { + assertThat(bst.search(e)).isTrue(); + } + assertThat(bst.size()).isEqualTo(someElements.length); } - //TODO: should be separate test?? @Test - void searchRootElement() { - BinarySearchTree bst = createOf(44); - - boolean foundExistingElement = bst.search(44); - boolean foundNotExistingElement = bst.search(23423); - - assertThat(foundExistingElement).isTrue(); - assertThat(foundNotExistingElement).isFalse(); - } - - @Test - void searchInEmptyTree() { + @Order(2) + void insertUniqueElements() { BinarySearchTree bst = createOf(); - boolean found = bst.search(55); - - assertThat(found).isFalse(); + for (var e: someElements) { + assertThat(bst.search(e)).isFalse(); //does not exist + assertThat(bst.insert(e)).isTrue(); //do insert + assertThat(bst.search(e)).isTrue(); //and exist + } + assertThat(bst.size()).isEqualTo(someElements.length); } @Test - void searchElements() { - BinarySearchTree bst = createOf(234, 54, 12, 544, 21, 10); - - assertThat(bst.search(234)).isTrue(); - assertThat(bst.search(54)).isTrue(); - assertThat(bst.search(12)).isTrue(); - assertThat(bst.search(544)).isTrue(); - assertThat(bst.search(21)).isTrue(); - assertThat(bst.search(10)).isTrue(); - assertThat(bst.search(1000)).isFalse(); + @Order(3) + void insertNonUniqueElements() { + BinarySearchTree bst = createOf(someElements); + for (var e: someElements) { + assertThat(bst.insert(e)).isFalse(); //do not insert + assertThat(bst.search(e)).isTrue(); //but exists + } + assertThat(bst.size()).isEqualTo(someElements.length); } @Test From 043d0ff0a4e43d7b993ad51887b43b4b4877b69a Mon Sep 17 00:00:00 2001 From: Maksym Stasiuk Date: Thu, 21 Jan 2021 00:11:13 +0200 Subject: [PATCH 19/74] GP-40: BinarySearchTree is clean; BinarySearchTreeTest almost ready --- .../com/bobocode/bst/BinarySearchTree.java | 13 +- .../bobocode/bst/BinarySearchTreeTest.java | 169 +++++++----------- pom.xml | 6 + 3 files changed, 75 insertions(+), 113 deletions(-) diff --git a/2-0-data-structures-and-algorithms/src/main/java/com/bobocode/bst/BinarySearchTree.java b/2-0-data-structures-and-algorithms/src/main/java/com/bobocode/bst/BinarySearchTree.java index 9dae6a0a..5dd6c160 100644 --- a/2-0-data-structures-and-algorithms/src/main/java/com/bobocode/bst/BinarySearchTree.java +++ b/2-0-data-structures-and-algorithms/src/main/java/com/bobocode/bst/BinarySearchTree.java @@ -5,31 +5,28 @@ public interface BinarySearchTree { /** * insert an element - * @return true if element did not exist in the tree and was inserted + * @return true if element did not exist in the tree and was inserted successfully */ boolean insert(T element); - //todo: rename /** - * @return true if element exists in the tree + * @return true if tree contains element */ - boolean search(T element); + boolean contains(T element); /** * @return number of elements in the tree */ int size(); - //todo: rename to depth /** - * @return 1 + max. number of nodes between root node and any other node(0 - if no elements in the tree; 1 - if there is only root) + * @return max. number of transition between root node and any other node; 0 - if tree is empty or contains 1 element */ - int height(); + int depth(); /** * traverse the tree in element's natural order * @param consumer accepts ref. to node during traversing */ void inOrderTraversal(Consumer consumer); - } diff --git a/2-0-data-structures-and-algorithms/src/test/java/com/bobocode/bst/BinarySearchTreeTest.java b/2-0-data-structures-and-algorithms/src/test/java/com/bobocode/bst/BinarySearchTreeTest.java index f84ff224..a213a1de 100644 --- a/2-0-data-structures-and-algorithms/src/test/java/com/bobocode/bst/BinarySearchTreeTest.java +++ b/2-0-data-structures-and-algorithms/src/test/java/com/bobocode/bst/BinarySearchTreeTest.java @@ -2,15 +2,19 @@ import com.bobocode.util.ExerciseNotCompletedException; import org.junit.jupiter.api.*; +import org.junit.jupiter.params.*; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import org.mockito.Mockito; import org.mockito.invocation.InvocationOnMock; -import org.mockito.stubbing.Answer; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; +import java.util.stream.Stream; -//import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.*; +import static org.junit.jupiter.params.provider.Arguments.arguments; /*import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; @@ -26,10 +30,10 @@ class BinarySearchTreeTest { @Test @Order(1) - void createOfElements() { - BinarySearchTree bst = createOf(someElements); + void createWithElements() { + BinarySearchTree bst = createTreeWith(someElements); for (var e: someElements) { - assertThat(bst.search(e)).isTrue(); + assertThat(bst.contains(e)).isTrue(); } assertThat(bst.size()).isEqualTo(someElements.length); } @@ -37,127 +41,82 @@ void createOfElements() { @Test @Order(2) void insertUniqueElements() { - BinarySearchTree bst = createOf(); - for (var e: someElements) { - assertThat(bst.search(e)).isFalse(); //does not exist + BinarySearchTree bst = createTreeWith(); + for (int i = 0; i < someElements.length; i++) { + var e = someElements[i]; + assertThat(bst.contains(e)).isFalse(); //does not contain + assertThat(bst.size()).isEqualTo(i); + assertThat(bst.insert(e)).isTrue(); //do insert - assertThat(bst.search(e)).isTrue(); //and exist + + assertThat(bst.contains(e)).isTrue(); //and contains + assertThat(bst.size()).isEqualTo(i + 1); } - assertThat(bst.size()).isEqualTo(someElements.length); } @Test @Order(3) void insertNonUniqueElements() { - BinarySearchTree bst = createOf(someElements); + BinarySearchTree bst = createTreeWith(someElements); for (var e: someElements) { - assertThat(bst.insert(e)).isFalse(); //do not insert - assertThat(bst.search(e)).isTrue(); //but exists + assertThat(bst.insert(e)).isFalse(); //does not insert + assertThat(bst.contains(e)).isTrue(); //but contains } assertThat(bst.size()).isEqualTo(someElements.length); } - @Test - void sizeOfEmptyTree() { - BinarySearchTree bst = createOf(); - int actualTreeSize = bst.size(); - - assertThat(actualTreeSize).isEqualTo(0); - } - - @Test - void size() { - BinarySearchTree bst = createOf(1, 2, 3, 4, 1); - int actualTreeSize = bst.size(); - - assertThat(actualTreeSize).isEqualTo(4); - } - - @Test - void heightOfEmptyTree() { - BinarySearchTree bst = createOf(); - int actualHeight = bst.height(); - - assertThat(actualHeight).isEqualTo(0); - } - - @Test - void heightOfOneElementTree() { - BinarySearchTree bst = createOf(24); - - int actualHeight = bst.height(); - - //todo: check value, this was initially - assertThat(actualHeight).isEqualTo(0); - } - - /** - * .......10 - * ....../ \ - * .....5 15 - * ..../ \ - * ...1 20 - */ - @Test - void height() { - BinarySearchTree bst = createOf(10, 5, 15, 1, 20); - - int actualHeight = bst.height(); - //todo: change contract, height should be 3 - assertThat(actualHeight).isEqualTo(2); - } - - /** - * ..1 - * ...\ - * ....2 - * .....\ - * ..... 3 - * .......\ - * ........4 - * .........\ - * ..........5 - */ - @Test - void heightOfLikedListTree() { - BinarySearchTree bst = createOf(1, 2, 3, 4, 5); - - int actualHeight = bst.height(); - - //todo: contr. will be changed - assertThat(actualHeight).isEqualTo(4); + @ParameterizedTest + @MethodSource("depthArguments") + @Order(4) + void depth(Integer[] elements, int depth) { + BinarySearchTree bst = createTreeWith(elements); + assertThat(bst.depth()).isEqualTo(depth); } @Test - void heightOfSingleElementTree() { - BinarySearchTree bst = createOf(1); - - int actualHeight = bst.height(); - //todo: contr. will be changed - assertThat(actualHeight).isEqualTo(0); - } + @Order(5) + void inorderTraversal() { + BinarySearchTree bst = createTreeWith(someElements); + Integer[] sortedElements = Arrays.copyOf(someElements, someElements.length); + Arrays.sort(sortedElements); - @Test - void inorderTraversalOfEmptyTree() { - BinarySearchTree bst = createOf(); - List treeElementsList = new ArrayList<>(bst.size()); - bst.inOrderTraversal(treeElementsList::add); + List traversedElements = new ArrayList<>(bst.size()); + bst.inOrderTraversal(traversedElements::add); - assertThat(treeElementsList).isEmpty(); + //assertThat(bst.size()).isEqualTo(treeElementsList.size()); + //assertThat(treeElementsList).contains(2, 14, 23, 324, 1551); + assertThat(traversedElements).isEqualTo(List.of(sortedElements)); } - @Test - void inorderTraversal() { - BinarySearchTree bst = createOf(324, 23, 14, 1551, 2); - - List treeElementsList = new ArrayList<>(bst.size()); - bst.inOrderTraversal(treeElementsList::add); - - assertThat(bst.size()).isEqualTo(treeElementsList.size()); - assertThat(treeElementsList).contains(2, 14, 23, 324, 1551); + public static Stream depthArguments() { + return Stream.of( + //empty tree + arguments(new Integer[]{}, 0), + //tree with a single element + arguments(new Integer[]{24}, 0), + /* + * .......10 + * ....../ \ + * .....5 15 + * ..../ \ + * ...1 20 + */ + arguments(new Integer[]{10, 5, 15, 1, 20}, 2), + /* + * ..1 + * ...\ + * ....2 + * .....\ + * ..... 3 + * .......\ + * ........4 + * .........\ + * ..........5 + */ + arguments(new Integer[]{1, 2, 3, 4, 5}, 4)); } - private BinarySearchTree createOf(Integer... elements) { + private BinarySearchTree createTreeWith(Integer... elements) { return Mockito.mock(BinarySearchTree.class, (InvocationOnMock i) -> {throw new ExerciseNotCompletedException();}); //todo } diff --git a/pom.xml b/pom.xml index dcb984d4..a71e93c5 100644 --- a/pom.xml +++ b/pom.xml @@ -33,6 +33,12 @@ 5.7.0 test + + org.junit.jupiter + junit-jupiter-params + 5.7.0 + test + org.assertj assertj-core From 58965154cb51ba1d6b0f606f53ef6e6eb3e1a9b8 Mon Sep 17 00:00:00 2001 From: Maksym Stasiuk Date: Thu, 21 Jan 2021 00:41:17 +0200 Subject: [PATCH 20/74] GP-40: pass all tests --- .../bst/RecursiveBinarySearchTree.java | 127 ++++++++++++++++++ .../bobocode/bst/BinarySearchTreeTest.java | 3 +- 2 files changed, 128 insertions(+), 2 deletions(-) create mode 100644 2-0-data-structures-and-algorithms/src/main/java/com/bobocode/bst/RecursiveBinarySearchTree.java diff --git a/2-0-data-structures-and-algorithms/src/main/java/com/bobocode/bst/RecursiveBinarySearchTree.java b/2-0-data-structures-and-algorithms/src/main/java/com/bobocode/bst/RecursiveBinarySearchTree.java new file mode 100644 index 00000000..190c3824 --- /dev/null +++ b/2-0-data-structures-and-algorithms/src/main/java/com/bobocode/bst/RecursiveBinarySearchTree.java @@ -0,0 +1,127 @@ +package com.bobocode.bst; + +import java.util.Objects; +import java.util.function.Consumer; +import java.util.stream.Stream; + +public class RecursiveBinarySearchTree implements BinarySearchTree { + private static class Node { + T element; + Node left; + Node right; + + private Node(T element) { + this.element = element; + } + + public static Node valueOf(T element) { + return new Node(element); + } + } + + private Node root; + private int size = 0; + + public static RecursiveBinarySearchTree of(T... elements) { + RecursiveBinarySearchTree bst = new RecursiveBinarySearchTree<>(); + Stream.of(elements).forEach(bst::insert); + return bst; + } + + @Override + public boolean insert(T element) { + Objects.requireNonNull(element); + boolean isInserted = insertElement(element); + if (isInserted) { + size++; + } + return isInserted; + } + + boolean insertElement(T element) { + if (root == null) { + root = Node.valueOf(element); + return true; + } else { + return insertIntoSubTree(root, element); + } + } + + private boolean insertIntoSubTree(Node subTreeRoot, T element) { + if (subTreeRoot.element.compareTo(element) > 0) { + return insertIntoLeftSubtree(subTreeRoot, element); + } else if (subTreeRoot.element.compareTo(element) < 0) { + return insertIntoRightSubtree(subTreeRoot, element); + } else { + return false; + } + } + + private boolean insertIntoLeftSubtree(Node node, T element) { + if (node.left != null) { + return insertIntoSubTree(node.left, element); + } else { + node.left = Node.valueOf(element); + return true; + } + } + + private boolean insertIntoRightSubtree(Node node, T element) { + if (node.right != null) { + return insertIntoSubTree(node.right, element); + } else { + node.right = Node.valueOf(element); + return true; + } + } + + + @Override + public boolean contains(T element) { + Objects.requireNonNull(element); + return findChildNodeByElement(root, element) != null; + } + + private Node findChildNodeByElement(Node node, T element) { + if (node == null) { + return null; + } else if (node.element.compareTo(element) > 0) { + return findChildNodeByElement(node.left, element); + } else if (node.element.compareTo(element) < 0) { + return findChildNodeByElement(node.right, element); + } else { + return node; + } + } + + @Override + public int size() { + return size; + } + + @Override + public int depth() { + return root != null ? depth(root) - 1 : 0; + } + + private int depth(Node node) { + if (node == null) { + return 0; + } else { + return 1 + Math.max(depth(node.left), depth(node.right)); + } + } + + @Override + public void inOrderTraversal(Consumer consumer) { + inOrderTraversal(root, consumer); + } + + private void inOrderTraversal(Node node, Consumer consumer) { + if (node != null) { + inOrderTraversal(node.left, consumer); + consumer.accept(node.element); + inOrderTraversal(node.right, consumer); + } + } +} diff --git a/2-0-data-structures-and-algorithms/src/test/java/com/bobocode/bst/BinarySearchTreeTest.java b/2-0-data-structures-and-algorithms/src/test/java/com/bobocode/bst/BinarySearchTreeTest.java index a213a1de..c1ffa789 100644 --- a/2-0-data-structures-and-algorithms/src/test/java/com/bobocode/bst/BinarySearchTreeTest.java +++ b/2-0-data-structures-and-algorithms/src/test/java/com/bobocode/bst/BinarySearchTreeTest.java @@ -117,7 +117,6 @@ public static Stream depthArguments() { } private BinarySearchTree createTreeWith(Integer... elements) { - return Mockito.mock(BinarySearchTree.class, - (InvocationOnMock i) -> {throw new ExerciseNotCompletedException();}); //todo + return RecursiveBinarySearchTree.of(elements); } } From 780aad2df92ab93b3bbd2967f2331d87a7b29c06 Mon Sep 17 00:00:00 2001 From: maxstasiuk92 Date: Thu, 21 Jan 2021 11:58:21 +0200 Subject: [PATCH 21/74] GP-40: clean tests --- .../test/java/com/bobocode/bst/BinarySearchTreeTest.java | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/2-0-data-structures-and-algorithms/src/test/java/com/bobocode/bst/BinarySearchTreeTest.java b/2-0-data-structures-and-algorithms/src/test/java/com/bobocode/bst/BinarySearchTreeTest.java index c1ffa789..fb656423 100644 --- a/2-0-data-structures-and-algorithms/src/test/java/com/bobocode/bst/BinarySearchTreeTest.java +++ b/2-0-data-structures-and-algorithms/src/test/java/com/bobocode/bst/BinarySearchTreeTest.java @@ -16,13 +16,7 @@ import static org.assertj.core.api.Assertions.*; import static org.junit.jupiter.params.provider.Arguments.arguments; -/*import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.contains; -import static org.hamcrest.Matchers.empty;*/ -//todo: add Order -//todo: checklist https://bobocode.atlassian.net/l/c/MeYPQPHx @TestMethodOrder(MethodOrderer.OrderAnnotation.class) class BinarySearchTreeTest { @@ -83,8 +77,6 @@ void inorderTraversal() { List traversedElements = new ArrayList<>(bst.size()); bst.inOrderTraversal(traversedElements::add); - //assertThat(bst.size()).isEqualTo(treeElementsList.size()); - //assertThat(treeElementsList).contains(2, 14, 23, 324, 1551); assertThat(traversedElements).isEqualTo(List.of(sortedElements)); } @@ -116,6 +108,7 @@ public static Stream depthArguments() { arguments(new Integer[]{1, 2, 3, 4, 5}, 4)); } + @SuppressWarnings("unchecked") private BinarySearchTree createTreeWith(Integer... elements) { return RecursiveBinarySearchTree.of(elements); } From 1d5b0a069e9e81a586eb8bc78d9f4a1ed15b7d49 Mon Sep 17 00:00:00 2001 From: maxstasiuk92 Date: Thu, 21 Jan 2021 12:01:16 +0200 Subject: [PATCH 22/74] GP-40: clean tests --- .../src/test/java/com/bobocode/bst/BinarySearchTreeTest.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/2-0-data-structures-and-algorithms/src/test/java/com/bobocode/bst/BinarySearchTreeTest.java b/2-0-data-structures-and-algorithms/src/test/java/com/bobocode/bst/BinarySearchTreeTest.java index fb656423..e4cb16a3 100644 --- a/2-0-data-structures-and-algorithms/src/test/java/com/bobocode/bst/BinarySearchTreeTest.java +++ b/2-0-data-structures-and-algorithms/src/test/java/com/bobocode/bst/BinarySearchTreeTest.java @@ -1,12 +1,9 @@ package com.bobocode.bst; -import com.bobocode.util.ExerciseNotCompletedException; import org.junit.jupiter.api.*; import org.junit.jupiter.params.*; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; -import org.mockito.Mockito; -import org.mockito.invocation.InvocationOnMock; import java.util.ArrayList; import java.util.Arrays; @@ -108,7 +105,6 @@ public static Stream depthArguments() { arguments(new Integer[]{1, 2, 3, 4, 5}, 4)); } - @SuppressWarnings("unchecked") private BinarySearchTree createTreeWith(Integer... elements) { return RecursiveBinarySearchTree.of(elements); } From 5e45e16d57d0d4324c69c249f400d9ed48c90000 Mon Sep 17 00:00:00 2001 From: Maksym Stasiuk Date: Thu, 21 Jan 2021 23:00:40 +0200 Subject: [PATCH 23/74] GP-40: prepare README.md --- .../src/main/java/com/bobocode/bst/README.md | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 2-0-data-structures-and-algorithms/src/main/java/com/bobocode/bst/README.md diff --git a/2-0-data-structures-and-algorithms/src/main/java/com/bobocode/bst/README.md b/2-0-data-structures-and-algorithms/src/main/java/com/bobocode/bst/README.md new file mode 100644 index 00000000..ad405c3e --- /dev/null +++ b/2-0-data-structures-and-algorithms/src/main/java/com/bobocode/bst/README.md @@ -0,0 +1,20 @@ +# Binary Search Tree exercise :muscle: +Improve your skill implementing Binary Search Tree + +### Task +**Binary Search Tree (BST)** is an ordered (sorted) data structure. You job is to implement the interface `BinarySearchTree` + +### Pre-conditions :heavy_exclamation_mark: +You're supposed to know about [Binary Search Tree](https://en.wikipedia.org/wiki/Binary_search_tree). + Knowledge about [recursion](https://en.wikipedia.org/wiki/Recursion_(computer_science)) also may be helpful. + +### How to start :question: +* Just clone the repository and start implementing `BinarySearchTree` interface +* Unit test are already prepared for you - use them to check your implementation +* Don't worry if you got stuck, checkout the [exercise/completed](https://github.com/bobocode-projects/tdd-exercises/tree/exercise/completed/binary-search-tree) branch and see the final implementation + +### Related materials :information_source: + * [Binary Search Tree](https://en.wikipedia.org/wiki/Binary_search_tree) + * [Recursion](https://en.wikipedia.org/wiki/Recursion_(computer_science)) + * [The Three Laws of TDD](https://www.youtube.com/watch?v=qkblc5WRn-U&t=3476s) + From db5602f28ccf5a2d04f499944df6b0a7e1b1a562 Mon Sep 17 00:00:00 2001 From: Maksym Stasiuk Date: Thu, 21 Jan 2021 23:38:52 +0200 Subject: [PATCH 24/74] GP-40: prepare TDD module --- .../src/main/java/com/bobocode/bst/README.md | 2 +- 6-0-test-driven-development/pom.xml | 8 +++++ .../com/bobocode/bst/BinarySearchTree.java | 32 +++++++++++++++++++ .../src/main/java/com/bobocode/bst/README.md | 26 +++++++++++++++ .../bobocode/bst/BinarySearchTreeTest.java | 22 +++++++++++++ 5 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 6-0-test-driven-development/src/main/java/com/bobocode/bst/BinarySearchTree.java create mode 100644 6-0-test-driven-development/src/main/java/com/bobocode/bst/README.md create mode 100644 6-0-test-driven-development/src/test/java/com/bobocode/bst/BinarySearchTreeTest.java diff --git a/2-0-data-structures-and-algorithms/src/main/java/com/bobocode/bst/README.md b/2-0-data-structures-and-algorithms/src/main/java/com/bobocode/bst/README.md index ad405c3e..49689cab 100644 --- a/2-0-data-structures-and-algorithms/src/main/java/com/bobocode/bst/README.md +++ b/2-0-data-structures-and-algorithms/src/main/java/com/bobocode/bst/README.md @@ -11,7 +11,7 @@ You're supposed to know about [Binary Search Tree](https://en.wikipedia.org/wiki ### How to start :question: * Just clone the repository and start implementing `BinarySearchTree` interface * Unit test are already prepared for you - use them to check your implementation -* Don't worry if you got stuck, checkout the [exercise/completed](https://github.com/bobocode-projects/tdd-exercises/tree/exercise/completed/binary-search-tree) branch and see the final implementation +* Don't worry if you got stuck, checkout branch **completed** and see the final implementation ### Related materials :information_source: * [Binary Search Tree](https://en.wikipedia.org/wiki/Binary_search_tree) diff --git a/6-0-test-driven-development/pom.xml b/6-0-test-driven-development/pom.xml index 7340e75b..97f516d1 100644 --- a/6-0-test-driven-development/pom.xml +++ b/6-0-test-driven-development/pom.xml @@ -10,6 +10,14 @@ 4.0.0 6-0-test-driven-development + + + com.bobocode + java-fundamentals-util + 1.0-SNAPSHOT + test + + 11 diff --git a/6-0-test-driven-development/src/main/java/com/bobocode/bst/BinarySearchTree.java b/6-0-test-driven-development/src/main/java/com/bobocode/bst/BinarySearchTree.java new file mode 100644 index 00000000..5dd6c160 --- /dev/null +++ b/6-0-test-driven-development/src/main/java/com/bobocode/bst/BinarySearchTree.java @@ -0,0 +1,32 @@ +package com.bobocode.bst; + +import java.util.function.Consumer; + +public interface BinarySearchTree { + /** + * insert an element + * @return true if element did not exist in the tree and was inserted successfully + */ + boolean insert(T element); + + /** + * @return true if tree contains element + */ + boolean contains(T element); + + /** + * @return number of elements in the tree + */ + int size(); + + /** + * @return max. number of transition between root node and any other node; 0 - if tree is empty or contains 1 element + */ + int depth(); + + /** + * traverse the tree in element's natural order + * @param consumer accepts ref. to node during traversing + */ + void inOrderTraversal(Consumer consumer); +} diff --git a/6-0-test-driven-development/src/main/java/com/bobocode/bst/README.md b/6-0-test-driven-development/src/main/java/com/bobocode/bst/README.md new file mode 100644 index 00000000..e720daa5 --- /dev/null +++ b/6-0-test-driven-development/src/main/java/com/bobocode/bst/README.md @@ -0,0 +1,26 @@ +# Binary Search Tree exercise :muscle: +Improve your TDD skill implementing Binary Search Tree + +### Task +**Binary Search Tree (BST)** is an ordered (sorted) data structure. Your job is to implement the interface `BinarySearchTree` + by practicing TDD discipline + +### Pre-conditions :heavy_exclamation_mark: +You're supposed to know **The Three laws of TDD**: +1. You should not write production code until you have written a failing unit test +2. You should not write more of a unit test than is sufficient to fail +3. You should not write more production code than is sufficient to pass the failing test + +Also refresh your knowledege about [Binary Search Tree](https://en.wikipedia.org/wiki/Binary_search_tree). +and [recursion](https://en.wikipedia.org/wiki/Recursion_(computer_science)). + +### How to start :question: +* Just clone the repository and start implementing `BinarySearchTree` interface following *three laws of TDD* +* If you don't have enough knowladge about this domain, check out the [links below](#related-materials-information_source) +* Don't worry if you got stuck, checkout branch **completed** and see the final implementation + +### Related materials :information_source: + * [ะฏะบ ะฒะธั€ะพะฑะธั‚ะธ ะทะฒะธั‡ะบัƒ ะฟะธัะฐั‚ะธ ั‚ะตัั‚ะธ? (Bobocode channel )](https://youtu.be/L_CiX9C51BI) + * [Binary Search Tree](https://en.wikipedia.org/wiki/Binary_search_tree) + * [The Three Laws of TDD](https://www.youtube.com/watch?v=qkblc5WRn-U&t=3476s) + diff --git a/6-0-test-driven-development/src/test/java/com/bobocode/bst/BinarySearchTreeTest.java b/6-0-test-driven-development/src/test/java/com/bobocode/bst/BinarySearchTreeTest.java new file mode 100644 index 00000000..817ea8bb --- /dev/null +++ b/6-0-test-driven-development/src/test/java/com/bobocode/bst/BinarySearchTreeTest.java @@ -0,0 +1,22 @@ +package com.bobocode.bst; + +import com.bobocode.util.ExerciseNotCompletedException; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; + +import static org.assertj.core.api.Assertions.fail; + +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +class BinarySearchTreeTest { + + /** + * Always failing test, remove it before doing the exercise + */ + @Test + @Order(1) + public void notImplemented() { + fail(null, new ExerciseNotCompletedException()); + } +} From e49e2349c903b44e2da460b221a7ea98360ace32 Mon Sep 17 00:00:00 2001 From: Maksym Stasiuk Date: Thu, 21 Jan 2021 23:40:53 +0200 Subject: [PATCH 25/74] GP-40: complete TDD module --- .../bst/RecursiveBinarySearchTree.java | 127 ++++++++++++++++++ .../bobocode/bst/BinarySearchTreeTest.java | 106 ++++++++++++++- 2 files changed, 226 insertions(+), 7 deletions(-) create mode 100644 6-0-test-driven-development/src/main/java/com/bobocode/bst/RecursiveBinarySearchTree.java diff --git a/6-0-test-driven-development/src/main/java/com/bobocode/bst/RecursiveBinarySearchTree.java b/6-0-test-driven-development/src/main/java/com/bobocode/bst/RecursiveBinarySearchTree.java new file mode 100644 index 00000000..190c3824 --- /dev/null +++ b/6-0-test-driven-development/src/main/java/com/bobocode/bst/RecursiveBinarySearchTree.java @@ -0,0 +1,127 @@ +package com.bobocode.bst; + +import java.util.Objects; +import java.util.function.Consumer; +import java.util.stream.Stream; + +public class RecursiveBinarySearchTree implements BinarySearchTree { + private static class Node { + T element; + Node left; + Node right; + + private Node(T element) { + this.element = element; + } + + public static Node valueOf(T element) { + return new Node(element); + } + } + + private Node root; + private int size = 0; + + public static RecursiveBinarySearchTree of(T... elements) { + RecursiveBinarySearchTree bst = new RecursiveBinarySearchTree<>(); + Stream.of(elements).forEach(bst::insert); + return bst; + } + + @Override + public boolean insert(T element) { + Objects.requireNonNull(element); + boolean isInserted = insertElement(element); + if (isInserted) { + size++; + } + return isInserted; + } + + boolean insertElement(T element) { + if (root == null) { + root = Node.valueOf(element); + return true; + } else { + return insertIntoSubTree(root, element); + } + } + + private boolean insertIntoSubTree(Node subTreeRoot, T element) { + if (subTreeRoot.element.compareTo(element) > 0) { + return insertIntoLeftSubtree(subTreeRoot, element); + } else if (subTreeRoot.element.compareTo(element) < 0) { + return insertIntoRightSubtree(subTreeRoot, element); + } else { + return false; + } + } + + private boolean insertIntoLeftSubtree(Node node, T element) { + if (node.left != null) { + return insertIntoSubTree(node.left, element); + } else { + node.left = Node.valueOf(element); + return true; + } + } + + private boolean insertIntoRightSubtree(Node node, T element) { + if (node.right != null) { + return insertIntoSubTree(node.right, element); + } else { + node.right = Node.valueOf(element); + return true; + } + } + + + @Override + public boolean contains(T element) { + Objects.requireNonNull(element); + return findChildNodeByElement(root, element) != null; + } + + private Node findChildNodeByElement(Node node, T element) { + if (node == null) { + return null; + } else if (node.element.compareTo(element) > 0) { + return findChildNodeByElement(node.left, element); + } else if (node.element.compareTo(element) < 0) { + return findChildNodeByElement(node.right, element); + } else { + return node; + } + } + + @Override + public int size() { + return size; + } + + @Override + public int depth() { + return root != null ? depth(root) - 1 : 0; + } + + private int depth(Node node) { + if (node == null) { + return 0; + } else { + return 1 + Math.max(depth(node.left), depth(node.right)); + } + } + + @Override + public void inOrderTraversal(Consumer consumer) { + inOrderTraversal(root, consumer); + } + + private void inOrderTraversal(Node node, Consumer consumer) { + if (node != null) { + inOrderTraversal(node.left, consumer); + consumer.accept(node.element); + inOrderTraversal(node.right, consumer); + } + } +} diff --git a/6-0-test-driven-development/src/test/java/com/bobocode/bst/BinarySearchTreeTest.java b/6-0-test-driven-development/src/test/java/com/bobocode/bst/BinarySearchTreeTest.java index 817ea8bb..61cdf6f4 100644 --- a/6-0-test-driven-development/src/test/java/com/bobocode/bst/BinarySearchTreeTest.java +++ b/6-0-test-driven-development/src/test/java/com/bobocode/bst/BinarySearchTreeTest.java @@ -1,22 +1,114 @@ package com.bobocode.bst; -import com.bobocode.util.ExerciseNotCompletedException; import org.junit.jupiter.api.MethodOrderer; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestMethodOrder; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.params.provider.Arguments.arguments; -import static org.assertj.core.api.Assertions.fail; @TestMethodOrder(MethodOrderer.OrderAnnotation.class) class BinarySearchTreeTest { - /** - * Always failing test, remove it before doing the exercise - */ + private static final Integer[] someElements = {10, 9, 11, 8, 12, 7}; + @Test @Order(1) - public void notImplemented() { - fail(null, new ExerciseNotCompletedException()); + void createWithElements() { + BinarySearchTree bst = createTreeWith(someElements); + for (var e: someElements) { + assertThat(bst.contains(e)).isTrue(); + } + assertThat(bst.size()).isEqualTo(someElements.length); + } + + @Test + @Order(2) + void insertUniqueElements() { + BinarySearchTree bst = createTreeWith(); + for (int i = 0; i < someElements.length; i++) { + var e = someElements[i]; + assertThat(bst.contains(e)).isFalse(); //does not contain + assertThat(bst.size()).isEqualTo(i); + + assertThat(bst.insert(e)).isTrue(); //do insert + + assertThat(bst.contains(e)).isTrue(); //and contains + assertThat(bst.size()).isEqualTo(i + 1); + } + } + + @Test + @Order(3) + void insertNonUniqueElements() { + BinarySearchTree bst = createTreeWith(someElements); + for (var e: someElements) { + assertThat(bst.insert(e)).isFalse(); //does not insert + assertThat(bst.contains(e)).isTrue(); //but contains + } + assertThat(bst.size()).isEqualTo(someElements.length); + } + + @ParameterizedTest + @MethodSource("depthArguments") + @Order(4) + void depth(Integer[] elements, int depth) { + BinarySearchTree bst = createTreeWith(elements); + assertThat(bst.depth()).isEqualTo(depth); + } + + @Test + @Order(5) + void inorderTraversal() { + BinarySearchTree bst = createTreeWith(someElements); + Integer[] sortedElements = Arrays.copyOf(someElements, someElements.length); + Arrays.sort(sortedElements); + + List traversedElements = new ArrayList<>(bst.size()); + bst.inOrderTraversal(traversedElements::add); + + assertThat(traversedElements).isEqualTo(List.of(sortedElements)); + } + + public static Stream depthArguments() { + return Stream.of( + //empty tree + arguments(new Integer[]{}, 0), + //tree with a single element + arguments(new Integer[]{24}, 0), + /* + * .......10 + * ....../ \ + * .....5 15 + * ..../ \ + * ...1 20 + */ + arguments(new Integer[]{10, 5, 15, 1, 20}, 2), + /* + * ..1 + * ...\ + * ....2 + * .....\ + * ..... 3 + * .......\ + * ........4 + * .........\ + * ..........5 + */ + arguments(new Integer[]{1, 2, 3, 4, 5}, 4)); + } + + private BinarySearchTree createTreeWith(Integer... elements) { + return RecursiveBinarySearchTree.of(elements); } } From 0ce8b6aeccba17eec38fb4d67f897b9dff99203f Mon Sep 17 00:00:00 2001 From: Taras Boychuk Date: Fri, 22 Jan 2021 22:23:35 +0200 Subject: [PATCH 26/74] * fix contains method * improve of() method * remove unused method --- .../com/bobocode/array_list/ArrayList.java | 30 ++++++------------- 1 file changed, 9 insertions(+), 21 deletions(-) diff --git a/2-0-data-structures-and-algorithms/src/main/java/com/bobocode/array_list/ArrayList.java b/2-0-data-structures-and-algorithms/src/main/java/com/bobocode/array_list/ArrayList.java index 416ea8ad..cf169b7d 100644 --- a/2-0-data-structures-and-algorithms/src/main/java/com/bobocode/array_list/ArrayList.java +++ b/2-0-data-structures-and-algorithms/src/main/java/com/bobocode/array_list/ArrayList.java @@ -11,7 +11,6 @@ * based on an array and is simplified version of {@link java.util.ArrayList}. */ public class ArrayList implements List { - private static final int DEFAULT_CAPACITY = 5; private Object[] elementData; private int size; @@ -23,11 +22,10 @@ public class ArrayList implements List { * @throws IllegalArgumentException โ€“ if the specified initial capacity is negative or 0. */ public ArrayList(int initCapacity) { - if (initCapacity > 0) { - elementData = new Object[initCapacity]; - } else { + if (initCapacity <= 0) { throw new IllegalArgumentException(); } + elementData = new Object[initCapacity]; } /** @@ -35,7 +33,7 @@ public ArrayList(int initCapacity) { * A default size of inner array is 5; */ public ArrayList() { - elementData = new Object[DEFAULT_CAPACITY]; + this(DEFAULT_CAPACITY); } /** @@ -45,10 +43,9 @@ public ArrayList() { * @return new instance */ public static List of(T... elements) { - List list = new ArrayList<>(elements.length); - for (T element : elements) { - list.add(element); - } + ArrayList list = new ArrayList<>(elements.length); + list.elementData = Arrays.copyOf(elements, elements.length); + list.size = elements.length; return list; } @@ -166,13 +163,9 @@ public T remove(int index) { */ @Override public boolean contains(T element) { - if (isEmpty()) { - return false; - } else { - for (Object elem : elementData) { - if (elem.equals(element)) { - return true; - } + for (int i = 0; i < size; i++) { + if (elementData[i].equals(element)) { + return true; } } return false; @@ -188,11 +181,6 @@ public boolean isEmpty() { return size == 0; } - @SuppressWarnings("unchecked") - private T[] getTrimmedArrayToSize(int size) { - return (T[]) Arrays.copyOf(elementData, size); - } - /** * @return amount of saved elements */ From a2d3b8fd15ee4736e2bdbaeb197f7c98164bd4ed Mon Sep 17 00:00:00 2001 From: Taras Boychuk Date: Fri, 22 Jan 2021 23:36:32 +0200 Subject: [PATCH 27/74] Implement Introduction --- .../src/main/java/com/bobocode/intro/Introduction.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/0-0-java-programming-intro/src/main/java/com/bobocode/intro/Introduction.java b/0-0-java-programming-intro/src/main/java/com/bobocode/intro/Introduction.java index fa99b374..ef1d4587 100644 --- a/0-0-java-programming-intro/src/main/java/com/bobocode/intro/Introduction.java +++ b/0-0-java-programming-intro/src/main/java/com/bobocode/intro/Introduction.java @@ -1,7 +1,5 @@ package com.bobocode.intro; -import com.bobocode.util.ExerciseNotCompletedException; - /** * Welcome! This is an introduction class that will show you a simple example of Bobocode exercise. *

@@ -19,6 +17,6 @@ public class Introduction { * @return "The key to efficient learning is practice!" */ public String welcomeMessage() { - throw new ExerciseNotCompletedException(); // todo: + return "The key to efficient learning is practice!"; } } From 937963c0bd52a7e48375a73a60725623ed1d4165 Mon Sep 17 00:00:00 2001 From: Maksym Stasiuk Date: Sat, 23 Jan 2021 00:30:57 +0200 Subject: [PATCH 28/74] GP-40: update test in TDD module to be identical to test in data structure module --- .../bobocode/bst/BinarySearchTreeTest.java | 107 ++++++++++++++++-- 1 file changed, 96 insertions(+), 11 deletions(-) diff --git a/6-0-test-driven-development/src/test/java/com/bobocode/bst/BinarySearchTreeTest.java b/6-0-test-driven-development/src/test/java/com/bobocode/bst/BinarySearchTreeTest.java index 817ea8bb..eba20a7f 100644 --- a/6-0-test-driven-development/src/test/java/com/bobocode/bst/BinarySearchTreeTest.java +++ b/6-0-test-driven-development/src/test/java/com/bobocode/bst/BinarySearchTreeTest.java @@ -1,22 +1,107 @@ package com.bobocode.bst; -import com.bobocode.util.ExerciseNotCompletedException; -import org.junit.jupiter.api.MethodOrderer; -import org.junit.jupiter.api.Order; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestMethodOrder; +import org.junit.jupiter.api.*; +import org.junit.jupiter.params.*; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.*; +import static org.junit.jupiter.params.provider.Arguments.arguments; -import static org.assertj.core.api.Assertions.fail; @TestMethodOrder(MethodOrderer.OrderAnnotation.class) class BinarySearchTreeTest { - /** - * Always failing test, remove it before doing the exercise - */ + private static final Integer[] someElements = {10, 9, 11, 8, 12, 7}; + @Test @Order(1) - public void notImplemented() { - fail(null, new ExerciseNotCompletedException()); + void createWithElements() { + BinarySearchTree bst = RecursiveBinarySearchTree.of(someElements); + for (var e: someElements) { + assertThat(bst.contains(e)).isTrue(); + } + assertThat(bst.size()).isEqualTo(someElements.length); + } + + @Test + @Order(2) + void insertUniqueElements() { + BinarySearchTree bst = RecursiveBinarySearchTree.of(); + for (int i = 0; i < someElements.length; i++) { + var e = someElements[i]; + assertThat(bst.contains(e)).isFalse(); //does not contain + assertThat(bst.size()).isEqualTo(i); + + assertThat(bst.insert(e)).isTrue(); //do insert + + assertThat(bst.contains(e)).isTrue(); //and contains + assertThat(bst.size()).isEqualTo(i + 1); + } + } + + @Test + @Order(3) + void insertNonUniqueElements() { + BinarySearchTree bst = RecursiveBinarySearchTree.of(someElements); + for (var e: someElements) { + assertThat(bst.insert(e)).isFalse(); //does not insert + assertThat(bst.contains(e)).isTrue(); //but contains + } + assertThat(bst.size()).isEqualTo(someElements.length); + } + + @ParameterizedTest + @MethodSource("depthArguments") + @Order(4) + void depth(Integer[] elements, int depth) { + BinarySearchTree bst = RecursiveBinarySearchTree.of(elements); + assertThat(bst.depth()).isEqualTo(depth); + } + + @Test + @Order(5) + void inorderTraversal() { + BinarySearchTree bst = RecursiveBinarySearchTree.of(someElements); + Integer[] sortedElements = Arrays.copyOf(someElements, someElements.length); + Arrays.sort(sortedElements); + + List traversedElements = new ArrayList<>(bst.size()); + bst.inOrderTraversal(traversedElements::add); + + assertThat(traversedElements).isEqualTo(List.of(sortedElements)); + } + + public static Stream depthArguments() { + return Stream.of( + //empty tree + arguments(new Integer[]{}, 0), + //tree with a single element + arguments(new Integer[]{24}, 0), + /* + * .......10 + * ....../ \ + * .....5 15 + * ..../ \ + * ...1 20 + */ + arguments(new Integer[]{10, 5, 15, 1, 20}, 2), + /* + * ..1 + * ...\ + * ....2 + * .....\ + * ..... 3 + * .......\ + * ........4 + * .........\ + * ..........5 + */ + arguments(new Integer[]{1, 2, 3, 4, 5}, 4)); } } From 9fa96170d072a106e53dec9a2f80f8d64d93d779 Mon Sep 17 00:00:00 2001 From: Ivan Date: Sat, 23 Jan 2021 09:40:06 +0200 Subject: [PATCH 29/74] GP-27 complete LinkedQueue --- .../bobocode/linked_queue/LinkedQueue.java | 43 ++++++++++++++++--- 1 file changed, 37 insertions(+), 6 deletions(-) diff --git a/2-0-data-structures-and-algorithms/src/main/java/com/bobocode/linked_queue/LinkedQueue.java b/2-0-data-structures-and-algorithms/src/main/java/com/bobocode/linked_queue/LinkedQueue.java index 763e4b2b..1b8c9871 100644 --- a/2-0-data-structures-and-algorithms/src/main/java/com/bobocode/linked_queue/LinkedQueue.java +++ b/2-0-data-structures-and-algorithms/src/main/java/com/bobocode/linked_queue/LinkedQueue.java @@ -1,7 +1,5 @@ package com.bobocode.linked_queue; -import com.bobocode.util.ExerciseNotCompletedException; - /** * {@link LinkedQueue} implements FIFO {@link Queue}, using singly linked nodes. Nodes are stores in instances of nested * class Node. In order to perform operations {@link LinkedQueue#add(Object)} and {@link LinkedQueue#poll()} @@ -10,6 +8,22 @@ * @param a generic parameter */ public class LinkedQueue implements Queue { + static final class Node { + private T element; + private Node next; + + static Node valueOf(T element) { + return new Node<>(element); + } + + private Node(T element) { + this.element = element; + } + } + + private Node head; + private Node tail; + private int size; /** * Adds an element to the end of the queue. @@ -17,7 +31,14 @@ public class LinkedQueue implements Queue { * @param element the element to add */ public void add(T element) { - throw new ExerciseNotCompletedException(); // todo: implement this method + Node newNode = Node.valueOf(element); + if (head == null) { + head = tail = newNode; + } else { + tail.next = newNode; + tail = newNode; + } + size++; } /** @@ -26,7 +47,17 @@ public void add(T element) { * @return an element that was retrieved from the head or null if queue is empty */ public T poll() { - throw new ExerciseNotCompletedException(); // todo: implement this method + if (head != null) { + T element = head.element; + head = head.next; + if (head == null) { + tail = null; + } + size--; + return element; + } else { + return null; + } } /** @@ -35,7 +66,7 @@ public T poll() { * @return an integer value that is a size of queue */ public int size() { - throw new ExerciseNotCompletedException(); // todo: implement this method + return size; } /** @@ -44,6 +75,6 @@ public int size() { * @return {@code true} if the queue is empty, returns {@code false} if it's not */ public boolean isEmpty() { - throw new ExerciseNotCompletedException(); // todo: implement this method + return head == null; } } From 67972332388268c3a94ef2c1524c0f559e1ed5c4 Mon Sep 17 00:00:00 2001 From: Taras Boychuk Date: Fri, 22 Jan 2021 22:25:14 +0200 Subject: [PATCH 30/74] * add new test to ArrayListTest.java --- .../java/com/bobocode/array_list/ArrayListTest.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/2-0-data-structures-and-algorithms/src/test/java/com/bobocode/array_list/ArrayListTest.java b/2-0-data-structures-and-algorithms/src/test/java/com/bobocode/array_list/ArrayListTest.java index e185d753..bea68985 100644 --- a/2-0-data-structures-and-algorithms/src/test/java/com/bobocode/array_list/ArrayListTest.java +++ b/2-0-data-structures-and-algorithms/src/test/java/com/bobocode/array_list/ArrayListTest.java @@ -307,6 +307,18 @@ public void containsElement() { assertThat(arrayList.contains(58)).isEqualTo(true); } + @Test + @Order(28) + void containsNotExistingWhenArrayIsNotFilled() { + arrayList = new ArrayList<>(100); + arrayList.add(5); + arrayList.add(8); + + boolean result = arrayList.contains(3); + + assertThat(result).isFalse(); + } + @Test @Order(30) public void findNotExistingElement() { From 2957807b1f0560397eeccff7509c4b7876268d54 Mon Sep 17 00:00:00 2001 From: Taras Boychuk Date: Fri, 22 Jan 2021 22:31:24 +0200 Subject: [PATCH 31/74] * fix order --- .../com/bobocode/array_list/ArrayListTest.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/2-0-data-structures-and-algorithms/src/test/java/com/bobocode/array_list/ArrayListTest.java b/2-0-data-structures-and-algorithms/src/test/java/com/bobocode/array_list/ArrayListTest.java index bea68985..62d8eba2 100644 --- a/2-0-data-structures-and-algorithms/src/test/java/com/bobocode/array_list/ArrayListTest.java +++ b/2-0-data-structures-and-algorithms/src/test/java/com/bobocode/array_list/ArrayListTest.java @@ -308,7 +308,7 @@ public void containsElement() { } @Test - @Order(28) + @Order(30) void containsNotExistingWhenArrayIsNotFilled() { arrayList = new ArrayList<>(100); arrayList.add(5); @@ -320,7 +320,7 @@ void containsNotExistingWhenArrayIsNotFilled() { } @Test - @Order(30) + @Order(31) public void findNotExistingElement() { arrayList = ArrayList.of(15, 69, 58, 78, 100); @@ -328,13 +328,13 @@ public void findNotExistingElement() { } @Test - @Order(31) + @Order(32) public void isEmptyOnEmptyList() { assertThat(arrayList.isEmpty()).isEqualTo(true); } @Test - @Order(32) + @Order(33) public void isEmpty() { arrayList = ArrayList.of(34, 5, 6); @@ -342,13 +342,13 @@ public void isEmpty() { } @Test - @Order(33) + @Order(34) public void clearOnEmptyList() { assertThat(arrayList.isEmpty()).isEqualTo(true); } @Test - @Order(34) + @Order(35) public void clearChangesTheSize() { arrayList = ArrayList.of(4, 5, 6); @@ -358,7 +358,7 @@ public void clearChangesTheSize() { } @Test - @Order(35) + @Order(36) public void clearRemovesElements() { arrayList = ArrayList.of(4, 5, 6); From 07fd236ce2c554cf43c737de31eb35ca95fd20b2 Mon Sep 17 00:00:00 2001 From: Taras Boychuk Date: Sat, 23 Jan 2021 14:17:36 +0200 Subject: [PATCH 32/74] GP-40 remove order from the test --- .../com/bobocode/bst/BinarySearchTreeTest.java | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/6-0-test-driven-development/src/test/java/com/bobocode/bst/BinarySearchTreeTest.java b/6-0-test-driven-development/src/test/java/com/bobocode/bst/BinarySearchTreeTest.java index eba20a7f..95e63b37 100644 --- a/6-0-test-driven-development/src/test/java/com/bobocode/bst/BinarySearchTreeTest.java +++ b/6-0-test-driven-development/src/test/java/com/bobocode/bst/BinarySearchTreeTest.java @@ -1,7 +1,7 @@ package com.bobocode.bst; -import org.junit.jupiter.api.*; -import org.junit.jupiter.params.*; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; @@ -10,27 +10,23 @@ import java.util.List; import java.util.stream.Stream; -import static org.assertj.core.api.Assertions.*; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.params.provider.Arguments.arguments; - -@TestMethodOrder(MethodOrderer.OrderAnnotation.class) class BinarySearchTreeTest { private static final Integer[] someElements = {10, 9, 11, 8, 12, 7}; @Test - @Order(1) void createWithElements() { BinarySearchTree bst = RecursiveBinarySearchTree.of(someElements); - for (var e: someElements) { + for (var e : someElements) { assertThat(bst.contains(e)).isTrue(); } assertThat(bst.size()).isEqualTo(someElements.length); } @Test - @Order(2) void insertUniqueElements() { BinarySearchTree bst = RecursiveBinarySearchTree.of(); for (int i = 0; i < someElements.length; i++) { @@ -46,10 +42,9 @@ void insertUniqueElements() { } @Test - @Order(3) void insertNonUniqueElements() { BinarySearchTree bst = RecursiveBinarySearchTree.of(someElements); - for (var e: someElements) { + for (var e : someElements) { assertThat(bst.insert(e)).isFalse(); //does not insert assertThat(bst.contains(e)).isTrue(); //but contains } @@ -58,14 +53,12 @@ void insertNonUniqueElements() { @ParameterizedTest @MethodSource("depthArguments") - @Order(4) void depth(Integer[] elements, int depth) { BinarySearchTree bst = RecursiveBinarySearchTree.of(elements); assertThat(bst.depth()).isEqualTo(depth); } @Test - @Order(5) void inorderTraversal() { BinarySearchTree bst = RecursiveBinarySearchTree.of(someElements); Integer[] sortedElements = Arrays.copyOf(someElements, someElements.length); From e268d8de3216af8144dd333af72018558c292efd Mon Sep 17 00:00:00 2001 From: Taras Boychuk Date: Sat, 23 Jan 2021 14:23:02 +0200 Subject: [PATCH 33/74] Remove ordering from TDD tests --- .../bobocode/linked_list/LinkedListTest.java | 40 +------------------ .../java/com/bobocode/stack/StackTest.java | 2 +- 2 files changed, 2 insertions(+), 40 deletions(-) diff --git a/6-0-test-driven-development/src/test/java/com/bobocode/linked_list/LinkedListTest.java b/6-0-test-driven-development/src/test/java/com/bobocode/linked_list/LinkedListTest.java index fd3c050c..d276554a 100644 --- a/6-0-test-driven-development/src/test/java/com/bobocode/linked_list/LinkedListTest.java +++ b/6-0-test-driven-development/src/test/java/com/bobocode/linked_list/LinkedListTest.java @@ -1,23 +1,18 @@ package com.bobocode.linked_list; -import org.junit.jupiter.api.MethodOrderer; -import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestMethodOrder; import java.util.NoSuchElementException; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; import static org.assertj.core.api.AssertionsForClassTypes.assertThatExceptionOfType; -@TestMethodOrder(MethodOrderer.OrderAnnotation.class) -public class LinkedListTest { +class LinkedListTest { private List intList = new LinkedList<>(); @Test - @Order(1) void addIntoEmptyList() { intList.add(41); @@ -28,7 +23,6 @@ void addIntoEmptyList() { } @Test - @Order(2) void getFirstElementFromSingleElementList() { intList.add(25); @@ -38,7 +32,6 @@ void getFirstElementFromSingleElementList() { } @Test - @Order(3) void addElements() { intList = LinkedList.of(43, 233, 54); @@ -49,7 +42,6 @@ void addElements() { } @Test - @Order(4) void size() { intList = LinkedList.of(4, 7, 9, 0, 7); @@ -59,7 +51,6 @@ void size() { } @Test - @Order(5) void getFirstElement() { intList = LinkedList.of(31, 32); @@ -68,7 +59,6 @@ void getFirstElement() { } @Test - @Order(6) void getLastElement() { intList = LinkedList.of(41, 42); @@ -78,14 +68,12 @@ void getLastElement() { } @Test - @Order(7) void getFirstOfEmptyList() { assertThatExceptionOfType(NoSuchElementException.class) .isThrownBy(() -> intList.getFirst()); } @Test - @Order(8) void getLastOfEmptyList() { assertThatExceptionOfType(NoSuchElementException.class) .isThrownBy(() -> intList.getLast()); @@ -93,7 +81,6 @@ void getLastOfEmptyList() { @Test - @Order(9) void getElements() { intList = LinkedList.of(25, 87, 45); @@ -108,7 +95,6 @@ void getElements() { } @Test - @Order(10) void addElementByZeroIndexIntoEmptyList() { intList.add(0, 45); @@ -119,7 +105,6 @@ void addElementByZeroIndexIntoEmptyList() { } @Test - @Order(11) void addElementByIndexToTheEndOfList() { intList = LinkedList.of(98, 64, 23, 1, 3, 4); @@ -131,7 +116,6 @@ void addElementByIndexToTheEndOfList() { } @Test - @Order(12) void addElementToTheHeadOfNonEmptyList() { intList = LinkedList.of(4, 6, 8, 9, 0, 2); @@ -143,7 +127,6 @@ void addElementToTheHeadOfNonEmptyList() { } @Test - @Order(13) void addElementByIndex() { intList = LinkedList.of(43, 5, 6, 8); @@ -159,14 +142,12 @@ void addElementByIndex() { } @Test - @Order(14) void addElementByNegativeIndex() { assertThatExceptionOfType(IndexOutOfBoundsException.class) .isThrownBy(() -> intList.add(-1, 66)); } @Test - @Order(15) void addElementByIndexLargerThanListSize() { intList = LinkedList.of(4, 6, 11, 9); @@ -177,7 +158,6 @@ void addElementByIndexLargerThanListSize() { } @Test - @Order(16) void addElementByIndexEqualToSize() { intList = LinkedList.of(1, 2, 3, 4, 5); // size = 5 @@ -189,14 +169,12 @@ void addElementByIndexEqualToSize() { } @Test - @Order(17) void setFirstElementOnEmptyTree() { assertThatExceptionOfType(IndexOutOfBoundsException.class) .isThrownBy(() -> intList.set(0, 34)); } @Test - @Order(18) void setElementByIndexEqualToSize() { intList = LinkedList.of(2, 3, 4); // size = 3 @@ -205,7 +183,6 @@ void setElementByIndexEqualToSize() { } @Test - @Order(19) void setElementByIndex() { intList = LinkedList.of(34, 78, 9, 8); @@ -220,21 +197,18 @@ void setElementByIndex() { } @Test - @Order(20) void getFirstElementFromEmptyList() { assertThatExceptionOfType(IndexOutOfBoundsException.class) .isThrownBy(() -> intList.get(0)); } @Test - @Order(21) void getElementByNegativeIndex() { assertThatExceptionOfType(IndexOutOfBoundsException.class) .isThrownBy(() -> intList.get(-1)); } @Test - @Order(22) void getElementByIndexEqualsToListSize() { intList = LinkedList.of(33, 46, 25, 87, 45); assertThatExceptionOfType(IndexOutOfBoundsException.class) @@ -242,14 +216,12 @@ void getElementByIndexEqualsToListSize() { } @Test - @Order(23) void removeElementFromEmptyList() { assertThatExceptionOfType(IndexOutOfBoundsException.class) .isThrownBy(() -> intList.remove(234)); } @Test - @Order(24) void removeFirstElement() { intList = LinkedList.of(4, 6, 8, 9); @@ -261,7 +233,6 @@ void removeFirstElement() { } @Test - @Order(25) void removeLastElement() { intList = LinkedList.of(4, 6, 8, 9); @@ -273,7 +244,6 @@ void removeLastElement() { } @Test - @Order(26) void removeElement() { intList = LinkedList.of(1, 2, 3, 4, 5); @@ -286,7 +256,6 @@ void removeElement() { } @Test - @Order(27) void containsOnEmptyList() { boolean contains = intList.contains(34); @@ -294,7 +263,6 @@ void containsOnEmptyList() { } @Test - @Order(28) void contains() { intList = LinkedList.of(45, 6, 3, 6); @@ -306,7 +274,6 @@ void contains() { } @Test - @Order(29) void isEmptyOnEmptyList() { boolean empty = intList.isEmpty(); @@ -314,7 +281,6 @@ void isEmptyOnEmptyList() { } @Test - @Order(30) void isEmpty() { intList = LinkedList.of(34, 5, 6); @@ -324,7 +290,6 @@ void isEmpty() { } @Test - @Order(31) void sizeOnEmptyList() { int size = intList.size(); @@ -332,7 +297,6 @@ void sizeOnEmptyList() { } @Test - @Order(32) void clearOnEmptyList() { intList.clear(); @@ -340,7 +304,6 @@ void clearOnEmptyList() { } @Test - @Order(33) void clearChangesTheSize() { intList = LinkedList.of(4, 5, 6); @@ -350,7 +313,6 @@ void clearChangesTheSize() { } @Test - @Order(34) void clearRemovesElements() { intList = LinkedList.of(4, 5, 6); diff --git a/6-0-test-driven-development/src/test/java/com/bobocode/stack/StackTest.java b/6-0-test-driven-development/src/test/java/com/bobocode/stack/StackTest.java index 2c4a0a51..91ed775c 100644 --- a/6-0-test-driven-development/src/test/java/com/bobocode/stack/StackTest.java +++ b/6-0-test-driven-development/src/test/java/com/bobocode/stack/StackTest.java @@ -5,7 +5,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertThrows; -public class StackTest { +class StackTest { private Stack intStack = new LinkedStack<>(); From b874b4e8db6f2f2c88ec9aee63b79406b09fabc7 Mon Sep 17 00:00:00 2001 From: Victor Kuzma Date: Sun, 24 Jan 2021 17:34:32 +0200 Subject: [PATCH 34/74] GP-46: complete `declarative-sum-of-squares` --- .../declarative_sum_of_squares/SumOfSquares.java | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/5-0-functional-programming/src/main/java/com/bobocode/declarative_sum_of_squares/SumOfSquares.java b/5-0-functional-programming/src/main/java/com/bobocode/declarative_sum_of_squares/SumOfSquares.java index 0bba006e..74e42db3 100644 --- a/5-0-functional-programming/src/main/java/com/bobocode/declarative_sum_of_squares/SumOfSquares.java +++ b/5-0-functional-programming/src/main/java/com/bobocode/declarative_sum_of_squares/SumOfSquares.java @@ -3,6 +3,8 @@ import com.bobocode.declarative_sum_of_squares.exception.InvalidRangeException; +import java.util.stream.IntStream; + /** * This class allow to calculate a sum of squares of integer number in a certain range. It was implemented using * OO approach. Your job is to refactor it using functional approach. E.g. avoid using mutable variables @@ -24,11 +26,8 @@ static int calculateSumOfSquaresInRange(int startInclusive, int endInclusive) { throw new InvalidRangeException(); } - // todo: refactor using functional approach - int sumOfSquares = 0; - for (int i = startInclusive; i <= endInclusive; i++) { - sumOfSquares += i * i; - } - return sumOfSquares; + return IntStream.rangeClosed(startInclusive, endInclusive) + .map(a -> a * a) + .sum(); } } From 7d6544657ba3890df9211ca5e47929a93fed3113 Mon Sep 17 00:00:00 2001 From: Taras Boychuk Date: Tue, 26 Jan 2021 12:03:16 +0200 Subject: [PATCH 35/74] Fix ArrayList method remove() --- .../src/main/java/com/bobocode/array_list/ArrayList.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/2-0-data-structures-and-algorithms/src/main/java/com/bobocode/array_list/ArrayList.java b/2-0-data-structures-and-algorithms/src/main/java/com/bobocode/array_list/ArrayList.java index cf169b7d..2f743135 100644 --- a/2-0-data-structures-and-algorithms/src/main/java/com/bobocode/array_list/ArrayList.java +++ b/2-0-data-structures-and-algorithms/src/main/java/com/bobocode/array_list/ArrayList.java @@ -147,10 +147,13 @@ public void set(int index, T element) { */ @Override @SuppressWarnings("unchecked") - public T remove(int index) { + public T remove(int index) {// 4,5,3,6,7,7 -> remove(3) Objects.checkIndex(index, size); T deletedElement = (T) elementData[index]; - System.arraycopy(elementData, index + 1, elementData, index, size - index - 1); + if (index < size - 1) { + System.arraycopy(elementData, index + 1, elementData, index, size - index - 1); + } + elementData[size - 1] = null; size--; return deletedElement; } From f05394e5e1c324b3b5912370ef2c0b8bf2960230 Mon Sep 17 00:00:00 2001 From: Serhii Date: Wed, 27 Jan 2021 13:26:39 +0200 Subject: [PATCH 36/74] GP-47 Update completed solution --- .../com/bobocode/lambda_math_functions/FunctionMap.java | 4 +--- .../com/bobocode/lambda_math_functions/Functions.java | 8 +++++++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/5-0-functional-programming/src/main/java/com/bobocode/lambda_math_functions/FunctionMap.java b/5-0-functional-programming/src/main/java/com/bobocode/lambda_math_functions/FunctionMap.java index 49e6abbf..85abae23 100644 --- a/5-0-functional-programming/src/main/java/com/bobocode/lambda_math_functions/FunctionMap.java +++ b/5-0-functional-programming/src/main/java/com/bobocode/lambda_math_functions/FunctionMap.java @@ -1,7 +1,5 @@ package com.bobocode.lambda_math_functions; -import com.bobocode.util.ExerciseNotCompletedException; - import java.util.HashMap; import java.util.Map; import java.util.function.Function; @@ -25,7 +23,7 @@ public Function getFunction(String name) { if (functionMap.containsKey(name)) { return functionMap.get(name); } else { - throw new ExerciseNotCompletedException(); + throw new InvalidFunctionNameException(name); } } } \ No newline at end of file diff --git a/5-0-functional-programming/src/main/java/com/bobocode/lambda_math_functions/Functions.java b/5-0-functional-programming/src/main/java/com/bobocode/lambda_math_functions/Functions.java index 50bd37f3..c91e0cf9 100644 --- a/5-0-functional-programming/src/main/java/com/bobocode/lambda_math_functions/Functions.java +++ b/5-0-functional-programming/src/main/java/com/bobocode/lambda_math_functions/Functions.java @@ -1,5 +1,7 @@ package com.bobocode.lambda_math_functions; +import static java.lang.Math.abs; + public class Functions { /** * A static factory method that creates an integer function map with basic functions: @@ -14,7 +16,11 @@ public class Functions { public static FunctionMap intFunctionMap() { FunctionMap intFunctionMap = new FunctionMap<>(); - // todo: add simple functions to the function map (abs, sgn, increment, decrement, square) + intFunctionMap.addFunction("square", n -> n * n); + intFunctionMap.addFunction("abs", Math::abs); + intFunctionMap.addFunction("increment", n -> n + 1); + intFunctionMap.addFunction("decrement", n -> n - 1); + intFunctionMap.addFunction("sgn", n -> (n != 0) ? n / abs(n) : 0); return intFunctionMap; } From 680f46b31e3cf5a6592b56170fbceb4cfa950824 Mon Sep 17 00:00:00 2001 From: Taras Boychuk Date: Mon, 1 Feb 2021 10:05:57 +0200 Subject: [PATCH 37/74] GP-39 fix packages in tdd exercises --- .../java/com/bobocode/tdd}/LinkedStack.java | 4 +- .../tdd/exception}/EmptyStackException.java | 2 +- .../test/java/com/bobocode/tdd/StackTest.java | 1 + .../java/com/bobocode/tdd/LinkedListTest.java | 322 ++++++++++++++++- .../{bst => tdd}/BinarySearchTree.java | 2 +- .../RecursiveBinarySearchTree.java | 2 +- .../bobocode/linked_list/LinkedListTest.java | 324 ------------------ 7 files changed, 328 insertions(+), 329 deletions(-) rename 6-0-test-driven-development/{src/main/java/com/bobocode/stack => 6-1-1-stack/src/main/java/com/bobocode/tdd}/LinkedStack.java (96%) rename 6-0-test-driven-development/{src/main/java/com/bobocode/stack => 6-1-1-stack/src/main/java/com/bobocode/tdd/exception}/EmptyStackException.java (63%) rename 6-0-test-driven-development/6-1-3-binary-search-tree/src/main/java/com/bobocode/{bst => tdd}/BinarySearchTree.java (96%) rename 6-0-test-driven-development/6-1-3-binary-search-tree/src/main/java/com/bobocode/{bst => tdd}/RecursiveBinarySearchTree.java (99%) delete mode 100644 6-0-test-driven-development/src/test/java/com/bobocode/linked_list/LinkedListTest.java diff --git a/6-0-test-driven-development/src/main/java/com/bobocode/stack/LinkedStack.java b/6-0-test-driven-development/6-1-1-stack/src/main/java/com/bobocode/tdd/LinkedStack.java similarity index 96% rename from 6-0-test-driven-development/src/main/java/com/bobocode/stack/LinkedStack.java rename to 6-0-test-driven-development/6-1-1-stack/src/main/java/com/bobocode/tdd/LinkedStack.java index a710d330..c4948e9f 100644 --- a/6-0-test-driven-development/src/main/java/com/bobocode/stack/LinkedStack.java +++ b/6-0-test-driven-development/6-1-1-stack/src/main/java/com/bobocode/tdd/LinkedStack.java @@ -1,4 +1,6 @@ -package com.bobocode.stack; +package com.bobocode.tdd; + +import com.bobocode.tdd.exception.EmptyStackException; import java.util.Objects; import java.util.stream.Stream; diff --git a/6-0-test-driven-development/src/main/java/com/bobocode/stack/EmptyStackException.java b/6-0-test-driven-development/6-1-1-stack/src/main/java/com/bobocode/tdd/exception/EmptyStackException.java similarity index 63% rename from 6-0-test-driven-development/src/main/java/com/bobocode/stack/EmptyStackException.java rename to 6-0-test-driven-development/6-1-1-stack/src/main/java/com/bobocode/tdd/exception/EmptyStackException.java index 68b78a2f..87894f17 100644 --- a/6-0-test-driven-development/src/main/java/com/bobocode/stack/EmptyStackException.java +++ b/6-0-test-driven-development/6-1-1-stack/src/main/java/com/bobocode/tdd/exception/EmptyStackException.java @@ -1,4 +1,4 @@ -package com.bobocode.stack; +package com.bobocode.tdd.exception; public class EmptyStackException extends RuntimeException{ diff --git a/6-0-test-driven-development/6-1-1-stack/src/test/java/com/bobocode/tdd/StackTest.java b/6-0-test-driven-development/6-1-1-stack/src/test/java/com/bobocode/tdd/StackTest.java index 924562cf..4c8bee48 100644 --- a/6-0-test-driven-development/6-1-1-stack/src/test/java/com/bobocode/tdd/StackTest.java +++ b/6-0-test-driven-development/6-1-1-stack/src/test/java/com/bobocode/tdd/StackTest.java @@ -1,5 +1,6 @@ package com.bobocode.tdd; +import com.bobocode.tdd.exception.EmptyStackException; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; diff --git a/6-0-test-driven-development/6-1-2-linked-list/src/test/java/com/bobocode/tdd/LinkedListTest.java b/6-0-test-driven-development/6-1-2-linked-list/src/test/java/com/bobocode/tdd/LinkedListTest.java index 4477db6b..3950f387 100644 --- a/6-0-test-driven-development/6-1-2-linked-list/src/test/java/com/bobocode/tdd/LinkedListTest.java +++ b/6-0-test-driven-development/6-1-2-linked-list/src/test/java/com/bobocode/tdd/LinkedListTest.java @@ -1,4 +1,324 @@ package com.bobocode.tdd; -public class LinkedListTest { + +import org.junit.jupiter.api.Test; + +import java.util.NoSuchElementException; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.assertj.core.api.AssertionsForClassTypes.assertThatExceptionOfType; + +class LinkedListTest { + + private List intList = new LinkedList<>(); + + @Test + void addIntoEmptyList() { + intList.add(41); + + int element = intList.get(0); + + assertThat(element).isEqualTo(41); + assertThat(intList.size()).isEqualTo(1); + } + + @Test + void getFirstElementFromSingleElementList() { + intList.add(25); + + int element = intList.get(0); + + assertThat(element).isEqualTo(25); + } + + @Test + void addElements() { + intList = LinkedList.of(43, 233, 54); + + assertThat(intList.size()).isEqualTo(3); + assertThat(intList.get(0)).isEqualTo(43); + assertThat(intList.get(1)).isEqualTo(233); + assertThat(intList.get(2)).isEqualTo(54); + } + + @Test + void size() { + intList = LinkedList.of(4, 7, 9, 0, 7); + + int size = intList.size(); + + assertThat(size).isEqualTo(5); + } + + @Test + void getFirstElement() { + intList = LinkedList.of(31, 32); + + int firstElement = intList.getFirst(); + assertThat(firstElement).isEqualTo(31); + } + + @Test + void getLastElement() { + intList = LinkedList.of(41, 42); + + int lastElement = intList.getLast(); + + assertThat(lastElement).isEqualTo(42); + } + + @Test + void getFirstOfEmptyList() { + assertThatExceptionOfType(NoSuchElementException.class) + .isThrownBy(() -> intList.getFirst()); + } + + @Test + void getLastOfEmptyList() { + assertThatExceptionOfType(NoSuchElementException.class) + .isThrownBy(() -> intList.getLast()); + } + + + @Test + void getElements() { + intList = LinkedList.of(25, 87, 45); + + int firstElement = intList.get(0); + int secondElement = intList.get(1); + int thirdElement = intList.get(2); + + assertThat(firstElement).isEqualTo(25); + assertThat(secondElement).isEqualTo(87); + assertThat(thirdElement).isEqualTo(45); + + } + + @Test + void addElementByZeroIndexIntoEmptyList() { + intList.add(0, 45); + + int element = intList.get(0); + + assertThat(element).isEqualTo(45); + assertThat(intList.size()).isEqualTo(1); + } + + @Test + void addElementByIndexToTheEndOfList() { + intList = LinkedList.of(98, 64, 23, 1, 3, 4); + + int newElementIndex = intList.size(); + intList.add(newElementIndex, 44); + + assertThat(intList.get(newElementIndex)).isEqualTo(44); + assertThat(intList.size()).isEqualTo(7); + } + + @Test + void addElementToTheHeadOfNonEmptyList() { + intList = LinkedList.of(4, 6, 8, 9, 0, 2); + + intList.add(0, 53); + + assertThat(intList.get(0)).isEqualTo(53); + assertThat(intList.get(1)).isEqualTo(4); + assertThat(intList.size()).isEqualTo(7); + } + + @Test + void addElementByIndex() { + intList = LinkedList.of(43, 5, 6, 8); + + int newElementIdx = 2; + intList.add(newElementIdx, 66); + + assertThat(intList.get(newElementIdx)).isEqualTo(66); + assertThat(intList.get(0)).isEqualTo(43); + assertThat(intList.get(1)).isEqualTo(5); + assertThat(intList.get(3)).isEqualTo(6); + assertThat(intList.get(4)).isEqualTo(8); + assertThat(intList.size()).isEqualTo(5); + } + + @Test + void addElementByNegativeIndex() { + assertThatExceptionOfType(IndexOutOfBoundsException.class) + .isThrownBy(() -> intList.add(-1, 66)); + } + + @Test + void addElementByIndexLargerThanListSize() { + intList = LinkedList.of(4, 6, 11, 9); + + int newElementIdx = 5; + + assertThatExceptionOfType(IndexOutOfBoundsException.class) + .isThrownBy(() -> intList.add(newElementIdx, 88)); + } + + @Test + void addElementByIndexEqualToSize() { + intList = LinkedList.of(1, 2, 3, 4, 5); // size = 5 + + intList.add(5, 111); + int element = intList.get(5); + + assertThat(element).isEqualTo(111); + assertThat(intList.size()).isEqualTo(6); + } + + @Test + void setFirstElementOnEmptyTree() { + assertThatExceptionOfType(IndexOutOfBoundsException.class) + .isThrownBy(() -> intList.set(0, 34)); + } + + @Test + void setElementByIndexEqualToSize() { + intList = LinkedList.of(2, 3, 4); // size = 3 + + assertThatExceptionOfType(IndexOutOfBoundsException.class) + .isThrownBy(() -> intList.set(3, 222)); + } + + @Test + void setElementByIndex() { + intList = LinkedList.of(34, 78, 9, 8); + + int index = 2; //element = 78 + intList.set(index, 99); + + assertThat(intList.get(index)).isEqualTo(99); + assertThat(intList.get(0)).isEqualTo(34); + assertThat(intList.get(1)).isEqualTo(78); + assertThat(intList.get(3)).isEqualTo(8); + assertThat(intList.size()).isEqualTo(4); + } + + @Test + void getFirstElementFromEmptyList() { + assertThatExceptionOfType(IndexOutOfBoundsException.class) + .isThrownBy(() -> intList.get(0)); + } + + @Test + void getElementByNegativeIndex() { + assertThatExceptionOfType(IndexOutOfBoundsException.class) + .isThrownBy(() -> intList.get(-1)); + } + + @Test + void getElementByIndexEqualsToListSize() { + intList = LinkedList.of(33, 46, 25, 87, 45); + assertThatExceptionOfType(IndexOutOfBoundsException.class) + .isThrownBy(() -> intList.get(5)); + } + + @Test + void removeElementFromEmptyList() { + assertThatExceptionOfType(IndexOutOfBoundsException.class) + .isThrownBy(() -> intList.remove(234)); + } + + @Test + void removeFirstElement() { + intList = LinkedList.of(4, 6, 8, 9); + + int deletedElement = intList.remove(0); + + assertThat(intList.get(0)).isEqualTo(6); + assertThat(intList.size()).isEqualTo(3); + assertThat(deletedElement).isEqualTo(4); + } + + @Test + void removeLastElement() { + intList = LinkedList.of(4, 6, 8, 9); + + int deletedElement = intList.remove(intList.size() - 1); + + assertThat(intList.get(intList.size() - 1)).isEqualTo(8); + assertThat(intList.size()).isEqualTo(3); + assertThat(deletedElement).isEqualTo(9); + } + + @Test + void removeElement() { + intList = LinkedList.of(1, 2, 3, 4, 5); + + int elementIndex = 2; + int deletedElement = intList.remove(elementIndex); // element = 3 + + assertThat(intList.get(elementIndex)).isEqualTo(4); + assertThat(intList.size()).isEqualTo(4); + assertThat(deletedElement).isEqualTo(3); + } + + @Test + void containsOnEmptyList() { + boolean contains = intList.contains(34); + + assertThat(contains).isFalse(); + } + + @Test + void contains() { + intList = LinkedList.of(45, 6, 3, 6); + + boolean containsExistingElement = intList.contains(3); + boolean containsNotExistingElement = intList.contains(54); + + assertThat(containsExistingElement).isTrue(); + assertThat(containsNotExistingElement).isFalse(); + } + + @Test + void isEmptyOnEmptyList() { + boolean empty = intList.isEmpty(); + + assertThat(empty).isTrue(); + } + + @Test + void isEmpty() { + intList = LinkedList.of(34, 5, 6); + + boolean empty = intList.isEmpty(); + + assertThat(empty).isFalse(); + } + + @Test + void sizeOnEmptyList() { + int size = intList.size(); + + assertThat(size).isEqualTo(0); + } + + @Test + void clearOnEmptyList() { + intList.clear(); + + assertThat(intList.size()).isEqualTo(0); + } + + @Test + void clearChangesTheSize() { + intList = LinkedList.of(4, 5, 6); + + intList.clear(); + + assertThat(intList.size()).isEqualTo(0); + } + + @Test + void clearRemovesElements() { + intList = LinkedList.of(4, 5, 6); + + intList.clear(); + + assertThatExceptionOfType(IndexOutOfBoundsException.class) + .isThrownBy(() -> intList.get(0)); + } } diff --git a/6-0-test-driven-development/6-1-3-binary-search-tree/src/main/java/com/bobocode/bst/BinarySearchTree.java b/6-0-test-driven-development/6-1-3-binary-search-tree/src/main/java/com/bobocode/tdd/BinarySearchTree.java similarity index 96% rename from 6-0-test-driven-development/6-1-3-binary-search-tree/src/main/java/com/bobocode/bst/BinarySearchTree.java rename to 6-0-test-driven-development/6-1-3-binary-search-tree/src/main/java/com/bobocode/tdd/BinarySearchTree.java index 5dd6c160..cea1ceff 100644 --- a/6-0-test-driven-development/6-1-3-binary-search-tree/src/main/java/com/bobocode/bst/BinarySearchTree.java +++ b/6-0-test-driven-development/6-1-3-binary-search-tree/src/main/java/com/bobocode/tdd/BinarySearchTree.java @@ -1,4 +1,4 @@ -package com.bobocode.bst; +package com.bobocode.tdd; import java.util.function.Consumer; diff --git a/6-0-test-driven-development/6-1-3-binary-search-tree/src/main/java/com/bobocode/bst/RecursiveBinarySearchTree.java b/6-0-test-driven-development/6-1-3-binary-search-tree/src/main/java/com/bobocode/tdd/RecursiveBinarySearchTree.java similarity index 99% rename from 6-0-test-driven-development/6-1-3-binary-search-tree/src/main/java/com/bobocode/bst/RecursiveBinarySearchTree.java rename to 6-0-test-driven-development/6-1-3-binary-search-tree/src/main/java/com/bobocode/tdd/RecursiveBinarySearchTree.java index 190c3824..aa134a7d 100644 --- a/6-0-test-driven-development/6-1-3-binary-search-tree/src/main/java/com/bobocode/bst/RecursiveBinarySearchTree.java +++ b/6-0-test-driven-development/6-1-3-binary-search-tree/src/main/java/com/bobocode/tdd/RecursiveBinarySearchTree.java @@ -1,4 +1,4 @@ -package com.bobocode.bst; +package com.bobocode.tdd; import java.util.Objects; import java.util.function.Consumer; diff --git a/6-0-test-driven-development/src/test/java/com/bobocode/linked_list/LinkedListTest.java b/6-0-test-driven-development/src/test/java/com/bobocode/linked_list/LinkedListTest.java deleted file mode 100644 index d276554a..00000000 --- a/6-0-test-driven-development/src/test/java/com/bobocode/linked_list/LinkedListTest.java +++ /dev/null @@ -1,324 +0,0 @@ -package com.bobocode.linked_list; - - -import org.junit.jupiter.api.Test; - -import java.util.NoSuchElementException; - -import static org.assertj.core.api.AssertionsForClassTypes.assertThat; -import static org.assertj.core.api.AssertionsForClassTypes.assertThatExceptionOfType; - -class LinkedListTest { - - private List intList = new LinkedList<>(); - - @Test - void addIntoEmptyList() { - intList.add(41); - - int element = intList.get(0); - - assertThat(element).isEqualTo(41); - assertThat(intList.size()).isEqualTo(1); - } - - @Test - void getFirstElementFromSingleElementList() { - intList.add(25); - - int element = intList.get(0); - - assertThat(element).isEqualTo(25); - } - - @Test - void addElements() { - intList = LinkedList.of(43, 233, 54); - - assertThat(intList.size()).isEqualTo(3); - assertThat(intList.get(0)).isEqualTo(43); - assertThat(intList.get(1)).isEqualTo(233); - assertThat(intList.get(2)).isEqualTo(54); - } - - @Test - void size() { - intList = LinkedList.of(4, 7, 9, 0, 7); - - int size = intList.size(); - - assertThat(size).isEqualTo(5); - } - - @Test - void getFirstElement() { - intList = LinkedList.of(31, 32); - - int firstElement = intList.getFirst(); - assertThat(firstElement).isEqualTo(31); - } - - @Test - void getLastElement() { - intList = LinkedList.of(41, 42); - - int lastElement = intList.getLast(); - - assertThat(lastElement).isEqualTo(42); - } - - @Test - void getFirstOfEmptyList() { - assertThatExceptionOfType(NoSuchElementException.class) - .isThrownBy(() -> intList.getFirst()); - } - - @Test - void getLastOfEmptyList() { - assertThatExceptionOfType(NoSuchElementException.class) - .isThrownBy(() -> intList.getLast()); - } - - - @Test - void getElements() { - intList = LinkedList.of(25, 87, 45); - - int firstElement = intList.get(0); - int secondElement = intList.get(1); - int thirdElement = intList.get(2); - - assertThat(firstElement).isEqualTo(25); - assertThat(secondElement).isEqualTo(87); - assertThat(thirdElement).isEqualTo(45); - - } - - @Test - void addElementByZeroIndexIntoEmptyList() { - intList.add(0, 45); - - int element = intList.get(0); - - assertThat(element).isEqualTo(45); - assertThat(intList.size()).isEqualTo(1); - } - - @Test - void addElementByIndexToTheEndOfList() { - intList = LinkedList.of(98, 64, 23, 1, 3, 4); - - int newElementIndex = intList.size(); - intList.add(newElementIndex, 44); - - assertThat(intList.get(newElementIndex)).isEqualTo(44); - assertThat(intList.size()).isEqualTo(7); - } - - @Test - void addElementToTheHeadOfNonEmptyList() { - intList = LinkedList.of(4, 6, 8, 9, 0, 2); - - intList.add(0, 53); - - assertThat(intList.get(0)).isEqualTo(53); - assertThat(intList.get(1)).isEqualTo(4); - assertThat(intList.size()).isEqualTo(7); - } - - @Test - void addElementByIndex() { - intList = LinkedList.of(43, 5, 6, 8); - - int newElementIdx = 2; - intList.add(newElementIdx, 66); - - assertThat(intList.get(newElementIdx)).isEqualTo(66); - assertThat(intList.get(0)).isEqualTo(43); - assertThat(intList.get(1)).isEqualTo(5); - assertThat(intList.get(3)).isEqualTo(6); - assertThat(intList.get(4)).isEqualTo(8); - assertThat(intList.size()).isEqualTo(5); - } - - @Test - void addElementByNegativeIndex() { - assertThatExceptionOfType(IndexOutOfBoundsException.class) - .isThrownBy(() -> intList.add(-1, 66)); - } - - @Test - void addElementByIndexLargerThanListSize() { - intList = LinkedList.of(4, 6, 11, 9); - - int newElementIdx = 5; - - assertThatExceptionOfType(IndexOutOfBoundsException.class) - .isThrownBy(() -> intList.add(newElementIdx, 88)); - } - - @Test - void addElementByIndexEqualToSize() { - intList = LinkedList.of(1, 2, 3, 4, 5); // size = 5 - - intList.add(5, 111); - int element = intList.get(5); - - assertThat(element).isEqualTo(111); - assertThat(intList.size()).isEqualTo(6); - } - - @Test - void setFirstElementOnEmptyTree() { - assertThatExceptionOfType(IndexOutOfBoundsException.class) - .isThrownBy(() -> intList.set(0, 34)); - } - - @Test - void setElementByIndexEqualToSize() { - intList = LinkedList.of(2, 3, 4); // size = 3 - - assertThatExceptionOfType(IndexOutOfBoundsException.class) - .isThrownBy(() -> intList.set(3, 222)); - } - - @Test - void setElementByIndex() { - intList = LinkedList.of(34, 78, 9, 8); - - int index = 2; //element = 78 - intList.set(index, 99); - - assertThat(intList.get(index)).isEqualTo(99); - assertThat(intList.get(0)).isEqualTo(34); - assertThat(intList.get(1)).isEqualTo(78); - assertThat(intList.get(3)).isEqualTo(8); - assertThat(intList.size()).isEqualTo(4); - } - - @Test - void getFirstElementFromEmptyList() { - assertThatExceptionOfType(IndexOutOfBoundsException.class) - .isThrownBy(() -> intList.get(0)); - } - - @Test - void getElementByNegativeIndex() { - assertThatExceptionOfType(IndexOutOfBoundsException.class) - .isThrownBy(() -> intList.get(-1)); - } - - @Test - void getElementByIndexEqualsToListSize() { - intList = LinkedList.of(33, 46, 25, 87, 45); - assertThatExceptionOfType(IndexOutOfBoundsException.class) - .isThrownBy(() -> intList.get(5)); - } - - @Test - void removeElementFromEmptyList() { - assertThatExceptionOfType(IndexOutOfBoundsException.class) - .isThrownBy(() -> intList.remove(234)); - } - - @Test - void removeFirstElement() { - intList = LinkedList.of(4, 6, 8, 9); - - int deletedElement = intList.remove(0); - - assertThat(intList.get(0)).isEqualTo(6); - assertThat(intList.size()).isEqualTo(3); - assertThat(deletedElement).isEqualTo(4); - } - - @Test - void removeLastElement() { - intList = LinkedList.of(4, 6, 8, 9); - - int deletedElement = intList.remove(intList.size() - 1); - - assertThat(intList.get(intList.size() - 1)).isEqualTo(8); - assertThat(intList.size()).isEqualTo(3); - assertThat(deletedElement).isEqualTo(9); - } - - @Test - void removeElement() { - intList = LinkedList.of(1, 2, 3, 4, 5); - - int elementIndex = 2; - int deletedElement = intList.remove(elementIndex); // element = 3 - - assertThat(intList.get(elementIndex)).isEqualTo(4); - assertThat(intList.size()).isEqualTo(4); - assertThat(deletedElement).isEqualTo(3); - } - - @Test - void containsOnEmptyList() { - boolean contains = intList.contains(34); - - assertThat(contains).isFalse(); - } - - @Test - void contains() { - intList = LinkedList.of(45, 6, 3, 6); - - boolean containsExistingElement = intList.contains(3); - boolean containsNotExistingElement = intList.contains(54); - - assertThat(containsExistingElement).isTrue(); - assertThat(containsNotExistingElement).isFalse(); - } - - @Test - void isEmptyOnEmptyList() { - boolean empty = intList.isEmpty(); - - assertThat(empty).isTrue(); - } - - @Test - void isEmpty() { - intList = LinkedList.of(34, 5, 6); - - boolean empty = intList.isEmpty(); - - assertThat(empty).isFalse(); - } - - @Test - void sizeOnEmptyList() { - int size = intList.size(); - - assertThat(size).isEqualTo(0); - } - - @Test - void clearOnEmptyList() { - intList.clear(); - - assertThat(intList.size()).isEqualTo(0); - } - - @Test - void clearChangesTheSize() { - intList = LinkedList.of(4, 5, 6); - - intList.clear(); - - assertThat(intList.size()).isEqualTo(0); - } - - @Test - void clearRemovesElements() { - intList = LinkedList.of(4, 5, 6); - - intList.clear(); - - assertThatExceptionOfType(IndexOutOfBoundsException.class) - .isThrownBy(() -> intList.get(0)); - } -} From ed2e1de61479a06be6d35fa796d45c5ae35a9eb0 Mon Sep 17 00:00:00 2001 From: Taras Boychuk Date: Mon, 1 Feb 2021 10:13:11 +0200 Subject: [PATCH 38/74] GP-39 fix RecursiveBinarySearchTree.java --- .../tdd/RecursiveBinarySearchTree.java | 106 ++++++++++++++++-- 1 file changed, 98 insertions(+), 8 deletions(-) diff --git a/6-0-test-driven-development/6-1-3-binary-search-tree/src/main/java/com/bobocode/tdd/RecursiveBinarySearchTree.java b/6-0-test-driven-development/6-1-3-binary-search-tree/src/main/java/com/bobocode/tdd/RecursiveBinarySearchTree.java index a9e368a9..aa134a7d 100644 --- a/6-0-test-driven-development/6-1-3-binary-search-tree/src/main/java/com/bobocode/tdd/RecursiveBinarySearchTree.java +++ b/6-0-test-driven-development/6-1-3-binary-search-tree/src/main/java/com/bobocode/tdd/RecursiveBinarySearchTree.java @@ -1,37 +1,127 @@ package com.bobocode.tdd; -import com.bobocode.util.ExerciseNotCompletedException; - +import java.util.Objects; import java.util.function.Consumer; +import java.util.stream.Stream; public class RecursiveBinarySearchTree implements BinarySearchTree { + private static class Node { + T element; + Node left; + Node right; + + private Node(T element) { + this.element = element; + } + + public static Node valueOf(T element) { + return new Node(element); + } + } + + private Node root; + private int size = 0; public static RecursiveBinarySearchTree of(T... elements) { - throw new ExerciseNotCompletedException(); + RecursiveBinarySearchTree bst = new RecursiveBinarySearchTree<>(); + Stream.of(elements).forEach(bst::insert); + return bst; } @Override public boolean insert(T element) { - throw new ExerciseNotCompletedException(); + Objects.requireNonNull(element); + boolean isInserted = insertElement(element); + if (isInserted) { + size++; + } + return isInserted; + } + + boolean insertElement(T element) { + if (root == null) { + root = Node.valueOf(element); + return true; + } else { + return insertIntoSubTree(root, element); + } + } + + private boolean insertIntoSubTree(Node subTreeRoot, T element) { + if (subTreeRoot.element.compareTo(element) > 0) { + return insertIntoLeftSubtree(subTreeRoot, element); + } else if (subTreeRoot.element.compareTo(element) < 0) { + return insertIntoRightSubtree(subTreeRoot, element); + } else { + return false; + } + } + + private boolean insertIntoLeftSubtree(Node node, T element) { + if (node.left != null) { + return insertIntoSubTree(node.left, element); + } else { + node.left = Node.valueOf(element); + return true; + } + } + + private boolean insertIntoRightSubtree(Node node, T element) { + if (node.right != null) { + return insertIntoSubTree(node.right, element); + } else { + node.right = Node.valueOf(element); + return true; + } } + @Override public boolean contains(T element) { - throw new ExerciseNotCompletedException(); + Objects.requireNonNull(element); + return findChildNodeByElement(root, element) != null; + } + + private Node findChildNodeByElement(Node node, T element) { + if (node == null) { + return null; + } else if (node.element.compareTo(element) > 0) { + return findChildNodeByElement(node.left, element); + } else if (node.element.compareTo(element) < 0) { + return findChildNodeByElement(node.right, element); + } else { + return node; + } } @Override public int size() { - throw new ExerciseNotCompletedException(); + return size; } @Override public int depth() { - throw new ExerciseNotCompletedException(); + return root != null ? depth(root) - 1 : 0; + } + + private int depth(Node node) { + if (node == null) { + return 0; + } else { + return 1 + Math.max(depth(node.left), depth(node.right)); + } } @Override public void inOrderTraversal(Consumer consumer) { - throw new ExerciseNotCompletedException(); + inOrderTraversal(root, consumer); + } + + private void inOrderTraversal(Node node, Consumer consumer) { + if (node != null) { + inOrderTraversal(node.left, consumer); + consumer.accept(node.element); + inOrderTraversal(node.right, consumer); + } } } From 0ee09d2faf0d1007edf93b60fd91e4e89aa5387b Mon Sep 17 00:00:00 2001 From: Taras Boychuk Date: Mon, 1 Feb 2021 10:54:02 +0200 Subject: [PATCH 39/74] GP-39 fix packages in OOP exercises --- .../src/main/java/com/bobocode/oop/data/FlightDao.java | 2 +- .../src/main/java/com/bobocode/oop/service/Flights.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/4-0-object-oriented-programming/4-3-1-flight-search/src/main/java/com/bobocode/oop/data/FlightDao.java b/4-0-object-oriented-programming/4-3-1-flight-search/src/main/java/com/bobocode/oop/data/FlightDao.java index 3c837b31..6865612f 100644 --- a/4-0-object-oriented-programming/4-3-1-flight-search/src/main/java/com/bobocode/oop/data/FlightDao.java +++ b/4-0-object-oriented-programming/4-3-1-flight-search/src/main/java/com/bobocode/oop/data/FlightDao.java @@ -1,6 +1,6 @@ package com.bobocode.oop.data; -import com.bobocode.flight_search.service.Flights; +import com.bobocode.oop.service.Flights; import java.util.HashSet; import java.util.Set; diff --git a/4-0-object-oriented-programming/4-3-1-flight-search/src/main/java/com/bobocode/oop/service/Flights.java b/4-0-object-oriented-programming/4-3-1-flight-search/src/main/java/com/bobocode/oop/service/Flights.java index b6df8ee3..be6793a1 100644 --- a/4-0-object-oriented-programming/4-3-1-flight-search/src/main/java/com/bobocode/oop/service/Flights.java +++ b/4-0-object-oriented-programming/4-3-1-flight-search/src/main/java/com/bobocode/oop/service/Flights.java @@ -1,4 +1,4 @@ -package com.bobocode.flight_search.service; +package com.bobocode.oop.service; import java.util.Set; From 56c6adf7da277806903ad9e98aac62673490406d Mon Sep 17 00:00:00 2001 From: Serhii Manko Date: Mon, 1 Feb 2021 17:09:06 +0100 Subject: [PATCH 40/74] Remove unnecessary checking --- .../src/main/java/com/bobocode/linked_list/LinkedList.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/2-0-data-structures-and-algorithms/src/main/java/com/bobocode/linked_list/LinkedList.java b/2-0-data-structures-and-algorithms/src/main/java/com/bobocode/linked_list/LinkedList.java index 553f0608..874a94fd 100644 --- a/2-0-data-structures-and-algorithms/src/main/java/com/bobocode/linked_list/LinkedList.java +++ b/2-0-data-structures-and-algorithms/src/main/java/com/bobocode/linked_list/LinkedList.java @@ -159,9 +159,8 @@ private void checkElementsExist() { */ @Override public T remove(int index) { - T deletedElement = null; + T deletedElement; if (index == 0) { - Objects.checkIndex(index, size); deletedElement = head.value; removeHead(); } else { From 231adbad260c4c7c94051596eb0b1531f50b65f2 Mon Sep 17 00:00:00 2001 From: Serhii Date: Fri, 5 Feb 2021 12:57:31 +0200 Subject: [PATCH 41/74] Refactor add() in LinkedList --- .../com/bobocode/linked_list/LinkedList.java | 51 +++++++++++-------- 1 file changed, 29 insertions(+), 22 deletions(-) diff --git a/2-0-data-structures-and-algorithms/src/main/java/com/bobocode/linked_list/LinkedList.java b/2-0-data-structures-and-algorithms/src/main/java/com/bobocode/linked_list/LinkedList.java index 553f0608..b8b845a9 100644 --- a/2-0-data-structures-and-algorithms/src/main/java/com/bobocode/linked_list/LinkedList.java +++ b/2-0-data-structures-and-algorithms/src/main/java/com/bobocode/linked_list/LinkedList.java @@ -11,8 +11,8 @@ * @param generic type parameter */ public class LinkedList implements List { - private Node head; - private Node tail; + private Node first; + private Node last; private int size; /** @@ -35,7 +35,14 @@ public static LinkedList of(T... elements) { */ @Override public void add(T element) { - add(size, element); + Node newNode = new Node<>(element); + if (size == 0) { + first = last = newNode; + } else { + last.next = newNode; + last = newNode; + } + size++; } /** @@ -59,16 +66,16 @@ public void add(int index, T element) { } private void addAsHead(Node newNode) { - newNode.next = head; - head = newNode; - if (head.next == null) { - tail = head; + newNode.next = first; + first = newNode; + if (first.next == null) { + last = first; } } private void addAsTail(Node newNode) { - tail.next = newNode; - tail = newNode; + last.next = newNode; + last = newNode; } private void add(int index, Node newNode) { @@ -80,14 +87,14 @@ private void add(int index, Node newNode) { private Node findNodeByIndex(int index) { Objects.checkIndex(index, size); if (index == size - 1) { - return tail; + return last; } else { return nodeAt(index); } } private Node nodeAt(int index) { - Node currentNode = head; + Node currentNode = first; for (int i = 0; i < index; i++) { currentNode = currentNode.next; } @@ -129,7 +136,7 @@ public T get(int index) { @Override public T getFirst() { checkElementsExist(); - return head.value; + return first.value; } /** @@ -141,11 +148,11 @@ public T getFirst() { @Override public T getLast() { checkElementsExist(); - return tail.value; + return last.value; } private void checkElementsExist() { - if (head == null) { + if (first == null) { throw new NoSuchElementException(); } } @@ -162,14 +169,14 @@ public T remove(int index) { T deletedElement = null; if (index == 0) { Objects.checkIndex(index, size); - deletedElement = head.value; + deletedElement = first.value; removeHead(); } else { Node previousNode = findNodeByIndex(index - 1); deletedElement = previousNode.next.value; previousNode.next = previousNode.next.next; if (index == size - 1) { - tail = previousNode; + last = previousNode; } } size--; @@ -177,9 +184,9 @@ public T remove(int index) { } private void removeHead() { - head = head.next; - if (head == null) { - tail = null; + first = first.next; + if (first == null) { + last = null; } } @@ -190,7 +197,7 @@ private void removeHead() { */ @Override public boolean contains(T element) { - Node currentNode = head; + Node currentNode = first; while (currentNode != null) { if (currentNode.value.equals(element)) { return true; @@ -207,7 +214,7 @@ public boolean contains(T element) { */ @Override public boolean isEmpty() { - return head == null; + return first == null; } /** @@ -225,7 +232,7 @@ public int size() { */ @Override public void clear() { - head = tail = null; + first = last = null; size = 0; } From 07fbe60a5a6695f454bfb06b8203f1c01330ed3a Mon Sep 17 00:00:00 2001 From: Taras Date: Wed, 10 Feb 2021 12:55:43 +0200 Subject: [PATCH 42/74] Update README.md --- 0-0-intro/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/0-0-intro/README.md b/0-0-intro/README.md index f8df80ec..24aade65 100644 --- a/0-0-intro/README.md +++ b/0-0-intro/README.md @@ -1,7 +1,7 @@ # Introduction The whole course consists of various exercises grouped by topics. Introduction itself is an exercise, so you can undersntand the idea on a simple example. -Each exercise has thee major parts: +Each exercise has three major parts: * **a task** โ€“ some logic that you should implement in order to complete the exercise ๐Ÿค” * **a test** โ€“ a corresponding test that verifies if you implement the task correctly โ–ถ๏ธ * **a completed solution** - a branch `completed` that holds implemented task โœ… @@ -13,7 +13,7 @@ Go ahead and complete this exercise by implementing a method in `Introduction` c * [Git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) ## How to start โ“ -* [clone](https://docs.github.com/en/github/creating-cloning-and-archiving-repositories/cloning-a-repository) this [repository](https://github.com/bobocode-projects/java-fundamentals-course) to your comuter +* [clone](https://docs.github.com/en/github/creating-cloning-and-archiving-repositories/cloning-a-repository) this [repository](https://github.com/bobocode-projects/java-fundamentals-course) to your computer * **open** the project via IDE ## Have a question? ๐Ÿง From c8be790b5e939218afda091ba3f738550681cd38 Mon Sep 17 00:00:00 2001 From: Taras Boychuk Date: Wed, 10 Feb 2021 12:58:14 +0200 Subject: [PATCH 43/74] Update CI pipeline name --- .github/workflows/maven.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 387f74a6..ec79ba7b 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -1,7 +1,7 @@ # This workflow will build a Java project with Maven # For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven -name: Java CI with Maven +name: Build completed branch with tests on: push: From 199408d2150dbb6e6ba74f8926533acc47775646 Mon Sep 17 00:00:00 2001 From: Taras Boychuk Date: Sun, 14 Feb 2021 16:21:37 +0200 Subject: [PATCH 44/74] GP-62 complete new Node exercise --- .../src/main/java/com/bobocode/node/Node.java | 7 +++- .../main/java/com/bobocode/node/Nodes.java | 33 ++++++++++++++----- 2 files changed, 31 insertions(+), 9 deletions(-) diff --git a/2-0-data-structures-and-algorithms/src/main/java/com/bobocode/node/Node.java b/2-0-data-structures-and-algorithms/src/main/java/com/bobocode/node/Node.java index c675e81e..51e4fa16 100644 --- a/2-0-data-structures-and-algorithms/src/main/java/com/bobocode/node/Node.java +++ b/2-0-data-structures-and-algorithms/src/main/java/com/bobocode/node/Node.java @@ -8,5 +8,10 @@ * @param a generic type T */ public class Node { - // todo: + T element; + Node next; + + public Node(T element) { + this.element = element; + } } diff --git a/2-0-data-structures-and-algorithms/src/main/java/com/bobocode/node/Nodes.java b/2-0-data-structures-and-algorithms/src/main/java/com/bobocode/node/Nodes.java index 3a31ee1a..c83859e4 100644 --- a/2-0-data-structures-and-algorithms/src/main/java/com/bobocode/node/Nodes.java +++ b/2-0-data-structures-and-algorithms/src/main/java/com/bobocode/node/Nodes.java @@ -1,7 +1,5 @@ package com.bobocode.node; -import com.bobocode.util.ExerciseNotCompletedException; - /** * A class that consists of static methods only and provides util methods for {@link Node}. */ @@ -17,7 +15,7 @@ private Nodes() { * @return a new instance of {@link Node} */ public static Node create(T element) { - throw new ExerciseNotCompletedException(); // todo: + return new Node<>(element); } /** @@ -28,7 +26,7 @@ public static Node create(T element) { * @param a genetic type */ public static void link(Node first, Node second) { - throw new ExerciseNotCompletedException(); // todo: + first.next = second; } /** @@ -41,7 +39,9 @@ public static void link(Node first, Node second) { * @return a reference to a first node created based on firstElement */ public static Node pairOf(T firstElement, T secondElement) { - throw new ExerciseNotCompletedException(); // todo: + Node firstNode = new Node<>(firstElement); + firstNode.next = new Node<>(secondElement); + return firstNode; } /** @@ -55,7 +55,11 @@ public static Node pairOf(T firstElement, T secondElement) { * @return a reference to the first node */ public static Node closedPairOf(T firstElement, T secondElement) { - throw new ExerciseNotCompletedException(); // todo: + Node firstNode = new Node<>(firstElement); + Node secondNode = new Node<>(secondElement); + firstNode.next = secondNode; + secondNode.next = firstNode; + return firstNode; } /** @@ -67,7 +71,13 @@ public static Node closedPairOf(T firstElement, T secondElement) { * @return a reference to the first element of the chain */ public static Node chainOf(T... elements) { - throw new ExerciseNotCompletedException(); // todo: + Node firstNode = new Node<>(elements[0]); + Node current = firstNode; + for (int i = 1; i < elements.length; i++) { + current.next = new Node<>(elements[i]); + current = current.next; + } + return firstNode; } /** @@ -80,6 +90,13 @@ public static Node chainOf(T... elements) { * @return a reference to the first element of the chain */ public static Node circleOf(T... elements) { - throw new ExerciseNotCompletedException(); // todo: + Node firstNode = new Node<>(elements[0]); + Node current = firstNode; + for (int i = 1; i < elements.length; i++) { + current.next = new Node<>(elements[i]); + current = current.next; + } + current.next = firstNode; + return firstNode; } } From 7295ced18f8ab7b67df317862382731d193de88c Mon Sep 17 00:00:00 2001 From: Taras Boychuk Date: Fri, 26 Feb 2021 16:45:45 +0200 Subject: [PATCH 45/74] GP-39 implement updated PrimeNumbers --- .../java/com/bobocode/fp/PrimeNumbers.java | 30 +++++++++++++++---- 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/5-0-functional-programming/5-4-1-fun-prime-numbers/src/main/java/com/bobocode/fp/PrimeNumbers.java b/5-0-functional-programming/5-4-1-fun-prime-numbers/src/main/java/com/bobocode/fp/PrimeNumbers.java index 1f941cef..69eec66d 100644 --- a/5-0-functional-programming/5-4-1-fun-prime-numbers/src/main/java/com/bobocode/fp/PrimeNumbers.java +++ b/5-0-functional-programming/5-4-1-fun-prime-numbers/src/main/java/com/bobocode/fp/PrimeNumbers.java @@ -1,9 +1,10 @@ package com.bobocode.fp; -import com.bobocode.util.ExerciseNotCompletedException; - import java.util.List; import java.util.function.IntConsumer; +import java.util.stream.IntStream; + +import static java.util.stream.Collectors.toList; /** * {@link PrimeNumbers} provides an API to work with prime numbers. It is using a stream of prime numbers. @@ -22,8 +23,9 @@ private PrimeNumbers() { * @return the sum of n prime numbers */ public static int sum(int n) { - throw new ExerciseNotCompletedException(); // todo: create an infinite stream of ints, then filter prime numbs - + return primeNumberStream() + .limit(n) + .reduce(0, Integer::sum); } /** @@ -32,7 +34,10 @@ public static int sum(int n) { * @return a list of collected prime numbers */ public static List collect(int n) { - throw new ExerciseNotCompletedException(); // todo: reuse the logic of prime numbers stream and collect them + return primeNumberStream() + .limit(n) + .boxed() + .collect(toList()); } /** @@ -42,6 +47,19 @@ public static List collect(int n) { * @param consumer a logic that should be applied to the found prime number */ public static void processByIndex(int idx, IntConsumer consumer) { - throw new ExerciseNotCompletedException(); // todo: reuse the logic of prime numbers stream then process the last one + primeNumberStream() + .limit(idx) + .reduce((a, b) -> b) + .ifPresent(consumer); + } + + private static IntStream primeNumberStream() { + return IntStream.iterate(1, i -> i + 1) + .filter(PrimeNumbers::isPrime); + } + + private static boolean isPrime(int n) { + return IntStream.range(2, n) + .noneMatch(i -> n % i == 0); } } From 7396a4efda2b64e481e80ecc517e1729088737fa Mon Sep 17 00:00:00 2001 From: Taras Boychuk Date: Fri, 26 Feb 2021 17:28:04 +0200 Subject: [PATCH 46/74] Upgrade the impl of PrimeNumbers.java --- .../src/main/java/com/bobocode/fp/PrimeNumbers.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/5-0-functional-programming/5-4-1-fun-prime-numbers/src/main/java/com/bobocode/fp/PrimeNumbers.java b/5-0-functional-programming/5-4-1-fun-prime-numbers/src/main/java/com/bobocode/fp/PrimeNumbers.java index 69eec66d..414619b0 100644 --- a/5-0-functional-programming/5-4-1-fun-prime-numbers/src/main/java/com/bobocode/fp/PrimeNumbers.java +++ b/5-0-functional-programming/5-4-1-fun-prime-numbers/src/main/java/com/bobocode/fp/PrimeNumbers.java @@ -25,7 +25,7 @@ private PrimeNumbers() { public static int sum(int n) { return primeNumberStream() .limit(n) - .reduce(0, Integer::sum); + .sum(); } /** From 7fcebab0ff16fb1c562303763ef17934a3544cfa Mon Sep 17 00:00:00 2001 From: Taras Boychuk Date: Thu, 1 Jul 2021 20:22:54 +0300 Subject: [PATCH 47/74] Complete hello-generics exercise --- .../src/main/java/com/bobocode/basics/Box.java | 10 +++++----- .../src/main/java/com/bobocode/basics/BoxDemoApp.java | 11 +++++------ 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/1-0-java-basics/1-3-0-hello-generics/src/main/java/com/bobocode/basics/Box.java b/1-0-java-basics/1-3-0-hello-generics/src/main/java/com/bobocode/basics/Box.java index a7d468d1..3e7b6978 100644 --- a/1-0-java-basics/1-3-0-hello-generics/src/main/java/com/bobocode/basics/Box.java +++ b/1-0-java-basics/1-3-0-hello-generics/src/main/java/com/bobocode/basics/Box.java @@ -7,18 +7,18 @@ *

* todo: refactor this class so it uses generic type and run {@link com.bobocode.basics.BoxTest} to verify it */ -public class Box { - private Object value; +public class Box { + private T value; - public Box(Object value) { + public Box(T value) { this.value = value; } - public Object getValue() { + public T getValue() { return value; } - public void setValue(Object value) { + public void setValue(T value) { this.value = value; } } diff --git a/1-0-java-basics/1-3-0-hello-generics/src/main/java/com/bobocode/basics/BoxDemoApp.java b/1-0-java-basics/1-3-0-hello-generics/src/main/java/com/bobocode/basics/BoxDemoApp.java index bc12174e..da2997c7 100644 --- a/1-0-java-basics/1-3-0-hello-generics/src/main/java/com/bobocode/basics/BoxDemoApp.java +++ b/1-0-java-basics/1-3-0-hello-generics/src/main/java/com/bobocode/basics/BoxDemoApp.java @@ -9,13 +9,12 @@ */ public class BoxDemoApp { public static void main(String[] args) { - Box intBox = new Box(123); - Box intBox2 = new Box(321); - System.out.println((int) intBox.getValue() + (int) intBox2.getValue()); + Box intBox = new Box<>(123); + Box intBox2 = new Box<>(321); + System.out.println((int) intBox.getValue() + intBox2.getValue()); intBox.setValue(222); - intBox.setValue("abc"); // this should not be allowed - // the following code will compile, but will throw runtime exception - System.out.println((int) intBox.getValue() + (int) intBox2.getValue()); +// intBox.setValue("abc"); // this should not be allowed + System.out.println((int) intBox.getValue() + intBox2.getValue()); } } From e12ebde6949fc86be87d55f81f83d4363ce5faf4 Mon Sep 17 00:00:00 2001 From: Taras Boychuk Date: Fri, 2 Jul 2021 19:35:49 +0300 Subject: [PATCH 48/74] Complete crazy-generics exercise --- .../com/bobocode/basics/CrazyGenerics.java | 44 ++++++++++--------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/1-0-java-basics/1-3-1-crazy-generics/src/main/java/com/bobocode/basics/CrazyGenerics.java b/1-0-java-basics/1-3-1-crazy-generics/src/main/java/com/bobocode/basics/CrazyGenerics.java index 62a12996..5737ae41 100644 --- a/1-0-java-basics/1-3-1-crazy-generics/src/main/java/com/bobocode/basics/CrazyGenerics.java +++ b/1-0-java-basics/1-3-1-crazy-generics/src/main/java/com/bobocode/basics/CrazyGenerics.java @@ -1,11 +1,11 @@ package com.bobocode.basics; import com.bobocode.basics.util.BaseEntity; -import com.bobocode.util.ExerciseNotCompletedException; import lombok.Data; import java.io.Serializable; import java.util.Collection; +import java.util.List; /** * {@link CrazyGenerics} is an exercise class. It consists of classes, interfaces and methods that should be updated @@ -25,8 +25,8 @@ public class CrazyGenerics { * @param โ€“ value type */ @Data - public static class Sourced { // todo: refactor class to make value generic - private Object value; + public static class Sourced { // todo: refactor class to make value generic + private T value; private String source; } @@ -37,10 +37,10 @@ public static class Sourced { // todo: refactor class to make value generic * @param โ€“ actual, min and max type */ @Data - public static class Bounded { // todo: refactor class to make fields generic numbers - private final Object actual; - private final Object min; - private final Object max; + public static class Bounded { // todo: refactor class to make fields generic numbers + private final T actual; + private final T min; + private final T max; } /** @@ -50,8 +50,8 @@ public static class Bounded { // todo: refactor class to make fields generic num * @param โ€“ source object type * @param - converted result type */ - public interface Converter { // todo: make interface generic - // todo: add convert method + public interface Converter { // todo: make interface generic + R convert(T obj); } /** @@ -61,10 +61,10 @@ public interface Converter { // todo: make interface generic * * @param โ€“ value type */ - public static class MaxHolder { // todo: refactor class to make it generic - private Object max; + public static class MaxHolder> { // todo: refactor class to make it generic + private T max; - public MaxHolder(Object max) { + public MaxHolder(T max) { this.max = max; } @@ -73,11 +73,13 @@ public MaxHolder(Object max) { * * @param val a new value */ - public void put(Object val) { - throw new ExerciseNotCompletedException(); // todo: update parameter and implement the method + public void put(T val) { + if (val.compareTo(max) > 0) { + max = val; + } } - public Object getMax() { + public T getMax() { return max; } } @@ -88,8 +90,8 @@ public Object getMax() { * * @param โ€“ the type of objects that can be processed */ - interface StrictProcessor { // todo: make it generic - void process(Object obj); + interface StrictProcessor> { // todo: make it generic + void process(T obj); } /** @@ -99,10 +101,10 @@ interface StrictProcessor { // todo: make it generic * @param โ€“ a type of the entity that should be a subclass of {@link BaseEntity} * @param โ€“ a type of any collection */ - interface CollectionRepository { // todo: update interface according to the javadoc - void save(Object entity); + interface CollectionRepository> { // todo: update interface according to the javadoc + void save(T entity); - Collection getEntityCollection(); + C getEntityCollection(); } /** @@ -111,7 +113,7 @@ interface CollectionRepository { // todo: update interface according to the java * * @param โ€“ a type of the entity that should be a subclass of {@link BaseEntity} */ - interface ListRepository { // todo: update interface according to the javadoc + interface ListRepository extends CollectionRepository> { // todo: update interface according to the javadoc } } From 9facc50447260081e29fe1e830fb10b12cb04528 Mon Sep 17 00:00:00 2001 From: Taras Boychuk Date: Tue, 13 Jul 2021 18:32:54 +0400 Subject: [PATCH 49/74] GP-119 complete tasks related to unbounded wildcard parameters --- .../src/main/java/com/bobocode/basics/CrazyGenerics.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/1-0-java-basics/1-3-1-crazy-generics/src/main/java/com/bobocode/basics/CrazyGenerics.java b/1-0-java-basics/1-3-1-crazy-generics/src/main/java/com/bobocode/basics/CrazyGenerics.java index 4e0c5607..d2367049 100644 --- a/1-0-java-basics/1-3-1-crazy-generics/src/main/java/com/bobocode/basics/CrazyGenerics.java +++ b/1-0-java-basics/1-3-1-crazy-generics/src/main/java/com/bobocode/basics/CrazyGenerics.java @@ -122,7 +122,7 @@ static class ConsoleUtil { * * @param list */ - void print(List list) { // todo: refactor it so the list of any type can be printed, not only integers + void print(List list) { // todo: refactor it so the list of any type can be printed, not only integers list.forEach(element -> System.out.println(" โ€“ " + element)); } } @@ -137,8 +137,12 @@ void print(List list) { // todo: refactor it so the list of any type ca * * @param a type of collection elements */ - interface ComparableCollection { // todo: refactor it to make generic and provide a default impl of compareTo + interface ComparableCollection extends Collection, Comparable> { // todo: refactor it to make generic and provide a default impl of compareTo + @Override + default int compareTo(Collection o) { + return Integer.compare(this.size(), o.size()); + } } From 34965a2203507505bcd9bab585362b2d92cbd848 Mon Sep 17 00:00:00 2001 From: Taras Boychuk Date: Fri, 30 Jul 2021 17:25:46 +0300 Subject: [PATCH 50/74] GP-119 complete new tasks related to generic methods --- .../com/bobocode/basics/CrazyGenerics.java | 25 +++++++++++++------ 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/1-0-java-basics/1-3-1-crazy-generics/src/main/java/com/bobocode/basics/CrazyGenerics.java b/1-0-java-basics/1-3-1-crazy-generics/src/main/java/com/bobocode/basics/CrazyGenerics.java index 25807cf5..6a78a494 100644 --- a/1-0-java-basics/1-3-1-crazy-generics/src/main/java/com/bobocode/basics/CrazyGenerics.java +++ b/1-0-java-basics/1-3-1-crazy-generics/src/main/java/com/bobocode/basics/CrazyGenerics.java @@ -5,7 +5,9 @@ import java.io.Serializable; import java.util.Collection; +import java.util.Comparator; import java.util.List; +import java.util.function.Predicate; /** * {@link CrazyGenerics} is an exercise class. It consists of classes, interfaces and methods that should be updated @@ -157,8 +159,9 @@ static class PersistenceUtil { * @param entities provided collection of entities * @return true if at least one of the elements has null id */ - public static boolean hasNewEntities(Collection entities) { - throw new ExerciseNotCompletedException(); // todo: param and implement method + public static boolean hasNewEntities(Collection entities) { + return entities.stream() + .anyMatch(e -> e.getUuid() == null); } /** @@ -169,8 +172,10 @@ public static boolean hasNewEntities(Collection entities) { * @param validationPredicate criteria for validation * @return true if all entities fit validation criteria */ - public static boolean isValidCollection() { - throw new ExerciseNotCompletedException(); // todo: add method parameters and implement the logic + public static boolean isValidCollection(Collection entities, + Predicate validationPredicate) { + return entities.stream() + .allMatch(validationPredicate); } /** @@ -183,8 +188,10 @@ public static boolean isValidCollection() { * @param entity type * @return true if entities list contains target entity more than once */ - public static boolean hasDuplicates() { - throw new ExerciseNotCompletedException(); // todo: update method signature and implement it + public static boolean hasDuplicates(Collection entities, T targetEntity) { + return entities.stream() + .filter(e -> e.getUuid().equals(targetEntity.getUuid())) + .count() > 1; } /** @@ -195,7 +202,11 @@ public static boolean hasDuplicates() { * @param entity type * @return an entity from the given collection that has the max createdOn value */ - // todo: create a method according to the JavaDoc + public static T findMostRecentlyCreatedEntity(Collection entities) { + return entities.stream() + .max(Comparator.comparing(BaseEntity::getCreatedOn)) + .orElseThrow(); + } } From 10bd3dfe441a384c06b5cae20aa95c889e0f4669 Mon Sep 17 00:00:00 2001 From: Taras Boychuk Date: Tue, 3 Aug 2021 13:15:56 +0300 Subject: [PATCH 51/74] GP-119 complete new method and fix conflicts after main updates --- .../com/bobocode/basics/CrazyGenerics.java | 38 ++++++++++++++----- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/1-0-java-basics/1-3-1-crazy-generics/src/main/java/com/bobocode/basics/CrazyGenerics.java b/1-0-java-basics/1-3-1-crazy-generics/src/main/java/com/bobocode/basics/CrazyGenerics.java index 61ec57ff..24ff1553 100644 --- a/1-0-java-basics/1-3-1-crazy-generics/src/main/java/com/bobocode/basics/CrazyGenerics.java +++ b/1-0-java-basics/1-3-1-crazy-generics/src/main/java/com/bobocode/basics/CrazyGenerics.java @@ -1,14 +1,10 @@ package com.bobocode.basics; import com.bobocode.basics.util.BaseEntity; -import com.bobocode.util.ExerciseNotCompletedException; import lombok.Data; import java.io.Serializable; -import java.util.Collection; -import java.util.Comparator; -import java.util.List; -import java.util.Objects; +import java.util.*; import java.util.function.Predicate; /** @@ -146,7 +142,7 @@ static class CollectionUtil { * * @param list */ - public static void print(List list) { + public static void print(List list) { list.forEach(element -> System.out.println(" โ€“ " + element)); } @@ -171,7 +167,7 @@ public static boolean hasNewEntities(Collection entities) * @param validationPredicate criteria for validation * @return true if all entities fit validation criteria */ - public static boolean isValidCollection(Collection entities, + public static boolean isValidCollection(Collection entities, Predicate validationPredicate) { return entities.stream() .allMatch(validationPredicate); @@ -202,7 +198,20 @@ public static boolean hasDuplicates(Collection entitie * @param type of elements * @return optional max value */ - // todo: create a method and implement its logic manually without using util method from JDK + public static Optional findMax(Iterable elements, Comparator comparator) { + var iterator = elements.iterator(); + if (!iterator.hasNext()) { + return Optional.empty(); + } + var max = iterator.next(); + while (iterator.hasNext()) { + var element = iterator.next(); + if (comparator.compare(element, max) > 0) { + max = element; + } + } + return Optional.of(max); + } /** * findMostRecentlyCreatedEntity is a generic util method that accepts a collection of entities and returns the @@ -216,7 +225,10 @@ public static boolean hasDuplicates(Collection entitie * @param entity type * @return an entity from the given collection that has the max createdOn value */ - // todo: create a method according to JavaDoc and implement it using previous method + public static T findMostRecentlyCreatedEntity(Collection entities) { + return findMax(entities, CREATED_ON_COMPARATOR) + .orElseThrow(); + } /** * An util method that allows to swap two elements of any list. It changes the list so the element with the index @@ -230,7 +242,13 @@ public static boolean hasDuplicates(Collection entitie public static void swap(List elements, int i, int j) { Objects.checkIndex(i, elements.size()); Objects.checkIndex(j, elements.size()); - throw new ExerciseNotCompletedException(); // todo: complete method implementation + swapHelper(elements, i, j); + } + + private static void swapHelper(List elements, int i, int j) { + T temp = elements.get(i); + elements.set(i, elements.get(j)); + elements.set(j, temp); } } } From 2e0d7f8b295a2f56767f4ccb4e479db5e14be325 Mon Sep 17 00:00:00 2001 From: Taras Boychuk Date: Mon, 16 Aug 2021 17:11:57 +0300 Subject: [PATCH 52/74] Complete hello-annotations exercise --- .../main/java/com/bobocode/basics/Exercise.java | 14 ++++++++++++++ .../bobocode/basics/HelloAnnotationsExercise.java | 1 + 2 files changed, 15 insertions(+) create mode 100644 1-0-java-basics/1-5-0-hello-annotations/src/main/java/com/bobocode/basics/Exercise.java diff --git a/1-0-java-basics/1-5-0-hello-annotations/src/main/java/com/bobocode/basics/Exercise.java b/1-0-java-basics/1-5-0-hello-annotations/src/main/java/com/bobocode/basics/Exercise.java new file mode 100644 index 00000000..70066cbf --- /dev/null +++ b/1-0-java-basics/1-5-0-hello-annotations/src/main/java/com/bobocode/basics/Exercise.java @@ -0,0 +1,14 @@ +package com.bobocode.basics; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface Exercise { + String value(); + + Level complexityLevel() default Level.BASIC; +} diff --git a/1-0-java-basics/1-5-0-hello-annotations/src/main/java/com/bobocode/basics/HelloAnnotationsExercise.java b/1-0-java-basics/1-5-0-hello-annotations/src/main/java/com/bobocode/basics/HelloAnnotationsExercise.java index 3dcf1f70..1a192c91 100644 --- a/1-0-java-basics/1-5-0-hello-annotations/src/main/java/com/bobocode/basics/HelloAnnotationsExercise.java +++ b/1-0-java-basics/1-5-0-hello-annotations/src/main/java/com/bobocode/basics/HelloAnnotationsExercise.java @@ -9,5 +9,6 @@ * todo: Add String value that will store exercise name * todo: Add complexityLevel with a default {@link Level} basic */ +@Exercise("hello-annotation-basic") public class HelloAnnotationsExercise { // todo: mark class with the annotation according to the javadoc } From 635ae420a32db632e4368ee703dae5b489480c54 Mon Sep 17 00:00:00 2001 From: Taras Boychuk Date: Fri, 20 Aug 2021 17:33:58 +0300 Subject: [PATCH 53/74] GP-119 add impl for MaxHolder --- .../java/com/bobocode/basics/CrazyGenerics.java | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/1-0-java-basics/1-3-1-crazy-generics/src/main/java/com/bobocode/basics/CrazyGenerics.java b/1-0-java-basics/1-3-1-crazy-generics/src/main/java/com/bobocode/basics/CrazyGenerics.java index bc1385b4..f8df5031 100644 --- a/1-0-java-basics/1-3-1-crazy-generics/src/main/java/com/bobocode/basics/CrazyGenerics.java +++ b/1-0-java-basics/1-3-1-crazy-generics/src/main/java/com/bobocode/basics/CrazyGenerics.java @@ -61,10 +61,10 @@ public interface Converter { * * @param โ€“ value type */ - public static class MaxHolder { // todo: refactor class to make it generic - private Object max; + public static class MaxHolder> { // todo: refactor class to make it generic + private T max; - public MaxHolder(Object max) { + public MaxHolder(T max) { this.max = max; } @@ -73,11 +73,13 @@ public MaxHolder(Object max) { * * @param val a new value */ - public void put(Object val) { - throw new ExerciseNotCompletedException(); // todo: update parameter and implement the method + public void put(T val) { + if (val.compareTo(max) > 0) { + max = val; + } } - public Object getMax() { + public T getMax() { return max; } } From bb47c02372a8c15a5910a1e91d50b246110044d7 Mon Sep 17 00:00:00 2001 From: Taras Boychuk Date: Tue, 5 Oct 2021 13:16:20 +0300 Subject: [PATCH 54/74] GP-39 fun-prime-numbers * refactor completed according to new tests --- .../java/com/bobocode/fp/PrimeNumbers.java | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/5-0-functional-programming/5-4-1-fun-prime-numbers/src/main/java/com/bobocode/fp/PrimeNumbers.java b/5-0-functional-programming/5-4-1-fun-prime-numbers/src/main/java/com/bobocode/fp/PrimeNumbers.java index fc2b3951..bf85262b 100644 --- a/5-0-functional-programming/5-4-1-fun-prime-numbers/src/main/java/com/bobocode/fp/PrimeNumbers.java +++ b/5-0-functional-programming/5-4-1-fun-prime-numbers/src/main/java/com/bobocode/fp/PrimeNumbers.java @@ -24,8 +24,7 @@ private PrimeNumbers() { * @return the sum of n prime numbers */ public static int sum(int n) { - return primeNumberStream() - .limit(n) + return primeNumberStream(n) .sum(); } @@ -35,8 +34,7 @@ public static int sum(int n) { * @return a list of collected prime numbers */ public static List collect(int n) { - return primeNumberStream() - .limit(n) + return primeNumberStream(n) .boxed() .collect(toList()); } @@ -48,19 +46,20 @@ public static List collect(int n) { * @param consumer a logic that should be applied to the found prime number */ public static void processByIndex(int idx, IntConsumer consumer) { - primeNumberStream() - .limit(idx) - .reduce((a, b) -> b) + primeNumberStream(idx + 1) + .skip(idx) + .findAny() .ifPresent(consumer); } - private static IntStream primeNumberStream() { - return IntStream.iterate(1, i -> i + 1) - .filter(PrimeNumbers::isPrime); + private static IntStream primeNumberStream(int size) { + return IntStream.iterate(2, i -> i + 1) + .filter(PrimeNumbers::isPrime) + .limit(size); } private static boolean isPrime(int n) { - return IntStream.range(2, n) + return (n != 1) && IntStream.range(2, n) .noneMatch(i -> n % i == 0); } } From c703cbb2482e2670236294e9968cf1a6dda6ade7 Mon Sep 17 00:00:00 2001 From: Taras Boychuk Date: Tue, 5 Oct 2021 13:39:26 +0300 Subject: [PATCH 55/74] GP-39 fun-prime-numbers * complete a new version of fun-prime-numbers --- .../java/com/bobocode/fp/PrimeNumbers.java | 37 +++++++++---------- 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/5-0-functional-programming/5-4-1-fun-prime-numbers/src/main/java/com/bobocode/fp/PrimeNumbers.java b/5-0-functional-programming/5-4-1-fun-prime-numbers/src/main/java/com/bobocode/fp/PrimeNumbers.java index ef746e3b..43f91361 100644 --- a/5-0-functional-programming/5-4-1-fun-prime-numbers/src/main/java/com/bobocode/fp/PrimeNumbers.java +++ b/5-0-functional-programming/5-4-1-fun-prime-numbers/src/main/java/com/bobocode/fp/PrimeNumbers.java @@ -1,12 +1,11 @@ package com.bobocode.fp; -import com.bobocode.util.ExerciseNotCompletedException; - import java.util.List; import java.util.Map; import java.util.function.IntConsumer; import java.util.stream.IntStream; +import static java.util.stream.Collectors.groupingBy; import static java.util.stream.Collectors.toList; /** @@ -26,7 +25,8 @@ private PrimeNumbers() { * @return an infinite int stream of prime numbers */ public static IntStream stream() { - throw new ExerciseNotCompletedException(); // todo: create an infinite stream of ints, then filter prime numbs + return IntStream.iterate(2, i -> i + 1) + .filter(PrimeNumbers::isPrime); } /** @@ -36,7 +36,8 @@ public static IntStream stream() { * @return an int stream of prime numbers with a specified size */ public static IntStream stream(int size) { - throw new ExerciseNotCompletedException(); // todo: use the prev to generate a stream method but limit its size + return stream() + .limit(size); } /** @@ -47,7 +48,7 @@ public static IntStream stream(int size) { * @return the sum of n prime numbers */ public static int sum(int n) { - return primeNumberStream(n) + return stream(n) .sum(); } @@ -56,8 +57,8 @@ public static int sum(int n) { * * @return a list of collected prime numbers */ - public static List collect(int n) { - return primeNumberStream(n) + public static List list(int n) { + return stream(n) .boxed() .collect(toList()); } @@ -69,23 +70,12 @@ public static List collect(int n) { * @param consumer a logic that should be applied to the found prime number */ public static void processByIndex(int idx, IntConsumer consumer) { - primeNumberStream(idx + 1) + stream(idx + 1) .skip(idx) .findAny() .ifPresent(consumer); } - private static IntStream primeNumberStream(int size) { - return IntStream.iterate(2, i -> i + 1) - .filter(PrimeNumbers::isPrime) - .limit(size); - } - - private static boolean isPrime(int n) { - return (n != 1) && IntStream.range(2, n) - .noneMatch(i -> n % i == 0); - } - /** * Creates a list of n prime numbers and returns a map where all of those prime numbers are groped. The key represents * an amount of digits and the value is a corresponding list of all prime numbers. @@ -97,6 +87,13 @@ private static boolean isPrime(int n) { * @return a map with prime number grouped by the amount of digits */ public static Map> groupByAmountOfDigits(int n) { - throw new ExerciseNotCompletedException(); // todo: group n prime numbers by the amount of digits + return stream(n) + .boxed() + .collect(groupingBy(x -> (int) (Math.log10(x) + 1))); + } + + private static boolean isPrime(int n) { + return (n != 1) && IntStream.range(2, n) + .noneMatch(i -> n % i == 0); } } From fe3ff96d6a88d8372c31a6266814367834150b80 Mon Sep 17 00:00:00 2001 From: Taras Boychuk Date: Fri, 5 Nov 2021 15:35:56 +0200 Subject: [PATCH 56/74] Issue-112 complete solution --- .../main/java/com/bobocode/se/CrazyRegex.java | 52 ++++++++++--------- 1 file changed, 28 insertions(+), 24 deletions(-) diff --git a/3-0-java-core/3-6-3-crazy-regex/src/main/java/com/bobocode/se/CrazyRegex.java b/3-0-java-core/3-6-3-crazy-regex/src/main/java/com/bobocode/se/CrazyRegex.java index e213d3f2..b6e1793c 100644 --- a/3-0-java-core/3-6-3-crazy-regex/src/main/java/com/bobocode/se/CrazyRegex.java +++ b/3-0-java-core/3-6-3-crazy-regex/src/main/java/com/bobocode/se/CrazyRegex.java @@ -2,6 +2,7 @@ import com.bobocode.util.ExerciseNotCompletedException; +import java.util.regex.Matcher; import java.util.regex.Pattern; /** @@ -21,7 +22,7 @@ public class CrazyRegex { * @return a pattern that looks for the word "Curiosity" */ public Pattern findSpecificWord() { - throw new ExerciseNotCompletedException(); + return Pattern.compile("Curiosity"); } /** @@ -30,7 +31,7 @@ public Pattern findSpecificWord() { * @return a pattern that looks for the first word in text */ public Pattern findFirstWord() { - throw new ExerciseNotCompletedException(); + return Pattern.compile("^\\w+"); } /** @@ -39,7 +40,7 @@ public Pattern findFirstWord() { * @return a pattern that looks for the last word in text */ public Pattern findLastWord() { - throw new ExerciseNotCompletedException(); + return Pattern.compile("\\w+$"); } /** @@ -50,7 +51,7 @@ public Pattern findLastWord() { * @return a pattern that looks for numbers */ public Pattern findAllNumbers() { - throw new ExerciseNotCompletedException(); + return Pattern.compile("\\d+"); } /** @@ -59,7 +60,7 @@ public Pattern findAllNumbers() { * @return a pattern that looks for dates */ public Pattern findDates() { - throw new ExerciseNotCompletedException(); + return Pattern.compile("\\d{4}-\\d{2}-\\d{2}"); } /** @@ -69,7 +70,7 @@ public Pattern findDates() { * @return a pattern that looks for different variations of word "color" */ public Pattern findDifferentSpellingsOfColor() { - throw new ExerciseNotCompletedException(); + return Pattern.compile("colou?rs?"); } /** @@ -80,7 +81,7 @@ public Pattern findDifferentSpellingsOfColor() { * @return a pattern that looks for zip codes */ public Pattern findZipCodes() { - throw new ExerciseNotCompletedException(); + return Pattern.compile("\\s\\d{5}\\s"); } /** @@ -90,7 +91,7 @@ public Pattern findZipCodes() { * @return a pattern that looks for different variations of word "link" */ public Pattern findDifferentSpellingsOfLink() { - throw new ExerciseNotCompletedException(); + return Pattern.compile("l[yi (]nk"); } /** @@ -100,7 +101,7 @@ public Pattern findDifferentSpellingsOfLink() { * @return a pattern that looks for phone numbers */ public Pattern findSimplePhoneNumber() { - throw new ExerciseNotCompletedException(); + return Pattern.compile("\\d\\d\\d-\\d\\d\\d-\\d\\d\\d\\d"); } /** @@ -111,7 +112,7 @@ public Pattern findSimplePhoneNumber() { * @return a pattern that looks for numbers with length 3 and digits from 0 to 5 in the middle */ public Pattern findNumbersFromZeroToFiveWithLengthThree() { - throw new ExerciseNotCompletedException(); + return Pattern.compile("[0-5]{3}"); } /** @@ -120,7 +121,7 @@ public Pattern findNumbersFromZeroToFiveWithLengthThree() { * @return a pattern that looks for the words that have length 5 */ public Pattern findAllWordsWithFiveLength() { - throw new ExerciseNotCompletedException(); + return Pattern.compile("\\b[A-Za-z]{5}\\b"); } /** @@ -131,7 +132,7 @@ public Pattern findAllWordsWithFiveLength() { * @return a pattern that looks for words and numbers that not shorter 2 and not longer 3 */ public Pattern findAllLettersAndDigitsWithLengthThree() { - throw new ExerciseNotCompletedException(); + return Pattern.compile("\\b\\w{2,3}\\b"); } /** @@ -140,7 +141,7 @@ public Pattern findAllLettersAndDigitsWithLengthThree() { * @return a pattern that looks for the words that begin with capital letter */ public Pattern findAllWordsWhichBeginWithCapitalLetter() { - throw new ExerciseNotCompletedException(); + return Pattern.compile("\\b[A-Z][a-z]*\\b"); } /** @@ -150,7 +151,7 @@ public Pattern findAllWordsWhichBeginWithCapitalLetter() { * @return a pattern that looks for the abbreviations above */ public Pattern findAbbreviation() { - throw new ExerciseNotCompletedException(); + return Pattern.compile("A[KLRZ]|C[AOT]|P[RAD]"); } /** @@ -159,7 +160,7 @@ public Pattern findAbbreviation() { * @return a pattern that looks for all open braces */ public Pattern findAllOpenBraces() { - throw new ExerciseNotCompletedException(); + return Pattern.compile("(\\{+)"); } /** @@ -168,7 +169,7 @@ public Pattern findAllOpenBraces() { * @return a pattern that looks for everything inside [] */ public Pattern findOnlyResources() { - throw new ExerciseNotCompletedException(); + return Pattern.compile("(?<=\\[).+?(?=\\])"); } /** @@ -177,7 +178,7 @@ public Pattern findOnlyResources() { * @return a pattern that looks for all https links in note.txt */ public Pattern findOnlyLinksInNote() { - throw new ExerciseNotCompletedException(); + return Pattern.compile("https://((www.)?+[\\w]+(.))com"); } /** @@ -186,7 +187,7 @@ public Pattern findOnlyLinksInNote() { * @return a pattern that looks for all http links in nasa.json */ public Pattern findOnlyLinksInJson() { - throw new ExerciseNotCompletedException(); + return Pattern.compile("http://(.*)JPG"); } /** @@ -195,7 +196,7 @@ public Pattern findOnlyLinksInJson() { * @return a pattern that looks for all .com, .net and .edu emails */ public Pattern findAllEmails() { - throw new ExerciseNotCompletedException(); + return Pattern.compile("[\\w.]+@[\\w]+\\.(net|com|edu)"); } /** @@ -207,7 +208,7 @@ public Pattern findAllEmails() { * @return a pattern that looks for phone numbers patterns above */ public Pattern findAllPatternsForPhoneNumbers() { - throw new ExerciseNotCompletedException(); + return Pattern.compile("\\(?\\d{3}[-.)]\\d{3}[-.]\\d{4}"); } /** @@ -216,7 +217,7 @@ public Pattern findAllPatternsForPhoneNumbers() { * @return a pattern that looks for duplicates */ public Pattern findOnlyDuplicates() { - throw new ExerciseNotCompletedException(); + return Pattern.compile("\\b(\\w+)\\s\\1\\b"); } /** @@ -227,7 +228,8 @@ public Pattern findOnlyDuplicates() { * @return String where all names recorded as last name first name */ public String replaceFirstAndLastNames(String names) { - throw new ExerciseNotCompletedException(); + Matcher matcher = Pattern.compile("(\\w+),\\s+(\\w+)").matcher(names); + return matcher.replaceAll("$2 $1"); } /** @@ -238,7 +240,8 @@ public String replaceFirstAndLastNames(String names) { * @return String where in all phone numbers last 7 digits replaced to X */ public String replaceLastSevenDigitsOfPhoneNumberToX(String phones) { - throw new ExerciseNotCompletedException(); + Matcher matcher = Pattern.compile("\\(?(\\d{3})[-.)]\\d{3}[-.]\\d{4}").matcher(phones); + return matcher.replaceAll("$1-XXX-XXXX"); } /** @@ -250,6 +253,7 @@ public String replaceLastSevenDigitsOfPhoneNumberToX(String phones) { * @return String where all resources embraced in href */ public String insertLinksAndResourcesIntoHref(String links) { - throw new ExerciseNotCompletedException(); + Matcher matcher = Pattern.compile("\\[(.*?)]\\((http.*?)\\)").matcher(links); + return matcher.replaceAll("$1"); } } From 4bb6d0f8d0b1e7892a87b82364c0ef4bf2e9bc03 Mon Sep 17 00:00:00 2001 From: Taras Boychuk Date: Fri, 12 Nov 2021 14:39:18 +0200 Subject: [PATCH 57/74] Update hello-exercise * rename test, so it corresponds to the class name * update javadoc to make the task clearer --- .../com/bobocode/basics/HelloAnnotationsExercise.java | 8 +++++--- ...tationsTest.java => HelloAnnotationsExerciseTest.java} | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) rename 1-0-java-basics/1-5-0-hello-annotations/src/test/java/com/bobocode/basics/{HelloAnnotationsTest.java => HelloAnnotationsExerciseTest.java} (98%) diff --git a/1-0-java-basics/1-5-0-hello-annotations/src/main/java/com/bobocode/basics/HelloAnnotationsExercise.java b/1-0-java-basics/1-5-0-hello-annotations/src/main/java/com/bobocode/basics/HelloAnnotationsExercise.java index 3173dfee..268ee071 100644 --- a/1-0-java-basics/1-5-0-hello-annotations/src/main/java/com/bobocode/basics/HelloAnnotationsExercise.java +++ b/1-0-java-basics/1-5-0-hello-annotations/src/main/java/com/bobocode/basics/HelloAnnotationsExercise.java @@ -1,11 +1,13 @@ package com.bobocode.basics; /** - * {@link HelloAnnotationsExercise} is an exercise class that should be annotation with appropriate @{@link Exercise} - * annotation. With that annotation it should specify exercise name "hello-annotation-basic" and the default complexity - * level. + * {@link HelloAnnotationsExercise} is an exercise class that is marked with be corresponding @{@link Exercise} + * annotation. The annotation value specifies exercise name "hello-annotation-basic". It does not specify any custom + * complexity level, because this exercise is a basic, which correspond to the default value provided by annotation. *

* todo: Create an annotation @{@link Exercise}. + * todo: Set its retention policy so it is visible at runtime + * todo: Set its target so it can only be applied to a class * todo: Add String value that will store exercise name * todo: Add complexityLevel with a default {@link Level} basic * diff --git a/1-0-java-basics/1-5-0-hello-annotations/src/test/java/com/bobocode/basics/HelloAnnotationsTest.java b/1-0-java-basics/1-5-0-hello-annotations/src/test/java/com/bobocode/basics/HelloAnnotationsExerciseTest.java similarity index 98% rename from 1-0-java-basics/1-5-0-hello-annotations/src/test/java/com/bobocode/basics/HelloAnnotationsTest.java rename to 1-0-java-basics/1-5-0-hello-annotations/src/test/java/com/bobocode/basics/HelloAnnotationsExerciseTest.java index 359d2e36..b0d99154 100644 --- a/1-0-java-basics/1-5-0-hello-annotations/src/test/java/com/bobocode/basics/HelloAnnotationsTest.java +++ b/1-0-java-basics/1-5-0-hello-annotations/src/test/java/com/bobocode/basics/HelloAnnotationsExerciseTest.java @@ -10,7 +10,7 @@ import static org.assertj.core.api.AssertionsForClassTypes.assertThatCode; @TestMethodOrder(MethodOrderer.OrderAnnotation.class) -public class HelloAnnotationsTest { +public class HelloAnnotationsExerciseTest { @Test @Order(1) From a8b422e6ad18b2a1a45364b11c003eb84db38f10 Mon Sep 17 00:00:00 2001 From: Serhii Hryhus Date: Thu, 13 Jan 2022 13:36:27 +0200 Subject: [PATCH 58/74] #121 add answer in 5-0-2 --- .../src/main/java/com/bobocode/fp/SumOfSquares.java | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/5-0-functional-programming/5-0-2-stream-sum-of-squares/src/main/java/com/bobocode/fp/SumOfSquares.java b/5-0-functional-programming/5-0-2-stream-sum-of-squares/src/main/java/com/bobocode/fp/SumOfSquares.java index a3d7f2d9..06f8e214 100644 --- a/5-0-functional-programming/5-0-2-stream-sum-of-squares/src/main/java/com/bobocode/fp/SumOfSquares.java +++ b/5-0-functional-programming/5-0-2-stream-sum-of-squares/src/main/java/com/bobocode/fp/SumOfSquares.java @@ -27,11 +27,8 @@ static int calculateSumOfSquaresInRange(int startInclusive, int endInclusive) { throw new InvalidRangeException(); } - // todo: refactor using functional approach โ€“ instead of using for loop, use IntStream.rangeClose() - int sumOfSquares = 0; - for (int i = startInclusive; i <= endInclusive; i++) { - sumOfSquares += i * i; - } - return sumOfSquares; + return IntStream.rangeClosed(startInclusive, endInclusive) + .map(a -> a * a) + .sum(); } } From 7265777689ac1fcdb2c74029a9c8e4a7ed5bc39a Mon Sep 17 00:00:00 2001 From: "andrii.kook" Date: Wed, 4 May 2022 23:14:01 +0300 Subject: [PATCH 59/74] [2-2-4-linked-list] fix NPE when removing element by zero index from empty list --- .../main/java/com/bobocode/cs/LinkedList.java | 2 +- .../java/com/bobocode/cs/LinkedListTest.java | 25 ++++++++++++------- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/2-0-data-structures-and-algorithms/2-2-4-linked-list/src/main/java/com/bobocode/cs/LinkedList.java b/2-0-data-structures-and-algorithms/2-2-4-linked-list/src/main/java/com/bobocode/cs/LinkedList.java index 2cac1ee3..ed0505a4 100644 --- a/2-0-data-structures-and-algorithms/2-2-4-linked-list/src/main/java/com/bobocode/cs/LinkedList.java +++ b/2-0-data-structures-and-algorithms/2-2-4-linked-list/src/main/java/com/bobocode/cs/LinkedList.java @@ -169,7 +169,7 @@ private void checkElementsExist() { @Override public T remove(int index) { T deletedElement; - if (index == 0) { + if (index == 0 && !isEmpty()) { deletedElement = first.value; removeHead(); } else { diff --git a/2-0-data-structures-and-algorithms/2-2-4-linked-list/src/test/java/com/bobocode/cs/LinkedListTest.java b/2-0-data-structures-and-algorithms/2-2-4-linked-list/src/test/java/com/bobocode/cs/LinkedListTest.java index 42f228f7..9da32321 100644 --- a/2-0-data-structures-and-algorithms/2-2-4-linked-list/src/test/java/com/bobocode/cs/LinkedListTest.java +++ b/2-0-data-structures-and-algorithms/2-2-4-linked-list/src/test/java/com/bobocode/cs/LinkedListTest.java @@ -188,7 +188,7 @@ void addToHeadWhenListIsNotEmpty() { @Test @Order(11) - void addThrowsExceptionWenIndexIsNegative() { + void addThrowsExceptionWhenIndexIsNegative() { assertThatExceptionOfType(IndexOutOfBoundsException.class) .isThrownBy(() -> intList.add(-1, 66)); } @@ -423,6 +423,13 @@ void removeWhenListIsEmpty() { @Test @Order(33) + void removeByZeroIndexWhenListIsEmpty() { + assertThatExceptionOfType(IndexOutOfBoundsException.class) + .isThrownBy(() -> intList.remove(0)); + } + + @Test + @Order(34) void size() { setInternalSize(5); @@ -432,7 +439,7 @@ void size() { } @Test - @Order(34) + @Order(35) void sizeWhenListIsEmpty() { int size = getInternalSize(); @@ -440,7 +447,7 @@ void sizeWhenListIsEmpty() { } @Test - @Order(35) + @Order(36) void contains() { addInternalElements(45, 6, 3, 6); @@ -452,7 +459,7 @@ void contains() { } @Test - @Order(36) + @Order(37) void containsWhenListIsEmpty() { boolean contains = intList.contains(34); @@ -460,7 +467,7 @@ void containsWhenListIsEmpty() { } @Test - @Order(37) + @Order(38) void isEmpty() { addInternalElements(34, 5, 6); @@ -470,7 +477,7 @@ void isEmpty() { } @Test - @Order(38) + @Order(39) void isEmptyWhenListIsEmpty() { boolean empty = intList.isEmpty(); @@ -478,7 +485,7 @@ void isEmptyWhenListIsEmpty() { } @Test - @Order(39) + @Order(40) void clearWhenListIsEmpty() { intList.clear(); @@ -488,7 +495,7 @@ void clearWhenListIsEmpty() { } @Test - @Order(40) + @Order(41) void clearChangesSize() { addInternalElements(4, 5, 6); @@ -500,7 +507,7 @@ void clearChangesSize() { } @Test - @Order(41) + @Order(42) void clearRemovesAllElements() { addInternalElements(4, 5, 6); From dd40752bb5ed6d3955106db512fe54bf05903176 Mon Sep 17 00:00:00 2001 From: Taras Boychuk Date: Fri, 17 Jun 2022 16:01:53 +0300 Subject: [PATCH 60/74] GP-145 Complete Hash Table Exercise * complete methods in HashTable --- .../main/java/com/bobocode/cs/HashTable.java | 154 ++++++++++++++++-- 1 file changed, 141 insertions(+), 13 deletions(-) diff --git a/2-0-data-structures-and-algorithms/2-2-7-hash-table/src/main/java/com/bobocode/cs/HashTable.java b/2-0-data-structures-and-algorithms/2-2-7-hash-table/src/main/java/com/bobocode/cs/HashTable.java index 431f2443..0137f62e 100644 --- a/2-0-data-structures-and-algorithms/2-2-7-hash-table/src/main/java/com/bobocode/cs/HashTable.java +++ b/2-0-data-structures-and-algorithms/2-2-7-hash-table/src/main/java/com/bobocode/cs/HashTable.java @@ -1,6 +1,8 @@ package com.bobocode.cs; -import com.bobocode.util.ExerciseNotCompletedException; +import lombok.ToString; + +import static java.util.Objects.requireNonNull; /** * {@link HashTable} is a simple Hashtable-based implementation of {@link Map} interface with some additional methods. @@ -24,6 +26,20 @@ * @author Taras Boychuk */ public class HashTable implements Map { + private static final int DEFAULT_CAPACITY = 8; + private static final float RESIZE_THRESHOLD = 1.0f; + private Node[] table; + private int size; + + @SuppressWarnings("unchecked") + public HashTable(int initialCapacity) { + verifyCapacity(initialCapacity); + this.table = new Node[initialCapacity]; + } + + public HashTable() { + this(DEFAULT_CAPACITY); + } /** * This method is a critical part of the hast table. The main idea is that having a key, you can calculate its index @@ -40,7 +56,8 @@ public class HashTable implements Map { * @return array index of the given key */ public static int calculateIndex(Object key, int tableCapacity) { - throw new ExerciseNotCompletedException(); // todo: + var hash = key.hashCode() ^ (key.hashCode() >> 16); + return hash & (tableCapacity - 1); } /** @@ -51,7 +68,40 @@ public static int calculateIndex(Object key, int tableCapacity) { */ @Override public V put(K element, V value) { - throw new ExerciseNotCompletedException(); // todo: + resizeIfNeeded(); + return putOnTable(table, element, value); + } + + private void resizeIfNeeded() { + if (size / (float) table.length > RESIZE_THRESHOLD) { + resizeTable(2 * table.length); + } + } + + private V putOnTable(Node[] table, K key, V value) { + var newNode = new Node<>(requireNonNull(key), requireNonNull(value)); + var index = calculateIndex(key, table.length); + if (table[index] == null) { // add new head key + table[index] = newNode; + } else { + var current = table[index]; + while (current.next != null) { // iterate linked list to new key + if (current.key.equals(key)) { + var prevValue = current.value; + current.value = value; + return prevValue; + } + current = current.next; + } + if (current.key.equals(key)) { + var prevValue = current.value; + current.value = value; + return prevValue; + } + current.next = newNode; // attach new key to the end of the list + } + size++; + return null; } /** @@ -63,7 +113,15 @@ public V put(K element, V value) { */ @Override public V get(K key) { - throw new ExerciseNotCompletedException(); // todo: + var index = calculateIndex(requireNonNull(key), table.length); + var current = table[index]; + while (current != null) { + if (current.key.equals(key)) { + return current.value; + } + current = current.next; + } + return null; } /** @@ -74,7 +132,7 @@ public V get(K key) { */ @Override public boolean containsKey(K key) { - throw new ExerciseNotCompletedException(); // todo: + return get(key) != null; } /** @@ -85,9 +143,19 @@ public boolean containsKey(K key) { */ @Override public boolean containsValue(V value) { - throw new ExerciseNotCompletedException(); // todo: + for (var head : table) { + var current = head; + while (current != null) { + if (current.value.equals(value)) { + return true; + } + current = current.next; + } + } + return false; } + /** * Return a number of elements in the table. * @@ -95,7 +163,7 @@ public boolean containsValue(V value) { */ @Override public int size() { - throw new ExerciseNotCompletedException(); // todo: + return size; } /** @@ -105,7 +173,7 @@ public int size() { */ @Override public boolean isEmpty() { - throw new ExerciseNotCompletedException(); // todo: + return size == 0; } /** @@ -116,7 +184,24 @@ public boolean isEmpty() { */ @Override public V remove(K key) { - throw new ExerciseNotCompletedException(); // todo: + var index = calculateIndex(requireNonNull(key), table.length); + var current = table[index]; + if (current != null) { + if (current.key.equals(key)) { + var value = current.value; + table[index] = current.next; + return value; + } + while (current.next != null) { + if (current.next.key.equals(key)) { + var value = current.next.value; + current.next = current.next.next; + return value; + } + current = current.next; + } + } + return null; } /** @@ -142,7 +227,22 @@ public V remove(K key) { */ @Override public String toString() { - throw new ExerciseNotCompletedException(); // todo: + var stringBuilder = new StringBuilder(); + var n = table.length; + for (int i = 0; i < n; i++) { // iterate array + stringBuilder.append(i).append(": "); + var current = table[i]; + if (current != null) { + while (current.next != null) { // iterate each linked list + stringBuilder.append(current.key).append("=").append(current.value).append(" -> "); + current = current.next; + } + stringBuilder.append(current.key).append("=").append(current.value).append("\n"); + } else { + stringBuilder.append("\n"); + } + } + return stringBuilder.toString(); } /** @@ -152,13 +252,41 @@ public String toString() { * (You can imagine a hash table, with a default capacity of 8 that stores hundreds of thousands of elements. * In that case it's just 8 huge linked lists. That's why we need this method.) *

- * PLEASE NOTE that such method should not be a part of the public API, but it was made public - * for learning purposes. You can create a table, print it using toString, then resizeTable and print it again. + * PLEASE NOTE that such method should be a part of the implementation details, but it was made public for learning + * purposes. You can create a table, print it using toString, then resizeTable and print it again. * It will help you to understand how it works. * * @param newCapacity a size of the new underlying array */ public void resizeTable(int newCapacity) { - throw new ExerciseNotCompletedException(); // todo: + verifyCapacity(newCapacity); + @SuppressWarnings("unchecked") Node[] newTable = new Node[newCapacity]; + for (var head : table) { + var current = head; + while (current != null) { + putOnTable(newTable, current.key, current.value); + current = current.next; + } + } + table = newTable; + } + + private void verifyCapacity(int capacity) { + if (capacity <= 0) { + throw new IllegalArgumentException("Capacity (table array size) must be positive"); + } } + + @ToString(exclude = "next") + public static class Node { + T key; + V value; + Node next; + + public Node(T key, V value) { + this.key = key; + this.value = value; + } + } + } From 7770514d10cdcfeec957ee1db12c79f89c27b843 Mon Sep 17 00:00:00 2001 From: "andrii.kook" Date: Wed, 4 May 2022 23:21:59 +0300 Subject: [PATCH 61/74] [2-2-4-linked-list] add test - removing by zero index from empty list --- .../java/com/bobocode/cs/LinkedListTest.java | 25 ++++++++++++------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/2-0-data-structures-and-algorithms/2-2-4-linked-list/src/test/java/com/bobocode/cs/LinkedListTest.java b/2-0-data-structures-and-algorithms/2-2-4-linked-list/src/test/java/com/bobocode/cs/LinkedListTest.java index 42f228f7..9da32321 100644 --- a/2-0-data-structures-and-algorithms/2-2-4-linked-list/src/test/java/com/bobocode/cs/LinkedListTest.java +++ b/2-0-data-structures-and-algorithms/2-2-4-linked-list/src/test/java/com/bobocode/cs/LinkedListTest.java @@ -188,7 +188,7 @@ void addToHeadWhenListIsNotEmpty() { @Test @Order(11) - void addThrowsExceptionWenIndexIsNegative() { + void addThrowsExceptionWhenIndexIsNegative() { assertThatExceptionOfType(IndexOutOfBoundsException.class) .isThrownBy(() -> intList.add(-1, 66)); } @@ -423,6 +423,13 @@ void removeWhenListIsEmpty() { @Test @Order(33) + void removeByZeroIndexWhenListIsEmpty() { + assertThatExceptionOfType(IndexOutOfBoundsException.class) + .isThrownBy(() -> intList.remove(0)); + } + + @Test + @Order(34) void size() { setInternalSize(5); @@ -432,7 +439,7 @@ void size() { } @Test - @Order(34) + @Order(35) void sizeWhenListIsEmpty() { int size = getInternalSize(); @@ -440,7 +447,7 @@ void sizeWhenListIsEmpty() { } @Test - @Order(35) + @Order(36) void contains() { addInternalElements(45, 6, 3, 6); @@ -452,7 +459,7 @@ void contains() { } @Test - @Order(36) + @Order(37) void containsWhenListIsEmpty() { boolean contains = intList.contains(34); @@ -460,7 +467,7 @@ void containsWhenListIsEmpty() { } @Test - @Order(37) + @Order(38) void isEmpty() { addInternalElements(34, 5, 6); @@ -470,7 +477,7 @@ void isEmpty() { } @Test - @Order(38) + @Order(39) void isEmptyWhenListIsEmpty() { boolean empty = intList.isEmpty(); @@ -478,7 +485,7 @@ void isEmptyWhenListIsEmpty() { } @Test - @Order(39) + @Order(40) void clearWhenListIsEmpty() { intList.clear(); @@ -488,7 +495,7 @@ void clearWhenListIsEmpty() { } @Test - @Order(40) + @Order(41) void clearChangesSize() { addInternalElements(4, 5, 6); @@ -500,7 +507,7 @@ void clearChangesSize() { } @Test - @Order(41) + @Order(42) void clearRemovesAllElements() { addInternalElements(4, 5, 6); From 276330765626b3ac97f5bb3efea2dcdfe2afdccf Mon Sep 17 00:00:00 2001 From: Taras Boychuk Date: Thu, 30 Jun 2022 18:36:11 +0300 Subject: [PATCH 62/74] GP-146 - complete Heterogeneous Max Holder Exercise * implement method put * overload method put * implement method getMax --- .../basics/HeterogeneousMaxHolder.java | 36 ++++++++++++++----- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/1-0-java-basics/1-3-2-heterogeneous-max-holder/src/main/java/com/bobocode/basics/HeterogeneousMaxHolder.java b/1-0-java-basics/1-3-2-heterogeneous-max-holder/src/main/java/com/bobocode/basics/HeterogeneousMaxHolder.java index f8b98b34..f6cda7be 100644 --- a/1-0-java-basics/1-3-2-heterogeneous-max-holder/src/main/java/com/bobocode/basics/HeterogeneousMaxHolder.java +++ b/1-0-java-basics/1-3-2-heterogeneous-max-holder/src/main/java/com/bobocode/basics/HeterogeneousMaxHolder.java @@ -1,7 +1,11 @@ package com.bobocode.basics; +import java.util.Comparator; +import java.util.HashMap; import java.util.Map; +import static java.util.Objects.requireNonNull; + /** * {@link HeterogeneousMaxHolder} is a multi-type container that holds maximum values per each type. It's kind of a * key/value map, where the key is a type and the value is the maximum among all values of this type that were put. @@ -9,9 +13,10 @@ * It's based on the {@link Map} and provides an API that allows to put a value by type, and get a max value by type. */ public class HeterogeneousMaxHolder { + private Map, Object> typeToMaxValueMap = new HashMap<>(); /** - * A method put stores a provided value by its type, if the value is greater than the current maximum. In other words, the logic + * Stores a provided value by its type, if the value is greater than the current maximum. In other words, the logic * of this method makes sure that only max value is stored and everything else is ignored. *

* If the current max value is less than a provided one, or if it's null, then a provided value gets stored and the old @@ -24,13 +29,13 @@ public class HeterogeneousMaxHolder { * @param value type parameter * @return a smaller value among the provided value and the current maximum */ - // todo: implement a method according to javadoc + public > T put(Class key, T value) { + return put(key, value, Comparator.naturalOrder()); + } /** - * An overloaded method put implements the same logic using a custom comparator. A given comparator is wrapped with - * a null-safe comparator, considering null smaller than any non-null object. - * - * All arguments must not be null. + * Implements a put logic using a custom comparator that is wrapped with a null-safe comparator, + * considering null smaller than any non-null object. See put method above. All arguments must not be null. * * @param key a provided value type * @param value a value to put @@ -38,14 +43,27 @@ public class HeterogeneousMaxHolder { * @param value type parameter * @return a smaller value among the provided value and the current maximum */ - // todo: implement a method according to javadoc + public T put(Class key, T value, Comparator comparator) { + var currentMax = getMax(requireNonNull(key)); + var nullSafeComparator = Comparator.nullsFirst(requireNonNull(comparator)); + requireNonNull(value); + if (nullSafeComparator.compare(value, currentMax) > 0) { + typeToMaxValueMap.put(key, value); + return currentMax; + } + return value; + } /** - * A method getMax returns a max value by the given type. If no value is stored by this type, then it returns null. + * Returns a max value by the given type. If no value is stored by this type, then it returns null. * * @param key a provided value type * @param value type parameter * @return current max value or null */ - // todo: implement a method according to javadoc + public T getMax(Class key) { + requireNonNull(key); + var currentMax = typeToMaxValueMap.get(key); + return key.cast(currentMax); + } } From 3acaf113dac5e94568cdebbc089f1fd3561d37c5 Mon Sep 17 00:00:00 2001 From: Taras Boychuk Date: Thu, 21 Jul 2022 00:13:42 +0300 Subject: [PATCH 63/74] GP-147 fix HeterogeneousMaxHolder --- .../main/java/com/bobocode/basics/HeterogeneousMaxHolder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/1-0-java-basics/1-3-2-heterogeneous-max-holder/src/main/java/com/bobocode/basics/HeterogeneousMaxHolder.java b/1-0-java-basics/1-3-2-heterogeneous-max-holder/src/main/java/com/bobocode/basics/HeterogeneousMaxHolder.java index f6cda7be..8d0e6a8c 100644 --- a/1-0-java-basics/1-3-2-heterogeneous-max-holder/src/main/java/com/bobocode/basics/HeterogeneousMaxHolder.java +++ b/1-0-java-basics/1-3-2-heterogeneous-max-holder/src/main/java/com/bobocode/basics/HeterogeneousMaxHolder.java @@ -43,7 +43,7 @@ public > T put(Class key, T value) { * @param value type parameter * @return a smaller value among the provided value and the current maximum */ - public T put(Class key, T value, Comparator comparator) { + public T put(Class key, T value, Comparator comparator) { var currentMax = getMax(requireNonNull(key)); var nullSafeComparator = Comparator.nullsFirst(requireNonNull(comparator)); requireNonNull(value); From 349b0964e0931177b770d8580a408ad6421fe74a Mon Sep 17 00:00:00 2001 From: Stanislav-Zabramnyi <110394880+Stanislav-Zabramnyi@users.noreply.github.com> Date: Wed, 3 Aug 2022 15:45:32 +0200 Subject: [PATCH 64/74] added tests for size change HashTableTest, (#134) added decrement of size when removing element --- .../main/java/com/bobocode/cs/HashTable.java | 6 +- .../java/com/bobocode/cs/HashTableTest.java | 72 +++++++++++++------ 2 files changed, 54 insertions(+), 24 deletions(-) diff --git a/2-0-data-structures-and-algorithms/2-2-7-hash-table/src/main/java/com/bobocode/cs/HashTable.java b/2-0-data-structures-and-algorithms/2-2-7-hash-table/src/main/java/com/bobocode/cs/HashTable.java index 0f91e293..f7f8663f 100644 --- a/2-0-data-structures-and-algorithms/2-2-7-hash-table/src/main/java/com/bobocode/cs/HashTable.java +++ b/2-0-data-structures-and-algorithms/2-2-7-hash-table/src/main/java/com/bobocode/cs/HashTable.java @@ -1,9 +1,9 @@ package com.bobocode.cs; -import lombok.ToString; - import static java.util.Objects.requireNonNull; +import lombok.ToString; + /** * {@link HashTable} is a simple Hashtable-based implementation of {@link Map} interface with some additional methods. * It is based on the array of {@link Node} objects. Both {@link HashTable} and {@link Node} have two type parameters: @@ -198,12 +198,14 @@ public V remove(K key) { if (current.key.equals(key)) { var value = current.value; table[index] = current.next; + size--; return value; } while (current.next != null) { if (current.next.key.equals(key)) { var value = current.next.value; current.next = current.next.next; + size--; return value; } current = current.next; diff --git a/2-0-data-structures-and-algorithms/2-2-7-hash-table/src/test/java/com/bobocode/cs/HashTableTest.java b/2-0-data-structures-and-algorithms/2-2-7-hash-table/src/test/java/com/bobocode/cs/HashTableTest.java index 8f2d5096..a952074d 100644 --- a/2-0-data-structures-and-algorithms/2-2-7-hash-table/src/test/java/com/bobocode/cs/HashTableTest.java +++ b/2-0-data-structures-and-algorithms/2-2-7-hash-table/src/test/java/com/bobocode/cs/HashTableTest.java @@ -1,9 +1,12 @@ package com.bobocode.cs; -import lombok.SneakyThrows; -import org.junit.jupiter.api.ClassOrderer.OrderAnnotation; -import org.junit.jupiter.api.*; -import org.mockito.Mockito; +import static java.lang.reflect.Modifier.isStatic; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.lang.reflect.Field; import java.util.Arrays; @@ -12,12 +15,15 @@ import java.util.concurrent.ThreadLocalRandom; import java.util.stream.Collectors; import java.util.stream.Stream; - -import static java.lang.reflect.Modifier.isStatic; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.*; +import lombok.SneakyThrows; +import org.junit.jupiter.api.ClassOrderer.OrderAnnotation; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestClassOrder; +import org.junit.jupiter.api.TestMethodOrder; /** * A Reflection-based step by step test for a {@link HashTable} class. PLEASE NOTE that Reflection API should not be used @@ -28,6 +34,7 @@ @TestClassOrder(OrderAnnotation.class) @DisplayName("HashTable Test") class HashTableTest { + private HashTable hashTable = new HashTable<>(); @Nested @@ -126,6 +133,7 @@ void nodeConstructorAcceptKeyValue() { @DisplayName("2. HashTable fields Test") @TestMethodOrder(MethodOrderer.OrderAnnotation.class) class HashTableFieldsTest { + @Test @Order(1) @DisplayName("HastTable has a field 'table' which is an array of nodes") @@ -155,6 +163,7 @@ void sizeFieldExists() { @DisplayName("3. HashTable constructors Test") @TestMethodOrder(MethodOrderer.OrderAnnotation.class) class HashTableConstructorsTest { + @Test @Order(1) @SneakyThrows @@ -198,6 +207,7 @@ void constructorWithTableCapacityWhenArgumentIsNegative() { @DisplayName("4. Hash Function Test") @TestMethodOrder(MethodOrderer.OrderAnnotation.class) class HashFunctionTest { + @Test @Order(1) @DisplayName("calculateIndex returns the same value for the same key") @@ -221,8 +231,8 @@ void calculateIndexReturnDifferentValuesWheKeysAreDifferent() { assertThat(indexSet) .hasSizeGreaterThan(1); - } - + } + @Test @Order(3) @DisplayName("calculateIndex returns values in array bounds") @@ -235,7 +245,7 @@ void calculateIndexReturnIndexInArrayBounds() { var indexes = keys.stream() .map(key -> HashTable.calculateIndex(key, arrayCapacity)) .toList(); - + assertThat(indexes) .isNotEmpty() .allMatch(i -> i >= 0 && i < arrayCapacity); @@ -262,7 +272,7 @@ class HashTableMethodsTest { @Test @SneakyThrows @Order(1) - @DisplayName("put creates new entry and returns null when the table is empty") + @DisplayName("put creates new entry and returns null when the table is empty, should increase the table size") void putWhenTableIsEmpty() { var previousValue = hashTable.put("madmax", 833); @@ -270,11 +280,12 @@ void putWhenTableIsEmpty() { assertNull(previousValue); assertTrue(keyValueExists); + assertEquals(1, getSize()); } @Test @Order(2) - @DisplayName("put elements adds entry to to the same bucket when the hash code is the same") + @DisplayName("put elements adds entry to the same bucket and increases table size when the hash code is the same") @SneakyThrows void putTwoElementsWithTheSameHashCode() { var table = getInternalTable(hashTable); @@ -290,11 +301,13 @@ void putTwoElementsWithTheSameHashCode() { assertTrue(containsKeyValueA); assertTrue(containsKeyValueB); assertThat(bucketIndexA).isEqualTo(bucketIndexB); + assertEquals(2, getSize()); } @Test @Order(3) - @DisplayName("put element updates the value and returns the previous one when key is the same") + @DisplayName( + "put element updates the value and returns the previous one when key is the same, should not increase table size") void putElementWithTheSameKey() { hashTable.put("madmax", 833); System.out.println(hashTable); @@ -305,6 +318,7 @@ void putElementWithTheSameKey() { assertThat(previousValue).isEqualTo(833); assertTrue(containsNewValueByKey); + assertEquals(1, getSize()); } @Test @@ -430,14 +444,15 @@ void isEmptyWhenThereIsNoElements() { @Test @Order(13) - @DisplayName("remove deletes the entry and returns a value") + @DisplayName("remove deletes the entry, decreases table size and returns a value") void remove() { addToTable("madmax", 833); - + setSize(1); var result = hashTable.remove("madmax"); assertThat(result).isEqualTo(833); assertFalse(checkKeyValueExists("madmaxx", 833)); + assertEquals(0, getSize()); } @Test @@ -451,27 +466,32 @@ void removeWhenKeyDoesNotExists() { @Test @Order(15) - @DisplayName("remove deletes the element when it's in the middle of the list") + @DisplayName("remove deletes the element when it's in the middle of the list and decreases the size of table") void removeFromTheMiddleOfTheList() { addToTable("AaAa", 843); addToTable("BBBB", 434); addToTable("AaBB", 587); + var size = 3; + setSize(size); var removedValue = hashTable.remove("BBBB"); assertTrue(checkKeyValueExists("AaAa", 843)); assertFalse(checkKeyExists("BBBB")); assertTrue(checkKeyValueExists("AaBB", 587)); assertThat(removedValue).isEqualTo(434); - } - + assertEquals(size - 1, getSize()); + } + @Test @Order(16) - @DisplayName("remove deletes the element when it's in the end of the list") + @DisplayName("remove deletes the element when it's in the end of the list and decreases the size of table") void removeFromTheEndOfTheList() { addToTable("AaAa", 843); addToTable("BBBB", 434); addToTable("AaBB", 587); + var size = 3; + setSize(size); var removedValue = hashTable.remove("AaBB"); @@ -479,6 +499,7 @@ void removeFromTheEndOfTheList() { assertTrue(checkKeyValueExists("BBBB", 434)); assertFalse(checkKeyExists("AaBB")); assertThat(removedValue).isEqualTo(587); + assertEquals(2, getSize()); } } @@ -585,6 +606,13 @@ private void setSize(int size) { sizeField.set(hashTable, size); } + @SneakyThrows + private int getSize() { + var sizeField = HashTable.class.getDeclaredField("size"); + sizeField.setAccessible(true); + return sizeField.getInt(hashTable); + } + private String tableToString(Object[] table) { StringBuilder result = new StringBuilder(); var n = table.length; From ee914af0655812f69a379478ea7293050bd381ee Mon Sep 17 00:00:00 2001 From: Stanislav-Zabramnyi <110394880+Stanislav-Zabramnyi@users.noreply.github.com> Date: Fri, 12 Aug 2022 10:09:55 +0200 Subject: [PATCH 65/74] Gp 155/random field comparator/completed (#137) * GP-155: add solution to random-field-comparator exercise --- .../3-6-4-random-field-comparator/README.MD | 17 ++ .../3-6-4-random-field-comparator/pom.xml | 15 ++ .../bobocode/se/RandomFieldComparator.java | 85 ++++++ .../se/RandomFieldComparatorTest.java | 253 ++++++++++++++++++ 3-0-java-core/pom.xml | 1 + 5 files changed, 371 insertions(+) create mode 100644 3-0-java-core/3-6-4-random-field-comparator/README.MD create mode 100644 3-0-java-core/3-6-4-random-field-comparator/pom.xml create mode 100644 3-0-java-core/3-6-4-random-field-comparator/src/main/java/com/bobocode/se/RandomFieldComparator.java create mode 100644 3-0-java-core/3-6-4-random-field-comparator/src/test/java/com/bobocode/se/RandomFieldComparatorTest.java diff --git a/3-0-java-core/3-6-4-random-field-comparator/README.MD b/3-0-java-core/3-6-4-random-field-comparator/README.MD new file mode 100644 index 00000000..c361e6aa --- /dev/null +++ b/3-0-java-core/3-6-4-random-field-comparator/README.MD @@ -0,0 +1,17 @@ +# Random Field Comparator +#### Improve your reflection-related skills implementing a random field comparator ๐Ÿ’ช + +### Objectives +* implement a logic of choosing a random field to use it for comparison of objects of provided type โœ… +* implement a mechanism to check if field type is `Comparable` โœ… +* implement a method `compare` that compares two objects by randomly-provided field โœ… +* extend a method `compare` to manage null field values following condition when null value grater than a non-null value โœ… +* implement method `getComparingFieldName` that retrieves the name of randomly-chosen comparing fieldโœ… +* implement method `toString` โœ… + +--- +#### ๐Ÿ†• First time here? โ€“ [See Introduction](https://github.com/bobocode-projects/java-fundamentals-exercises/tree/main/0-0-intro#introduction) +#### โžก๏ธ Have any feedback? โ€“ [Please fill the form ](https://forms.gle/u6kHcecFuzxV232LA) + +## +

\ No newline at end of file diff --git a/3-0-java-core/3-6-4-random-field-comparator/pom.xml b/3-0-java-core/3-6-4-random-field-comparator/pom.xml new file mode 100644 index 00000000..557dc1f3 --- /dev/null +++ b/3-0-java-core/3-6-4-random-field-comparator/pom.xml @@ -0,0 +1,15 @@ + + + + 3-0-java-core + com.bobocode + 1.0-SNAPSHOT + + 4.0.0 + + 3-6-4-random-field-comparator + + + \ No newline at end of file diff --git a/3-0-java-core/3-6-4-random-field-comparator/src/main/java/com/bobocode/se/RandomFieldComparator.java b/3-0-java-core/3-6-4-random-field-comparator/src/main/java/com/bobocode/se/RandomFieldComparator.java new file mode 100644 index 00000000..ee33f415 --- /dev/null +++ b/3-0-java-core/3-6-4-random-field-comparator/src/main/java/com/bobocode/se/RandomFieldComparator.java @@ -0,0 +1,85 @@ +package com.bobocode.se; + +import static java.util.Objects.requireNonNull; + +import java.lang.reflect.Field; +import java.util.Arrays; +import java.util.Comparator; +import java.util.Objects; +import lombok.SneakyThrows; + +/** + * A generic comparator that is comparing a random field of the given class. The field is either primitive or + * {@link Comparable}. It is chosen during comparator instance creation and is used for all comparisons. + *

+ * If no field is available to compare, the constructor throws {@link IllegalArgumentException} + * + * @param the type of the objects that may be compared by this comparator + *

+ * TODO: to get the most out of your learning, visit our website + *

+ * + * @author Stanislav Zabramnyi + */ +public class RandomFieldComparator implements Comparator { + + private final Class targetType; + private final Field fieldToCompare; + + public RandomFieldComparator(Class targetType) { + this.targetType = requireNonNull(targetType); + this.fieldToCompare = chooseFieldToCompare(targetType); + } + + /** + * Compares two objects of the class T by the value of the field that was randomly chosen. It allows null values + * for the fields, and it treats null value grater than a non-null value. + * + * @param o1 + * @param o2 + * @return positive int in case of first parameter {@param o1} is greater than second one {@param o2}, + * zero if objects are equals, + * negative int in case of first parameter {@param o1} is less than second one {@param o2}. + */ + @Override + public int compare(T o1, T o2) { + Objects.requireNonNull(o1); + Objects.requireNonNull(o2); + return compareFieldValues(o1, o2); + } + + /** + * Returns the name of the randomly-chosen comparing field. + */ + public String getComparingFieldName() { + return fieldToCompare.getName(); + } + + /** + * Returns a statement "Random field comparator of class '%s' is comparing '%s'" where the first param is the name + * of the type T, and the second parameter is the comparing field name. + * + * @return a predefined statement + */ + @Override + public String toString() { + return String.format("Random field comparator of class '%s' is comparing '%s'", targetType.getSimpleName(), + getComparingFieldName()); + } + + private Field chooseFieldToCompare(Class targetType) { + return Arrays.stream(targetType.getDeclaredFields()) + .filter(f -> Comparable.class.isAssignableFrom(f.getType()) || f.getType().isPrimitive()) + .findAny().orElseThrow(() -> new IllegalArgumentException("There are no fields available to compare")); + } + + @SneakyThrows + @SuppressWarnings("unchecked") + private > int compareFieldValues(T o1, T o2) { + fieldToCompare.setAccessible(true); + var value1 = (U) fieldToCompare.get(o1); + var value2 = (U) fieldToCompare.get(o2); + Comparator comparator = Comparator.nullsLast(Comparator.naturalOrder()); + return comparator.compare(value1, value2); + } +} diff --git a/3-0-java-core/3-6-4-random-field-comparator/src/test/java/com/bobocode/se/RandomFieldComparatorTest.java b/3-0-java-core/3-6-4-random-field-comparator/src/test/java/com/bobocode/se/RandomFieldComparatorTest.java new file mode 100644 index 00000000..1733568e --- /dev/null +++ b/3-0-java-core/3-6-4-random-field-comparator/src/test/java/com/bobocode/se/RandomFieldComparatorTest.java @@ -0,0 +1,253 @@ +package com.bobocode.se; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.lang.reflect.Field; +import java.util.Arrays; +import lombok.AllArgsConstructor; +import lombok.NoArgsConstructor; +import lombok.SneakyThrows; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.MethodOrderer.OrderAnnotation; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; + +@TestMethodOrder(OrderAnnotation.class) +class RandomFieldComparatorTest { + + private final RandomFieldComparator randomFieldComparator = new RandomFieldComparator<>(Account.class); + + @Test + @Order(1) + @DisplayName("Constructor throws an exception when parameter is null") + void classDoesNotApplyNullInConstructor() { + assertThrows(NullPointerException.class, () -> new RandomFieldComparator<>(null)); + } + + @Test + @Order(2) + @SneakyThrows + @DisplayName("Constructor throws an exception when the target type has no Comparable fields") + void constructorThrowsExceptionIfNoComparableFieldsInProvidedType() { + assertThrows(IllegalArgumentException.class, () -> new RandomFieldComparator<>(ClassWithNotComparableField.class)); + } + + @Test + @Order(3) + @DisplayName("Method 'compare' throws an exception when any parameter is null") + void compareWhenFirstParameterAreNull() { + + assertThrows(NullPointerException.class, () -> randomFieldComparator.compare(null, new Account())); + assertThrows(NullPointerException.class, () -> randomFieldComparator.compare(new Account(), null)); + } + + @Test + @Order(4) + @DisplayName("Method 'compare' returns 0 when field values of both objects are null") + void compareWhenBothFieldValuesIsNull() { + setFieldToCompare("lastName", Account.class); + int compareResult = randomFieldComparator.compare(new Account(), new Account()); + + assertThat(compareResult).isZero(); + } + + @Test + @Order(5) + @DisplayName("Method compare returns positive int when the first field value is null") + void compareWhenFieldValuesOfFirstObjectIsNull() { + Account emptyAccount = new Account(); + Account account = new Account("Sibma", "LoinKing", "simba-bimba@gmail.com", 14); + setFieldToCompare("email", Account.class);//set field to compare explicitly as there are int field which has default value 0 + int compareResult = randomFieldComparator.compare(emptyAccount, account); + + assertThat(compareResult).isPositive(); + } + + @Test + @Order(6) + @DisplayName("Method compare returns negative int when the second field value is null") + void compareWhenFieldValuesOfSecondObjectIsNull() { + Account account = new Account("Mufasa", "LoinKing", "simba-bimba@gmail.com", 47); + Account emptyAccount = new Account(); + setFieldToCompare("firstName", Account.class); + int compareResult = randomFieldComparator.compare(account, emptyAccount); + + assertThat(compareResult).isNegative(); + } + + @Test + @Order(7) + @SneakyThrows + @DisplayName("Method 'compare' returns positive int when the first value is greater") + void compareWhenFieldValueOfFirstObjectIsGrater() { + var fieldToCompareName = "firstName"; + Account account1 = new Account(); + Account account2 = new Account(); + Field fieldToCompareAccount = account1.getClass().getDeclaredField(fieldToCompareName); + fieldToCompareAccount.setAccessible(true); + + fieldToCompareAccount.set(account1, "Bob"); + fieldToCompareAccount.set(account2, "Alice"); + + setFieldToCompare(fieldToCompareName, Account.class); + int compareResult = randomFieldComparator.compare(account1, account2); + + assertThat(compareResult).isPositive(); + } + + @Test + @Order(8) + @SneakyThrows + @DisplayName("Method 'compare' returns negative int when the first value is smaller") + void compareWhenFieldValueOfSecondObjectIsGrater() { + var fieldToCompareName = "firstName"; + Account account1 = new Account(); + Account account2 = new Account(); + Field fieldToCompareAccount = account1.getClass().getDeclaredField(fieldToCompareName); + fieldToCompareAccount.setAccessible(true); + + fieldToCompareAccount.set(account1, "Alice"); + fieldToCompareAccount.set(account2, "Bob"); + + setFieldToCompare(fieldToCompareName, Account.class); + int compareResult = randomFieldComparator.compare(account1, account2); + + assertThat(compareResult).isNegative(); + } + + @Test + @Order(9) + @SneakyThrows + @DisplayName("Method 'compare' returns zero when the field values are equal") + void compareWhenFieldValuesOfObjectsAreEqual() { + var fieldToCompareName = "firstName"; + Account account1 = new Account(); + Account account2 = new Account(); + Field fieldToCompareAccount = account1.getClass().getDeclaredField(fieldToCompareName); + fieldToCompareAccount.setAccessible(true); + + fieldToCompareAccount.set(account1, "Carol"); + fieldToCompareAccount.set(account2, "Carol"); + + setFieldToCompare(fieldToCompareName, Account.class); + int compareResult = randomFieldComparator.compare(account1, account2); + + assertThat(compareResult).isZero(); + } + + @Test + @Order(10) + @SneakyThrows + @DisplayName("Method 'compare' returns positive int when the first primitive value is greater") + void comparePrimitivesWhenFieldValueOfFirstObjectIsGrater() { + var fieldToCompareName = "age"; + Account account1 = new Account(); + Account account2 = new Account(); + Field fieldToCompareAccount = account1.getClass().getDeclaredField(fieldToCompareName); + fieldToCompareAccount.setAccessible(true); + + fieldToCompareAccount.setInt(account1, 7); + fieldToCompareAccount.setInt(account2, 3); + + setFieldToCompare(fieldToCompareName, Account.class); + int compareResult = randomFieldComparator.compare(account1, account2); + + assertThat(compareResult).isPositive(); + } + + @Test + @Order(11) + @SneakyThrows + @DisplayName("Method 'compare' returns zero when the primitive field values are equal") + void comparePrimitivesWhenFieldValuesOfObjectsAreEqual() { + var fieldToCompareName = "age"; + Account account1 = new Account(); + Account account2 = new Account(); + Field fieldToCompareAccount = account1.getClass().getDeclaredField(fieldToCompareName); + fieldToCompareAccount.setAccessible(true); + + fieldToCompareAccount.setInt(account1, 15); + fieldToCompareAccount.setInt(account2, 15); + + setFieldToCompare(fieldToCompareName, Account.class); + int compareResult = randomFieldComparator.compare(account1, account2); + + assertThat(compareResult).isZero(); + } + + @Test + @Order(12) + @SneakyThrows + @DisplayName("Method 'compare' returns negative int when the first primitive value is smaller") + void comparePrimitivesWhenFieldValueOfSecondObjectIsGrater() { + var fieldToCompareName = "age"; + Account account1 = new Account(); + Account account2 = new Account(); + Field fieldToCompareAccount = account1.getClass().getDeclaredField(fieldToCompareName); + fieldToCompareAccount.setAccessible(true); + + fieldToCompareAccount.setInt(account1, 4); + fieldToCompareAccount.setInt(account2, 8); + + setFieldToCompare(fieldToCompareName, Account.class); + int compareResult = randomFieldComparator.compare(account1, account2); + + assertThat(compareResult).isNegative(); + } + + @Test + @Order(13) + @SneakyThrows + @DisplayName("Method 'getComparingFieldName' returns the name of randomly-chosen field to be compared") + void getComparingFieldName() { + var fieldToCompareName = "lastName"; + setFieldToCompare(fieldToCompareName, Account.class); + + assertEquals(fieldToCompareName, randomFieldComparator.getComparingFieldName()); + } + + @Test + @Order(14) + @SneakyThrows + @DisplayName("Method toString is properly overridden") + void toStringOverriding() { + var expectedString = "Random field comparator of class 'Account' is comparing 'email'"; + var fieldToCompareName = "email"; + setFieldToCompare(fieldToCompareName, Account.class); + + assertEquals(expectedString, randomFieldComparator.toString()); + } + + @SneakyThrows + private void setFieldToCompare(String fieldName, Class classType) { + Field fieldToCompare = Arrays.stream(randomFieldComparator.getClass().getDeclaredFields()) + .filter(f -> f.getType().equals(Field.class)) + .findAny() + .orElseThrow(); + fieldToCompare.setAccessible(true); + fieldToCompare.set(randomFieldComparator, classType.getDeclaredField(fieldName)); + } + + private static class EmptyClass { + + } + + @AllArgsConstructor + private static class ClassWithNotComparableField { + + private Object field; + } + + @NoArgsConstructor + @AllArgsConstructor + private static class Account { + + private String firstName; + private String lastName; + private String email; + private int age; + } +} \ No newline at end of file diff --git a/3-0-java-core/pom.xml b/3-0-java-core/pom.xml index 792d82a8..917e0cf1 100644 --- a/3-0-java-core/pom.xml +++ b/3-0-java-core/pom.xml @@ -14,6 +14,7 @@ 3-6-1-file-reader 3-6-2-file-stats 3-6-3-crazy-regex + 3-6-4-random-field-comparator From eb858fb037d53ca2b7fac148d1c0001dca659cc7 Mon Sep 17 00:00:00 2001 From: Dmytro Yakovenko Date: Wed, 26 Oct 2022 19:50:36 +0300 Subject: [PATCH 66/74] fix test add check for tail value (#149) --- .../src/test/java/com/bobocode/cs/LinkedListTest.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/2-0-data-structures-and-algorithms/2-2-4-linked-list/src/test/java/com/bobocode/cs/LinkedListTest.java b/2-0-data-structures-and-algorithms/2-2-4-linked-list/src/test/java/com/bobocode/cs/LinkedListTest.java index 9da32321..3a83dc79 100644 --- a/2-0-data-structures-and-algorithms/2-2-4-linked-list/src/test/java/com/bobocode/cs/LinkedListTest.java +++ b/2-0-data-structures-and-algorithms/2-2-4-linked-list/src/test/java/com/bobocode/cs/LinkedListTest.java @@ -407,10 +407,12 @@ void removeLast() { int deletedElement = intList.remove(getInternalSize() - 1); int newLastElement = getInternalElement(getInternalSize() - 1); + int tailElement = (int) getNodeValue(TAIL_NODE_FIELD); int size = getInternalSize(); assertThat(deletedElement).isEqualTo(9); assertThat(newLastElement).isEqualTo(8); + assertThat(tailElement).isEqualTo(8); assertThat(size).isEqualTo(3); } @@ -600,4 +602,12 @@ private Field getAccessibleFieldByPredicate(Object object, Predicate pred field.setAccessible(true); return field; } + + @SneakyThrows + private Object getNodeValue(Predicate predicate) { + Object field = getAccessibleFieldByPredicate(intList, predicate).get(intList); + final Field value = getAccessibleFieldByPredicate(field, ELEMENT_FIELD); + value.setAccessible(true); + return value.get(field); + } } \ No newline at end of file From 691458ce8399985e91047e56129421c4fc30dfcb Mon Sep 17 00:00:00 2001 From: Taras Boychuk Date: Wed, 30 Nov 2022 14:19:27 +0200 Subject: [PATCH 67/74] GP-119 fix tests to follow PECS --- .../src/main/java/com/bobocode/basics/CrazyGenerics.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/1-0-java-basics/1-3-1-crazy-generics/src/main/java/com/bobocode/basics/CrazyGenerics.java b/1-0-java-basics/1-3-1-crazy-generics/src/main/java/com/bobocode/basics/CrazyGenerics.java index c7ca460d..8faec080 100644 --- a/1-0-java-basics/1-3-1-crazy-generics/src/main/java/com/bobocode/basics/CrazyGenerics.java +++ b/1-0-java-basics/1-3-1-crazy-generics/src/main/java/com/bobocode/basics/CrazyGenerics.java @@ -2,6 +2,7 @@ import com.bobocode.basics.util.BaseEntity; import lombok.Data; +import lombok.val; import java.io.Serializable; import java.util.*; @@ -66,7 +67,7 @@ public interface Converter { * * @param โ€“ value type */ - public static class MaxHolder> { // todo: refactor class to make it generic + public static class MaxHolder> { // todo: refactor class to make it generic private T max; public MaxHolder(T max) { @@ -95,7 +96,7 @@ public T getMax() { * * @param โ€“ the type of objects that can be processed */ - interface StrictProcessor> { // todo: make it generic + interface StrictProcessor> { // todo: make it generic void process(T obj); } From 4fd58b2a4b953b3c2bda30805e705a0cc45db2b5 Mon Sep 17 00:00:00 2001 From: Taras Boychuk Date: Sat, 10 Dec 2022 18:51:14 +0200 Subject: [PATCH 68/74] CrazyLambdas upgrade * complete comparing and thenComparing method --- .../src/main/java/com/bobocode/fp/CrazyLambdas.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/5-0-functional-programming/5-1-1-crazy-lambdas/src/main/java/com/bobocode/fp/CrazyLambdas.java b/5-0-functional-programming/5-1-1-crazy-lambdas/src/main/java/com/bobocode/fp/CrazyLambdas.java index c4db8534..86812db5 100644 --- a/5-0-functional-programming/5-1-1-crazy-lambdas/src/main/java/com/bobocode/fp/CrazyLambdas.java +++ b/5-0-functional-programming/5-1-1-crazy-lambdas/src/main/java/com/bobocode/fp/CrazyLambdas.java @@ -265,7 +265,7 @@ public static BiFunction, String, IntUnaryOperator * @return a comparator instance */ public static > Comparator comparing(Function mapper) { - throw new ExerciseNotCompletedException(); + return (o1, o2) -> mapper.apply(o1).compareTo(mapper.apply(o2)); } /** @@ -285,7 +285,13 @@ public static > Comparator comparing(Funct */ public static > Comparator thenComparing( Comparator comparator, Function mapper) { - throw new ExerciseNotCompletedException(); + return (o1, o2) -> { + var initialResult = comparator.compare(o1, o2); + if (initialResult != 0) { + return initialResult; + } + return mapper.apply(o1).compareTo(mapper.apply(o2)); + }; } /** From ef0fe4a2d76cc0b512b31ade68ea714a45d1f7da Mon Sep 17 00:00:00 2001 From: Ruslan Hladchenko <60611618+hladchenko@users.noreply.github.com> Date: Thu, 21 Sep 2023 11:25:24 +0300 Subject: [PATCH 69/74] Update CrazyOptionals.java --- .../src/main/java/com/bobocode/fp/CrazyOptionals.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/5-0-functional-programming/5-3-1-crazy-optionals/src/main/java/com/bobocode/fp/CrazyOptionals.java b/5-0-functional-programming/5-3-1-crazy-optionals/src/main/java/com/bobocode/fp/CrazyOptionals.java index 03a5270a..cccaaee2 100644 --- a/5-0-functional-programming/5-3-1-crazy-optionals/src/main/java/com/bobocode/fp/CrazyOptionals.java +++ b/5-0-functional-programming/5-3-1-crazy-optionals/src/main/java/com/bobocode/fp/CrazyOptionals.java @@ -248,7 +248,7 @@ public static void processAccountWithMaxBalance(List accounts, AccountS */ public static double calculateTotalCreditBalance(List accounts) { // If you have a stream of optionals and you want to filter empty ones, you can do the trick and call - // `Stream#flatMap` and pass `Optional#sream`. This logic transforms each optional object into a stream of either + // `Stream#flatMap` and pass `Optional#stream`. This logic transforms each optional object into a stream of either // one of zero elements and then all those streams are flattened into one using `flatMap` which automatically // filters all empty optional return accounts.stream() From f6ad76043b0dcf1298d81818207799d7f1f183b7 Mon Sep 17 00:00:00 2001 From: Mykola Klushyn Date: Sun, 14 May 2023 12:59:05 +0200 Subject: [PATCH 70/74] CrazyGenerics add test for checking public accessor type by new methods --- .../bobocode/basics/CrazyGenericsTest.java | 27 ++++++++++++++----- 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/1-0-java-basics/1-3-1-crazy-generics/src/test/java/com/bobocode/basics/CrazyGenericsTest.java b/1-0-java-basics/1-3-1-crazy-generics/src/test/java/com/bobocode/basics/CrazyGenericsTest.java index 4623dabf..390c37ba 100644 --- a/1-0-java-basics/1-3-1-crazy-generics/src/test/java/com/bobocode/basics/CrazyGenericsTest.java +++ b/1-0-java-basics/1-3-1-crazy-generics/src/test/java/com/bobocode/basics/CrazyGenericsTest.java @@ -722,9 +722,16 @@ static Stream hasDuplicatesArgs() { ); } + @Test + @Order(58) + @DisplayName("Method findMax has public accessor type") + void findMaxHasPublicAccessorType() { + var findMaxMethod = getMethodByName(CollectionUtil.class, "findMax"); + assertThat(findMaxMethod).isNotNull(); + } @ParameterizedTest - @Order(58) + @Order(59) @MethodSource("findMaxArgs") @DisplayName("Method findMax returns the max value based on given comparator") @SneakyThrows @@ -755,7 +762,15 @@ static Stream findMaxArgs() { } @Test - @Order(59) + @Order(60) + @DisplayName("Method findMostRecentlyCreatedEntity has public accessor type") + void findMostRecentlyCreatedEntityHasPublicAccessorType() { + var findMaxMethod = getMethodByName(CollectionUtil.class, "findMostRecentlyCreatedEntity"); + assertThat(findMaxMethod).isNotNull(); + } + + @Test + @Order(61) @DisplayName("findMostRecentlyCreatedEntity is a generic method that accepts a collection of entities") void findMostRecentlyCreatedEntityIsAGenericMethod() { var hasDuplicatesMethod = getMethodByName(CollectionUtil.class, "findMostRecentlyCreatedEntity"); @@ -767,7 +782,7 @@ void findMostRecentlyCreatedEntityIsAGenericMethod() { } @ParameterizedTest - @Order(60) + @Order(62) @MethodSource("findMostRecentlyCreatedEntityArgs") @DisplayName("findMostRecentlyCreatedEntity returns the most recently created entity") @SneakyThrows @@ -794,7 +809,7 @@ static Stream findMostRecentlyCreatedEntityArgs() { } @Test - @Order(61) + @Order(63) @DisplayName("findMostRecentlyCreatedEntity throws exception when collection is empty") @SneakyThrows void findMostRecentlyCreatedEntityThrowsException() { @@ -805,7 +820,7 @@ void findMostRecentlyCreatedEntityThrowsException() { } @Test - @Order(62) + @Order(64) @DisplayName("Method swap does not declare type parameter") void swapMethodDoesNotDeclareTypeParameter() { var swapMethod = getMethodByName(CollectionUtil.class, "swap"); @@ -815,7 +830,7 @@ void swapMethodDoesNotDeclareTypeParameter() { } @Test - @Order(63) + @Order(65) @DisplayName("Method swap change elements by indexes") @SneakyThrows void swapChangesElements() { From 474373d7a466848b1817b8ee9b28e0396713a4b2 Mon Sep 17 00:00:00 2001 From: Mykola Klushyn Date: Sun, 14 May 2023 13:04:13 +0200 Subject: [PATCH 71/74] rename test variable --- .../src/test/java/com/bobocode/basics/CrazyGenericsTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/1-0-java-basics/1-3-1-crazy-generics/src/test/java/com/bobocode/basics/CrazyGenericsTest.java b/1-0-java-basics/1-3-1-crazy-generics/src/test/java/com/bobocode/basics/CrazyGenericsTest.java index 390c37ba..cfb315c1 100644 --- a/1-0-java-basics/1-3-1-crazy-generics/src/test/java/com/bobocode/basics/CrazyGenericsTest.java +++ b/1-0-java-basics/1-3-1-crazy-generics/src/test/java/com/bobocode/basics/CrazyGenericsTest.java @@ -765,8 +765,8 @@ static Stream findMaxArgs() { @Order(60) @DisplayName("Method findMostRecentlyCreatedEntity has public accessor type") void findMostRecentlyCreatedEntityHasPublicAccessorType() { - var findMaxMethod = getMethodByName(CollectionUtil.class, "findMostRecentlyCreatedEntity"); - assertThat(findMaxMethod).isNotNull(); + var findMostRecentlyCreatedEntity = getMethodByName(CollectionUtil.class, "findMostRecentlyCreatedEntity"); + assertThat(findMostRecentlyCreatedEntity).isNotNull(); } @Test From d50009b43cf74bf0a6d93fc4d208a8142a3cd913 Mon Sep 17 00:00:00 2001 From: Mykola Klushyn Date: Sun, 14 May 2023 14:24:36 +0200 Subject: [PATCH 72/74] rename method names --- .../test/java/com/bobocode/basics/CrazyGenericsTest.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/1-0-java-basics/1-3-1-crazy-generics/src/test/java/com/bobocode/basics/CrazyGenericsTest.java b/1-0-java-basics/1-3-1-crazy-generics/src/test/java/com/bobocode/basics/CrazyGenericsTest.java index cfb315c1..d616e908 100644 --- a/1-0-java-basics/1-3-1-crazy-generics/src/test/java/com/bobocode/basics/CrazyGenericsTest.java +++ b/1-0-java-basics/1-3-1-crazy-generics/src/test/java/com/bobocode/basics/CrazyGenericsTest.java @@ -724,8 +724,8 @@ static Stream hasDuplicatesArgs() { @Test @Order(58) - @DisplayName("Method findMax has public accessor type") - void findMaxHasPublicAccessorType() { + @DisplayName("Method findMax has public access type") + void findMaxHasPublicAccessType() { var findMaxMethod = getMethodByName(CollectionUtil.class, "findMax"); assertThat(findMaxMethod).isNotNull(); } @@ -763,8 +763,8 @@ static Stream findMaxArgs() { @Test @Order(60) - @DisplayName("Method findMostRecentlyCreatedEntity has public accessor type") - void findMostRecentlyCreatedEntityHasPublicAccessorType() { + @DisplayName("Method findMostRecentlyCreatedEntity has public access type") + void findMostRecentlyCreatedEntityHasPublicAccessType() { var findMostRecentlyCreatedEntity = getMethodByName(CollectionUtil.class, "findMostRecentlyCreatedEntity"); assertThat(findMostRecentlyCreatedEntity).isNotNull(); } From 0916202a3bea4edc03ac6cf0256761c64b68a6da Mon Sep 17 00:00:00 2001 From: vitalish Date: Wed, 25 Oct 2023 22:54:04 +0300 Subject: [PATCH 73/74] Rename test name on ArrayListTest --- .../src/test/java/com/bobocode/cs/ArrayListTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/2-0-data-structures-and-algorithms/2-2-5-array-list/src/test/java/com/bobocode/cs/ArrayListTest.java b/2-0-data-structures-and-algorithms/2-2-5-array-list/src/test/java/com/bobocode/cs/ArrayListTest.java index 7c7ef668..3db9b465 100644 --- a/2-0-data-structures-and-algorithms/2-2-5-array-list/src/test/java/com/bobocode/cs/ArrayListTest.java +++ b/2-0-data-structures-and-algorithms/2-2-5-array-list/src/test/java/com/bobocode/cs/ArrayListTest.java @@ -240,7 +240,7 @@ void setElementByIndexThrowsExceptionWhenIndexIsOutOfBound() { @Test @Order(23) - void setFirstElementOnEmptyTree() { + void setFirstElementOnEmptyList() { assertThatExceptionOfType(IndexOutOfBoundsException.class) .isThrownBy(() -> arrayList.set(0, 34)); } From 88e17bfa4ff2be62385b57dca3d30a749a894a3e Mon Sep 17 00:00:00 2001 From: Taras Boychuk Date: Mon, 15 Jan 2024 10:24:34 +0200 Subject: [PATCH 74/74] GP-145 Fix resizeTable method to calculate HashTable size correctly --- .../main/java/com/bobocode/cs/HashTable.java | 5 +++-- .../java/com/bobocode/cs/HashTableTest.java | 22 +++++++++++++++---- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/2-0-data-structures-and-algorithms/2-2-9-hash-table/src/main/java/com/bobocode/cs/HashTable.java b/2-0-data-structures-and-algorithms/2-2-9-hash-table/src/main/java/com/bobocode/cs/HashTable.java index f7f8663f..1af07147 100644 --- a/2-0-data-structures-and-algorithms/2-2-9-hash-table/src/main/java/com/bobocode/cs/HashTable.java +++ b/2-0-data-structures-and-algorithms/2-2-9-hash-table/src/main/java/com/bobocode/cs/HashTable.java @@ -1,9 +1,9 @@ package com.bobocode.cs; -import static java.util.Objects.requireNonNull; - import lombok.ToString; +import static java.util.Objects.requireNonNull; + /** * {@link HashTable} is a simple Hashtable-based implementation of {@link Map} interface with some additional methods. * It is based on the array of {@link Node} objects. Both {@link HashTable} and {@link Node} have two type parameters: @@ -271,6 +271,7 @@ public String toString() { public void resizeTable(int newCapacity) { verifyCapacity(newCapacity); @SuppressWarnings("unchecked") Node[] newTable = new Node[newCapacity]; + size = 0; for (var head : table) { var current = head; while (current != null) { diff --git a/2-0-data-structures-and-algorithms/2-2-9-hash-table/src/test/java/com/bobocode/cs/HashTableTest.java b/2-0-data-structures-and-algorithms/2-2-9-hash-table/src/test/java/com/bobocode/cs/HashTableTest.java index b7355496..1a3565c2 100644 --- a/2-0-data-structures-and-algorithms/2-2-9-hash-table/src/test/java/com/bobocode/cs/HashTableTest.java +++ b/2-0-data-structures-and-algorithms/2-2-9-hash-table/src/test/java/com/bobocode/cs/HashTableTest.java @@ -503,10 +503,10 @@ class HashTableHelperMethodsTest { @Order(1) @DisplayName("resizeTable creates a new array and put there all elements") void resizeTable() { - addToTable("madmax", 833); - addToTable("altea", 553); - addToTable("AaAa", 123); - addToTable("BBBB", 456); + hashTable.put("madmax", 833); + hashTable.put("altea", 553); + hashTable.put("AaAa", 123); + hashTable.put("BBBB", 456); hashTable.resizeTable(16); @@ -518,6 +518,20 @@ void resizeTable() { } @Test + @Order(2) + @DisplayName("resizeTable does not change the size") + void resizeTableDoesNotChangeSize() { + hashTable.put("madmax", 833); + hashTable.put("altea", 553); + hashTable.put("AaAa", 123); + + hashTable.resizeTable(32); + + assertThat(hashTable.size()).isEqualTo(3); + } + + @Test + @Order(3) @DisplayName("toString returns a string that represents an underlying table") void toStringTest() { addToTable("madmax", 833);