different of defining object inside and outside loops in Java - java

I was struggling with part of my code today to read some data from a file and add them to an object as its properties (I'm aware of how to add/read object in file without this hassle but wanted to do it this way) as below:
file is like this:
111,john,23.1
222,jack,22.5
234,adam,12.8
I was trying to read this file using following:
public ArrayList<Staff> LoadAllStaffs(){
ArrayList<Staff> staffs = new ArrayList<Staff>();
File file = new File(stafffile);
Staff tmpstaff = new Staff();
try {
BufferedReader inputfile = new BufferedReader(new FileReader(stafffile));
String tmp;
while((tmp = inputfile.readLine()) != null){
StringTokenizer st = new StringTokenizer(tmp , ",");
tmpstaff.setID(Integer.valueOf(st.nextToken()));
tmpstaff.setFirstName(st.nextToken());
tmpstaff.setSalary(Double.valueOf(st.nextToken()));
staffs.add(tmpstaff);
}
}
catch (IOException e) {
}
return staffs;
}
using println this output shown from the returned ArrayList:
234,adam,12.8
234,adam,12.8
234,adam,12.8
I just moved the Staff tmpstaff = new Staff(); inside the while loop and it shows what it should.
why this is happening? I read -even here- that defining variables (well, here its an Object ) inside or outside loops doesn't make any difference.

You are not creating a new Staff instance inside the loop, you are reusing the same instance for all the iterations. So, you overwrite the values and add the same object.
Move
Staff tmpstaff = new Staff();
to the first line inside the loop.
UPDATE: To address Vash's comment, the issue here is that can be explained as that, if you want to store 3 objects, you need to create such 3 objects. You can reuse the reference (the tmpstaff variable) and effectively where it is defined it is not important (as long as all references to it are in the same scope. But you must create the 3 objects, which means 3 new commands.
UPDATE 2: To put things simpler, the text I read -even here- that defining variables (well, here its an Object ) inside or outside loops doesn't make any difference. means that
File file = new File(stafffile);
Staff tmpstaff = null; // or simpler, Staff tmpstaff;
try {
...
while((tmp = inputfile.readLine()) != null){
tmpstaff = new Staff();
...
and
while((tmp = inputfile.readLine()) != null){
Staff tmpstaff = new Staff();
...
are equivalent.

The operator new is responsible for "object creation" called instance. So when you create him outside the loop you have only one instance, that you modify every loop run. When you create that object inside the loop you have separate instance for each run.

Well Java operates by reference on Objects. Because you are creating only one Staff() Object, there's only one Reference. Therefore, in the while Loop you change just the attributes of the Object and add the same Reference three times to the List.
You can define the variables outside the loop, but you have to instantiate a new Object inside the loop so it lookes like this:
Staff tmpStaff
while((tmp = inputfile.readLine()) != null){
tmpStaff = new Staff();
[additional Code here]
}

If you want to create a new Staff on each pass of the loop, then you have to do the creation inside of the loop before using it.
Done outside the loop as you did, you created a new Staff indeed, but that same keeps being used each time.
A new object is created only when the new keyword is used.

Related

How to store multiple custom objects from an array list into json file in java?

obBelow is my Java code to write to JSON file. I'm quite new to using JSON. I have an arraylist called myAnimals and it has multiple objects of animals(sloth, cat etc.) I want to run a loop that goes through these objects and fills in the JSON file with objects storing them. The first .put is just an example of how it will go, instead of 0 I'd ideally have a reference variable like i that will loop through so I can add all. The idea is this runs every time a new object is added to the arraylist to keep the jsonfile updated. If anyone can advise me on how to do this, that would be great.
The current issue with a loop is that the file would be overwritten each time and only have one json object not many.
public void writeJson(){
JSONObject obj = new JSONObject();
obj.put("name", myAnimals.get(0).getAnimalName());
obj.put("penType", ?);
obj.put("landSpace", ?);
obj.put("waterSpace", ?);
obj.put("airSpace", ?);
try (FileWriter file = new FileWriter("animals.json")) {
file.write(obj.toJSONString());
file.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
change
try (FileWriter file = new FileWriter("animals.json")) {
to
try (FileWriter file = new FileWriter("animals.json", true)) {
adding the true boolean value will append to the end of your file instead of overwriting it. This will work for adding new ones to your file to keep it up to date.
However you need to consider the case where a user erases an animal from your list. In that case, loop the entire arraylist of animals and overwrite whatever is in your animals.json file at that time.

Weka J48 classification not following tree

My original tree was much bigger, but since I'm stuck with this issue for quite some time I decided to try to simplify my tree. I Ended up with something like this:
As you can see, I only have a single attribute called "LarguraBandaRede" with 3 possible nominal values "Congestionado", "Livre" and "Merda".
After that I exported the j48.model from weka to use on my java code.
With this piece of code I import the model to use as a classifier:
ObjectInputStream objectInputStream = new ObjectInputStream(in);
classifier = (J48) objectInputStream.readObject();
After that I started to create a arraylist of my attributes and a Instances File
for (int i = 0; i <features.length; i++) {
String feature = features[i];
Attribute attribute;
if (feature.equals("TamanhoDados(Kb)")) {
attribute = new Attribute(feature);
} else {
String[] strings = null;
if(i==0) strings = populateAttributes(7);
if(i==1) strings = populateAttributes(10);
ArrayList<String> attValues = new ArrayList<String>(Arrays.asList(strings));
attribute = new Attribute(feature,attValues);
}
atts.add(attribute);
}
where populateAttributes gives the possible values for each attribute, in this case "Livre, Merda, Congestionado;" for LarguraBandaRede and "Sim,Nao" for Resultado, my class attribute.
Instances instances = new Instances("header",atts,atts.size());
instances.setClassIndex(instances.numAttributes()-1);
After creating my instances is time to create my Instance File, that is, the instances that I'm trying to classify
Instance instanceLivre = new DenseInstance(features.length);
Instance instanceMediano = new DenseInstance(features.length);
Instance instanceCongestionado = new DenseInstance(features.length);
instanceLivre.setDataset(instances);
instanceMediano.setDataset(instances);
instanceCongestionado.setDataset(instances);
then I set each of this instances with the 3 possible values for "LarguraBandaRede". 'instanceLivre' with "Livre", 'instanceMediano' with "Merda" and 'instanceCongestionado' with "Congestionado".
After that I only classify this 3 instances using the classifyInstance method
System.out.println(instance.toString());
double resp = classifier.classifyInstance(instance);
System.out.println("valor: "+resp);
and this is my result:
As you can see, the instance that has Merda as "LarguraBandaRede" was classify to be the same class as Congestionado, the class 'Nao'. But that doesn't make any sense, since the tree above clearly show that when "LarguraBandaRede" is "Merda" or "Livre" the class should be the same.
So that's my question. How this happened and how to fix it?
Thanks in advance.
EDIT
I didn't know that this:
made any difference in the way the model works. But we have to follow this order when feeding a nominal attribute with possible values.
Have you checked if the weka nominal attribute index is equal in order to your populateAttributes method?

Retreving only latest record in iteration after adding multiple values to list using java

I'm trying to add multiple records to a list and iterate. But its displaying only latest records added
Here is my code
List<ExportBean> exportBeans = new ArrayList<ExportBean>();
ExportBean exportBean = new ExportBean();
exportBean.setBooleanValue(true);
exportBean.setKeyValue("PRE_APPROVED_OFFER");
exportBean.setStringValue("111");
exportBeans.add(exportBean);
exportBean.setBooleanValue(true);
exportBean.setKeyValue("PRE_APPROVED_OFFER1");
exportBean.setStringValue("222");
exportBeans.add(exportBean);
getLopRefNo(exportBeans);
when I iterate it
def getLopRefNo = {
exportBeans->
println "in function ${exportBeans}"
}
It shows only
in function [ExportMessagingBean{stringValue='222', keyValue='PRE_APPROVED_OFFER1', exportBoolean=true}, ExportMessagingBean{stringValue='222', keyValue='PRE_APPROVED_OFFER1', exportBoolean=true}]
It doesnt show the first record added. Is it missing anything?
The problem has nothing to do with Groovy. In your code, you are not actually adding two objects, you are adding one object and modifying it.
List<ExportBean> exportBeans = new ArrayList<ExportBean>();
ExportBean exportBean = new ExportBean();
exportBean.setBooleanValue(true);
exportBean.setKeyValue("PRE_APPROVED_OFFER");
exportBean.setStringValue("111");
exportBeans.add(exportBean); // add object to list
exportBean.setBooleanValue(true);
exportBean.setKeyValue("PRE_APPROVED_OFFER1");
exportBean.setStringValue("222");
exportBeans.add(exportBean); // this time, the same reference is "added". This does not result in an addition (in fact, "add" will return false here
getLopRefNo(exportBeans);
You are calling add with an object that is already present in the list so it has no effect. What you should do is create another instance of ExportBean, like this:
List<ExportBean> exportBeans = new ArrayList<ExportBean>();
ExportBean exportBean = new ExportBean();
exportBean.setBooleanValue(true);
exportBean.setKeyValue("PRE_APPROVED_OFFER");
exportBean.setStringValue("111");
exportBeans.add(exportBean); // add object to list
exportBean = new ExportBean(); //create new instance of ExportBean
exportBean.setBooleanValue(true);
exportBean.setKeyValue("PRE_APPROVED_OFFER1");
exportBean.setStringValue("222");
exportBeans.add(exportBean); // this new instance will be correctly added
getLopRefNo(exportBeans);
You only have one ExportBean object in your code (you only said new ExportBean() once) so you have added the same object to the list twice. Your second set of calls to the set methods on the bean just updates your existing object rather than creating a new one.

How to store array values in Jmeter variables?

I have a CSV file containing data which I read using a Bean Shell script and populate an ArrayList based upon it.Below is the code for it.
//Populate Beanshell script
import java.text.*;
import java.io.*;
import java.util.*;
ArrayList strList = new ArrayList();
try {
File file = new File("path/to/csv");
if (!file.exists()) {
throw new Exception ("ERROR: file not found");
}
BufferedReader bufRdr = new BufferedReader(new FileReader(file));
String line = null;
while((line = bufRdr.readLine()) != null) {
strList.add(line);
}
bufRdr.close();
}
catch (Exception ex) {
IsSuccess = false;
log.error(ex.getMessage());
System.err.println(ex.getMessage());
}
catch (Throwable thex) {
System.err.println(thex.getMessage());
}
Now I want to utilize this data in a random manner so I am trying to use something like this
//Consumer bean shell script
//Not able to access strList since vars.put cannot store an object
Random rnd = new java.util.Random();
vars.put("TheValue",strList.get(rnd.nextInt(strList.size())));
But I am unable to do this because in vars.put I cannot store an array or a list,I can only store only primitive types.So there is no way in which I can access the populate function's ArrayList from an another BeanShell script.
How do I achieve randomization in this scenario since calling populate function each and every time is not good from a performance point of view.
vars.put only supports String values. There is vars.putObject:
Scripts can also access JMeter variables using the get() and put()
methods of the "vars" variable, for example: vars.get("HOST");
vars.put("MSG","Successful"); . The get() and put() methods only
support variables with String values, but there are also getObject()
and putObject() methods which can be used for arbitrary objects.
JMeter variables are local to a thread, but can be used by all test
elements (not just Beanshell).
I would recommend using bsh.shared namespace, this way you will be able to store any Java object and access it even from different Thread Groups if needed.
JMeter-specific example is in the official documentation, in Sharing Variables chapter
At the end of 1st script:
bsh.shared.strList = strList;
At the beginning of the 2nd script:
List strList = bsh.shared.strList;
Random rnd = new java.util.Random();
vars.put("TheValue",strList.get(rnd.nextInt(strList.size())));
See How to use BeanShell: JMeter's favorite built-in component guide for more details on Beanshell scripting for JMeter.
Hope this will help someone.
For sharing variables globally, i.e. among threads in all thread groups, use props.
For e.g,
In setup thread group, do this
props.put("mylist", new ArrayList());
Now, in post processor for every thread in the thread group, add values to the list.props.get("mylist").add(<some value>);
In tear down thread group, fetch the entire list again.
log.info(props.get("mylist").toString());
In thread group, list is updated by multiple threads then prefer to use Vector instead of ArrayList.
Ref : scope-of-variables-sharing-among-threads-and-thread-groups

How to get the updated list in main method

I am having a list of queues as follows:
public class QueueSelection {
public List initQueueCollection()
{
QueueLoad d1 = new QueueLoad("QUEUEA1", "QUEUEB1", true);
QueueLoad d2 = new QueueLoad("QUEUEA2", "QUEUEB2", false);
QueueLoad d3 = new QueueLoad("QUEUEA3", "QUEUEB3", true);
QueueLoad d4 = new QueueLoad("QUEUEA4", "QUEUEB4", false);
List list = new ArrayList();
list.add(d1);
list.add(d2);
list.add(d3);
list.add(d4);
return list;
}
Now from the main method, i call the above method like,
QueueSelection selection = new QueueSelection();
List<QueueLoad> queueList =selection.initQueueCollection();
When the first input/file comes, queue is checked to see which one of these is false, so I am fetching the second one ("QUEUEA2", "QUEUEB2", false);
As soon as I fetch it, I should change the status to true like ("QUEUEA2", "QUEUEB2", true); I am doing it using
for (QueueLoad s:queueList)
{
if(s.getStatus()==false)
{
str1=s.getQueueName1();
str2=s.getQueueName2();
str3=s.getStatus();
particularCollection=s;
System.out.println(s);
particularCollection.setStatus(true);
particularCollection.setQueueName1(str1);
particularCollection.setQueueName2(str2);
int j=queueList.indexOf(particularCollection);
System.out.println("The index is"+j);
s = new QueueLoad(str1, str2, true);
newqueueList=queueList.set(j, s);
And the list is updated. Now when the second input comes, since in the first line it is seeing the List<QueueLoad> queueList =selection.initQueueCollection();
it is always getting the old list and not the updated one.
Please help.
Now when the second input comes, since in the first line it is seeing
the List queueList =selection.initQueueCollection(); it is
always getting the old list and not the updated one.
I interpret this to mean that you don't want the init method to be invoked again 'when the second input comes'.
There are a lot of ways to address this. Have you considered moving the init method into the constructor for QueueSelection, for example?
Take a look at the javadocs for ArrayList.set. It returns the old value at that location. So you'll want to do:
queueList.set(j, s);
newqueueList = queuelist;
If I have understood you correctly then you're problem is that at each new input you are intializing a new list. Maybe just put queueList as class attribute?
Also:
newqueueList=queueList.set(j, s);
I think set() returns the object that was previously at that index. So your newqueueList would actually contain a QueueLoad object.
Since you haven't shown your main() method, I'm not certain what it looks like, but something along these lines should work:
Instead of:
QueueSelection selection = new QueueSelection();
List<QueueLoad> queueList =selection.initQueueCollection();
Declare queueList outside the method something like this:
private static List<QueueLoad> queueList = null;
Then, in the method,
if (queueList == null)
{
QueueSelection selection = new QueueSelection();
queueList =selection.initQueueCollection();
}

Categories

Resources