I have a hashmap of objects. Each object has two attributes (let say int length and int weight).
I want to remove k elements with the smallest length.
What is the efficient way of doing this?
Map<K, V> map = new HashMap<>();
...
Set<K> keys = map.keySet();
TreeSet<K> smallest = new TreeSet<>(new Comparator<K>(){
public int compare(K o1, K o2) {
return o1.getLength() - o2.getLength();
}
});
smallest.addAll(keys);
for(int x = 0; x < num; x++) {
keys.remove(smallest.pollFirst());
}
Where K is your key type, V is your value type, and num is the number of elements you wish to remove.
If you are doing this frequently, it might be a good idea to use a TreeMap in the first place.
The easiest, but certainly not the most efficient is to create an instance of a TreeMap with provided Comparator for your type, putAll() elements from your map to the map you just created and remove k-elements with help of keySet(). In the end a TreeMap will not contain k-smallest elements.
You didn't mention if the attribute you discriminate on is part of the key or the value, if it's the key then teh treemap discussed above is applicbale.
Otherwise If you need to do this often I'd be inclined to implement my own map, delegating everything in the map interface to a hashmap (or appropriate structure0. Override the add/remove and if necessary iterator, then use the add/remove to maintain a sorted list of the values.
This obviously assumes the values don't change and is highly coupled to your problem.
Keep in mind that TreeMap sorts by the natural ordering of its keys. Hence you can create a key with comparable based on the length of it's value. For example (Since I am on Lunch the code isn't perfect but should get you to what you need):
package com.trip.test;
import java.util.SortedMap;
import java.util.TreeMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ComparisonTest {
private static Logger logger = LoggerFactory.getLogger(ComparisonTest.class);
private static String[] a = {"1","2","3","4"};
private static String[] b = {"A","B","D"};
private static String[] c = {"1","B","D","1","B","D"};
/**
* #param args
*/
static SortedMap<KeyWithLength, String[]> myMap = new TreeMap<KeyWithLength, String[]>();
static {
myMap.put(new KeyWithLength("a", a.length), a);
myMap.put(new KeyWithLength("b", b.length), b);
myMap.put(new KeyWithLength("c", c.length), c);
}
public static void main(String[] args) {
// print Map
logger.info("Original Map:");
int i = 0;
for (String[] strArray: myMap.values() ){
logger.info(String.format("*** Entry %s: ", i++));
printStrings(strArray);
}
// chop off 2 shortest
chopNShortest(myMap, 2);
// print Map
logger.info("ShortenedMap:");
i = 0;
for (String[] strArray: myMap.values() ){
logger.info(String.format("*** Entry %s: ", i++));
printStrings(strArray);
}
}
static void printStrings(String[] strArray){
StringBuffer buf = new StringBuffer();
for (String str: strArray){
buf.append(String.format("%s, ", str));
}
logger.info(buf.toString());
}
static void chopNShortest(SortedMap<KeyWithLength, String[]> sortedMap, int n) {
// Assuming map is not unmodifiable
if (n <= sortedMap.size()-1){
for (int i = 0; i< n;i++){
sortedMap.remove(sortedMap.firstKey());
}
}
}
}
class KeyWithLength implements Comparable<KeyWithLength> {
private String key;
private Integer length;
public KeyWithLength(String key, int length) {
super();
this.key = key;
this.length = length;
}
public String getKey() {
return key;
}
public int getLength() {
return length;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((key == null) ? 0 : key.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
KeyWithLength other = (KeyWithLength) obj;
if (key == null) {
if (other.key != null)
return false;
} else if (!key.equals(other.key))
return false;
return true;
}
#Override
public int compareTo(KeyWithLength another) {
// TODO Auto-generated method stub
return compare(this.length, another.length);
}
public static int compare(int x, int y) {
return (x < y) ? -1 : ((x == y) ? 0 : 1);
}
}
The output:
Original Map:
*** Entry 0:
A, B, D,
*** Entry 1:
1, 2, 3, 4,
*** Entry 2:
1, B, D, 1, B, D,
ShortenedMap:
*** Entry 0:
1, B, D, 1, B, D,
Related
I am making a priority queue heap of type T. When I add more than one integer to my heap, I get a null pointer exception on line 55, which is where the reheapUp method uses the comparator to decide which integer gets priority.
I've been stuck on this for hours. At first I thought I had to implement a generic compare method but that doesn't make sense because there would be nothing specific enough to compare. The compare method I am using is from an old project where I made a binary search tree map that compared strings.
/*
* PQHeap.java
* 11/12/18
*/
import java.util.Comparator;
import java.util.*;
public class PQHeap<T>{
private Object[] heap; //hash table
private int heapSize;
private int capacity;
private Comparator<T> comparator;
public PQHeap(Comparator<T> comparator){
heapSize = 0;
capacity = 100;
heap = new Object[capacity];
}
public int size(){
return this.heapSize;
}
public void add(T obj){
ensureCapacity();
//add to lower right most leaf
heap[heapSize++] = obj;
reheapUp();
}
public void ensureCapacity(){
if(heapSize < heap.length/2)
return;
Object newHeap[] = new Object[2*heap.length];
for(int i=0; i<heap.length; i++)
newHeap[i] = heap[i];
heap = newHeap;
}
#SuppressWarnings("unchecked")
private void reheapUp(){
int outOfPlaceInd = heapSize - 1;
while(outOfPlaceInd > 0){
int parentInd = (outOfPlaceInd - 1)/2;
**if (comparator.compare((T)heap[outOfPlaceInd], (T)heap[parentInd]) < 0)**
{
swap(outOfPlaceInd, parentInd);
outOfPlaceInd = (outOfPlaceInd-1)/2;
}
else{
return;
}
}
}
private void swap(int i, int j){
Object copy = heap[i];
heap[i] = heap[j];
heap[j] = copy;
}
#SuppressWarnings("unchecked")
public T remove(){
if(heapSize == 0)
throw new IllegalStateException("Trying to remove from an empty PQ!");
Object p = heap[0];
heap[0] = heap[--heapSize];
reheapDown();
return (T)p;
}
#SuppressWarnings("unchecked")
private void reheapDown(){
int outOfPlaceInd = 0;
int leftInd = 2*outOfPlaceInd+1; //left child
int rightInd = 2*outOfPlaceInd+2; //right child
while(leftInd <= heapSize-1){
int smallerChildInd = leftInd;
if ((rightInd < heapSize) && (comparator.compare((T)heap[rightInd], (T)heap[leftInd]) < 0))
smallerChildInd = rightInd;
// is the parent smaller or equal to the smaller child
int compare = comparator.compare((T)heap[outOfPlaceInd], (T)heap[smallerChildInd]);
// if the parent is larger than the child...swap with smaller child
if (compare > 0)
{
swap(outOfPlaceInd, smallerChildInd);
// update indices
outOfPlaceInd = smallerChildInd;
leftInd = 2*outOfPlaceInd + 1;
rightInd = 2*outOfPlaceInd + 2;
}
else
{
return;
}
}
}
public static void main( String[] args ) {
PQHeap<Integer> pq = new PQHeap<Integer>(new TestIntComparator());
pq.add( 10 );
pq.add( 20 );
System.out.println(pq.size());
pq.add( 20 );
pq.add( 30 );
class TestIntComparator implements Comparator<Integer> {
public TestIntComparator() {;}
public int compare(Integer o1, Integer o2) {
return o1-o2;
}
}
}
}
// class NaturalComparator<T extends Comparable<T>> implements Comparator<T> {
// public int compar(T a, T b) {
// return a.compareTo(b);
// }
// }
In PQHeap constructor you don't assign input comparator object to class field. Add line like this:
this.comparator = comparator;
in your constructor
In my program I have a pair class:
class Pair {
public int ind = 0;
public String letter = "";
public Pair(int a, String b) {
ind = a; //index
letter = b;
}
}
how do I set the index (ind) of an element in an arraylist of Pairs? I have tried
RightMotor.ind.set(j, i);
and
LeftMotor.set(j, i).ind;
but they don't seem to work.
First you need to 'get' the Pair instance, like:
Pair pair = LeftMotor.get(i);
then you can change its fields:
pair.ind = j;
This can also be done in one line:
LeftMotor.get(i).ind = j;
Hint 1: this is not changing the index (position) of the instance in the list, LeftMotor.get(i) will still return the same element. i and ind are two completely disjunct values.
Hint 2: normally it is better to have private fields and have a method (setter) to change the fields (encapsulation):
class Pair {
private int ind = 0;
private String letter = "";
public Pair(int a, String b) {
ind = a; //index
letter = b;
}
public void setInd(int newInd) {
ind = newInd;
}
}
Hint 3: just to be clear, just because it is called ind it is not the index (position) of the list. It is a whole different question if you want to change the order of the elements in the list.
You want to make those instance variables private, then getters/setters to access/modify them. This allows you to safely and securely manipulate the data with a reduced chance of bleedover (which can crash your program or cause unintended consequences).
Within your class:
class Pair {
private int ind = 0;
private String letter = "";
public Pair(int index, String letter) {
ind = index;
letter = letter;
}
public int getIndex() {
return index;
}
public void setIndex(int index) {
this.index = index;
}
public String getLetter() {
return this.letter;
}
public void setLetter(String letter) {
this.letter = letter;
}
public void setIndexAndLetter(int index, String letter) {
this.index = index;
this.letter = letter;
}
}
Elsewhere in your program:
Pair rightMotor = new Pair(1, "a");
Pair leftMotor = new Pair(2, "b");
Pair middleMotor = new Pair(0, "");
rightMotor.setInd(3);
leftMotor.setLetter("d");
middleMotor.setIndAndLetter(rightMotor.getInd() + leftMotor.getInd(), "z");
You have to create a Pair instance using the Pair constructor before adding it to the ArrayList:
RightMotor.add(new Pair(i,j)); // assuming i is an int and j is a String
If you want to replace the Pair stored in a given index use:
RightMotor.set(index,new Pair(i,j));
If you want to change an existing Pair stored in the ArrayList:
RightMotor.get(index).setInd(newValue);
This will require a setter method in your Pair class:
public void setInd (int i) {
ind = i;
}
So I think that you could solve this in a couple of different ways... however I think I know what you are trying to do.... I feel like you are trying to keep the ArrayList and the Pair index synchronized... either way you will need a helper method to accomplish this. I agree with hendripd that you should use private variables and utilize the getters and setters. However this is my solution.
Pair:
public class Pair implements Comparable<Pair> {
private int index = 0;
private String letter = "";
public Pair(int index, String letter) {
this.index = index;
this.letter = letter;
}
public Pair(Pair pair) {
this.index = pair.getIndex();
this.letter = pair.getLetter();
}
#Override
public int compareTo(Pair pair) {
if (this.index > pair.index) {
return 1;
} else if (this.index < pair.index) {
return -1;
} else {
return 0;
}
}
#Override
public String toString() {
return this.index + " " + this.letter;
}
public int getIndex() {
return this.index;
}
public String getLetter() {
return this.letter;
}
public void setIndex(int index) {
this.index = index;
}
public void setLetter(String letter) {
this.letter = letter;
}
}
Main:
import java.util.ArrayList;
import java.util.Collections;
public class Main {
public static ArrayList<Pair> rightMotor;
public static void main(String[] args) {
rightMotor = new ArrayList<Pair>();
rightMotor.add(new Pair(0, "a"));
rightMotor.add(new Pair(1, "b"));
setIndex(rightMotor, 0, 1);
// If you choose to go with the second option utilizing Comparable<Pair>
// Collections.sort(rightMotor);
for (Pair pair : rightMotor) {
System.out.println(pair.toString());
}
}
public static void setIndex(ArrayList<Pair> motor, int oldIndex, int newIndex) {
Pair tempPair = new Pair(motor.get(oldIndex));
if (oldIndex < newIndex) {
for (int i = oldIndex; i < newIndex; i++) {
motor.set(i, motor.get(i + 1));
motor.get(i).setIndex(i);
}
} else if (oldIndex > newIndex) {
for (int i = oldIndex; i > newIndex; i--) {
motor.set(i, motor.get(i - 1));
motor.get(i).setIndex(i);
}
}
tempPair.setIndex(newIndex);
motor.set(newIndex, tempPair);
}
}
Note that the Pair class implements comparable... you could use Collections.Sort(rightMotor) which then you would only need to fix the indexes of the instances... i.e.
public static void setIndex(ArrayList<Pair> motor, int oldIndex, int newIndex) {
motor.get(oldIndex).setIndex(newIndex);
if (oldIndex < newIndex) {
for (int i = oldIndex; i < newIndex; i++) {
motor.get(i + 1).setIndex(i);
}
} else if (oldIndex > newIndex) {
for (int i = oldIndex; i > newIndex; i--) {
motor.get(i - 1).setIndex(i);
}
}
}
Or... you can use the original one I posted which also handles the sorting at the same time. This keeps your Arraylist in numerical order by index either way.
Test casing:
rightMotor.add(new Pair(0, "a"));
rightMotor.add(new Pair(1, "b"));
rightMotor.add(new Pair(2, "c"));
rightMotor.add(new Pair(3, "d"));
rightMotor.add(new Pair(4, "e"));
rightMotor.add(new Pair(5, "f"));
setIndex(rightMotor, 0, 1);
setIndex(rightMotor, 3, 1);
setIndex(rightMotor, 4, 3);
outputs this result:
0 b
1 d
2 a
3 e
4 c
5 f
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;
}
}
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; }
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.