Combinations of attributes values - java

I struggle with generating all possible combinations of values of a List of Attributes. As an example, for three attributes A, B,C, with the following values:{a1,a2} for A ,{b1,b2} for B, and {c1,c2} for C, I should get 8 combinations:
a1,b1,c1
a1,b1,c2
a1,b2,c1
a1,b2,c2
a2,b1,c1
a2,b1,c2
a2,b2,c1
a2,b2,c2
I used the following two recursive java functions where attribute_to_domain is a Map where we put each attribute as a key and its values as a value, and we add each combination as an <ArrayList<String> toenumerate_tuples as an ArrayList<ArrayList<String>>
public void fillTuples(Map<String, Set<String>> attribute_to_domain, ArrayList<String> attributes, ArrayList<ArrayList<String>> enumerate_tuples)
{
for (Map.Entry<String, Set<String>> entrySet :attribute_to_domain.entrySet()) {
String attribute=entrySet.getKey();
attributes.add(attribute);
}
int pos = 0;
Set<String> domain = attribute_to_domain.get(attributes.get(pos));
for (Iterator<String> it = domain.iterator(); it.hasNext();) {
String val = it.next();
ArrayList<String> tuple=new ArrayList<String>();
tuple.add(val);
fillTuples(attribute_to_domain, attributes, 1, tuple, enumerate_tuples);
tuple.remove(tuple.size()-1);
assert(tuple.isEmpty());
}
}
public void fillTuples(Map<String, Set<String>> attribute_to_domain, ArrayList<String> attributes, int pos, ArrayList<String> tuple, ArrayList<ArrayList<String>> enumerate_tuples)
{
assert(tuple.size() == pos);
if (pos == attributes.size())
{
enumerate_tuples.add(tuple);
return;
}
Set<String> domain = attribute_to_domain.get(attributes.get(pos));
for (Iterator<String> it = domain.iterator(); it.hasNext();) {
String val = it.next();
tuple.add(val);
fillTuples(attribute_to_domain, attributes, pos+1, tuple, enumerate_tuples);
tuple.remove(tuple.size()-1);
}
}
The problem that I get enumerate_tuples with empty elements and I can not keep changes that happened on it through the calls.
How can I solve this problem, please? Thanks in advance.

There is a simpler and faster solution, one that does not require recursion.
The number of output combinations can be calculated in advanced: multiplication of attributes in your case 2*2*2 but it is true for every combination.
Furthermore, we can calculate which value will be placed in each combination based on the combination index. if we assume combination index goes from 0 to 7:
for A:
- combinations 0-3 will contain a1
- combinations 4-7 will contain a2
for B
- combinations 0,1,4,5 will contain b1
- combinations 2,3,6,7 will contain b2
for C
- combinations 0,2,4,6 will contain c1
- combinations 1,3,5,7 will contain c2
so the formula for value placement is based on the combination index, the order of the attributes (A first etc) and the order of the values in the attribute.
the complexity of this algorithm is o(n*m) where n is number of attributes and m number of values.

Revised from Cartesian product of arbitrary sets in Java
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
public class CartesianProduct {
public static Set<Set<Object> > cartesianProduct(Set<?>... sets) {
if (sets.length < 2)
throw new IllegalArgumentException(
"Can't have a product of fewer than two sets (got " +
sets.length + ")");
return _cartesianProduct(0, sets);
}
private static Set<Set<Object> > _cartesianProduct(int index, Set<?>... sets) {
Set<Set<Object> > ret = new TreeSet<Set<Object> >(new Comparator<Set<Object> >() {
#Override
public int compare(Set<Object> o1, Set<Object> o2) {
return o1.toString().compareTo(o2.toString());
}
});
if (index == sets.length) {
ret.add(new TreeSet<Object>());
} else {
for (Object obj : sets[index]) {
for (Set<Object> set : _cartesianProduct(index+1, sets)) {
set.add(obj);
ret.add(set);
}
}
}
return ret;
}
public static void main(String[] args) {
Map<String, Set<String> > dataMap = new HashMap<String, Set<String> >();
dataMap.put("A", new TreeSet<String>(Arrays.asList("a1", "a2")));
dataMap.put("B", new TreeSet<String>(Arrays.asList("b1", "b2")));
dataMap.put("C", new TreeSet<String>(Arrays.asList("c1", "c2")));
System.out.println(cartesianProduct(dataMap.values().toArray(new Set<?>[0])));
}
}

Related

How to find minimum in multiple records with same key in java?

I have collected some records from file and want to perform group by and minimum on the records similar to SQL. Records is in the form of key value pairs where value is not a float or double value like:
here values are version numbers like every software has version numbers based on releases like : 10.1.1 , 10.1.2, 10.1.3 etc
A 1.12
A 1.13
A 1.45
B 5.6
B 4.5
C 5.6.4
Output should be -> A 1.12
B 4.5
C 5.6.4
Initially I started to solve this problem by using a HashMap data structure:
Map<String,List<String>> map = new HashMap<>();
As values are not float or double I iterate through all values and removed decimal point and concatenated digits to form integer.
Eg : A 112 A 113
I got stuck at point , how to find key which has minimum value ? I tried to use TreeMap but no luck.
Can anyone help me how to find key which has minimum value ?
output should be : A 1.12 B 4.5 C 5.6.4
for single record like Eg : C 5.6.4 , minimum is single record.
Based on my data structure selection, Map<String,List<Integer>>, I am stuck is how to find key which has minimum value, like we do in SQL queries, using group by and min aggregate function. , here i got A -> [] A -> [] A -> [] B -> [] B -> [] C -> [] ** Here challenge is finding minimum among multiple list for same key ** , as you can see based on my data structure selection same key has multiple lists.
Please Find the below solution :
You can maintain a HashMap with "key" as String and "value" as PriorityQueue.
HashMap<String,PriorityQueue<String>> map = new HashMap<String,PriorityQueue<String>>();
You can group the values by the Key and can maintain the values in the PriorityQueue.
Java
's PriorityQueue is a Min Heap with the smallest value stored at the root.
when you invoke the peek() method on the priorityQueue it will return the min value stored at the root.
Below is the sample code which will help you :
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.PriorityQueue;
public class GroupAndFindMinimum {
public static void main(String[] args) {
HashMap<String,PriorityQueue<String>> map = new HashMap<String,PriorityQueue<String>>();
PriorityQueue<String> q1 = new PriorityQueue<String>();
q1.add("1.12");q1.add("1.13");q1.add("1.45");
PriorityQueue<String> q2 = new PriorityQueue<String>();
q2.add("5.6");q2.add("4.5");
PriorityQueue<String> q3 = new PriorityQueue<String>();
q3.add("5.6.4");
map.put("A",q1);
map.put("B", q2);
map.put("C", q3);
for(Iterator<? extends Map.Entry<? extends String, ? extends PriorityQueue<String>>> it = map.entrySet().iterator(); it.hasNext() ;)
{
Map.Entry<? extends String, ? extends PriorityQueue<String>> t = it.next();
System.out.println(t.getKey() + " " + t.getValue().peek());
}
}
}
Below is the Output of above program :
A 1.12
B 4.5
C 5.6.4
If you need the MAX value to be returned for each group then you can achieve it with the help of an "Comparator" as well.
Below is the code for that :
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.PriorityQueue;
public class GroupAndFindMinimum {
public static void main(String[] args) {
HashMap<String,PriorityQueue<String>> map = new HashMap<String,PriorityQueue<String>>();
comparatorPQ comp = new comparatorPQ<String>();
PriorityQueue<String> q1 = new PriorityQueue<String>(3,comp);
q1.add("1.12");q1.add("1.13");q1.add("1.45");
PriorityQueue<String> q2 = new PriorityQueue<String>(2,comp);
q2.add("5.6");q2.add("4.5");
PriorityQueue<String> q3 = new PriorityQueue<String>(1,comp);
q3.add("5.6.4");
map.put("A",q1);
map.put("B", q2);
map.put("C", q3);
for(Iterator<? extends Map.Entry<? extends String, ? extends PriorityQueue<String>>> it = map.entrySet().iterator(); it.hasNext() ;)
{
Map.Entry<? extends String, ? extends PriorityQueue<String>> t = it.next();
System.out.println(t.getKey() + " " + t.getValue().peek());
}
}
}
class comparatorPQ<k> implements Comparator
{
#Override
public int compare(Object a1, Object b1) {
String a = null ,b= null;
if(a1 instanceof String)
a = (String)a1;
if(b1 instanceof String)
b = (String)b1;
if( b.compareTo(a) > 1 )
return 1;
else if(b.compareTo(a) < 1)
return -1;
return 0;
}
}
Output :
A 1.45
B 5.6
C 5.6.4
The first challenge would be to find the "minimal" value. Simply removing the periods and treating the values as integers is insufficient - that would result in 6.5.4. being "smaller" than 1.2.3.4., which doesn't seem to be what you intended. A better approach would be to split the strings by the period and treat each element individually as an int:
public String min(String v1, String v2) {
// Any string should be "smaller" than null
if (v1 == null) {
return v2;
}
if (v2 == null) {
return v1;
}
// Split both of them and iterate the common indexes:
String[] v1parts = v1.split("\\.");
String[] v2parts = v2.split("\\.");
int commonLenth = Math.min(v1parts.length, v2parts.length);
for (int i = 0; i < commonLength; ++i) {
int v1elem = Integer.parseInt(v1parts[i]);
int v2elem = Integer.parseInt(v2parts[i]);
if (v1elem < v2elem) {
return v1;
} else if (v1elem > v2elem) {
return v2;
}
}
// Done iterating the common indexes and they are all equal
// The shorter string is therefore the minimal one:
if (v1parts.length < v2parts.length) {
return v1;
}
return v2;
}
Now that you have such a function, it's just a matter of iterating the key-value pairs and placing the minimal value in a Map. E.g. (pseudo-code assuming you have some sort of Pair class):
Map<String, String> minimums = new HashMap<>();
for (Pair<String, String> entry : myListOfPairs) {
String key = entry.getKey();
minimums.put(key, min(entry.getValue(), minimums.get(key));
}
An O(n) algorithm that parses the input once and keeps on updating the output map should suffice. I am assuming that the input is provided in the form of two lists with equal size.
public Map<String, String> groupAndFindMinimum(ArrayList< String > inputKeys, ArrayList< String > inputValues){
Map<String, String> output = new ArrayMap<String, String>();
int i = 0;
for(String key : inputKeys){
if(output.containsKey(key)){
output[key] = min(output[key], inputValues.get(i));
}
else{
output.insert(key, inputValues.get(i));
}
i++;
}
return output;
}
I hope this helps.
Your algorithm to find minimum is correct.

List of string with occurrences count and sort

I'm developing a Java Application that reads a lot of strings data likes this:
1 cat (first read)
2 dog
3 fish
4 dog
5 fish
6 dog
7 dog
8 cat
9 horse
...(last read)
I need a way to keep all couple [string, occurrences] in order from last read to first read.
string occurrences
horse 1 (first print)
cat 2
dog 4
fish 2 (last print)
Actually i use two list:
1) List<string> input; where i add all data
In my example:
input.add("cat");
input.add("dog");
input.add("fish");
...
2)List<string> possibilities; where I insert the strings once in this way:
if(possibilities.contains("cat")){
possibilities.remove("cat");
}
possibilities.add("cat");
In this way I've got a sorted list where all possibilities.
I use it like that:
int occurrence;
for(String possible:possibilities){
occurrence = Collections.frequency(input, possible);
System.out.println(possible + " " + occurrence);
}
That trick works good but it's too slow(i've got millions of input)... any help?
(English isn’t my first language, so please excuse any mistakes.)
Use a Map<String, Integer>, as #radoslaw pointed, to keep the insertion sorting use LinkedHashMap and not a TreeMap as described here:
LinkedHashMap keeps the keys in the order they were inserted, while a TreeMap is kept sorted via a Comparator or the natural Comparable ordering of the elements.
Imagine you have all the strings in some array, call it listOfAllStrings, iterate over this array and use the string as key in your map, if it does not exists, put in the map, if it exists, sum 1 to actual result...
Map<String, Integer> results = new LinkedHashMap<String, Integer>();
for (String s : listOfAllStrings) {
if (results.get(s) != null) {
results.put(s, results.get(s) + 1);
} else {
results.put(s, 1);
}
}
Make use of a TreeMap, which will keep ordering on the keys as specified by the compare of your MyStringComparator class handling MyString class which wraps String adding insertion indexes, like this:
// this better be immutable
class MyString {
private MyString() {}
public static MyString valueOf(String s, Long l) { ... }
private String string;
private Long index;
public hashcode(){ return string.hashcode(); }
public boolean equals() { // return rely on string.equals() }
}
class MyStringComparator implements Comparator<MyString> {
public int compare(MyString s1, MyString s2) {
return -s1.getIndex().compareTo(s2.gtIndex());
}
}
Pass the comparator while constructing the map:
Map<MyString,Integer> map = new TreeMap<>(new MyStringComparator());
Then, while parsing your input, do
Long counter = 0;
while (...) {
MyString item = MyString.valueOf(readString, counter++);
if (map.contains(item)) {
map.put(map.get(item)+1);
} else {
map.put(item,1);
}
}
There will be a lot of instantiation because of the immutable class, and the comparator will not be consistent with equals, but it should work.
Disclaimer: this is untested code just to show what I'd do, I'll come back and recheck it when I get my hands on a compiler.
Here is the complete solution for your problem,
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class DataDto implements Comparable<DataDto>{
public int count = 0;
public String string;
public long lastSeenTime;
public DataDto(String string) {
this.string = string;
this.lastSeenTime = System.currentTimeMillis();
}
public boolean equals(Object object) {
if(object != null && object instanceof DataDto) {
DataDto temp = (DataDto) object;
if(temp.string != null && temp.string.equals(this.string)) {
return true;
}
}
return false;
}
public int hashcode() {
return string.hashCode();
}
public int compareTo(DataDto o) {
if(o != null) {
return o.lastSeenTime < this.lastSeenTime ? -1 : 1;
}
return 0;
}
public String toString() {
return this.string + " : " + this.count;
}
public static final void main(String[] args) {
String[] listOfAllStrings = {"horse", "cat", "dog", "fish", "cat", "fish", "dog", "cat", "horse", "fish"};
Map<String, DataDto> results = new HashMap<String, DataDto>();
for (String s : listOfAllStrings) {
DataDto dataDto = results.get(s);
if(dataDto != null) {
dataDto.count = dataDto.count + 1;
dataDto.lastSeenTime = System.nanoTime();
} else {
dataDto = new DataDto(s);
results.put(s, dataDto);
}
}
List<DataDto> finalResults = new ArrayList<DataDto>(results.values());
System.out.println(finalResults);
Collections.sort(finalResults);
System.out.println(finalResults);
}
}
Ans
[horse : 1, cat : 2, fish : 2, dog : 1]
[fish : 2, horse : 1, cat : 2, dog : 1]
I think this solution will be suitable for your requirement.
If you know that your data is not going to exceed your memory capacity when you read it all into memory, then the solution is simple - using a LinkedList or a and a LinkedHashMap.
For example, if you use a Linked list:
LinkedList<String> input = new LinkedList();
You then proceed to use input.add() as you did originally. But when the input list is full, you basically use Jordi Castilla's solution - but put the entries in the linked list in reverse order. To do that, you do:
Iterator<String> iter = list.descendingIterator();
LinkedHashMap<String,Integer> map = new LinkedHashMap<>();
while (iter.hasNext()) {
String s = iter.next();
if ( map.containsKey(s)) {
map.put( s, map.get(s) + 1);
} else {
map.put(s, 1);
}
}
Now, the only real difference between his solution and mine is that I'm using list.descendingIterator() which is a method in LinkedList that gives you the entries in backwards order, from "horse" to "cat".
The LinkedHashMap will keep the proper order - whatever was entered first will be printed first, and because we entered things in reverse order, then whatever was read last will be printed first. So if you print your map the result will be:
{horse=1, cat=2, dog=4, fish=2}
If you have a very long file, and you can't load the entire list of strings into memory, you had better keep just the map of frequencies. In this case, in order to keep the order of entry, we'll use an object such as this:
private static class Entry implements Comparable<Entry> {
private static long nextOrder = Long.MIN_VALUE;
private String str;
private int frequency = 1;
private long order = nextOrder++;
public Entry(String str) {
this.str = str;
}
public String getString() {
return str;
}
public int getFrequency() {
return frequency;
}
public void updateEntry() {
frequency++;
order = nextOrder++;
}
#Override
public int compareTo(Entry e) {
if ( order > e.order )
return -1;
if ( order < e.order )
return 1;
return 0;
}
#Override
public String toString() {
return String.format( "%s: %d", str, frequency );
}
}
The trick here is that every time you update the entry (add one to the frequency), it also updates the order. But the compareTo() method orders Entry objects from high order (updated/inserted later) to low order (updated/inserted earlier).
Now you can use a simple HashMap<String,Entry> to store the information as you read it (I'm assuming you are reading from some sort of scanner):
Map<String,Entry> m = new HashMap<>();
while ( scanner.hasNextLine() ) {
String str = scanner.nextLine();
Entry entry = m.get(str);
if ( entry == null ) {
entry = new Entry(str);
m.put(str, entry);
} else {
entry.updateEntry();
}
}
Scanner.close();
Now you can sort the values of the entries:
List<Entry> orderedList = new ArrayList<Entry>(m.values());
m = null;
Collections.sort(orderedList);
Running System.out.println(orderedList) will give you:
[horse: 1, cat: 2, dog: 4, fish: 2]
In principle, you could use a TreeMap whose keys contained the "order" stuff, rather than a plain HashMap like this followed by sorting, but I prefer not having either mutable keys in a map, nor changing the keys constantly. Here we are only changing the values as we fill the map, and each key is inserted into the map only once.
What you could do:
Reverse the order of the list using
Collections.reverse(input). This runs in linear time - O(n);
Create a Set from the input list. A Set garantees uniqueness.
To preserve insertion order, you'll need a LinkedHashSet;
Iterate over this set, just as you did above.
Code:
/* I don't know what logic you use to create the input list,
* so I'm using your input example. */
List<String> input = Arrays.asList("cat", "dog", "fish", "dog",
"fish", "dog", "dog", "cat", "horse");
/* by the way, this changes the input list!
* Copy it in case you need to preserve the original input. */
Collections.reverse(input);
Set<String> possibilities = new LinkedHashSet<String>(strings);
for (String s : possibilities) {
System.out.println(s + " " + Collections.frequency(strings, s));
}
Output:
horse 1
cat 2
dog 4
fish 2

Comparing Maps of Objects

I have set up a test that:
retrieves data concerning several court cases: each court case is stored in a CourtCase object
a set of CourtCase objects is then stored in a Map
I retrieve these data twice (from two different sources) so I end up with two Maps
The data within the objects should be the same between the Maps, but the order of the objects within the Maps may not be:
Map1:
A, case1 - B, case2 - C, case3
Map2:
B, case2 - A, case1 - C, case3
How can I best compare these two Maps?
Map#equals does not care about the order. As long as your 2 maps contain the same mapping it will return true.
Map#equals uses Set#equals method, applied to the entry set. Set#equals contract:
Returns true if the specified object is also a set, the two sets have the same size, and every member of the specified set is contained in this set (or equivalently, every member of this set is contained in the specified set).
Note: this assumes that your CourtCase objects have proper equals and hashcode methods to be compared.
Map implementations provides an equals method which do the trick. Map.equals
#user973718 the best to compare two map objects in java is - you can add the keys of a map to list and with those 2 lists you can use the methods retainAll() and removeAll() and add them to another common keys list and different keys list. Using the keys of the common list and different list you can iterate through map, using equals you can compare the maps.
The below code gives this output :
Before {b=2, c=3, a=1}
After {c=333, a=1}
Unequal: Before- 3 After- 333
Equal: Before- 1 After- 1
Values present only in before map: 2
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.commons.collections.CollectionUtils;
public class Demo
{
public static void main(String[] args)
{
Map<String, String> beforeMap = new HashMap<String, String>();
beforeMap.put("a", "1");
beforeMap.put("b", "2");
beforeMap.put("c", "3");
Map<String, String> afterMap = new HashMap<String, String>();
afterMap.put("a", "1");
afterMap.put("c", "333");
System.out.println("Before "+beforeMap);
System.out.println("After "+afterMap);
List<String> beforeList = getAllKeys(beforeMap);
List<String> afterList = getAllKeys(afterMap);
List<String> commonList1 = beforeList;
List<String> commonList2 = afterList;
List<String> diffList1 = getAllKeys(beforeMap);
List<String> diffList2 = getAllKeys(afterMap);
commonList1.retainAll(afterList);
commonList2.retainAll(beforeList);
diffList1.removeAll(commonList1);
diffList2.removeAll(commonList2);
if(commonList1!=null & commonList2!=null) // athough both the size are same
{
for (int i = 0; i < commonList1.size(); i++)
{
if ((beforeMap.get(commonList1.get(i))).equals(afterMap.get(commonList1.get(i))))
{
System.out.println("Equal: Before- "+ beforeMap.get(commonList1.get(i))+" After- "+afterMap.get(commonList1.get(i)));
}
else
{
System.out.println("Unequal: Before- "+ beforeMap.get(commonList1.get(i))+" After- "+afterMap.get(commonList1.get(i)));
}
}
}
if (CollectionUtils.isNotEmpty(diffList1))
{
for (int i = 0; i < diffList1.size(); i++)
{
System.out.println("Values present only in before map: "+beforeMap.get(diffList1.get(i)));
}
}
if (CollectionUtils.isNotEmpty(diffList2))
{
for (int i = 0; i < diffList2.size(); i++)
{
System.out.println("Values present only in after map: "+afterMap.get(diffList2.get(i)));
}
}
}
/**getAllKeys API adds the keys of the map to a list */
private static List<String> getAllKeys(Map<String, String> map1)
{
List<String> key = new ArrayList<String>();
if (map1 != null)
{
Iterator<String> mapIterator = map1.keySet().iterator();
while (mapIterator.hasNext())
{
key.add(mapIterator.next());
}
}
return key;
}
}

ArrayList as key in HashMap

Would it be possible to add an ArrayList as the key of HashMap. I would like to keep the frequency count of bigrams. The bigram is the key and the value is its frequency.
For each of the bigrams like "he is", I create an ArrayList for it and insert it into the HashMap. But I am not getting the correct output.
public HashMap<ArrayList<String>, Integer> getBigramMap(String word1, String word2) {
HashMap<ArrayList<String>, Integer> hm = new HashMap<ArrayList<String>, Integer>();
ArrayList<String> arrList1 = new ArrayList<String>();
arrList1 = getBigram(word1, word2);
if (hm.get(arrList1) != null) {
hm.put(arrList1, hm.get(arrList1) + 1);
} else {
hm.put(arrList1, 1);
}
System.out.println(hm.get(arrList1));
return hm;
}
public ArrayList<String> getBigram(String word1, String word2) {
ArrayList<String> arrList2 = new ArrayList<String>();
arrList2.add(word1);
arrList2.add(word2);
return arrList2;
}
Yes you can have ArrayLists as a keys in a hash map, but it is a very bad idea since they are mutable.
If you change the ArrayList in any way (or any of its elements), the mapping will basically be lost, since the key won't have the same hashCode as it had when it was inserted.
The rule of thumb is to use only immutable data types as keys in a hash map. As suggested by Alex Stybaev, you probably want to create a Bigram class like this:
final class Bigram {
private final String word1, word2;
public Bigram(String word1, String word2) {
this.word1 = word1;
this.word2 = word2;
}
public String getWord1() {
return word1;
}
public String getWord2() {
return word2;
}
#Override
public int hashCode() {
return word1.hashCode() ^ word2.hashCode();
}
#Override
public boolean equals(Object obj) {
return (obj instanceof Bigram) && ((Bigram) obj).word1.equals(word1)
&& ((Bigram) obj).word2.equals(word2);
}
}
Why can't you use something like this:
class Bigram{
private String firstItem;
private String secondItem;
<getters/setters>
#Override
public int hashCode(){
...
}
#Override
public boolean equals(){
...
}
}
instead of using the dynamic collection for limited number of items (two).
From the documentation:
Note: great care must be exercised if mutable objects are used as map
keys. The behavior of a map is not specified if the value of an object is
changed in a manner that affects equals comparisons while the
object is a key in the map. A special case of this prohibition is that it
is not permissible for a map to contain itself as a key. While it is
permissible for a map to contain itself as a value, extreme caution is
advised: the equals and hashCode methods are no longer
well defined on such a map.
You have to take care when you are using mutable objects as keys for the sake of hashCode and equals.
The bottom line is that it is better to use immutable objects as keys.
Try this ,this will work.
public Map<List, Integer> getBigramMap (String word1,String word2){
Map<List,Integer> hm = new HashMap<List, Integer>();
List<String> arrList1 = new ArrayList<String>();
arrList1 = getBigram(word1, word2);
if(hm.get(arrList1) !=null){
hm.put(arrList1, hm.get(arrList1)+1);
}
else {
hm.put(arrList1, 1);
}
System.out.println(hm.get(arrList1));
return hm;
}
I've come up with this solution. It is obviously not usable in all cases, for example over stepping the hashcodes int capacity, or list.clone() complications(if the input list gets changed, key stays the same as intended, but when the items of List are mutable, cloned list has the same reference to its items, which would result in changing the key itself).
import java.util.ArrayList;
public class ListKey<T> {
private ArrayList<T> list;
public ListKey(ArrayList<T> list) {
this.list = (ArrayList<T>) list.clone();
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
for (int i = 0; i < this.list.size(); i++) {
T item = this.list.get(i);
result = prime * result + ((item == null) ? 0 : item.hashCode());
}
return result;
}
#Override
public boolean equals(Object obj) {
return this.list.equals(obj);
}
}
---------
public static void main(String[] args) {
ArrayList<Float> createFloatList = createFloatList();
ArrayList<Float> createFloatList2 = createFloatList();
Hashtable<ListKey<Float>, String> table = new Hashtable<>();
table.put(new ListKey(createFloatList2), "IT WORKS!");
System.out.println(table.get(createFloatList2));
createFloatList2.add(1f);
System.out.println(table.get(createFloatList2));
createFloatList2.remove(3);
System.out.println(table.get(createFloatList2));
}
public static ArrayList<Float> createFloatList() {
ArrayList<Float> floatee = new ArrayList<>();
floatee.add(34.234f);
floatee.add(new Float(33));
floatee.add(null);
return floatee;
}
Output:
IT WORKS!
null
IT WORKS!
Sure it possible. I suppose the issue in your put. Try obtain key for bigram, increment it, remove entry with this bigram and insert updated value
Unlike Array, List can be used as the key of a HashMap, but it is not a good idea, since we should always try to use an immutable object as the key.
.toString() method getting the String represtenation is a good key choice in many cases, since String is an immuteable object and can prefectly stands for the array or list.
Please check below my code in order to understand if key is ArrayList in Map and how JVM will do it for inputs:
here i write hashCode and equals method for TesthashCodeEquals class.
package com.msq;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
class TesthashCodeEquals {
private int a;
private int b;
public TesthashCodeEquals() {
// TODO Auto-generated constructor stub
}
public TesthashCodeEquals(int a, int b) {
super();
this.a = a;
this.b = b;
}
public int getA() {
return a;
}
public void setA(int a) {
this.a = a;
}
public int getB() {
return b;
}
public void setB(int b) {
this.b = b;
}
public int hashCode() {
return this.a + this.b;
}
public boolean equals(Object o) {
if (o instanceof TesthashCodeEquals && o != null) {
TesthashCodeEquals c = (TesthashCodeEquals) o;
return ((this.a == c.a) && (this.b == c.b));
} else
return false;
}
}
public class HasCodeEquals {
public static void main(String[] args) {
Map<List<TesthashCodeEquals>, String> m = new HashMap<>();
List<TesthashCodeEquals> list1=new ArrayList<>();
list1.add(new TesthashCodeEquals(1, 2));
list1.add(new TesthashCodeEquals(3, 4));
List<TesthashCodeEquals> list2=new ArrayList<>();
list2.add(new TesthashCodeEquals(10, 20));
list2.add(new TesthashCodeEquals(30, 40));
List<TesthashCodeEquals> list3=new ArrayList<>();
list3.add(new TesthashCodeEquals(1, 2));
list3.add(new TesthashCodeEquals(3, 4));
m.put(list1, "List1");
m.put(list2, "List2");
m.put(list3, "List3");
for(Map.Entry<List<TesthashCodeEquals>,String> entry:m.entrySet()){
for(TesthashCodeEquals t:entry.getKey()){
System.out.print("value of a: "+t.getA()+", value of b: "+t.getB()+", map value is:"+entry.getValue() );
System.out.println();
}
System.out.println("######################");
}
}
}
.
output:
value of a: 10, value of b: 20, map value is:List2
value of a: 30, value of b: 40, map value is:List2
######################
value of a: 1, value of b: 2, map value is:List3
value of a: 3, value of b: 4, map value is:List3
######################
so this will check the number of objects in List and the values of valriabe in object. if number of objects are same and the values of instance variables is also same then it will consider duplicate key and override the key.
now if i change only the value of object on list3
list3.add(new TesthashCodeEquals(2, 2));
then it will print:
output
value of a: 2, value of b: 2, map value is:List3
value of a: 3, value of b: 4, map value is:List3
######################
value of a: 10, value of b: 20, map value is:List2
value of a: 30, value of b: 40, map value is:List2
######################
value of a: 1, value of b: 2, map value is:List1
value of a: 3, value of b: 4, map value is:List1
######################
so that It always check the number of objects in List and the value of instance variable of object.
thanks
ArrayList.equals() is inherited from java.lang.Object - therefore equals() on ArrayList is independent of the content of the list.
If you want to use an ArrayList as a map key, you will need to override equals() and hashcode() in order to make two arraylists with the same content in the same order return true on a call to equals() and return the same hashcode on a call to hashcode().
Is there any particular reason you have to use an ArrayList as opposed to say a simple String as the key?
edit: Ignore me, as Joachim Sauer pointed out below, I am so wrong it's not even funny.

Algorithm to map one key values to another key's values in the Map

I have some Maps which I would like to calculate the cartesian product of. Can someone please suggest a good algorithm:
Data:
Key1 {100,101,102}
Key2 {200,201}
Key3 {300}
Required Output: (Order does matter)
100,200,300
101,200,300
102,200,300
100,201,300
101,201,300
102,201,300
Map is dynamic so Key and values can vary in size.
Thanks.
You will want to switch to using a LinkedHashMap so that order is preserved when you're iterating over keys.
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
public class CartesianPrint {
public static void main(String[] args) {
Map<Integer,List<Integer>> groupMap = new LinkedHashMap<Integer,List<Integer>>();
groupMap.put(1,Arrays.asList(new Integer[]{100,101,102}));
groupMap.put(2,Arrays.asList(new Integer[]{200,201}));
groupMap.put(3,Arrays.asList(new Integer[]{300}));
List<List<Integer>> values = new ArrayList<List<Integer>>(groupMap.values());
int[] printList = new int[values.size()];
print(values,printList,values.size()-1);
}
static void print(List<List<Integer>> values, int[] printList, int level){
for (Integer value: values.get(level)) {
printList[level] = value;
if(level == 0){
System.out.println(Arrays.toString(printList));
}else{
print(values,printList,level-1);
}
}
}
}
Same as Ondra Žižka, if you don't need a map, take a List it works the same way.
Here is a not so optimized way (I should clone instead of recalculating product in recursion. But the idea is still here and its pretty short. I took special care to keep correct order, that's why I run through List backwards.
public static List<List<Integer>> getCart(List<List<Integer>> a_list) {
List<List<Integer>> l_result = new ArrayList<List<Integer>>();
if (a_list == null || a_list.isEmpty()) {
l_result.add(new ArrayList<Integer>());
return l_result;
}
for (Integer l_value : a_list.get(a_list.size()-1)) {
List<List<Integer>> l_resultPortion = getCart(a_list.subList(0, a_list.size() - 1));
for (List<Integer> l_list : l_resultPortion) {
l_list.add(l_value);
}
l_result.addAll(l_resultPortion);
}
return l_result;
}
I suggest to create a store of tuples (triplet in your example).
List<List<Integer>> store = new LinkedList();
Then create a Stack of numbers.
Stack<Integer> stack = new Stack();
Then write a recursive function:
In each recursive function call, push the actually processed value of the array into the stack, and add the current tuple to the store.
private static process( Iterator<String> keys ){
// Bottom-most key
if( ! keys.hasNext() ){
// Construct the tuple from the stack and add it to store.
}
else {
String currentKey = keys.next();
List<Integer> numbers = map.get( currentKey );
for( int i : numbers ){
stack.push( i );
process ( keys );
stack.pop(); // Dispose processed number.
}
}
}
I hope I figured out the problem right (no guarantee).
Sorry for not implementing it whole but that's your homework :)

Categories

Resources