Skip to content

Commit 4707140

Browse files
committed
Better initial sizing of HashMaps
`HashMap` has default load factor of 0.75. It is often not enough to specify desired initial capacity in order for the map to not resize. This commit adds a helper method to create `HashMap` that will not resize.
1 parent 6f8699a commit 4707140

File tree

6 files changed

+80
-14
lines changed

6 files changed

+80
-14
lines changed

driver/src/main/java/org/neo4j/driver/internal/Bookmark.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
package org.neo4j.driver.internal;
2020

2121
import java.util.Collections;
22-
import java.util.HashMap;
2322
import java.util.Iterator;
2423
import java.util.Map;
2524
import java.util.Objects;
@@ -28,6 +27,7 @@
2827

2928
import static java.util.Collections.emptyMap;
3029
import static java.util.Collections.singleton;
30+
import static org.neo4j.driver.internal.util.Iterables.newHashMapWithSize;
3131
import static org.neo4j.driver.v1.Values.value;
3232

3333
public final class Bookmark
@@ -93,7 +93,7 @@ public Map<String,Value> asBeginTransactionParameters()
9393
// {bookmarks: ["one", "two", "max"]} for backwards compatibility reasons. Old servers can only accept single
9494
// bookmark that is why driver has to parse and compare given list of bookmarks. This functionality will
9595
// eventually be removed.
96-
Map<String,Value> parameters = new HashMap<>( 4 );
96+
Map<String,Value> parameters = newHashMapWithSize( 2 );
9797
parameters.put( BOOKMARK_KEY, value( maxValue ) );
9898
parameters.put( BOOKMARKS_KEY, value( values ) );
9999
return parameters;

driver/src/main/java/org/neo4j/driver/internal/messaging/PackStreamMessageFormatV1.java

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
import java.io.IOException;
2222
import java.util.ArrayList;
2323
import java.util.Arrays;
24-
import java.util.HashMap;
24+
import java.util.Collections;
2525
import java.util.LinkedHashMap;
2626
import java.util.List;
2727
import java.util.Map;
@@ -73,8 +73,6 @@ public class PackStreamMessageFormatV1 implements MessageFormat
7373

7474
public static final int NODE_FIELDS = 3;
7575

76-
private static final Map<String,Value> EMPTY_STRING_VALUE_MAP = new HashMap<>( 0 );
77-
7876
@Override
7977
public MessageFormat.Writer newWriter( PackOutput output, boolean byteArraySupportEnabled )
8078
{
@@ -543,7 +541,7 @@ private InternalNode unpackNode() throws IOException
543541
labels.add( unpacker.unpackString() );
544542
}
545543
int numProps = (int) unpacker.unpackMapHeader();
546-
Map<String,Value> props = new HashMap<>();
544+
Map<String,Value> props = Iterables.newHashMapWithSize( numProps );
547545
for ( int j = 0; j < numProps; j++ )
548546
{
549547
String key = unpacker.unpackString();
@@ -636,9 +634,9 @@ private Map<String,Value> unpackMap() throws IOException
636634
int size = (int) unpacker.unpackMapHeader();
637635
if ( size == 0 )
638636
{
639-
return EMPTY_STRING_VALUE_MAP;
637+
return Collections.emptyMap();
640638
}
641-
Map<String,Value> map = new HashMap<>( size );
639+
Map<String,Value> map = Iterables.newHashMapWithSize( size );
642640
for ( int i = 0; i < size; i++ )
643641
{
644642
String key = unpacker.unpackString();

driver/src/main/java/org/neo4j/driver/internal/util/Iterables.java

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929

3030
public class Iterables
3131
{
32+
private static final float DEFAULT_HASH_MAP_LOAD_FACTOR = 0.75F;
33+
3234
public static int count( Iterable<?> it )
3335
{
3436
if ( it instanceof Collection ) { return ((Collection) it).size(); }
@@ -68,7 +70,7 @@ public static <T> T single( Iterable<T> it )
6870

6971
public static Map<String, String> map( String ... alternatingKeyValue )
7072
{
71-
Map<String, String> out = new HashMap<>();
73+
Map<String,String> out = newHashMapWithSize( alternatingKeyValue.length / 2 );
7274
for ( int i = 0; i < alternatingKeyValue.length; i+=2 )
7375
{
7476
out.put( alternatingKeyValue[i], alternatingKeyValue[i+1] );
@@ -107,4 +109,22 @@ public void remove()
107109
}
108110
};
109111
}
112+
113+
public static <K, V> HashMap<K,V> newHashMapWithSize( int expectedSize )
114+
{
115+
return new HashMap<>( hashMapCapacity( expectedSize ) );
116+
}
117+
118+
private static int hashMapCapacity( int expectedSize )
119+
{
120+
if ( expectedSize < 3 )
121+
{
122+
if ( expectedSize < 0 )
123+
{
124+
throw new IllegalArgumentException( "Illegal map size: " + expectedSize );
125+
}
126+
return expectedSize + 1;
127+
}
128+
return (int) ((float) expectedSize / DEFAULT_HASH_MAP_LOAD_FACTOR + 1.0F);
129+
}
110130
}

driver/src/main/java/org/neo4j/driver/v1/Statement.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,15 @@
1818
*/
1919
package org.neo4j.driver.v1;
2020

21-
import java.util.HashMap;
2221
import java.util.Map;
2322

2423
import org.neo4j.driver.v1.summary.ResultSummary;
2524
import org.neo4j.driver.v1.util.Immutable;
2625

2726
import static java.lang.String.format;
28-
import static org.neo4j.driver.v1.Values.value;
27+
import static org.neo4j.driver.internal.util.Iterables.newHashMapWithSize;
2928
import static org.neo4j.driver.v1.Values.ofValue;
29+
import static org.neo4j.driver.v1.Values.value;
3030

3131
/**
3232
* An executable statement, i.e. the statements' text and its parameters.
@@ -136,7 +136,7 @@ public Statement withUpdatedParameters( Value updates )
136136
}
137137
else
138138
{
139-
Map<String, Value> newParameters = new HashMap<>( Math.max( parameters.size(), updates.size() ) );
139+
Map<String,Value> newParameters = newHashMapWithSize( Math.max( parameters.size(), updates.size() ) );
140140
newParameters.putAll( parameters.asMap( ofValue() ) );
141141
for ( Map.Entry<String, Value> entry : updates.asMap( ofValue() ).entrySet() )
142142
{

driver/src/main/java/org/neo4j/driver/v1/Values.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@
4444
import org.neo4j.driver.v1.types.TypeSystem;
4545
import org.neo4j.driver.v1.util.Function;
4646

47+
import static org.neo4j.driver.internal.util.Iterables.newHashMapWithSize;
48+
4749
/**
4850
* Utility for wrapping regular Java types and exposing them as {@link Value}
4951
* objects, and vice versa.
@@ -247,7 +249,7 @@ public static Value value( final boolean val )
247249

248250
public static Value value( final Map<String,Object> val )
249251
{
250-
Map<String,Value> asValues = new HashMap<>( val.size() );
252+
Map<String,Value> asValues = newHashMapWithSize( val.size() );
251253
for ( Map.Entry<String,Object> entry : val.entrySet() )
252254
{
253255
asValues.put( entry.getKey(), value( entry.getValue() ) );
@@ -284,7 +286,7 @@ public static Value parameters( Object... keysAndValues )
284286
"alternating key and value. Arguments were: " +
285287
Arrays.toString( keysAndValues ) + "." );
286288
}
287-
HashMap<String,Value> map = new HashMap<>( keysAndValues.length / 2 );
289+
HashMap<String,Value> map = newHashMapWithSize( keysAndValues.length / 2 );
288290
for ( int i = 0; i < keysAndValues.length; i += 2 )
289291
{
290292
Object value = keysAndValues[i + 1];
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*
2+
* Copyright (c) 2002-2017 "Neo Technology,"
3+
* Network Engine for Objects in Lund AB [http://neotechnology.com]
4+
*
5+
* This file is part of Neo4j.
6+
*
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
*/
19+
package org.neo4j.driver.internal.util;
20+
21+
import org.junit.Test;
22+
23+
import static org.junit.Assert.assertNotNull;
24+
import static org.junit.Assert.fail;
25+
26+
public class IterablesTest
27+
{
28+
@Test
29+
public void shouldCreateHashMapWithExpectedSize()
30+
{
31+
assertNotNull( Iterables.newHashMapWithSize( 42 ) );
32+
}
33+
34+
@Test
35+
public void shouldThrowWhenNegativeHashMapSizeGiven()
36+
{
37+
try
38+
{
39+
Iterables.newHashMapWithSize( -42 );
40+
fail( "Exception expected" );
41+
}
42+
catch ( IllegalArgumentException ignore )
43+
{
44+
}
45+
}
46+
}

0 commit comments

Comments
 (0)