Custom implementation of a Map collection in Java - java

I'm trying to create an implementation of a Map collection that stores a pair of key and value items.
The error occurs during runtime when I try to register a key-value pair and hit this line.
EntryNode<K, V> mapEntry = mapEntryList[mapSize];
I'm running out of ideas on what could be the issue, any help is appreciated. Thanks.
//Driver class to test output
public class Driver{
public static void main(String[] args) {
MyMap<String, String> mapInstance = new MyMap<String, String>();
myMap.register("Key1", "Value");
System.out.println(myMap.get("Key1"));
}
}
public class MyMap<K, V> implements MapInterface<K, V>{
private EntryNode<K, V>[] mapEntryList;
private int mapSize = 0;
public MyMap(){
}
public MyMap(int capacity){
this.mapEntryList = new EntryNode[mapSize];
}
//static
public class EntryNode<K, V>{
K keyElement;
V valueElement;
EntryNode<K, V> nextMapEntry;
public EntryNode(K keyElement, V valueElement, EntryNode<K, V> nextMapEntry) {
this.keyElement = keyElement;
this.valueElement = valueElement;
this.nextMapEntry = nextMapEntry;
}
public K getKey() {
return keyElement;
}
public V getValue() {
return valueElement;
}
public EntryNode<K, V> getNextMapEntry() {
return nextMapEntry;
}
public final V setNewValue(V newValueElement) {
V oldValueElement = valueElement;
valueElement = newValueElement;
return oldValueElement;
}
public String toString() {
return "{" + keyElement + ", " + valueElement + "}";
}
}
public int size() {
return mapSize;
}
public V get(K keyElement) {
EntryNode<K, V> mapEntry = mapEntryList[mapSize];
int count = mapSize;
boolean entryPresent = false;
EntryNode tempNode = firstEntry;
while (!entryPresent && mapEntry != null) {
if (keyElement == mapEntry.keyElement) {
entryPresent = true;
return mapEntry.valueElement;
}
else {
mapEntry = mapEntry.nextMapEntry;
}
}
return null;
}
public void register(K newKeyElement, V newValueElement) {
EntryNode<K, V> newEntry = new EntryNode(newKeyElement, newValueElement, null);
EntryNode<K, V> mapEntry = mapEntryList[mapSize];
boolean entryPresent = false;
while (!entryPresent && mapEntry != null) {
if (newKeyElement == mapEntry.keyElement) {
entryPresent = true;
break;
}
else {
if(mapEntry.nextMapEntry == null){
break;
}
mapEntry = mapEntry.nextMapEntry;
}
}
if(!entryPresent){
mapEntry.nextMapEntry = newEntry;
mapSize++;
}
}
public void remove(K removeKeyElement) {
EntryNode previousEntry = null;
EntryNode<K, V> mapEntry = mapEntryList[mapSize];
while (mapEntry != null) {
if (removeKeyElement == mapEntry.keyElement) {
mapEntry.keyElement = null;
mapEntry.valueElement = null;
previousEntry.nextMapEntry = mapEntry.nextMapEntry;
break;
}
else {
previousEntry = mapEntry;
mapEntry = mapEntry.nextMapEntry;
}
}
}
private int getEntriesSize() {
return mapEntryList.length;
}
public String toString() {
StringBuilder stringOutput = new StringBuilder();
for (EntryNode entry : mapEntryList) {
stringOutput.append("[");
while (entry != null) {
stringOutput.append(entry);
if (entry.nextMapEntry != null) {
stringOutput.append(", ");
}
entry = entry.nextMapEntry;
}
stringOutput.append("]");
}
return "{" + stringOutput.toString() + "}";
}
}

Let's say I make a new map, and then call .get() on it.
MyMap<Integer, String> m = new MyMap<>();
m.get(5);
This constructor (the no-args one) means that mapEntryList is never set, which means it defaults to null. Thus, when the get is invoked, the first thing your get method does is dereference the mapEntryList field (the foo[idx] construct dereferences foo). Dereferencing a null value means: NullPointerException is thrown. Clearly the intent of your code is to return null if the key is not in the map, so, that part is broken.
Alternatively, I'll go with:
MyMap<Integer, String> m = new MyMap<>(10);
m.get(5);
This time, I call the second constructor. This constructor takes the 'capacity' value, tosses it in the trash, and makes a 0-sized array. Then, get is invoked, and your code runs:
mapEntry = mapEntryList[mapSize];
This cannot work; you cannot get anything out of a 0-length array. In fact, if you write:
int[] a = new int[5];
a[5];
(as in, you make a new array of size X and then ask for the element with index X), you always get an IndexOutOfBoundsException: in java all arrays are 0-indexed. a[0] is the first element, and new int[1] makes a 1-size int array, so doing a[1] (asking for the second element) on a 1-size array is not possible (it only has one element).
There are about 50 other issues with this code, you really need to take it one small step at a time and debug this code: Instead of just looking at all this code and going: Uh, it doesn't work - you need to debug it:
Write some code, then run it. As you run it, 'mentally run it': Take pen and paper if you have to, walk through the code line by line and figure out what each line should be doing, by hand. Then check what you think should happen vs. what actually happens, either by using a debugger or adding a ton of System.out statements if you have to. There where the code does something different from what you thought? You found a bug. Probably the first in quite a large list of them. Fix it, and keep going until the bugs are gone.

Related

Linked HashMap - Iteration (Java) [duplicate]

This question already has answers here:
How do I efficiently iterate over each entry in a Java Map?
(46 answers)
Closed 3 years ago.
I'm trying to iterate through a Linked HashMap keySet however I am having difficulties in getting it to work.
Essentially I am searching the keySet to find a word, and another word. If the second word is immediately after the first word I wish to return true. This is the progress I have made so far.
for (String word : storedWords.keySet()) {
value0++;
if(word.equals(firstWord)){
value1 = value0;
}
if(word.equals(secondWord)){
value2 = value0;
}
int value3 = value2 - 1;
if(value1 == value3){
result = true;
break;
}
}
EDIT:
I've solved my problem and am thankful for all of those who helped. I apologise for making a post when there was a lot of information available on the website however I just lacked the understanding of the logic behind it.
You can avoid iterating over the whole keySet by storing the indices of each element in a separate map; then you can just test if both keys are present and have indices differing by 1. For convenience, encapsulate both maps into an object:
import java.util.*;
public class MapWithIndices<K, V> {
private final Map<K, V> map = new LinkedHashMap<>();
private final Map<K, Integer> indices = new HashMap<>();
public V get(K k) {
return map.get(k);
}
public V put(K k, V v) {
V old = map.put(k, v);
if(old == null) {
indices.put(k, indices.size());
}
return old;
}
public boolean areAdjacent(K k1, K k2) {
Integer i1 = indices.get(k1);
Integer i2 = indices.get(k2);
return i1 != null && i2 != null && i1 + 1 == i2;
}
}
You can add more Map methods (e.g. size) by delegating them to map. However, the remove method cannot be implemented efficiently since it requires recomputing all later indices. If removing from the map is required, an alternative data structure design should be considered; for example, indices can store the original insertion order of each key, and an order statistic tree can be used to count how many existing keys have a lower original-insertion-order.
Map<String, String> map = ...
for (Map.Entry<String, String> entry : map.entrySet()) {
System.out.println(entry.getKey() + "/" + entry.getValue());
}
I think this is sort of in line with what you started with. You might want to test the performance though.
import java.util.LinkedHashMap;
import java.util.Map;
class Testing {
Map<String, Integer> storedWords = new LinkedHashMap<>();
{
storedWords.put("One",1);
storedWords.put("Two",2);
storedWords.put("Three",3);
storedWords.put("Four",4);
storedWords.put("Five",5);
}
public static void main(String[] args) {
Testing t = new Testing();
String firstWord;
String secondWord;
firstWord = "Three";
secondWord = "Five";
System.out.println(t.consecutive(firstWord, secondWord)); // false
firstWord = "Two";
secondWord = "Three";
System.out.println(t.consecutive(firstWord, secondWord)); // true
}
public boolean consecutive(String firstWord, String secondWord) {
boolean foundfirst = false;
for (String word : storedWords.keySet()) {
if (!foundfirst && word.equals(firstWord)){
foundfirst = true;
continue;
}
if (foundfirst) {
if(word.equals(secondWord)){
return true;
} else {
foundfirst = false; // reset to search for the first word again
}
}
}
return false;
}
}

Get message from hashmap by approximate ID value, not identical one

Say I have a HashMap like this:
message.put(10, "Message 1");
message.put(20, "Message 2");
message.put(30, "Message 3");
message.put(40, "Message 4");
I would get a message comparing a given ID:
if( message.containsKey(sampleValue) ) {
return message.get(sampleValue);
}
But that's useless if sampleValue isn't contained in the message hashmap. Is there a way or function to get it by approximate ID value? For example, if sampleValue is 19, it would return "Message 2". How can I achieve this?
You could use a TreeMap for your task. It contains ceilingKey/floorKey methods returning closest keys from the right/left. So, these methods can be used for finding the closest key and retrieving its corresponding value in O(Log(N)).
class ClosestKeyTreeMap extends TreeMap<Integer, Object> {
public Object getClosestValue(int key) {
Integer leftKey = this.floorKey(key);
Integer rightKey = this.ceilingKey(key);
if (leftKey == null && rightKey == null) {
return null;
} else if (rightKey == null) {
return this.get(leftKey);
} else if (leftKey == null) {
return this.get(rightKey);
}
int leftDiff = key - leftKey;
int rightDiff = rightKey - key;
if (leftDiff < rightDiff) {
return this.get(leftKey);
} else {
return this.get(rightKey);
}
}
}
I believe you are trying to get the value of the closest to the input. Then I will suggest first get the closest integer from the KeySet. Once you get that you just use map.get(closet_int) to retrieve the value.
HashMap<Integer, String> message = new HashMap<>();
message.put(10, "Message 1");
message.put(20, "Message 2");
message.put(30, "Message 3");
message.put(40, "Message 4");
int input = 19; // change the input as you want, I have set it 19 for testing
Integer c = message.keySet().stream()
.min(Comparator.comparingInt(i -> Math.abs(i - input))).get(); // find the closest number to the input
String result = message.get(c);
System.out.println("result : "+result);
Output:
result : Message 2
int input = 19;
int roundInput = BigDecimal
.valueOf(input)
.divide(BigDecimal.TEN,0,BigDecimal.ROUND_HALF_UP)
.multiply(BigDecimal.TEN)
.intValue();
You can extends HashMap to add your specific desired behaviour:
public class ApproximateKeyMap<K, V> extends HashMap<K, V> {
#Override
public V get(Object key) {
// Map your key -> approximated key
K approximatedKey = ...
return super.get(approximatedKey);
}
}
Now use your ApproximateKeyMap instead of your HashMap.
To prevent misusage, you can add a helper function instead of overriding:
public class ApproximateKeyMap<K, V> extends HashMap<K, V> {
public V getApproximate(Object key) {
// Map your key -> approximated key
K approximatedKey = ...
return super.get(approximatedKey);
}
}

my 'smallest method gives different results if order of when the words/numbers are added is changed

So basically my code is doing what the question says. In the way the code is laid out now it gives the correct results, but when I change the order of the .add pieces of code it gives different results each time. I feel the compareTo method is fine, but am i missing something? I'm trying to get the smallest result.
Thanks in advance.
package lists;
import java.util.*;
public class Lab4 {
public static <T extends Comparable> int smallest(List<T> l) {
if (l.size() == 0)
return -1;
else {
Iterator<T> it = l.iterator();
T smallestSoFar = it.next();
T temp;
int smallestPos = 0;
int i = 0; //used to indicate position in list of next item
while (it.hasNext()) {
temp = it.next();
if (temp.compareTo(smallestSoFar) > 0) {
smallestSoFar = temp;
smallestPos++;
}
i++;
}
return smallestPos;
}
}
public static <T extends Comparable> void deleteSmallest(List<T> l) { // for exercise 3
}
public static void main(String[] args) {
Vector<String> vec1 = new Vector<String>();
vec1.add("Hello");
vec1.add("xxxx");
vec1.add("world");
vec1.add("aardvark");
int smallPos = smallest(vec1);
if (smallPos != -1)
System.out.println("smallest entry is " + vec1.elementAt(smallPos) + " at position " + smallPos);
Vector<Integer> vec2 = new Vector<Integer>();
vec2.add(new Integer(47));
vec2.add(new Integer(247));
vec2.add(new Integer(17));
vec2.add(new Integer(399));
smallPos = smallest(vec2);
if (smallPos != -1)
System.out.println("smallest entry is " + vec2.elementAt(smallPos) + " at position " + smallPos);
}
}
Your comparison test is the wrong way around. Currently you're picking the largest value.
if (temp.compareTo(smallestSoFar) > 0) {
Should be
if (temp.compareTo(smallestSoFar) < 0) {
Also, smallestPos++;should be smallestPos=i;
Currently you're returning a count of the number of times the "smallest" value changed.
With java8 you can make your smallest() method more compact:
public static <T extends Comparable<T>> int smallest( List<T> list ){
return list.stream() // get Stream<T> from list
.sorted(Comparable::compareTo) // Stream<T> is now sorted
.mapToInt(list::indexOf) // maps Stream<T> to an IntStream consisting of indices
.findFirst() // find the first value (the smallest)
.orElse(-1); // if nothing found, hence list was empty, then return -1
}
and when i tested it with my function there were no inconsistencies

NullPointerException in add() Method

My problem is I created an add() method for my ArrayList.
I get an NullPointerException. How can I implement an add() method in my class as the following code suggests?
here is the code:
public class XY{
private List<DictEntry> dict = new ArrayList<DictEntry>();
public void add(String word, int frequency) {
DictEntry neu = new DictEntry(word, frequency);
if (word == null || frequency == 0) {
return;
}
if (!dict.isEmpty()) {
for (int i = 0; i < dict.size(); i++) {
if (dict.get(i).getWord() == word) {
return;
}
}
}
dict.add(neu);
}
}
You have a null element in your array. dict.get(i).getWord() is like null.getWord()
Without the line number it's thrown on, it's harder to say. But I'd suggest not taking the approach you are, anyhow.
First off: don't reimplement functionality that exists:
public class XY{
private List<DictEntry> dict = new ArrayList<DictEntry>();
public void add(String word, int frequency) {
if (word == null || frequency == 0) {
return;
}
DictEntry neu = new DictEntry(word, frequency);
if (!dict.contains(word)) {
dict.add(word);
}
}
}
Even better, use a structure more appropriate to the problem. You're mapping a word to a count - that's all that you appear to be doing with the DictEntry, here. So why not:
public class XY{
private Map<String, Integer> dict = new HashMap<String, Integer>();
public void add(String word, int frequency) {
dict.put(word, frequency);
}

remove smallest k elements from hashmap in JAVA

I have a hashmap of objects. Each object has two attributes (let say int length and int weight).
I want to remove k elements with the smallest length.
What is the efficient way of doing this?
Map<K, V> map = new HashMap<>();
...
Set<K> keys = map.keySet();
TreeSet<K> smallest = new TreeSet<>(new Comparator<K>(){
public int compare(K o1, K o2) {
return o1.getLength() - o2.getLength();
}
});
smallest.addAll(keys);
for(int x = 0; x < num; x++) {
keys.remove(smallest.pollFirst());
}
Where K is your key type, V is your value type, and num is the number of elements you wish to remove.
If you are doing this frequently, it might be a good idea to use a TreeMap in the first place.
The easiest, but certainly not the most efficient is to create an instance of a TreeMap with provided Comparator for your type, putAll() elements from your map to the map you just created and remove k-elements with help of keySet(). In the end a TreeMap will not contain k-smallest elements.
You didn't mention if the attribute you discriminate on is part of the key or the value, if it's the key then teh treemap discussed above is applicbale.
Otherwise If you need to do this often I'd be inclined to implement my own map, delegating everything in the map interface to a hashmap (or appropriate structure0. Override the add/remove and if necessary iterator, then use the add/remove to maintain a sorted list of the values.
This obviously assumes the values don't change and is highly coupled to your problem.
Keep in mind that TreeMap sorts by the natural ordering of its keys. Hence you can create a key with comparable based on the length of it's value. For example (Since I am on Lunch the code isn't perfect but should get you to what you need):
package com.trip.test;
import java.util.SortedMap;
import java.util.TreeMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ComparisonTest {
private static Logger logger = LoggerFactory.getLogger(ComparisonTest.class);
private static String[] a = {"1","2","3","4"};
private static String[] b = {"A","B","D"};
private static String[] c = {"1","B","D","1","B","D"};
/**
* #param args
*/
static SortedMap<KeyWithLength, String[]> myMap = new TreeMap<KeyWithLength, String[]>();
static {
myMap.put(new KeyWithLength("a", a.length), a);
myMap.put(new KeyWithLength("b", b.length), b);
myMap.put(new KeyWithLength("c", c.length), c);
}
public static void main(String[] args) {
// print Map
logger.info("Original Map:");
int i = 0;
for (String[] strArray: myMap.values() ){
logger.info(String.format("*** Entry %s: ", i++));
printStrings(strArray);
}
// chop off 2 shortest
chopNShortest(myMap, 2);
// print Map
logger.info("ShortenedMap:");
i = 0;
for (String[] strArray: myMap.values() ){
logger.info(String.format("*** Entry %s: ", i++));
printStrings(strArray);
}
}
static void printStrings(String[] strArray){
StringBuffer buf = new StringBuffer();
for (String str: strArray){
buf.append(String.format("%s, ", str));
}
logger.info(buf.toString());
}
static void chopNShortest(SortedMap<KeyWithLength, String[]> sortedMap, int n) {
// Assuming map is not unmodifiable
if (n <= sortedMap.size()-1){
for (int i = 0; i< n;i++){
sortedMap.remove(sortedMap.firstKey());
}
}
}
}
class KeyWithLength implements Comparable<KeyWithLength> {
private String key;
private Integer length;
public KeyWithLength(String key, int length) {
super();
this.key = key;
this.length = length;
}
public String getKey() {
return key;
}
public int getLength() {
return length;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((key == null) ? 0 : key.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
KeyWithLength other = (KeyWithLength) obj;
if (key == null) {
if (other.key != null)
return false;
} else if (!key.equals(other.key))
return false;
return true;
}
#Override
public int compareTo(KeyWithLength another) {
// TODO Auto-generated method stub
return compare(this.length, another.length);
}
public static int compare(int x, int y) {
return (x < y) ? -1 : ((x == y) ? 0 : 1);
}
}
The output:
Original Map:
*** Entry 0:
A, B, D,
*** Entry 1:
1, 2, 3, 4,
*** Entry 2:
1, B, D, 1, B, D,
ShortenedMap:
*** Entry 0:
1, B, D, 1, B, D,

Categories