Java : ArrayList<HashMap<Integer,Integer>> - java

Is there a better approach to do the below in Java, without using external libraries.
I need to model group/child (tree like) structure of int (primitive). In Json
[{1,1}, {1,2}, {2,1},{3,1}]
I need to support addition/removal of elements (element is a pair {group, child} ) without duplication.
I am thinking of, keeping a data structure like.
ArrayList<HashMap<Integer,Integer>>
To add.
Iterate through ArrayList, check HashMap key and value against the value to insert, and insert if not exist.
To delete:
Iterate through ArrayList, check HashMap key and value against the value to delete, and delete if exist.
Is there a better data structure/approach with standard library.
As per one of the answer below, I made a class like this.
Please let me know anything to watchout. I am expecting (and going to try out) arraylist would handle add/remove correctly by using the equal method in KeyValue class. thanks.
static class KeyValue {
int groupPos;
int childPos;
KeyValue(int groupPos, int childPos) {
this.groupPos = groupPos;
this.childPos = childPos;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
KeyValue keyValue = (KeyValue) o;
if (childPos != keyValue.childPos) return false;
if (groupPos != keyValue.groupPos) return false;
return true;
}
#Override
public int hashCode() {
int result = groupPos;
result = 31 * result + childPos;
return result;
}
}

If I understand what you're trying to do, this may be simpler:
TreeMap<Integer,TreeSet<Integer>>
or
HashMap<Integer,HashSet<Integer>>
So, rather than
[{1,1}, {1,2}, {2,1}, {3,1}]
you'd have
[{1, {1, 2}},
{2, {1}},
{3, {1}}]
Note that all 4 of the above classes automatically handles eliminating duplicates.
To add:
TreeMap<Integer, TreeSet<Integer>> map;
TreeSet<Integer> set = map.get(group);
if (set == null) // create it if it doesn't exist
{
set = new TreeSet<Integer>();
map.put(group, set);
}
set.add(child);
To remove:
TreeMap<Integer, TreeSet<Integer>> map;
TreeSet<Integer> set = map.get(group);
set.remove(child);
if (set.isEmpty()) // remove it if it is now empty
map.remove(group);

You may write a class with name KeyValue with two properties to hold group and child. Add KeyValue Objects to ArrayList. For CRUD operations, you may implement equals and compare in your KeyValue pair class.

Instead of HashMap, use a class called Pair with two fields {group,child} which will implement Comparable interface. Then implement/override its equals(), hashCode() and compareTo() methods. Then use either a List<Pair> or Set<Pair> depending on your needs to hold them. Having compareTo() implemented gives you the flexibility to sort Pairs easily too.

I am new to the Data Structure world but I think we can use this based on the assumption that no two Set Objects will be similar
Set validSet=new HashSet(); // Use Generics here
HashSet will provide a constant time for add/delete/contains
SomeObject{
Integer parent ;
Integer child;
//define equals method based on your requirement
}

Going By your Question i think that You want to show this line
[{1,1}, {1,2}, {2,1},{3,1}]
as
Group 1-> 1 , 2 (from first two pair) Group 2-> 1(from
third pair) Group 3-> 1 (from fourth pair)
The data structure that suites most for storing this hierarchy is :
Map<Integer,Set<Integer>> map = new HashMap<Integer,Set<Integer>>();
Where the key part of map stores the group Number. And the value part of map is storing TreeSet which stores the children of that group.
As Example of code:
import java.util.HashMap;
import java.util.ListIterator;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
class TreeLike
{
public static void main(String[] args)
{
Map<Integer,Set<Integer>> map = new HashMap<Integer,Set<Integer>>();
int groups[] = {1,2,3,4,5,6,7};
//To add new group in map
for (int i = 0 ; i < groups.length; i++)
{
Set<Integer> child = new TreeSet<Integer>();
child.add(1);child.add(2);child.add(3);child.add(4);child.add(5);
map.put(groups[i],child);
}
//To add new child(8) to a group (say group 1)
Set<Integer> child = map.get(1);
if (child != null)
{
child.add(8);
map.put(1,child);
}
//To remove a child (say child 4) from group 3
child = map.get(3);
if (child != null)
{
child.remove(4);
map.put(1,child);
}
//To Iterate through all trees
Set<Map.Entry<Integer,Set<Integer>>> entrySet = map.entrySet();
Iterator<Map.Entry<Integer,Set<Integer>>> iterator = entrySet.iterator();
while (iterator.hasNext())
{
Map.Entry<Integer,Set<Integer>> entry = iterator.next();
int group = entry.getKey();
Set<Integer> children = entry.getValue();
System.out.println("Group "+group+" children-->"+children);
}
}
}

Related

How to see the distribution of keys in a HashMap?

When using a hash map, it's important to evenly distribute the keys over the buckets.
If all keys end up in the same bucket, you essentially end up with a list.
Is there a way to "audit" a HashMap in Java in order to see how well the keys are distributed?
I tried subtyping it and iterating Entry<K,V>[] table, but it's not visible.
I tried subtyping it and iterating Entry[] table, but it's not visible
Use Reflection API!
public class Main {
//This is to simulate instances which are not equal but go to the same bucket.
static class A {
#Override
public boolean equals(Object obj) { return false;}
#Override
public int hashCode() {return 42; }
}
public static void main(String[] args) {
//Test data
HashMap<A, String> map = new HashMap<A, String>(4);
map.put(new A(), "abc");
map.put(new A(), "def");
//Access to the internal table
Class clazz = map.getClass();
Field table = clazz.getDeclaredField("table");
table.setAccessible(true);
Map.Entry<Integer, String>[] realTable = (Map.Entry<Integer, String>[]) table.get(map);
//Iterate and do pretty printing
for (int i = 0; i < realTable.length; i++) {
System.out.println(String.format("Bucket : %d, Entry: %s", i, bucketToString(realTable[i])));
}
}
private static String bucketToString(Map.Entry<Integer, String> entry) throws Exception {
if (entry == null) return null;
StringBuilder sb = new StringBuilder();
//Access to the "next" filed of HashMap$Node
Class clazz = entry.getClass();
Field next = clazz.getDeclaredField("next");
next.setAccessible(true);
//going through the bucket
while (entry != null) {
sb.append(entry);
entry = (Map.Entry<Integer, String>) next.get(entry);
if (null != entry) sb.append(" -> ");
}
return sb.toString();
}
}
In the end you'll see something like this in STDOUT:
Bucket : 0, Entry: null
Bucket : 1, Entry: null
Bucket : 2, Entry: Main$A#2a=abc -> Main$A#2a=def
Bucket : 3, Entry: null
HashMap uses the keys produced by the hashCode() method of your key objects, so I guess you are really asking how evenly distributed those hash code values are. You can get hold of the key objects using Map.keySet().
Now, the OpenJDK and Oracle implementations of HashMap do not use the key hash codes directly, but apply another hashing function to the provided hashes before distributing them over the buckets. But you should not rely on or use this implementation detail. So you ought to ignore it. So you should just ensure that the hashCode() methods of your key values are well distributed.
Examining the actual hash codes of some sample key value objects is unlikely to tell you anything useful unless your hash cide method is very poor. You would be better doing a basic theoretical analysis of your hash code method. This is not as scary as it might sound. You may (indeed, have no choice but to do so) assume that the hash code methods of the supplied Java classes are well distributed. Then you just need a check that the means you use for combining the hash codes for your data members behaves well for the expected values of your data members. Only if your data members have values that are highly correlated in a peculiar way is this likely to be a problem.
You can use reflection to access the hidden fields:
HashMap map = ...;
// get the HashMap#table field
Field tableField = HashMap.class.getDeclaredField("table");
tableField.setAccessible(true);
Object[] table = (Object[]) tableField.get(map);
int[] counts = new int[table.length];
// get the HashMap.Node#next field
Class<?> entryClass = table.getClass().getComponentType();
Field nextField = entryClass.getDeclaredField("next");
nextField.setAccessible(true);
for (int i = 0; i < table.length; i++) {
Object e = table[i];
int count = 0;
if (e != null) {
do {
count++;
} while ((e = nextField.get(e)) != null);
}
counts[i] = count;
}
Now you have an array of the entry counts for each bucket.
Client.java
public class Client{
public static void main(String[] args) {
Map<Example, Number> m = new HashMap<>();
Example e1 = new Example(100); //point 1
Example e2 = new Example(200); //point2
Example e3 = new Example(300); //point3
m.put(e1, 10);
m.put(e2, 20);
m.put(e3, 30);
System.out.println(m);//point4
}
}
Example.java
public class Example {
int s;
Example(int s) {
this.s =s;
}
#Override
public int hashCode() {
// TODO Auto-generated method stub
return 5;
}
}
Now at point 1, point 2 and point 3 in Client.java, we are inserting 3 keys of type Example in hashmap m. Since hashcode() is overridden in Example.java, all three keys e1,e2,e3 will return same hashcode and hence same bucket in hashmap.
Now the problem is how to see the distribution of keys.
Approach :
Insert a debug point at point4 in Client.java.
Debug the java application.
Inspect m.
Inside m, you will find table array of type HashMap$Node and size 16.
This is literally the hashtable. Each index contains a linked list of Entry objects that are inserted into hashmap. Each non null index has a hash variable that correspond to the hash value returned by the hash() method of Hashmap. This hash value is then sent to indexFor() method of HashMap to find out the index of table array , where the Entry object will be inserted. (Refer #Rahul's link in comments to question to understand the concept of hash and indexFor).
For the case, taken above, if we inspect table, you will find all but one key null.
We had inserted three keys but we can see only one, i.e. all three keys have been inserted into the same bucket i.e same index of table.
Inspect the table array element(in this case it will be 5), key correspond to e1, while value correspond to 10 (point1)
next variable here points to next node of Linked list i.e. next Entry object which is (e2, 200) in our case.
So in this way you can inspect the hashmap.
Also i would recommend you to go through internal implementation of hashmap to understand HashMap by heart.
Hope it helped..

Java - How to use a for each loop to check the different occurrences of a value in a list of objects

Sorry if the title isn't clear, I wasn't sure how to word it. I have an arraylist of objects and within each of these objects I store an integer value referring to a category and one referring to an ID.
I want to find the number of unique combinations of category and IDs that there are.
So at the moment I have
for(Object object: listofObjects){
//For each unique type of object.getID
//For each unique type of object.getCategory
//Add 1 to counter
}
I can't figure out how to do this. Doing things like for(int cat: object.getCategory()) brings up an error.
I can add the values to a new list within the initial for each loop like so,
ArrayList<Integer> aList= new ArrayList<Integer>();
for (Object object : spriteExplore) {
aList.add(object.getCategory());
}
for (int cat : aList) {
testCounter++;
}
but this obviosuly does not take into account uniqueness and also makes it awkward for factoring in the other variable of ID.
I feel like there is probably some easier work around that I am missing. Any advice?
Thanks in advance.
So you list of UserDefine object in ArrayList and you want to find unique Object.Just create set from list.
For e.g Suppose you have
List<Customer> list=new ArrayList<Custeomer>();
list.add(new Customer("A",12));
list.add(new Customer("B",13));
list.add(new Customer("A",12));
now
create set From this list
Set<Customer> set = new HashSet<Customer>(list);
this will have unique Customer
IMP : dont forget to override equals and hashcode method for Customer
Your best approach would be storing the data correctly.
It's possible that you still need to store non-unique items, if that's so - continue using an ArrayList, but in addition, use the following:
Override the hashcode & equels function as shown in this link:
What issues should be considered when overriding equals and hashCode in Java?
Then, use a Set (HashSet would probably be enough for you) to store all your objects. This data structure will disregard elements which are not unique to elements already inside the set.
Then, all you need to do is query the size of the set, and that gives you the amount of unique elements in the list.
I don't know any library that does this automatically, but you can do it manually using sets. Sets will retain only unique object so if you try to add the same value twice it will only keep one reference.
Set<Integer> categories = new HashSet<Integer>();
Set<Integer> ids= new HashSet<Integer>();
for (Object object : listofObjects) {
categories.add(object.getCategory());
ids.add(object.getID());
}
Then you get the number of unique categories / ids by doing
categories.size()
ids.size()
And all your unique values are stored in the sets if you want to use them.
I would look into using a (Hash)Map<Integer, Integer>. Then just have 1 foreach loop, checking to see if the value of Map<object.getId(), object.getCategory()> is null by checking if map.get(object.getId()) is null - if it is, then this pair does not exist yet, so add this pair into the map by using map.put(object.getId(), object.getCategory()). If not, do nothing. Then at the end, to find the number of unique pairs you can just use map.size()
Hope this helps
Map<Integer,List<Integer>> uniqueCombinations = new HashMap<Integer,List<Integer>>();
for (Object object : listofObjects) {
if(uniqueCombinations.get(object.getCategoryId())==null) {
uniqueCombinations.put(object.getCategoryId(), new LinkedList<Integer>);
}
uniqueCombinations.get(object.getCategoryId()).add(object.getId());
}
return uniqueCombinations.size()
I believe you want unique combinations of both category and id, right?
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
public class SO {
class MyObject{
private int id;
private int category;
private String name;
private MyObject(int id, int category,String name) {
super();
this.id = id;
this.category = category;
this.name = name;
}
protected int getId() {
return id;
}
protected int getCategory() {
return category;
}
#Override
public String toString() {
return "MyObject [id=" + id + ", category=" + category + ", name=" + name + "]";
}
}
public static void main(String[] args) {
SO so = new SO();
List<Object> listofObjects = new ArrayList<Object>();
listofObjects.add(so.new MyObject(1,1,"One"));
listofObjects.add(so.new MyObject(1,1,"Two"));
listofObjects.add(so.new MyObject(1,2,"Three"));
Map<String,List<MyObject>> combinations = new HashMap<String,List<MyObject>>();
for(Object object: listofObjects ){
//For each unique type of object.getID
//For each unique type of object.getCategory
//Add 1 to counter
if (object instanceof MyObject){
MyObject obj = (MyObject)object;
String unique = obj.id+"-"+obj.category;
if (combinations.get(unique) == null){
combinations.put(unique, new ArrayList<MyObject>());
}
combinations.get(unique).add(obj);
}
}
System.out.println(combinations);
//counts
for(Entry<String,List<MyObject>> entry:combinations.entrySet()){
System.out.println(entry.getKey()+"="+entry.getValue().size());
}
}
}
Use the Hashmap to save occurence. Dont forget to implement hashcode und equals Methods. You can generate them if you work with Eclipse IDE.
public static void main(String[] args) {
List<MyObject> myObjects = Arrays.asList(new MyObject(1, 2), new MyObject(2, 3), new MyObject(3, 4), new MyObject(3, 4));
Map<MyObject, Integer> map = new HashMap<>();
for (MyObject myObject : myObjects) {
Integer counter = map.get(myObject);
if(counter == null){
counter = 1;
} else {
counter = counter + 1;
}
map.put(myObject, counter);
}
long uniqueness = 0;
for(Integer i : map.values()){
if(i == 1){
++uniqueness;
}
}
System.out.println(uniqueness);
}
The last part can be replaced by this one line expression if you are working with Java 8:
long uniqueness = map.values().stream().filter(i -> i == 1).count();

Iterate through two TreeMaps to compare in Java

At first I had something like this:
public static boolean equals(TreeMap<?, Boolean> a, TreeMap<?, Boolean> b) {
boolean isEqual = false;
int count = 0;
if (a.size() == b.size()) {
for (boolean value1 : a.values()) {
for (boolean value2 : b.values()) {
if (value2 == value1) {
count++;
isEqual = true;
continue;
} else {
isEqual = false;
return isEqual;
}
}
}
if (count == a.size()) {
return true;
}
}
}
Then found that nope it didn't work. I'm checking to see if every element in Object a is the same as in Object b without using Iterate or Collection. and in the same place... any suggestions? Would implementing a for-each loop over the keySet() work?
So, something along these lines? Needing to take in account BOTH keys and values: (Not an answer - test code for suggestions)
This should work as values() are backed up by the TreeMap, so are sorted according to the key values.
List<Boolean> aList = new ArrayList<>(a.values());
List<Boolean> bList = new ArrayList<>(b.values());
boolean equal = aList.equals(bList);
This should be a bit faster than a HashSet version.
And this won't work as #AdrianPronk noticed:
a.values().equals(b.values())
How about this :
Set values1 = new HashSet(map1.values());
Set values2 = new HashSet(map2.values());
boolean equal = values1.equals(value2);
For Comparing two Map Objects in java, 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.
The correct way to compare maps is to:
Check that the maps are the same size(!)
Get the set of keys from one map
For each key from that set you retrieved, check that the value retrieved from each map for that key is the same

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 :)

How to get index of an item in java.util.Set

I know the differences between Set and List(unique vs. duplications allowed, not ordered/ordered, etc). What I'm looking for is a set that keeps the elements ordered(that's easy), but I also need to be able to recover the index in which an element was inserted. So if I insert four elements, then I want to be able to know the order in which one of them was inserted.
MySet<String> set = MySet<String>();
set.add("one");
set.add("two");
set.add("three");
set.add("four");
int index = set.getIndex("two");
So at any given moment I can check if a String was already added, and get the index of the string in the set. Is there anything like this, or I need to implement it myself?
After creating Set just convert it to List and get by index from List:
Set<String> stringsSet = new HashSet<>();
stringsSet.add("string1");
stringsSet.add("string2");
List<String> stringsList = new ArrayList<>(stringsSet);
stringsList.get(0); // "string1";
stringsList.get(1); // "string2";
A small static custom method in a Util class would help:
public static <T> int getIndex(Set<T> set, T value) {
int result = 0;
for (T entry:set) {
if (entry.equals(value)) return result;
result++;
}
return -1;
}
If you need/want one class that is a Set and offers a getIndex() method, I strongly suggest to implement a new Set and use the decorator pattern:
public class IndexAwareSet<T> implements Set {
private Set<T> set;
public IndexAwareSet(Set<T> set) {
this.set = set;
}
// ... implement all methods from Set and delegate to the internal Set
public int getIndex(T entry) {
int result = 0;
for (T entry:set) {
if (entry.equals(value)) return result;
result++;
}
return -1;
}
}
you can extend LinkedHashSet adding your desired getIndex() method. It's 15 minutes to implement and test it. Just go through the set using iterator and counter, check the object for equality. If found, return the counter.
One solution (though not very pretty) is to use Apache common List/Set mutation
import org.apache.commons.collections.list.SetUniqueList;
final List<Long> vertexes=SetUniqueList.setUniqueList(new LinkedList<>());
it is a list without duplicates
https://commons.apache.org/proper/commons-collections/javadocs/api-3.2.2/index.html?org/apache/commons/collections/list/SetUniqueList.html
How about add the strings to a hashtable where the value is an index:
Hashtable<String, Integer> itemIndex = new Hashtable<>();
itemIndex.put("First String",1);
itemIndex.put("Second String",2);
itemIndex.put("Third String",3);
int indexOfThirdString = itemIndex.get("Third String");
you can send your set data to a new list
Java ArrayList<String> myList = new ArrayList<>(); myList.addAll(uniqueNameSet); myList.indexOf("xxx");

Categories

Resources