Skip to content

Commit fc4cc58

Browse files
committed
Modifiable JsonArray
1 parent 499f5d4 commit fc4cc58

File tree

6 files changed

+245
-9
lines changed

6 files changed

+245
-9
lines changed

pom_parent.xml

+5
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,11 @@
6565
<version>${jakarta.json.version}</version>
6666
<scope>provided</scope>
6767
</dependency>
68+
<dependency>
69+
<groupId>com.google.guava</groupId>
70+
<artifactId>guava</artifactId>
71+
<version>31.0.1-jre</version>
72+
</dependency>
6873
<!-- Tests -->
6974
<dependency>
7075
<groupId>org.glassfish</groupId>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
package com.apicatalog.jsonld;
2+
3+
import com.google.common.hash.BloomFilter;
4+
import com.google.common.hash.Funnels;
5+
import jakarta.json.JsonArray;
6+
import jakarta.json.JsonNumber;
7+
import jakarta.json.JsonObject;
8+
import jakarta.json.JsonString;
9+
import jakarta.json.JsonValue;
10+
11+
import java.util.AbstractList;
12+
import java.util.List;
13+
14+
/**
15+
* Don't use this class in production. It is only for testing the performance of a modifiable
16+
* JsonArray. It is essentially a modified copy of the Glassfish JsonArray implementation.
17+
*/
18+
public class ModifiableJsonArray extends AbstractList<JsonValue> implements JsonArray {
19+
private final List<JsonValue> valueList;
20+
private int hashCode;
21+
private BloomFilter<Integer> filter;
22+
23+
24+
public ModifiableJsonArray(List<JsonValue> valueList) {
25+
this.valueList = valueList;
26+
if (valueList.size() > 1000) {
27+
filter = BloomFilter.create(
28+
Funnels.integerFunnel(),
29+
5000,
30+
0.01);
31+
for (JsonValue value : valueList) {
32+
filter.put(value.hashCode());
33+
}
34+
}
35+
36+
}
37+
38+
@Override
39+
public int size() {
40+
return valueList.size();
41+
}
42+
43+
@Override
44+
public JsonObject getJsonObject(int index) {
45+
return (JsonObject) valueList.get(index);
46+
}
47+
48+
@Override
49+
public JsonArray getJsonArray(int index) {
50+
return (JsonArray) valueList.get(index);
51+
}
52+
53+
@Override
54+
public JsonNumber getJsonNumber(int index) {
55+
return (JsonNumber) valueList.get(index);
56+
}
57+
58+
@Override
59+
public JsonString getJsonString(int index) {
60+
return (JsonString) valueList.get(index);
61+
}
62+
63+
@Override
64+
@SuppressWarnings("unchecked")
65+
public <T extends JsonValue> List<T> getValuesAs(Class<T> clazz) {
66+
return (List<T>) valueList;
67+
}
68+
69+
@Override
70+
public String getString(int index) {
71+
return getJsonString(index).getString();
72+
}
73+
74+
@Override
75+
public String getString(int index, String defaultValue) {
76+
try {
77+
return getString(index);
78+
} catch (Exception e) {
79+
return defaultValue;
80+
}
81+
}
82+
83+
@Override
84+
public int getInt(int index) {
85+
return getJsonNumber(index).intValue();
86+
}
87+
88+
@Override
89+
public int getInt(int index, int defaultValue) {
90+
try {
91+
return getInt(index);
92+
} catch (Exception e) {
93+
return defaultValue;
94+
}
95+
}
96+
97+
@Override
98+
public boolean getBoolean(int index) {
99+
JsonValue jsonValue = get(index);
100+
if (jsonValue == JsonValue.TRUE) {
101+
return true;
102+
} else if (jsonValue == JsonValue.FALSE) {
103+
return false;
104+
} else {
105+
throw new ClassCastException();
106+
}
107+
}
108+
109+
@Override
110+
public boolean getBoolean(int index, boolean defaultValue) {
111+
try {
112+
return getBoolean(index);
113+
} catch (Exception e) {
114+
return defaultValue;
115+
}
116+
}
117+
118+
@Override
119+
public boolean isNull(int index) {
120+
return valueList.get(index).equals(JsonValue.NULL);
121+
}
122+
123+
@Override
124+
public ValueType getValueType() {
125+
return ValueType.ARRAY;
126+
}
127+
128+
@Override
129+
public JsonValue get(int index) {
130+
return valueList.get(index);
131+
}
132+
133+
public boolean add(JsonValue value) {
134+
if (filter != null) {
135+
filter.put(value.hashCode());
136+
}
137+
return valueList.add(value);
138+
}
139+
140+
@Override
141+
public boolean contains(Object o) {
142+
if(valueList.isEmpty()) return false;
143+
if(o == null) return false;
144+
145+
if (filter == null && valueList.size() > 100) {
146+
filter = BloomFilter.create(
147+
Funnels.integerFunnel(),
148+
100000,
149+
0.01);
150+
for (JsonValue value : valueList) {
151+
filter.put(value.hashCode());
152+
}
153+
}
154+
if (filter != null) {
155+
boolean mightContain = filter.mightContain(o.hashCode());
156+
if (!mightContain) {
157+
return false;
158+
}
159+
}
160+
161+
int oHashCode = o.hashCode();
162+
if(valueList.size() == 1){
163+
JsonValue jsonValue = valueList.get(0);
164+
if(oHashCode == jsonValue.hashCode()){
165+
return jsonValue.equals(o);
166+
}
167+
}
168+
169+
for (JsonValue value : valueList) {
170+
if (value.hashCode() == oHashCode) {
171+
if (value.equals(o)) {
172+
return true;
173+
}
174+
}
175+
}
176+
return false;
177+
178+
}
179+
180+
@Override
181+
public int hashCode() {
182+
if (hashCode == 0) {
183+
hashCode = super.hashCode();
184+
}
185+
return hashCode;
186+
}
187+
188+
@Override
189+
public String toString() {
190+
return "ModifiableJsonArray{" +
191+
"valueList=" + valueList +
192+
'}';
193+
}
194+
195+
@Override
196+
public JsonArray asJsonArray() {
197+
return this;
198+
}
199+
}

src/main/java/com/apicatalog/jsonld/compaction/UriCompaction.java

+3
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
import java.net.URI;
1919
import java.util.ArrayList;
2020
import java.util.Collection;
21+
import java.util.HashMap;
22+
import java.util.Map;
2123
import java.util.Map.Entry;
2224
import java.util.Objects;
2325
import java.util.Optional;
@@ -55,6 +57,7 @@ public final class UriCompaction {
5557
private JsonValue value;
5658
private boolean vocab;
5759
private boolean reverse;
60+
private Map<String, URI> uriCache = new HashMap<>();
5861

5962
private UriCompaction(final ActiveContext activeContext) {
6063
this.activeContext = activeContext;

src/main/java/com/apicatalog/jsonld/flattening/NodeMapBuilder.java

+13-2
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616
package com.apicatalog.jsonld.flattening;
1717

18+
import java.util.ArrayList;
1819
import java.util.LinkedHashMap;
1920
import java.util.LinkedHashSet;
2021
import java.util.List;
@@ -25,6 +26,7 @@
2526

2627
import com.apicatalog.jsonld.JsonLdError;
2728
import com.apicatalog.jsonld.JsonLdErrorCode;
29+
import com.apicatalog.jsonld.ModifiableJsonArray;
2830
import com.apicatalog.jsonld.json.JsonProvider;
2931
import com.apicatalog.jsonld.json.JsonUtils;
3032
import com.apicatalog.jsonld.lang.BlankNode;
@@ -299,9 +301,14 @@ private void handle6_6(String id) {
299301
if (noneMatch(activePropertyValue, reference)) {
300302
JsonArray build;
301303
if (activePropertyValue.isEmpty()) {
302-
build = JsonProvider.instance().createArrayBuilder(List.of(reference)).build();
304+
build = new ModifiableJsonArray(new ArrayList<>(List.of(reference)));
303305
} else {
304-
build = JsonProvider.instance().createArrayBuilder(activePropertyValue).add(reference).build();
306+
if(activePropertyValue instanceof ModifiableJsonArray) {
307+
build = activePropertyValue;
308+
} else {
309+
build = new ModifiableJsonArray(new ArrayList<>(activePropertyValue));
310+
}
311+
build.add(reference);
305312
}
306313
nodeMap.set(activeGraph, activeSubject, activeProperty, build);
307314
}
@@ -429,6 +436,10 @@ private void handle6_12(Map<String, JsonValue> elementObject, String id) throws
429436

430437
private static boolean noneMatch(JsonArray activePropertyValue, JsonStructure reference) {
431438

439+
if(activePropertyValue instanceof ModifiableJsonArray) {
440+
return !activePropertyValue.contains(reference);
441+
}
442+
432443
if (activePropertyValue.isEmpty()) {
433444
return true;
434445
}

src/test/java/com/apicatalog/jsonld/benchmark/BasicProcessingAlgorithmsBenchmark.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ public static void main(String[] args) throws URISyntaxException, JsonLdError {
5151
for (int i = 0; i < 20; i++) {
5252
System.out.println("Iteration " + i);
5353
loadingBenchmark.setUp();
54-
loadingBenchmark.flatten();
54+
loadingBenchmark.compact();
5555
}
5656
}
5757

src/test/java/com/apicatalog/jsonld/benchmark/README.md

+24-6
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,15 @@ Benchmark Mode Cnt Score Error Units
44
LoadingBenchmark.toRdfApiGet avgt 5 5859.114 ± 522.500 ms/op
55
```
66

7+
```
8+
Benchmark Mode Cnt Score Error Units
9+
BasicProcessingAlgorithmsBenchmark.compact avgt 5 1896.694 ± 81.070 ms/op
10+
BasicProcessingAlgorithmsBenchmark.expand avgt 5 673.813 ± 9.570 ms/op
11+
BasicProcessingAlgorithmsBenchmark.flatten avgt 5 3586.524 ± 124.667 ms/op
12+
13+
```
14+
15+
716
#### OOMBenchmark.toRdfApiGet
817
```
918
Iteration 1: 7466.977 ms/op
@@ -15,14 +24,23 @@ Iteration 3: Terminating due to java.lang.OutOfMemoryError: Java heap space
1524
## Current results
1625
```
1726
Benchmark Mode Cnt Score Error Units
18-
LoadingBenchmark.toRdfApiGet avgt 5 3363.833 ± 448.450 ms/op
27+
LoadingBenchmark.toRdfApiGet avgt 5 1998.111 ± 135.659 ms/op
28+
```
29+
30+
```
31+
Benchmark Mode Cnt Score Error Units
32+
BasicProcessingAlgorithmsBenchmark.compact avgt 5 1541.122 ± 33.854 ms/op
33+
BasicProcessingAlgorithmsBenchmark.expand avgt 5 376.108 ± 17.546 ms/op
34+
BasicProcessingAlgorithmsBenchmark.flatten avgt 5 816.228 ± 28.191 ms/op
1935
```
2036

2137
#### OOMBenchmark.toRdfApiGet
2238
```
23-
Iteration 1: 3271.161 ms/op
24-
Iteration 2: 1867.856 ms/op
25-
Iteration 3: 1798.996 ms/op
26-
Iteration 4: 1805.217 ms/op
27-
Iteration 5: Terminating due to java.lang.OutOfMemoryError: Java heap space
39+
Iteration 1: 2420.124 ms/op
40+
Iteration 2: 1260.448 ms/op
41+
Iteration 3: 1204.927 ms/op
42+
Iteration 4: 1191.012 ms/op
43+
Iteration 5: 1193.424 ms/op
44+
Iteration 6: 1209.333 ms/op
45+
Iteration 7: Terminating due to java.lang.OutOfMemoryError: Java heap space
2846
```

0 commit comments

Comments
 (0)