I have a homework problem as follows:
Created HeapPriorityQueue that will implement Priority queue
import java.util.Arrays;
public class HeapPriorityQueue<K,V extends Comparable<K>> implements PriorityQueue<K, V> {
private static final int DEFAULT_CAPACITY = 10;
protected K[] array;
protected int size;
/**
* Constructs a new BinaryHeap.
*/
#SuppressWarnings("unchecked")
public HeapPriorityQueue() {
// Java doesn't allow construction of arrays of placeholder data types
array = (K[])new Comparable[DEFAULT_CAPACITY];
size = 0;
}
/**
* Adds a value to the min-heap.
*/
public void add(K value) {
// grow array if needed
if (size >= array.length - 1) {
array = this.resize();
}
// place element into heap at bottom
size++;
int index = size;
array[index] = value;
bubbleUp();
}
/**
* Returns true if the heap has no elements; false otherwise.
*/
public boolean isEmpty() {
return size == 0;
}
/**
* Returns (but does not remove) the minimum element in the heap.
*/
public K peek() {
if (this.isEmpty()) {
throw new InvalidKeyException("Invalid Key");
}
return array[1];
}
/**
* Removes and returns the minimum element in the heap.
*/
public K remove() {
// what do want return?
K result = peek();
// get rid of the last leaf/decrement
array[1] = array[size];
array[size] = null;
size--;
bubbleDown();
return result;
}
/**
* Returns a String representation of BinaryHeap with values stored with
* heap structure and order properties.
*/
public String toString() {
return Arrays.toString(array);
}
/**
* Performs the "bubble down" operation to place the element that is at the
* root of the heap in its correct place so that the heap maintains the
* min-heap order property.
*/
protected void bubbleDown() {
int index = 1;
// bubble down
while (hasLeftChild(index)) {
// which of my children is smaller?
int smallerChild = leftIndex(index);
// bubble with the smaller child, if I have a smaller child
if (hasRightChild(index)
&& array[leftIndex(index)].compareTo(array[rightIndex(index)]) > 0) {
smallerChild = rightIndex(index);
}
if (array[index].compareTo(array[smallerChild]) > 0) {
swap(index, smallerChild);
} else {
// otherwise, get outta here!
break;
}
// make sure to update loop counter/index of where last el is put
index = smallerChild;
}
}
/**
* Performs the "bubble up" operation to place a newly inserted element
* (i.e. the element that is at the size index) in its correct place so
* that the heap maintains the min-heap order property.
*/
protected void bubbleUp() {
int index = this.size;
while (hasParent(index)
&& (parent(index).compareTo(array[index]) > 0)) {
// parent/child are out of order; swap them
swap(index, parentIndex(index));
index = parentIndex(index);
}
}
protected boolean hasParent(int i) {
return i > 1;
}
protected int leftIndex(int i) {
return i * 2;
}
protected int rightIndex(int i) {
return i * 2 + 1;
}
protected boolean hasLeftChild(int i) {
return leftIndex(i) <= size;
}
protected boolean hasRightChild(int i) {
return rightIndex(i) <= size;
}
protected K parent(int i) {
return array[parentIndex(i)];
}
protected int parentIndex(int i) {
return i / 2;
}
protected K[] resize() {
return Arrays.copyOf(array, array.length * 2);
}
protected void swap(int index1, int index2) {
K tmp = array[index1];
array[index1] = array[index2];
array[index2] = tmp;
}
#Override
public int size() {
// TODO Auto-generated method stub
return 0;
}
#Override
public Entry<K, V> max() throws EmptyPriorityQueueException {
// TODO Auto-generated method stub
return null;
}
#Override
public Entry<K, V> insert(K key, V value) throws InvalidKeyException {
// TODO Auto-generated method stub
return null;
}
#Override
public Entry<K, V> extractMax() throws EmptyPriorityQueueException {
// TODO Auto-generated method stub
return null;
}
}
this should implement this PriorityQueue
/**
* Interface for the priority queue ADT
*
* K is the key of the entry stored in the priority queue and denotes the priority of the entry.
*
* V is the auxillary data of the entry
* #author bryann
*
*/
public interface PriorityQueue<K extends Comparable<K>,V> {
/**
* Returns the number of items in the priority queue
*
* #return number of items in the priority queue
*/
public int size();
/**
* Returns whether the priority queue is empty.
*
* #return true if the priority queue is empty. Otherwise, false.
*/
public boolean isEmpty();
/**
* Returns but does not remove an entry with maximum priority key
*
* #return entry that has the highest priority key
* #throws EmptyPriorityQueueException
*/
public Entry<K,V> max() throws EmptyPriorityQueueException;
/**
* Inserts a key-value pair and returns the entry created.
*
* #param key priority key of the entry to be inserted
* #param value value of the entry to be inserted
* #return entry that was inserted into the priority queue
* #throws InvalidKeyException
*/
public Entry<K,V> insert(K key, V value) throws InvalidKeyException;
/**
* Removes and returns an entry with maximum priority key
*
* #return entry that has the highest priority key
* #throws EmptyPriorityQueueException
*/
public Entry<K,V> extractMax() throws EmptyPriorityQueueException;
}
while the Driver class is this
public class HeapPriorityQueueDriver {
public static void main(String[] args) {
PriorityQueue<Integer, String> queue = new HeapPriorityQueue<Integer, String>();
queue.insert(0, "Zero");
queue.insert(10, "Ten");
queue.insert(1, "One");
queue.insert(5, "Five");
queue.insert(3, "Three");
queue.insert(7, "Seven");
queue.insert(9, "Nine");
while(!queue.isEmpty()) {
System.out.println(queue.extractMax());
} // end while
} // end main
}
the problems I get are
Bound mismatch: The type String is not a valid substitute for the bounded parameter <V extends Comparable<K>> of the type HeapPriorityQueue<K,V> HeapPriorityQueueDriver.java /MP7/src/simon/mp7 line 6
Java Problem
Bound mismatch: The type K is not a valid substitute for the bounded parameter <K extends Comparable<K>> of the type Entry<K,V> HeapPriorityQueue.java /MP7/src/simon/mp7 line 199
Java Problem
Bound mismatch: The type K is not a valid substitute for the bounded parameter <K extends Comparable<K>> of the type Entry<K,V> HeapPriorityQueue.java /MP7/src/simon/mp7 line 193
Java Problem
Bound mismatch: The type K is not a valid substitute for the bounded parameter <K extends Comparable<K>> of the type Entry<K,V> HeapPriorityQueue.java /MP7/src/simon/mp7 line 187
Java Problem
The method compareTo(K) is undefined for the type K HeapPriorityQueue.java /MP7/src/simon/mp7 line 126
Java Problem
The method compareTo(K) is undefined for the type K HeapPriorityQueue.java /MP7/src/simon/mp7 line 104
Java Problem
The method compareTo(K) is undefined for the type K HeapPriorityQueue.java /MP7/src/simon/mp7 line 100
Java Problem
Bound mismatch: The type K is not a valid substitute for the bounded parameter <K extends Comparable<K>> of the type PriorityQueue<K,V> HeapPriorityQueue.java /MP7/src/simon/mp7 line 5
the message in the console is:
Exception in thread "main" java.lang.Error: Unresolved compilation problem:
Bound mismatch: The type String is not a valid substitute for the bounded parameter <V extends Comparable<K>> of the type HeapPriorityQueue<K,V>
at simon.mp7.HeapPriorityQueueDriver.main(HeapPriorityQueueDriver.java:6)
You declare HeapPriorityQueue<K,V extends Comparable<K>>, but you try to use it as:
PriorityQueue<Integer, String> queue = new HeapPriorityQueue<Integer, String>();
But String doesn't extends Comparable<Integer> so it gives you compilation error.
Related
I new in Java and trying to run some tests. I cannot use the .getLast() function as its showing error and also cannot seem to create the method. What am I doing wrong.
Here is my partial code. I am trying to create the getLast method which is failing.
Here is the error The type of the expression must be an array type but it resolved to ListOfNVersion03PartA.
*/
public class ListOfNVersion03PartA
{
private int thisNumber; // the number stored in this node
private ListOfNVersion03PartA next; // forms a linked list of objects
private final int nodeID; // a unique ID for each object in the list
private static int nodeCount = 0; // the number of list objects that have been created
/**
* #param num the value to be stored in this object
*/
public ListOfNVersion03PartA(int num)
{
thisNumber = num;
next = null;
++nodeCount;
nodeID = nodeCount;
} // constructor(int num)
/**
* #param num the multiple values to be stored in the list, in that order
*/
public ListOfNVersion03PartA(int [] num)
{
this(num[0]); // in this context, "this" invokes the other constructor
for (int i=1 ; i<num.length ; ++i)
insertLast(num[i]);
} // constructor(int [] num)
/**
* #return the number of elements stored in this list
*/
public int getListSize()
{
return nodeCount;
} // method getListSize
/**
* #return the last element in the list
*/
public int getLast()
{
int y = next[nodeCount-1];
return y;
} // method getLast
/**
* prints this object
*/
public void printNode()
{
System.out.print("[" + nodeID + "," + thisNumber + "]->");
} // method printListNode
/**
* prints the tail of a list
*/
private void printListTail()
{
printNode();
if ( next != null )
next.printListTail();
} // method printListTail
/**
* prints the contents of the list, in order from first to last
*/
public void printList()
{
printNode();
if ( next != null )
next.printListTail();
} // method printList
/**
* This method is NOT examinable in this test.
*
* prints the contents of the list, in order from first to last, and
* then moves the cursor to the next line
*/
public void printlnList()
{
printList();
System.out.println();
} // method printlnList
/**
* #return the number of times the element occurs in the list
*
* #param element the element to be counted
*/
public int countElement(int element)
{
return 999;
} // method countElement
/**
* #return the number of times the replacement was made
*
* #param replaceThis the element to be replaced
* #param withThis the replacement
*/
public int replaceAll(int replaceThis, int withThis)
{
return 999;
} // method replaceAll
/**
* #return a reference to the first object in the list that contains the parameter value, or null if it is not found
*
* #param findThis the value to be found
*/
public ListOfNVersion03PartA findUnSorted(int findThis)
{
// This algorithm is known as "linear search"
if ( thisNumber == findThis )
return this;
if ( next != null )
return next.findUnSorted(findThis);
return null;
} // method findUnSorted
/**
* #return the reference to the object containing the smallest element in the list
*/
public ListOfNVersion03PartA minRef()
{
// add and/or modify code to complete the method
ListOfNVersion03PartA minOfTail;
if ( next == null )
return this;
minOfTail = next.minRef();
if ( thisNumber <= minOfTail.thisNumber )
return this;
else
return minOfTail;
} // method minRef
/**
* Inserts an element in the last position. The pre-existing elements in the
* list are unaffected.
*
* #param newElement the element to be inserted
*/
public void insertLast(int newElement)
{
if ( next == null )
next = new ListOfNVersion03PartA(newElement);
else
next.insertLast(newElement);
} // method insertLast
} // class ListOfNVersion03PartA
I have a memory cache class that I use for storing the Product objects and the number of the items sold.
public class MemoryCache<K, V> {
private long timeToLive;
private LRUMap lruMap;
/**
* custom class that stores the cache value
* and the timestamp for the last access
*/
protected class CacheObject {
public long lastAccessed = System.currentTimeMillis();
public V value;
protected CacheObject(V value) {
this.value = value;
}
}
/**
* #param timeToLive this is the permitted period of time for an object to live since
* they are last accessed.
*
* <p>
* #param timerInterval For the expiration of items use the timestamp of the last access
* and in a separate thread remove the items when the time to live
* limit is reached. This is nice for reducing memory pressure for
* applications that have long idle time in between accessing the
* cached objects. We have disabled the cleanup for this case scenario
*
* <p>
* #param maxItems Cache will keep most recently used items if we will try to add more
* items then max specified. The Apache common collections have an LRUMap,
* which, removes the least used entries from a fixed size map
*/
public MemoryCache(long timeToLive, final long timerInterval, int maxItems) {
this.timeToLive = timeToLive * 1000;
lruMap = new LRUMap(maxItems);
if (this.timeToLive > 0 && timerInterval > 0) {
Thread t = new Thread(new Runnable() {
public void run() {
while (true) {
try {
Thread.sleep(timerInterval * 1000);
} catch (InterruptedException ex) {
}
/*
* clean the objects from the cache that has reached
* the timeToLive period after the last access.
* */
cleanup();
}
}
});
t.setDaemon(true);
t.start();
}
}
/**
* insert a new key and value inside the cache memory
*
* #param key
* #param value
*/
public void put(K key, V value) {
synchronized (lruMap) {
if (key == null) {
return;
}
/**
* we have reached the max. size of items decided for the cache
* and hence, we are not allowed to add more items for now. We
* will need for the cache cleaning to add further items.
*/
if (lruMap.isFull()) {
return;
}
lruMap.put(key, new CacheObject(value));
}
}
/**
* retrieve the cache object from the memory using the key
*
* #param key
* #return
*/
#SuppressWarnings("unchecked")
public V get(K key) {
synchronized (lruMap) {
MapIterator iterator = lruMap.mapIterator();
K k = null;
V v = null;
CacheObject o = null;
while (iterator.hasNext()) {
k = (K) iterator.next();
v = (V) iterator.getValue();
Product product = (Product) k;
Product product1 = (Product) key;
if (product.getProductId().equalsIgnoreCase(product1.getProductId())) {
o = (CacheObject) v;
}
}
if (o == null) {
return null;
} else {
o.lastAccessed = System.currentTimeMillis();
return o.value;
}
}
}
/**
* remove a cache object from the memory using the key
*
* #param key
*/
public void remove(K key) {
synchronized (lruMap) {
lruMap.remove(key);
}
}
/**
* find the size of the memory cache
*
* #return size of the cache
*/
public int size() {
synchronized (lruMap) {
return lruMap.size();
}
}
/**
* we will look after the cache objects with a certain time interval
* that has stayed in the memory inactively more than the time to live
* period and remove them iteratively.
*/
#SuppressWarnings("unchecked")
public void cleanup() {
long now = System.currentTimeMillis();
ArrayList<K> deleteKey = null;
synchronized (lruMap) {
MapIterator iterator = lruMap.mapIterator();
deleteKey = new ArrayList<K>((lruMap.size() / 2) + 1);
K key = null;
CacheObject object = null;
while (iterator.hasNext()) {
key = (K) iterator.next();
object = (CacheObject) iterator.getValue();
if (object != null && (now > (object.lastAccessed + timeToLive))) {
deleteKey.add(key);
}
}
}
for (K key : deleteKey) {
synchronized (lruMap) {
lruMap.remove(key);
}
Thread.yield();
}
}
/**
* convert the cache full of items to regular HashMap with the same
* key and value pair
*
* #return
*/
public Map<Product, Integer> convertToMap() {
synchronized (lruMap) {
Map<Product, Integer> map = new HashMap<>();
MapIterator iterator = lruMap.mapIterator();
K k = null;
V v = null;
CacheObject o = null;
while (iterator.hasNext()) {
k = (K) iterator.next();
v = (V) iterator.getValue();
Product product = (Product) k;
// this fails right here
int value = Integer.parseInt(String.valueOf(v));
map.put(product, value);
}
return map;
}
}
}
Inside the API class, it's introduced as,
MemoryCache<Product, Integer> cache = new MemoryCache<>(1800, 500, 10000);
I store the product data with the items sold in the API class,
cache.put(product, 0);
The product class defined below,
#Entity
public class Product {
#Id
#Column(name = "productId")
private String productId;
#Column(name = "stockId")
private String id;
#Column(name = "stock_timestamp")
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", timezone = "UTC")
private Timestamp timestamp;
#Column(name = "quantity")
private int quantity;
public Product() {
}
public Product(String productId, Timestamp requestTimestamp, String id, Timestamp timestamp, int quantity) {
this.productId = productId;
this.id = id;
this.timestamp = timestamp;
this.quantity = quantity;
}
// getter, setter
// equals and hasCode
// toString
}
The convertToMap method in the MemoryCache class takes the caches storages and turns that into HashMap. The method has a bug in the line where I try to store an int as value.
int value = Integer.parseInt(String.valueOf(v));
I have a screenshot for the debugging session.
As you see, I need to get the values (ie 1000, 100) and put that as the value of the intended HashMap. What's the correct way to write the convertToMap method for the purpose?
Generics are designed to avoid casts and so to make the code more robust but your way of using generics defeats it in a some way.
Your issue illustrates that perfectly :
MapIterator iterator = lruMap.mapIterator(); // no type associated to the iterator
// ....
k = (K) iterator.next(); // unsafe conversion
v = (V) iterator.getValue(); // unsafe conversion
Product product = (Product) k; // unsafe conversion
// this fails right here
int value = Integer.parseInt(String.valueOf(v)); // how to be sure that v is an Integer ?
LRUMap and MapIterator that are probably custom classes have to be generic classes that relies on similarly to MemoryCache<K,V> to make the whole thing consistent.
In the same way this method specified in the generic class is clearly an abuse of the typing of the map. You pass from generic types declared in the class to Product and Integer :
public class MemoryCache<K, V> {
//..
public Map<Product, Integer> convertToMap() {}
//..
}
Finally it makes your MemoryCache designed to work only with Product and Integer as K, V concrete type. In this case, generics are useless, you have to remove them.
If later you want/need to get a more generic solution, go further in the generic application and you should finish with a convertToMap() defined as :
public Map<K, V> convertToMap() {...}
As you see in your LruMap key is of type Product but value is of type MemoryCache$CacheObject not Integer.
So you need to change you code to
int value = Integer.parseInt(String.valueOf(v.value)); //Assuming v is of type MemoryCache$CacheObject
Or you can even use this
Integer value = (Integer) v.value;
I am in desperate need of help here. I am to migrate this existing code to generics and I've really hit a wall. Any help would be greatly appreciated.
The existing code is an algorithm library and is accompanied by some classes with constructors for vehicles (i.e Bike.Java).
I've tried alot of different things but I just can't seem to figure it out. I'd love some insight.
public class Algo
{
/**
* Copies all objects from src to tgt, for which the predicate pred holds.
*
* #param src source list
* #param tgt target list
* #param pred unary predicate
*/
static public
void copyIf(List src, List tgt, UnaryPredicate pred)
{
for (Object obj : src)
{
if (pred.test(obj)) tgt.add(obj);
}
}
/**
* Copies all objects from src to tgt that are greater than yardstick.
*
* #param src source
* #param tgt target
* #param yardstick determines if objects in src should be copied to tgt.
*/
static public
void copyIfGreaterThan(List src, List tgt, final Comparable yardstick)
{
copyIf(src, tgt, new UnaryPredicate() {
public boolean test(Object o)
{
return yardstick.compareTo(o) < 0;
}
});
}
/**
* Finds a maximum object in lst.
*
* #param lst a list containing non-null references
* #return a maximum object in lst
*/
static public
Comparable findMax(List lst)
{
assert lst != null;
Comparable max = null;
Iterator iter = lst.iterator();
// handle first element
if (iter.hasNext())
max = (Comparable) iter.next();
// handle remaining elements
while (iter.hasNext())
{
assert max != null;
Comparable cand = (Comparable) iter.next();
if (max.compareTo(cand) < 0)
max = cand;
}
return max;
}
/**
* Adds the smaller of lhs and rhs to dst.
*
* #param lhs left hand side object
* #param rhs right hand side object
* #param dst destination list
*/
static public
void storeMin(Comparable lhs, Comparable rhs, List dst)
{
Comparable min = lhs;
if (min.compareTo(rhs) > 0) min = rhs;
dst.add(min);
}
/**
* swaps the elements at a and b in lst.
*
* #param lst a list
* #param a first location in lst
* #param b second location in lst
*/
static private
void swap(ArrayList objs, int a, int b)
{
Object tmp = objs.get(a);
objs.set(a, objs.get(b));
objs.set(b, tmp);
}
/**
* Sorts the elements in lst.
*
* #param lst an array list containing non-null references
*/
static public
void selectionSort(ArrayList lst)
{
for (int i = 0; i < lst.size(); ++i)
{
int min = i;
Comparable minobj = (Comparable) lst.get(min);
for (int j = i+1; j < lst.size(); ++j)
{
if (minobj.compareTo(lst.get(j)) > 0)
{
min = j;
minobj = (Comparable) lst.get(min);
}
}
swap(lst, min, i);
}
}
}
Since Java 8 you could do it like that:
public static <T> List<T> copyIf(List<T> src, Predicate<T> predicate){
return src.stream().filter(predicate).collect(Collectors.toList());
}
public static <T> List<T> copyIfGreaterThan(List<T> src, Comparable<T> yardstick) {
return copyIf(src, t -> (yardstick.compareTo(t) < 0));
}
For more infos about generics see:
https://docs.oracle.com/javase/tutorial/java/generics/types.html
For more infos on streams see e.g.
https://www.tutorialspoint.com/java8/java8_streams.htm
I am trying to create a fast search function to determine the prefix of a phone number.
I am loading prefix data from database into memory as TreeMap, where key is prefix and value is an object containing information about this prefix(country etc).
This is how TreeMap is populated:
private static TreeMap<String, PrefixData> prefixMap = new TreeMap<>();
//EXAMPLE of DB query
try {
Class.forName("org.postgresql.Driver");
Connection connection = DriverManager.getConnection(dbURL, dbUser, dbPass);
connection.setAutoCommit(false);
Statement stmt = connection.createStatement();
ResultSet rs = stmt.executeQuery("SELECT * FROM countries_prefixes");
//Looping resultset
while (rs.next()) {
//TODO Review fields that must be stored in memory
String country = rs.getString("name");
//Populating map with data object (keeping nr prefix as key and object as value)
prefixMap.put(rs.getString("country_prefix"), new PrefixData(country));
}
rs.close();
stmt.close();
connection.close();
} catch (ClassNotFoundException | SQLException e) {
System.out.println(e.toString());
}
Lets say I have phone numbers I want to check:
37251845632;
35844021546;
34651478966
etc ...
Some prefixes are 1 digit long, some 2 digits long, some 3 digits long and so on...
So I created a loop, that works:
//TODO Try some other search methods (Tries)
//array for prefix length priority
int[] sequence = {3, 4, 2, 1, 5, 6};
//Performing search from the map
for (int i = 0; i < sequence.length; i++) {
//Extracting prefix from phone nr
String prefix = phoneNr.substring(1, sequence[i] + 1);
if (prefixMap.containsKey(prefix)) {
PrefixData pdata = prefixMap.get(prefix);
System.out.println(String.format("Found matching key [%s] in TreeMap", prefix));
System.out.println(String.format("[NAME: %s] [REGEX: %s] ", pdata.getCountryName(), pdata.getNrRegex()));
//Validate number format with regex
if (pdata.getNrRegex().trim() != null && !pdata.getNrRegex().trim().isEmpty()) {
System.out.println("Regex for number validation is present!");
if (phoneNr.matches(pdata.getNrRegex().replaceAll("^/|/$", ""))) {
System.out.println("NUMBER IS VALID!");
} else {
System.out.println("INVALID NUMBER!");
}
}
return pdata;
}
}
return null;
}
Now the loop works well, but it is slow. I've heard something about Tries, which is faster, but I don't understand how to implement this in my scenario.
Any help is appreciated!
As I said, the loop works, but this is not a nice way to achieve my goal.
So I did a little bit of research and came up with solution that is using prefix tree (Trie) implementation.
Little reading what Trie is can be found here.
And now the Trie implementation part. I knew that it would be faster to find a code that is already written and tested, so I found Google implementation here. And Vladimir Kroz's implementation here.
Made some minor modifications and this is the solution. I will provide both solutions:
Prefixmap interface
package tiesImpl;
/**
* Maps string prefixes to values. For example, if you {#code put("foo", 1)},
* {#code get("foobar")} returns {#code 1}. Prohibits null values.
*
* <p>Use instead of iterating over a series of string prefixes calling
* {#code String.startsWith(prefix)}.
*
* #author crazybob#google.com (Bob Lee)
* #param <T>
*/
public interface PrefixMap<T> {
/**
* Maps prefix to value.
*
* #param prefix
* #param value
* #return The previous value stored for this prefix, or null if none.
* #throws IllegalArgumentException if prefix is an empty string.
*/
T put(CharSequence prefix, T value);
/**
* Finds a prefix that matches {#code s} and returns the mapped value.
*
* If multiple prefixes in the map match {#code s}, the longest match wins.
*
* #param s
* #return value for prefix matching {#code s} or {#code null} if none match.
*/
T get(CharSequence s);
}
PrefixTrie class
package tiesImpl;
/*
* Copyright (C) 2007 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.util.LinkedHashMap;
import java.util.Map;
/**
* Trie implementation supporting CharSequences as prefixes.
*
* Prefixes are sequences of characters, and the set of allowed characters is
* specified as a range of sequential characters. By default, any seven-bit
* character may appear in a prefix, and so the trie is a 128-ary tree.
*
* #author crazybob#google.com (Bob Lee)
* #author mharris#google.com (Matthew Harris)
* #param <T>
*/
public class PrefixTrie<T> implements PrefixMap<T> {
// The set of allowed characters in prefixes is given by a range of
// consecutive characters. rangeOffset denotes the beginning of the range,
// and rangeSize gives the number of characters in the range, which is used as
// the number of children of each node.
private final char rangeOffset;
private final int rangeSize;
private final Node<T> root;
/**
* Constructs a trie for holding strings of seven-bit characters.
*/
public PrefixTrie() {
rangeOffset = '\0';
rangeSize = 128;
root = new Node<>(rangeSize);
}
/**
* Constructs a trie for holding strings of characters.
*
* The set of characters allowed in prefixes is given by the range
* [rangeOffset, lastCharInRange], inclusive.
*
* #param firstCharInRange
* #param lastCharInRange
*/
public PrefixTrie(char firstCharInRange, char lastCharInRange) {
this.rangeOffset = firstCharInRange;
this.rangeSize = lastCharInRange - firstCharInRange + 1;
if (rangeSize <= 0) {
throw new IllegalArgumentException("Char range must include some chars");
}
root = new Node<>(rangeSize);
}
/**
* {#inheritDoc}
*
* #param prefix
* #param value
* #throws IllegalArgumentException if prefix contains a character outside
* the range of legal prefix characters.
*/
#Override
public T put(CharSequence prefix, T value) {
if (value == null) {
throw new NullPointerException();
}
Node<T> current = root;
for (int i = 0; i < prefix.length(); i++) {
int nodeIndex = prefix.charAt(i) - rangeOffset;
try {
Node<T> next = current.next[nodeIndex];
if (next == null) {
next = current.next[nodeIndex] = new Node<>(rangeSize);
}
current = next;
} catch (ArrayIndexOutOfBoundsException e) {
throw new IllegalArgumentException(
"'" + prefix.charAt(i) + "' is not a legal prefix character.");
}
}
T oldValue = current.value;
current.value = value;
return oldValue;
}
/**
* {#inheritDoc}
* #param s
*/
#Override
public T get(CharSequence s) {
Node<T> deepestWithValue = root;
Node<T> current = root;
for (int i = 0; i < s.length(); i++) {
int nodeIndex = s.charAt(i) - rangeOffset;
if (nodeIndex < 0 || rangeSize <= nodeIndex) {
return null;
}
current = current.next[nodeIndex];
if (current == null) {
break;
}
if (current.value != null) {
deepestWithValue = current;
}
}
return deepestWithValue.value;
}
/**
* Returns a Map containing the same data as this structure.
*
* This implementation constructs and populates an entirely new map rather
* than providing a map view on the trie, so this is mostly useful for
* debugging.
*
* #return A Map mapping each prefix to its corresponding value.
*/
public Map<String, T> toMap() {
Map<String, T> map = newLinkedHashMap();
addEntries(root, new StringBuilder(), map);
return map;
}
/**
* Adds to the given map all entries at or below the given node.
*
* #param node
* #param builder A StringBuilder containing the prefix for the given node.
* #param map
*/
private void addEntries(Node<T> node,
StringBuilder builder,
Map<String, T> map) {
if (node.value != null) {
map.put(builder.toString(), node.value);
}
for (int i = 0; i < node.next.length; i++) {
Node<T> next = node.next[i];
if (next != null) {
builder.append((char) (i + rangeOffset));
addEntries(next, builder, map);
builder.deleteCharAt(builder.length() - 1);
}
}
}
private static class Node<T> {
T value;
final Node<T>[] next;
#SuppressWarnings("unchecked")
Node(int numChildren) {
next = new Node[numChildren];
}
}
/**
* Creates a {#code LinkedHashMap} instance.
*
* #param <K>
* #param <V>
* #return a newly-created, initially-empty {#code LinkedHashMap}
*/
public static <K, V> LinkedHashMap<K, V> newLinkedHashMap() {
return new LinkedHashMap<>();
}
}
Vladimir Kroz implementation: Trie class
package tiesImpl;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
/**
* Prefix table based on Trie structure. Allows to perform incremental lookup
* and match based on search key prefixes (classic example - determine phone
* area code for given phone number)
*
* #param <V> a type of value object to be stored along with prefix (e.g when
* key is a country name, the value could be a name of the country)
*
* #author Vladimir Kroz
* https://vkroz.wordpress.com/2012/03/23/prefix-table-trie-implementation-in-java/
*/
public class Trie<V> {
Entry<V> entry;
char key;
Map<Character, Trie<V>> children;
public Trie() {
this.children = new HashMap<>(10);
entry = new Entry<>();
}
/**
* non-public, used by _put()
*/
Trie(char key) {
this.children = new HashMap<>(10);
this.key = key;
entry = new Entry<>();
}
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<>(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. if key is "37251656565" and prefix table has node "372" then
* this call will return the value of "372"
*
* #param key a string which starts with prefix to be searched in the table
* (e.g. phone number)
* #return an Object associated 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
*
* #param key
* #return
*/
public boolean hasPrefix(String key) {
return this.get(key) != null;
}
V _get(StringBuffer key, int level) {
if (key.length() > 0) {
Trie<V> t = children.get(key.charAt(0));
if (t != null) {
//FYI: modified code to return deepest with value instead of returning null if prefix doesn't have corresponding value.
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
//For debugging
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)).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 + "]";
}
}
}
And finally the Testing part
package tiesImpl;
/**
* Class for testing different implementations of prefix tree (Trie).
*
* #author lkallas
*/
public class TriesTest {
private static final PrefixTrie<String> googleTrie = new PrefixTrie<>();
private static final Trie<String> krozTrie = new Trie<>();
public static void main(String[] args) {
//Inserting prefixes to Google implementation of Trie
googleTrie.put("7", "Russia");
googleTrie.put("77", "Abkhazia");
googleTrie.put("746", "Some unknown country");
//Inserting prefixes to Vladimir Kroz implementation of Trie
krozTrie.put("7", "Russia");
krozTrie.put("77", "Abkhazia");
krozTrie.put("746", "Some unknown country");
System.out.println("Google version of get: " + googleTrie.get("745878787"));
System.out.println("Vladimir Kroz version of get: " + krozTrie.get("745878787"));
}
}
Hope that this answer is somewhat helpful to others also!
Cheers!
This is a school assignment. Overall this should read a text file, add words from it to a hash table. I've coded it out, but I'm testing it and it's giving me some problems. When I try to find the index of the object, it always returns -1, meaning it's saying the words aren't in the array even if they are. There are a few other problems as well. It's giving me a headache.
import java.util.Iterator;
import java.util.LinkedList;
public class MyMap<K,V> implements Iterable<MyMap.MyEntry<K,V>> {
int collision; // maintain the current count of collisions here.
int slots = 0;
int key = 0;
MyEntry<K,V> tempPair;
LinkedList<MyEntry<K,V>> [] bucketArray;
int keyMod;
/**
* Create a MyMap instance with the specified number of
* buckets.
*
* #param buckets the number of buckets to make in this map
*/
public MyMap(int buckets) {
slots = buckets;
bucketArray = (LinkedList<MyEntry<K,V>> [])new LinkedList[buckets];
}
/**
* Puts an entry into the map. If the key already exists,
* it's value is updated with the new value and the previous
* value is returned.
*
* #param key the object used as a key to retrieve the value
* #param value the object stored in association with the key
*
* #return the previously stored value or null if the key is new
*/
public V put(K key, V value) {
// don't forget hashcodes can be any integer value. You'll
// need to compress them to ensure they give you a valid bucket.
MyEntry<K,V> tempPair = new MyEntry<K,V>(key, value);
Word newWord = new Word((String)key);
keyMod = newWord.hashCode((String)key) % slots;
if ((bucketArray[keyMod]) == null){
LinkedList<MyEntry<K,V>> firstList = new LinkedList<MyEntry<K,V>>();
firstList.add(tempPair);
bucketArray[keyMod] = firstList;
return null;
}
else {
int indexNode = bucketArray[keyMod].indexOf(tempPair);
if (indexNode == -1) {
bucketArray[keyMod].add(tempPair);
collision += 1;
//System.out.println(indexNode );
return null;
}
else {
MyEntry<K,V> oldNode = bucketArray[keyMod].get(indexNode);
V oldValue = oldNode.value;
oldNode.value = tempPair.value;
//System.out.println(indexNode );
return oldValue;
}
}
}
/**
* Retrieves the value associated with the specified key. If
* it exists, the value stored with the key is returned, if no
* value has been associated with the key, null is returned.
*
* #param key the key object whose value we wish to retrieve
* #return the associated value, or null
*/
public V get(K key) {
//MyEntry<K,V> tempPair = new MyEntry<K,V>(key,value);
Word newWord = new Word((String)key);
int keyMod = newWord.hashCode((String)key) % slots;
if (bucketArray[keyMod] == null) {
return null;
}
else {
int temp = bucketArray[keyMod].indexOf(key);
if (temp == -1) {
return null;
}
else {
MyEntry<K,V> tempNode = bucketArray[keyMod].get(temp);
return tempNode.value;
}
}
}
/**
*
* I've implemented this method, however, you must correctly
* maintain the collisions member variable.
*
* #return the current count of collisions thus far.
*/
public int currentCollisions(K key) {
return collision;
}
/**
* Looks through the entire bucket where the specified key
* would be found and counts the number of keys in this bucket
* that are not equal to the current key, yet still have the
* same hash code.
*
* #param key
* #return a count of collisions
*/
public int countCollisions(K key) {
Word newKey = new Word((String) key);
int keyMod = newKey.hashCode((String) key) % slots;
if (bucketArray[keyMod].indexOf(key) == -1){
return bucketArray[keyMod].size();
}
return (bucketArray[keyMod].size()-1);
}
/**
* Removes the value associated with the specifed key, if it exists.
* #param key the key used to find the value to remove.
* #return the value if the key was found, or null otherwise.
*/
public V remove(K key) {
Word newWord = new Word((String)key);
//int keyMod = newWord.hashCode((String)key) % slots;
int tempNodeIndex = bucketArray[newWord.hashCode((String)key)].indexOf(key);
if (tempNodeIndex == -1) {
return null;
}
else{
tempPair = bucketArray[key.hashCode()].get(tempNodeIndex);
V returnValue = tempPair.value;
tempPair.value = null;
return returnValue;}
}
/**
* Returns the number of entries in this map
* #return the number of entries.
*/
public int size() {
int size = 0;
for (int i =0; i< bucketArray.length; i++){
size = bucketArray[i].size() + size;
}
return size;
}
/**
* Creates and returns a new Iterator object that
* iterates over the keys currently in the map. The iterator
* should fail fast, and does not need to implement the remove
* method.
*
* #return a new Iterator object
*/
public Iterator<MyEntry<K,V>> iterator() {
return null;
}
public static class MyEntry<K,V> {
K key;
V value;
public MyEntry(K k, V v) {
key = k;
value = v;
}
}
}
Here is the Word Class
/* The reason you can't extend String Class is because String is a final class and you can not have
* a subclass that might alter components of a final class. Since the word class would extend the
* String class, it would have the capability to change variables within the String Final Class.
*/
public class Word {
String word;
/**
* Creates a Word object representing the specified String
*
* #param w the String version of this word.
*/
public Word(String w) {
word = w;
}
/**
* Returns a hashcode for this Word -- an integer whose value is based on the
* word's instance data. Words that are .equals() *must* have the same hashcode.
* However, the converse need not hold -- that is, it *is* acceptable for
* two words that are not .equals() to have the same hashcode.
*/
public int hashCode(String word) {
int code = 0;
for ( int i =0; i<word.length(); i++){
code = word.charAt(i) + code;
}
return code; //word.hashCode();
//int hashCode = 0;
//for ( int i = 0; i<word.length(); i++) {
//hashCode = Math.abs(hashCode*13 + word.charAt(i));
//}
//System.out.println(hashCode);
//return hashCode;
}
/**
* Returns true if and only if this Word object represents the same
* sequence of characters as the specified object. Here, you can assume
* that the object being passed in will be a Word.
*/
public boolean equals(Object o) {
String passedIn = o.toString();
boolean returnValue = word.equals(passedIn);
return returnValue;
}
/**
* This method returns the string representation of the object.
* A correct implementation will return the String representation of the
* word that is actually being stored. ie., if you had a word object representing
* 'hi', it should return 'hi'
*/
public String toString() {
String thisString = word;
return thisString;
}
}
Here is the beginnings of my tester:
import java.io.*;
import java.util.*;
public class Tester<K,V> {
public static void main(String [] args) throws FileNotFoundException {
MyMap<String, Integer> pain = new MyMap<String, Integer>(3000);
Scanner s = new Scanner (new File("pg4.txt"));
while (s.hasNext()) {
String word = s.next();
Integer value = (Integer) pain.get(word);
if (value == null) {
pain.put(word, 1);
}
else {
value +=1;
pain.put(word, value);
}
}
s.close();
pain.put("the",1);
pain.put("the",5);
pain.get("the");
System.out.println("'the' gives this many collisions: " + pain.get("the") );
pain.remove("the");
System.out.println("'the' gives this many collisions: " + pain.get("the") );
}
}
indexOf uses equals for comparisons, so your calls to indexOf do not work. You need to implement equals for MyEntry.
public static class MyEntry<K,V> {
K key;
V value;
public MyEntry(K k, V v) {
key = k;
value = v;
}
#Override
public int hashCode() {
// (overriding hashCode
// just because we are overriding equals)
return ( key == null ? 0 : key.hashCode() );
}
#Override
public boolean equals(Object o) {
if(!(o instanceof MyEntry<?, ?>))
return false;
MyEntry<?, ?> that = (MyEntry<?, ?>)o;
return( this.key == null ?
that.key == null : this.key.equals(that.key)
);
}
}
If you don't do this, then you need to create your own indexOf method where you loop through the LinkedList yourself.
Your remove method does not actually do a removal, just set the value to null.
tempPair = bucketArray[key.hashCode()].get(tempNodeIndex);
V returnValue = tempPair.value;
tempPair.value = null;
More correctly it would be:
tempPair = bucketArray[key.hashCode()].remove(tempNodeIndex);
return tempPair.value;
As far as I can tell, you do not need the Word class at all. Your casting to String makes assumptions about what the type of K is, which is dubious for a generic class. (What if I have a MyMap<Long, Double>?)
You are only using it to get a hashCode which your K will already have (because hashCode is declared on java.lang.Object).
You could use hashCode from a temp MyEntry like I defined above or call it directly:
int keyMod = ( key == null ? 0 : key.hashCode() ) % slots;
To get your Word class working, you need to override hashCode correctly:
// now you can call hashCode() on a Word
// when a Word is passed in to MyMap as a key
#Override
public int hashCode() {
int code = 0;
// 'word' now refers to the instance variable
for ( int i =0; i<word.length(); i++){
code = word.charAt(i) + code;
}
return code;
}
// also implementing equals correctly, but your
// implementation in the question probably did
// not cause an error
#Override
public boolean equals(Object o) {
if(!(o instanceof Word))
return false;
String passedIn = ((Word)o).word;
boolean returnValue = word.equals(passedIn);
return returnValue;
}
Then you will be able to use a MyMap<Word, Integer>.
I found a few problems in my MyMap class where I wasn't constructing correctly. I found the error and fixed it. My tests were also causing errors. I corrected them. There were no problems in my Word Class. Once I corrected these the map constructed correctly, all methods worked.