How can I update Java hashmap values by previous value - java

This question is a bit more complex that the title states.
What I am trying to do is store a map of {Object:Item} for a game where the Object represents a cupboard and the Item represents the content of the cupboard (i.e the item inside).
Essentially what I need to do is update the values of the items in a clockwise (positive) rotation; though I do NOT want to modify the list in any way after it is created, only shift the positions of the values + 1.
I am currently doing almost all That I need, however, there are more Object's than Item's so I use null types to represent empty cupboards. However, when I run my code, the map is being modified (likely as it's in the for loop) and in turn, elements are being overwritten incorrectly which after A while may leave me with a list full of nulls (and empty cupboards)
What I have so far...
private static Map<Integer, Integer> cupboardItems = new HashMap<Integer, Integer>();
private static Map<Integer, Integer> rewardPrices = new HashMap<Integer, Integer>();
private static final int[] objects = { 10783, 10785, 10787, 10789, 10791, 10793, 10795, 10797 };
private static final int[] rewards = { 6893, 6894, 6895, 6896, 6897 };
static {
int reward = rewards[0];
for (int i = 0; i < objects.length; i++) {
if (reward > rewards[rewards.length - 1])
cupboardItems.put(objects[i], null);
else
cupboardItems.put(objects[i], reward);
reward++;
}
}
// updates the items in the cupboards in clockwise rotation.
for (int i = 0; i < cupboardItems.size(); i++) {
if (objects[i] == objects[objects.length - 2])
cupboardItems.put(objects[i], cupboardItems.get(objects[0]));
else if (objects[i] == objects[objects.length - 1])
cupboardItems.put(objects[i], cupboardItems.get(objects[1]));
else
cupboardItems.put(objects[i], cupboardItems.get(objects[i + 2]));
}
So how may I modify my code to update so i get the following results..
======
k1:v1
k2:v2
k3:v3
k4:none
=======
k1:none
k2:v1
k3:v2
k4:v3
?

HashMap doesn't guarantee ordering, therefore if you need ordering, use ArrayList or LinkedList.
If you want to stick with HashMap, you need to sort the HashMap based on the key before each rotation. You can sort easily since the keys are Integer objects. But this will affect the performace.

Ragavan has a good answer if you want to stick to your approach. However, you are doing a lot of work to just rotate the items. It would be much more efficient to just rotate the index (using modulus) and keep the arrays the same:
final static List<Integer> objects = new ArrayList<Integer>(
Arrays.asList(10783, 10785, 10787, 10789, 10791, 10793, 10795, 10797));
final static List<Integer> rewards = new ArrayList<Integer>(
Arrays.asList(6893, 6894, 6895, 6896, 6897, -1, -1, -1));
public static int getReward(int obj, int rot){
int rotIndex = (objects.indexOf(obj) - rot)%objects.size();
//modulus in java can be negative
rotIndex = rotIndex < 0 ? rotIndex+objects.size():rotIndex;
return rewards.get(rotIndex);
}
public static void main(String... args){
//This should give 6897, which is the reward for obj 10783 after 4 rotations
System.out.println(getReward(10783,4));
}

Related

How to convert a List into a Map using List as key in Java

I read an Excel table containing four columns and create a List. Now, I'd like to use the first three columns as key and use the last column as value. I've seen similar questions asked, but in all those questions, either String or Integer is used as a key.
public class initial {
private int from;
private int to;
private int via;
private int cost;
//constructor
//set and get methods
//hashCode and equals methods
}
public class myTuple {
private int from;
private int to;
private int via;
}
//main function
//get the Excel Table as a list
ArrayList<initial> myList = new ArrayList<initial>();
for(int i= mySheet.getFirstRowNum()+1 ; i<= mySheet.getLastRowNum(); i++) {
initial e = new initial();
Row ro = mySheet.getRow(i);
for(int j = ro.getFirstCellNum(); j <= ro.getLastCellNum(); j++) {
Cell ce = ro.getCell(j);
switch(j) {
case 0:
e.setFrom((int) ce.getNumericCellValue());
break;
.....
case 3:
e.setCost((int) ce.getNumericCellValue());
break;
}
}
myList.add(e);
}
//Create map
Map<myTuple, Integer> myMap = new HashMap<>();
I do not know how to proceed after this point. I believe I should use something like;
Map<myTuple, Integer> myMap= myList.stream().collectC(ollectors.toMap(myList:: , myList::));
If someone could assist me, I'd really appreciate.
Also, if you believe that there is a more efficient way to perform this (e.g., the way I read my data and parse into a list, the way I convert the list into a map), please let me know. Even though it is not in the content of this question, if there is a better way to read a multi dimensional table and parse into a List as I do, I 'd love to hear that too. In the future, I will have a bigger tables with more columns. Hence, I'm not quite sure if going through every column with a switch statement is the way to go.
You can just create the map while looping.
Tuple key = new Tuple(row.getNum(0), row.getNum(1), row.getNum(2));
List<Integer> value = new ArrayList<>();
for (int cell = 3; cell < row.getCount(); cell++) {
value.add(row.getNum(cell));
}
Map.put(key,value);
The toMap collector needs 2 functions (1 to create a key & 1 to create a value). You can use lambdas (to extract the relevant fields from your source type):
Map<MyTuple, Integer> myMap = myList
.stream()
.collect(Collectors.toMap(
i -> new myTuple(i.from, i.to, i.via),
i -> i.cost
));
Your destination type "MyTuple" needs a constructor, equals, and hashcode.
Here is an example:
class Tuple implements Comparable<Tuple> {
Object one;
Object two;
Object three;
public Tuple(final Object one, final Object two, final Object three) {
this.one = one;
this.two = two;
this.three = three;
}
#Override
public int compareTo(final Tuple that) {
// TODO: Do your comparison here for the fields one, two and three
return 0;
}
}
Map<Tuple, Object> mapKeyedByCompositeTuple = new HashMap<>();
// TODO: Inside your loop
for (int i = 10; i > 0; i--) {
Tuple key = new Tuple("cell-one-value-" + i, "cell-two-value-" + i, "cell-three-value-" + i);
mapKeyedByCompositeTuple.put(key, "cell-four-value-" + i);
}
System.out.println(mapKeyedByCompositeTuple);
Hope that helps,
Cheers,
Michael

Genetic Algorithm using Roulette Wheel Selection

I'm trying to create different selection methods for a genetic algorithm I'm working on but one problem I meet in all selection methods is that my fitness of each node must be different. This is a problem for me as my fitness calculator is quite basic and will yield several identical fitness's
public static Map<String, Double> calculateRouletteSelection(Map<String, Double> population) {
String[] keys = new String[population.size()];
Double[] values = new Double[population.size()];
Double[] unsortedValues = new Double[population.size()];
int index = 0;
for(Map.Entry<String, Double> mapEntry : population.entrySet()) {
keys[index] = mapEntry.getKey();
values[index] = mapEntry.getValue();
unsortedValues[index] = mapEntry.getValue();
index++;
}
Arrays.sort(values);
ArrayList<Integer> numbers = new ArrayList<>();
while(numbers.size() < values.length/2) {
int random = rnd.nextInt(values.length);
if (!numbers.contains(random)) {
numbers.add(random);
}
}
HashMap<String, Double> finalHashMap = new HashMap<>();
for(int i = 0; i<numbers.size(); i++) {
for(int j = 0; j<values.length; j++) {
if(values[numbers.get(i)] == unsortedValues[j]) {
finalHashMap.put(keys[j], unsortedValues[j]);
}
}
}
return finalHashMap;
}
90% of all my different selection methods are the same so I'm sure if I could solve it for one I can solve it for all.
Any help on what I'm doing wrong would be appreciated
EDIT: I saw I'm meant to post the general behavior of what's happening so essentially the method takes in a HashMap<>, sorts the values based on their fitness, picks half sorted values randomly and adds these to a new HashMap<> with their corresponding chromosomes.
I think you'd be much better off using collection classes.
List<Map.Entry<String, Double>> sorted = new ArrayList<>(population.entrySet());
// sort by fitness
Collections.sort(sorted, Comparator.comparing(Map.Entry::getValue));
Set<Integer> usedIndices = new HashSet<>(); // keep track of used indices
Map<String, Double> result = new HashMap<>();
while (result.size() < sorted.size()/2) {
int index = rnd.nextInt(sorted.size());
if (!usedIndices.add(index)) {
continue; // was already used
}
Map.Entry<String,Double> survivor = sorted.get(index);
result.put(survivor.getKey(), survivor.getValue());
}
return result;
But, as Sergey stated, I don't believe this is what you need for your algorithm; you do need to favor the individuals with higher fitness.
As mentioned in the comments, in roulette wheel selection order is not important, only weights are. A roulette wheel is like a pie chart with different sections occupying different portions of the disk, but in the end they all sum up to unit area (the area of the disk).
I'm not sure if there is an equivalent in Java, but in C++ you have std::discrete_distribution. It generates a distribution [0,n) which you initialise with weights representing the probability of each of those integers being picked. So what I normally do is have the IDs of my agents in an array and their corresponding fitness values in another array. Order is not important as long as indices match. I pass the array of fitness values to the discrete distribution, which returns an integer interpretable as an array index. I then use that integer to select the individual from the other array.

Extracting Value from an Array in Java

I have this code:
_leftArray[0] = _square[6][4];
_leftArray[1] = _square[8][4];
_leftArray[2] = _square[9][5];
I want to be able to extract the values of the array. I'd like to write a method that takes the array position as an argument and returns the coordinates. So if the method was called returnCoordinatesFromArray, I could type returnCoordinatesFromArray[1] and return 6 as a variable and 4 as a variable, so I could use them in another method.
If these are static, hard-coded values, why not do something like this:
Map<Integer, int[]> indexToCoordinateMap = new LinkedHashMap<Integer, int[]>();
indexToCoordinateMap.put(0, new int[]{6, 4});
indexToCoordinateMap.put(1, new int[]{8, 4});
indexToCoordinateMap.put(2, new int[]{9, 5});
Then you don't need the method. You can simply get an array of values where the 0th index is the x coordinate and the 1st index is the y coordinate. Of course, this is by convention. If you want to be more specific, you can use Point and do something like this:
Map<Integer, Point> indexToPointMap = new LinkedHashMap<Integer, Point>();
indexToPointMap.put(0, new new Point(6, 4));
indexToPointMap.put(1, new Point(8, 4));
indexToPointMap.put(2, new Point(9, 5));
Then you can simply do:
Point point = indexToPointMap.get(0);
Do a double for loop and save the x and y positions.
_square value;
int posX=0;
int posY=0;
for(int i=0; i<arr.length; i++) {
for(int j=0; j<arr.length; j++) {
if(arr[i][j]==value) {
posX=i;
posY=j;
}
}
}
"I could type returnCoordinatesFromArray[1] and return 6 as a variable and 4 as a variable"
By design it is not possible in Java to return two values at once. If you need to do something like this you could either build your own little Object that holds two variables:
public class Tupel{
private int firstIndex;
private int lastIndex;
public Tupel(int firstIndex, int lastIndex){
this.firstIndex=firstIndex;
this.lastIndex=lastIndex;
}
public int getFirstIndex(){
return this.firstIndex;
}
// the same for lastIndex
}
Then you you store your Tupels in an array Tupel[] leftArray where for example
leftArray[1] = new Tupel(6,4);
Or you use existing classes like Point if they fit your needs.

Converting string arrays into Map

I have two string arrays keys and values
String[] keys = {a,b,c,d};
String[] values = {1,2,3,4};
What is the fastest way to convert them into a map? I know we can iterate through them. But, is there any utility present?
Faster than this?
Map<String,String> map = new HashMap<>();
if(keys.length == values.length){
for(int index = 0; index < keys.length; index++){
map.put(keys[index], values[index]);
}
}
I purpose to you two very simple implementations. One with stream Api of Java 8, one without.
Java < 8 (without stream api)
if(keys.length != values.length) {
throw new IllegalArgumentException("Keys and Values need to have the same length.");
}
Map<String,String> map = new HashMap<>();
for (int i = 0; i < keys.length; i++) {
map.put(keys[i], values[i]);
}
Java > 8 (with stream api)
if(keys.length != values.length) {
throw new IllegalArgumentException("Keys and Values need to have the same length.");
}
Map<String,String> map = IntStream.range(0, keys.length).boxed()
.collect(Collectors.toMap(i -> keys[i], i -> values[i]));
Constant time lookup from the start
If you are looking for a Map that retrieves the value associated with a key in constant time (meaning without having to look at most values), then you cannot do much faster, because the arrays need to be processed.
However, you can use a utility already written that way : com.google.common.collect.Maps.uniqueIndex
Instantaneous conversion, Linear time lookup
If you are ok with a Map that searches the array for the key every time, then you can create the Map instantly using your two arrays, by defining a new class that implements the Map interface :
class TwoArrayMap implements Map<String, String> {
private final String[] keys;
private final String[] values;
// If you want to enable to add more key value pairs to your map, and
// want to make the process faster, you could use ArrayLists instead of arrays
public TwoArrayMap(String[] array1, String[] array2){
if(array1 == null || array2 == null || array2.length < array1.length)
throw new IllegalArgumentException();
keys = array1;
values = array2;
// Alternatively, you could want to clone the arrays, to
// make sure they are not modified, using array1.clone(), etc
}
public String get(String key){
for(int i=0; i<keys.length; i++)
if(key == null && key == null || key != null && key.equals(k) )
return values[i];
return null;
}
public String put(String key, String Value) throws OperationNotSupportedException {
throw new OperationNotSupportedException();
// alternatively, you could resize the arrays and add a new key, or use an ArrayList
}
}
Map<String, String> myMap = new TwoArrayMap(keys, values);
Lazy conversion, constant time lookup after conversion
Another approach would be to do it "lazily", meaning modify the above class, so that it keeps a reference to a HashMap internally, and fills it only when it is looking up elements :
class TwoArrayMap implements Map<String, String> {
private final Map<String, String> hashmap;
private int maxIndexAlreadyTransferred = -1;
private final String[] keys;
private final String[] values;
public TwoArrayMap(String[] array1, String[] array2){
if(array1 == null || array2 == null || array2.length < array1.length)
throw new IllegalArgumentException();
hashmap = new HashMap<>();
keys = array1;
values = array2;
// Alternatively, you could want to clone the arrays, to
// make sure they are not modified, using array1.clone(), etc
}
public String get(String key){
if(hashmap.containsKey(key))
return hashmap.get(key);
String k, value;
while( maxIndexAlreadyTransferred + 1 < keys.length ){
k = keys[ maxIndexAlreadyTransferred + 1 ];
value = values[ maxIndexAlreadyTransferred +1 ];
if(!hashmap.containsKey(k))
hashmap.put( k, value );
maxIndexAlreadyTransferred++;
if(key == null && k == null || key != null && key.equals(k) )
return value;
}
return null;
}
public String put(String key, String Value) {
hashmap.put(key, value);
}
}
This solution would mean :
an instantaneous creation of your new object
linear time lookup for the first times you will query it, until everything is transferred
constant time lookup after that, behaving as a hash table
IMHO, it's highly unlikely that you will find a utility like that.
But, even if you find one chances are really low that it will provide any performance gain. Because, I think you won't able to do it without iterate through all the elements in both the arrays.
One thing I can suggest is (only if your arrays have a huge number of elements) that you can specify the capacity of the map while instantiating it to reduce overhead of resizing while you put entries into it.
Map<String, String> map = new HashMap<String, String>(keys.length);
//put keys and values into map ...
Convert two String arrays to Map in Java
import java.util.HashMap;
public static void main(String[] args){
String[] keys= {"a", "b", "c"};
int[] vals= {1, 2, 3};
HashMap<String, Integer> hash= new HashMap<String, Integer>();
for(int i= 0; i < keys.length; i++){
hash.put(keys[i], vals[i]);
}
}
Check this LINK for more solutions in different programming languages
Note : The keys should be unique..

What's the shortest way to add a constant value to all the elements of an int[]? How about matrix addition?

I've got an array of int's. I need to add 1 to each of its elements. What's the shortest, most general way to add a given constant to each element, that is, without explicit loops? I've looked online and keep getting pages about adding an element onto an array (i.e., concatenation).
Isn't there something more elegant than looping through each element and adding the constant? Is there some sort of standard library method?
For that matter, how would one add two arrays together (i.e., matrix addition) without an explicit loop?
Here is how you can write the two cases above with Functional Java.
1. Adding 1 to each element:
It's a functional programming library, and so it does not provide mutating operations. But you can easily add them yourself.
public static <A> void transform(Array<A> xs, F<A, A> f) {
for(int i = 0; i < xs.length(); i++) {
xs.set(i, f.f(xs.get(i)));
}
}
Use:
transform(yourArray, Integers.add.f(1));
2. Adding two matrices:
Let xss and yss be two matrices, both of types Array<Array<Integer>>. Then you can add them with:
xss.zip(yss).map(new F<P2<Array<Integer>, Array<Integer>>, Array<Integer>>() {
public Array<Integer> f(P2<Array<Integer>, Array<Integer>> p) {
Array<Integer> xs = p._1();
Array<Integer> ys = p._2();
return xs.zip(ys).map(new F<P2<Integer, Integer>, Integer>() {
public Integer f(P2<Integer, Integer> q) {
return q._1() + q._2();
}
});
}
});
This may feel more verbose than necessary, and that is because Java does not support lambda expressions yet. In the meantime, IDEs can help make such techniques more approachable.
If an unmodifiable result is ok, and you're not stuck to using arrays, you could use a cute trick to delay the addition until the last possible moment. But as soon as you need to visit the whole result array, any performance benefit is lost and the code ends up being more complicated than necessary.
public class OffsetArrayList extends AbstractList< Integer > {
private final List< Integer > liUnderlying;
private final int iOffset;
public OffsetArrayList( int iOffset, liUnderlying ) {
this.iOffset = iOffset;
this.liUnderlying = liUnderlying;
}
#Override
public Integer get( int iIndex ) {
return liUnderlying.get( iIndex ) + iOffset;
}
#Override
public Integer set( int iIndex, Integer iNewValue ) {
return liUnderlying.set( iIndex, iNewValue - iOffset ) + iOffset;
}
// etc.
}
// create new array
int[] aiInput = new int[] { 23, 98, -45 };
// two object allocations, but no new array created and no additions performed
OffsetArrayList alPlusOne = new OffsetArrayList( 1, Arrays.asList( aiInput ) );
// one addition performed; constant time
assert( alPlusOne.get( 0 ) == 24 );
// benefit murky, as we are adding to the same element repeatedly
for ( int i = 0; i < 1000000; ++i )
assert( alPlusOne.get( 2 ) == -44 );
// benefit lost, input destroyed
int[] aiOutput = alPlusOne.toArray( aiInput );

Categories

Resources