Skip to content

Commit 0cedcd6

Browse files
authored
Merge pull request #1 from lewysDavies/master
Update fork
2 parents c907446 + 3f8ea42 commit 0cedcd6

File tree

5 files changed

+143
-65
lines changed

5 files changed

+143
-65
lines changed

README.md

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,13 @@ C: 11.765% | 11.769%
2525
```
2626

2727
# Performance
28-
Get performance has been significantly improved in comparison to my previous map implementation. This has been achieved with custom compared TreeSets.
29-
0.314ms to just 0.004.
28+
Get performance has been significantly improved in comparison to my previous map implementation. This has been achieved with custom compared TreeSets.
3029
```
31-
Benchmark Mode Cnt Score Error Units
32-
new_collectionAddSingle avgt 10 0.002 ± 0.001 s/op
33-
new_collectionGet avgt 10 0.004 ± 0.001 s/op
34-
old_mapAddSingle avgt 10 0.001 ± 0.001 s/op
35-
old_mapGet avgt 10 0.314 ± 0.069 s/op
30+
Benchmark Mode Cnt Score Error Units
31+
BenchmarkProbability.collectionAddSingle avgt 5 501.688 ± 33.925 ns/op
32+
BenchmarkProbability.collectionGet avgt 5 69.373 ± 2.198 ns/op
33+
BenchmarkProbability.mapAddSingle avgt 5 25809.712 ± 984.980 ns/op
34+
BenchmarkProbability.mapGet avgt 5 902.414 ± 22.388 ns/op
3635
```
3736

3837
# Installation
@@ -50,7 +49,7 @@ or for the fancy users, you could use Maven:<br>
5049
<dependency>
5150
<groupId>com.github.lewysDavies</groupId>
5251
<artifactId>Java-Probability-Collection</artifactId>
53-
<version>v0.6</version>
52+
<version>v0.8</version>
5453
</dependency>
5554
```
5655
**Maven Shade This Dependency:**

pom.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@
55

66
<groupId>com.lewdev</groupId>
77
<artifactId>probability-lib</artifactId>
8-
<version>0.5</version>
8+
<version>0.8</version>
99
<packaging>jar</packaging>
1010

1111
<name>probability-lib</name>
12-
<url>http://example.com</url>
12+
<url>lewdev.uk</url>
1313

1414
<properties>
1515
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

src/main/java/com/lewdev/probabilitylib/ProbabilityCollection.java

Lines changed: 55 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,32 @@
1+
/*
2+
* Copyright (c) 2020 Lewys Davies
3+
*
4+
* Permission is hereby granted, free of charge, to any person obtaining a copy
5+
* of this software and associated documentation files (the "Software"), to deal
6+
* in the Software without restriction, including without limitation the rights
7+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8+
* copies of the Software, and to permit persons to whom the Software is
9+
* furnished to do so, subject to the following conditions:
10+
*
11+
* The above copyright notice and this permission notice shall be included in all
12+
* copies or substantial portions of the Software.
13+
*
14+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20+
* SOFTWARE.
21+
*/
122
package com.lewdev.probabilitylib;
223

324
import java.util.Comparator;
425
import java.util.Iterator;
26+
import java.util.NavigableSet;
527
import java.util.Objects;
28+
import java.util.SplittableRandom;
629
import java.util.TreeSet;
7-
import java.util.concurrent.ThreadLocalRandom;
830

931
/**
1032
* ProbabilityCollection for retrieving random elements based on probability.
@@ -23,24 +45,22 @@
2345
* </ul>
2446
*
2547
* @author Lewys Davies
26-
* @version 0.6
48+
* @version 0.8
2749
*
2850
* @param <E> Type of elements
2951
*/
3052
public class ProbabilityCollection<E> {
31-
32-
protected final Comparator<ProbabilitySetElement<E>> comparator =
33-
(o1, o2)-> Integer.compare(o1.getIndex(), o2.getIndex());
3453

35-
private final TreeSet<ProbabilitySetElement<E>> collection;
54+
private final NavigableSet<ProbabilitySetElement<E>> collection;
55+
private final SplittableRandom random = new SplittableRandom();
3656

3757
private int totalProbability;
3858

3959
/**
4060
* Construct a new Probability Collection
4161
*/
4262
public ProbabilityCollection() {
43-
this.collection = new TreeSet<>(this.comparator);
63+
this.collection = new TreeSet<>(Comparator.comparingInt(ProbabilitySetElement::getIndex));
4464
this.totalProbability = 0;
4565
}
4666

@@ -52,28 +72,28 @@ public int size() {
5272
}
5373

5474
/**
55-
* @return Collection contains no elements
75+
* @return True if collection contains no elements, else False
5676
*/
5777
public boolean isEmpty() {
5878
return this.collection.isEmpty();
5979
}
6080

6181
/**
62-
* @param object
63-
* @return True if the collection contains the object, else False
64-
* @throws IllegalArgumentException if object null
82+
* @param <E> object
83+
* @return True if collection contains the object, else False
84+
* @throws IllegalArgumentException if object is null
6585
*/
6686
public boolean contains(E object) {
6787
if(object == null) {
68-
throw new IllegalArgumentException("Cannot check if null object is contained in a collection");
88+
throw new IllegalArgumentException("Cannot check if null object is contained in this collection");
6989
}
7090

7191
return this.collection.stream()
7292
.anyMatch(entry -> entry.getObject().equals(object));
7393
}
7494

7595
/**
76-
* @return Iterator over collection
96+
* @return Iterator over this collection
7797
*/
7898
public Iterator<ProbabilitySetElement<E>> iterator() {
7999
return this.collection.iterator();
@@ -82,7 +102,7 @@ public Iterator<ProbabilitySetElement<E>> iterator() {
82102
/**
83103
* Add an object to this collection
84104
*
85-
* @param object. Not null.
105+
* @param <E> object. Not null.
86106
* @param probability share. Must be greater than 0.
87107
*
88108
* @throws IllegalArgumentException if object is null
@@ -97,33 +117,35 @@ public void add(E object, int probability) {
97117
throw new IllegalArgumentException("Probability must be greater than 0");
98118
}
99119

100-
this.collection.add(new ProbabilitySetElement<E>(object, probability));
101-
this.totalProbability += probability;
120+
ProbabilitySetElement<E> entry = new ProbabilitySetElement<E>(object, probability);
121+
entry.setIndex(this.totalProbability + 1);
102122

103-
this.updateIndexes();
123+
this.collection.add(entry);
124+
this.totalProbability += probability;
104125
}
105126

106127
/**
107128
* Remove a object from this collection
108129
*
109-
* @param object
130+
* @param <E> object
110131
* @return True if object was removed, else False.
111132
*
112-
* @throws IllegalArgumentException if object null
133+
* @throws IllegalArgumentException if object is null
113134
*/
114135
public boolean remove(E object) {
115136
if(object == null) {
116137
throw new IllegalArgumentException("Cannot remove null object");
117138
}
118139

119140
Iterator<ProbabilitySetElement<E>> it = this.iterator();
120-
boolean removed = it.hasNext();
141+
boolean removed = false;
121142

122143
while(it.hasNext()) {
123144
ProbabilitySetElement<E> entry = it.next();
124145
if(entry.getObject().equals(object)) {
125146
this.totalProbability -= entry.getProbability();
126147
it.remove();
148+
removed = true;
127149
}
128150
}
129151

@@ -132,6 +154,14 @@ public boolean remove(E object) {
132154
return removed;
133155
}
134156

157+
/**
158+
* Remove all objects from this collection
159+
*/
160+
public void clear() {
161+
this.collection.clear();
162+
this.totalProbability = 0;
163+
}
164+
135165
/**
136166
* Get a random object from this collection, based on probability.
137167
*
@@ -141,11 +171,11 @@ public boolean remove(E object) {
141171
*/
142172
public E get() {
143173
if(this.isEmpty()) {
144-
throw new IllegalStateException("Cannot get an element out of a empty set");
174+
throw new IllegalStateException("Cannot get an object out of a empty collection");
145175
}
146176

147177
ProbabilitySetElement<E> toFind = new ProbabilitySetElement<>(null, 0);
148-
toFind.setIndex(ThreadLocalRandom.current().nextInt(1, this.totalProbability + 1));
178+
toFind.setIndex(this.random.nextInt(1, this.totalProbability + 1));
149179

150180
return Objects.requireNonNull(this.collection.floor(toFind).getObject());
151181
}
@@ -164,7 +194,7 @@ public final int getTotalProbability() {
164194
* We then only need to store the start index of each element,
165195
* as we make use of the TreeSet#floor
166196
*/
167-
private void updateIndexes() {
197+
private final void updateIndexes() {
168198
int previousIndex = 0;
169199

170200
for(ProbabilitySetElement<E> entry : this.collection) {
@@ -190,7 +220,7 @@ final static class ProbabilitySetElement<T> {
190220
private int index;
191221

192222
/**
193-
* @param object
223+
* @param <T> object
194224
* @param probability
195225
*/
196226
protected ProbabilitySetElement(T object, int probability) {
@@ -199,7 +229,7 @@ protected ProbabilitySetElement(T object, int probability) {
199229
}
200230

201231
/**
202-
* @return The actual object
232+
* @return <T> The actual object
203233
*/
204234
public final T getObject() {
205235
return this.object;
Lines changed: 27 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,27 @@
11
package com.lewdev.probabilitylib;
22

3-
import java.util.HashMap;
4-
import java.util.Map;
53
import java.util.concurrent.TimeUnit;
64

75
import org.openjdk.jmh.annotations.Benchmark;
86
import org.openjdk.jmh.annotations.BenchmarkMode;
9-
import org.openjdk.jmh.annotations.Measurement;
7+
import org.openjdk.jmh.annotations.Fork;
8+
import org.openjdk.jmh.annotations.Level;
109
import org.openjdk.jmh.annotations.Mode;
10+
import org.openjdk.jmh.annotations.OutputTimeUnit;
1111
import org.openjdk.jmh.annotations.Scope;
1212
import org.openjdk.jmh.annotations.Setup;
1313
import org.openjdk.jmh.annotations.State;
14-
import org.openjdk.jmh.annotations.Timeout;
15-
import org.openjdk.jmh.annotations.Warmup;
14+
import org.openjdk.jmh.annotations.TearDown;
1615
import org.openjdk.jmh.infra.Blackhole;
1716
import org.openjdk.jmh.runner.Runner;
1817
import org.openjdk.jmh.runner.RunnerException;
1918
import org.openjdk.jmh.runner.options.Options;
2019
import org.openjdk.jmh.runner.options.OptionsBuilder;
2120

2221
@BenchmarkMode(Mode.AverageTime)
22+
@OutputTimeUnit(TimeUnit.NANOSECONDS)
2323
@State(Scope.Benchmark)
24-
@Warmup(iterations = 5, time = 5)
25-
@Timeout(time = 25, timeUnit = TimeUnit.SECONDS)
26-
@Measurement(iterations = 10, time = 2)
24+
@Fork(value = 2, jvmArgs = {"-Xms2G", "-Xmx2G"})
2725
public class BenchmarkProbability {
2826

2927
public static void main(String[] args) throws RunnerException {
@@ -35,52 +33,51 @@ public static void main(String[] args) throws RunnerException {
3533
new Runner(opt).run();
3634
}
3735

38-
private final int elements = 10_000;
36+
public int elements = 1_000;
3937

40-
ProbabilityMap<Integer> map = new ProbabilityMap<>();
41-
ProbabilityCollection<Integer> collection = new ProbabilityCollection<>();
38+
public int toAdd = elements + 1;
39+
public int toAddProb = 10;
4240

43-
private Map<Integer, Integer> addAllTest = new HashMap<>();
41+
private ProbabilityMap<Integer> map;
42+
private ProbabilityCollection<Integer> collection;
4443

45-
@Setup
44+
@Setup(Level.Iteration)
4645
public void setup() {
46+
this.map = new ProbabilityMap<>();
47+
this.collection = new ProbabilityCollection<>();
48+
4749
for(int i = 0; i < elements; i++) {
4850
map.add(i, 1);
4951
collection.add(i, 1);
5052
}
51-
52-
for(int i = elements; i < elements * 2; i++) {
53-
addAllTest.put(i, 1);
54-
}
5553
}
5654

57-
@Benchmark
58-
public void mapAddSingle(Blackhole bh) {
59-
boolean added = this.map.add(25000, 1);
60-
bh.consume(added);
55+
@TearDown(Level.Iteration)
56+
public void tearDown() {
57+
this.map.clear();
58+
this.collection.clear();
59+
60+
this.map = null;
61+
this.collection = null;
6162
}
6263

6364
@Benchmark
64-
public void mapAddAll() {
65-
map.addAll(addAllTest);
65+
public void mapAddSingle() {
66+
this.map.add(toAdd, toAddProb);
6667
}
6768

6869
@Benchmark
6970
public void collectionAddSingle() {
70-
this.collection.add(25000, 1);
71+
this.collection.add(toAdd, toAddProb);
7172
}
7273

7374
@Benchmark
7475
public void mapGet(Blackhole bh) {
75-
for(int i = 0; i < elements * 2; i++) {
76-
bh.consume(map.get());
77-
}
76+
bh.consume(this.map.get());
7877
}
7978

8079
@Benchmark
8180
public void collectionGet(Blackhole bh) {
82-
for(int i = 0; i < elements * 2; i++) {
83-
bh.consume(collection.get());
84-
}
81+
bh.consume(this.collection.get());
8582
}
8683
}

0 commit comments

Comments
 (0)