I have code for prefix tree (Trie) like this:
public class Trie<V> {
Entry<V> entry;
char key;
Map<Character, Trie<V>> childrens;
public Trie() {
this.childrens = new HashMap<Character, Trie<V>>(10);
entry = new Entry<V>();
}
/** non-public, used by _put() */
Trie(char key) {
this.childrens = new HashMap<Character, Trie<V>>(10);
this.key = key;
entry = new Entry<V>();
}
public void put(String key, V value) {
_put(new StringBuffer(key), new StringBuffer(""), value);
}
void _put(StringBuffer remainder, StringBuffer prefix, V value) {
if (remainder.length() > 0) {
char keyElement = remainder.charAt(0);
Trie<V> t = null;
try {
t = childrens.get(keyElement);
} catch (IndexOutOfBoundsException e) {
}
if (t == null) {
t = new Trie<V>(keyElement);
childrens.put(keyElement, t);
}
prefix.append(remainder.charAt(0));
t._put(remainder.deleteCharAt(0), prefix, value);
} else {
this.entry.value = value;
this.entry.prefix = prefix.toString();
}
}
/**
* Retrieves element from prefix table matching as a prefix to provided key.
* E.g. is key is "abcde" and prefix table has node "ab" then this call will
* return "ab"
*
* #param key
* a string which starts with prefix to be searched in the table
* (e.g. phone number)
* #return an Object assosiated with matching prefix (i.e if key is a phone
* number it may return a corresponding country name)
*/
public V get(String key) {
return _get(new StringBuffer(key), 0);
}
/**
* Returns true if key has matching prefix in the table
*/
public boolean hasPrefix(String key) {
return ((this.get(key) != null) ? true : false);
}
V _get(StringBuffer key, int level) {
if (key.length() > 0) {
Trie<V> t = childrens.get(key.charAt(0));
if (t != null) {
return t._get(key.deleteCharAt(0), ++level);
} else {
return (level > 0) ? entry.value : null;
}
} else {
return entry.value;
}
}
#Override
public String toString() {
return "Trie [entry=" + entry + ", key=" + key + ", childrens="
+ childrens + "]";
}
static public class Entry<V> {
String prefix;
V value;
public Entry() {
}
public Entry(String p, V v) {
prefix = p;
value = v;
}
public String prefix() {
return prefix;
}
public V value() {
return value;
}
#Override
public String toString() {
return "Entry [prefix=" + prefix + ", value=" + value + "]";
}
}
}
Insertion goes like this:
private static Trie<String> trie = new Trie<>();
trie.put("7", "Some country");
trie.put("77", "Some other country");
trie.put("745", "Entirely different place");
Searching will go like this:
String result = trie.get("746878788");
System.out.println(result);
This search will result null because there is no value for 74.
My question is: how can I modify _get method in Trie class so that it will remember last value that is not null. So when it ends up in 74, then it will remember that 7 had some value "Some country", so it will return that instead of null.
Any ideas how to solve this?
Any help is appreciated!
First of all, I modified the toString() method of the Trie to get some better debug information. The only important rows to achieve what you are asking are these in _get method:
V result = t._get(key.deleteCharAt(0), ++level);
return result == null ? entry.value : result;
The trie now prefers current entry's value, if subentry's value is null.
The whole code modified:
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
public class Trie<V> {
Entry<V> entry;
char key;
Map<Character, Trie<V>> children;
public Trie() {
this.children = new HashMap<Character, Trie<V>>(10);
entry = new Entry<V>();
}
/** non-public, used by _put() */
Trie(char key) {
this.children = new HashMap<Character, Trie<V>>(10);
this.key = key;
entry = new Entry<V>();
}
public void put(String key, V value) {
_put(new StringBuffer(key), new StringBuffer(""), value);
}
void _put(StringBuffer remainder, StringBuffer prefix, V value) {
if (remainder.length() > 0) {
char keyElement = remainder.charAt(0);
Trie<V> t = null;
try {
t = children.get(keyElement);
} catch (IndexOutOfBoundsException e) {
}
if (t == null) {
t = new Trie<V>(keyElement);
children.put(keyElement, t);
}
prefix.append(remainder.charAt(0));
t._put(remainder.deleteCharAt(0), prefix, value);
} else {
this.entry.value = value;
this.entry.prefix = prefix.toString();
}
}
/**
* Retrieves element from prefix table matching as a prefix to provided key.
* E.g. is key is "abcde" and prefix table has node "ab" then this call will
* return "ab"
*
* #param key
* a string which starts with prefix to be searched in the table
* (e.g. phone number)
* #return an Object assosiated with matching prefix (i.e if key is a phone
* number it may return a corresponding country name)
*/
public V get(String key) {
return _get(new StringBuffer(key), 0);
}
/**
* Returns true if key has matching prefix in the table
*/
public boolean hasPrefix(String key) {
return ((this.get(key) != null) ? true : false);
}
V _get(StringBuffer key, int level) {
if (key.length() > 0) {
Trie<V> t = children.get(key.charAt(0));
if (t != null) {
V result = t._get(key.deleteCharAt(0), ++level);
return result == null ? entry.value : result;
} else {
return (level > 0) ? entry.value : null;
}
} else {
return entry.value;
}
}
#Override
public String toString() {
Iterator<Character> it = children.keySet().iterator();
StringBuffer childs = new StringBuffer();
while (it.hasNext()) {
Character key = it.next();
childs.append(String.format("\n%s\n",
// adding a tab to the beginning of every line to create a visual tree
String.format("%s: %s", key, children.get(key)).toString().replaceAll("(?m)(^)", "\t")));
}
return String.format("Trie [entry=%s, children=%s]", entry, childs);
}
static public class Entry<V> {
String prefix;
V value;
public Entry() {
}
public Entry(String p, V v) {
prefix = p;
value = v;
}
public String prefix() {
return prefix;
}
public V value() {
return value;
}
#Override
public String toString() {
return "Entry [prefix=" + prefix + ", value=" + value + "]";
}
}
}
Related
I'm trying to create a binary tree of strings with constructors and methods of searching for an element and printing a binary tree. In main I want to check if the string is in a binary tree. Can someone help me? Thank you in advance
Here is an implementation of binary search tree with strings :
public static void main(String[] args) throws IOException {
boolean toExit = false;
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
BinaryTreeNode binaryTreeNode = new BinaryTreeNode("hello", "hello");
binaryTreeNode.put("world","world");
binaryTreeNode.put("ab","ab");
binaryTreeNode.put("cd","cd");
while(!toExit) {
System.out.println("Please enter string to search in the binary search tree or -1 to exit: ");
String input = bufferedReader.readLine();
if("-1".equals(input)){
toExit = true;
} else {
if (binaryTreeNode.get(input) == null) {
System.out.println("The input string does not exist");
} else {
System.out.println(input + " exists");
}
}
}
}
public static class BinaryTreeNode
{
private String key;
private Object value;
private BinaryTreeNode left, right;
public BinaryTreeNode(String key, Object value)
{
this.key = key;
this.value = value;
}
public Object get( String key )
{
if (this.key.equals(key))
{
return value;
}
if (key.compareTo(this.key) < 0 )
{
return left == null ? null : left.get(key);
}
else
{
return right == null ? null : right.get(key);
}
}
public void put(String key, Object value)
{
if (key.compareTo(this.key) < 0)
{
if (left != null)
{
left.put(key, value);
}
else
{
left = new BinaryTreeNode(key, value);
}
}
else if (key.compareTo(this.key) > 0)
{
if (right != null)
{
right.put(key, value);
}
else
{
right = new BinaryTreeNode(key, value);
}
}
else
{
this.value = value;
}
}
}
Hope that it helps...
I would like to ask you for help. I have a Device object
public class Device {
public String name;
public String deviceId;
#JsonSerialize(using = CustomResourceSerializer.class)
public Map<String, Map<String, Object>> customResources;
}
My goal is to "extract" this map directly to Device Object. Firstly I used #JsonAnyGetter which worked well and Map was nested under field String of first map directly under Device object.
But I need more complex logic and I have two problems which I don't know how to solve.
Key of first map is for example "configuration/inputOne". With #JsonAnyGetter the example output is { "configuration/inputOne": { "rate":23 } }
What I need is nested structure based on delimiter, so
{ "configuration": { "inputOne": { "rate":23 } } }
This I was almost able to do easily with custom JsonSerializer
jsonGenerator.writeStartObject();
foreach(splited key)
jsonGenerator.writeObjectFieldStart(resourceUriItem);
foreach(value)
jsonGenerator.writeObjectField(k, v);
foreach(splitted key)
jsonGenerator.writeEndObject();
jsonGenerator.writeEndObject();
But final object looks like
{ "customResource": {"configuration": { "inputOne": { "rate":23 } } } }
CustomResource field is from Device object and I don't know how to get rid of it. As with JsonAnyGetter. That's the first problem.
As you see, I am splitting the key of the map to have more nested strucutre, so from the "configuration/inputOne" to { configuration { inputOne { .. } }. But the map customResources can have of course multiple items, so for example:
"configuration/inputOne"
"configuration/inputTwo"
"configuration"
Now you probably see where is the problem. As I am iterating over keys and I am creating nested structure, I will override it. So for example, firstly I will create object configuration, then inputOne and fill it with fields. Closing objects. Then second item in map, creating configuration object and inputTwo object. But with creation of configuration, I will delete the one previously created with inputOne.
Do you have any proposal how to solve this? Thanks.
You can turn your map into a type of a tree by splitting on the / and creating a parent child relationship on the split items.
Using the following class as a tree element / node.
class TreeElement {
private String key;
private Object value;
private List<TreeElement> children;
public TreeElement(String key) {
this.key = key;
}
// getters and setters here
public void addChild(TreeElement child) {
if (this.children == null) {
this.children = new ArrayList<TreeElement>();
}
this.children.add(child);
}
#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;
TreeElement other = (TreeElement) obj;
if (key == null) {
if (other.key != null)
return false;
} else if (!key.equalsIgnoreCase(other.key))
return false;
return true;
}
#Override
public String toString() {
return "TreeElement [key=" + key + ", value=" + value + ", children=" + children + "]";
}
}
And the following test code.
public static void main(String[] args) {
try {
// create the config1, config2, etc.. here
Device device1 = new Device();
device1.customResources = new HashMap<String, Map<String, Object>>();
device1.customResources.put("configuration/inputOne", config1);
device1.customResources.put("configuration/inputTwo", config2);
device1.customResources.put("configuration", config3);
device1.customResources.put("configuration", duplicateConfig3);
device1.customResources.put("otherConfig", otherConfig);
device1.customResources.put("thirdConfig1", thirdConfig1);
device1.customResources.put("thirdConfig1/inputOne", thirdConfig2);
device1.customResources.put("thirdConfig1/inputOne", duplicateThirdConfig2);
List<TreeElement> elements = new ArrayList<TreeElement>();
for (Map.Entry<String, Map<String, Object>> entry : device1.customResources.entrySet()) {
TreeElement element = generateElement(null, entry.getKey(), entry.getValue());
elements.add(element);
}
List<TreeElement> joinedElements = joinElements(elements);
for (TreeElement e : joinedElements) {
System.out.println(e.getKey() + " - " + e.getValue());
if (e.getChildren() != null) {
for (TreeElement c : e.getChildren()) {
System.out.println("\t" + c.getKey() + " - " + c.getValue());
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
This method generates a TreeElement from a Map> variable.
private static TreeElement generateElement(TreeElement parent, String item, Map<String, Object> value) {
try {
List<String> tokens = new ArrayList<String>(Arrays.asList(item.split("/")));
TreeElement child = new TreeElement(tokens.get(0));
boolean parentWasNull = false;
if (parent == null) {
parent = child;
parentWasNull = true;
}
if (tokens.size() > 1) {
if (parentWasNull == false) {
parent.addChild(child);
}
tokens.remove(0);
generateElement(child, StringUtils.join(tokens, "/"), value);
} else {
child.setValue(value);
if (parentWasNull == false) {
parent.addChild(child);
}
}
} catch (Exception e) {
e.printStackTrace();
}
return parent;
}
This method joins common TreeElement objects into one parent and multiple children.
private static List<TreeElement> joinElements(List<TreeElement> elements) {
List<TreeElement> joinedElements = new ArrayList<TreeElement>();
for (TreeElement element : elements) {
if (joinedElements.contains(element) == true) {
// joined elment does not have children
if (joinedElements.get(joinedElements.indexOf(element)).getChildren() == null) {
joinedElements.get(joinedElements.indexOf(element)).setChildren(element.getChildren());
} else {
//joined element has children and the current element also has children
if (element.getChildren() != null) {
joinedElements.get(joinedElements.indexOf(element)).getChildren().addAll(element.getChildren());
}
}
/*
* set the value of joined element to the value of the current element; will overwrite
* any existing value if duplicates exist
*/
if (element.getValue() != null) {
joinedElements.get(joinedElements.indexOf(element)).setValue(element.getValue());
}
} else {
joinedElements.add(element);
}
}
return joinedElements;
}
I'm not sure how efficient this code is, but you get the below output which you can traverse in your custom serializer to print to JSON.
thirdConfig1 - {rate=30}
inputOne - {rate=3020}
configuration - {rate=1200}
inputOne - {rate=23}
inputTwo - {rate=50}
otherConfig - {rate=10}
I have an arraylist with below string elements -->
['STM-1000-H', 'STM-2000-E', 'STM-4000-H', 'STM-200-H', 'SFP-1000-A',
'SFP-300-H', 'SAFPX-1000-H', 'SAFPX-2000-A', 'STM-1000-H-L1', 'STM-1000-H-L1+VA+GH', 'STM-1000-H-L2']
I want an arraylist with grouping like ...
display STM- 400, 500, 600, 100, 2000, 4000 (in that order), followed by SPX- 1000, 2000, 4000 (in that order, then followed by the SAFPX- 1000. Also, I have to list each by the L, L1, L2, M, M1, M2, M3, H, H! and VH (in that Order).
Can you please assist with this, Array list is string elements only.
You should create a custom comparator and then use that to sort the arraylist.
For example (this does some of the sorting you asked for but is not checked for edge-cases, that is up to you to fix). Note: Java 8 is used
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("STM-1000-H");
list.add("STM-2000-E");
list.add("SAFPX-1000-H-L2");
list.add("SAFPX-1000-H-L1");
list.add("STM-1000-H-L1");
list.add("SFP-1000-B");
list.add("SFP-1000-A");
HashMap hm = new HashMap();
hm.put("STM", 1);
hm.put("SFP", 2);
hm.put("SAFPX", 3);
list = sort(list, hm);
for (String s : list) {
System.out.println(s);
}
}
public static ArrayList<String> sort(ArrayList<String> list, HashMap<String, Integer> hashmap) {
Comparator<String> comparator = (string1, string2) ->
{
String[] tokens1 = string1.split("-");
String[] tokens2 = string2.split("-");
if (hashmap.get(tokens1[0]) > hashmap.get(tokens2[0]))
return 1;
else if (hashmap.get(tokens1[0]) < hashmap.get(tokens2[0]))
return -1;
if (Integer.parseInt(tokens1[1]) > Integer.parseInt(tokens2[1]))
return 1;
else if (Integer.parseInt(tokens1[1]) < Integer.parseInt(tokens2[1]))
return -1;
if (tokens1.length > 2 && tokens2.length > 2) {
int res = tokens1[2].compareTo(tokens2[2]);
if(res != 0)
return res;
}
if (tokens1.length > 3 && tokens2.length > 3) {
int res = tokens1[3].compareTo(tokens2[3]);
if(res != 0)
return res;
}
return 0;
};
Collections.sort(list, comparator);
return list;
}
Outputs:
STM-1000-H
STM-1000-H-L1
STM-2000-E
SFP-1000-A
SFP-1000-B
SAFPX-1000-H-L1
SAFPX-1000-H-L2
Stream.of("STM-1000-H",
"STM-2000-E",
"STM-4000-H",
"STM-200-H",
"SFP-1000-A",
"SFP-300-H",
"SAFPX-1000-H",
"SAFPX-2000-A",
"STM-1000-H-L1",
"STM-1000-H-L1+VA+GH",
"STM-1000-H-L2")
.collect(Collectors.groupingBy(s -> s.substring(0, s.indexOf('-'))))
.entrySet()
.stream()
.sorted((e1, e2) -> -e1.getKey().compareTo(e2.getKey()))
.forEach(e -> {
System.out.println(e.getKey() + ":");
e.getValue().stream().sorted().forEach(System.out::println);
});
--
STM:
STM-1000-H
STM-1000-H-L1
STM-1000-H-L1+VA+GH
STM-1000-H-L2
STM-200-H
STM-2000-E
STM-4000-H
SFP:
SFP-1000-A
SFP-300-H
SAFPX:
SAFPX-1000-H
SAFPX-2000-A
package com.skadakov.examples.devrev;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.TreeMap;
/**
*
* #author s.kadakov
*/
public class DevRev {
private final String series;
private final int model;
private final String version;
private final String revision;
public DevRev(String firmware) {
String[] ss = firmware.split("-");
if (ss.length < 3) {
throw new IllegalArgumentException(firmware);
}
this.series = ss[0];
this.model = Integer.parseInt(ss[1]);
this.version = ss[2];
this.revision = ss.length == 3 ? null : ss[3];
}
public String getSeries() {
return series;
}
public int getModel() {
return model;
}
public String getVersion() {
return version;
}
public String getRevision() {
return revision;
}
#Override
public int hashCode() {
int hash = 7;
hash = 97 * hash + Objects.hashCode(this.series);
hash = 97 * hash + this.model;
hash = 97 * hash + Objects.hashCode(this.version);
hash = 97 * hash + Objects.hashCode(this.revision);
return hash;
}
#Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final DevRev other = (DevRev) obj;
if (this.model != other.model) {
return false;
}
if (!Objects.equals(this.series, other.series)) {
return false;
}
if (!Objects.equals(this.version, other.version)) {
return false;
}
return Objects.equals(this.revision, other.revision);
}
#Override
public String toString() {
return series + "-" + model + "-" + version + (revision != null ? "-" + revision : "");
}
public static void main(String[] args) {
String[] firmare = {
"STM-1000-H",
"STM-2000-E",
"STM-4000-H",
"STM-200-H",
"SFP-1000-A",
"SFP-300-H",
"SAFPX-1000-H",
"SAFPX-2000-A",
"STM-1000-H-L1",
"STM-1000-H-L1+VA+GH",
"STM-1000-H-L2"};
Map<String, List<DevRev>> revs = new TreeMap<>();
for (String f : firmare) {
DevRev dr = new DevRev(f);
List<DevRev> sdevs = revs.get(dr.getSeries());
if (sdevs == null) {
sdevs = new ArrayList<>();
revs.put(dr.getSeries(), sdevs);
}
sdevs.add(dr);
}
for (Map.Entry<String, List<DevRev>> entry : revs.entrySet()) {
String series = entry.getKey();
List<DevRev> devices = entry.getValue();
System.out.println(series);
devices.sort(new Comparator<DevRev>() {
#Override
public int compare(DevRev o1, DevRev o2) {
return new Integer(o1.getModel()).compareTo(o2.getModel());
}
});
for (DevRev dr : devices) {
System.out.println("-> " + dr);
}
}
}
}
I have implemented Dictionary with Vector(Array). In array i store a String data. Now i have get position Method. But i want to retrieve data at some position. what will be the method? Thank you.
private int findpositionProfile(String smkey){
DictionaryProfile p = new DictionaryProfile(smkey,null);
return data.getposi(p);
}
public Profile getProfile(int i){
// DictionaryProfile p = new DictionaryProfile(null,null);
return data.get(i);
this is not working
public class Dictionary {
private Vector data;
private Vector data1;
public Dictionary() {
data = new Vector(100);
data1 = new Vector(100);
}
public void addProfile(String smkey, Profile smvalue) {
DictionaryProfile d = new DictionaryProfile(smkey, smvalue);
if (data.getposi(d) == -1) {
data.addLast(d);
}
data.replace(d);
}
public void addCorporate(String smkey, CorporateProfile smvalue) {
DictionaryCorporate d = new DictionaryCorporate(smkey, smvalue);
if (data1.getposi(d) == -1) {
data1.addLast(d);
}
data1.replace(d);
}
private int findpositionProfile(String smkey) {
DictionaryProfile p = new DictionaryProfile(smkey,null);
return data.getposi(p);
}
public CorporateProfile getCorporate(int i){
return data.get(i);
}
public Profile getProfile(int i){
DictionaryProfile p = new DictionaryProfile(null,null);
return data.get(i);
}
My dictionaryPair::
public class DictionaryProfile implements Comparable
{
private String userName ;
private Profile completeProfile ;
public DictionaryProfile ( String name,Profile p){
userName = name;
completeProfile = p;
}
public String getUserName(){
return userName;
}
public Profile getProfile(){
return completeProfile;
}
public void setUsename ( String newname ){
userName= newname;
}
public void setProfile ( Profile pro ){
completeProfile = pro;
}
public int compareTo(Object obj){
DictionaryProfile dp = (DictionaryProfile) obj;
return (this.getUserName()).compareTo(dp.getUserName());
}
}
No one should be using the JDK 1.0 vintage Vector class. This doesn't look like a generic Dictionary ADT to me.
This method makes no sense whatsoever:
public Profile getProfile(int i){
DictionaryProfile p = new DictionaryProfile(null,null);
return data.get(i);
}
The local variable p is instantiated, never used, and eligible for GC when it goes out of scope. Data is a Vector holding type Object. Where do you expect to get a Profile from?
This code makes no sense.
This will work, unless you pass an index that's out of bounds.
public Profile getProfile(int i){
return (Profile) data.get(i);
}
None of this describes how a Dictionary works. It's a synonym for a Map, which has a key/value pair. Your code isn't doing that. Doesn't use generics for key or value. Why would you do this instead of just using a Map<K, V>?
I think you should start with this:
package collections;
public interface Dictionary<K, V> {
V get(K key);
V put(K key, V value);
boolean containsKey(K key);
int size();
}
Your keys ought to be immutable.
This is what I would consider the minimal interface for a proper Dictionary.
Here's an implementation that uses backing ArrayList:
package collections;
import java.util.ArrayList;
import java.util.List;
/**
* Implementation of a Dictionary interface
* Created by Michael
* Creation date 12/30/2015.
* #link https://stackoverflow.com/questions/34538520/data-structures-and-algorithms-implementation-dictionary/34538668?noredirect=1#comment56819702_34538668
*/
public class DictionaryImpl<K, V> implements Dictionary<K, V> {
private List<K> keys;
private List<V> values;
public DictionaryImpl() {
this.keys = new ArrayList<>();
this.values = new ArrayList<>();
}
#Override
public V get(K key) {
V value = null;
if (this.keys.contains(key)) {
int index = this.getIndex(key);
if (index != -1) {
value = this.values.get(index);
}
}
return value;
}
#Override
public V put(K key, V value) {
V previousValue = null;
if (this.keys.contains(key)) {
previousValue = this.get(key);
}
this.keys.add(key);
this.values.add(value);
return previousValue;
}
#Override
public boolean containsKey(K key) {
return this.keys.contains(key);
}
#Override
public int size() {
return this.keys.size();
}
private int getIndex(K keyToFind) {
int index = -1;
if (this.keys.contains(keyToFind)) {
for (K key : this.keys) {
++index;
if (key.equals(keyToFind)) {
break;
}
}
}
return index;
}
}
Here's a Junit test to prove that it's all working:
package collections;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
/**
* Junit test for Dictionary
* Created by Michael
* Creation date 12/30/2015.
* #link https://stackoverflow.com/questions/34538520/data-structures-and-algorithms-implementation-dictionary/34538668?noredirect=1#comment56819702_34538668
*/
public class DictionaryTest {
private Dictionary<String, Integer> testDictionary;
#Before
public void setUp() {
this.testDictionary = new DictionaryImpl<>();
this.testDictionary.put("foo", 17);
this.testDictionary.put("bar", 23);
this.testDictionary.put("baz", 31);
this.testDictionary.put("bat", 41);
}
#Test
public void testContainsKey_True() {
String [] keys = { "foo", "bar", "baz", "bat" };
for (String key : keys) {
Assert.assertTrue(String.format("Should have contained key '%s'", key), this.testDictionary.containsKey(key));
}
}
#Test
public void testContainsKey_False() {
String [] keys = { "dopey", "sleepy", "doc", "sneezy" };
for (String key : keys) {
Assert.assertTrue(String.format("Should not have contained key '%s'", key), !this.testDictionary.containsKey(key));
}
}
#Test
public void testGet_Success() {
String [] keys = { "foo", "bar", "baz", "bat" };
Integer [] values = { 17, 23, 31, 41 };
for (int i = 0; i < keys.length; ++i) {
Assert.assertEquals(String.format("Should have returned value %d for key '%s'", values[i], keys[i]), values[i], this.testDictionary.get(keys[i]));
}
}
#Test
public void testGet_NoSuchKey() {
String [] keys = { "dopey", "sleepy", "doc", "sneezy" };
for (String key : keys) {
Assert.assertNull(String.format("Should have returned null for key '%s'", key), this.testDictionary.get(key));
}
}
#Test
public void testSize() {
int expected = 4;
Assert.assertEquals(expected, this.testDictionary.size());
}
}
I've implemented a HashMap with Linear Probing for hash collisions.
import java.util.Optional;
#SuppressWarnings("unchecked")
public abstract class HashMap<T, R> {
private static final int MIN_CAPACITY = 2;
private Entry<T, R>[] table;
private int internalSize, size;
private float fillRatio;
public HashMap() {
this(MIN_CAPACITY);
}
public HashMap(int initialCapacity) {
this(initialCapacity, .75f);
}
public HashMap(int initialCapacity, float fillRatio) {
this.table = new Entry[Math.max(MIN_CAPACITY, initialCapacity)];
this.fillRatio = fillRatio;
}
public Optional<R> put(T key, R value) {
int index = getIndex(key);
Entry<T, R> current = table[index];
table[index] = new Entry<>(key, value);
if(value == null && current != null && current.getValue() != null) {
size--;
} else if(value != null && (current == null || current.getValue() == null)){
size++;
}
if(current == null && ++internalSize >= (table.length * fillRatio)) {
resizeTable();
}
if(current != null) {
return Optional.ofNullable(current.getValue());
}
return Optional.empty();
}
public Optional<R> get(T key) {
int index = getIndex(key);
Entry<T, R> entry = table[index];
if(entry != null)
return Optional.ofNullable(entry.getValue());
return Optional.empty();
}
public boolean has(T key) {
return get(key).isPresent();
}
public int getSize() {
return size;
}
protected void resizeTable() {
internalSize = size = 0;
Entry<T, R>[] tmp = table;
table = new Entry[(int) ((table.length /fillRatio)* 2)];
for(Entry<T, R> entry : tmp){
if(entry != null) {
put(entry.getKey(), entry.getValue());
}
}
}
private int getIndex(T key) {
int hash = key.hashCode();
int index = (((hash % table.length) + table.length) % table.length);
while(table[index] != null && table[index].getKey().hashCode() != hash) {
if(++index == table.length) {
index = 0;
}
}
return index;
}
public static final class Entry <T, R> {
private final T key;
private final R value;
public Entry(T key, R value) {
this.key = key;
this.value = value;
}
public T getKey() {
return key;
}
public R getValue() {
return value;
}
}
}
it seems to work exactly as expected except for every 100,000 entries or so will return the wrong value for a hash. I can reproduce it fairly reliably with this test
java.util.HashMap<UUID, UUID> javaMap = new java.util.HashMap<>();
HashMap<UUID, UUID> map = new HashMap<>();
for (int i = 0; i < 200000; i++) {
UUID key = UUID.randomUUID(), value = UUID.randomUUID();
javaMap.put(key, value);
map.put(key, value);
}
for (java.util.HashMap.Entry<UUID, UUID> entry : javaMap.entrySet()) {
Optional<UUID> value = map.get(entry.getKey());
assertTrue(value.isPresent());
assertEquals(value.get(), entry.getValue());
}
I'm not seeing what my problem is and I'm not thinking of a good way to debug such a rare occurrence. Any thoughts on what I might be doing wrong or how to debug this without spending forever on it?
How does a Java HashMap handle different objects with the same hash code? answered my question. My HashMap implementation does not handle different objects with the same hash code. It only works correctly if hashcodes are unique to equal objects.