Skip to content

Commit

Permalink
Merge pull request #202 from ratcashdev/master
Browse files Browse the repository at this point in the history
fix toArray() for Maps avoiding allocation
  • Loading branch information
mjpt777 authored Feb 6, 2020
2 parents 45d8634 + e95962c commit 13a9083
Show file tree
Hide file tree
Showing 8 changed files with 367 additions and 0 deletions.
35 changes: 35 additions & 0 deletions agrona/src/main/java/org/agrona/collections/Int2IntHashMap.java
Original file line number Diff line number Diff line change
Expand Up @@ -1102,5 +1102,40 @@ public boolean contains(final Object o)

return value != null && value.equals(entry.getValue());
}

/**
* {@inheritDoc}
*/
@Override
public Object[] toArray()
{
final Object[] array = new Object[size()];
return toArray(array);
}

/**
* {@inheritDoc}
*/
@Override
public <T> T[] toArray(final T[] a)
{
final T[] array = a.length >= size ? a : (T[])java.lang.reflect.Array
.newInstance(a.getClass().getComponentType(), size);
final EntryIterator it = iterator();
for (@DoNotSub int i = 0; i < array.length; i++)
{
if (it.hasNext())
{
it.next();
array[i] = (T)it.allocateDuplicateEntry();
}
else
{
array[i] = null;
break;
}
}
return array;
}
}
}
35 changes: 35 additions & 0 deletions agrona/src/main/java/org/agrona/collections/Int2ObjectHashMap.java
Original file line number Diff line number Diff line change
Expand Up @@ -774,6 +774,41 @@ public boolean contains(final Object o)
final V value = getMapped(key);
return value != null && value.equals(mapNullValue(entry.getValue()));
}

/**
* {@inheritDoc}
*/
@Override
public Object[] toArray()
{
final Object[] array = new Object[size()];
return toArray(array);
}

/**
* {@inheritDoc}
*/
@Override
public <T> T[] toArray(final T[] a)
{
final T[] array = a.length >= size ? a : (T[])java.lang.reflect.Array
.newInstance(a.getClass().getComponentType(), size);
final EntryIterator it = iterator();
for (@DoNotSub int i = 0; i < array.length; i++)
{
if (it.hasNext())
{
it.next();
array[i] = (T)it.allocateDuplicateEntry();
}
else
{
array[i] = null;
break;
}
}
return array;
}
}

///////////////////////////////////////////////////////////////////////////////////////////////
Expand Down
38 changes: 38 additions & 0 deletions agrona/src/main/java/org/agrona/collections/Object2IntHashMap.java
Original file line number Diff line number Diff line change
Expand Up @@ -801,6 +801,44 @@ public boolean contains(final Object o)

return value != null && value.equals(entry.getValue());
}

/**
* {@inheritDoc}
*/
/**
* {@inheritDoc}
*/
@Override
public Object[] toArray()
{
final Object[] array = new Object[size()];
return toArray(array);
}

/**
* {@inheritDoc}
*/
@Override
public <T> T[] toArray(final T[] a)
{
final T[] array = a.length >= size ? a : (T[])java.lang.reflect.Array
.newInstance(a.getClass().getComponentType(), size);
final EntryIterator it = iterator();
for (@DoNotSub int i = 0; i < array.length; i++)
{
if (it.hasNext())
{
it.next();
array[i] = (T)it.allocateDuplicateEntry();
}
else
{
array[i] = null;
break;
}
}
return array;
}
}

///////////////////////////////////////////////////////////////////////////////////////////////
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import static java.util.Objects.requireNonNull;
import static org.agrona.BitUtil.findNextPositivePowerOfTwo;
import static org.agrona.collections.CollectionUtil.validateLoadFactor;
import org.agrona.generation.DoNotSub;

/**
* A open addressing with linear probing hash map, same algorithm as {@link Int2IntHashMap}.
Expand Down Expand Up @@ -870,5 +871,40 @@ public boolean contains(final Object o)
final V value = getMapped(entry.getKey());
return value != null && value.equals(mapNullValue(entry.getValue()));
}

/**
* {@inheritDoc}
*/
@Override
public Object[] toArray()
{
final Object[] array = new Object[size()];
return toArray(array);
}

/**
* {@inheritDoc}
*/
@Override
public <T> T[] toArray(final T[] a)
{
final T[] array = a.length >= size ? a : (T[])java.lang.reflect.Array
.newInstance(a.getClass().getComponentType(), size);
final EntryIterator it = iterator();
for (@DoNotSub int i = 0; i < array.length; i++)
{
if (it.hasNext())
{
it.next();
array[i] = (T)it.allocateDuplicateEntry();
}
else
{
array[i] = null;
break;
}
}
return array;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -659,6 +659,55 @@ public void shouldIterateEntriesBySpecialisedTypeAndSetValue()
assertEquals(expected, map);
}

@Test
public void testToArray()
{
final Int2IntHashMap cut = new Int2IntHashMap(-127);
cut.put(1, 11);
cut.put(2, 12);
cut.put(3, 13);

final Object[] array = cut.entrySet().toArray();
for (final Object entry : array)
{
cut.remove(((Entry<Integer, Integer>)entry).getKey());
}
assertTrue(cut.isEmpty());
}

@Test
public void testToArrayTyped()
{
final Int2IntHashMap cut = new Int2IntHashMap(-127);
cut.put(1, 11);
cut.put(2, 12);
cut.put(3, 13);

final Entry[] type = new Entry[1];
final Entry[] array = cut.entrySet().toArray(type);
for (final Entry entry : array)
{
cut.remove(((Entry<Integer, Integer>)entry).getKey());
}
assertTrue(cut.isEmpty());
}

@Test
public void testToArrayWithArrayListConstructor()
{
final Int2IntHashMap cut = new Int2IntHashMap(-127);
cut.put(1, 11);
cut.put(2, 12);
cut.put(3, 13);

final List<Map.Entry<Integer, Integer>> list = new ArrayList<>(cut.entrySet());
for (final Map.Entry<Integer, Integer> entry : list)
{
cut.remove(entry.getKey());
}
assertTrue(cut.isEmpty());
}

private void assertEntryIs(final Entry<Integer, Integer> entry, final int expectedKey, final int expectedValue)
{
assertEquals(expectedKey, entry.getKey().intValue());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,13 @@
*/
package org.agrona.collections;

import java.util.ArrayList;
import org.junit.jupiter.api.Test;

import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import static org.hamcrest.MatcherAssert.assertThat;
Expand Down Expand Up @@ -424,4 +426,53 @@ protected String unmapNullValue(final Object value)

assertThat(map.size(), is(1));
}

@Test
public void testToArray()
{
final Int2ObjectHashMap<String> cut = new Int2ObjectHashMap<>();
cut.put(1, "a");
cut.put(2, "b");
cut.put(3, "c");

final Object[] array = cut.entrySet().toArray();
for (final Object entry : array)
{
cut.remove(((Map.Entry<Integer, String>)entry).getKey());
}
assertTrue(cut.isEmpty());
}

@Test
public void testToArrayTyped()
{
final Int2ObjectHashMap<String> cut = new Int2ObjectHashMap<>();
cut.put(1, "a");
cut.put(2, "b");
cut.put(3, "c");

final Map.Entry[] type = new Map.Entry[1];
final Map.Entry[] array = cut.entrySet().toArray(type);
for (final Map.Entry entry : array)
{
cut.remove(((Map.Entry<Integer, String>)entry).getKey());
}
assertTrue(cut.isEmpty());
}

@Test
public void testToArrayWithArrayListConstructor()
{
final Int2ObjectHashMap<String> cut = new Int2ObjectHashMap<>();
cut.put(1, "a");
cut.put(2, "b");
cut.put(3, "c");

final List<Map.Entry<Integer, String>> list = new ArrayList<>(cut.entrySet());
for (final Map.Entry<Integer, String> entry : list)
{
cut.remove(entry.getKey());
}
assertTrue(cut.isEmpty());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,15 @@
*/
package org.agrona.collections;

import java.util.ArrayList;
import org.junit.jupiter.api.Test;

import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.closeTo;
Expand Down Expand Up @@ -438,4 +441,53 @@ public void shouldCopyConstructAndBeEqual()
final Object2IntHashMap<String> mapCopy = new Object2IntHashMap<>(objectToIntMap);
assertThat(mapCopy, is(objectToIntMap));
}

@Test
public void testToArray()
{
final Object2IntHashMap<String> cut = new Object2IntHashMap<>(-127);
cut.put("a", 1);
cut.put("b", 2);
cut.put("c", 3);

final Object[] array = cut.entrySet().toArray();
for (final Object entry : array)
{
cut.remove(((Entry<String, Integer>)entry).getKey());
}
assertTrue(cut.isEmpty());
}

@Test
public void testToArrayTyped()
{
final Object2IntHashMap<String> cut = new Object2IntHashMap<>(-127);
cut.put("a", 1);
cut.put("b", 2);
cut.put("c", 3);

final Entry[] type = new Entry[1];
final Entry[] array = cut.entrySet().toArray(type);
for (final Entry entry : array)
{
cut.remove(((Entry<String, Integer>)entry).getKey());
}
assertTrue(cut.isEmpty());
}

@Test
public void testToArrayWithArrayListConstructor()
{
final Object2IntHashMap<String> cut = new Object2IntHashMap<>(-127);
cut.put("a", 1);
cut.put("b", 2);
cut.put("c", 3);

final List<Map.Entry<String, Integer>> list = new ArrayList<>(cut.entrySet());
for (final Map.Entry<String, Integer> entry : list)
{
cut.remove(entry.getKey());
}
assertTrue(cut.isEmpty());
}
}
Loading

0 comments on commit 13a9083

Please sign in to comment.