Skip to content
Open
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
17 changes: 12 additions & 5 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,26 @@
<version>3.3</version>
<configuration>
<encoding>UTF-8</encoding>
<source>1.8</source>
<target>1.8</target>
<source>11</source>
<target>11</target>
</configuration>
</plugin>
</plugins>
</build>

<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.9.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>3.23.1</version>
<scope>test</scope>
</dependency>

</dependencies>
</project>
229 changes: 210 additions & 19 deletions src/main/java/de/comparus/opensource/longmap/LongMapImpl.java
Original file line number Diff line number Diff line change
@@ -1,43 +1,234 @@
package de.comparus.opensource.longmap;

import java.lang.reflect.Array;

public class LongMapImpl<V> implements LongMap<V> {
public V put(long key, V value) {
return null;

private static final int DEFAULT_CAPACITY = 16;
private static final float DEFAULT_LOAD_FACTOR = 0.75f;

private int capacity;
private float loadFactor;
private int size;
public Entry<V>[] buckets;

Class<V> type;

public LongMapImpl(Class<V> type) {
this(DEFAULT_CAPACITY, DEFAULT_LOAD_FACTOR, type);
}

public LongMapImpl(int capacity, float loadFactor, Class<V> type) {
if (capacity <= 0) {
throw new IllegalArgumentException("Invalid capacity: " + capacity);
}
if (loadFactor <= 0 || loadFactor > 1) {
throw new IllegalArgumentException("Invalid load factor: " + loadFactor);
}

this.type = type;

this.capacity = capacity;
this.loadFactor = loadFactor;
this.size = 0;
this.buckets = new Entry[capacity];
}

public V put(long key, V value) {
int bucketIndex = getBucketIndex(key, capacity);
Entry<V> bucket = buckets[bucketIndex];

if (bucket == null) {
buckets[bucketIndex] = new Entry(key, value);
} else {
Entry<V> prev = null;
while (bucket != null) {
if (bucket.getKey() == key) {
V oldValue = bucket.getValue();
bucket.setValue(value);
return oldValue;
}
prev = bucket;
bucket = bucket.next;
}

public V get(long key) {
return null;
prev.next = new Entry(key, value);
}

public V remove(long key) {
return null;
size++;

if (size > capacity * loadFactor) {
resize();
}

public boolean isEmpty() {
return false;
return null;
}

public V get(long key) {
int bucketIndex = getBucketIndex(key, capacity);
Entry<V> bucket = buckets[bucketIndex];

if (bucket != null) {
while (bucket != null) {
if (bucket.getKey() == key) {
return bucket.getValue();
}
bucket = bucket.next;
}
}

public boolean containsKey(long key) {
return false;
return null;
}

public V remove(long key) {
int bucketIndex = getBucketIndex(key, capacity);
Entry<V> bucket = buckets[bucketIndex];

if (bucket != null) {
Entry<V> prev = null;
while (bucket != null) {
if (bucket.getKey() == key) {
V value = bucket.getValue();
if (prev == null) {
if (bucket.next != null) {
buckets[bucketIndex] = bucket.next;
} else {
buckets[bucketIndex] = null;
}
} else {
prev.next = null;
}
size--;
return value;
}
prev = bucket;
bucket = bucket.next;
}
}

public boolean containsValue(V value) {
return false;
return null;
}

public boolean isEmpty() {
return size == 0;
}

public boolean containsKey(long key) {
int bucketIndex = getBucketIndex(key, capacity);
Entry<V> bucket = buckets[bucketIndex];

if (bucket != null) {
while (bucket != null) {
if (bucket.getKey() == key) {
return true;
}
bucket = bucket.next;
}
}
return false;
}

public long[] keys() {
return null;
public boolean containsValue(V value) {
for (Entry<V> bucket : buckets) {
while (bucket != null) {
if (bucket.getValue().equals(value)) {
return true;
}
bucket = bucket.next;
}
}

public V[] values() {
return null;
return false;
}

public long[] keys() {
long[] keys = new long[size];
int index = 0;

for (Entry<V> bucket : buckets) {
while (bucket != null) {
keys[index++] = bucket.getKey();
bucket = bucket.next;
}
}

public long size() {
return 0;
return keys;
}

public V[] values() {
V[] values = (V[]) Array.newInstance(type, size);
int index = 0;

for (Entry<V> bucket : buckets) {
while (bucket != null) {
values[index++] = bucket.getValue();
bucket = bucket.next;
}
}

return values;
}

public long size() {
return size;
}

public void clear() {
size = 0;
buckets = new Entry[capacity];
}


private int getBucketIndex(long key, int capacity) {
return (int) Math.abs(key) % capacity;
}

private void resize() {
int newCapacity = capacity + capacity/2;
if (newCapacity < 0) {
throw new IllegalArgumentException("Couldn't be resized. Maximum size has been reached.");
}
Entry<V>[] newBuckets = new Entry[newCapacity];

for (Entry<V> bucket : buckets) {
while (bucket != null) {
int newBucketIndex = getBucketIndex(bucket.getKey(), newCapacity);
Entry<V> newBucket = newBuckets[newBucketIndex];
if (newBucket == null) {
newBuckets[newBucketIndex] = bucket;
} else {
newBucket.next = bucket;
}
bucket = bucket.next;
}
}

capacity = newCapacity;
buckets = newBuckets;
}

private static class Entry<V> {

private long key;
private V value;

private Entry<V> next;

public Entry(long key, V value) {
this.key = key;
this.value = value;
}

public void clear() {
public long getKey() {
return key;
}

public V getValue() {
return value;
}

public void setValue(V value) {
this.value = value;
}
}
}
92 changes: 92 additions & 0 deletions src/main/test/java/de/comparus/opensource/longmap/LongMapTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package de.comparus.opensource.longmap;

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;

import java.util.stream.LongStream;
import java.util.stream.Stream;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertAll;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

@Tag("Unit")
public class LongMapTest {

public static final long GENERATED_AMOUNT = 10;

@Test
@DisplayName("Complex test of get, contains key/value, put, resize methods")
public void longMapTest() {
final long TESTED_AMOUNT_OF_ENTRIES = 10000;
LongMap<Long> map = generateMapWithValues(TESTED_AMOUNT_OF_ENTRIES);
assertMapRange(map, -TESTED_AMOUNT_OF_ENTRIES, TESTED_AMOUNT_OF_ENTRIES);
}

@Test
public void removeTest() {
LongMap<Long> map = generateMapWithValues(GENERATED_AMOUNT);
for(long i = 1; i <= GENERATED_AMOUNT; i++) {
map.remove(i);
}
assertEquals(map.size(), 11);
assertMapRange(map, -GENERATED_AMOUNT, 0);
}

@Test
@DisplayName("Complex test of clear, isEmpty methods")
public void clearTest() {
LongMap<Long> map = generateMapWithValues(GENERATED_AMOUNT);
map.clear();
assertAll(
() -> assertEquals(map.size(), map.values().length),
() -> assertTrue(map.isEmpty())
);
}

@Test
public void valuesTest() {
LongMap<Long> map = generateMapWithValues(GENERATED_AMOUNT);
Long [] valuesOfMap = map.values();
Long [] expectedValuesOfMap = Stream
.iterate(-GENERATED_AMOUNT, i -> i+1)
.limit(20)
.toArray(Long[]::new);
assertAll(
() -> assertEquals( 20, valuesOfMap.length),
() -> assertThat(valuesOfMap).containsOnly(expectedValuesOfMap)
);
}

@Test
public void keysTest() {
LongMap<Long> map = generateMapWithValues(GENERATED_AMOUNT);
long [] keysOfMap = map.keys();
long [] expectedKeysOfMap = LongStream
.iterate(-GENERATED_AMOUNT, i -> i+1)
.limit(20)
.toArray();
assertAll(
() -> assertEquals( 20, keysOfMap.length),
() -> assertThat(keysOfMap).containsOnly(expectedKeysOfMap)
);
}

private LongMap<Long> generateMapWithValues(long TESTED_AMOUNT_OF_ENTRIES) {
LongMap<Long> map = new LongMapImpl<>(Long.class);
for (long i = -TESTED_AMOUNT_OF_ENTRIES; i < TESTED_AMOUNT_OF_ENTRIES; i++) {
map.put(i, i);
}
return map;
}

private static void assertMapRange(LongMap<Long> map, long minBound, long maxBound) {
for (long i = minBound; i < maxBound; i++) {
assertEquals(i, map.get(i));
assertTrue(map.containsKey(i));
assertTrue(map.containsValue(i));
}
}
}