Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add KTypeArrayList removeAt and rename remove to removeElement. #247

Merged
merged 2 commits into from
May 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

** New features and API changes

GH-247: Add KTypeArrayList removeAt and rename remove to removeElement. (Bruno Roustant)

GH-244: Hide RamUsageEstimator from the public API. (Dawid Weiss)

GH-239: Minimum Java bumped to 11 (from 8). (Dawid Weiss)
Expand Down
44 changes: 33 additions & 11 deletions hppc/src/main/templates/com/carrotsearch/hppc/KTypeArrayList.java
Original file line number Diff line number Diff line change
Expand Up @@ -218,15 +218,28 @@ public KType set(int index, KType e1) {
* {@inheritDoc}
*/
@Override
public KType remove(int index) {
public KType removeAt(int index) {
assert (index >= 0 && index < size()) : "Index " + index + " out of bounds [" + 0 + ", " + size() + ").";

final KType v = Intrinsics.<KType> cast(buffer[index]);
if (index + 1 < elementsCount) {
System.arraycopy(buffer, index + 1, buffer, index, elementsCount - index - 1);
}
elementsCount--;
System.arraycopy(buffer, index + 1, buffer, index, --elementsCount - index);
/* #if ($TemplateOptions.KTypeGeneric) */
buffer[elementsCount] = Intrinsics.empty();
/* #end */
return v;
}

/**
* {@inheritDoc}
*/
@Override
public KType removeLast() {
assert elementsCount > 0;

final KType v = Intrinsics.<KType> cast(buffer[--elementsCount]);
/* #if ($TemplateOptions.KTypeGeneric) */
buffer[elementsCount] = Intrinsics.empty();
/* #end */
return v;
}

Expand All @@ -245,7 +258,17 @@ public void removeRange(int fromIndex, int toIndex) {
System.arraycopy(buffer, toIndex, buffer, fromIndex, elementsCount - toIndex);
final int count = toIndex - fromIndex;
elementsCount -= count;
/* #if ($TemplateOptions.KTypeGeneric) */
Arrays.fill(buffer, elementsCount, elementsCount + count, Intrinsics.empty());
/* #end */
}

/**
* {@inheritDoc}
*/
@Override
public boolean removeElement(KType e1) {
return removeFirst(e1) != -1;
}

/**
Expand All @@ -255,7 +278,7 @@ public void removeRange(int fromIndex, int toIndex) {
public int removeFirst(KType e1) {
final int index = indexOf(e1);
if (index >= 0)
remove(index);
removeAt(index);
return index;
}

Expand All @@ -266,7 +289,7 @@ public int removeFirst(KType e1) {
public int removeLast(KType e1) {
final int index = lastIndexOf(e1);
if (index >= 0)
remove(index);
removeAt(index);
return index;
}

Expand All @@ -278,19 +301,18 @@ public int removeAll(KType e1) {
int to = 0;
for (int from = 0; from < elementsCount; from++) {
if (Intrinsics.equals(this, e1, buffer[from])) {
buffer[from] = Intrinsics.empty();
continue;
}

if (to != from) {
buffer[to] = buffer[from];
buffer[from] = Intrinsics.empty();
}
to++;
}

final int deleted = elementsCount - to;
this.elementsCount = to;
/* #if ($TemplateOptions.KTypeGeneric) */
Arrays.fill(buffer, elementsCount, elementsCount + deleted, Intrinsics.empty());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think it's going to be faster this way? Those null assignments should be within the same cache line, so super-fast already? And previously it'd zero the removed slot, now it doesn't. Don't know if it makes a difference in practice.

Copy link
Collaborator Author

@bruno-roustant bruno-roustant May 24, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since you asked the question, I did a benchmark running removeAll on lists of size 10, 100, 1_000, 10_000, and with four categories of number of duplicate elements to make the removal longer.

  • Surprisingly (to me) I see no perf difference for primitive IntArrayList with and without zeroing removed slots.
  • For ObjectArrayList, the new version with Arrays.fill is 20% faster for large lists.

So I would tend to keep this new code, unless you see an interest in zeroing removed slots for primitives. BTW the KTypeSlack implementation of pop was already not zeroing removed slots, and that suggested me to do the same for list.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for running the benchmark. LGTM.

/* #end */
return deleted;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@
*/
/*! ${TemplateOptions.generatedAnnotation} !*/
public interface KTypeIndexedContainer<KType> extends KTypeCollection<KType>, RandomAccess {
/**
* Removes the first element that equals <code>e1</code>, returning whether
* an element has been removed.
*/
public boolean removeElement(KType e1);

/**
* Removes the first element that equals <code>e1</code>, returning its
* deleted position or <code>-1</code> if the element was not found.
Expand Down Expand Up @@ -68,7 +74,13 @@ public interface KTypeIndexedContainer<KType> extends KTypeCollection<KType>, Ra
* @see #removeLast
* @see #removeAll
*/
public KType remove(int index);
public KType removeAt(int index);

/**
* Removes and returns the last element of this container.
* This container must not be empty.
*/
public KType removeLast();

/**
* Removes from this container all of the elements with indexes between
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,13 +156,7 @@ public void discard() {
* Remove the top element from the stack and return it.
*/
public KType pop() {
assert elementsCount > 0;

final KType v = Intrinsics.<KType> cast(buffer[--elementsCount]);
/* #if ($TemplateOptions.KTypeGeneric) */
buffer[elementsCount] = null;
/* #end */
return v;
return removeLast();
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ public void initialize()
list = new KTypeArrayList<>();
}

/*! #if ($TemplateOptions.KTypeGeneric) !*/
@After
public void checkTrailingSpaceUninitialized()
{
Expand All @@ -43,6 +44,7 @@ public void checkTrailingSpaceUninitialized()
assertTrue(Intrinsics.<KType> empty() == list.buffer[i]);
}
}
/*! #end !*/

/* */
@Test
Expand Down Expand Up @@ -125,17 +127,34 @@ public void testSet()

/* */
@Test
public void testRemove()
public void testRemoveAt()
{
list.add(asArray(0, 1, 2, 3, 4));

list.remove(0);
list.remove(2);
list.remove(1);
list.removeAt(0);
list.removeAt(2);
list.removeAt(1);

assertListEquals(list.toArray(), 1, 4);
}

/* */
@Test
public void testRemoveLast() {
list.add(asArray(0, 1, 2, 3, 4));

assertEquals2(4, list.removeLast());
assertEquals2(4, list.size());
assertListEquals(list.toArray(), 0, 1, 2, 3);
assertEquals2(3, list.removeLast());
assertEquals2(3, list.size());
assertListEquals(list.toArray(), 0, 1, 2);
assertEquals2(2, list.removeLast());
assertEquals2(1, list.removeLast());
assertEquals2(0, list.removeLast());
assertTrue(list.isEmpty());
}

/* */
@Test
public void testRemoveRange()
Expand Down Expand Up @@ -173,17 +192,16 @@ public void testRemoveFirstLast()
assertListEquals(list.toArray(), 2, 1);
assertEquals(-1, list.removeLast(k0));

/*! #if ($TemplateOptions.KTypeGeneric) !*/
list.clear();
list.add(newArray(k0, null, k2, null, k0));
assertEquals(1, list.removeFirst(null));
assertEquals(2, list.removeLast(null));
assertListEquals(list.toArray(), 0, 2, 0);
/*! #end !*/
list.add(newArray(k1, Intrinsics.empty(), k2, Intrinsics.empty(), k1));
assertEquals(1, list.removeFirst(Intrinsics.empty()));
assertEquals(2, list.removeLast(Intrinsics.empty()));
assertListEquals(list.toArray(), 1, 2, 1);
}

/* */
@Test
@SuppressWarnings("unchecked")
public void testRemoveAll()
{
list.add(asArray(0, 1, 0, 1, 0));
Expand All @@ -195,13 +213,11 @@ public void testRemoveAll()
assertEquals(2, list.removeAll(k1));
assertTrue(list.isEmpty());

/*! #if ($TemplateOptions.KTypeGeneric) !*/
list.clear();
list.add(newArray(k0, null, k2, null, k0));
assertEquals(2, list.removeAll((KType) null));
assertEquals(0, list.removeAll((KType) null));
assertListEquals(list.toArray(), 0, 2, 0);
/*! #end !*/
list.add(newArray(k1, Intrinsics.empty(), k2, Intrinsics.empty(), k1));
assertEquals(2, list.removeAll((KType) Intrinsics.empty()));
assertEquals(0, list.removeAll((KType) Intrinsics.empty()));
assertListEquals(list.toArray(), 1, 2, 1);
}

/*! #if (not $TemplateOptions.isKTypeAnyOf("DOUBLE", "FLOAT", "BYTE")) !*/
Expand Down Expand Up @@ -286,33 +302,31 @@ public boolean apply(KType v)

/* */
@Test
@SuppressWarnings("unchecked")
public void testIndexOf()
{
list.add(asArray(0, 1, 2, 1, 0));
list.add(asArray(3, 1, 2, 1, 3));

/*! #if ($TemplateOptions.KTypeGeneric) !*/
list.add((KType) null);
assertEquals(5, list.indexOf(null));
/*! #end !*/
list.add((KType) Intrinsics.empty());
assertEquals(5, list.indexOf(Intrinsics.empty()));

assertEquals(0, list.indexOf(k0));
assertEquals(-1, list.indexOf(k3));
assertEquals(0, list.indexOf(k3));
assertEquals(-1, list.indexOf(k4));
assertEquals(2, list.indexOf(k2));
}

/* */
@Test
@SuppressWarnings("unchecked")
public void testLastIndexOf()
{
list.add(asArray(0, 1, 2, 1, 0));
list.add(asArray(3, 1, 2, 1, 3));

/*! #if ($TemplateOptions.KTypeGeneric) !*/
list.add((KType) null);
assertEquals(5, list.lastIndexOf(null));
/*! #end !*/
list.add((KType) Intrinsics.empty());
assertEquals(5, list.lastIndexOf(Intrinsics.empty()));

assertEquals2(4, list.lastIndexOf(k0));
assertEquals2(-1, list.lastIndexOf(k3));
assertEquals2(4, list.lastIndexOf(k3));
assertEquals2(-1, list.lastIndexOf(k4));
assertEquals2(2, list.lastIndexOf(k2));
}

Expand Down Expand Up @@ -487,7 +501,8 @@ public void testClear()
{
list.add(asArray( 1, 2, 3));
list.clear();
checkTrailingSpaceUninitialized();
assertTrue(list.isEmpty());
assertEquals(-1, list.indexOf(cast(1)));
}

/* */
Expand Down Expand Up @@ -539,17 +554,15 @@ public void testEqualElements()
assertTrue(l2.equalElements(l1));
}

/*! #if ($TemplateOptions.KTypeGeneric) !*/
@Test
public void testHashCodeWithNulls()
public void testHashCodeWithEmptyKeys()
{
KTypeArrayList<KType> l1 = KTypeArrayList.from(k1, null, k3);
KTypeArrayList<KType> l2 = KTypeArrayList.from(k1, null, k3);
KTypeArrayList<KType> l1 = KTypeArrayList.from(k1, Intrinsics.empty(), k3);
KTypeArrayList<KType> l2 = KTypeArrayList.from(k1, Intrinsics.empty(), k3);

assertEquals(l1.hashCode(), l2.hashCode());
assertEquals(l1, l2);
}
/*! #end !*/

/*! #if ($TemplateOptions.KTypeGeneric) !*/
@Test
Expand All @@ -561,10 +574,10 @@ class A {
class B extends A {
}

KTypeArrayList<B> list2 = new KTypeArrayList<B>();
KTypeArrayList<B> list2 = new KTypeArrayList<>();
list2.add(new B());

KTypeArrayList<A> list3 = new KTypeArrayList<A>();
KTypeArrayList<A> list3 = new KTypeArrayList<>();
list3.add(new B());
list3.add(new A());
list3.addAll(list2);
Expand Down