Repository: apache/jena (java)

Issue:
CacheSimple removes slots for same hash, different key.
### Change

CacheSimple invalidates the hash slot when the key hash matches even if it is a different key.

```
        Object key1 = new Object() { @Override public int hashCode() { return 0 ; } };
        Object key2 = new Object() { @Override public int hashCode() { return 0 ; } };
        Cache<Object, String> cache = CacheFactory.createSimpleCache(16);
        cache.put(key1, "object1");
        cache.put(key2, null);
        String s = cache.getIfPresent(key1);
        System.out.println(s);
        // s is null, should be "object1"
```
This is functionally safe - it just looses the cache entry unnecessarily.

### Are you interested in contributing a pull request for this task?

Yes

Pull request description:
GH-2802: Remove only if same key
GitHub issue resolved #2802

Pull request Description:
Call `CacheSimple.remove` if the `put` value is null.

----

 - [x] Tests are included.
 - [ ] Documentation change and updates are provided for the [Apache Jena website](https://github.com/apache/jena-site/)
 - [ ] Commits have been squashed to remove intermediate development commit messages.
 - [x] Key commit messages start with the issue number (GH-xxxx)

By submitting this pull request, I acknowledge that I am making a contribution to the Apache Software Foundation under the terms and conditions of the [Contributor's Agreement](https://www.apache.org/licenses/contributor-agreements.html).


Patch:
diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml
index d76c3044e1..266e3e2bb9 100644
--- a/.github/workflows/maven.yml
+++ b/.github/workflows/maven.yml
@@ -21,13 +21,14 @@ jobs:
       fail-fast: false
       matrix:
         os: [ubuntu-latest]
+        java_version: ['17', '21']
 
     steps:
     - uses: actions/checkout@v4
-    - name: Set up JDK 17
+    - name: Set up JDK ${{ matrix.java_version }}
       uses: actions/setup-java@v4
       with:
         distribution: 'temurin'
-        java-version: '17'
+        java-version: ${{ matrix.java_version }}
     - name: Build with Maven
       run: mvn -B --file pom.xml -Dmaven.javadoc.skip=true install
diff --git a/jena-base/src/main/java/org/apache/jena/atlas/lib/cache/CacheSimple.java b/jena-base/src/main/java/org/apache/jena/atlas/lib/cache/CacheSimple.java
index 30a8059dc2..c12997fffe 100644
--- a/jena-base/src/main/java/org/apache/jena/atlas/lib/cache/CacheSimple.java
+++ b/jena-base/src/main/java/org/apache/jena/atlas/lib/cache/CacheSimple.java
@@ -115,19 +115,14 @@ public class CacheSimple<K, V> implements Cache<K, V> {
         }
     }
 
-
     @Override
     public void put(K key, V thing) {
         Objects.requireNonNull(key);
-        final int idx = calcIndex(key);
-        if(thing == null) { //null value causes removal of entry
-            if (keys[idx] != null) {
-                keys[idx] = null;
-                values[idx] = null;
-                currentSize--;
-            }
+        if (thing == null) {
+            remove(key);
             return;
         }
+        final int idx = calcIndex(key);
         if(!thing.equals(values[idx])) {
             values[idx] = thing;
         }
diff --git a/jena-text/pom.xml b/jena-text/pom.xml
index 1382fabe7a..f389fc9153 100644
--- a/jena-text/pom.xml
+++ b/jena-text/pom.xml
@@ -27,8 +27,20 @@
     <artifactId>jena</artifactId>
     <version>5.3.0-SNAPSHOT</version>
   </parent>
-
+  <profiles>
+   <!-- apache lucene emits warnings for native access and for the vector module for java version >= 21 -->
+   <profile><id>enable-native-and-incubator-vector</id>
+   <activation>
+     <jdk>[21,22,23]</jdk>
+   </activation>
+     <properties>
+       <jena.text.test.argLine>--enable-native-access=ALL-UNNAMED --add-modules jdk.incubator.vector</jena.text.test.argLine>
+     </properties>
+   </profile>
+ </profiles>
   <properties>
+    <jena.text.test.argLine></jena.text.test.argLine>
+    <argLine></argLine>
     <automatic.module.name>org.apache.jena.text</automatic.module.name>
   </properties>
 
@@ -122,6 +134,7 @@
           <includes>
             <include>**/TS_*.java</include>
           </includes>
+          <argLine>@{argLine} ${jena.text.test.argLine}</argLine>
         </configuration>
       </plugin>
 


Test patch:
diff --git a/jena-base/src/test/java/org/apache/jena/atlas/lib/cache/TestCacheSimple.java b/jena-base/src/test/java/org/apache/jena/atlas/lib/cache/TestCacheSimple.java
index 81c0c0f427..a3db7546e0 100644
--- a/jena-base/src/test/java/org/apache/jena/atlas/lib/cache/TestCacheSimple.java
+++ b/jena-base/src/test/java/org/apache/jena/atlas/lib/cache/TestCacheSimple.java
@@ -32,7 +32,7 @@ public class TestCacheSimple {
 
     /**
      * Simple test to ensure that {@link CacheSimple} evidences
-     * the fixed-size behavior we desire.
+     * the fixed-size behaviour we desire.
      */
     @Test
     public void testFixedSize() {
@@ -208,6 +208,24 @@ public class TestCacheSimple {
         }
     }
 
+    @Test
+    public void testRemoveSameHash() {
+        CompoundKey key1 = new CompoundKey(1, 1);
+        CompoundKey key2 = new CompoundKey(1, 2);
+        String value1 = "v1";
+        String value2 = "v2";
+
+        Cache<CompoundKey, String> cache = new CacheSimple<>(10);
+
+        cache.put(key1, value1);
+        // Delete - different key
+        cache.put(key2, null);
+        String s = cache.getIfPresent(key1);
+
+        assertEquals(1, cache.size());
+        assertEquals(value1, s);
+    }
+
     @Test
     public void testGet() {
         Cache<String, String> cache = new CacheSimple<>(10);
@@ -325,7 +343,7 @@ public class TestCacheSimple {
         assertEquals(16, cache.getAllocatedSize());
     }
 
-    // Compound key for tests. 
+    // Compound key for tests.
     private static final class CompoundKey {
         private final int a;
         private final int b;