Java: LinkedHashMaps overlaps over themselves - java

I want to create a copy of linked hash map and then I want to remove all values (from the List) instead of the first entry. Here is what I got:
LinkedHashMap<String, List<Value>> facetsInCategoriesCopy = new LinkedHashMap<>(facetsInCategories);
if (!facets.equals("something")) {
for (List<Value> value : facetsInCategoriesCopy.values()) {
if (value.size() > 1) {
int nbrOfElements = value.size();
for (int i = nbrOfElements-1; i > 0; i--) {
value.remove(i);
}
}
}
}
After this operation it turns out that facetsInCategories are modified too. Why? And how to solve the issue?
Any help would be appreciated.

I don't have a 50 reputation to add a comment. See this answer Assigning Hashmap to Hashmap
Essentially, the copy constructor you used to make the new map has references to the mutable objects i.e. facetsInCategories and will update that as well when you update the facetsInCategoriesCopy map.
The solution would be to instead do a deep copy instead. I have added test code below, I used String instead of Value
//Test for https://stackoverflow.com/questions/27324315/
public static void testStackO_Q_27324315() {
Map<String, List<String>> facetsInCategories = new LinkedHashMap<String, List<String>>();
String[] values = new String[]{"Test1", "Test2", "Test3"};
List<String> valuesList = new ArrayList<String>(Arrays.asList(values));
facetsInCategories.put("Test", valuesList);
Map temp = Collections.unmodifiableMap(facetsInCategories);
LinkedHashMap<String, List<String>> facetsInCategoriesCopy = (LinkedHashMap<String, List<String>>)deepCopy(temp);
String facets = "test_me";
if (!facets.equals("something")) {
for (List<String> value : facetsInCategoriesCopy.values()) {
if (value.size() > 1) {
int nbrOfElements = value.size();
for (int i = nbrOfElements-1; i > 0; i--) {
value.remove(i);
}
}
}
}
System.out.println(facetsInCategories);
System.out.println(facetsInCategoriesCopy);
}
public static <K1, K2, V> Map<K1, List<V>> deepCopy(
Map<K1, List<V>> original){
Map<K1, List<V>> copy = new LinkedHashMap<K1, List<V>>();
for(Map.Entry<K1, List<V>> entry : original.entrySet()){
copy.put(entry.getKey(), new ArrayList<V>(entry.getValue()));
}
return copy;
}

Related

How to transform this piece of code using Java Stream

In order to defend against XSS attacks, I wrote a class that extends from HttpServletRequestWrapper and overrides the getParameterValues method, the code is shown below:
#Override
public Map<String, String[]> getParameterMap() {
Map<String, String[]> parameters = super.getParameterMap();
LinkedHashMap<String, String[]> map = new LinkedHashMap<>();
if (parameters != null) {
for (String key : parameters.keySet()) {
String[] values = parameters.get(key);
for (int i = 0; i < values.length; i++) {
String value = values[i];
if (!StrUtil.hasEmpty(value)) {
value = HtmlUtil.filter(value);
}
values[i] = value;
}
map.put(key, values);
}
}
return map;
}
I'm wondering if this piece of code could be transformed using Java Stream because I see a lot of if condition judgement and for loop.
Methods:StrUtil.hasEmpty and HtmlUtil.filter are come from here: Hutool
Any suggestions to improve the performance of this code are welcome.
Yes you can make elegent solution with streams plus map / filter but it will be re-allocating the memory footprint of the request data creating many new for objects for Map, N * Map.Entry, N * String[], and filtered strings and other intermediate steps.
Alternatively consider simplifying the logic of your existing loop just to fix each String[] value in place and return the existing map:
public Map<String, String[]> getParameterMap() {
Map<String, String[]> parameters = super.getParameterMap();
if (parameters != null) {
for (String[] values : parameters.values()) {
for (int i = 0; i < values.length; i++) {
String value = values[i];
if (!StrUtil.hasEmpty(value)) {
values[i] = HtmlUtil.filter(value);
}
}
}
}
return parameters;
}
#Override
public Map<String, String[]> getParameterMap() {
Map<String, String[]> parameters = super.getParameterMap();
LinkedHashMap<String, String[]> map = new LinkedHashMap<>();
if (parameters != null) {
map = parameters.entrySet().stream().collect(Collectors.toMap(
Map.Entry::getKey,
v -> Arrays.stream(v.getValue())
.filter(val -> !StrUtil.hasEmpty(val))
.map(HtmlUtil::filter).collect(Collectors.toList()).toArray(new String[0]),
(x, y) -> y, LinkedHashMap::new
));
}
return map;
}

Want to concatenate Value of one map to key of another map

I want to concatenate to the value of one map to key of another map and add them into list.
Compare value on basis of key of first map to value of another map.
e.g:
map1= {37=core__error_code_based, 153=core__app_dialog, 123=core__date}
map2={copy_2=37,button_back=37,button_cancel=153,button_confirm=153}
My approach is in first loop i get the key of map1 and then in second loop iterate the map2 values on basis map1 key.
So that I get the value of map1 and key of map2 and later concatenate in string.
List<String> finalKey=new ArrayList<>();
Iterator<Map.Entry<String,String>> entrySet=map1.entrySet().iterator();
Iterator<Map.Entry<String,String>> pageKey=map2.entrySet().iterator();
while(entrySet.hasNext()){
Map.Entry<String,String> entry = entrySet.next();
Map.Entry<String,String> pageValue = pageKey.next();
while(entry.getKey()==pageValue.getValue()){
finalKey.add(entry.getValue()+"__"+pageValue.getKey());
}
}
I had tried using iterator and entryset to iterate through the both map but not succeed
{core__error_code_based__copy_2,core__error_code_based__button_back,core__app_dialog__button_confirm,core__app_dialog__button_cancel}
Well i achieved this using
public class translatekeyName {
static List<String> finalString = new ArrayList<>();
public static Map<String, String> initialMap() {
Map<String, String> map1 = new HashMap<>();
map1.put("37", "core__error_code_based");
map1.put("153", "core__app_dialog");
return map1;
}
public static Map<String, String> secondMap() {
Map<String, String> map2 = new HashMap<>();
map2.put("copy_2", "37");
map2.put("button_back", "37");
map2.put("button_cancel", "153");
map2.put("button_confirm", "153");
return map2;
}
public List<String> concatenateString(Map page, Map source) {
Map<String, String> moduleKey = page;
Map<String, String> pageKey = source;
List<String> temp;
Iterator<Map.Entry<String, String>> entrySet = page.entrySet().iterator();
Iterator<Map.Entry<String, String>> pageKeyset = source.entrySet().iterator();
for (String value : moduleKey.keySet()) {
temp = getallKeys(source, value);
String tempValue = moduleKey.get(value);
for (int i = 0; i < temp.size(); i++) {
tempValue += "__" + temp.get(i);
finalString.add(tempValue);
}
}
return finalString;
}
static <K, V> List<K> getallKeys(Map<K, V> mapOfWords, V value) {
List<K> keylist = null;
if (mapOfWords.containsValue(value)) {
keylist = new ArrayList<>();
for (Map.Entry<K, V> entry : mapOfWords.entrySet()) {
if (entry.getValue().equals(value)) {
keylist.add(entry.getKey());
}
}
}
return keylist;
}
public static void main(String[] args) {
translatekeyName obj = new translatekeyName();
obj.concatenateString(initialMap(), secondMap());
System.out.println(finalString);
}
}

Get k highest values from HashMap

In the code I have posted below, I need to remove the duplicates from the HashMap (the highest alphabetical value gets to stay in the map) and print the keys of the k highest values after the duplicates are removed. How do I do this? I tried with a HashSet but I am pretty clueless.
public ArrayList<String> mostOften(int k)
{
ArrayList<String> lista = new ArrayList<String>();
HashMap<String,Integer> temp = new HashMap<String, Integer>();
for(String it : wordList)
{
if(temp.containsKey(it))
temp.put(it, temp.get(it)+1);
else
temp.put(it, 1);
}
temp = sortByValues(temp);
Set<Integer> set = new HashSet<Integer>(temp.values());
System.out.println(set);
return lista;
}
private static HashMap sortByValues(HashMap map)
{
List list = new LinkedList(map.entrySet());
Collections.sort(list, new Comparator()
{
public int compare(Object o1, Object o2)
{
return ((Comparable)((Map.Entry) (o1)).getValue()).compareTo(((Map.Entry) (o2)).getValue());
}
});
HashMap sortedHashMap = new LinkedHashMap();
for (Iterator it = list.iterator(); it.hasNext();)
{
Map.Entry entry = (Map.Entry) it.next();
sortedHashMap.put(entry.getKey(), entry.getValue());
}
return sortedHashMap;
}
If you are trying to do a frequency count of words you are heading down the wrong road. Java 8 does this much easier and cleaner.
You need these imports
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
The code
public static void main(String[] args) {
printTopWords(Arrays.asList("Hello World Hello , Bye World".split(" ")), 2);
}
public static void printTopWords(List<String> words, int limit) {
// using the Stream API
words.stream()
// create a map of words with the count of those words
.collect(Collectors.groupingBy(w -> w, Collectors.counting()))
// take that map as a stream of entries
.entrySet().stream()
// sort them by count in reverse order
.sorted(Comparator.comparing(Map.Entry<String, Long>::getValue).reversed())
// limit the number to get top Strings
.limit(limit)
// keep just the key ie. drop the count.
.map(Map.Entry::getKey)
// print them
.forEach(System.out::println);
}
prints
Hello
World
If you are not familiar with java 8 streams and lambdas then below answer would be helpful to you :
public class Java7Way {
public static void main(String[] args) {
Map<String, Integer> myMap = new HashMap<>();
myMap.put("A", 20);
myMap.put("A", 38);
myMap.put("B", 40);
myMap.put("K", 23);
System.out.println(sortByValue(myMap,2).toString());
}
public static <K, V extends Comparable<? super V>> Map<K, V>
sortByValue(Map<K, V> map,int limit) {
List<Map.Entry<K, V>> list
= new LinkedList<>(map.entrySet());
Collections.sort(list, new Comparator<Map.Entry<K, V>>() {
#Override
public int compare(Map.Entry<K, V> o1, Map.Entry<K, V> o2) {
return (o1.getValue()).compareTo(o2.getValue());
}
}
.reversed()//to arrange it in decending order
);
Map<K, V> result = new LinkedHashMap<>();//maintains the order which the entries were put into the map
for (Map.Entry<K, V> entry : list) {
if (limit == 0) {
break;
}
result.put(entry.getKey(), entry.getValue());
limit--;
}
return result;
}
}
Out-put :
{B=40, A=38}
I recommend using TreeBidiMap from Apache Commons Collection. In this structure all keys and all values sorted according to the natural order for the key's and value's classes.
For your code:
BidiMap<String,Integer> temp = new TreeBidiMap<String, Integer>();
for(String it : wordList)
{
if(temp.containsKey(it))
temp.put(it, temp.get(it)+1);
else
temp.put(it, 1);
}
// print values unsing natural sorting in reverse order
BidiMap inverse = temp.inverseBidiMap();
for (MapIterator it = inverse.mapIterator(); it.hasPrevious();) {
String k = it.next();
Integer s = it.getValue();
System.out.printLn(s + " = " + k);
}

How to get 5 highest values from a hashmap?

I have a Hashmap that links a zipcodes stored as keys and population stored as values in a hashmap.
The hashmap contains around 33k entries.
I'm trying to get the 5 highest population values from 5 zip codes and print out the 5 zip codes ASSOCIATED with the 5 highest population, but I'm having trouble understanding the algorithm of how to do it.
If it was just one, its easy but the 5 restriction is giving me some trouble.
I know to store the 5 values in an int array and I have a counter to determine when 5 of them are stored, but thats it.
Thanks
int populatedCounter = 0;
int[] populatedZip = new int[5];
it = zipCodePop.entrySet().iterator();
while (it.hasNext())
{
Map.Entry pairs = (Map.Entry)it.next();
for (int i = 0; i < populatedZip.length; i++)
{
}
}
}
Putting the entries of such a set into a list and sorting it is one option. But 33k elements is a number where the O(n*log(n)) complexity of sorting might already have a noticable performance impact.
One apporach would be to employ the PriorityQueue that nr4bt already mentioned (I wrote this snippet while he answered). It basically inserts all elements into a PriorityQueue that is sorted according to the values of the map entries.
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.PriorityQueue;
public class GreatestOfMap
{
public static void main(String[] args)
{
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("zip000", 1234);
map.put("zip001", 2345);
map.put("zip002", 3456);
map.put("zip003", 4567);
map.put("zip004", 5678);
map.put("zip005", 6789);
map.put("zip006", 123);
map.put("zip007", 234);
map.put("zip008", 456);
map.put("zip009", 567);
map.put("zip010", 7890);
map.put("zip011", 678);
map.put("zip012", 789);
map.put("zip013", 890);
int n = 5;
List<Entry<String, Integer>> greatest = findGreatest(map, 5);
System.out.println("Top "+n+" entries:");
for (Entry<String, Integer> entry : greatest)
{
System.out.println(entry);
}
}
private static <K, V extends Comparable<? super V>> List<Entry<K, V>>
findGreatest(Map<K, V> map, int n)
{
Comparator<? super Entry<K, V>> comparator =
new Comparator<Entry<K, V>>()
{
#Override
public int compare(Entry<K, V> e0, Entry<K, V> e1)
{
V v0 = e0.getValue();
V v1 = e1.getValue();
return v0.compareTo(v1);
}
};
PriorityQueue<Entry<K, V>> highest =
new PriorityQueue<Entry<K,V>>(n, comparator);
for (Entry<K, V> entry : map.entrySet())
{
highest.offer(entry);
while (highest.size() > n)
{
highest.poll();
}
}
List<Entry<K, V>> result = new ArrayList<Map.Entry<K,V>>();
while (highest.size() > 0)
{
result.add(highest.poll());
}
return result;
}
}
Try this, using standard methods and assuming that the population count is stored as Integers in the HashMap:
List<Integer> list = new ArrayList<Integer>(zipCodePop.values());
Collections.sort(list, Collections.reverseOrder());
List<Integer> top5 = list.subList(0, 5);
public class CheckHighiestValue {
public static void main(String... s) {
HashMap<String, Integer> map = new HashMap<String, Integer>();
map.put("first", 10000);
map.put("second", 20000);
map.put("third", 300);
map.put("fourth", 800012);
map.put("fifth", 5000);
map.put("sixth", 30012);
map.put("seventh", 1234);
map.put("eighth", 45321);
map.put("nineth", 5678);
Set<Entry<String, Integer>> set = map.entrySet();
List<Entry<String, Integer>> list = new ArrayList<Entry<String, Integer>>(
set);
Collections.sort(list, new Comparator<Map.Entry<String, Integer>>() {
#Override
public int compare(Entry<String, Integer> o1,
Entry<String, Integer> o2) {
return o2.getValue().compareTo(o1.getValue());
}
});
System.out.println(list.subList(0, 5));
}
}
PriorityQueue would help too, and also a nice topic about how to get top k from a list, you can check this link
PriorityQueue<Integer> p = new PriorityQueue<Integer>(5);
int[] a = new int[]{3,5,10,1,23,42,66,1333,545,110};
for (int i : a){
p.add(i);
if (p.size() > 5){
p.poll();
}
}
//output will be highest 5, [42, 66, 110, 1333, 545]
You can have O(n log(k)) time complexity // k is your top value count.
This is something i made and hopefully provides you something that you want to use.
public class TopsCollection {
private static Map<String, Integer> collectors = new HashMap<>();
public TopsCollection() {
}
public void add(String playerName, int score) {
collectors.put(playerName, score);
}
public void clearCollectors() {
synchronized (collectors) {
collectors.clear();
}
}
public List<Map.Entry<String, Integer>> getTops() {
return collectors.entrySet().stream().sorted(comparing(Map.Entry::getValue, reverseOrder())).limit(5).collect(toList());
}
public int getTopByName(String name) {
for (int i = 0; i < getTops().size(); i++) {
if (getTops().get(i).getKey().contains(name)) {
return i;
}
}
return 0;
}
getTopByName allows you to get the top place of the specified name.
How would you do this without a computer, with just a piece of paper and a pencil? Pretend you had a stack of index cards that had numbers on them, and it was your job to find the 5 highest numbers. How would you do that? Write down steps that somebody else could follow to achieve the goal, and when you have those steps written out, you'll have an algorithm that you can start thinking about implementing with code.
You say that a single maximum is easy, so do it exactly like you would with a single maximum, but keep track of the five maximums instead. An array of maximums might be helpful here.
Using Streams
int[] populatedZip = map.entrySet().parallelStream()
.sorted(Map.Entry.<String, Integer>comparingByValue())
.limit(5)
.mapToInt(entry -> entry.getValue())
.toArray();

Java HashMap losing value on loop iteration

i'm attempting to reorder an List of Maps in alphabetical order. i can see that the "name" String gets filled out with the appropriate value, but groupDataCopy is never updated. as far as i know, using the new operator and calling "put" will place the value in the Map. but I can see that on the following iteration, the ArrayList contains:
{name = null}
i don't know why i'm losing values in my Map List. here is the code:
private void sortByName() {
List<Map<String, String>> groupDataCopy = new ArrayList<Map<String, String>>();
List<List<Map<String, String>>> childDataCopy = new ArrayList<List<Map<String, String>>>();
int groupPos = 0;
int nextNamePos = 0;
String name = null;
while(groupPos<groupData.size()) {
//main loop
int groupDataComparison = 0;
name = null;
while(groupDataComparison<groupData.size()) {
//comparison traversal for group
if(!groupDataCopy.isEmpty()) { //if groupDataCopy has data
if(groupDataCopy.get(groupDataCopy.size()-1).get("name").compareTo(groupData.get(groupDataComparison).get("name")) > 0) { //if the last index of groupDataCopy is alphabetically after (or equal to) last chosen name
if(name==null || groupData.get(groupDataComparison).get("name").compareTo(name) < 0) {
name = groupData.get(groupDataComparison).get("name");
nextNamePos = groupDataComparison;
}
}
} else {
if(name==null || groupData.get(groupDataComparison).get("name").compareTo(name) < 0) {
name = groupData.get(groupDataComparison).get("name");
nextNamePos = groupDataComparison;
}
}
groupDataComparison++;
}
groupDataCopy.add(new HashMap<String, String>());
groupDataCopy.get(groupPos).put("name", name);
childDataCopy.add(new ArrayList<Map<String, String>>());
for(Map<String, String> data : childData.get(nextNamePos)) {
childDataCopy.get(groupPos).add(data);
}
groupPos++;
}
groupData = groupDataCopy;
childData = childDataCopy;
}
Comparator<Map<String, String> comparator = new Comparator<Map<String, String>()
{
public int compare(Map<String, String> o1, Map<String, String> o2)
{
return o1.get("name").compartTo(o2.get("name");
}
}
Collections.sort(groupData, comparator);
Try creating a Comparator that will let you use Collections.sort:
Something like:
Comparator<Map<String, String> comp = new Comparator<Map<String, String>()
{
public int compare(Map<String, String> o1, Map<String, String> o2)
{
//write code to compare values
}
}
After which you can simply do:
Collections.sort(groupData, comp);

Categories