How to reposition keys by Number Order in Java Map? - java

I'm trying to remove a particular key from Map and reposition all keys index wise. For example: My map is like Map<Integer,String>
0,"A"
1,"B"
2,"C"
3,"D"
if i remove key 1 then output should be
0,"A"
1,"C"
2,"D"
How do i keep keys in index wise (0 to size-1) and assign next value to previous key after remove?
This is required because before insertion in map i need to check whether key exists or not. This scenario required for my recycler view adapter to hold unique positions to avoid reloading of items if exists in map

In your situation, if u want the index to be in that behavior. Map is not the answer. use List - can use ArrayList[insertion is often] or LinkedList[fast when reading].
Sample:
List<String> items = new ArrayList<>();
items.add("A");
items.add("B");
items.add("C");
items.add("D");
So if you remove index 1 or B;
items.remove(1);
The index of C will automatically be 1. Just like you wanted above.

Maps inherently have no order. There is no way to change the keys without taking all the values out of the Map and reinserting them with new keys.
It sounds like you may be better off using a List instead of a Map.
ArrayList<String> strings = new ArrayList(Arrays.asList("A", "B", "C"));
// 0 => "A", 1 => "B", 2 => "C"
strings.remove(1);
// 0 => "A", 1 => "C"

You can't order the keys on deletion by updating key values. In MAP only way to update keys is removing the key and and re-insert it with new value. That is tricky and expensive. You can go with ArrayList for the desired result as suggested.

The TreeMap<K,V> in the jdk java.util package can meet your needs. Whatever key you delete , the sort will never change.
If you want to define yourself sort method, just use its custom comparator to over-write construction method TreeMap(Comparator<? super K> comparator) to achieve key sorting.
public static void main(String[] args) {
Map<Integer, String> map = new TreeMap<Integer, String>();
map.put(0, "A");
map.put(1, "B");
map.put(2, "C");
map.put(3, "D");
// whatever key you delete , the sort will not change...
for (Map.Entry<Integer, String> entry : resultMap.entrySet()) {
System.out.println(entry.getKey() + " " + entry.getValue());
}
}

you choose a very bad way to implement this in adapter
but you can solve it with below code:
final Map<Integer, String> myStupidMap = new HashMap<>();
myStupidMap.put(1, "A");
myStupidMap.put(2, "B");
myStupidMap.put(3, "C");
myStupidMap.put(4, "D");
final int[] count = {0};
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
myStupidMap.forEach(new BiConsumer<Integer, String>() {
#Override
public void accept(Integer integer, String s) {
if (count[0] == 1){
myStupidMap.remove(integer);
}
count[0]++;
}
});
}
i hope useful for you But my suggestion is not to use the map.

Related

Is there a Java Map class that satisfies these needs? [duplicate]

This question already has answers here:
How to have a key with multiple values in a map?
(6 answers)
Closed 2 years ago.
Is there a Java Map class that satisfies these functionalities:
It's a generic class with the form UnknownMap<Key, Object>, I plan to use it in a node based application where each node in stored in this map and corresponds to a float value object like UnknownMap<Float, Node>.
It can store objects that can map to the same float value, so for example:
UnknownMap<Float, String> map = new UnknownMap<Float, String>();
map.put(1, "a");
map.put(2, "b");
map.put(3, "c");
map.put(1, "d");
System.out.println(map);
Would print out: {1="a", 1="d", 2="b", 3="c"} as opposed to: {1="d", 2="b", 3="c"}.
It has a method called subMap that returns a map with all the nodes within a key range, similar to TreeMap.subMap.
It must perform a binary search on the float key values to retrieve the correct object, as opposed to iterating through the whole collection. It needs to be fast since there will likely be thousands if not millions of nodes.
Does such a class exist, or will I have to write one from scratch?
Thanks.
Use Map.computeIfAbsent
Try it like this:
The first time the key is encountered, it creates a list and adds the string to it. The next time, it just adds the string to the list. This is one technique for mapping multiple values to the same key.
Map<Float,List<String>> map = new HashMap<>();
map.computeIfAbsent(10f, k->new ArrayList<>()).add("a");
System.out.println(map);
map.computeIfAbsent(10f, k->new ArrayList<>()).add("b");
System.out.println(map);
map.computeIfAbsent(30f, k->new ArrayList<>()).add("c");
System.out.println(map);
map.computeIfAbsent(30f, k->new ArrayList<>()).add("d");
System.out.println(map);
map.computeIfAbsent(10f, k->new ArrayList<>()).add("e");
System.out.println(map);
Prints the following after each addition to see the map changing.
{10.0=[a]}
{10.0=[a, b]}
{30.0=[c], 10.0=[a, b]}
{30.0=[c, d], 10.0=[a, b]}
{30.0=[c, d], 10.0=[a, b, e]}
You could also concatenate new strings to the existing string using a similar technique with Map.compute(). Because the variable in a lambda must be effectively final, this is most easily done by isolating the map in a method.
Map<Float, String> map2 = new HashMap<>();
add(10f, "a", map2);
add(10f, "b", map2);
add(30f, "c", map2);
add(30f, "d", map2);
add(10f, "e", map2);
map2.entrySet().forEach(System.out::println);
Prints
30.0=c, d
10.0=a, b, e
The method
public static void add(float key, String value, Map<Float, String> map) {
map.compute(key,
(k, v) -> v == null ? value : v + ", " + value);
}
TreeMap will do the job. Make the 'value' part a List (or possibly Set) to deal with the 'one key can map to multiple values' clause:
public void put(float k, String v) {
map.computeIfAbsent(k, k_ -> new ArrayList<String>()).add(v);
}
for example.

How to put together multiple Maps <Character, Set<String>> without overriding Sets

In my project I am using two maps Map<Character, Set<String>>.
map1 - is temporally holding needed values
map2 - is summing all data from map1 after each loop
for example i got:
map2 = (B; Beryllium, Boron, Bromine)
map2 = (H; Hellum, Hydrogen, Hafnium)
now new map1 is:
map1 = (B; Bismuth)
map1 = (O; Oxygen)
In my code adding Oxygen as new entry is ok, but adding new entry for B ends by overraidding existing data in values and leave me only Bismuth.
My code:
while (iterator.hasNext()) {
Set<String> words = new TreeSet<>();
String word = iterator.next();
char[] wordChars = word.toCharArray();
//some code
words.add(word);
map1.put(wordChars[i], words);
}
map2.putAll(map1);
I tought about using .merge but I have no idea how to use it with Sets as values, and I cannot use simple Strings with concat.
You can use Map#merge like this:
Map<String, Set<String>> map1; // [key="B";values=["Beryllium", "Boron", "Bromine"]]
Map<String, Set<String>> map2; // [key="B";values=["Bismuth"] key="I";values=["Iron"]]
for (Entry<String, Set<String>> entry : map2.entrySet()) {
map1.merge(entry.getKey(), entry.getValue(), (s1, s2) -> {s1.addAll(s2); return s1;});
}
//map1 = [key="B";values=["Beryllium", "Boron", "Bromine", "Bismuth"] key="I";values=["Iron"]]
Map::compute is probably what you're looking for. This gives you a way to map any existing value (if there is one), or provide one if not.
For example, in your case something like the following would probably suffice:
oldMap.compute("B", current -> {
if (current == null) {
// No existing entry, so use newMap's one
return newMap.get("B");
} else {
// There was an existing value, so combine the Sets
final Set<String> newValue = new HashSet<>(current);
newValue.addAll(newMap.get("B"));
return newValue;
}
});
There's also MultiValueMap and Multimap from spring and guava respectively (if you're ok bringing in dependencies) which cover this case with less work already.
Temporary map1 will not be needed in this case. Get the set for that character, if null create a new set. Add the word to that set and put in the map:
while (iterator.hasNext()) {
String word = iterator.next();
//some code
Set<String> words = map2.get(word.charAt(0));
if(words == null) {
words = new TreeSet<>();
}
words.add(word);
map2.put(word.charAt(0), words);
}
When using the merge() function, if the specified key is not already associated with a value or the value is null, it associates the key with the given value.
Otherwise, i.e if the key is associated with a value, it replaces the value with the results of the given remapping function. So in order to do not overwrite the old value you must write your remapping function so that it combines the old and new values.
To do so replace this line :
map2.putAll(map1);
with
map1.forEach( (key, value)->{
map2.merge(key, value, (value1,value2) -> Stream.of(value1,value2)
.flatMap(Set::stream)
.collect(Collectors.toSet()));
});
This will iterate over map1 and add echh key which is not present into map2 and associate it with the given value and for each key which is already present it combines the old values and new values.
Alternative you can also work with Map.computeIfPresent and Map.putIfAbsent
map1.forEach( (key, value)->{
map2.computeIfPresent(key, (k,v) -> Stream.of(v,value).flatMap(Set::stream).collect(Collectors.toSet()));
map2.putIfAbsent(key, value);
});

combine key value as a new value for another map with duplicates in java

i am having map1 as <k1,v1> and i have to create map2 with map1 as a value like map2=<k3,map1>.
But keys k1 ans k3 are having duplicate and we have to retain duplictes.
Example:
map1={(1,a),(1,b),(2,c)}
map2={5={1,a},5={1,b},6={2,c}}
How to achieve this using hashmaps or maps(without using guava's multimap concept)
As HashMap doesn't allow to store duplicate keys, you might want to consider changing your code a bit and create HashMap<Key,ArrayList<HashMap>>. Maps with the same key would be stored under the same Key in the ArrayList of your HashMaps. "ArrayList of HashMaps" would be the value of parent HashMap. There is a simple example how I see you could achieve something similar to containing more values to duplicate keys (I used hardcoded key values to make it a bit simpler to read, I also added some explanations in the comments to the code):
import java.util.*;
public class A {
public static void main(String[] args) {
Map<Integer, ArrayList<Character>> map1 = new HashMap<>(); // Map containing few Characters under one key
map1.put(1, new ArrayList<Character>());
map1.get(1).add('a');
map1.get(1).add('b');
map1.put(2, new ArrayList<Character>());
map1.get(2).add('c');
System.out.println("map1: " + map1); // prints: map1: {1=[a, b], 2=[c]}
Map<Integer, ArrayList<HashMap<Integer, Character>>> map2 = new HashMap<>(); // Map storing as keys 5 and 6, values are maps from map1
map2.put(5, new ArrayList<HashMap<Integer, Character>>());
map2.put(6, new ArrayList<HashMap<Integer, Character>>());
for(Map.Entry<Integer, ArrayList<Character>> entry : map1.entrySet()) { // For each Integer-ArrayList pair from map1...
if(entry.getKey().equals(1)) { // Check if key Integer is equal to 1, if it is...
for(Character character : entry.getValue()) { // Create Maps containg pairs of Integer as key and each Character as value...
HashMap<Integer, Character> innerMap = new HashMap<>();
innerMap.put(entry.getKey(), character);
map2.get(5).add((new HashMap<Integer,Character>(innerMap)));
}
}
if(entry.getKey().equals(2)) { // Check if key Integer is equal to 1, if it is...
for(Character character : entry.getValue()) { // Create Maps containg pairs of Integer as key and each Character as value...
HashMap<Integer, Character> innerMap = new HashMap<>();
innerMap.put(entry.getKey(), character);
map2.get(6).add((new HashMap<Integer,Character>(innerMap)));
}
}
}
System.out.println("map2: " + map2); // prints: map2: {5=[{1=a}, {1=b}], 6=[{2=c}]}
}
}
Output you get:
map1: {1=[a, b], 2=[c]}
map2: {5=[{1=a}, {1=b}], 6=[{2=c}]}
You can use result Map and combination of getKey() and getValue() methods to create Maps like 5={1,a} and 5=[{1=b} on-the-go (but be aware that you cannot store them in the same Map).
Maps don't allow more than one mapping for any given key.
What you can do, though, is to have a Map<K1, List<V1>> for map1, which maps keys to lists of values.
To add an entry, instead of using Map.put, use Map.computeIfAbsent:
map1.computeIfAbsent(1, k -> new ArrayList<>()).add("a");
map1.computeIfAbsent(1, k -> new ArrayList<>()).add("b");
map1.computeIfAbsent(2, k -> new ArrayList<>()).add("c");
This has the following structure:
map1: {1=[a, b], 2=[c]}
Now, for map2, it's not clear if you'd need to take the same approach or if you'd be done using Map.put.

How to iterate hashmap in reverse order in Java

I am trying this for some hour but not finding any best approach to achieve iteration of hashmap in reverse order, this is the hashmap I have.
Map<Integer, List<String>> map = new HashMap<Integer, List<String>>();
for(Integer key : map.keySet()) {
List<String> value = map.get(key);
List<Map<String,?>> security = new LinkedList<Map<String,?>>();
for(int ixy = 0; ixy < value.size()-1; ixy++){
security.add(createItem(value.get(ixy), value.get(ixy+1)));
}
adapter.addSection(Integer.toString(key), new SimpleAdapter(getApplicationContext(), security, R.layout.list_complex, new String[] { ITEM_TITLE, ITEM_CAPTION }, new int[] { R.id.list_complex_title, R.id.list_complex_caption }));
}
I have seen example of TreeMap as well,
Map<Integer, List<String>> sortedMap = new TreeMap<Integer, List<String>>(map);
But treemap also gives in ascending order, what I want is in descending order.
best approach to acheive iteration of hashmap in reverse order
HashMap does not define any particular ordering of its element. Therefore the "reverse" order isn't defined either.
For a TreeMap, you can use descendingMap().
Hashmap does not have specific order. But you can use TreeMap.
Perhaps this simple example can help you :
Map<Integer, String> map = new TreeMap<Integer, String>();
map.put(1, "abc1");
map.put(2, "abc2");
map.put(3, "abc3");
ArrayList<Integer> keys = new ArrayList<Integer>(map.keySet());
for(int i=keys.size()-1; i>=0;i--){
System.out.println(map.get(keys.get(i)));
}
A HashMap doesn't maintain eny order between keys.
A TreeMap orders its keys by their natural order, or by the order imposed by a comparator that you pass when constructing the map. So if you want to have Integer keys ordered in reverse order, construct the TreeMap this way:
Map<Integer, List<String>> sortedMap =
new TreeMap<Integer, List<String>>(Collections.reverseOrder());
Map<Integer, List<String>> sortedMap = new TreeMap<Integer, List<String>>(Collections.reverseOrder());
Collections.reverseOrder() keeps the map sorted in descending order.
You can use TreeMap#descendingKeySet method.
Map<Integer, List<String>> map = new TreeMap<Integer, List<String>>();
for(Integer key : map.descendingKeySet()) {
List<String> value = map.get(key);
List<Map<String,?>> security = new LinkedList<Map<String,?>>();
for(int ixy = 0; ixy < value.size()-1; ixy++){
security.add(createItem(value.get(ixy), value.get(ixy+1)));
}
adapter.addSection(Integer.toString(key), new SimpleAdapter(getApplicationContext(), security, R.layout.list_complex, new String[] { ITEM_TITLE, ITEM_CAPTION }, new int[] { R.id.list_complex_title, R.id.list_complex_caption }));
}
Reference:
https://docs.oracle.com/javase/8/docs/api/java/util/TreeMap.html#descendingKeySet--
You can't iterate over a HashMap in reverse because of this:
This class makes no guarantees as to the order of the map; in
particular, it does not guarantee that the order will remain constant
over time.
What you should use is a LinkedHashMap:
This implementation differs from HashMap in that it maintains a
doubly-linked list running through all of its entries. This linked
list defines the iteration ordering, which is normally the order in
which keys were inserted into the map (insertion-order). Note that
insertion order is not affected if a key is re-inserted into the map.
(A key k is reinserted into a map m if m.put(k, v) is invoked when
m.containsKey(k) would return true immediately prior to the
invocation.)
The hashmap is not an ordered collection. Use TreeMap instead, which has descendingKeySet for reverse iteration. See the javadocs. LinkedHashMap is also a good choice.
TreeMap<Integer, String> map = new TreeMap<Integer, String>();
map.put(1, "abc1");
map.put(2, "abc2");
map.put(3, "abc3");
NavigableMap<Integer, String> nmap = map.descendingMap();
for (NavigableMap.Entry<Integer, String> entry : nmap.entrySet()) {
System.out.println("Key : " + entry.getKey() + " Value : " + entry.getValue());
}
An implementation of NPE idea.
Perhaps you need a NavigableMap, like a TreeMap.
But treemap also gives in asecding order, what i want is in descending order.
Implement a Comparator that will compare it reverse than natural order and then just iterate normally you will have reverse iteration
Use insted:
new TreeMap<>(Collections.reverseOrder())
and you will get what you want.
I've found that the Iterators obtained from Java Hashtable via:
Hashtable.values().iterator() and Hashtable.keys().asIterator()
are both in reverse order by default. One oddity, The values().iterator has a first final value of "0" which I didn't add when populating it.

how to shuffle key-value pairs?

I have a set of values which need to be shuffled when needed.
I don't know which variable type is best for me. Data is actually based on key-value structure.Like;
100 "white"
200 "black"
300 "red"
and like that. What I want to do is to change the key-value pairs according to I don't know yet, some algorithm.But they need to be shuffled like this, but shuffling need to be not random, so I can revert data when I need.
100 "red"
200 "white"
300 "black"
I don't really know how my approach should be to the solution. Should I use HashTable or something, and how can I shuffle them dynamically?
Any help is appreciated
Another way for shuffling the key-value mappings randomly:
public static <K,V> void shuffleMap(Map<K,V> map) {
List<V> valueList = new ArrayList<V>(map.values());
Collections.shuffle(valueList);
Iterator<V> valueIt = valueList.iterator();
for(Map.Entry<K,V> e : map.entrySet()) {
e.setValue(valueIt.next());
}
}
Edit:
If you don't want to change the original map (since you need it afterwards), you can create a new one instead:
public static <K,V> Map<K,V> shuffleMap(Map<K,V> map) {
List<V> valueList = new ArrayList<V>(map.values());
Collections.shuffle(valueList);
Iterator<V> valueIt = valueList.iterator();
Map<K,V> newMap = new HashMap<K,V>(map.size());
for(K key : map.keySet()) {
newMap.put(key, valueIt.next());
}
return newMap;
}
You do not really want a seemingly-randomly mixing which can be reverted (which quickly gets complicated), but simply retain your original map. If this does not fit, you need to describe your problem better.
Okay, you want to encrypt the mapping by using a secret key, giving another mapping, and then decrypt it again. Obviously random shuffling does not help here, and even pseudorandom is no good, since it gives no reliable way to reshuffle. In the basic case, your key would be a invertible map between the keys of our mapping.
public static <K,V> Map<K,V> encryptMap(Map<K,V> plainMap, Map<K,K> key) {
Map<K,V> cryptoMap = new HashMap<K,V>(plainMap.size());
for(Map.Entry<K,V> entry : plainMap.entrySet()) {
cryptoMap.put(key.get(entry.getKey()), entry.getValue());
}
return cryptoMap;
}
Decryption works the same, in fact, only using the reverse map of the key.
So, when you have your example keys of {100, 200, 300}, any permutation of these keys is a valid key for our "encryption scheme".
(There are only 6 possible ones, which is not very secure.)
Map sampleKey = new HashMap<Integer, Integer>();
sampleKey.put(100, 200);
sampleKey.put(200, 300);
sampleKey.put(300, 100);
Map sampleUnKey = new HashMap<Integer, Integer>();
for(Map.Entry<Integer, Integer> e : sampleKey) {
sampleUnKey.put(e.getValue(), e.getKey());
}
Map<Integer, String> data = new HashMap<Integer, String>();
data.put(100, "white");
data.put(200, "black");
data.put(300, "red");
System.out.println(data);
Map<Integer, String> encrypted = encryptMap(data, sampleKey);
System.out.println(encrypted);
Map<Integer, String> decrypted = encryptMap(data, sampleUnKey);
System.out.println(decrypted);
The map decrypted now should be the same as the original map.
For bigger keysets you would want to find a scheme to get a suitable
permutation of keys from some input-able key.
It looks like you need a list of tupples. A Map is exactly that. However, a standard like HashMap has no functionality for changing the relationship between key and value.
I think I would have implemented my own Map for this. Create a class that implements java.util.Map, implement the required methods and create some other methods for "mixing".
It all dependes on what functionality you really need on the list of tupples. Do you need to look up colors very fast? Can there be more than one tupple with the same numbers?
I am not sure how exactly you are going to shuffle the pairs, but if you need to shuffle them based on the key, you can use a Map:
Map<String, String> map = new HashMap<String, String>();
map.put("100", "white");
map.put("200", "black");
map.put("300", "red");
// swap 100 with 200
String temp = map.get("100");
map.put("100", map.get("200"));
map.put("200", temp);
Alternatively, if you need to shuffle the pair randomly, you can create a class Pair (which will basically store an int and a String), as suggested by larsmans, and store them in an array. Then, a slightly-modified version of Fisher-Yates shuffle can be used. Something along these lines:
// initialize list
List<Pair<Integer, String>> values = new ArrayList<Pair<Integer, String>>();
values.add(new Pair<Integer, String>(100, "white"));
values.add(new Pair<Integer, String>(200, "black"));
values.add(new Pair<Integer, String>(300, "red"));
// shuffle
System.out.println(values); // e.g., [100 white, 200 black, 300 red]
Random random = new Random();
for (int i = values.size() - 1; i > 1; i--) {
int j = random.nextInt(i + 1);
// swap values between i-th Pair and j-th Pair
Pair<Integer, String> iPair = values.get(i); // the iPair :-)
Pair<Integer, String> jPair = values.get(j);
String iString = iPair.getSecond();
iPair.setSecond(jPair.getSecond());
jPair.setSecond(iString);
}
System.out.println(values); // e.g., [100 red, 200 black, 300 white]

Categories

Resources