Value overridden in hashmap - java

I'm using a HashMap to store a list of tags that are required in a certain message, for multiple messages, ie a HashMap<String,ArrayList<TagObject>>. Each TagObject has attributes, the main one is whether that tag is required or not in a certain message. Now, if a tag is required in one message, and not in another, the final required value ends up being the one that was processed last. I am also creating a HashMap<String,TagObject> to store all the tags processed. Maybe that is causing the issue? I think the issue is the same variable gets overridden repeatedly, how do I make separate copies for each message to store in the main HashMap of messages?
I've looked up HashMap overridden by last value, but I am creating a new Field everytime, so this doesn't seem to be the reason.
for(TagObject o:allTags) {
if(o instanceof Field){
fieldResult=new Field();
Field field = (Field) o;
String required=field.getRequired(); //this is the value found in the file where the specifications are, ie whether the field should be required or no in this message. this value is received correctly
if(required == null || required.equalsIgnoreCase("yes")) {
required="true";
}else{
required="false";
}
else {
fieldResult=ctd.searchFieldMap(field.name); //find the already created Field object
fieldResult.setRequired(required); //set the required now as got from the specifications
}
fieldsInMessage.add(fieldResult); //add this field to list of fields to be there in the message
//while being added, I can see the value of the required tag go as in the specs, however when I later print the hashmap of all messages, the tag which was required in one message, but set as not required in another, has changed value to the value of required of the last appearance of the field in the specifications
}
}
I was hoping that a new copy of the field will be created for each message, but seems like the same object is used in all. How do I make sure each message can have a separate copy of the tag, and hence unique attributes?

I guess that you're adding the tags incorrectly to the HashMap.
For example, if I would like to do a structure with messages and tags I would do as the following snippet:
public static void main(String[] args) {
MessageTagsMap map = new MessageTagsMap();
map.addNewMessage("Hello World!", "Snippet");
map.addNewMessage("StackOverflow", "Webpage", "Interesting");
map.paintMap();
map.addTagsToMessage("Hello World!", "Deprecated");
map.paintMap();
}
public class MessageTagsMap {
HashMap<String, ArrayList<String>> content;
public MessageTagsMap() {
content = new HashMap<>();
}
void addNewMessage(String message, String... tags) {
ArrayList<String> messageTags = new ArrayList<>();
for (String tag : tags) {
messageTags.add(tag);
}
this.content.put(message, messageTags);
}
void addTagsToMessage(String message, String... tags) {
ArrayList<String> messageTags = this.content.get(message);
for (String tag : tags) {
messageTags.add(tag);
}
}
void paintMap() {
for (Map.Entry<String, ArrayList<String>> entry : content.entrySet()) {
System.out.print("Message: " + entry.getKey() + " tags: {");
for (String tag : entry.getValue()) {
System.out.print(tag + " ");
}
System.out.println("}");
}
}
}
As you can see, it's not the same to add more information to a previous Map.Entry than to add a new Map.Entry to your HashMap.
Of course, if you create a new instance of an object each time that you want to add information to your Map, and you put that information with the same key that already exists, you're overriding the value in the Map.

Figured out what I really needed. I wanted to create a copy of a value in a HashMap, what I had been doing was copying over the reference to the object, which is why it kept getting overridden.
What I really needed was a copy constructor.
This helped: https://stackoverflow.com/a/33547834/1677804

Related

Save object to file. "Delete object", but find it in a statistic- and history search. UPDATED

I am working on a insurance-schoolproject where I should be able to create a new client, add insurances (house, car, boat, etc) and save to file. I am using
public void skrivTilFil(HashMap hm) {
if ( hm != null ) {
try (ObjectOutputStream utStream =
new ObjectOutputStream(
new FileOutputStream(filename))) {
utStream.writeObject (hm);
The thing is that I should be able to delete objects (a house-, a car-insurance, the client itself, etc) BUT I must have a history- and statisticsearch where both the objects and deleted objects show.
I have thought about saving to two different files, and delete from just one file. Meaning I have one file where I delete and an other file for the history- and statisticsearch. But I am definitely not allowes to double-save. So I understand it so I only can have one file therefore cannot delete the objects.
I have also thought about having a boolean value where "false" = show, and "true" = deleted. Then show only "show-objects" and not "deleted" when necessary, and both "false" and "true" when searching history and statistics.
So I have made this method:
public void eraseCustomer( int insurancenr )
{
Iterator<Map.Entry<Integer, Person>> iterator = vindu.register.personTM.entrySet().iterator() ;
while(iterator.hasNext())
{
Map.Entry<Integer, Person> insuranceNrEntry = iterator.next();
if( insurancenr == insuranceNrEntry.getKey())
{
Vindu.setUtskriftsomraadet("Found person " + insuranceNrEntry.getValue() );
JOptionPane.showMessageDialog(null, "arrived at eraseCustomer");
}
Running this code shows that I get a value, like "Person#2936643".
What I need is to get the name of the person/object (String lastname) and to set that personboolean to true.
I have a insurancenrkey to find this person.
So 1:
Is it possible to update that personobject with boolean = true an easy way, or do I have to make a new object and then replace it with the object I found? I just need to change the booleanvalue, not the name, birthday, etc.
2:
How do I get the name of the object and not Person#2936643?
The thing is that this is way over my knowledge... Do you have any tips or suggestions on how I can do this, that would be very much appreciated!
(I'm norwegian so please excuse me for the bad english)

JHDF5 - How to avoid dataset being overwritten

I am using JHDF5 to log a collection of values to a hdf5 file. I am currently using two ArrayLists to do this, one with the values and one with the names of the values.
ArrayList<String> valueList = new ArrayList<String>();
ArrayList<String> nameList = new ArrayList<String>();
valueList.add("Value1");
valueList.add("Value2");
nameList.add("Name1");
nameList.add("Name2");
IHDF5Writer writer = HDF5Factory.configure("My_Log").keepDataSetsIfTheyExist().writer();
HDF5CompoundType<List<?>> type = writer.compound().getInferredType("", nameList, valueList);
writer.compound().write("log1", type, valueList);
writer.close();
This will log the values in the correct way to the file My_Log and in the dataset "log1". However, this example always overwrites the previous log of the values in the dataset "log1". I want to be able to log to the same dataset everytime, adding the latest log to the next line/index of the dataset. For example, if I were to change the value of "Name2" to "Value3" and log the values, and then change "Name1" to "Value4" and "Name2" to "Value5" and log the values, the dataset should look like this:
I thought the keepDataSetsIfTheyExist() option to would prevent the dataset to be overwritten, but apparently it doesn't work that way.
Something similar to what I want can be achieved in some cases with writer.compound().writeArrayBlock(), and specify by what index the array block shall be written. However, this solution doesn't seem to be compatible with my current code, where I have to use lists for handling my data.
Is there some option to achieve this that I have overlooked, or can't this be done with JHDF5?
I don't think that will work. It is not quite clear to me, but I believe the getInferredType() you are using is creating a data set with 2 name -> value entries. So it is effectively creating an object inside the hdf5. The best solution I could come up with was to read the previous values add them to the valueList before outputting:
ArrayList<String> valueList = new ArrayList<>();
valueList.add("Value1");
valueList.add("Value2");
try (IHDF5Reader reader = HDF5Factory.configure("My_Log.h5").reader()) {
String[] previous = reader.string().readArray("log1");
for (int i = 0; i < previous.length; i++) {
valueList.add(i, previous[i]);
}
} catch (HDF5FileNotFoundException ex) {
// Nothing to do here.
}
MDArray<String> values = new MDArray<>(String.class, new long[]{valueList.size()});
for (int i = 0; i < valueList.size(); i++) {
values.set(valueList.get(i), i);
}
try (IHDF5Writer writer = HDF5Factory.configure("My_Log.h5").writer()) {
writer.string().writeMDArray("log1", values);
}
If you call this code a second time with "Value3" and "Value4" instead, you will get 4 values. This sort of solution might become unpleasant if you start to have hierarchies of datasets however.
To solve your issue, you need to define the dataset log1 as extendible so that it can store an unknown number of log entries (that are generated over time) and write these using a point or hyperslab selection (otherwise, the dataset will be overwritten).
If you are not bound to a specific technology to handle HDF5 files, you may wish to give a look at HDFql which is an high-level language to manage HDF5 files easily. A possible solution for your use-case using HDFql (in Java) is:
public class Example
{
public Class Log
{
String name1;
String name2;
}
public boolean doSomething(Log log)
{
log.name1 = "Value1";
log.name2 = "Value2";
return true;
}
public static void main(String args[])
{
// declare variables
Log log = new Log();
int variableNumber;
// create an HDF5 file named 'My_Log.h5' and use (i.e. open) it
HDFql.execute("CREATE AND USE FILE My_Log.h5");
// create an extendible HDF5 dataset named 'log1' of data type compound
HDFql.execute("CREATE DATASET log1 AS COMPOUND(name1 AS VARCHAR, name2 AS VARCHAR)(0 TO UNLIMITED)");
// register variable 'log' for subsequent usage (by HDFql)
variableNumber = HDFql.variableRegister(log);
// call function 'doSomething' that does something and populates variable 'log' with an entry
while(doSomething(log))
{
// alter (i.e. extend) dataset 'log1' to +1 (i.e. add a new row)
HDFql.execute("ALTER DIMENSION log1 TO +1");
// insert (i.e. write) data stored in variable 'log' into dataset 'log1' using a point selection
HDFql.execute("INSERT INTO log1(-1) VALUES FROM MEMORY " + variableNumber);
}
}
}

Java Properties - int becomes null

Whilst I've seen similar looking questions asked before, the accepted answers have seemingly provided an answer to a different question (IMO).
I have just joined a company and before I make any changes/fixes, I want to ensure that all the tests pass. I've fixed all but one, which I've discovered is due to some (to me) unexpected behavior in Java.
If I insert a key/value pair into a Properties object where the value is an int, I expected autoboxing to come into play and getProperty would return a string. However, that's not what's occuring (JDK1.6) - I get a null. I have written a test class below:
import java.util.*;
public class hacking
{
public static void main(String[] args)
{
Properties p = new Properties();
p.put("key 1", 1);
p.put("key 2", "1");
String s;
s = p.getProperty("key 1");
System.err.println("First key: " + s);
s = p.getProperty("key 2");
System.err.println("Second key: " + s);
}
}
And the output of this is:
C:\Development\hacking>java hacking
First key: null
Second key: 1
Looking in the Properties source code, I see this:
public String getProperty(String key) {
Object oval = super.get(key);
String sval = (oval instanceof String) ? (String)oval : null;
return ((sval == null) && (defaults != null)) ? defaults.getProperty(key) : sval;
}
The offending line is the second line - if it's not a String, it uses null.
I can't see any reason why this behavior would be desired/expected. The code was written by almost certainly someone more capable than I am, so I assume there is a good reason for it. Could anyone explain? If I've done something dumb, save time and just tell me that! :-)
Many thanks
This is form docs:
"Because Properties inherits from Hashtable, the put and putAll methods can be applied to a Properties object. Their use is strongly discouraged as they allow the caller to insert entries whose keys or values are not Strings. The setProperty method should be used instead. If the store or save method is called on a "compromised" Properties object that contains a non-String key or value, the call will fail. Similarly, the call to the propertyNames or list method will fail if it is called on a "compromised" Properties object that contains a non-String key."
I modified your code to use the setProperty method as per the docs and it brings up compilation error
package com.stackoverflow.framework;
import java.util.*;
public class hacking
{
public static void main(String[] args)
{
Properties p = new Properties();
p.setProperty("key 1", 1);
p.setProperty("key 2", "1");
String s;
s = p.getProperty("key 1");
System.err.println("First key: " + s);
s = p.getProperty("key 2");
System.err.println("Second key: " + s);
}
}

Hashmap keys will not work

I have a HashMap which certain keys like "crash" and "crashes" which return the same response. I want to create a new HashMap that maps synonyms to a unique key in the responseMap (for example, map "crash", "crashes" and "crashed" to "crash" in the synonymMap).
private void fillSynonymMap()
{
synonymMap.put("crash", "crash");
synonymMap.put("crashes", "crash");
synonymMap.put("crashed", "crash");
}
What I am stuck on is how to input these keys so that I can simplify the code below.
private void fillResponseMap()
{
responseMap.put("crash",
"Well, it never crashes on our system. It must have something\n" +
"to do with your system. Tell me more about your configuration.");
responseMap.put("crashes",
"Well, it never crashes on our system. It must have something\n" +
"to do with your system. Tell me more about your configuration.");\
responseMap.put("crashed",
"Well, it never crashes on our system. It must have something\n" +
"to do with your system. Tell me more about your configuration.");
}
public String generateResponse(HashSet<String> words)
{
for (String word : words) {
String response = responseMap.get(word);
if(response != null) {
return response;
}
}
// If we get here, none of the words from the input line was recognized.
// In this case we pick one of our default responses (what we say when
// we cannot think of anything else to say...)
return pickDefaultResponse();
}
After a little messing about I wrote a function that will look for a synonym, before returning a default message.
public String getResponse()
{
HashMap<String, String> responseMap = new HashMap<String, String>();
HashMap<String, String> synonymMap = new HashMap<String, String>();
responseMap.put("crash", "Hello there");
// Load the response value.
synonymMap.put("crash", "crash");
synonymMap.put("crashed", "crash");
synonymMap.put("crashes", "crash");
// Load the synonyms.
String input = "crashed";
// Select input value.
if(responseMap.containsKey(input))
{
// Response is already mapped to the word.
return responseMap.get(input);
}
else
{
// Look for a synonym of the word.
String synonym = synonymMap.get(input);
if(!synonym.equals(input) && responseMap.containsKey(synonym))
{
// If a new value has been found that is a key..
return responseMap.get(synonym);
}
}
// If no response, set default response.
input = "This is a default response";
return input;
}
As you can see the function first checks if the key exists. If it doesn't, it attempts a synonym. If that synonym doesn't pass the test, it will move to the default code at the bottom, which will set input to some default value and return that instead :)
You could use a second map.
The first map translates the synonyms to the basic key, which in turn can be used for the second map with the answers.
This also allows flexible extension of synonyms without enlarging the actual response map.
Besides, in this case you can actually use another type as key for the answser map. It just has to be same as the value type of synonyms maps.
That way, you could also use
Map<String, YourEnum> and Map<YourEnum, String>
with EnumMap as implementation of the Map interface.

Trying to compare a HashSet element with an element in a List

I have a HashSet that I created and this is what it contains. It will contain more later on, this is pasted from standard out when I did a toString on it. Just to show the contents.
foo.toString(): Abstractfoo [id=2, serial=1d21d, value=1.25, date=2012-09-02 12:00:00.0]
INFO [STDOUT] price.toString(): Abstractfoo [id=1, serial=1d24d, value=1.30, date=2012-09-19 12:00:00.0]
I have a List that I also have and I need to compare the two. One of the elements in List is:
Bar.toString(): Bar [id=1d21d, name=Dell, description=Laptop, ownerId=null]
Here is what I am trying to do...
Bar contains all of the elements I want foo to have. There will only be one unique serial. I would like my program to see if an element in the list that is in HashSet contains the id for bar. So serial == id.
Here is what I've been trying to do
Removed code and added clearer code below
I've verified the data is getting entered into the HashSet and List correctly by viewing it through the debugger.
foo is being pulled from a database through hibernate, and bar is coming from a different source. If there is an element in bar I need to add it to a list and I'm passing it back to my UI where I'll enter some additional data and then commit it to the database.
Let me know if this makes sense and if I can provide anymore information.
Thanks
EDIT: Here is the class
#RequestMapping(value = "/system", method = RequestMethod.GET)
public #ResponseBody
List<AbstractSystem> SystemList() {
// Retrieve system list from database
HashSet<AbstractSystem> systemData = new HashSet<AbstractSystem>(
systemService.getSystemData());
// Retrieve system info from cloud API
List<SystemName> systemName= null;
try {
systemName = cloudClass.getImages();
} catch (Exception e) {
LOG.warn("Unable to get status", e);
}
// Tried this but, iter2 only has two items and iter has many more.
// In production it will be the other way around, but I need to not
// Have to worry about that
Iterator<SystemName> iter = systemName.iterator();
Iterator<AbstractSystem> iter2 = systemData .iterator();
while(iter.hasNext()){
Image temp = iter.next();
while(iter2.hasNext()){
AbstractPricing temp2 = iter2.next();
System.out.println("temp2.getSerial(): " + temp2.getSerial());
System.out.println("temp.getId(): " + temp.getId());
if(temp2.getSerial().equals(temp.getId())){
System.out.println("This will be slow...");
}
}
}
return systemData;
}
If N is the number of items in systemName and M is the number of items in systemData, then you've effectively built an O(N*M) method.
If you instead represent your systemData as a HashMap of AbstractSystem by AbstractSystem.getSerial() values, then you just loop through the systemName collection and lookup by systemName.getId(). This becomes more like O(N+M).
(You might want to avoid variables like iter, iter2, temp2, etc., since those make the code harder to read.)
EDIT - here's what I mean:
// Retrieve system list from database
HashMap<Integer, AbstractSystem> systemDataMap = new HashMap<AbstractSystem>(
systemService.getSystemDataMap());
// Retrieve system info from cloud API
List<SystemName> systemNames = cloudClass.getImages();
for (SystemName systemName : systemNames) {
if (systemDataMap.containsKey(systemName.getId()) {
System.out.println("This will be slow...");
}
}
I used Integer because I can't tell from your code what the type of AbstractSystem.getSerial() or SystemName.getId() are. This assumes that you store the system data as a Map elsewhere. If not, you could construct the map yourself here.

Categories

Resources