Doing a .contains() on HashTables of ArrayLists - java

Say I have the following two structures:
ArrayList<String> test =new ArrayList<String>();
HashTable <ArrayList<String>, Integer> h1 = new Hashtable<Arraylist<String>, Integer>();
HashTable <ArrayList<String>, Integer> h2 = new Hashtable<Arraylist<String>, Integer>();
Can I possibly check if h1 contains a key (Basically an Arraylist) that is present in h2 and then replace the int value in it as in:
for (Hashtable <ArrayList<String>, Integer> entry : h2.entrySet()) {
if(h1.containsKey(entry.getKey()))
entry.replace(entry.getKey(), 1);
}
Or do:
for (Hashtable <ArrayList<String>, int> entry : h2.entrySet()) {
if(h1.containsKey(entry.getKey()))
h2.put(entry.getKey(),1);
}
?? Please do help me out...

Except for the compilation errors your last code block should do exactly what you want.
ArrayList<String> test = new ArrayList<String>();
Hashtable <ArrayList<String>, Integer> h1 = new Hashtable <ArrayList<String>, Integer>();
Hashtable <ArrayList<String>, Integer> h2 = new Hashtable <ArrayList<String>, Integer>();
for (ArrayList<String> key : h2.keySet()) {
if(h1.containsKey(key))
h2.put(key, 1);
}

This is a realy funcy thing to do, and as people already have said you should seriously consider your use of datatypes. But this should do the trick.
Hashtable<ArrayList<String>, Integer> h3 = (Hashtable<ArrayList<String>, Integer>) h2.clone();
for(ArrayList<String> a: h2.keySet()){
if(h1.containsKey(a)){
h3.put(a, 1);
}
}
h2 = h3;

Yes you can do. Because contains() internally does first hashCode() to find out the hash bucket. And then does equals() with each key in the hash bucket with the passed search object.
equals() is overriden in ArrayList (through AbstractList)
and the Javadoc says
This implementation first checks if the specified object is this list.
If so, it returns true; if not, it checks if the specified object is a
list. If not, it returns false; if so, it iterates over both lists,
comparing corresponding pairs of elements. If any comparison returns
false, this method returns false. If either iterator runs out of
elements before the other it returns false (as the lists are of
unequal length); otherwise it returns true when the iterations
complete.
In other words, if 2 lists contains same elements in same sequence, they are eqaul.
And If two ArrayList have same elements their hashCode() should be same

Related

Losing data when transferring data from TreeMap to TreeSet.

I have a TreeMap like so.
// Create a map of word and their counts.
// Put them in TreeMap so that they are naturally sorted by the words
Map<String, Integer> wordCount = new TreeMap<String, Integer>();
wordCount.put("but", 100);
wordCount.put("all", 10);
Since it is a TreeMap the content are sorted by key, i.e. the words.
// Iterate over the map to confirm that the data is stored sorted by words.
// This part is also working nicely and I can see that the ouput is sorted by
// words.
Set<String> words = wordCount.keySet();
logger.debug("word, count");
for (Iterator<String> itForWords = words.iterator(); itForWords.hasNext();) {
String word = (String) itForWords.next();
Integer count = wordCount.get(word);
logger.debug("{}, {}", word, count);
}
Now I am trying to sort them by count. Since TreeMap will not drop the trick I am moving them to a SortedSet.
// Trying to sort the collection by the count now.
// TreeMap cant be sorted on values.
// Lets put them in a sorted set and put a comparator to sort based on values
// rather than keys.
SortedSet<Map.Entry<String, Integer>> wordCountSortedByCount = new TreeSet<Map.Entry<String, Integer>>(
new Comparator<Map.Entry<String, Integer>>() {
#Override
public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) {
return o1.getValue().compareTo(o1.getValue());
}
});
wordCountSortedByCount.addAll(wordCount.entrySet());
At this point I am expecting the TreeSet to have 2 entries. But it is showing only one. Please help.
// This is NOT WORKING
// The size is only 1. It should have been two.
logger.debug("Size of sorted collection is {}", wordCountSortedByCount.size());
To avoid such errors, it worth to use comparator in Java 8:
Comparator.comparing(Map.Entry::getValue)
SortedSet<Map.Entry<String, Integer>> wordCountSortedByCount =
new TreeSet<>(Comparator.comparing(Map.Entry::getValue));
Modify return o1.getValue().compareTo(o1.getValue()); to
return o1.getValue().compareTo(o2.getValue());
Output will be 2.

How can be the keys of a Map be treated equal if they have similar properties but in different order?

I have a Pair class and -
Map<Pair<String, String>, String> map = new HashMap<Pair<String, String>, String>();
map.put(new Pair("name1", "address1"), "Young");
map.put(new Pair("name2", "address2"), "Old");
.
.
. and so on.
Now I have a requirement where I have to treat two pairs as equal where
pair1 = new Pair("name1", "address1");
pair2 = new Pair("address1", "name1");
Note that pair2 in not as a key in the map but pair1 is. So that when I do
map.get(pair1);
map.get(pair2);
gives me same result as "Young";
What additional things should I do to achieve it?
You will have to override equals() and hashcode() in the Pair class.
Also refer these https://stackoverflow.com/a/2265637/869488
https://stackoverflow.com/a/27609/869488 (check the 'Also remember' heading of these answer)
Override hashCode() and equals() method in Pair class so that,
pair1 = new Pair("name1", "address1");
pair2 = new Pair("address1", "name1");
will be in same hash bucket in a hashmap. When you do correctly override the hashCode() method to ensure that equal objects get equal hash codes, the hashmap is able to find the two equal objects and place them in the same hash bucket.

Selecting random key and value sets from a Map in Java

I want to get random keys and their respective values from a Map. The idea is that a random generator would pick a key and display that value. The tricky part is that both key and value will be strings, for example myMap.put("Geddy", "Lee").
HashMap<String, String> x;
Random random = new Random();
List<String> keys = new ArrayList<String>(x.keySet());
String randomKey = keys.get( random.nextInt(keys.size()) );
String value = x.get(randomKey);
This question should be of help to you Is there a way to get the value of a HashMap randomly in Java? and this one also Picking a random element from a set because HashMap is backed by a HashSet. It would be either O(n) time and constant space or it would be O(n) extra space and constant time.
If you don't mind the wasted space, one approach would be to separately keep a List of all keys that are in the Map. For best performance, you'll want a List that has good random-access performance (like an ArrayList). Then, just get a random number between 0 (inclusive) and list.size() (exclusive), pull out the key at that index, and look that key up.
Random rand = something
int randIndex = rand.nextInt(list.size());
K key = list.get(randIndex);
V value = map.get(key);
This approach also means that adding a key-value pair is a good deal cheaper than removing one. To add the key-value pair, you would test to see if the key is already in the map (if your values can be null, you'll have to separately call map.containsKey; if not, you can just add the key-value pair and see if the "old value" it returns is null). If the key is already in the map, the list is unchanged, but if not, you add the key to the list (an O(1) operation for most lists). Removing a key-value pair, though, involves an O(N) operation to remove the key from the list.
If space is a big concern, but performance is less so, you could also get an Iterator over the map's entry set (Map.entrySet()), and skip randIndex entries before returning the one you want. But that would be an O(N) operation, which kinda defeats the whole point of a map.
Finally, you can just get the entry set's toArray() and randomly index into that. That's simpler, though less efficient.
if your keys are integer, or something comparable, you can use TreeMap to do that.
TreeMap<Integer, Integer> treeMap = new TreeMap<>();
int key = RandomUtils.ranInt(treeMap.lastKey());
int value = treeMap.ceilingKey(key);
I would copy the Map into an array and select the entry you want at random. This avoid the need to also lookup the value from the key.
Map<String, String> x = new HashMap<String, String>();
Map.Entry<String,String>[] entries = x.entrySet().toArray(new Map.Entry[0]);
Random rand = new Random();
// call repeatedly
Map.Entry<String, String> keyValue = entries[rand.nextInt(entries.length)];
If you want to avoid duplication, you can randomize the order of the entries
Map<String, String> x = new HashMap<String, String>();
List<Map.Entry<String,String>> entries = new ArrayList<Map.Entry<String, String>> (x.entrySet());
Collections.shuffle(entries);
for (Map.Entry<String, String> entry : entries) {
System.out.println(entry);
}
Use reservoir sampling to select a list of random keys, then insert them into a map (along with their corresponding values in the source map.)
This way you do not need to copy the whole keySet into an array, only the selected keys.
public static <K, V>Map<K, V> sampleFromMap(Map<? extends K, ? extends V> source, int n, Random rnd) {
List<K> chosenKeys = new ArrayList<K>();
int count = 0;
for (K k: source.keySet()) {
if (count++ < n) {
chosenKeys.add(k);
if (count == n) {
Collections.shuffle(chosenKeys, rnd);
}
} else {
int pos = rnd.nextInt(count);
if (pos < n) {
chosenKeys.set(pos, k);
}
}
}
Map<K, V> result = new HashMap<K, V>();
for (K k: chosenKeys) {
result.put(k, source.get(k));
}
return Collections.unmodifiableMap(result);
}
In some cases you might want to preserve an order you put the elements in the Set,
In such scenario you can use, This
Set<Integer> alldocsId = new HashSet<>();
for (int i=0;i<normalized.length;i++)
{
String sql = "SELECT DISTINCT movieID FROM postingtbl WHERE term=?";
PreparedStatement prepstm = conn.prepareStatement(sql);
prepstm.setString(1,normalized[i]);
ResultSet rs = prepstm.executeQuery();
while (rs.next())
{
alldocsId.add(rs.getInt("MovieID"));
}
prepstm.close();
}
List<Integer> alldocIDlst = new ArrayList<>();
Iterator it = alldocsId.iterator();
while (it.hasNext())
{
alldocIDlst.add(Integer.valueOf(it.next().toString()));
}
Been a while since a played with java, but doesn't keySet() give you a list that you can select from using a numerical index? I think you could pick a random number and select that from the keySet of myMap, then select the corresponding value from myMap. Can't test this right now, but it seems to strike me as possible!

Sorting Java TreeMap by value not working when more than two values have the same sort-property

I want to sort a Java TreeMap based on some attribute of value. To be specific, I want to sort a TreeMap<Integer, Hashset<Integer>> based on the size of Hashset<Integer>. To achieve this, I have done the following:
A Comparator class:
private static class ValueComparer implements Comparator<Integer> {
private Map<Integer, HashSet<Integer>> map = null;
public ValueComparer (Map<Integer, HashSet<Integer>> map){
super();
this.map = map;
}
#Override
public int compare(Integer o1, Integer o2) {
HashSet<Integer> h1 = map.get(o1);
HashSet<Integer> h2 = map.get(o2);
int compare = h2.size().compareTo(h1.size());
if (compare == 0 && o1!=o2){
return -1;
}
else {
return compare;
}
}
}
A usage example:
TreeMap<Integer, HashSet<Integer>> originalMap = new TreeMap<Integer, HashSet<Integer>>();
//load keys and values into map
ValueComparer comp = new ValueComparer(originalMap);
TreeMap<Integer, HashSet<Integer>> sortedMap = new TreeMap<Integer, HashSet<Integer>>(comp);
sortedMap.putAll(originalMap);
The problem:
This doesn't work when originalMap contains more than 2 values of the same size. For other cases, it works alright. When more than two values in the map are of same size, the third value in the new sorted-map is null and throws NullPointerException when I try to access it.
I can't figure out what the problem is. Woule be nice if someone could point out.
Update:
Here's an example that works when two values have the same size: http://ideone.com/iFD9c
In the above example, if you uncomment lines 52-54, this code will fail- that's what my problem is.
Update: You cannot return -1 from ValueComparator just because you want to avoid duplicate keys to not be removed. Check the contract of Comparator.compare.
When you pass a Comparator to TreeMap you compute a ("new") place to put the entry. No (computed) key can exist more than once in a TreeMap.
If you want to sort the orginalMap by size of the value you can do as follows:
public static void main(String[] args) throws Exception {
TreeMap<Integer, HashSet<Integer>> originalMap =
new TreeMap<Integer, HashSet<Integer>>();
originalMap.put(0, new HashSet<Integer>() {{ add(6); add(7); }});
originalMap.put(1, new HashSet<Integer>() {{ add(6); }});
originalMap.put(2, new HashSet<Integer>() {{ add(9); add(8); }});
ArrayList<Map.Entry<Integer, HashSet<Integer>>> list =
new ArrayList<Map.Entry<Integer, HashSet<Integer>>>();
list.addAll(originalMap.entrySet());
Collections.sort(list, new Comparator<Map.Entry<Integer,HashSet<Integer>>>(){
public int compare(Map.Entry<Integer, HashSet<Integer>> o1,
Map.Entry<Integer, HashSet<Integer>> o2) {
Integer size1 = (Integer) o1.getValue().size();
Integer size2 = (Integer) o2.getValue().size();
return size2.compareTo(size1);
}
});
System.out.println(list);
}
Your comparator logic (which I'm not sure I follow why you'd return -1 if the set sizes are equal but they keys are different) shouldn't affect what the Map itself returns when you call get(key).
Are you positive you aren't inserting null values into the initial map? What does this code look like?
Your comparator doesn't respect the Comparator contract: if compare(o1, o2) < 0, then compare(o2, o1) should be > 0. You must find a deterministic way of comparing your elements when both sizes are the same and the integers are not identical. You could perhaps use the System.identityHashCode() of the integers to compare them in this case.
That said, I really wonder what you could do with such a map: you can't create new Integers and use them to get a value out of the map, and you can't modify the sets that it holds.
Side note: your comparator code sample uses map and data to refer to the same map.
You can have TreeMap ordered only by keys. There is no way of creating TreeMap ordered by values, because you will get StackOverflowException.
Think about it. To get an element from a tree, you need to perform comparisions, but to perform comparisions, you need to get elements.
You will have to sort it in other collection or to use Tree, you will have to encapsulate the integer (from entry value) also into the entry key and define comparator using that integer taken from a key.
Assuming you cannot use a comparator that returns 0 with a Set, this might work: Add all the elements in originalMap.entrySet() to an ArrayList and then sort the ArrayList using your ValueComparer, changing it to return 0 as necessary.
Then add all the entries in the sorted ArrayList to a LinkedHashMap.
I had a similar problem as the original poster. I had a TreeMap i wanted to sort on a value. But when I made a comparator that looked at the value, i had issues because of the breaking of the comparator that JB talked about. I was able to use my custom comparator and still observe the contract. When the valuse I was looking at were equal, i fell back to comparing the keys. I didn't care about the order if values were equal.
public int compare(String a, String b) {
if(base.get(a)[0] == base.get(b)[0]){ //need to handle when they are equal
return a.compareTo(b);
}else if (base.get(a)[0] < base.get(b)[0]) {
return -1;
} else {
return 1;
} // returning 0 would merge keys

Comparing maps with lists in java

Ive come across a nice problem recently,
I have a Map
LinkedHashMap<String, List<MyCustomObject>>
I need to compare this to another Map, which will be populated in the exact same method is the data in the DB is the same.
What really is happening is , that I am populating map1 at time 0, and populating map2 at time t, using the same java method(when I say this, i mean using the same DB/tables etc)
So if the data on the DB side is same, then these two maps should have the same content, in the same order(hopefully!!)
Now I want to compare these two maps and if they are not equal (i.e some new data was fetched from the DB), then I want to do some action.If they are the same, I just want to leave it like that.
Can someone point me out to tutes that will help me understand how to do that??
Is serialization an answer to this??
Thanks,
Neeraj
just use equals:
if (!map1.equals(map2)) {
//they differ
}
you will have to implement equals (and hashCode!) on MyCustomObject, since (if I understand correctly) the objects will have the same content but different identity (the default equals just compares the pointers - i.e., the memory address in which the object live in).
Make sure that your MyCustomObject class implements an appropriate equals and hashcode methods and then it's as easy as getting the result of map1.equals(map2).
This is what the javadocs have to say about it:
Compares the specified object with
this map for equality. Returns true if
the given object is also a map and the
two Maps represent the same mappings.
More formally, two maps t1 and t2
represent the same mappings if
t1.entrySet().equals(t2.entrySet()).
This ensures that the equals method
works properly across different
implementations of the Map interface.
The other answers are good and here's an illustration that indeed, the map functions iterate through the keys and the values (and compare items from the lists) to do equality comparison. As they say, you're going to need to override hashCode and equals.
static public List<String> getStringList(String... arg) {
ArrayList<String> arrayList = new ArrayList<String>();
for(int x=0;x<arg.length;x++) arrayList.add(arg[x]);
return arrayList;
}
static public void main(String[] arg) {
LinkedHashMap<String, List<String>> mapA = new LinkedHashMap<String, List<String>>();
LinkedHashMap<String, List<String>> mapB = new LinkedHashMap<String, List<String>>();
LinkedHashMap<String, List<String>> mapC = new LinkedHashMap<String, List<String>>();
mapA.put("same", getStringList("a", "b", "c"));
mapB.put("same", getStringList("a", "b", "c"));
mapC.put("same", getStringList("a", "b", "d"));
System.out.println(mapA.equals(mapB) ? "true" : "false");
System.out.println(mapA.equals(mapC) ? "true" : "false");
}

Categories

Resources