refactor(hw-01): rename source directory to solution
This commit is contained in:
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* This source file was generated by the Gradle 'init' task
|
||||
*/
|
||||
package org.example;
|
||||
|
||||
public class App {
|
||||
public String getGreeting() {
|
||||
return "Hello World!";
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println(new App().getGreeting());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
package org.example;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class MyHashMap<Key, Value> {
|
||||
|
||||
private Key[] keys;
|
||||
private Value[] values;
|
||||
private boolean[] occupied;
|
||||
private boolean[] deleted;
|
||||
private int size;
|
||||
|
||||
public MyHashMap(int capacity) {
|
||||
keys = createArray(capacity);
|
||||
values = createArray(capacity);
|
||||
occupied = new boolean[capacity];
|
||||
deleted = new boolean[capacity];
|
||||
}
|
||||
|
||||
public Value get(Key key) {
|
||||
int index = findIndex(key);
|
||||
|
||||
return index == -1 ? null : values[index];
|
||||
}
|
||||
|
||||
public Value put(Key key, Value value) {
|
||||
if (size == keys.length) {
|
||||
resize();
|
||||
}
|
||||
|
||||
int index = findIndex(key);
|
||||
|
||||
if (index != -1) {
|
||||
Value oldValue = values[index];
|
||||
values[index] = value;
|
||||
return oldValue;
|
||||
}
|
||||
|
||||
index = findEmptyIndex(key);
|
||||
|
||||
keys[index] = key;
|
||||
values[index] = value;
|
||||
occupied[index] = true;
|
||||
deleted[index] = false;
|
||||
size++;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public Value remove(Key key) {
|
||||
int index = findIndex(key);
|
||||
|
||||
if (index == -1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Value oldValue = values[index];
|
||||
occupied[index] = false;
|
||||
deleted[index] = true;
|
||||
size--;
|
||||
|
||||
return oldValue;
|
||||
}
|
||||
|
||||
private int findIndex(Key key) {
|
||||
int index = index(key);
|
||||
int start = index;
|
||||
|
||||
while (occupied[index] || deleted[index]) {
|
||||
if (occupied[index] && Objects.equals(keys[index], key)) {
|
||||
return index;
|
||||
}
|
||||
|
||||
index = (index + 1) % keys.length;
|
||||
|
||||
if (index == start) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
private int findEmptyIndex(Key key) {
|
||||
int index = index(key);
|
||||
int start = index;
|
||||
int deletedIndex = -1;
|
||||
|
||||
while (occupied[index]) {
|
||||
if (Objects.equals(keys[index], key)) {
|
||||
return index;
|
||||
}
|
||||
|
||||
index = (index + 1) % keys.length;
|
||||
|
||||
if (index == start) {
|
||||
return deletedIndex;
|
||||
}
|
||||
}
|
||||
|
||||
return deletedIndex == -1 ? index : deletedIndex;
|
||||
}
|
||||
|
||||
private int index(Key key) {
|
||||
return Math.floorMod(key == null ? 0 : key.hashCode(), keys.length);
|
||||
}
|
||||
|
||||
private void resize() {
|
||||
Key[] oldKeys = keys;
|
||||
Value[] oldValues = values;
|
||||
boolean[] oldOccupied = occupied;
|
||||
|
||||
keys = createArray(oldKeys.length * 2);
|
||||
values = createArray(oldValues.length * 2);
|
||||
occupied = new boolean[keys.length];
|
||||
deleted = new boolean[keys.length];
|
||||
size = 0;
|
||||
|
||||
for (int i = 0; i < oldKeys.length; i++) {
|
||||
if (oldOccupied[i]) {
|
||||
put(oldKeys[i], oldValues[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T> T[] createArray(int capacity) {
|
||||
return (T[]) new Object[capacity];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* This source file was generated by the Gradle 'init' task
|
||||
*/
|
||||
package org.example;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class AppTest {
|
||||
@Test void appHasAGreeting() {
|
||||
App classUnderTest = new App();
|
||||
assertNotNull(classUnderTest.getGreeting(), "app should have a greeting");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,319 @@
|
||||
package org.example;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class MyHashMapTest {
|
||||
|
||||
@Test
|
||||
void shouldPutAndGet() {
|
||||
MyHashMap<String, Integer> map = new MyHashMap<>(4);
|
||||
|
||||
map.put("A", 10);
|
||||
|
||||
assertEquals(10, map.get("A"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldUpdateValue() {
|
||||
MyHashMap<String, Integer> map = new MyHashMap<>(4);
|
||||
|
||||
map.put("A", 10);
|
||||
map.put("A", 20);
|
||||
|
||||
assertEquals(20, map.get("A"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnOldValueWhenUpdating() {
|
||||
MyHashMap<String, Integer> map = new MyHashMap<>(4);
|
||||
|
||||
assertNull(map.put("A", 10));
|
||||
assertEquals(10, map.put("A", 20));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnNullForMissingKey() {
|
||||
MyHashMap<String, Integer> map = new MyHashMap<>(4);
|
||||
|
||||
assertNull(map.get("A"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRemoveValue() {
|
||||
MyHashMap<String, Integer> map = new MyHashMap<>(4);
|
||||
|
||||
map.put("A", 10);
|
||||
|
||||
assertEquals(10, map.remove("A"));
|
||||
assertNull(map.get("A"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnNullWhenRemovingMissingKey() {
|
||||
MyHashMap<String, Integer> map = new MyHashMap<>(4);
|
||||
|
||||
assertNull(map.remove("A"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldHandleCollision() {
|
||||
MyHashMap<Integer, String> map = new MyHashMap<>(4);
|
||||
|
||||
map.put(1, "A");
|
||||
map.put(5, "B");
|
||||
|
||||
assertEquals("A", map.get(1));
|
||||
assertEquals("B", map.get(5));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldHandleRemoveWithCollision() {
|
||||
MyHashMap<Integer, String> map = new MyHashMap<>(4);
|
||||
|
||||
map.put(1, "A");
|
||||
map.put(5, "B");
|
||||
map.put(9, "C");
|
||||
|
||||
assertEquals("A", map.remove(1));
|
||||
assertEquals("B", map.get(5));
|
||||
assertEquals("C", map.get(9));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldResize() {
|
||||
MyHashMap<Integer, String> map = new MyHashMap<>(2);
|
||||
|
||||
map.put(1, "A");
|
||||
map.put(2, "B");
|
||||
map.put(3, "C");
|
||||
|
||||
assertEquals("A", map.get(1));
|
||||
assertEquals("B", map.get(2));
|
||||
assertEquals("C", map.get(3));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReuseDeletedSlot() {
|
||||
MyHashMap<Integer, String> map = new MyHashMap<>(4);
|
||||
|
||||
map.put(1, "A");
|
||||
map.put(5, "B");
|
||||
|
||||
map.remove(1);
|
||||
map.put(9, "C");
|
||||
|
||||
assertEquals("C", map.get(9));
|
||||
assertEquals("B", map.get(5));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldHandleNullKey() {
|
||||
MyHashMap<String, Integer> map = new MyHashMap<>(4);
|
||||
|
||||
map.put(null, 100);
|
||||
|
||||
assertEquals(100, map.get(null));
|
||||
assertEquals(100, map.remove(null));
|
||||
assertNull(map.get(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldHandleNullValue() {
|
||||
MyHashMap<String, Integer> map = new MyHashMap<>(4);
|
||||
|
||||
assertNull(map.put("A", null));
|
||||
assertNull(map.get("A"));
|
||||
|
||||
assertNull(map.put("A", 10));
|
||||
assertEquals(10, map.get("A"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldUpdateNullValue() {
|
||||
MyHashMap<String, Integer> map = new MyHashMap<>(4);
|
||||
|
||||
map.put("A", 10);
|
||||
|
||||
assertEquals(10, map.put("A", null));
|
||||
assertNull(map.get("A"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRemoveMiddleElementFromCollisionChain() {
|
||||
MyHashMap<Integer, String> map = new MyHashMap<>(4);
|
||||
|
||||
map.put(1, "A");
|
||||
map.put(5, "B");
|
||||
map.put(9, "C");
|
||||
|
||||
assertEquals("B", map.remove(5));
|
||||
|
||||
assertEquals("A", map.get(1));
|
||||
assertNull(map.get(5));
|
||||
assertEquals("C", map.get(9));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRemoveLastElementFromCollisionChain() {
|
||||
MyHashMap<Integer, String> map = new MyHashMap<>(4);
|
||||
|
||||
map.put(1, "A");
|
||||
map.put(5, "B");
|
||||
map.put(9, "C");
|
||||
|
||||
assertEquals("C", map.remove(9));
|
||||
|
||||
assertEquals("A", map.get(1));
|
||||
assertEquals("B", map.get(5));
|
||||
assertNull(map.get(9));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReinsertRemovedKey() {
|
||||
MyHashMap<String, Integer> map = new MyHashMap<>(4);
|
||||
|
||||
map.put("A", 10);
|
||||
assertEquals(10, map.remove("A"));
|
||||
|
||||
assertNull(map.get("A"));
|
||||
assertNull(map.put("A", 20));
|
||||
assertEquals(20, map.get("A"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldKeepCollidingEntriesAfterRemovingFirst() {
|
||||
MyHashMap<Integer, String> map = new MyHashMap<>(4);
|
||||
|
||||
map.put(1, "A");
|
||||
map.put(5, "B");
|
||||
map.put(9, "C");
|
||||
|
||||
map.remove(1);
|
||||
|
||||
assertEquals("B", map.get(5));
|
||||
assertEquals("C", map.get(9));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldKeepCollidingEntriesAfterRemovingMiddle() {
|
||||
MyHashMap<Integer, String> map = new MyHashMap<>(4);
|
||||
|
||||
map.put(1, "A");
|
||||
map.put(5, "B");
|
||||
map.put(9, "C");
|
||||
map.put(13, "D");
|
||||
|
||||
map.remove(5);
|
||||
|
||||
assertEquals("A", map.get(1));
|
||||
assertEquals("C", map.get(9));
|
||||
assertEquals("D", map.get(13));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldHandleManyCollisions() {
|
||||
MyHashMap<Integer, String> map = new MyHashMap<>(2);
|
||||
|
||||
map.put(1, "A");
|
||||
map.put(3, "B");
|
||||
map.put(5, "C");
|
||||
map.put(7, "D");
|
||||
map.put(9, "E");
|
||||
|
||||
assertEquals("A", map.get(1));
|
||||
assertEquals("B", map.get(3));
|
||||
assertEquals("C", map.get(5));
|
||||
assertEquals("D", map.get(7));
|
||||
assertEquals("E", map.get(9));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldResizeAndKeepCollisions() {
|
||||
MyHashMap<Integer, String> map = new MyHashMap<>(2);
|
||||
|
||||
map.put(1, "A");
|
||||
map.put(3, "B");
|
||||
map.put(5, "C");
|
||||
map.put(7, "D");
|
||||
map.put(9, "E");
|
||||
map.put(11, "F");
|
||||
|
||||
assertEquals("A", map.get(1));
|
||||
assertEquals("B", map.get(3));
|
||||
assertEquals("C", map.get(5));
|
||||
assertEquals("D", map.get(7));
|
||||
assertEquals("E", map.get(9));
|
||||
assertEquals("F", map.get(11));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldUpdateCollidingKey() {
|
||||
MyHashMap<Integer, String> map = new MyHashMap<>(4);
|
||||
|
||||
map.put(1, "A");
|
||||
map.put(5, "B");
|
||||
map.put(9, "C");
|
||||
|
||||
assertEquals("B", map.put(5, "NEW"));
|
||||
|
||||
assertEquals("A", map.get(1));
|
||||
assertEquals("NEW", map.get(5));
|
||||
assertEquals("C", map.get(9));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldHandleEqualButDifferentStringInstances() {
|
||||
MyHashMap<String, Integer> map = new MyHashMap<>(4);
|
||||
|
||||
map.put(new String("A"), 10);
|
||||
|
||||
assertEquals(10, map.get(new String("A")));
|
||||
assertEquals(10, map.remove(new String("A")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldNotAffectOtherKeysWhenUpdating() {
|
||||
MyHashMap<Integer, String> map = new MyHashMap<>(4);
|
||||
|
||||
map.put(1, "A");
|
||||
map.put(2, "B");
|
||||
map.put(3, "C");
|
||||
|
||||
map.put(2, "UPDATED");
|
||||
|
||||
assertEquals("A", map.get(1));
|
||||
assertEquals("UPDATED", map.get(2));
|
||||
assertEquals("C", map.get(3));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldNotFindKeyAfterRemoveAndResize() {
|
||||
MyHashMap<Integer, String> map = new MyHashMap<>(2);
|
||||
|
||||
map.put(1, "A");
|
||||
map.put(2, "B");
|
||||
map.put(3, "C");
|
||||
map.put(4, "D");
|
||||
|
||||
map.remove(2);
|
||||
|
||||
assertNull(map.get(2));
|
||||
assertEquals("A", map.get(1));
|
||||
assertEquals("C", map.get(3));
|
||||
assertEquals("D", map.get(4));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldHandleRepeatedPutAndRemove() {
|
||||
MyHashMap<String, Integer> map = new MyHashMap<>(4);
|
||||
|
||||
for (int i = 0; i < 100; i++) {
|
||||
assertNull(map.put("A", i));
|
||||
assertEquals(i, map.get("A"));
|
||||
assertEquals(i, map.remove("A"));
|
||||
assertNull(map.get("A"));
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user