Implementing isEmpty() for Linked Dictionary from scratch in Java - java

Currently, I'm working on a project in which I am implementing a Linked Dictionary interface from scratch. It was smooth sailing until I realized my code claims the entire dictionary is empty as soon as it sees a single null value.
public class LinkedDictionary<K, V> implements Dictionary<K, V> {
/** List node. */
private class Node {
K key;
V value;
Node next;
Node(K key, V value, Node next) {
this.key = key;
this.value = value;
this.next = next;
}
}
/** The first node in this dictionary, or null if this dictionary is empty. */
private Node front;
#Override
public void put(K key, V value) {
if(value == null) { // if a null value is given, the dictionary is empty
isEmpty();
return;
}
for (Node n = front; n != null; n = n.next) {
if (n.key.equals(key)) {
return;
}
}
front = new Node(key, value, front); // front node now contains new key and value
}
#Override
public V get(K key) {
if (key == null) // if a null key is given, null is returned
return null;
for (Node n = front; n != null; n = n.next) {
if (n.key.equals(key)) {
return n.value; // if they key is within the contents of the dictionary, return the corresponding value
}
}
return null; //if the key does not appear in the dictionary, null is returned.
}
#Override
public boolean isEmpty() {
return front == null;
}
}
I see my problem pretty clearly. In my "put" method, if the user tries to input a null value, the entire dictionary is marked as "empty."
I'm imagining this scenario:
Dictionary<Integer, String> d = new LinkedDictionary<>();
d.put(1, "one");
d.put(2, "two");
d.put(1, null);
As soon as my implementation reaches the third line in which the key 1 is set to null, it is going to set the entire dictionary to empty, despite the fact there is still a valid entry at key 2.
I've been wracking my brain over how to alter my code to allow for this sort of scenario, but I cannot figure it out. I've tried altering my isEmpty method, and I've even tried adding an additional method, but I just can't seem to figure out what to do here!
Any pointers or shoves in the right direction would be much appreciated!

public final class LinkedDictionary<K, V> {
private Node<K, V> head;
private Node<K, V> tail;
private int size;
public int getSize() {
return size;
}
public void put(K key, V value) {
Objects.requireNonNull(key, "Node.key");
Node<K, V> node = new Node<>(key, value);
remove(key);
if (isEmpty())
head = node;
else
tail.next = node;
size++;
tail = node;
}
public V remove(K key) {
Objects.requireNonNull(key, "Node.key");
if (isEmpty())
return null;
if (head.key.equals(key)) {
V value = head.value;
head = head.next;
tail = head == null ? null : tail;
size--;
return value;
}
Node<K, V> prv = head;
Node<K, V> it = head;
while (it != null) {
if (it.key.equals(key)) {
V value = it.value;
prv.next = it.next;
it.next = null;
size--;
return value;
}
prv = it;
it = it.next;
}
return null;
}
public V get(K key) {
Objects.requireNonNull(key, "Node.key");
Node<K, V> node = head;
while (node != null) {
if (node.key.equals(key))
return node.value;
node = node.next;
}
return null;
}
public boolean isEmpty() {
return head == null;
}
private static class Node<K, V> {
private final K key;
private final V value;
private Node<K, V> next;
public Node(K key, V value) {
this.key = key;
this.value = value;
}
}
}

I'm copy yours code excluding implementation and #Override, everything works fine
and d.isEmpty() give me a false as result

Related

How to add and compare keys of a generic hash table in Java?

I'm writing my own class for a generic hash table and trying to create an adding method as well as a method for comparing keys and returning true or false if they exist. So far my add method is not adding anything into my buckets, and my in my main, I cant pass any data type into my generic compare method without it asking me to define a new method with that type. Could anyone help point out the issues with my code as I'm trying to learn how to implement the data structure? Thank you.
My node and my table classes
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Objects;
class GenericHashNode <K, V> {
K key;
V value;
GenericHashNode<K, V> next;
public GenericHashNode (K key, V value) {
this.key = key;
this.value = value;
next = null;
}
public GenericHashNode() {
next = null;
}
}
public class GenericHashTable <K, V> {
private ArrayList<GenericHashNode<K, V> > hashArray;
private int tableSize;
public GenericHashTable(int tableSize)
{
hashArray = new ArrayList<>(tableSize);
this.tableSize = tableSize;
for (int i = 0; i < tableSize; i++) {
hashArray.add(new GenericHashNode());
}
}
private int getTableSize() {
return tableSize;
}
private int createHashCode(K key)
{
int h = key.hashCode();
h = h % getTableSize();
int hashCode = h < 0 ? h -= -h : h;
return hashCode;
}
public void add(K key) {
int hashIndex = createHashCode(key);
GenericHashNode head = hashArray.get(hashIndex);
GenericHashNode newNode = new GenericHashNode(key, key);
newNode.next = head.next;
head.next = newNode;
}
public boolean compare(K key) {
int hashIndex = createHashCode(key);
GenericHashNode current = new GenericHashNode();
current = hashArray.get(0);
Iterator<GenericHashNode<K, V>> iterate = hashArray.iterator();
while(iterate.hasNext()) {
if(current == null) {
current = current.next;
}
else {
if(current.key == key) {
return true;
}
else {
current = current.next;
}
}
}
return false;
}
}

How to implement compareTo() for two nodes in a linked list

I am new to Java and I am trying to implement the Comparable interface for a linked list. I have a standard linked list.
public class LinkedList<E extends Comparable<E>>{
private static class Node<T> {
private T value;
private Node<T> next;
private Node(T value, Node<T> next) {
this.value = value;
this.next = next;
}
}
private Node<E> head;
private int size=0;
public boolean isEmpty() {
return head == null;
//or
//return size == 0;
}
public void addFirst(E elem) {
if (elem == null) {
throw new NullPointerException();
}
head = new Node<E>(elem, head);
size++;
}
public void addLast(E elem) {
if (elem == null) {
throw new NullPointerException();
}
if (head == null) {
head = new Node<E>(elem, null);
} else {
Node<E> current = head;
while (current.next != null) {
current = current.next;
}
current.next = new Node<E>(elem, null);
}
size++;
}
//adding at a specific index
public void add(E elem, int index) {
if (elem == null) {
throw new NullPointerException();
}
if (index < 0 || index > size) {
throw new IndexOutOfBoundsException(Integer.toString(index));
}
if (index == 0) {
head = new Node<E>(elem, head);
} else {
Node<E> p = head;
for (int i=0; i<(index-1); i++) {
p = p.next;
}
p.next = new Node<E>(elem, p.next);
}
size++;
}
public E removeFirst() {
if (head == null) {
throw new NullPointerException();
}
E saved = head.value;
head = head.next;
size--;
return saved;
}
public E removeLast() {
if (head == null) {
throw new NullPointerException();
}
E saved;
if (head.next == null) {
saved = head.value;
head = null;
} else {
Node<E> p = head;
while (p.next.next != null) {
p = p.next;
}
saved = p.next.value;
p.next = null;
}
size--;
return saved;
}
public int size() {
return size;
}
public String toString() {
String str = "[";
Node<E> p = head;
while (p!=null) {
if (p != head) {
str += ", ";
}
str += p.value;
p = p.next;
}
str += "]";
return str;
}
public int compareTo(Node<E> other){
return Integer.compare(this.value,other.value);
}
}
Currently I am assuming the contents of the list are of type int. When I try to compile, I get the error as
LinkedList.java:144: error: cannot find symbol
return Integer.compare(this.value,other.value);
^
Based on what I can understand, it's because I am trying to compare Objects of type Node and not linkedList, therefore "this" is referring linkedList. I am not sure how I can change my code to be able to compare two nodes. Any help or suggestions is appreciated.
Edit:
Is my general methodology incorrect or just my implementation? Is there another way I should implement compareTo? e.g. Add it as a method in the class node?
value is a field in Node and this in your current compareTo method refers to a the linked list object.
You could make the Node class to implement Comparable as below:
public static class Node<T> implements Comparable<Node<T>> {
// rest of the code
#Override
public int compareTo(Node<T> other) {
// this.value is accessible.
}
}
Now in your LinkedList class write another compareTo method (or any other name as this method is not related to Comparable.compareTo) which would invoke the above.
public int compareTo(Node<E> other) {
return this.head.compareTo(other);
}
NOTE: In your current code, you haven't actually "implemented" the Comparable interface. You have only mentioned that the generic type E is a Comparable type.
You didn't understand this<E extends Comparable<E>>,There are two solutions
1.
public class LinkedList<E extends Comparable<E>>{
//public class LinkedList<E extends Integer>{
private static class Node<T> {
private T value;
private Node<T> next;
private Node(T value, Node<T> next) {
this.value = value;
this.next = next;
}
}
private Node<E> head;
private int size=0;
public int compareTo(Node<E> other){
return this.head.value.compareTo(other.value);
//return Integer.compare(this.head.value, other.value);
}
}
//public class LinkedList<E extends Comparable<E>>{
public class LinkedList<E extends Integer>{
private static class Node<T> {
private T value;
private Node<T> next;
private Node(T value, Node<T> next) {
this.value = value;
this.next = next;
}
}
private Node<E> head;
private int size=0;
public int compareTo(Node<E> other){
//return this.head.value.compareTo(other.value);
return Integer.compare(this.head.value, other.value);
}
}

remove(Object key) for a Sorted Binary Tree

I'm tasked to implement a remove() method for my Sorted Binary Tree. I need to pass a set of tests which was assigned by our teacher, and I'm struggling to do so. This is the code I have currently, which was heavily borrowed from our schoolbook.
#Override
#SuppressWarnings("unchecked")
public V remove(Object key) throws NoSuchElementException {
if(!containsKey((K) key)){
throw new NoSuchElementException();
}
ReturnObject oldEntry = new ReturnObject(null);
SortedTreeMap<K, V> newRoot = removeEntry(getRoot(this), (K) key, oldEntry);
if(newRoot != null){
setRoot(newRoot.data);
setLeftChild(newRoot.leftChild);
setRightChild(newRoot.rightChild);
} else{
this.data = null;
}
if(oldEntry.get() != null){
size--;
}
return oldEntry.get();
}
/*
* Removes the entry in a given root node of a subtree.
* rootNode is the root node of the subtree.
* Returns the root node of the revised subtree.
*
* */
private SortedTreeMap<K,V> removeFromRoot(SortedTreeMap<K, V> rootNode){
if(rootNode.hasLeftChild() && rootNode.hasRightChild()){
SortedTreeMap<K,V> leftSubtreeRoot = rootNode.getLeftChild();
SortedTreeMap<K,V> largestNode = findLargest(leftSubtreeRoot);
rootNode.setRoot(largestNode.getRoot());
rootNode.setLeftChild(removeLargest(leftSubtreeRoot));
} else if(rootNode.hasRightChild()) {
rootNode = rootNode.getRightChild();
} else{
rootNode = rootNode.getLeftChild();
}
return rootNode;
}
/*
* Finds the node containing the largest entry in a given tree.
* rootNode is the root node of the tree.
* Returns the node containing the largest entry in the tree.
*
* */
private SortedTreeMap<K,V> findLargest(SortedTreeMap<K, V> rootNode){
if(rootNode.hasRightChild()){
rootNode = findLargest(rootNode.getRightChild());
}
return rootNode;
}
/*
* Removes the node containing the largest entry in a given tree.
* rootNode is the root node of the tree.
* Returns the root node of the revised tree.
*
* */
private SortedTreeMap<K,V> removeLargest(SortedTreeMap<K, V> rootNode){
if(rootNode.hasRightChild()){
SortedTreeMap<K,V> rightEntry = rootNode.getRightChild();
rightEntry = removeLargest(rightEntry);
rootNode.setRightChild(rightEntry);
} else{
rootNode = rootNode.getLeftChild();
}
return rootNode;
}
/*
* Removes an entry from the tree rooted at a given node.
* rootNode is a reference to the root of a tree.
* entry is the object to be removed.
* oldEntry is an object whose data field is null
* Returns the root node of the resulting tree; if entry matches
* an entry in the tree, oldEntry's data field is the entry
* that was removed from the tree; otherwise it is null.
* */
private SortedTreeMap<K,V> removeEntry(SortedTreeMap<K,V> rootNode, K entry, ReturnObject oldEntry){
if(rootNode != null){
K rootData = rootNode.data.key;
int comparison = entry.compareTo(rootData);
if(comparison == 0){
oldEntry.set(rootNode.data.value);
rootNode = removeFromRoot(rootNode);
} else if(comparison < 0){
SortedTreeMap<K,V> leftEntry = rootNode.getLeftChild();
SortedTreeMap<K,V> subtreeRoot = removeEntry(leftEntry, entry, oldEntry);
rootNode.setLeftChild(subtreeRoot);
} else{
SortedTreeMap<K,V> rightEntry = rootNode.getRightChild();
rootNode.setRightChild(removeEntry(rightEntry, entry, oldEntry));
}
}
return rootNode;
}
And this is the test I'm struggling to pass:
/**
* Check that entry is no longer in map after remove
*/
public Property remove_removes_entry() {
return property(isKVList,
kvs -> implies(kvs.length() > 0,
() -> property(choose(0, kvs.length()-1),
i -> {
P2<Integer, String> entry = kvs.index(i);
SortedTreeMap<Integer, String> tm = new SortedTreeMap<>(intOrd.toComparator());
kvs.foreachDoEffect(kv -> tm.add(kv._1(), kv._2()));
tm.remove(entry._1());
List<Integer> keys = fromIterator(tm.keys().iterator());
return prop(!keys.exists(key -> key.equals(entry._1())));
})
)
);
}
The error message I'm getting is this:
java.lang.Error: Falsified after 2 passed tests with arguments:
List(List((0,gzprxt),(4,lntpqj),(-5,caki),(-6,jzf)),2)
I can get in between 0 to 60 passes and I don't get which scenario the remove method fails in. I've tried to make a Junit test, but didn't succeed.
Here is some more context for my class:
public class Entry<K, V> {
public final K key;
public final V value;
public Entry(K key, V value) {
this.key = key;
this.value = value;
}
#Override
public boolean equals(Object o) {
if (o instanceof Entry) {
Entry other = (Entry)o;
return this.key.equals(other.key) && this.value.equals(other.value);
}
return false;
}
}
public class SortedTreeMap<K extends Comparable<? super K>, V> implements ISortedTreeMap<K, V> {
private int size;
private Entry<K,V> data;
private SortedTreeMap<K,V> leftChild, rightChild, parent;
private Comparator<K> keyComparator;
private SortedTreeMap(K key, V value, Comparator<K> keyComparator, SortedTreeMap<K, V> parent){
data = new Entry<>(key, value);
this.parent = parent;
this.keyComparator = keyComparator;
}
public SortedTreeMap(Comparator<K> kComparator) {
keyComparator = kComparator;
}
private Entry<K,V> getRoot(){
return this.data;
}
private SortedTreeMap<K,V> getLeftChild(){
return this.leftChild;
}
private SortedTreeMap<K,V> getRightChild(){
return this.rightChild;
}
private void setLeftChild(SortedTreeMap<K, V> newLeftChild){
leftChild = newLeftChild;
}
private void setRightChild(SortedTreeMap<K, V> newRightChild){
rightChild = newRightChild;
}
private void setRoot(Entry<K, V> newRoot){
data = newRoot;
}
private boolean hasLeftChild(){
return leftChild != null;
}
private boolean hasRightChild(){
return rightChild != null;
}
#Override
public V add(K key, V value) {
V result = null;
if(isEmpty()) {
data = new Entry<>(key, value);
} else{
int comparison = keyComparator.compare(key, data.key);
SortedTreeMap<K,V> newChild = new SortedTreeMap<>(key, value, keyComparator, this);
if(comparison < 0){
if (hasLeftChild()) {
result = leftChild.add(key, value);
} else{
setLeftChild(newChild);
}
} else if(comparison > 0){
if (hasRightChild()) {
result = rightChild.add(key, value);
} else{
setRightChild(newChild);
}
} else{
result = data.value;
this.data = new Entry<>(key, value);
}
}
if (result == null){
size++;
}
return result;
}
#Override
public Iterable<K> keys() {
return new KeyIterator(this);
}
public class KeyIterator implements Iterable<K>, Iterator<K>{
private SortedTreeMap<K,V> next;
KeyIterator(SortedTreeMap<K, V> root){
next = root;
while(next.leftChild != null){
next = next.leftChild;
}
}
#Override
public Iterator<K> iterator() {
return this;
}
public boolean hasNext(){
return next != null && !next.isEmpty();
}
public K next(){
if(!hasNext()){
throw new NoSuchElementException();
}
SortedTreeMap<K,V> r = next;
if(next.rightChild != null){
next = next.rightChild;
while(next.leftChild != null){
next = next.leftChild;
}
return r.data.key;
}
while(true){
if(next.parent == null){
next = null;
return r.data.key;
}
if(next.parent.leftChild == next){
next = next.parent;
return r.data.key;
}
next = next.parent;
}
}
}

Java Generic HashMap implementation: Object cannot be converted V

I'm trying to implement a generic HashMap, but for some reason the java compiler will not allow me to return the proper generic type.
Here is my HashMap code:
public class SimpleHashMap<K,V> {
private int tableSize;
private HashEntry[] table;
public SimpleHashMap(){
table = new HashEntry[tableSize];
for(int i = 0; i < table.length; i++){
table[i] = null;
}
}
public V put(K key, V value){
int keyIndex = getHashCode(key);
if(table[keyIndex] == null){
table[keyIndex] = new HashEntry<K, V>(key, value);
}
else{
table[keyIndex] = new HashEntry<K, V>(key, value, table[keyIndex]);
}
return value;
}
public V get(K key){
int keyIndex = getHashCode(key);
if(table[keyIndex] == null){
return null;
}
else{
HashEntry temp = table[keyIndex];
while(temp != null){
if(temp.key.equals(key)){
return temp.value;
}
temp = temp.next;
}
}
}
public int getHashCode(K key){
return key.hashCode() % tableSize;
}
}
Here is my HashEntry code:
public class HashEntry<K,V>{
public K key;
public V value;
public HashEntry next;
public HashEntry(K key, V value){
this(key, value, null);
}
public HashEntry(K key, V value, HashEntry next){
this.key = key;
this.value = value;
this.next = next;
}
}
The only error I get at compile time is:
error: incompatible types: Object cannot be converted to V
return temp.value;
^
where V is a type-variable:
V extends Object declared in class SimpleHashMap
I've tried explicitly casting it, but it still refuses to return a object of type V.
You need to declare your temp variable with type like this:
HashEntry<K,V> temp = table[keyIndex];
Your get method can be updated as follows:
public V get(K key){
int keyIndex = getHashCode(key);
if(table[keyIndex] == null){
return null;
}
else{
HashEntry<K,V> temp = table[keyIndex];
while(temp != null){
if(temp.key.equals(key)){
return temp.value;
}
temp = temp.next;
}
return temp.value;
}
}
HashEntry temp = table[keyIndex];
HashEntry is a generic type, but you are using it without type information.
If you want to use it like that, you have to make HashEntry a non-generic inner class and re-use the outer class type bounds.
public class HashEntry{ // has to be inside SimpleHashMap
public K key; // <-- type variables from
public V value; // <-- SimpleHashMap
public HashEntry next;
public HashEntry(K key, V value){
this(key, value, null);
}
public HashEntry(K key, V value, HashEntry next){
this.key = key;
this.value = value;
this.next = next;
}
}
The other possibility is to leave HashEntry as it is and change the line to
HashEntry<K, V> temp = table[keyIndex];

Implementing a remove method in a java Hashmap?

So a hashmap is a hash-based implementation of a map structure in java. I've figured out how to get the hashmap put method to work, but I want to write a method that removes the key value pair, and I'm having trouble implementing it.
The only thing I can really understand right now is how to tell the function to stop in the event that the key is empty or doesn't exist.. I'd love any sort of help. An explanation as to how the method will work, or some basic pseudo-code examples would be much appreciated.
This is what I have in the delete method so far:
public void delete(K key) {
if (key == null) {
throw new IllegalArgumentException("Null Key!");
}
// Implement this method
}
If it helps, here is my completed Map Entry class:
public class MapEntry<K, V> {
MapEntry<K, V> next;
K key;
V value;
public MapEntry(K key, V value) {
this.setKey(key);
this.setValue(value);
}
public void setKey(K key) {
this.key = key;
}
public void setValue(V value) {
this.value = value;
}
public K getKey() {
return key;
}
public V getValue() {
return value;
}
public void setNext(MapEntry<K, V> next) {
this.next = next;
}
public MapEntry<K, V> getNext() {
return next;
}
}
Also, here's the entirety of my HashMap class if it helps.
public class HashMap<K, V> {
private int DEFAULT_CAPACITY = 10;
private MapEntry<K, V>[] Hash;
private int size;
public HashMap() {
Hash = new MapEntry[DEFAULT_CAPACITY];
}
public int getHashCode(K key) {
int bucketIndex = key.hashCode() % Hash.length;
return bucketIndex;
}
public V get(K key) {
if (key == null) {
throw new IllegalArgumentException("Null Key!");
}
MapEntry<K, V> entry = Hash[getHashCode(key)];
while (entry != null && !key.equals(entry.getKey()))
entry = entry.getNext();
if (entry != null)
return entry.getValue();
else
return null;
}
/**
*
* #param key
* #param value
* The put method works by associating the specified value with
* the given key in the map.
* If the key is already in the map,
* the old value is replaced with the new one.
*/
public void put(K key, V value) {
int keyBucket = hash(key);
MapEntry<K, V> temp = Hash[keyBucket];
while (temp != null) {
if ((temp.key == null && key == null)
|| (temp.key != null && temp.key.equals(key))) {
temp.value = value;
return;
}
temp = temp.next;
}
Hash[keyBucket] = new MapEntry<K, V>(key, value);
size++;
}
public void delete(K key) {
if (key == null) {
throw new IllegalArgumentException("Null Key!");
}
// Implement this method
}
public void print(){
//Bonus Method
}
private int hash(K key) {
if (key == null) {
return 0;
} else {
return Math.abs(key.hashCode() % this.Hash.length);
}
} }
Using the same logic that you do in get(), locate the correct bucket and, within that bucket, the correct MapEntry (let's call it e). Then simply remove e from the bucket—basically, this is removing a node from a single-linked list. If e is the first element in the bucket, set the corresponding element of Hash to e.next; otherwise set the next field of the element just before e to e.next. Note that you need one more variable (updated as you're finding e) to keep track of the previous entry in the bucket.
source code
public class MyInternalMap<K, V> implements Map<K, V> {
/**
* https://stackoverflow.com/questions/16266459/implementing-a-remove-method-in-a-java-hashmap
*/
private final int initialCapacity;
private MyMapEntry<K, V>[] mapEntries;
private int size;
public MyInternalMap() {
this(16);
}
public MyInternalMap(int initialCapacity) {
this.initialCapacity = initialCapacity;
mapEntries = new MyMapEntry[initialCapacity];
}
#Override
public int size() {
return size;
}
#Override
public boolean isEmpty() {
return size == 0;
}
#Override
public boolean containsKey(Object key) {
return get(key) != null;
}
#Override
public boolean containsValue(Object value) {
for (int i = 0; i < mapEntries.length; i++) {
MyMapEntry<K, V> mapEntry = mapEntries[i];
if (containsValue(value, mapEntry)) {
return true;
}
}
return false;
}
private boolean containsValue(Object value, MyMapEntry<K, V> mapEntry) {
if (mapEntry == null) {
return false;
}
if (value == mapEntry.getValue() || mapEntry.getValue().equals(value)) {
return true;
}
return containsValue(value, mapEntry.next);
}
#Override
public V get(Object key) {
if (key == null) {
return null;
}
MyMapEntry<K, V> entry = mapEntries[getHashCode(key)];
while (entry != null) {
if (key.equals(entry.key)) {
return entry.value;
}
entry = entry.next;
}
return null;
}
#Override
public V put(K key, V value) {
int keyBucket = getHashCode(key);
MyMapEntry<K, V> temp = mapEntries[keyBucket];
if (temp == null) {
//create new head node in this bucket
mapEntries[keyBucket] = new MyMapEntry<>(key, value);
size++;
return null;
}
while (temp != null) {
if ((temp.key == null && key == null)
|| (temp.key != null && temp.key.equals(key))) {
V returnValue = temp.value;
temp.value = value;
return returnValue;
}
temp = temp.next;
}
//create new node in this bucket
mapEntries[keyBucket].next = new MyMapEntry<>(key, value);
size++;
return null;
}
#Override
public V remove(Object key) {
/**
* Using the same logic that you do in get(), locate the correct bucket and, within that bucket, the correct MapEntry (let's call it e). Then simply remove e from the bucket—basically,
* this is removing a node from a single-linked list. If e is the first element in the bucket, set the corresponding element of Hash to e.next;
* otherwise set the next field of the element just before e to e.next. Note that you need one more variable (updated as you're finding e) to keep track of the previous entry in the bucket
*/
int keyBucket = getHashCode(key);
MyMapEntry<K, V> temp = mapEntries[keyBucket];
if (temp == null)
return null;
MyMapEntry<K, V> prev = temp;
while (temp != null) {
if (temp.key != null && temp.key.equals(key)) {
V valueReturn = temp.value;
if (prev == temp) { //first element?
mapEntries[keyBucket] = temp.next;
} else {
prev.next = temp.next;
}
size--;
return valueReturn;
}
prev = temp;
temp = temp.next;
}
return null;
}
#Override
public void putAll(Map<? extends K, ? extends V> m) {
//TODO impl
}
#Override
public void clear() {
mapEntries = new MyMapEntry[initialCapacity];
size = 0;
}
#Override
public Set<K> keySet() {
Set<K> resultKeys = new HashSet<>();
for (int i = 0; i < mapEntries.length; i++) {
MyMapEntry<K, V> mapEntry = mapEntries[i];
addKeySet(mapEntry, resultKeys);
}
return resultKeys;
}
private void addKeySet(MyMapEntry<K, V> mapEntry, Set<K> resultKeys) {
if (mapEntry != null) {
resultKeys.add(mapEntry.key);
addKeySet(mapEntry.next, resultKeys);
}
}
#Override
public Collection<V> values() {
Collection<V> resultValues = new ArrayList<>();
for (int i = 0; i < mapEntries.length; i++) {
MyMapEntry<K, V> mapEntry = mapEntries[i];
addValue(mapEntry, resultValues);
}
return resultValues;
}
private void addValue(MyMapEntry<K, V> mapEntry, Collection<V> resultValues) {
if (mapEntry != null) {
resultValues.add(mapEntry.value);
addValue(mapEntry.next, resultValues);
}
}
#Override
public Set<Entry<K, V>> entrySet() {
Set<Entry<K, V>> entrySetResult = new HashSet<>();
for (int i = 0; i < mapEntries.length; i++) {
MyMapEntry<K, V> mapEntry = mapEntries[i];
addEntrySet(mapEntry, entrySetResult);
}
return entrySetResult;
}
private void addEntrySet(MyMapEntry<K, V> mapEntry, Set<Entry<K, V>> entrySetResult) {
if (mapEntry != null) {
entrySetResult.add(mapEntry);
addEntrySet(mapEntry.next, entrySetResult);
}
}
private int getHashCode(Object key) {
if (key == null)
return 0;
int bucketIndex = Math.abs(key.hashCode()) % initialCapacity;
return bucketIndex;
}
class MyMapEntry<K, V> implements Map.Entry<K, V> {
private K key;
private V value;
private MyMapEntry<K, V> next;
public MyMapEntry(K key, V value) {
this.key = key;
this.value = value;
}
#Override
public K getKey() {
return key;
}
#Override
public V getValue() {
return value;
}
#Override
public V setValue(V value) {
V oldValue = this.value;
this.value = value;
return oldValue;
}
}
}
public class MyInternalMapTest {
#ParameterizedTest()
#MethodSource({"mapArgumentsProvider"})
public void mapTest(Map<Integer, String> map) {
assertNull(map.get(0));
assertNull(map.get(null));
assertNull(map.remove(0));
assertNull(map.remove(null));
assertNull(map.remove(1));
assertEquals(0, map.size());
assertNull(map.put(1, "1"));
assertEquals(1, map.size());
assertEquals("1", map.put(1, "2"));
assertEquals(1, map.size());
assertEquals("2", map.get(1));
assertEquals(1, map.size());
assertNull(map.put(2, "3"));
assertEquals(2, map.size());
assertEquals("2", map.remove(1));
assertEquals(1, map.size());
assertNull(map.remove(1));
assertEquals("3", map.remove(2));
assertEquals(0, map.size());
}
#ParameterizedTest()
#MethodSource({"mapArgumentsProvider"})
public void mapSameHashCodeTest(Map<Integer, String> map) {
assertNull(map.put(1, "1"));
assertEquals("1", map.get(1));
assertNull(map.put(17, "2"));
assertEquals("1", map.get(1));
assertEquals("2", map.get(17));
assertEquals("1", map.get(1));
assertTrue(map.containsValue("1"));
assertTrue(map.containsValue("2"));
assertFalse(map.containsValue("3"));
assertEquals(Arrays.asList("1", "2"), map.values().stream().sorted().collect(Collectors.toList()));
}
private static Stream<Arguments> mapArgumentsProvider() {
return Stream.of(
Arguments.of(new MyInternalMap<>()),
Arguments.of(new HashMap<>())
);
}
}

Categories

Resources