├── .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 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 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 | --------------------------------------------------------------------------------