Original code from here.
Looking at the code below (ignoring import statements):
public class JsonTest2 {
public static void main(String[] args){
Gson gson = new Gson();
JsonParser jsonParser = new JsonParser();
JsonReader reader = null;
try {
reader = new JsonReader(new InputStreamReader(new
FileInputStream("./twitterUser.json")));
}
catch (IOException e) {
}
JsonArray userArray = jsonParser.parse(reader).getAsJsonArray();
List<TwitterUser> twitterUsers = new ArrayList<TwitterUser>();
//For each element in the JSON array, create a new TwitterUser
//and populate with the json data:
for ( JsonElement aUser : userArray ) {
TwitterUser aTwitterUser =
gson.fromJson(aUser, TwitterUser.class);
twitterUsers.add(aTwitterUser);
}
//For each new TwitterUser that was created, print them out:
for ( TwitterUser tUser : twitterUsers) {
System.out.println(tUser);
}
}
}
The array List twitterUsers = new ArrayList(); gets created and contains two instances of TwitterUser.
How do I reference each instance individually (what is their variable name)? If this is not possible e.g. you can only use TwitterUser[0], how can I use JSON to create say five objects each with individual variable names?
Also a bit of explaining-around the subject is probably needed...
A List is not an array. An ArrayList is a List that is backed by an array (that you can't access directly). Instead you use the List interface to
List.get(int) which Returns the element at the specified position in this list.
Use the List.iterator() to iterate the List, perhaps indirectly with a for-each loop.
So, for the first case
TwitterUser tu = twitterUsers.get(0); // <-- get the first TwitterUser
// in the List.
or for the second
for (TwitterUser tu : twitterUsers) {
// do something with each TwitterUser in tu
}
How do I reference each instance individually
As twitterUsers[0] and twitterUsers[1].
(what is their variable name)?
They aren't variables, and they don't have names.
If this is not possible e.g. you can only use TwitterUser[0], how can I use JSON to create say five objects each with individual variable names?
You can't. Variable names things that you write in your source code ... before it is compiled. A library class (such as the JSON library you are using) can only do things at runtime ... after the source code is compiled.
But if you really want different variables, then it is easy to write your code to do this, provided you know exactly how many variables are required; e.g.
TwitterUser userOne = twitterUsers[0];
TwitterUser userTwo = twitterUsers[1];
Obviously, this is hard-wiring the assumption that there are (at least) two elements in the JSON array you will be receiving.
Is this a good idea? Probably not:
It doesn't scale.
If the number of variables to be populated is not constant, your code gets rather complicated and fragile. (You can initialize them to null, but you then have to deal with possible nulls each time you use the variables!)
You are hard-wiring assumptions that typically shouldn't be hard-wired. (At a minimum you should check that the number of elements in the JSON array matches your expectations ... and treat an incorrect assumption as an error!)
But in some circumstances, it could make the code easier to read; e.g. if the named variables (userOne and userTwo) are going to be used extensively, or they have more descriptive names.
What about using a Map instead of variables? It does scale (sort of) but you still have problems when the number of entries in the array is variable. And the code is more complicated.
Related
I have an object which contains some package-private member variables and I'm adding them to a Google Sheets v4 ValueRange in another object. The current code looks a little bit like this:
List<List<Object>> data = new ArrayList<>();
...
/**
* Sets all the values in the ValueRange member variable
* #return the ValueRange object
*/
ValueRange requestBuilder() {
...
//For each case, add it to the value range
for (int i = 0; i < closedCases.size(); i++) {
data.add(
Arrays.asList(
closedCases.get(i).number,
closedCases.get(i).priority,
closedCases.get(i).firstResp,
closedCases.get(i).accName,
closedCases.get(i).subject,
closedCases.get(i).assigned,
closedCases.get(i).lastUpdated,
closedCases.get(i).daysOld,
closedCases.get(i).jiraCase
)
);
}
vr.setValues(data);
return vr;
}
The question that I'm seeking to answer is, is there any way to do Arrays.asList( closeCases.get(i) ) or add some kind of method on the case object to simply fill all that stuff in, rather than calling out each member variable in the Arrays.asList(). I'm also aware I can use a foreach, but would still need to use the same notation for adding items, which is what I'm trying to avoid.
In case anyone is interested, closedCases is just an ArrayList of an object with some strings and doubles in it.
You somehow need to specify what fields go into this list, in what order. If you want to capture all fields, you could use reflection to iterate over the object (potentially choosing declared, not inherited fields, and potentially choosing only package-private fields), as described here.
But that is not the idiomatic way to do it in Java.
Can you change the definition of the "object which contains some package-private member variables" so that instead it has a Map with key-value pairs?
You could add a List field in the object that is held by closedcases and call that field from inside the loop.
For instance, say the object is Foo,
Inside foo, create a field:
ArrayList<String> allFields = new ArrayList<String>{number. priority …… };
Method:
public ArrayList<String> getAll() {
return allFields;
}
And from inside the loop, just do
data.add(closedCases.get(i).getAll());
If the fields are not just string, you could create different arraylist that holds different types of object, which will increase the list again but could be substantially less that what you gave us.
I have created a Class that has a constructor that takes a string line and after some processing of the data turns it into an object.
I need to feed data from a CSV one line at a time into the constructor to create an object for every line of the file. However from all my searching I cannot figure out how to create these objects as,from what I have learnt, to create object you have to name these objects. Is there a way to create an array of objects so I don't have to name each object? for example the first line would be like Object[0] and so on?
public class Object{
String Name, Example, Example2;
Object(String data){
//Data manipulation to get into Name Example and Example2 creating an
//object
}
public String getName{
return Name;
}
}
public class ObjectFeed{
//This is where I would open the file and feed it line by line into the
//object class
}
I should be able to use the getter methods i created for the Object class on any line number and it should get that information. I just do not understand how to feed data into Objects and create multiple objects.
I would do something like this:
public void createYourObjects() {
List<String> lines = // however you are reading lines from the CSV file
List<YourObject> objects = new ArrayList<>();
for(String line in lines) {
objects.add(methodB(line);
}
}
public YourObject createYourObjectFrom(String line) {
List<String> columns = line.spilt(",");
return new YourObject(columns.get(0), columns.get(1) ..... columns(n-1)); // where n is the size of the list n-1 is the index of the last item in the list
}
Its bit pseudocode-ish but I think it illustrates the basic idea. By using a separate method to construct your object you separate the concern, isolating the creation of your list of objects from the structure of the CSV file.
It also gives you a place to provide any error or special case handling you may need.
In a real world scenario you may need to handle different types of data, translating from String to say int, and also when a column is empty.
In the case where a column is empty you will get a smaller list of columns, and you will have to handle this.
If the data is coming from a CSV, each cell should be separated by a comma, so you should be able to take the input string data and split it.
This could be done like:
String[] csvList = data.split(",");
You can then assign each element of csvList as one of your object's properties, and loop through all of its elements to produce a list of your objects.
List<YourObject> objects = new List<YourObject>();
// increment i by the how many properties YourObject has
for (int i = 0; i < csvList.length; i += 2) {
YourObject obj = new YourObject();
obj.FirstProperty = csvList[i];
obj.SecondProperty = csvList[i+1];
...
objects.add(obj);
}
I have a for loop for iterating over a vector, in which I need to create an object each time a condition proves true, it's as follows,
for(String element : myVector){
if(conditon==true) {
Object objectName = new Object(element) ;
}
.....
....
For each time I need to change the object name, for which I tried a silly solution
String objectName = "object" + an auto incrementing integer
But my compiler does not recognize it as a previously declared string and gives an error stating such a variable is already created
Is there a way to fix this problem or a more efficient method to do my task?
First of all, there are a few syntax errors:
For should be for, If should be if and string should be String.
You are storing every newly-created object into the same reference variable.
To keep all created objects you have to save/store them into a list (or another appropriate data-structure).
List<Object> myList = new ArrayList<>(); //In this list we will store all the created objects
for (String element : myVector) {
if (condition==true) {
myList.add(new Object(element));
}
}
Basically I have a variable, zlort = one;
I want to concatenate the value of zlort into a variable (object reference) name.
Like
BankAccount Accountzlort = new BankAccount;
I want the zlort in Account.zlort to actually be the replaced with value of zlort (one--meaning I want the value to be Accountone), and not zlort itself.
Is it possible to do this?
Thanks!
No you can't, but you might put the instance in a map:
Map<String,BankAccount> map = new HashMap<String,BankAccount>();
map.put("Account" + zlort, new BankAccount());
If you mean dynamically choosing the name to assign a variable to, then no.
You could use a HashMap to achieve the same effect.
It is not possible to change the name of a variable at runtime. That would lead to extreme security and stability problems when dealing with any real-world application.
However, as the two answers here have mentioned, a HashMap might acheive what you are looking for. (See the javadoc!!)
A HashMap (or any other map, for that matter) maps a Key to a Value. The concept is similar to a variable, which is a name -> value mapping. The only difference is that variables are part of the actual program code, which is effectively unmodifiable after compiling. A Map is a data structure that can be modified by the running program. This allows you to freely add key-value pairings to it.
Note that in Java, type-safety is encouraged through the use of Generics. Basically this ensures that the key can only be of one type (e.g. String) and the value can be of only one type (BankAccount). A thorough coverage of Generics can be found here.
You would declare this as follows:
Map<String, BankAccount> accounts = new HashMap<String, BankAccount>();
And then to add a key-value pair to the map, you would use the put() method (which 'puts' a value into the map, associated with a key)
String key = "Key"
BankAccount value = new BankAccount();
accounts.put(key, value);
To retrieve it, you would use the get() method.
BankAccount retrievedValue;
retrievedValue = accounts.get(key);
After reading the explanations in your comments, the fact that you can't use an array but can use an `ArrayList'...
Rather than creating a new variable name (or array element, or map value) for each BankAccount, you can probably use scope to your advantage.
Scope is the concept that a reference to a variable only has meaning within a certain part of code. If you declare a variable inside a method, that variable can only be seen within that method. A variable declared within a block (a loop, if statement, etc ) can only be seen from within that block.
Class fields have a different kind of scoping that can be adjusted with keywords (see here).
For example:
public class ScopeExample
int classInt = 10;
public void method() {
int methodInt = 0; // This integer can only be seen by code in
// this method
}
public void method2() {
//doSomething(methodInt) // This line won't compile because i is
// declared in a different method!
doSomething(classInt); // This line will compile and work
// because x is declared in the class that
// contains this method.
int index = 0;
while (index < 3) {
int whileInt = index; // This integer can only be seen from within
// this while loop! It is created each
// loop iteration.
doSomething(whileInt);
}
doSomething(whileInt); //This line won't work, whileInt is out of scope!
}
public doSomething(int a) {
System.out.println(a);
}
}
SO! If you create a BankAccount object within the loop, you don't have to worry about creating a new name for the next one. Each time the loop iterates it will become a new object (when you create it).
If you have to store it, you definitely will need to use an array or other data structure (ArrayList!).
Building on the idea of scope, you -can- have the same variable name for each new BankAccount. A variable reference name isn't guaranteed to be paired with the object that it refers to. That is a convenience to the programmer, so you don't have to know the exact memory address it is being stored in.
For example:
public static void main(String[] args) {
Object o;
int i = 0;
while (i < 5) {
Object reference = new Object(); // Create a new Object and store
// it in 'reference'
o = obj; // The Object 'o' now refers to the object in 'reference'
i++;
}
System.out.println(o); // This should print information about the
// LAST object created.
}
The new Object created in the loop does not belong to 'obj'. You as a programmer use 'obj' to point to the Object. The program doesn't really know what obj means, other than the fact that it points to the Object you just created.
Finally, you can use this along with an ArrayList to make your life easier.
public static void main(String[] args) {
// Our new ArrayList to hold our objects!
ArrayList<Object> stuff = new ArrayList<Object>();
int i = 0;
while (i < 5) {
Object obj = new Object(); // Create an object and make obj point to it.
stuff.add(obj); // Put "the object that 'obj' points to" in 'stuff'.
i++;
}
// This loop goes through all of the Objects in the ArrayList and prints them
for (int index = 0; index < stuff.size(); index++) {
System.out.println(stuff.get(i)); // This will print a single
// object in the ArrayList each time.
}
}
I've got a problem.. I've got something like...
if(condition(TEST) == true){
something (NAME) = new something(this);
}
This is inside a loop where there are many TEST variables being iterated through. I don't know how many and its possible each of them would need a "(NAME)" variable so basically I want to know how would I make java "on the fly" generate a name for these variables?
Thanks!
Are you sure you don't want to store the results in either an array or a collection?
The closest thing would be to use a Map. Randomly generate a String or an Integer key, and use it as a key to your Something value.
// Before all
Map<String, Something> myMap = new HashMap<String, Something>();
// For each of these
if(condition(TEST) == true){
String name = "VAR" + Math.random() // Don't remember the syntax here
myMap.put(name, new something(this));
}
Unless I'm misunderstanding your problem, you won't need to do this.
Variables in Java are lexically scoped, in that they are defined solely for the block in which they exist. On every iteration through the loop, your name parameter will refer to a different object, and will not be affected by the values it held on previous loops.
So you will only need as many parameters in your loop as there are attributes you want to operate on within the loop (possibly only one), which in all cases is something you'll know for sure when you write your code (at compile time) and is divorced from the (runtime) number of TEST objects.
If you're not storing them or referencing them later, and they're all the same type, you could use the same variable for all of them:
// Outside loop ...
something $name;
// Inside loop ...
if(condition(TEST) == true){
$name = new something(this);
}
The simplest way to generate in id (e.g. for a Map) is to use an AtomicInteger.
AtomicInteger counter = new AtomicInteger();
Map<String, Something> map = ...
String id = Integer.toString(counter.getAndIncrement(), 36);
map.put(id, new Something());
// later
Something s = map.get("0");
or it would be simpler to use a List which is naturally indexed.
List<Something> list = ...
list.add(new Something());
// later
Something s = map.get(0);