How can i iterate over a Hashmap<String, ArrayList<String>> which looks like those?
Name:Sam,Peter,Andrea,Sandra
Age:20,17,24,40
City:London,London,Munich,London
The result should look like this four arrays.
Sam,20,London
Peter,17,London
Andrea,24,Munich
Sandra,40,London
I've tried with two for(...) loops again and again, but it doesn't work.
Best regards :D
If this is something that you've written, then I'd like to suggest an alternate data structure. Instead of Hashmap<String, ArrayList<String>>, use ArrayList<HashMap<String, String>>
so that you have
{Name:Sam, Age:20, City:London},
{Name:Peter, Age:17, City:London},
{Name:Andrea, Age:24, City:Munich},
{Name:Sandra, Age:40, City:London}
If the HashMap of Lists is not something that you've written, and you still need help to figure out how to iterate that, please show us what you have tried.
Though I absolutely agree with #GreyBeardedGeek in his answer, and you should probably change your data structure. I did however have a look at the code needed to change Map<String, List<String>> into List<Map<String, String>>
public static <S, T> List<Map<S, T>> unravel(Map<S, List<T>> org) {
List<Map<S, T>> out = new LinkedList<>();
for (Entry<S, List<T>> entry : org.entrySet()) {
if (entry.getValue() == null || entry.getValue().isEmpty()) {
continue;
}
while (out.size() < entry.getValue().size()) {
out.add(new HashMap<S, T>());
}
for (int i = 0, size = entry.getValue().size(); i < size; i++) {
T value = entry.getValue().get(i);
if (value == null) {
continue;
}
out.get(i).put(entry.getKey(), value);
}
}
return out;
}
Related
I used LinkedHashMap<String, Double> . I want to take separate values from it. If it is Array we can use .get[2] ,.get[5] etc. for take 2nd and 5th value. But for LinkedHashMap<String, Double> how to do it. I used following code. But it print all the values contained in LinkedHashMap<String, Double>. I need to take separately.
Set set = mylist.entrySet();
Iterator i = set.iterator();
while(i.hasNext()) {
Map.Entry me1 = (Map.Entry)i.next();
System.out.print(me1.getKey());
System.out.println(me1.getValue());
You may use the LinkedHashMap#get(Object key) method
Which will return the value corresponding to the key parameter. Since your keys are String, you can not use an int to retrieve them.
Example
If your LinkedHashMap contains ["key", 2.5], calling
System.out.println(lnkHashMap.get("key"));
will print
2.5
Addition
If you're using java-8, there is a workaround using a Stream object.
Double result = hashmap.values()
.stream()
.skip(2)
.findFirst()
.get();
This will skip the two first values and get to the third one directly and return it.
If not, here is a solution
public <T> T getValueByIndex (Map<? extends Object, T> map, int index){
Iterator<T> it = map.values().iterator();
T temp = null;
for (int i = 0 ; i < index ; i++){
if (it.hasNext()){
temp = it.next();
} else {
throw new IndexOutOfBoundsException();
}
}
return temp;
}
It could be the case that you are using the wrong data structure for your purpose.
If you look closely to the LinkedHashMap API you will notice that it is indeed a Map and the only way to access a previously stored value is by providing its key.
But if you really think you need to access the ith value of the LinkedHashMap according to its insertion-order (or access-order) you can use a simple utility method like the following:
Java 8 Solution
private static <K, V> Optional<V> getByInsertionOrder(LinkedHashMap<K, V> linkedHashMap, int index) {
return linkedHashMap.values().stream()
.skip(index)
.findFirst();
}
Java 7 Soution
private static <K, V> V getByInsertionOrder(LinkedHashMap<K, V> linkedHashMap, int index) {
if (index < 0 || index >= linkedHashMap.size()) {
throw new IndexOutOfBoundsException();
}
Iterator<Entry<K, V>> iterator = linkedHashMap.entrySet().iterator();
for (int i = 0; i < index; i++) {
iterator.next();
}
return iterator.next().getValue();
}
This is what I have tried and somehow I get the feeling that this is not right or this is not the best performing application, so is there a better way to do the searching and fetching the duplicate values from a Map or as a matter of fact any collection. And a better way to traverse through a collection.
public class SearchDuplicates{
public static void main(String[] args) {
Map<Integer, String> directory=new HashMap<Integer, String>();
Map<Integer, String> repeatedEntries=new HashMap<Integer, String>();
// adding data
directory.put(1,"john");
directory.put(2,"michael");
directory.put(3,"mike");
directory.put(4,"anna");
directory.put(5,"julie");
directory.put(6,"simon");
directory.put(7,"tim");
directory.put(8,"ashley");
directory.put(9,"john");
directory.put(10,"michael");
directory.put(11,"mike");
directory.put(12,"anna");
directory.put(13,"julie");
directory.put(14,"simon");
directory.put(15,"tim");
directory.put(16,"ashley");
for(int i=1;i<=directory.size();i++) {
String result=directory.get(i);
for(int j=1;j<=directory.size();j++) {
if(j!=i && result==directory.get(j) &&j<i) {
repeatedEntries.put(j, result);
}
}
System.out.println(result);
}
for(Entry<Integer, String> entry : repeatedEntries.entrySet()) {
System.out.println("repeated "+entry.getValue());
}
}
}
Any help would be appreciated. Thanks in advance
You can use a Set to determine whether entries are duplicate. Also, repeatedEntries might as well be a Set, since the keys are meaningless:
Map<Integer, String> directory=new HashMap<Integer, String>();
Set<String> repeatedEntries=new HashSet<String>();
Set<String> seen = new HashSet<String>();
// ... initialize directory, then:
for(int j=1;j<=directory.size();j++){
String val = directory.get(j);
if (!seen.add(val)) {
// if add failed, then val was already seen
repeatedEntries.add(val);
}
}
At the cost of extra memory, this does the job in linear time (instead of quadratic time of your current algorithm).
EDIT: Here's a version of the loop that doesn't rely on the keys being consecutive integers starting at 1:
for (String val : directory.values()) {
if (!seen.add(val)) {
// if add failed, then val was already seen
repeatedEntries.add(val);
}
}
That will detect duplicate values for any Map, regardless of the keys.
You can use this to found word count
Map<String, Integer> repeatedEntries = new HashMap<String, Integer>();
for (String w : directory.values()) {
Integer n = repeatedEntries.get(w);
n = (n == null) ? 1 : ++n;
repeatedEntries.put(w, n);
}
and this to print the stats
for (Entry<String, Integer> e : repeatedEntries.entrySet()) {
System.out.println(e);
}
List, Vector have a method contains(Object o) which return Boolean value based either this object is exist in collection or not.
You can use Collection.frequency to find all possible duplicates in any collection using
Collections.frequency(list, "a")
Here is a proper example
Most generic method to find
Set<String> uniqueSet = new HashSet<String>(list);
for (String temp : uniqueSet) {
System.out.println(temp + ": " + Collections.frequency(list, temp));
}
References from above link itself
I know this is possible:
Map<Integer, Object> map = new HashMap<Integer, Object>();
...
List<Object> arrayList = new ArrayList<Object>(map.values());
But according to android SparseArray<Object> is more efficient, hence, I am wondering if it is possible to convert a SparseArray to Arraylist.
Much appreciate any input.
This will get just the values, ignoring gaps between indices (as your existing Map solution does):
public static <C> List<C> asList(SparseArray<C> sparseArray) {
if (sparseArray == null) return null;
List<C> arrayList = new ArrayList<C>(sparseArray.size());
for (int i = 0; i < sparseArray.size(); i++)
arrayList.add(sparseArray.valueAt(i));
return arrayList;
}
ArrayMap looks like a better choice, which is available since API 19.
Kotlin version:
fun <T> SparseArray<T>.values(): List<T> {
val list = ArrayList<T>()
forEach { _, value ->
list.add(value)
}
return list.toList()
I have a list containing 305899 Strings (which is the username for a website). After I remove all the duplicates, the number goes down to 172123 Strings.
I want to find how many times a particular String (the username) is repeated in that ArrayList. I wrote a simple bubble sort type logic but it was too slow.
private static Map<String, Integer> findNumberOfPosts(List<String> userNameList) {
Map<String, Integer> numberOfPosts = new HashMap<String, Integer>();
int duplicate = 0;
int size = userNameList.size();
for (int i = 0; i < size - 1; i++) {
duplicate = 0;
for (int j = i + 1; j < size; j++) {
if (userNameList.get(i).equals(userNameList.get(j))) {
duplicate++;
userNameList.remove(j);
j--;
size--;
}
}
numberOfPosts.put(userNameList.get(i), duplicate);
}
return numberOfPosts;
}
Then I changed it to this:
private static Map<String, Integer> findNumberOfPosts(List<String> userNameList) {
Map<String, Integer> numberOfPosts = new HashMap<String, Integer>();
Set<String> unique = new HashSet<String>(userNameList);
for (String key : unique) {
numberOfPosts.put(key, Collections.frequency(userNameList, key));
}
return numberOfPosts;
}
This was really slow as well. When I mean slow, it would take like 30+ minutes to through the list.
Is there any other efficient way to handle this problem? Just reduce the time it takes to find and count duplicate elements?
Your findNumberOfPosts method is on the right track, but your implementation is doing loads of unnecessary work.
Try this:
private static Map<String, Integer> findNumberOfPosts(List<String> userNameList) {
Map<String, Integer> numberOfPosts = new HashMap<String, Integer>();
for (String userName : userNameList) {
Integer count = numberOfPosts.get(userName);
numberOfPosts.put(userName, count == null ? 1 : ++count);
}
return numberOfPosts;
}
This should execute in a couple of seconds on most machines.
See if this variation of your second method works faster:
private static Map<String, Integer> findNumberOfPosts(
List<String> userNameList) {
Map<String, Integer> numberOfPosts = new HashMap<String, Integer>();
for (String name : userNameList) {
Integer count = numberOfPosts.get(name);
numberOfPosts.put(name, count == null ? 1 : (1 + count));
}
return numberOfPosts;
}
It has some boxing/unboxing overhead, but should operate a lot faster than what you were doing, which required iterating over the entire list of names for each unique name.
You could attempt to build a Trie structure out of the usernames. Then it would be trivial to find the number of distinct elements(username). The code for Trie is little bit complicated, so you better look up resources to see how the implementation can be done.
On other thought, considering the practical scenario, you should not have this duplicate list in the first place. I mean, if the system providing the username was properly designed, then duplicates wouldn't exist in the first place.
This goes even faster than Bohemian's:
private static Map<String, Integer> findNumberOfPosts(List<String> userNameList) {
Map<String, Integer> numberOfPosts = new HashMap<String, Integer>();
for (String userName : userNameList) {
if (!numberOfPosts.containsKey(userName)) {
numberOfPosts.put(userName, Collections.frequency(userNameList, userName));
}
}
return numberOfPosts;
}
The best solution is to add all the elements to an Array and then sort that array.
Then you can just iterate over the array and the duplicates will be placed next to each other in the array.
You should try improving the first implementation: for each entry you're iterating through the entire list. How about something like:
Map<String, Integer> map;
for (String username : usernames) {
if (!map.containsKey(username)) {
map.put(username, new Integer(0));
} else {
map.put(username, new Integer(map.get(username).intValue() + 1));
}
}
return map;
Use the data structure that was designed to support this natively. Store the user names in a Multiset and let it automatically maintain the frequency/count for you.
Read this tutorial to understand how multiset works/
The following is the best and convenient method to remove duplicates and count the number of duplicate elements in a List. No need to have extra logic.
List<String> userNameList = new ArrayList<String>();
// add elements to userNameList, including duplicates
userNameList.add("a");
userNameList.add("a");
userNameList.add("a");
userNameList.add("a");
userNameList.add("b");
userNameList.add("b");
userNameList.add("b");
userNameList.add("b");
userNameList.add("c");
userNameList.add("c");
userNameList.add("c");
userNameList.add("c");
int originalSize=userNameList.size();
HashSet hs = new HashSet(); //Set would handle the duplicates automatically.
hs.addAll(userNameList);
userNameList.clear();
userNameList.addAll(hs);
Collections.sort(userNameList); //Sort the List, if needed.
//Displays elements after removing duplicate entries.
for(Object element:userNameList)
{
System.out.println(element);
}
int duplicate=originalSize-userNameList.size();
System.out.println("Duplicate entries in the List:->"+duplicate); //Number of duplicate entries.
/*Map<String, Integer> numberOfPosts = new HashMap<String, Integer>(); //Store duplicate entries in your Map using some key.
numberOfPosts.put(userNameList.get(i), duplicate);
return(numberOfPosts);*/
hello
I need to implement a method that receives a HashMap and sorts (mergeSort) it's values by key (without using TreeMap, SortedMap or Collections.Sort or use any sort solutions from JAVA Packages).
my problem is dealing with the wildcard Types...
this is my implementation (that returns compilation errors because of wildcards use)
public HashMap<?, ?> mergeSort(HashMap<?, ?> map) {
if (map.size() < 1) {
return map;
}
// rounds downwards
int middle = map.size() / 2;
int location = 0;
HashMap<?,?> mapLeft = new HashMap<?, ?>();
HashMap<?,?> mapRight = new HashMap<?, ?>();
// splitting map
for (Iterator<?> keyIter = map.keySet().iterator(); keyIter.hasNext();) {
if (location < middle) {
mapLeft.put(keyIter, map.get(keyIter));
} else {
mapRight.put(keyIter, map.get(keyIter));
}
location++;
}
// recursive call
mapLeft = mergeSort(mapLeft);
mapRight = mergeSort(mapRight);
return merge(mapLeft, mapRight);
}
public HashMap<?, ?> merge(HashMap<?, ?> mapLeft, HashMap<?, ?> mapRight) {
HashMap<?, ?> result = new HashMap<?, ?>();
Iterator<?> keyLeftIter = mapLeft.keySet().iterator();
Iterator<?> keyRightIter = mapRight.keySet().iterator();
String keyLeft;
String keyRight;
while (keyLeftIter.hasNext()) {
keyLeft = keyLeftIter.next();
while (keyRightIter.hasNext()) {
keyRight = keyRightIter.next();
if (keyLeft.compareTo(keyRight) < 0) {
result.put(keyLeft, mapLeft.get(keyLeft));
keyLeft = keyLeftIter.next();
} else {
result.put(keyRight, mapRight.get(keyRight));
keyRight = keyRightIter.next();
}
}
}
return result;
}
I appreciate your help!
If all you have to do is meet a method contract, you can do this.
public HashMap<?, ?> mergeSort(HashMap<?, ?> map) {
return new LinkedHashMap(new TreeMap(map));
}
This will sort the keys and return a subclass of HashMap. The design of this method is broken, but sometimes you can't change things.
If you are sorting a map, you should be using a SortedMap like TreeMap. hashmap doesn't retain an order so using it for a merge sort is not possible. Using a merge sort for a TreeMap is redundant.
You cannot assume that ? is a Comparable. You can write something like.
public static <K extends Comparable<K>, V> SortedMap<K,V> sort(Map<K,V> map) {
return new TreeMap<K, V>(map);
}
As you can see this is shorter and simpler than your approach. Is this homework? Do you reall need to use a merge sort?
The problem you have is that you cannot return a HashMap as it doesn't keep the order, adn you cannot return a TreeMap because it will sort the keys for you making anything else you redundant. For this task you can only return a LinkedHashMap as it does retain order, without doing the sorting for you.
here is an example using LinkedHashMap. Note it doesn't create copies of Maps as it goes, it creates a single array and merge sorts portions of it until its completely sorted.
Note: I use TreeMap as a SortedMap to show its sorted correctly. ;)
public static void main(String... args) throws IOException {
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
for(int i=0;i<100;i++)
map.put((int)(Math.random()*1000), i);
System.out.println("Unsorted "+map);
System.out.println("Sorted "+sort(map));
final String sortedToString = sort(map).toString();
final String treeMapToString = new TreeMap<Integer, Integer>(map).toString();
if (!sortedToString.equals(treeMapToString))
System.out.println(sortedToString+" != \n"+treeMapToString);
}
public static <K extends Comparable<K>, V> Map<K, V> sort(Map<K, V> map) {
return mergeSort(map);
}
// a very bad design idea, but needed for compatibility.
public static <K extends Comparable<K>, V> HashMap<K, V> mergeSort(Map<K, V> map) {
Map.Entry<K, V>[] entries = map.entrySet().toArray(new Map.Entry[map.size()]);
mergeSort0(entries, 0, entries.length);
HashMap<K, V> ret = new LinkedHashMap<K, V>();
for (Map.Entry<K, V> entry : entries)
ret.put(entry.getKey(), entry.getValue());
return ret;
}
private static <K extends Comparable<K>, V> void mergeSort0(Map.Entry<K, V>[] entries, int start, int end) {
int len = end - start;
if (len < 2) return;
int mid = (end + start) >>> 1;
mergeSort0(entries, start, mid);
mergeSort0(entries, mid, end);
// merge [start, mid) and [mid, end) to [start, end)
for(int p = start, l=start, r=mid; p < end && l < r && r < end; p++) {
int cmp = entries[l].getKey().compareTo(entries[r].getKey());
if (cmp <= 0) {
l++;
// the entry is in the right place already
} else if (p != r) {
// we need to insert the entry from the right
Map.Entry<K,V> e= entries[r];
// shift up.
System.arraycopy(entries, p, entries, p+1, r - p);
l++;
// move down.
entries[p] = e;
r++;
}
}
}
prints
Unsorted {687=13, 551=0, 2=15, 984=3, 608=6, 714=16, 744=1, 272=5, 854=9, 96=2, 918=18, 829=8, 109=14, 346=7, 522=4, 626=19, 495=12, 695=17, 247=11, 725=10}
Sorted {2=15, 96=2, 109=14, 247=11, 272=5, 346=7, 495=12, 522=4, 551=0, 608=6, 626=19, 687=13, 695=17, 714=16, 725=10, 744=1, 829=8, 854=9, 918=18, 984=3}
Like other commenters I would suggest reading up on the subject of generics in Java. What you did in merge is using wildcards on the result HashMap
HashMap<?, ?> result = new HashMap<?, ?>();
When you put wildcards on it, you are basically saying "I will only be reading from this". Later on you trying to push something in
result.put(keyLeft, mapLeft.get(keyLeft));
The compiler will say "Hey, you just told me you would only read and now you want to put something in... FAIL
Then it generates your compile time errors.
Solution
Don't put wildcards on collections you will modify.
Why are you always using ?. Give the kids a name like Key or Value.
Edit: You should work through this tutorial fist: Lesson: Generics
Here is a method that sorts a Map by its keys. It makes use of the Collections.sort(List, Comparator) method.
static Map sortByKey(Map map) {
List list = new LinkedList(map.entrySet());
Collections.sort(list, new Comparator() {
public int compare(Object o1, Object o2) {
return ((Comparable) ((Map.Entry) (o1)).getKey())
.compareTo(((Map.Entry) (o2)).getKey());
}
});
Map result = new LinkedHashMap();
for (Iterator it = list.iterator(); it.hasNext();) {
Map.Entry entry = (Map.Entry)it.next();
result.put(entry.getKey(), entry.getValue());
}
return result;
}