Linked HashMap - Iteration (Java) [duplicate] - java

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;
}
}

Related

Custom implementation of a Map collection in 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.

How to get N most often words in given text, sorted from max to min?

I have been given a large text as input. I have made a HashMap that stores each different word as a key, and number of times that occurs as value (Integer).
Now I have to make a method called mostOften(int k):List that return a List that gives the first k-words that from max number of occurrence to min number of occurrence ( descending order ) using the HashMap that I have made before.
The problem is that whenever 2 words have the same number of occurrence, then they should be sorted alphabetically.
The first idea that was on my mind was to swap keys and values of the given HashMap, and put it into TreeMap and TreeMap will sort the words by the key(Integer - number of occurrence of the word ) and then just pop the last/first K-entries from the TreeMap.
But I will have collision for sure, when the number of 2 or 3 words are the same. I will compare the words alphabetically but what Integer should I put as a key of the second word comming.
Any ideas how to implement this, or other options ?
Hints:
Look at the javadocs for the Collections.sort methods ... both of them!
Look at the javadocs for Map.entries().
Think about how to implement a Comparator that compares instances of a class with two fields, using the 2nd as a "tie breaker" when the other compares as equal.
Here's the solution with I come up.
First you create a class MyWord that can store the String value of the word and the number of occurences it appears.
You implement the Comparable interface for this class to sort by occurences first and then alphabetically if the number of occurences is the same
Then for the most often method, you create a new List of MyWord from your original map. You add the entries of this to your List
You sort this list
You take the k-first items of this list using subList
You add those Strings to the List<String> and you return it
public class Test {
public static void main(String [] args){
Map<String, Integer> m = new HashMap<>();
m.put("hello",5);
m.put("halo",5);
m.put("this",2);
m.put("that",2);
m.put("good",1);
System.out.println(mostOften(m, 3));
}
public static List<String> mostOften(Map<String, Integer> m, int k){
List<MyWord> l = new ArrayList<>();
for(Map.Entry<String, Integer> entry : m.entrySet())
l.add(new MyWord(entry.getKey(), entry.getValue()));
Collections.sort(l);
List<String> list = new ArrayList<>();
for(MyWord w : l.subList(0, k))
list.add(w.word);
return list;
}
}
class MyWord implements Comparable<MyWord>{
public String word;
public int occurence;
public MyWord(String word, int occurence) {
super();
this.word = word;
this.occurence = occurence;
}
#Override
public int compareTo(MyWord arg0) {
int cmp = Integer.compare(arg0.occurence,this.occurence);
return cmp != 0 ? cmp : word.compareTo(arg0.word);
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + occurence;
result = prime * result + ((word == null) ? 0 : word.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;
MyWord other = (MyWord) obj;
if (occurence != other.occurence)
return false;
if (word == null) {
if (other.word != null)
return false;
} else if (!word.equals(other.word))
return false;
return true;
}
}
Output : [halo, hello, that]
In addition to your Map to store word counts I would use a PriorityQueue of fixed size K (with natural order). It will allow to reach O(N) complexity. Here is a code which use this approach:
In constructor we are reading input stream word by word filling the counters in the Map.
In the same time we are updating priority queue keeping it's max size = K (we need count top K words)
public class TopNWordsCounter
{
public static class WordCount
{
String word;
int count;
public WordCount(String word)
{
this.word = word;
this.count = 1;
}
}
private PriorityQueue<WordCount> pq;
private Map<String, WordCount> dict;
public TopNWordsCounter(Scanner scanner)
{
pq = new PriorityQueue<>(10, new Comparator<WordCount>()
{
#Override
public int compare(WordCount o1, WordCount o2)
{
return o2.count-o1.count;
}
});
dict = new HashMap<>();
while (scanner.hasNext())
{
String word = scanner.next();
WordCount wc = dict.get(word);
if (wc == null)
{
wc = new WordCount(word);
dict.put(word, wc);
}
if (pq.contains(wc))
{
pq.remove(wc);
wc.count++;
pq.add(wc);
}
else
{
wc.count++;
if (pq.size() < 10 || wc.count >= pq.peek().count)
{
pq.add(wc);
}
}
if (pq.size() > 10)
{
pq.poll();
}
}
}
public List<String> getTopTenWords()
{
Stack<String> topTen = new Stack<>();
while (!pq.isEmpty())
{
topTen.add(pq.poll().word);
}
return topTen;
}
}

Sorting Map using Comparator

I'm trying Comparator to implement a sort in TreeMap according to a sequence.
final String sequence="People,Object,Environment,Message,Service";
Comparator<String> comparator = new Comparator<String>() {
#Override
public int compare(String key1, String key2) {
int returned = sequence.indexOf(key1) - sequence.indexOf(key2);
if (returned == 0 && !key1.contains(key2))
returned = -1;
return returned;
}
};
List<String> list=new ArrayList<String>();
Map<String,String> lhm = new TreeMap<String,String>(comparator);
// Put elements to the map
lhm.put("Object", "biu");
lhm.put("Message", "nuios");
lhm.put("Service", "sdfe");
lhm.put("People", "dfdfh");
lhm.put("Environment", "qwe");
lhm.put("Other", "names");
lhm.put("Elements", "ioup");
lhm.put("Rand", "uiy");
for(Entry<String, String> entry : lhm.entrySet()) {
System.out.println(entry.getKey());
}
The Output which I'm getting here is
Rand
Elements
Other
People
Object
Environment
Message
Service
The Elements in treemap which equals the sequence are ordered correctly but other elements which are not following the sequence should come after the sequence.My expectation is like following
People
Object
Environment
Message
Service
Rand
Elements
Other
How to implement this?
Suppose If I add some more words to the elements of my TreeMap means my Comparator doesn't even order the elements.Like this
lhm.put("Object IOn", "biu");
lhm.put("Message dfb", "nuios");
lhm.put("Serviceabc", "sdfe");
lhm.put("Peoplexxx", "dfdfh");
lhm.put("Environmentxxx", "qwe");
lhm.put("Other", "names");
lhm.put("Elements", "ioup");
lhm.put("Rand", "uiy");
My Output become
Rand
Elements
Other
Environmentxxx
Peoplexxx
Serviceabc
Message dfb
Object IOn
Somebody help me to rewrite my Comparator to fix this problem?
Here is some simple code that should to the task.
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
public class FixedOrderComparator implements Comparator<String> {
private final Map<String, Integer> index = new HashMap<>();
public FixedOrderComparator(String elements) {
String[] split = elements.split(",");
for (int i = 0; i < split.length; i++) {
index.put(split[i], i);
}
}
#Override
public int compare(String left, String right) {
Integer rankLeft = index.get(left);
Integer rankRight = index.get(right);
if (rankLeft != null && rankRight != null) {
return rankLeft.compareTo(rankRight);
}
if (rankLeft == null && rankRight == null) {
return left.compareTo(right);
}
return Boolean.compare(rankLeft == null, rankRight == null);
}
}
You have to correct your logic used in the comparator.
final String sequence="People,Object,Environment,Message,Service";
System.out.println(sequence.indexOf("People")); // 0
System.out.println(sequence.indexOf("Object")); // 7
System.out.println(sequence.indexOf("Message")); // 26
System.out.println(sequence.indexOf("Environment")); // 14
.indexOf(key1) returns the index of the first character of the String and not the String.
int returned = sequence.indexOf(key1) - sequence.indexOf(key2);
if(returned < 0){
// then it is sorted;
return 1;
}
else{ return -1; }

Efficiently Compare Successive Characters in String

I'm doing some text analysis, and need to record the frequencies of character transitions in a String. I have n categories of characters: for the sake of example, isUpperCase(), isNumber(), and isSpace().
Given that there are n categories, there will be n^2 categories of transitions, e.g. "isUpperCase() --> isUpperCase()", "isUpperCase --> isLetter()", "isLetter() --> isUpperCase()", etc.
Given a block of text, I would like to record the number of transitions that took place. I would imagine constructing a Map with the transition types as the Keys, and an Integer as each Value.
For the block of text "TO", the Map would look like [isUpper -> isUpper : 1, isUpper -> isSpace : 1]
The part I cannot figure out, though, is how to construct a Map where, from what I can see, the Key would consist of 2 boolean methods.
Create an enum that represents character types - you need a way to get a character type enum given a character. I'm sure there are better ways to do that than what I have done below but that is left as an exercise to the reader.
Next create a method that takes the previous and current characters and concatenates their types into a unique String.
Finally loop over the input string and hey presto.
private static enum CharacterType {
UPPER {
#Override
boolean isA(final char c) {
return Character.isUpperCase(c);
}
},
LOWER {
#Override
boolean isA(final char c) {
return Character.isLowerCase(c);
}
},
SPACE {
#Override
boolean isA(final char c) {
return Character.isWhitespace(c);
}
},
UNKOWN {
#Override
boolean isA(char c) {
return false;
}
};
abstract boolean isA(final char c);
public static CharacterType toType(final char c) {
for (CharacterType type : values()) {
if (type.isA(c)) {
return type;
}
}
return UNKOWN;
}
}
private static String getTransitionType(final CharacterType prev, final CharacterType current) {
return prev + "_TO_" + current;
}
public static void main(String[] args) {
final String myString = "AAaaA Aaa AA";
final Map<String, Integer> countMap = new TreeMap<String, Integer>() {
#Override
public Integer put(final String key, final Integer value) {
final Integer currentCount = get(key);
if (currentCount == null) {
return super.put(key, value);
}
return super.put(key, currentCount + value);
}
};
final char[] myStringAsArray = myString.toCharArray();
CharacterType prev = CharacterType.toType(myStringAsArray[0]);
for (int i = 1; i < myStringAsArray.length; ++i) {
final CharacterType current = CharacterType.toType(myStringAsArray[i]);
countMap.put(getTransitionType(prev, current), 1);
prev = current;
}
for (final Entry<String, Integer> entry : countMap.entrySet()) {
System.out.println(entry);
}
}
Output:
LOWER_TO_LOWER=2
LOWER_TO_SPACE=1
LOWER_TO_UPPER=1
SPACE_TO_SPACE=1
SPACE_TO_UPPER=2
UPPER_TO_LOWER=2
UPPER_TO_SPACE=1
UPPER_TO_UPPER=2
Running the method on the content of your question (825 chars) took 9ms.
If you think most of the transitions will be present, then a 2 dimension Array would work best:
int n = _categories.size();
int[][] _transitionFreq = new int[n][n];
If you think it will be a parse array, then a map will be more efficient in terms of memory usage, but less efficient in terms of performance.
It's a trade-off you'll have to make depending on your data and the number of character types.

Null ArrayList returned by TreeMap Java

I have to do a synonyms dictionary using a TreeMap. The TreeMap is of <Word, ArrayList<Word>> type. That means that for each key represented by a Word there will be a list of synonyms. When I want to list the contents of the dictionary, by using the method below, I discover that the ArrayList returned is null. What can I do? I tried tracing the code but I don't seem to find the error.
The method is :
public String listContent() {
Set set = wordList.keySet();
Iterator it = set.iterator();
String result = new String();
ArrayList<Word> words = new ArrayList<Word>();
while (it.hasNext()) {
Word temp = (Word) it.next();
words = wordList.get(temp);
if (words != null) {
Iterator it2 = words.iterator();
result += temp.getContent();
result += " - ";
int size = words.size();
while (it2.hasNext()) {
result += ((Word) it2.next()).getContent();
if (size != 1)
result += ", ";
size--;
}
result += "\n";
}
}
return result;
}
The ArrayList returned by wordList.get(temp) is null for some of the inserted elements. I checked the watches but there, they're not. What should I do ?
wordList is a TreeMap<Word, ArrayList<Word>>;
EDIT - the addWord method
public void addWord(String content1, String content2)
{
Word w1 = new Word(content1);
Word w2 = new Word(content2);
Set set = wordList.entrySet();
Iterator it = set.iterator();
boolean ok=false;
while(it.hasNext())
{
Map.Entry<Word,ArrayList<Word>> temp = (Map.Entry<Word,ArrayList<Word>>) it.next();
if(temp.getKey().getContent().matches(content1))
{
ArrayList<Word> words = temp.getValue();
Iterator it2 = words.iterator();
if(words.isEmpty()) words.add(w2);
else
{
boolean ok2=true;
while(it2.hasNext())
{
Word tempy = (Word) it2.next();
if(tempy.getContent().equals(content2))
{
ok2=false;
break;
}
}
if(ok2) words.add(w2);
}
ok=true;
}
}
if(!ok) {
ArrayList<Word> tempys = new ArrayList<Word>();
tempys.add(w2);
wordList.put(w1,tempys);
}
}
EDIT 2 - Word Class
public class Word implements Serializable,Comparable {
private String content;
public Word (String content)
{
this.content = content;
}
public void setContent(String content)
{
this.content=content;
}
public String getContent()
{
return content;
}
#Override
public int compareTo(Object o) {
if(((Word)o).getContent().equals(this.getContent())) return 0;
return 1;
}
}
Your compareTo method is wrong. The contract is that if A > B, then you must have B < A. Your implementation always returns 1 if the contents are not equal.
You should implement it like this:
#Override
public int compareTo(Word w) {
return this.content.compareTo(w.content);
}
(and the Word class should implement Comparable<Word>, not Comparable).
Since a TreeMap uses this method to tell if some word is bigger or smaller than another one, and since the method returns incoherent results, the Map also returns incoherent results.
Did you check that when you insert a synonym everything is ok?
BTW you should use StringBuilder for concatenating strings (better in perf) and you'd better use worklist.entrySet() for iterating on key and value at the same time, instead of several get and iterators.
The addWord method is a horrible mess and I'm getting a headache when I try to look at it, but my educated guess is that the system does not work because the Word class implements neither the equals method nor the hashCode method. Try adding these to it:
#Override
public int hashCode() {
return this.content.hashCode();
}
#Override
public boolean equals(Object o) {
return this.content.equals(o);
}
With those methods the TreeMap and the other structures are able to identify that two instances of Word classes that represent the same word are, indeed, equal.
I've cleaned up your existing code to use proper Java idioms, like for-each loop, StringBuilder instead of concatenating a String, avoid that size-- hack, stuff like that.
public String listContent() {
final StringBuilder result = new StringBuilder();
for (Map.Entry<Word, List<Word>> e : wordList.entrySet()) {
final List<Word> words = e.getValue();
if (words != null) {
result.append(e.getKey().getContent()).append(" - ");
final Iterator<Word> it = words.iterator();
result.append(it.next().getContent());
while(it.hasNext()) result.append(", ").append(it.next().getContent());
result.append("\n");
}
}
return result.toString();
}
Here's also a cleaned-up version of addWord, but still a heavy mess of program logic. If anyone has patience for this, I encourage him to steal and improve on this.
public void addWord(String content1, String content2) {
final Word w1 = new Word(content1), w2 = new Word(content2);
final Set<Map.Entry<Word, List<Word>>> set = wordList.entrySet();
for (Map.Entry<Word, List<Word>> temp : set) {
if (!temp.getKey().getContent().matches(content1)) {
final List<Word> newList = new ArrayList<Word>();
newList.add(w2);
wordList.put(w1,newList);
break;
}
final List<Word> words = temp.getValue();
if (words.isEmpty()) words.add(w2);
else {
for (Word w : words) {
if (w.getContent().equals(content2)) {
words.add(w2);
break;
}
}
}
}
}

Categories