├── .gitignore
├── LICENSE
├── README.md
├── pom.xml
├── sample_gc_flags.txt
└── src
├── main
├── java
│ └── optjava
│ │ ├── Caching.java
│ │ ├── ClassicSort.java
│ │ ├── ModelAllocator.java
│ │ ├── ModelObjectAllocation.java
│ │ ├── OverLocking.java
│ │ ├── SHA256Finder.java
│ │ ├── SchedulerOverhead.java
│ │ ├── StringHash.java
│ │ ├── ch11
│ │ ├── DeliverySchedule.java
│ │ ├── Order.java
│ │ ├── OrderItem.java
│ │ └── SimpleDict.java
│ │ ├── counters
│ │ ├── AtomicCounter.java
│ │ ├── Counter.java
│ │ ├── CounterMain.java
│ │ ├── SynchronizedCounter.java
│ │ ├── UnprotectedCounter.java
│ │ └── VolatileCounter.java
│ │ ├── execution
│ │ ├── AtomicsExamples.java
│ │ └── ExecExamples.java
│ │ └── maps
│ │ ├── BadMapExamples.java
│ │ ├── DictMain.java
│ │ └── Dictionary.java
└── resources
│ ├── basic.bc
│ ├── example-alloc.bat
│ ├── example-alloc.sh
│ ├── scratch.bc
│ ├── synchronized.bc
│ └── ta_tb_basic.bc
└── test
└── java
└── optjava
└── ch11
└── SimpleTestSimpleDict.java
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | *.pdf
3 | /nbproject/
4 | build.xml
5 | manifest.mf
6 | /build/
7 | /dist/
8 | *.class
9 | *.log
10 |
11 | # Maven
12 | target/
13 | pom.xml.tag
14 | pom.xml.releaseBackup
15 | pom.xml.versionsBackup
16 | pom.xml.next
17 | release.properties
18 | dependency-reduced-pom.xml
19 | buildNumber.properties
20 | .mvn/timing.properties
21 |
22 | # Netbeans files
23 | /nbproject/private/
24 | nbactions.xml
25 | /lib/
26 |
27 | # IntelliJ
28 | .idea/
29 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | This repository contains some example pieces of code from the book "Optimizing Java"
2 | by Ben Evans, James Gough and Chris Newland, published by O'Reilly.
3 |
4 | You can use the code for education purposes only, but not for any production use.
5 |
6 | Please also consider buying a copy of the book.
7 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # optimizing-java
2 | Code examples for "Optimizing Java" published by O'Reilly
3 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 | optjava
5 | optimizing-java
6 | 1.0.0-SNAPSHOT
7 | jar
8 |
9 | UTF-8
10 | 1.8
11 | 1.8
12 |
13 | 4.13.1
14 | 7.0
15 | 1.13
16 | 3.1.1
17 | 2.1.9
18 |
19 |
20 |
21 |
22 |
23 | junit
24 | junit
25 | ${junit.version}
26 | test
27 | jar
28 |
29 |
30 |
31 | javax
32 | javaee-api
33 | ${javaee-api.version}
34 | provided
35 |
36 |
37 |
38 | org.hdrhistogram
39 | HdrHistogram
40 | ${hdrhist.version}
41 |
42 |
43 | org.openjdk.jmh
44 | jmh-core
45 | ${jmh.version}
46 |
47 |
48 | org.openjdk.jmh
49 | jmh-generator-annprocess
50 | ${jmh.version}
51 | provided
52 |
53 |
54 |
--------------------------------------------------------------------------------
/sample_gc_flags.txt:
--------------------------------------------------------------------------------
1 | -Xmx2G -Xloggc:gc.log -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -XX:+PrintTenuringDistribution -XX:+UseG1GC -XX:MaxGCPauseMillis=50
2 |
3 |
4 |
--------------------------------------------------------------------------------
/src/main/java/optjava/Caching.java:
--------------------------------------------------------------------------------
1 | package optjava;
2 |
3 | // tag::CACHE_EFFECT[]
4 | public class Caching {
5 | private final int ARR_SIZE = 2 * 1024 * 1024;
6 | private final int[] testData = new int[ARR_SIZE];
7 |
8 | private void run() {
9 | for (int i = 0; i < 10_000; i += 1) {
10 | touchEveryLine();
11 | touchEveryItem();
12 | }
13 | System.out.println("Line Item");
14 | for (int i = 0; i < 100; i += 1) {
15 | long t0 = System.nanoTime();
16 | touchEveryLine();
17 | long t1 = System.nanoTime();
18 | touchEveryItem();
19 | long t2 = System.nanoTime();
20 | long elEveryLine = t1 - t0;
21 | long elEveryItem = t2 - t1;
22 | System.out.println(elEveryLine + " " + elEveryItem);
23 | }
24 | }
25 |
26 | private void touchEveryItem() {
27 | for (int i = 0; i < testData.length; i += 1)
28 | testData[i] += 1;
29 | }
30 |
31 | private void touchEveryLine() {
32 | for (int i = 0; i < testData.length; i += 16)
33 | testData[i] += 1;
34 | }
35 |
36 | public static void main(String[] args) {
37 | Caching c = new Caching();
38 | c.run();
39 | }
40 | }
41 | // end::CACHE_EFFECT[]
42 |
--------------------------------------------------------------------------------
/src/main/java/optjava/ClassicSort.java:
--------------------------------------------------------------------------------
1 | package optjava;
2 |
3 | import java.util.ArrayList;
4 | import java.util.Collections;
5 | import java.util.List;
6 | import java.util.Random;
7 |
8 | // tag::CLASSIC_SORT[]
9 | public class ClassicSort {
10 | private static final int NUM_ITEMS = 100_000;
11 | private static final int ITERATIONS = 300_000;
12 | private static final List testData = new ArrayList<>();
13 |
14 | public static void main(String[] args) {
15 | Random randomGenerator = new Random();
16 | for (int i = 0; i < NUM_ITEMS; i++) {
17 | testData.add(randomGenerator.nextInt(Integer.MAX_VALUE));
18 | }
19 |
20 | System.out.println("Testing Sort Algorithm");
21 |
22 | double startTime = System.nanoTime();
23 |
24 | for (int i = 0; i < ITERATIONS; i++) {
25 | List copy = new ArrayList<>(testData);
26 | Collections.sort(copy);
27 | }
28 |
29 | double endTime = System.nanoTime();
30 | double timePerOperation = ((endTime - startTime) / (NUM_ITEMS * ITERATIONS));
31 | System.out.println("Result: " + (1 / timePerOperation) + " op/s");
32 | }
33 | }
34 | // end::CLASSIC_SORT[]
35 |
--------------------------------------------------------------------------------
/src/main/java/optjava/ModelAllocator.java:
--------------------------------------------------------------------------------
1 | package optjava;
2 |
3 | import java.util.concurrent.Executor;
4 | import java.util.concurrent.Executors;
5 |
6 | /**
7 | *
8 | * @author ben
9 | */
10 | // tag::MODEL_ALLOC[]
11 | public class ModelAllocator implements Runnable {
12 | private volatile boolean shutdown = false;
13 |
14 | private double chanceOfLongLived = 0.02;
15 | private int multiplierForLongLived = 20;
16 | private int x = 1024;
17 | private int y = 1024;
18 | private int mbPerSec = 50;
19 | private int shortLivedMs = 100;
20 | private int nThreads = 8;
21 | private Executor exec = Executors.newFixedThreadPool(nThreads);
22 | // end::MODEL_ALLOC[]
23 |
24 | public static void main(String[] args) {
25 | ModelAllocator ma = new ModelAllocator();
26 | ma.run();
27 | }
28 |
29 | // tag::MODEL_ALLOC_MAIN[]
30 | public void run() {
31 | final int mainSleep = (int) (1000.0 / mbPerSec);
32 |
33 | while (!shutdown) {
34 | for (int i = 0; i < mbPerSec; i++) {
35 | ModelObjectAllocation to = new ModelObjectAllocation(x, y, lifetime());
36 | exec.execute(to);
37 | try {
38 | Thread.sleep(mainSleep);
39 | } catch (InterruptedException ex) {
40 | shutdown = true;
41 | }
42 | }
43 | }
44 | }
45 |
46 | // Simple function to model Weak Generational Hypothesis
47 | // Returns the expected lifetime of an object - usually this
48 | // is very short, but there is a small chance of an object
49 | // being "long-lived"
50 | public int lifetime() {
51 | if (Math.random() < chanceOfLongLived) {
52 | return multiplierForLongLived * shortLivedMs;
53 | }
54 |
55 | return shortLivedMs;
56 | }
57 | }
58 | // end::MODEL_ALLOC_MAIN[]
59 |
--------------------------------------------------------------------------------
/src/main/java/optjava/ModelObjectAllocation.java:
--------------------------------------------------------------------------------
1 | package optjava;
2 |
3 | /**
4 | * This class contains an amount of storage and possesses a lifetime in milliseconds.
5 | * After the object has "expired" it will become eligible for GC.
6 | *
7 | * @author ben
8 | */
9 | // tag::OBJECT_ALLOC[]
10 | public class ModelObjectAllocation implements Runnable {
11 | private final byte[][] allocated;
12 | private final int lifeTime;
13 |
14 | public ModelObjectAllocation(final int x, final int y, final int liveFor) {
15 | allocated = new byte[x][y];
16 | lifeTime = liveFor;
17 | }
18 |
19 | @Override
20 | public void run() {
21 | try {
22 | Thread.sleep(lifeTime);
23 | System.err.println(System.currentTimeMillis() +": "+ allocated.length);
24 | } catch (InterruptedException ex) {
25 | }
26 | }
27 | }
28 | // end::OBJECT_ALLOC[]
--------------------------------------------------------------------------------
/src/main/java/optjava/OverLocking.java:
--------------------------------------------------------------------------------
1 | package optjava;
2 |
3 | import java.security.SecureRandom;
4 |
5 | public final class OverLocking {
6 |
7 | private static final Object lock = new Object();
8 |
9 | public static void main(String[] args) {
10 | SecureRandom sr = new SecureRandom();
11 | Runnable r = () -> {
12 | sr.setSeed(System.nanoTime());
13 | final int initialSleep = Math.abs(sr.nextInt() % 10_000);
14 | try {
15 | Thread.sleep(initialSleep);
16 | } catch (InterruptedException e) {
17 | e.printStackTrace();
18 | }
19 | synchronized (lock) {
20 | try {
21 | final int sleep = Math.abs(sr.nextInt() % 2_000);
22 | Thread.sleep(sleep);
23 | System.out.println("Thread: "+ Thread.currentThread().getName() +" finished");
24 | } catch (InterruptedException e) {
25 | e.printStackTrace();
26 | }
27 | }
28 | };
29 |
30 | Thread[] t = new Thread[20];
31 | for (int i = 0; i < t.length; i++) {
32 | t[i] = new Thread(r);
33 | t[i].setName("Waiter-"+ i);
34 | t[i].start();
35 | }
36 | for (int i = 0; i < t.length; i++) {
37 | try {
38 | t[i].join();
39 | System.out.println("Thread: "+ t[i].getName() +" joined");
40 | } catch (InterruptedException e) {
41 | e.printStackTrace();
42 | }
43 | }
44 |
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/main/java/optjava/SHA256Finder.java:
--------------------------------------------------------------------------------
1 | package optjava;
2 |
3 | import java.nio.charset.StandardCharsets;
4 | import java.security.MessageDigest;
5 | import java.security.NoSuchAlgorithmException;
6 | import java.util.concurrent.atomic.AtomicBoolean;
7 |
8 | public final class SHA256Finder {
9 |
10 | private final MessageDigest digest;
11 |
12 | public SHA256Finder() {
13 | try {
14 | digest = MessageDigest.getInstance("SHA-256");
15 | } catch (NoSuchAlgorithmException e) {
16 | throw new RuntimeException(e);
17 | }
18 | }
19 |
20 | // Threshold mask for remaining bits
21 | public static final int[] thresholdMask = {128, 64, 32, 16, 8, 4, 2, 1};
22 |
23 | public static String makeRandomString(int length) {
24 | final StringBuilder sb = new StringBuilder();
25 | for (int j = 0; j < length; j++) {
26 | // Select a random char ch >= 32 && ch < 127
27 | char c = (char)(32 + Math.random() * 96);
28 | sb.append(c);
29 | }
30 | return sb.toString();
31 | }
32 |
33 | public static void main(String[] args) throws Exception {
34 | SHA256Finder s = new SHA256Finder();
35 | s.findMatcher(1024, 18);
36 | }
37 |
38 | void findMatcher(int length, int target) {
39 | System.out.println("Length: " + length + " ; target: " + target);
40 | long attempts = 0;
41 |
42 | // Search indefinitely for a match
43 | String candidate = null;
44 | FOREVER:
45 | while (true) {
46 | candidate = makeRandomString(length);
47 | if (++attempts % 100_000 == 0) {
48 | System.out.print(".");
49 | }
50 | if (belowThreshold(candidate, target)) {
51 | System.out.println();
52 | System.out.println("Length: " + length + " took attempts: " + attempts +" to find a "+ target +" prefix");
53 | System.out.println("Winner: " + candidate);
54 | System.out.println("Winning hash: " + sha256Hash(candidate));
55 | }
56 | }
57 | }
58 |
59 | public boolean belowThreshold(final String s, final int numBits) {
60 | final byte[] hash = digest.digest(s.getBytes(StandardCharsets.UTF_8));
61 | int bitsCounted = 0;
62 | final int fullBytes = numBits / 8;
63 | final int remBits = numBits % 8;
64 |
65 | // Check full bytes first
66 | for (int i = 0; i < fullBytes; i++) {
67 | // Each hash byte contains 8 bits, so use an int to store them unsigned
68 | int compare = 0xff & hash[i];
69 | if (compare == 0) {
70 | bitsCounted += 8;
71 | if (bitsCounted >= numBits) {
72 | return true;
73 | }
74 | } else {
75 | return false;
76 | }
77 | }
78 |
79 | // Now check the bits using one final byte
80 | return thresholdMask[remBits] > (hash[fullBytes] & 0xff);
81 |
82 | }
83 |
84 | String sha256Hash(String s) {
85 | final byte[] hash = digest.digest(s.getBytes(StandardCharsets.UTF_8));
86 |
87 | final StringBuilder sb = new StringBuilder();
88 | for (byte aHash : hash) {
89 | String hex = Integer.toHexString(0xff & aHash);
90 | // We are converting an unsigned byte to a hex string.
91 | // This will give either 1 or 2 hex digits e.g. 'a' or '23' (35 dec).
92 | // If we only get 1 back, we need to include the zero byte so we stay fixed-width as a string.
93 | if (hex.length() == 1) sb.append('0');
94 | sb.append(hex);
95 | }
96 | return sb.toString();
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/src/main/java/optjava/SchedulerOverhead.java:
--------------------------------------------------------------------------------
1 | package optjava;
2 |
3 | public class SchedulerOverhead {
4 |
5 | public static void main(String[] args) throws InterruptedException {
6 | SchedulerOverhead so = new SchedulerOverhead();
7 | so.run();
8 | }
9 |
10 | private void run() throws InterruptedException {
11 | // tag::SCHEDULER_OVERHEAD[]
12 | long start = System.currentTimeMillis();
13 | for (int i = 0; i < 1_000; i++) {
14 | Thread.sleep(1);
15 | }
16 | long end = System.currentTimeMillis();
17 | System.out.println("Millis elapsed: " + (end - start));
18 | // end::SCHEDULER_OVERHEAD[]
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/main/java/optjava/StringHash.java:
--------------------------------------------------------------------------------
1 | package optjava;
2 |
3 | public final class StringHash {
4 |
5 | public static void main(String[] args) {
6 | StringHash sh = new StringHash();
7 | sh.run();
8 | }
9 |
10 | void run() {
11 | for (int i = 1; i < 2_000; i++) {
12 | timeHashing(i, 'x');
13 | }
14 | // System.identityHashCode(this);
15 | }
16 |
17 |
18 | void timeHashing(int length, char c) {
19 | final StringBuilder sb = new StringBuilder();
20 | for (int j = 0; j < length * 1_000_000; j++) {
21 | sb.append(c);
22 | }
23 | final String s = sb.toString();
24 | final long now = System.nanoTime();
25 | final int hash = s.hashCode();
26 | final long duration = System.nanoTime() - now;
27 | // System.err.println("Length: " + length + " took: " + duration + " ns");
28 | System.out.println(length + ","+ duration +","+ hash);
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/main/java/optjava/ch11/DeliverySchedule.java:
--------------------------------------------------------------------------------
1 | package optjava.ch11;
2 |
3 | import java.time.LocalDate;
4 |
5 | /**
6 | *
7 | * @author ben
8 | */
9 | // tag::DELIVERY_SCHEDULE[]
10 | public final class DeliverySchedule {
11 | private final LocalDate deliveryDate;
12 | private final String address;
13 | private final double deliveryCost;
14 |
15 | private DeliverySchedule(LocalDate deliveryDate, String address, double deliveryCost) {
16 | this.deliveryDate = deliveryDate;
17 | this.address = address;
18 | this.deliveryCost = deliveryCost;
19 | }
20 |
21 | public static DeliverySchedule of(LocalDate deliveryDate, String address, double deliveryCost) {
22 | return new DeliverySchedule(deliveryDate, address, deliveryCost);
23 | }
24 |
25 | @Override
26 | public String toString() {
27 | return "DeliverySchedule{" + "deliveryDate=" + deliveryDate + ", address=" + address + ", deliveryCost=" + deliveryCost + '}';
28 | }
29 | }
30 | // end::DELIVERY_SCHEDULE[]
--------------------------------------------------------------------------------
/src/main/java/optjava/ch11/Order.java:
--------------------------------------------------------------------------------
1 | package optjava.ch11;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 |
6 | /**
7 | *
8 | * @author ben
9 | */
10 | // tag::ORDER[]
11 | public class Order {
12 | private final long id;
13 | private final List items = new ArrayList<>();
14 | private DeliverySchedule schedule;
15 |
16 | public Order(long id) {
17 | this.id = id;
18 | }
19 |
20 | public DeliverySchedule getSchedule() {
21 | return schedule;
22 | }
23 |
24 | public void setSchedule(DeliverySchedule schedule) {
25 | this.schedule = schedule;
26 | }
27 |
28 | public List getItems() {
29 | return items;
30 | }
31 |
32 | public long getId() {
33 | return id;
34 | }
35 | }
36 | // end::ORDER[]
--------------------------------------------------------------------------------
/src/main/java/optjava/ch11/OrderItem.java:
--------------------------------------------------------------------------------
1 | package optjava.ch11;
2 |
3 | /**
4 | *
5 | * @author ben
6 | */
7 | // tag::ORDER_ITEM[]
8 | public class OrderItem {
9 | private final long id;
10 | private final String description;
11 | private final double price;
12 |
13 | public OrderItem(long id, String description, double price) {
14 | this.id = id;
15 | this.description = description;
16 | this.price = price;
17 | }
18 |
19 | @Override
20 | public String toString() {
21 | return "OrderItem{" + "id=" + id + ", description=" + description + ", price=" + price + '}';
22 | }
23 | }
24 | // end::ORDER_ITEM[]
--------------------------------------------------------------------------------
/src/main/java/optjava/ch11/SimpleDict.java:
--------------------------------------------------------------------------------
1 | package optjava.ch11;
2 |
3 | import java.util.Collection;
4 | import java.util.Map;
5 | import java.util.Set;
6 |
7 | /**
8 | *
9 | * @author ben
10 | */
11 | public class SimpleDict implements Map {
12 |
13 | private final Node[] table;
14 | private int size;
15 |
16 | /**
17 | * SimpleDict requires a capacity (and is non-resizable). If the capacity
18 | * specified is not a power of 2, it is rounded down to the nearest power
19 | * of 2.
20 | *
21 | * @param capacity the number of hash buckets
22 | */
23 | public SimpleDict(int capacity) {
24 | if (capacity <= 0) {
25 | table = new Node[16];
26 | } else {
27 | byte count = 0;
28 | while (capacity > 0) {
29 | capacity = capacity >> 1;
30 | count++;
31 | }
32 | table = new Node[1 << (count - 1)];
33 | }
34 | }
35 |
36 | private static class Node implements Entry {
37 |
38 | final int hash;
39 | final String key;
40 | String value;
41 | Node next;
42 |
43 | Node(int h, String k, String v, Node n) {
44 | hash = h;
45 | key = k;
46 | value = v;
47 | next = n;
48 | }
49 |
50 | @Override
51 | public String getKey() {
52 | return key;
53 | }
54 |
55 | @Override
56 | public String getValue() {
57 | return value;
58 | }
59 |
60 | @Override
61 | public String setValue(String v) {
62 | value = v;
63 | return value;
64 | }
65 | }
66 |
67 | public int capacity() {
68 | return table.length;
69 | }
70 |
71 | @Override
72 | public int size() {
73 | return size;
74 | }
75 |
76 | @Override
77 | public boolean isEmpty() {
78 | return size == 0;
79 | }
80 |
81 | @Override
82 | public boolean containsKey(Object key) {
83 | if (key == null) return false;
84 | return getNode(key) != null;
85 | }
86 |
87 | private Node getNode(Object key) {
88 | int hash = key.hashCode();
89 | Node[] tab;
90 | Node first, e;
91 | int n;
92 | String k;
93 | if ((tab = table) != null && (n = tab.length) > 0
94 | && (first = tab[(n - 1) & hash]) != null) {
95 | if (first.hash == hash && // always check first node
96 | ((k = first.key) == key || key.equals(k)))
97 | return first;
98 | if ((e = first.next) != null) {
99 | do {
100 | if (e.hash == hash
101 | && ((k = e.key) == key || key.equals(k)))
102 | return e;
103 | } while ((e = e.next) != null);
104 | }
105 | }
106 | return null;
107 | }
108 |
109 | @Override
110 | public String get(Object key) {
111 | // Null keys are not supported
112 | if (key == null)
113 | return null;
114 |
115 | int hash = key.hashCode();
116 | int i = indexFor(hash, table.length);
117 | for (Node e = table[i]; e != null; e = e.next) {
118 | String k;
119 | if (e.hash == hash
120 | && ((k = e.key) == key || key.equals(k)))
121 | return e.value;
122 | }
123 |
124 | return null;
125 | }
126 |
127 | private int indexFor(int h, int length) {
128 | return h & (length - 1);
129 | }
130 |
131 | @Override
132 | public String put(String key, String value) {
133 | // Null keys are not supported
134 | if (key == null)
135 | return null;
136 |
137 | int hash = key.hashCode();
138 | int i = indexFor(hash, table.length);
139 |
140 | if (table[i] == null) {
141 | size++;
142 | table[i] = new Node(hash, key, value, null);
143 | return table[i].getValue();
144 | }
145 | Node curr = table[i];
146 | for (Node e = table[i]; e != null; e = e.next) {
147 | String k;
148 | if (e.hash == hash
149 | && ((k = e.key) == key || key.equals(k))) {
150 | e.setValue(value);
151 | return e.value;
152 | }
153 | curr = e;
154 | }
155 | size++;
156 | curr.next = new Node(hash, key, value, null);
157 | return curr.next.getValue();
158 | }
159 |
160 | @Override
161 | public String remove(Object key) {
162 | // Null keys are not supported
163 | if (key == null)
164 | return null;
165 |
166 | int hash = key.hashCode();
167 | int i = indexFor(hash, table.length);
168 |
169 | Node last = table[i];
170 | for (Node e = table[i]; e != null; e = e.next) {
171 | String k;
172 | if (e.hash == hash
173 | && ((k = e.key) == key || key.equals(k))) {
174 | // Remove the Node
175 | last.next = e.next;
176 | size--;
177 | return e.value;
178 | }
179 | last = e;
180 | }
181 |
182 | return null;
183 | }
184 |
185 | @Override
186 | public void clear() {
187 | Node[] tab;
188 | if ((tab = table) != null && size > 0) {
189 | size = 0;
190 | for (int i = 0; i < tab.length; ++i)
191 | tab[i] = null;
192 | }
193 | }
194 |
195 | /////////////////////////////////////////////////////////////////////
196 | @Override
197 | public void putAll(Map extends String, ? extends String> m) {
198 | throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
199 | }
200 |
201 | @Override
202 | public boolean containsValue(Object value) {
203 | throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
204 | }
205 |
206 | @Override
207 | public Set keySet() {
208 | throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
209 | }
210 |
211 | @Override
212 | public Collection values() {
213 | throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
214 | }
215 |
216 | @Override
217 | public Set> entrySet() {
218 | throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
219 | }
220 |
221 | }
222 |
--------------------------------------------------------------------------------
/src/main/java/optjava/counters/AtomicCounter.java:
--------------------------------------------------------------------------------
1 | package optjava.counters;
2 |
3 | import sun.misc.Unsafe;
4 | import java.lang.reflect.Field;
5 | //import java.util.concurrent.atomic.AtomicInteger;
6 |
7 | public final class AtomicCounter implements Counter {
8 |
9 | private static final Unsafe unsafe; // = Unsafe.getUnsafe();
10 | private static final long valueOffset;
11 |
12 | private volatile int value = 0;
13 |
14 | // setup to use Unsafe.compareAndSwapInt for updates
15 | static {
16 | try {
17 | Field f = Unsafe.class.getDeclaredField("theUnsafe");
18 | f.setAccessible(true);
19 | unsafe = (Unsafe) f.get(null);
20 | valueOffset = unsafe.objectFieldOffset(AtomicCounter.class.getDeclaredField("value"));
21 | System.out.println("Offset: "+ valueOffset);
22 | } catch (Exception ex) { throw new Error(ex); }
23 | }
24 |
25 | /**
26 | * Atomically increments by one the current value.
27 | *
28 | * @return the updated value
29 | */
30 | public int increment() {
31 | return 1 + unsafe.getAndAddInt(this, valueOffset, 1);
32 | }
33 |
34 | /**
35 | * Gets the current value.
36 | *
37 | * @return the current value
38 | */
39 | public int get() {
40 | return value;
41 | }
42 |
43 | /**
44 | * Sets to the given value.
45 | *
46 | * @param newValue the new value
47 | */
48 | public void set(int newValue) {
49 | value = newValue;
50 | }
51 |
52 | }
53 |
--------------------------------------------------------------------------------
/src/main/java/optjava/counters/Counter.java:
--------------------------------------------------------------------------------
1 | package optjava.counters;
2 |
3 | public interface Counter {
4 | int increment();
5 |
6 | int get();
7 | }
8 |
--------------------------------------------------------------------------------
/src/main/java/optjava/counters/CounterMain.java:
--------------------------------------------------------------------------------
1 | package optjava.counters;
2 |
3 | /**
4 | * @author ben
5 | */
6 | public class CounterMain {
7 |
8 | public static final int REPS = 10_000_000;
9 |
10 | public static void main(String[] args) throws InterruptedException {
11 | final Counter c = new UnprotectedCounter();
12 |
13 | Runnable r = () -> {
14 | for (int i = 0; i < REPS; i++) {
15 | c.increment();
16 | }
17 | };
18 |
19 | Thread t1 = new Thread(r);
20 | Thread t2 = new Thread(r);
21 | long start = System.currentTimeMillis();
22 | t1.start();
23 | t2.start();
24 | t1.join();
25 | t2.join();
26 | long fin = System.currentTimeMillis();
27 | int diff = 2 * REPS - c.get();
28 | System.out.println("Diff: " + diff);
29 | System.out.println("Elapsed: " + (fin - start));
30 | }
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/src/main/java/optjava/counters/SynchronizedCounter.java:
--------------------------------------------------------------------------------
1 | package optjava.counters;
2 |
3 | /**
4 | *
5 | * @author ben
6 | */
7 | public final class SynchronizedCounter implements Counter {
8 |
9 | private int i = 0;
10 |
11 | public synchronized int increment() {
12 | return i = i + 1;
13 | }
14 |
15 | public synchronized int get() {
16 | return i;
17 | }
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/src/main/java/optjava/counters/UnprotectedCounter.java:
--------------------------------------------------------------------------------
1 | package optjava.counters;
2 |
3 | /**
4 | *
5 | * @author ben
6 | */
7 | public final class UnprotectedCounter implements Counter {
8 |
9 | private int i = 0;
10 |
11 | public int increment() {
12 | return i = i + 1;
13 | }
14 |
15 | public int get() {
16 | return i;
17 | }
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/src/main/java/optjava/counters/VolatileCounter.java:
--------------------------------------------------------------------------------
1 | package optjava.counters;
2 |
3 | /**
4 | *
5 | * @author ben
6 | */
7 | public final class VolatileCounter implements Counter {
8 |
9 | private volatile int value = 0;
10 |
11 | public int increment() {
12 | return value = value + 1;
13 | }
14 |
15 | public int get() {
16 | return value;
17 | }
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/src/main/java/optjava/execution/AtomicsExamples.java:
--------------------------------------------------------------------------------
1 | package optjava.execution;
2 |
3 | import java.util.concurrent.atomic.AtomicInteger;
4 |
5 | public class AtomicsExamples implements Runnable {
6 |
7 | private final AtomicInteger count = new AtomicInteger(0);
8 |
9 | public static void main(String[] args) throws InterruptedException {
10 | AtomicsExamples ae = new AtomicsExamples();
11 | Thread tA = new Thread(ae);
12 | Thread tB = new Thread(ae);
13 | tA.start();
14 | tB.start();
15 | tA.join();
16 | tB.join();
17 | }
18 |
19 |
20 | public void run() {
21 | String name = Thread.currentThread().getName();
22 | System.out.println("Starting on thread: "+ name);
23 | for (int i = 0; i < 100; i += 1) {
24 | try {
25 | Thread.sleep((long) (100 * Math.random()));
26 | System.out.println(name +": "+ count.incrementAndGet());
27 | } catch (InterruptedException e) {
28 | System.out.println("Interrupted on "+ name);
29 | }
30 | }
31 | System.out.println("Finished on "+ name);
32 | }
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/src/main/java/optjava/execution/ExecExamples.java:
--------------------------------------------------------------------------------
1 | package optjava.execution;
2 |
3 | import java.util.concurrent.*;
4 |
5 | public class ExecExamples {
6 |
7 | public static void main(String[] args) {
8 | ExecExamples ee = new ExecExamples();
9 | ee.run();
10 | }
11 |
12 | private void run() {
13 | Runnable r = () -> {
14 | String name = Thread.currentThread().getName();
15 | System.out.println("Starting on thread: "+ name);
16 | try {
17 | Thread.sleep((long) (5 * Math.random()));
18 | } catch (InterruptedException e) {
19 | System.out.println("Interrupted on "+ name);
20 | }
21 | System.out.println("Finished on "+ name);
22 | };
23 |
24 | ExecutorService ex = Executors.newSingleThreadExecutor();
25 | ex.execute(r);
26 | ex.execute(r);
27 | ex.execute(r);
28 | ex.execute(r);
29 | ex.execute(r);
30 |
31 | ex.execute(r);
32 | ex.execute(r);
33 | ex.execute(r);
34 | ex.execute(r);
35 | ex.execute(r);
36 |
37 | ex.execute(r);
38 | ex.execute(r);
39 | ex.execute(r);
40 | ex.execute(r);
41 | ex.execute(r);
42 |
43 | ex.execute(r);
44 | ex.execute(r);
45 | ex.execute(r);
46 | ex.execute(r);
47 | ex.execute(r);
48 |
49 | ex.shutdown();
50 |
51 | while (!ex.isTerminated()) {
52 | try {
53 | Thread.sleep(1);
54 | } catch (InterruptedException e) {
55 | e.printStackTrace();
56 | }
57 | }
58 | System.out.println("Exiting");
59 | }
60 |
61 | // private void run2() {
62 | // Callable r = () -> {
63 | // System.out.println("Hello Thread: "+ Thread.currentThread().getName());
64 | // Thread.sleep((long) (1000 * Math.random()));
65 | // return 17;
66 | // };
67 | //
68 | // ExecutorService x = null;
69 | // Future fi = x.submit(r);
70 | // x.submit(r);
71 | // x.submit(r);
72 | // x.submit(r);
73 | // x.submit(r);
74 | // while (!fi.isDone()) {
75 | // try {
76 | // System.out.print(".");
77 | // Thread.sleep(10);
78 | // } catch (InterruptedException e) {
79 | // e.printStackTrace();
80 | // }
81 | // }
82 | // // f.isDone() is TRUE - result has beeen generated
83 | // x.shutdown();
84 | // System.out.println();
85 | // try {
86 | // System.out.println("Result: "+ fi.get());
87 | // } catch (InterruptedException | ExecutionException e) {
88 | // e.printStackTrace();
89 | // }
90 | // }
91 | }
92 |
--------------------------------------------------------------------------------
/src/main/java/optjava/maps/BadMapExamples.java:
--------------------------------------------------------------------------------
1 | package optjava.maps;
2 |
3 | import java.util.Collections;
4 | import java.util.HashMap;
5 | import java.util.Map;
6 | import java.util.concurrent.ConcurrentHashMap;
7 |
8 | public class BadMapExamples {
9 | public static void main(String[] args) {
10 | Map map = new HashMap<>();
11 | int SIZE = 100;
12 |
13 | Runnable r1 = () -> {
14 | for (int i = 0; i < SIZE; i = i + 1) {
15 | map.put("t1" + i, "0");
16 | }
17 | System.out.println("Thread 1 done");
18 | };
19 | Runnable r2 = () -> {
20 | for (int i = 0; i < SIZE; i = i + 1) {
21 | map.put("t2" + i, "0");
22 | }
23 | System.out.println("Thread 2 done");
24 | };
25 | Thread t1 = new Thread(r1);
26 | Thread t2 = new Thread(r2);
27 | t1.start();
28 | t2.start();
29 | try {
30 | t1.join();
31 | t2.join();
32 | } catch (InterruptedException __) {
33 | System.err.println("Threads interrupted");
34 | }
35 | System.out.println("Diff: "+ (2 * SIZE - map.size()));
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/main/java/optjava/maps/DictMain.java:
--------------------------------------------------------------------------------
1 | package optjava.maps;
2 |
3 | public class DictMain {
4 |
5 | public static void main(String[] args) {
6 |
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/src/main/java/optjava/maps/Dictionary.java:
--------------------------------------------------------------------------------
1 | package optjava.maps;
2 |
3 | import java.util.*;
4 |
5 | public class Dictionary implements Map {
6 | private Node[] table = new Node[8];
7 | private int size;
8 |
9 | @Override
10 | public int size() {
11 | return size;
12 | }
13 |
14 | @Override
15 | public boolean isEmpty() {
16 | return size == 0;
17 | }
18 |
19 | @Override
20 | public String get(Object key) {
21 | if (key == null)
22 | return null;
23 | int hash = hash(key.hashCode());
24 | for (Node e = table[indexFor(hash, table.length)];
25 | e != null;
26 | e = e.next) {
27 | Object k;
28 | if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
29 | return e.value;
30 | }
31 | return null;
32 | }
33 |
34 | @Override
35 | public String put(String key, String value) {
36 | if (key == null)
37 | return null;
38 |
39 | int hash = hash(key.hashCode());
40 | int i = indexFor(hash, table.length);
41 | for (Node e = table[i]; e != null; e = e.next) {
42 | Object k;
43 | if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
44 | String oldValue = e.value;
45 | e.value = value;
46 | return oldValue;
47 | }
48 | }
49 |
50 | Node e = table[i];
51 | table[i] = new Node(hash, key, value, e);
52 |
53 | return null;
54 | }
55 |
56 | @Override
57 | public String remove(Object key) {
58 | int hash = (key == null) ? 0 : hash(key.hashCode());
59 | int i = indexFor(hash, table.length);
60 | Node prev = table[i];
61 | Node e = prev;
62 |
63 | while (e != null) {
64 | Node next = e.next;
65 | Object k;
66 | if (e.hash == hash &&
67 | ((k = e.key) == key || (key != null && key.equals(k)))) {
68 | size--;
69 | if (prev == e)
70 | table[i] = next;
71 | else
72 | prev.next = next;
73 | }
74 | prev = e;
75 | e = next;
76 | }
77 |
78 | return (e == null ? null : e.value);
79 | }
80 |
81 | @Override
82 | public void clear() {
83 | Node[] tab;
84 | if ((tab = table) != null && size > 0) {
85 | size = 0;
86 | for (int i = 0; i < tab.length; ++i)
87 | tab[i] = null;
88 | }
89 | }
90 |
91 | @Override
92 | public boolean containsKey(Object key) {
93 | int hash = (key == null) ? 0 : hash(key.hashCode());
94 | for (Node e = table[indexFor(hash, table.length)];
95 | e != null;
96 | e = e.next) {
97 | Object k;
98 | if (e.hash == hash &&
99 | ((k = e.key) == key || (key != null && key.equals(k))))
100 | return true;
101 | }
102 | return false;
103 | }
104 |
105 | @Override
106 | public boolean containsValue(Object value) {
107 | if (value == null)
108 | return false;
109 |
110 | Node[] tab = table;
111 | for (int i = 0; i < tab.length ; i++)
112 | for (Node e = tab[i]; e != null ; e = e.next)
113 | if (value.equals(e.value))
114 | return true;
115 | return false;
116 | }
117 |
118 | ////////////////////////////////
119 | // Unsupported
120 |
121 | @Override
122 | public void putAll(Map extends String, ? extends String> m) {
123 | throw new UnsupportedOperationException();
124 | }
125 |
126 | @Override
127 | public Set keySet() {
128 | throw new UnsupportedOperationException();
129 | }
130 |
131 | @Override
132 | public Collection values() {
133 | throw new UnsupportedOperationException();
134 | }
135 |
136 | @Override
137 | public Set> entrySet() {
138 | throw new UnsupportedOperationException();
139 | }
140 |
141 | ////////////////////////////////
142 |
143 | static int indexFor(int h, int length) {
144 | return h & (length-1);
145 | }
146 |
147 | static final int hash(Object key) {
148 | int h;
149 | return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
150 | }
151 |
152 | static class Node implements Entry {
153 | final int hash;
154 | final String key;
155 | String value;
156 | Node next;
157 |
158 | Node(int hash, String key, String value, Node next) {
159 | this.hash = hash;
160 | this.key = key;
161 | this.value = value;
162 | this.next = next;
163 | }
164 |
165 | public final String getKey() { return key; }
166 | public final String getValue() { return value; }
167 | public final String toString() { return key + "=" + value; }
168 |
169 | public final int hashCode() {
170 | return Objects.hashCode(key) ^ Objects.hashCode(value);
171 | }
172 |
173 | public final String setValue(String newValue) {
174 | String oldValue = value;
175 | value = newValue;
176 | return oldValue;
177 | }
178 |
179 | public final boolean equals(Object o) {
180 | if (o == this)
181 | return true;
182 | if (o instanceof Node) {
183 | Node e = (Node)o;
184 | if (Objects.equals(key, e.getKey()) &&
185 | Objects.equals(value, e.getValue()))
186 | return true;
187 | }
188 | return false;
189 | }
190 | }
191 | }
192 |
--------------------------------------------------------------------------------
/src/main/resources/basic.bc:
--------------------------------------------------------------------------------
1 | public int increment();
2 | Code:
3 | 0: aload_0
4 | 1: aload_0
5 | 2: getfield #2 // Field i:I
6 | 5: iconst_1
7 | 6: iadd
8 | 7: dup_x1
9 | 8: putfield #2 // Field i:I
10 | 11: ireturn
11 |
12 |
--------------------------------------------------------------------------------
/src/main/resources/example-alloc.bat:
--------------------------------------------------------------------------------
1 | CLS
2 | ECHO OFF
3 | ECHO ####################################
4 | ECHO Please run with Administrator rights
5 | ECHO ####################################
6 | ECHO .
7 | set CP=optimizing-java-1.0.0-SNAPSHOT.jar
8 | rem set GC_LOG_OPTS="-Xloggc:gc-alloc-`date +%s`.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps -XX:+PrintTenuringDistribution"
9 | set GC_LOG_OPTS=-Xloggc:gc-alloc-%todaysdate%.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps -XX:+PrintTenuringDistribution
10 | set MEM_OPTS=-Xmx1G
11 |
12 | REM DATE
13 | FOR /F "TOKENS=1 eol=/ DELIMS=/ " %%A IN ('DATE/T') DO SET dd=%%A
14 | FOR /F "TOKENS=1,2 eol=/ DELIMS=/ " %%A IN ('DATE/T') DO SET mm=%%B
15 | FOR /F "TOKENS=1,2,3 eol=/ DELIMS=/ " %%A IN ('DATE/T') DO SET yyyy=%%C
16 | SET todaysdate=%yyyy%_%mm%_%dd%
17 | REM EPOCH TIME
18 | REM Would be nice...
19 | REM Maybe?.. http://gnuwin32.sourceforge.net/packages/coreutils.htm
20 |
21 | if exist "%JAVA_HOM%" goto opt1 ELSE goto opt2
22 |
23 | :opt1
24 | set JAVA_CMD=%JAVA_HOME%\bin\
25 | goto exec
26 |
27 | :opt2
28 | if exist "%JAVA_BIN%" goto opt3 ELSE goto end
29 | goto exec
30 |
31 | :opt3
32 | set JAVA_CMD=JAVA_BIN
33 | goto exec
34 |
35 | :end
36 | echo "For this command to run, either $JAVA_HOME must be set, or java must be in the path."
37 | exit
38 |
39 | :exec
40 | ECHO "%JAVA_CMD%"
41 | cd "%JAVA_CMD%"
42 | java -cp %CP% %GC_LOG_OPTS% %MEM_OPTS% optjava.ModelAllocator"
43 |
44 |
45 |
--------------------------------------------------------------------------------
/src/main/resources/example-alloc.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # Simple script for running the model toy allocator
4 |
5 | CP=./target/optimizing-java-1.0.0-SNAPSHOT.jar
6 |
7 | GC_LOG_OPTS="-Xloggc:gc-alloc-`date +%s`.log -XX:+PrintCompilation -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps -XX:+PrintTenuringDistribution"
8 |
9 | MEM_OPTS="-Xmx1G"
10 |
11 | JAVA_BIN=`which java`
12 |
13 | if [ $JAVA_HOME ]; then
14 | JAVA_CMD=$JAVA_HOME/bin/java
15 | elif [ $JAVA_BIN ]; then
16 | JAVA_CMD=$JAVA_BIN
17 | else
18 | echo "For this command to run, either $JAVA_HOME must be set, or java must be in the path."
19 | exit 1
20 | fi
21 |
22 | exec $JAVA_CMD -cp $CP $GC_LOG_OPTS $MEM_OPTS optjava.ModelAllocator
23 |
--------------------------------------------------------------------------------
/src/main/resources/scratch.bc:
--------------------------------------------------------------------------------
1 | public void run();
2 | Code:
3 | A0: aload_0
4 | A1: getfield #2 // Field shutdown:Z
5 | A4: ifne 10
6 |
7 | // Main execution goes here.
8 |
9 | A7: goto 0
10 | A10: return
11 |
12 | public void shutdown();
13 | Code:
14 | 0: aload_0
15 | 1: iconst_1
16 | 2: putfield #2 // Field shutdown:Z
17 | 5: return
18 |
--------------------------------------------------------------------------------
/src/main/resources/synchronized.bc:
--------------------------------------------------------------------------------
1 | A0: aload_0
2 | A1: aload_0
3 | XXXXXXXXXXXXXXX monitorenter
4 | A2: getfield #2 // Field i:I *** READ 17
5 | A5: iconst_1
6 | A6: iadd
7 | A7: dup_x1
8 | ///////////////////////////////////////////// Context Switch
9 |
10 | B0: aload_0
11 | B1: aload_0
12 | XXXXXXXXXXXXXXX monitorenter BLOCK BLOCK BLOCK
13 |
14 | ///////////////////////////////////////////// Context Switch
15 |
16 | A8: putfield #2 // Field i:I *** WRITE 18
17 | XXXXXXXXXXXXXXX monitorexit
18 | A11: ireturn
19 |
20 | ///////////////////////////////////////////// Context Switch
21 |
22 |
23 | XXXXXXXXXXXXXXX monitorenter
24 | B2: getfield #2 // Field i:I *** READ 18
25 | B5: iconst_1
26 | B6: iadd
27 | B7: dup_x1
28 | B8: putfield #2 // Field i:I *** WRITE 19
29 | XXXXXXXXXXXXXXX monitorexit
30 | B11: ireturn
31 |
32 |
33 |
--------------------------------------------------------------------------------
/src/main/resources/ta_tb_basic.bc:
--------------------------------------------------------------------------------
1 | A0: aload_0
2 | A1: aload_0
3 | A2: getfield #2 // Field i:I : 17
4 |
5 | // A:[17, this]
6 |
7 | ///////////////////////////// Context Switch
8 |
9 | // B:[]
10 |
11 | B0: aload_0
12 | B1: aload_0
13 | B2: getfield #2 // Field i:I : 17
14 |
15 | // B:[17, this]
16 |
17 | B5: iconst_1
18 | // B:[1, 17, this]
19 |
20 | B6: iadd
21 | // B:[18, this]
22 |
23 | B7: dup_x1
24 | // B:[18, this, 18]
25 |
26 | B8: putfield #2 // Field i:I : 18
27 | // B:[18]
28 |
29 | B11: ireturn
30 |
31 | ///////////////////////////// Context Switch
32 |
33 |
34 | A5: iconst_1
35 | // A:[1, 17, this]
36 |
37 | A6: iadd
38 | // A:[18, this]
39 |
40 | A7: dup_x1
41 | // A:[18, this, 18]
42 |
43 | A8: putfield #2 // Field i:I : 18
44 | // B:[18]
45 |
46 | A11: ireturn
47 |
48 |
--------------------------------------------------------------------------------
/src/test/java/optjava/ch11/SimpleTestSimpleDict.java:
--------------------------------------------------------------------------------
1 | package optjava.ch11;
2 |
3 | import static org.junit.Assert.*;
4 | import org.junit.Test;
5 |
6 | /**
7 | *
8 | * @author ben
9 | */
10 | public class SimpleTestSimpleDict {
11 |
12 | @Test
13 | public void empty_dicts_are_empty() {
14 | SimpleDict sd = new SimpleDict(16);
15 | assertTrue("Newly ctor'd object should be empty", sd.isEmpty());
16 | assertEquals("Newly ctor'd object should be empty", 0, sd.size());
17 | String rem = sd.remove("Foo");
18 | assertNull("Removed value should be null", rem);
19 | }
20 |
21 | @Test
22 | public void simple_put_and_get() {
23 | SimpleDict sd = new SimpleDict(16);
24 | sd.put("Foo", "Bar");
25 | assertEquals(1, sd.size());
26 | assertFalse("Dict with items should be non-empty", sd.isEmpty());
27 | assertEquals("get should retrieve Bar", "Bar", sd.get("Foo"));
28 | sd.put("Foo", "Bar");
29 | assertEquals(1, sd.size());
30 | assertTrue("Should contain Foo as a key", sd.containsKey("Foo"));
31 | assertFalse("Should not contain Fox as a key", sd.containsKey("Fox"));
32 | assertEquals("get should retrieve Bar", "Bar", sd.get("Foo"));
33 | sd.put("Foo", "Baz");
34 | assertEquals(1, sd.size());
35 | assertEquals("get should retrieve Baz", "Baz", sd.get("Foo"));
36 | sd.put("Fox", "Box");
37 | assertEquals(2, sd.size());
38 | assertEquals("get(Fox) should retrieve Box", "Box", sd.get("Fox"));
39 | }
40 |
41 | @Test
42 | public void simple_put_and_remove() {
43 | SimpleDict sd = new SimpleDict(16);
44 | sd.put("Foo", "Baz");
45 | sd.put("Fox", "Box");
46 | assertEquals(2, sd.size());
47 | String rem = sd.remove("Foo");
48 | assertEquals("Removed value should be Baz", "Baz", rem);
49 | assertEquals(1, sd.size());
50 |
51 | }
52 |
53 | @Test
54 | public void remove_on_empty_gives_null() {
55 | SimpleDict sd = new SimpleDict(16);
56 | String rem = sd.remove("Foo");
57 | assertNull("Removed value should be null", rem);
58 | assertEquals(0, sd.size());
59 | }
60 |
61 | @Test
62 | public void add_1_and_remove_on_empty_gives_initial_add() {
63 | SimpleDict sd = new SimpleDict(16);
64 | sd.put("Foo", "Bar");
65 | String rem = sd.remove("Foo");
66 | assertEquals("Removed value should be Bar", "Bar", rem);
67 | assertEquals(0, sd.size());
68 | }
69 |
70 | @Test
71 | public void add_a_and_b_then_remove_b_gives_initial_vb() {
72 | // SimpleDict sd = new SimpleDict(16);
73 | // sd.put("Foo", "Bar");
74 | // String rem = sd.remove("Foo");
75 | // assertEquals("Removed value should be Bar", "Bar", rem);
76 | // assertEquals(0, sd.size());
77 | }
78 |
79 | @Test
80 | public void capacity_ctor() {
81 | SimpleDict sd = new SimpleDict(16);
82 | assertEquals("Default capacity is 16", 16, sd.capacity());
83 | sd = new SimpleDict(8);
84 | assertEquals("Expected capacity is 8", 8, sd.capacity());
85 | sd = new SimpleDict(17);
86 | assertEquals("Expected capacity is 16", 16, sd.capacity());
87 | sd = new SimpleDict(-128);
88 | assertEquals("Expected capacity is 16", 16, sd.capacity());
89 | sd = new SimpleDict(1);
90 | assertEquals("Expected capacity is 1", 1, sd.capacity());
91 | sd = new SimpleDict(256);
92 | assertEquals("Expected capacity is 256", 256, sd.capacity());
93 | sd = new SimpleDict(1023);
94 | assertEquals("Expected capacity is 512", 512, sd.capacity());
95 | }
96 |
97 | }
98 |
--------------------------------------------------------------------------------