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]
Related
I was wondering if it is possible to get a random value from a HashMap and then straight after remove that key/value from the HashMap? I can't seem to find any method that works, would a different data structure be more appropriate for this?
Edit:
I should've been more clear, I generate a random number and then retrieve the value that corresponds with that random number. I need to return the value and then remove the entry from the map.
Maybe Map#computeIfPresent would work in your case. From its documentation:
If the value for the specified key is present and non-null, attempts to compute a new mapping given the key and its current mapped value.
If the remapping function returns null, the mapping is removed.
var map = new HashMap<Integer, String>();
map.put(1, "One");
map.put(2, "Two");
map.put(3, "Three");
map.computeIfPresent(2, (k, v) -> {
// `v` is equal to "Two"
return null; // Returning `null` removes the entry from the map.
});
System.out.println(map);
The above code outputs the following:
{1=One, 3=Three}
If you were to use a ConcurrentHashMap, then this would be an atomic operation.
The best way to both return and remove the key-value pair from a HashMap is by using the remove(key) method. This method removes the entry associated with the key and returns its corresponding value.
Integer randomNumber = new Random().nextInt(10);
Map<Integer, String> map = new HashMap<>();
String valueOfRandomNumberKey = map.remove(randomNumber);
The problem, as I understand it, is this: given a HashMap you want to
Choose a key at random from among the the keys currently associated in the Map;
Remove that association of that randomly chosen key from the map; and
Return the value that had, until recently, been associated with that key
Here's an example of how to do this, along with some a little test/demonstration routine:
public class Main
{
private static <K, V> V removeRandomEntry(Map<K, V> map){
Set<K> keySet = map.keySet();
List<K> keyList = new ArrayList<>(keySet);
K keyToRemove = keyList.get((int)(Math.random()*keyList.size()));
return map.remove(keyToRemove);
}
public static void main(String[] args){
Map<String, String> map = new HashMap<>();
for(int i = 0; i < 100; ++i)
map.put("Key" + i, "Value"+i);
int pass = 0;
while (!map.isEmpty())
System.out.println("Pass " + (++pass) + ": Removed: " + removeRandomEntry(map));
}
}
I would do it like this:
Hashmap<Integer, Object> example;
int randomNum = ThreadLocalRandom.current().nextInt(0, example.size());
example.getValue() //do something
example.remove(new Integer(randomNum));
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.
If I have a hashmap say HashMap<Integer, String> map = new HashMap<>(); If I have all the values e.g 1 up to 100 all storing the same object. In memory, will this be 100 instances of that object or 100 pointers to one object.
Why?
Well if you have a map with HashMap<String, Integer> (notice the swap in generics) and the string is a word and the integer is the number of occurrences if I need to pick a word at random however such that it is proportional to the number of its occurences then a quick way would be just to fill an arraylist with the word "cat" 100 times and the rest accordingly (to "convert" the hashmap into an arraylist) and that way when a random number is picked using list.get(i) then its proportional to its occurences.
So this will take as n words * m occurences which means a huge list. So how efficient would it be to use a HashMap instead.
If indeed there will be pointers from the key to the value (when they repeat) then surely the map is a better approach.
After looking in the Map implemetation, Map#put() uses the static class Node<K,V> which is handling references
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
V value;
Node<K,V> next;
Node(int hash, K key, V value, Node<K,V> next) {
this.hash = hash;
this.key = key;
this.value = value;
this.next = next;
}
example:
final Map<Integer, Point> map = new HashMap<>();
final Point xPoint = new Point(0, 0);
map.put(1, xPoint);
map.put(2, xPoint);
map.put(3, xPoint);
System.out.println(map);
// modify the point
System.out.println(xPoint);
xPoint.setX(555);
System.out.println(xPoint);
System.out.println(map);
and I gave a try defininf a custom MAp Integer, Point, (custom point)
Map<Integer, Point> map = new HashMap<>();
Point xPoint = new Point(0, 0);
map.put(1, xPoint);
map.put(2, xPoint);
map.put(3, xPoint);
System.out.println(map);
// modify the point
System.out.println(xPoint);
xPoint.setX(555);
System.out.println(xPoint);
System.out.println(map);
as you can see, modifing the point will affect the hole map, since all nodes.V are pointing to the same ref.
It seems to me that the two options you are considering are either :
List<String> words = new ArrayList<>();
words.add("cat");
words.add("cat");
...
words.add("cat");
vs.
Map<Integer,String> words = new HashMap<>();
words.put(0,"cat");
words.put(1,"cat");
...
words.put(99,"cat");
Both the List and the Map would contain multiple reference to the same String object ("cat"). The Map would require more memory, though, since it also has to store the keys.
In addition, you have no easy way of obtaining the i'th String value of the Map for a given random i, since HashMap has no order.
Therefore your List solution is preferable to your suggested Map<Integer,String> alternative.
That said, you could build a more efficient TreeMap that would allow you to get a random String depending on its number of occurrences.
One way I can think of :
TreeMap<Integer,String> map = new TreeMap<>();
map.put(0,"cat");
map.put(100,"dog");
This TreeMap represents 100 occurrences of "cat" and 20 occurrences of "dog". Now, if you draw a random number from 0 to 119, you can easily check whether it lands in the range of "cat" or "dog".
For example, if you draw the number 105, you obtain the corresponding String with :
String randomStr = map.ceilingEntry(105).getValue();
All that remains is to convert the HashMap<String, Integer> containing the number of occurrences to the corresponding TreeMap<Integer, String> :
HashMap<String, Integer> occurrences = ...
TreeMap<Integer, String> map = new TreeMap<>();
int count = 0;
for (Map.Entry<String,Integer> entry : occurrences.entrySet()) {
map.put (count, entry.getKey());
count += entry.getValue();
}
Note that I'm using TreeMap instead of HashMap in order to be able to efficiently obtain the entry having the least key greater than or equal to the given key (without having to iterate over all the entries). This is only possible in NavigableMaps.
Good day, this is kind of confusing me now(brain freeze!) and seem to be missing something. Have an ArrayList which i populate with a HashMap. now i put in my HashMap and arraylist.
Map.put(DATE, value1);
Map.put(VALUE, value2);
arraylist.put(Map);
Since am parsing a JSON, the arraylist increases in significant size. now my question is how do you get the values from both map keys in the arraylist? i have tried this
if(!list.isEmpty()){ // list is an ArrayList
for(int k = 0; k < list.size(); k++){
map = (HashMap)list.get(k);
}
}
Log.d(TAG, "map size is" + map.size());
String [] keys = new String[map.size()];
String [] date_value = new String[map.size()];
String [] value_values = new String[map.size()];
int i = 0;
Set entries = map.entrySet();
Iterator iterator = entries.iterator();
while(iterator.hasNext()){
Map.Entry mapping = (Map.Entry)iterator.next();
keys[i] = mapping.getKey().toString();
date_value[i] = map.get(keys[i]);
if(keys[i].equals(DATE)){
date_value[i] = map.get(keys[i]);
} else if(keys[i].equals(VALUE)){
value_values[i] = map.get(keys[i]);
}
i++;
}
But i can't seem to get all the values. the Map size always return a value of 2, which is just the elements. how can i get all the values from the Map keys in the ArrayList? Thanks
Why do you want to re-invent the wheel, when you already have something to do your work. Map.keySet() method gives you a Set of all the keys in the Map.
Map<String, Integer> map = new HashMap<String, Integer>();
for (String key: map.keySet()) {
System.out.println("key : " + key);
System.out.println("value : " + map.get(key));
}
Also, your 1st for-loop looks odd to me: -
for(int k = 0; k < list.size(); k++){
map = (HashMap)list.get(k);
}
You are iterating over your list, and assigning each element to the same reference - map, which will overwrite all the previous values.. All you will be having is the last map in your list.
EDIT: -
You can also use entrySet if you want both key and value for your map. That would be better bet for you: -
Map<String, Integer> map = new HashMap<String, Integer>();
for(Entry<String, Integer> entry: map.entrySet()) {
System.out.println(entry.getKey());
System.out.println(entry.getValue());
}
P.S.: -
Your code looks jumbled to me. I would suggest, keep that code aside, and think about your design one more time. For now, as the code stands, it is very difficult to understand what its trying to do.
List constructor accepts any data structure that implements Collection interface to be used to build a list.
To get all the keys from a hash map to a list:
Map<String, Integer> map = new HashMap<String, Integer>();
List<String> keys = new ArrayList<>(map.keySet());
To get all the values from a hash map to a list:
Map<String, Integer> map = new HashMap<String, Integer>();
List<Integer> values = new ArrayList<>(map.values());
Try it this way...
I am considering the HashMap with key and value of type String, HashMap<String,String>
HashMap<String,String> hmap = new HashMap<String,String>();
hmap.put("key1","Val1");
hmap.put("key2","Val2");
ArrayList<String> arList = new ArrayList<String>();
for(Map.Entry<String,String> map : hmap.entrySet()){
arList.add(map.getValue());
}
Create an ArrayList of String type to hold the values of the map. In its constructor call the method values() of the Map class.
Map <String, Object> map;
List<Object> list = new ArrayList<Object>(map.values());
Put i++ somewhere at the end of your loop.
In the above code, the 0 position of the array is overwritten because i is not incremented in each loop.
FYI: the below is doing a redundant search:
if(keys[i].equals(DATE)){
date_value[i] = map.get(keys[i]);
} else if(keys[i].equals(VALUE)){
value_values[i] = map.get(keys[i]);
}
replace with
if(keys[i].equals(DATE)){
date_value[i] = mapping.getValue();
} else if(keys[i].equals(VALUE)){
value_values[i] = mapping.getValue()
}
Another issue is that you are using i for date_value and value_values. This is not valid unless you intend to have null values in your array.
This is incredibly old, but I stumbled across it trying to find an answer to a different question.
my question is how do you get the values from both map keys in the arraylist?
for (String key : map.keyset()) {
list.add(key + "|" + map.get(key));
}
the Map size always return a value of 2, which is just the elements
I think you may be confused by the functionality of HashMap. HashMap only allows 1 to 1 relationships in the map.
For example if you have:
String TAG_FOO = "FOO";
String TAG_BAR = "BAR";
and attempt to do something like this:
ArrayList<String> bars = ArrayList<>("bar","Bar","bAr","baR");
HashMap<String,String> map = new HashMap<>();
for (String bar : bars) {
map.put(TAG_BAR, bar);
}
This code will end up setting the key entry "BAR" to be associated with the final item in the list bars.
In your example you seem to be confused that there are only two items, yet you only have two keys recorded which leads me to believe that you've simply overwritten the each key's field multiple times.
Suppose I have Hashmap with key datatype as KeyDataType
and value datatype as ValueDataType
HashMap<KeyDataType,ValueDataType> list;
Add all items you needed to it.
Now you can retrive all hashmap keys to a list by.
KeyDataType[] mKeys;
mKeys=list.keySet().toArray(new KeyDataType[list.size()]);
So, now you got your all keys in an array mkeys[]
you can now retrieve any value by calling
list.get(mkeys[position]);
Java 8 solution for produce string like "key1: value1,key2: value2"
private static String hashMapToString(HashMap<String, String> hashMap) {
return hashMap.keySet().stream()
.map((key) -> key + ": " + hashMap.get(key))
.collect(Collectors.joining(","));
}
and produce a list simple collect as list
private static List<String> hashMapToList(HashMap<String, String> hashMap) {
return hashMap.keySet().stream()
.map((key) -> key + ": " + hashMap.get(key))
.collect(Collectors.toList());
}
It has method to find all values from map:
Map<K, V> map=getMapObjectFromXyz();
Collection<V> vs= map.values();
Iterate over vs to do some operation
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!