23
23
import java .nio .file .Paths ;
24
24
import java .nio .file .StandardOpenOption ;
25
25
import java .util .ArrayList ;
26
+ import java .util .Comparator ;
26
27
import java .util .HashMap ;
28
+ import java .util .HashSet ;
27
29
import java .util .List ;
28
30
import java .util .Map ;
31
+ import java .util .Set ;
32
+ import java .util .concurrent .ConcurrentHashMap ;
29
33
import java .util .stream .Collectors ;
30
34
31
35
/**
@@ -35,11 +39,22 @@ public class CalculateAverage_adriacabeza {
35
39
36
40
private static final Path FILE_PATH = Paths .get ("./measurements.txt" );
37
41
public static final int CITY_NAME_MAX_CHARACTERS = 128 ;
42
+ private static final int N_PROCESSORS = Runtime .getRuntime ().availableProcessors ();
43
+ private static final int DJB2_INIT = 5381 ;
44
+ private static final Map <Integer , String > cityMap = new ConcurrentHashMap <>(10_000 , 1 , N_PROCESSORS );
38
45
39
46
/**
40
47
* Represents result containing a HashMap with city as key and ResultRow as value.
41
48
*/
42
49
private static class Result {
50
+ public void addStation (int hash , int value ) {
51
+ resultMap .put (hash , new StationData (value ));
52
+ }
53
+
54
+ public StationData getData (int hash ) {
55
+ return resultMap .get (hash );
56
+ }
57
+
43
58
private static class StationData {
44
59
private int min , sum , count , max ;
45
60
@@ -63,28 +78,16 @@ public String toString() {
63
78
64
79
}
65
80
66
- private final Map <String , StationData > resultMap ;
81
+ private final Map <Integer , StationData > resultMap ;
67
82
68
83
public Result () {
69
- this .resultMap = new HashMap <>();
84
+ this .resultMap = new HashMap <>(10_000 , 1 );
70
85
}
71
86
72
- public Map <String , StationData > getResultMap () {
87
+ public Map <Integer , StationData > getResultMap () {
73
88
return resultMap ;
74
89
}
75
90
76
- public void addMeasurement (String city , int value ) {
77
- resultMap .compute (city , (_ , resultRow ) -> {
78
- if (resultRow == null ) {
79
- return new StationData (value );
80
- }
81
- else {
82
- resultRow .update (value );
83
- return resultRow ;
84
- }
85
- });
86
- }
87
-
88
91
public void merge (Result other ) {
89
92
other .getResultMap ().forEach ((city , resultRow ) -> resultMap .merge (city , resultRow , (existing , incoming ) -> {
90
93
existing .min = Math .min (existing .min , incoming .min );
@@ -96,9 +99,9 @@ public void merge(Result other) {
96
99
}
97
100
98
101
public String toString () {
99
- return this .resultMap .entrySet ().stream ()
100
- .sorted ( Map . Entry . comparingByKey ( ))
101
- .map ( entry -> "%s=%s" . formatted ( entry . getKey (), entry . getValue () ))
102
+ return this .resultMap .entrySet ().parallelStream ()
103
+ .map ( entry -> "%s=%s" . formatted ( cityMap . get ( entry . getKey ()), entry . getValue () ))
104
+ .sorted ( Comparator . comparing ( s -> s . split ( "=" )[ 0 ] ))
102
105
.collect (Collectors .joining (", " , "{" , "}" ));
103
106
}
104
107
}
@@ -155,6 +158,21 @@ private static List<MappedByteBuffer> getMappedByteBuffers(int nProcessors) thro
155
158
}
156
159
}
157
160
161
+ private static int readNumberFromBuffer (ByteBuffer buffer , int limit ) {
162
+ var number = 0 ;
163
+ var sign = 1 ;
164
+ while (buffer .position () < limit ) {
165
+ var numberByte = buffer .get ();
166
+ if (numberByte == '-' )
167
+ sign = -1 ;
168
+ else if (numberByte == '\n' )
169
+ break ;
170
+ else if (numberByte != '.' )
171
+ number = number * 10 + (numberByte - '0' );
172
+ }
173
+ return sign * number ;
174
+ }
175
+
158
176
/**
159
177
* Calculates average measurements from the file.
160
178
*
@@ -167,28 +185,31 @@ private static Result calculateAverageMeasurements(List<MappedByteBuffer> chunks
167
185
Result partialResult = new Result ();
168
186
var limit = buffer .limit ();
169
187
var field = new byte [CITY_NAME_MAX_CHARACTERS ];
188
+ Set <Integer > seenHashes = new HashSet <>(10_000 , 1 );
170
189
while (buffer .position () < limit ) {
171
190
var fieldCurrentIndex = 0 ;
172
- field [fieldCurrentIndex ++] = buffer .get ();
191
+ var fieldByte = buffer .get ();
192
+ field [fieldCurrentIndex ++] = fieldByte ;
193
+ // implement djb2 hash: https://theartincode.stanis.me/008-djb2/
194
+ int hash = DJB2_INIT ;
173
195
while (buffer .position () < limit ) {
174
- var fieldByte = buffer .get ();
196
+ // hash = hash * 33 + fieldByte
197
+ hash = (((hash << 5 ) + hash ) + fieldByte );
198
+ fieldByte = buffer .get ();
175
199
if (fieldByte == ';' )
176
200
break ;
177
201
field [fieldCurrentIndex ++] = fieldByte ;
178
202
}
179
- var fieldStr = new String (field , 0 , fieldCurrentIndex );
180
- var number = 0 ;
181
- var sign = 1 ;
182
- while (buffer .position () < limit ) {
183
- var numberByte = buffer .get ();
184
- if (numberByte == '-' )
185
- sign = -1 ;
186
- else if (numberByte == '\n' )
187
- break ;
188
- else if (numberByte != '.' )
189
- number = number * 10 + (numberByte - '0' );
203
+
204
+ var number = readNumberFromBuffer (buffer , limit );
205
+ if (!seenHashes .contains (hash )) {
206
+ seenHashes .add (hash );
207
+ cityMap .put (hash , new String (field , 0 , fieldCurrentIndex ));
208
+ partialResult .addStation (hash , number );
209
+ }
210
+ else {
211
+ partialResult .getData (hash ).update (number );
190
212
}
191
- partialResult .addMeasurement (fieldStr , sign * number );
192
213
}
193
214
return partialResult ;
194
215
}).reduce (new Result (), (partialResult1 , partialResult2 ) -> {
0 commit comments