diff --git a/collections-benchmarking/.classpath b/collections-benchmarking/.classpath new file mode 100644 index 0000000..889b676 --- /dev/null +++ b/collections-benchmarking/.classpath @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/collections-benchmarking/.project b/collections-benchmarking/.project new file mode 100644 index 0000000..2b2ed11 --- /dev/null +++ b/collections-benchmarking/.project @@ -0,0 +1,23 @@ + + + collections-benchmarking + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.jdt.core.javanature + org.eclipse.m2e.core.maven2Nature + + diff --git a/collections-benchmarking/.settings/org.eclipse.core.resources.prefs b/collections-benchmarking/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 0000000..f9fe345 --- /dev/null +++ b/collections-benchmarking/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,4 @@ +eclipse.preferences.version=1 +encoding//src/main/java=UTF-8 +encoding//src/test/java=UTF-8 +encoding/=UTF-8 diff --git a/collections-benchmarking/.settings/org.eclipse.jdt.core.prefs b/collections-benchmarking/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..ecb498c --- /dev/null +++ b/collections-benchmarking/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,16 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate +org.eclipse.jdt.core.compiler.codegen.targetPlatform=17 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=17 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning +org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning +org.eclipse.jdt.core.compiler.release=enabled +org.eclipse.jdt.core.compiler.source=17 diff --git a/collections-benchmarking/.settings/org.eclipse.m2e.core.prefs b/collections-benchmarking/.settings/org.eclipse.m2e.core.prefs new file mode 100644 index 0000000..f897a7f --- /dev/null +++ b/collections-benchmarking/.settings/org.eclipse.m2e.core.prefs @@ -0,0 +1,4 @@ +activeProfiles= +eclipse.preferences.version=1 +resolveWorkspaceProjects=true +version=1 diff --git a/collections-benchmarking/pom.xml b/collections-benchmarking/pom.xml new file mode 100644 index 0000000..eb3915b --- /dev/null +++ b/collections-benchmarking/pom.xml @@ -0,0 +1,129 @@ + + 4.0.0 + + com.example + collections-benchmarking + 0.0.1-SNAPSHOT + jar + + JMH benchmark sample: Java + + + 3.0 + + + + + org.eclipse.collections + eclipse-collections + 9.0.0 + + + org.openjdk.jmh + jmh-core + ${jmh.version} + + + org.openjdk.jmh + jmh-generator-annprocess + ${jmh.version} + provided + + + + + UTF-8 + 1.19 + 1.8 + benchmarks + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.1 + + ${javac.target} + ${javac.target} + ${javac.target} + + + + org.apache.maven.plugins + maven-shade-plugin + 2.2 + + + package + + shade + + + ${uberjar.name} + + + org.openjdk.jmh.Main + + + + + *:* + + META-INF/*.SF + META-INF/*.DSA + META-INF/*.RSA + + + + + + + + + + + + maven-clean-plugin + 2.5 + + + maven-deploy-plugin + 2.8.1 + + + maven-install-plugin + 2.5.1 + + + maven-jar-plugin + 2.4 + + + maven-javadoc-plugin + 2.9.1 + + + maven-resources-plugin + 2.6 + + + maven-site-plugin + 3.3 + + + maven-source-plugin + 2.2.1 + + + maven-surefire-plugin + 2.17 + + + + + + diff --git a/collections-benchmarking/src/main/java/com/example/benchmarks/ListBenchmark.java b/collections-benchmarking/src/main/java/com/example/benchmarks/ListBenchmark.java new file mode 100644 index 0000000..39965f3 --- /dev/null +++ b/collections-benchmarking/src/main/java/com/example/benchmarks/ListBenchmark.java @@ -0,0 +1,127 @@ +package com.example.benchmarks; + +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Deque; +import java.util.LinkedList; +import java.util.List; + +import org.eclipse.collections.impl.list.mutable.FastList; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +public class ListBenchmark { + + private static final int DATA_SIZE = 100_000; + + @Benchmark + public void addTail_FastList() { + List list = new FastList<>(); + for (int i = 0; i < DATA_SIZE; ++i) + list.add(i); + } + + @Benchmark + public void addTail_FastList_FullCapacity() { + List list = new FastList<>(DATA_SIZE); + for (int i = 0; i < DATA_SIZE; ++i) + list.add(i); + } + + @Benchmark + public void addHead_FastList() { + List list = new FastList<>(); + for (int i = 0; i < DATA_SIZE; ++i) + list.add(0,i); + } + + @Benchmark + public void addHead_FastList_FullCapacity() { + List list = new FastList<>(DATA_SIZE); + for (int i = 0; i < DATA_SIZE; ++i) + list.add(0,i); + } + + @Benchmark + public void addTail_ArrayList() { + List list = new ArrayList<>(); + for (int i = 0; i < DATA_SIZE; ++i) + list.add(i); + } + + @Benchmark + public void addTail_ArrayList_fullCapacity() { + List list = new ArrayList<>(DATA_SIZE); + for (int i = 0; i < DATA_SIZE; ++i) + list.add(i); + } + + @Benchmark + public void addTail_ArrayDeque() { + Deque list = new ArrayDeque<>(); + for (int i = 0; i < DATA_SIZE; ++i) + list.addLast(i); + } + + @Benchmark + public void addTail_ArrayDeque_fullCapacity() { + Deque list = new ArrayDeque<>(DATA_SIZE); + for (int i = 0; i < DATA_SIZE; ++i) + list.addLast(i); + } + + @Benchmark() + public void addHead_ArrayList() { + List list = new ArrayList<>(); + for (int i = 0; i < DATA_SIZE; ++i) + list.add(0, i); + } + + @Benchmark() + public void addHead_ArrayList_fullCapacity() { + List list = new ArrayList<>(DATA_SIZE); + for (int i = 0; i < DATA_SIZE; ++i) + list.add(0, i); + } + + @Benchmark + public void addHead_ArrayDeque() { + Deque list = new ArrayDeque<>(); + for (int i = 0; i < DATA_SIZE; ++i) + list.addFirst(i); + } + + @Benchmark + public void addHead_ArrayDeque_fullCapacity() { + Deque list = new ArrayDeque<>(DATA_SIZE); + for (int i = 0; i < DATA_SIZE; ++i) + list.addFirst(i); + } + + @Benchmark + public void addTail_LinkedList() { + List list = new LinkedList<>(); + for (int i = 0; i < DATA_SIZE; ++i) + list.add(i); + } + + @Benchmark + public void addHead_LinkedList() { + List list = new LinkedList<>(); + for (int i = 0; i < DATA_SIZE; ++i) + list.add(0, i); + } + + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder() + .include(ListBenchmark.class.getSimpleName()) + .warmupIterations(5) + .measurementIterations(5) + .forks(1).build(); + + new Runner(opt).run(); + } +} diff --git a/collections-benchmarking/src/main/java/com/example/benchmarks/SetBenchmark.java b/collections-benchmarking/src/main/java/com/example/benchmarks/SetBenchmark.java new file mode 100644 index 0000000..0dacd85 --- /dev/null +++ b/collections-benchmarking/src/main/java/com/example/benchmarks/SetBenchmark.java @@ -0,0 +1,49 @@ +package com.example.benchmarks; + +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.Set; +import java.util.TreeSet; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +public class SetBenchmark { + + private static final int DATA_SIZE= 250_000; + + @Benchmark + public void add_HashSet() { + Set set = new HashSet<>(); + for (int i = 0; i < DATA_SIZE; ++i) + set.add(i); + } + + @Benchmark + public void add_LinkedHashSet() { + Set set = new LinkedHashSet<>(); + for (int i = 0; i < DATA_SIZE; ++i) + set.add(i); + } + + @Benchmark + public void add_TreeHashSet() { + Set set = new TreeSet<>(); + for (int i = 0; i < DATA_SIZE; ++i) + set.add(i); + } + + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder() + .include(SetBenchmark.class.getSimpleName()) + .warmupIterations(5) + .measurementIterations(10) + .forks(1) + .build(); + + new Runner(opt).run(); + } +} diff --git a/collections-benchmarking/src/main/java/com/example/benchmarks/info.txt b/collections-benchmarking/src/main/java/com/example/benchmarks/info.txt new file mode 100644 index 0000000..5b85e66 --- /dev/null +++ b/collections-benchmarking/src/main/java/com/example/benchmarks/info.txt @@ -0,0 +1,25 @@ +# Warmup Iteration 1: 209.528 ops/s +# Warmup Iteration 2: 267.464 ops/s +# Warmup Iteration 3: 209.574 ops/s +# Warmup Iteration 4: 359.673 ops/s +# Warmup Iteration 5: 292.768 ops/s +Iteration 1: 424.268 ops/s +Iteration 2: 507.136 ops/s +Iteration 3: 523.435 ops/s +Iteration 4: 547.116 ops/s +Iteration 5: 605.583 ops/s + +ListBenchmark.addHead_ArrayDeque thrpt 5 521.507 ± 253.896 ops/s +ListBenchmark.addHead_ArrayDeque_fullCapacity thrpt 5 1007.702 ± 573.402 ops/s +ListBenchmark.addHead_ArrayList thrpt 5 0.650 ± 0.484 ops/s +ListBenchmark.addHead_ArrayList_fullCapacity thrpt 5 0.746 ± 0.492 ops/s +ListBenchmark.addHead_FastList thrpt 5 0.466 ± 0.108 ops/s +ListBenchmark.addHead_FastList_FullCapacity thrpt 5 0.720 ± 0.377 ops/s +ListBenchmark.addHead_LinkedList thrpt 5 340.539 ± 331.427 ops/s +ListBenchmark.addTail_ArrayDeque thrpt 5 400.145 ± 573.084 ops/s +ListBenchmark.addTail_ArrayDeque_fullCapacity thrpt 5 1040.503 ± 800.128 ops/s +ListBenchmark.addTail_ArrayList thrpt 5 683.712 ± 514.071 ops/s +ListBenchmark.addTail_ArrayList_fullCapacity thrpt 5 850.254 ± 264.504 ops/s +ListBenchmark.addTail_FastList thrpt 5 659.356 ± 352.003 ops/s +ListBenchmark.addTail_FastList_FullCapacity thrpt 5 1151.413 ± 777.516 ops/s +ListBenchmark.addTail_LinkedList thrpt 5 586.896 ± 319.978 ops/s \ No newline at end of file diff --git a/core-banking/src/com/example/banking/application/BankingApplication.java b/core-banking/src/com/example/banking/application/BankingApplication.java index 593fe12..ba73034 100644 --- a/core-banking/src/com/example/banking/application/BankingApplication.java +++ b/core-banking/src/com/example/banking/application/BankingApplication.java @@ -2,12 +2,13 @@ import com.example.banking.domain.Account; import com.example.banking.domain.Customer; +import com.example.banking.domain.exception.InsufficientBalanceException; // Ctrl + Shift + F // Ctrl + Shift + O public class BankingApplication { @SuppressWarnings("unused") - public static void main(String[] args) { + public static void main(String[] args) throws InsufficientBalanceException { System.out.println("Number of accounts is %d".formatted(Account.getCount())); // i) Automatic/Stack/local/temporary variable // ii) reference variable -> Account @@ -18,10 +19,14 @@ public static void main(String[] args) { System.out.println(acc); acc.deposit(2_500); System.out.println(acc); - acc.withdraw(3_500); - System.out.println(acc); - acc.withdraw(1_000_000); - System.out.println(acc); + try { + acc.withdraw(3_500); + System.out.println(acc); + acc.withdraw(1_000_000); + System.out.println(acc); + } catch (InsufficientBalanceException e) { + System.err.println("Problem: "+e.getMessage()+", deficit: "+e.getDeficit()); + } // i) Stack/local/temporary variable // ii) value-typed variable -> int (primitive type) @@ -33,12 +38,26 @@ public static void main(String[] args) { jack.addAccount(new Account("tr3", 30_000)); System.out.println(jack.getAccounts().size()); jack.removeAccount("tr4") - .ifPresent( account -> account.withdraw(account.getBalance())); - var account = jack.findAccount("tr5"); + .ifPresent( account -> { + try { + account.withdraw(account.getBalance()); + } catch (InsufficientBalanceException e) { + e.printStackTrace(); + } + }); + var account = jack.findAccount("tr2"); + account.withdraw(1_000_000_000); // if (account != null) // account.withdraw(1_000); jack.getAccount("tr5") - .ifPresent( hesap -> hesap.withdraw(1_000)); + .ifPresent( hesap -> { + try { + hesap.withdraw(1_000); + } catch (InsufficientBalanceException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + }); System.out.println("Number of accounts is %d".formatted(Account.getCount())); } } diff --git a/core-banking/src/com/example/banking/application/StudyPolymorphism.java b/core-banking/src/com/example/banking/application/StudyPolymorphism.java index 763fa78..e77d78c 100644 --- a/core-banking/src/com/example/banking/application/StudyPolymorphism.java +++ b/core-banking/src/com/example/banking/application/StudyPolymorphism.java @@ -5,11 +5,12 @@ import com.example.banking.domain.Account; import com.example.banking.domain.CheckingAccount; +import com.example.banking.domain.exception.InsufficientBalanceException; @SuppressWarnings("unused") public class StudyPolymorphism { - public static void main(String[] args) { + public static void main(String[] args) throws InsufficientBalanceException { Account account ; if (ThreadLocalRandom.current().nextBoolean()) { diff --git a/core-banking/src/com/example/banking/domain/Account.java b/core-banking/src/com/example/banking/domain/Account.java index 29936c2..4a25b8c 100644 --- a/core-banking/src/com/example/banking/domain/Account.java +++ b/core-banking/src/com/example/banking/domain/Account.java @@ -4,6 +4,8 @@ // 2. class/method -> cannot extend class/cannot override method // since java se 17, sealed +import com.example.banking.domain.exception.InsufficientBalanceException; + // Ctrl + Shift + + // Alt + Shift + S // DDD (Domain-Driven Design) @@ -50,23 +52,27 @@ public double getBalance() { } // (3) business methods - public final boolean deposit(final double amount) { - // validation - if (amount <= 0.0) return false; + public final double deposit(final double amount) throws IllegalArgumentException { + // validation + if (amount <= 0.0) + throw new IllegalArgumentException("Amount must be positive"); // this.balance = this.balance + amount; this.balance += amount; - return true; + return balance; } - public boolean withdraw(final double amount) { + public double withdraw(final double amount) throws InsufficientBalanceException { System.err.println("Account::withdraw"); // validation - if (amount <= 0.0) return false; + if (amount <= 0.0) + throw new IllegalArgumentException("Amount must be positive"); // business rule - if (amount > this.balance) return false; + if (amount > this.balance) + throw new InsufficientBalanceException( + "Your balance does not cover expenses",amount-balance); //this.balance = this.balance - amount; this.balance -= amount; - return true; + return balance; } @Override diff --git a/core-banking/src/com/example/banking/domain/CheckingAccount.java b/core-banking/src/com/example/banking/domain/CheckingAccount.java index 85325ac..3e9a134 100644 --- a/core-banking/src/com/example/banking/domain/CheckingAccount.java +++ b/core-banking/src/com/example/banking/domain/CheckingAccount.java @@ -1,4 +1,7 @@ package com.example.banking.domain; + +import com.example.banking.domain.exception.InsufficientBalanceException; + // Account -> super-class / base class // CheckingAccount -> sub-class / derived class public class CheckingAccount extends Account { // Single Inheritance @@ -14,13 +17,16 @@ public double getOverdraftAmount() { } @Override - public boolean withdraw(double amount) { + public double withdraw(double amount) throws InsufficientBalanceException { System.err.println("CheckingAccount::withdraw"); - if (amount <= 0.0) return false; + if (amount <= 0.0) + throw new IllegalArgumentException("Amount must be positive"); final double maxBalance = balance + overdraftAmount; - if (amount > maxBalance) return false; + if (amount > maxBalance) + throw new InsufficientBalanceException( + "Your balance does not cover expenses",amount-maxBalance); this.balance -= amount; - return true; + return balance; } @Override diff --git a/core-banking/src/com/example/banking/domain/Customer.java b/core-banking/src/com/example/banking/domain/Customer.java index e199f43..aaea019 100644 --- a/core-banking/src/com/example/banking/domain/Customer.java +++ b/core-banking/src/com/example/banking/domain/Customer.java @@ -1,15 +1,15 @@ package com.example.banking.domain; -import java.util.ArrayList; -import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Optional; public final class Customer { // 1) attributes -> information hiding: private + getter->immutable private final String identityNo; private String fullName; - private List accounts = new ArrayList<>(); + private Map accounts = new HashMap<>(); // 2) constructors public Customer(String identityNo, String fullName) { @@ -30,44 +30,29 @@ public String getIdentityNo() { return identityNo; } - public List getAccounts() { - return Collections.unmodifiableList(accounts); // immutable + public List getAccounts() { + return accounts.values().stream().toList(); // immutable // return accounts; // violates information hiding principle } // 4. business method public void addAccount(Account account) { - this.accounts.add(account); + this.accounts.put(account.getIban(), account); } - + public Optional removeAccount(String iban) { - var optionalAccount = getAccount(iban); - if (optionalAccount.isPresent()) - accounts.remove(optionalAccount.get()); - return optionalAccount; + return Optional.ofNullable(accounts.remove(iban)); } - + public Optional getAccount(String iban) { - for (var account : accounts) { - if (account.getIban().equals(iban)) { - return Optional.of(account); - } - } - return Optional.empty(); + return Optional.ofNullable(accounts.get(iban)); } + public Account findAccount(String iban) { - for (var account : accounts) { - if (account.getIban().equals(iban)) { - return account; - } - } - return null; + return accounts.get(iban); } + public double getTotalBalance() { - var sum = 0.0; - for (var account : accounts) { - sum += account.getBalance(); - } - return sum; + return accounts.values().stream().mapToDouble(Account::getBalance).sum(); } } \ No newline at end of file diff --git a/core-banking/src/com/example/banking/domain/exception/InsufficientBalanceException.java b/core-banking/src/com/example/banking/domain/exception/InsufficientBalanceException.java new file mode 100644 index 0000000..f0b3d83 --- /dev/null +++ b/core-banking/src/com/example/banking/domain/exception/InsufficientBalanceException.java @@ -0,0 +1,16 @@ +package com.example.banking.domain.exception; + +@SuppressWarnings("serial") +public class InsufficientBalanceException extends Exception { + private final double deficit; + + public InsufficientBalanceException(String message, double deficit) { + super(message); + this.deficit = deficit; + } + + public double getDeficit() { + return deficit; + } + +} diff --git a/core-banking/test/com/example/banking/domain/AccountTest.java b/core-banking/test/com/example/banking/domain/AccountTest.java index 56d14eb..7339159 100644 --- a/core-banking/test/com/example/banking/domain/AccountTest.java +++ b/core-banking/test/com/example/banking/domain/AccountTest.java @@ -2,7 +2,7 @@ import static org.junit.jupiter.api.Assertions.assertAll; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.DisplayName; @@ -13,6 +13,8 @@ import org.junit.jupiter.params.provider.CsvFileSource; import org.junit.jupiter.params.provider.CsvSource; +import com.example.banking.domain.exception.InsufficientBalanceException; + @TestMethodOrder(MethodOrderer.OrderAnnotation.class) class AccountTest { // CUT (Class Under Test) -> Account @@ -74,10 +76,11 @@ void toStringTest(String iban) throws Exception { void withdrawWithNegativeAmountShouldFail(String iban, double balance, double amount) throws Exception { // 1. fixture/setup var account = new Account(iban, balance); - // 2. call exercise method - var result = account.withdraw(amount); // 3. verification - assertAll(() -> assertFalse(result), () -> assertEquals(balance, account.getBalance(), Double.MIN_NORMAL)); + assertAll( + () -> assertThrows(IllegalArgumentException.class, () -> account.withdraw(amount)), + () -> assertEquals(balance, account.getBalance(), Double.MIN_NORMAL) + ); // 4. destroy setup } @@ -91,9 +94,11 @@ void withdrawOverBalanceShouldFail(String iban, double balance, double amount) t // 1. fixture/setup var account = new Account(iban, balance); // 2. call exercise method - var result = account.withdraw(amount); // 3. verification - assertAll(() -> assertFalse(result), () -> assertEquals(balance, account.getBalance(), Double.MIN_NORMAL)); + assertAll( + () -> assertThrows(InsufficientBalanceException.class,() -> account.withdraw(amount)), + () -> assertEquals(balance, account.getBalance(), Double.MIN_NORMAL) + ); // 4. destroy setup } @@ -109,7 +114,7 @@ void withdrawAllBalanceShouldSuccess(String iban, double balance) throws Excepti // 2. call exercise method var result = account.withdraw(balance); // 3. verification - assertAll(() -> assertTrue(result), () -> assertEquals(0.0, account.getBalance(), Double.MIN_NORMAL)); + assertEquals(0.0,result, Double.MIN_NORMAL); // 4. destroy setup } @@ -123,9 +128,11 @@ void depositWithNegativeAmountShouldFail(String iban, double balance, double amo // 1. fixture/setup var account = new Account(iban, balance); // 2. call exercise method - var result = account.deposit(amount); // 3. verification - assertAll(() -> assertFalse(result), () -> assertEquals(balance, account.getBalance(), Double.MIN_NORMAL)); + assertAll( + () -> assertThrows(IllegalArgumentException.class,() -> account.deposit(amount)), + () -> assertEquals(balance, account.getBalance(), Double.MIN_NORMAL) + ); // 4. destroy setup } @@ -141,7 +148,7 @@ void depositWithPositiveAmountShouldSuccess(String iban, double balance, double // 2. call exercise method var result = account.deposit(amount); // 3. verification - assertAll(() -> assertTrue(result), () -> assertEquals(finalBalance, account.getBalance(), Double.MIN_NORMAL)); + assertEquals(finalBalance,result, Double.MIN_NORMAL); // 4. destroy setup } }