I have two lists, after find which one has the highest value I store the other one's name into a string variable, like this:
iterateList = "lstDrives"; //lstDrives
So, I want to use that stored name in the next lines:
for(Integer currentElement : iterateList){
if((mostExpensiveValue + currentElement) > b){
result = -1;
}
}
right now I obtained an error because I'm trying to iterate the String var and I want to refer to its content, my question is: how can I achieve this?
This is the entire piece of code:
if(mostExpensiveKb > mostExpensiveDv ){
iterateList = "lstDrives"; //lstDrives
mostExpensiveValue = lstKb.get(keyboards.length-1);
} else{
iterateList = "lstKb"; //listKb
mostExpensiveValue = lstDrives.get(keyboards.length-1);
}
for(Integer currentElement : iterateList){
if((mostExpensiveValue + currentElement) > b){
result = -1;
}
}
That's not how java works. Object do not have names; variable names do not survive the compilation process.
Object o = new Object();
This does not mean that your object's name is 'o'. After all:
Object o = new Object();
Object v = o;
There is only one object here (to make an object, new must be invoked; it is only invoked once here, therefore, there cannot possibly be 2 objects). You have 2 variables that are both referring to the same object. It's like having 2 post-it notes with the same house address written on each. That doesn't mean you have 2 houses.
So, does that mean this one object's name is both o and v?
Object o = new Object();
Object v = o;
o = null;
v = null;
Does that mean that the name of the object used to be o, but then it became both o and v, and then it became just v, and then it became nameless?
Hopefully this gives you the insight as to why objects do not have names and java is not now and not ever going to let you do something like obj.getName().
Java compiles code to an intermediate step (.class files) before you run them. In this intermediate step, local variable names disappear; they can remain for debug purposes but the actual act of running class files ignores this, and you can see this in class files: The local var names don't need to be there in the first place (depends on whether you're using -g when compiling or not to save debug vars), and if they are there, it's in a separate attachment to a method which the JVM just skips completely when reading a class file.
As a consequence, something like getVar("varName") does not now and will never exist in java either. Let alone that java is strongly typed and getVar wouldn't let you type it, given that "varName" could as far as you know, be supplied by the user on the fly.
In other words, you're not 'thinking in java' here. What you're trying to do doesn't work, never has, and never will.
What you can instead do is simply make another variable. After all, all non-primitive variables are merely references (postit notes that contain an address to an object, NOT the object itself). Postit notes are cheap. Here:
List<String> someList = readTheCollectedWorksOfShakespeare();
Let's assume here that this method makes a humongous list with all the lines of everything shakespeare ever wrote. That's a sizable list! Let's say 100MB's worth, so your VM's memory load spikes considerably after this line. Then you do:
List<String> list2 = someList;
List<String> list3 = someList;
List<String> list4 = someList;
List<String> list5 = someList;
and check memory load again: No effect. These are 4 references (pointers). The above 4 lines execute pretty much instantaneously, and cost no memory. It's just that you now have 5 post it notes, all with the address to some ginormous castle on it. The computer did not have to painstakingly copy the castle brick by brick. You can do the same thing in your code:
List<Integer> iterateList;
if (mostExpensiveKb > mostExpensiveDv) {
iterateList = lstDrives;
mostExpensiveValue = lstKb.get(keyboards.length-1);
} else {
iterateList = lstKb;
mostExpensiveValue = lstDrives.get(keyboards.length-1);
}
for (Integer currentElement : iterateList) {
if ((mostExpensiveValue + currentElement) > b) {
result = -1;
}
}
You can use an hashMap to store the list name like key and the list object like value:
Map<String, List<Integer>> namesAndLists= new HashMap<String, List<Integer>>();
namesAndLists.put("lstDrives",lstDrives);
namesAndLists.put("lstKb",lstKb);
Related
I have a list of objects like the next class:
class A {
private String property1;
private String property2;
//Setters && Getters
}
So, after some operations I need to update the list with a default value with some logic like next:
listOfA.forEach(item -> {
item.setProperty1(findSomething());
}
This logic is repeated several times so I'm looking to export it to a method. So, my question is related to this method: Should I update the copy reference of the list with a void method, return it or create new list and update it?:
Option 1: Update the copy reference of the list
private void updateList(List<A> listOfA) {
listOfA.forEach(item -> {
item.setProperty1(findSomething());
}
}
Option 2: Return it
private List<A> updateList(List<A> listOfA) {
listOfA.forEach(item -> {
item.setProperty1(findSomething());
}
return listOfA;
}
Option 3: Create a new list from the other, update this and return it
private List<A> updateList(List<A> listOfA) {
List<A> newList = new ArrayList<>();
//Logic to copy listOfA in newList....
newList.forEach(item -> {
item.setProperty1(findSomething());
}
return newList ;
}
In the end it is very much personal opinion which option you prefer. There are however some considerations that might help you in the decision process:
The first and second option are virtually the same. Both operate on the List that is passed in as a parameter. The updating of the list does not create anything new. This would suggest option 1 as the solution to chose, as with only the signature alone (no additional documentation) option 2 might indicate that the returned list is a new List instance.
Returning the List as in option 2 and 3, has the advantage that you can execute further operations on that list, which make it changeable.
The last option employs defensive copy to actually create a new instance of the input List and operate on that list. While it is good practice to do so, it can have some side effects, that may be unwanted. With option 2 it is not required that the returned list is assigned to anything, as it is the same instance that was passed in as parameter. This is not the case of option 3. Here the result must be assigned, otherwise it becomes eligible for garbage collection.
Everything depends on the situation.
Option 1
This case is suitable when you do not need to use your list anywhere else. I mean that if other object has this list reference as member then this object will have this member modified too.
Option 2
This case seems like case 1 and you can use this method as case 1 but here you return your list. It brings some advantage in first case, you can use your method in chain of Stream API or Optionals:
private List<A> updateList(List<A> listOfA) {
List<A> newList = new ArrayList<>();
//Logic to copy listOfA in newList....
newList.forEach(item -> item.setProperty1(findSomething()));
return newList ;
}
public List<A> myMethod() {
List<A> myList = null;
// possible initialization of list
// ...
// here we update list. In the case when list is null then we do not modify it
return Optional.ofNullable(myList).map(this::updateList).orElse(null);
}
Option 3
In this case you copy array into another one. It make sense in the case when initial array should not be modified, for example it is field of some other class.
Method :3
when u r making a new list object it needs a certain memory in heap. And then you are just copy the content from your agrument list and update it and return the new list. Here creating a new list object takes an extra headache. No need for it.
Method :2
In this method no new list is created bt you return the same list. While java works on call by value (here values means values of the ref object). You can get the updated list on the calling method not need to return it. While u r returing an object u r increasing metadata of that method.
Method :1
It is the best approach. Here u are using the benefit of call by value (here values means values of the ref object). No unnecessary extra memory is occupied.
No unnecessary return. Thats the right approch.
I was trying something, and I came across this interesting scenario. I just wanted to understand what is the major difference in these two snippets.
Initially, I took two sets, initialized one and assigned the reference to other. When I cleared set A, I noticed that Set B size changed to zero as well
Set<String> A = new HashSet<String>();
A.add("hello");
A.add("HEY");
A.add("hey");
Set<String > B = A;
System.out.println("initial sizes :" + A.size() + " " + B.size());
A.clear();
System.out.println("final sizes :" + A.size() + " " + B.size());
The output of this was something like this :
initial sizes :3 3
final sizes :0 0
Now, I tried to depict the same behavior for objects as follows:
Object a1 = new Object();
Object b1 = a1;
System.out.println("initial sizes :" + b1.toString() + " " + a1.toString());
a1 = null;
System.out.println("initial sizes :" + b1.toString() + " " + a1);
The output for this was something like :
initial sizes :java.lang.Object#54182d86 java.lang.Object#54182d86
initial sizes :java.lang.Object#54182d86 null
What is the exact difference here, I was expecting to get a NullPointerException when i tried to print b1.toString()
For primitives such as int, double, float etc, a copy of the value is made and that is passed by value:-
int x = 10;
public void foo(int k){
}
foo(x)
Here k will get a copy of the value stored in x so k will now have a value of 10. However, x and y are in two different memory locations. Changing the value of x will not change the value of k.
For object references a copy of the reference is made and that is passed by value (a reference is nothing more than the address of some memory). So in essence both references will now point to the same object (that is, the same memory location).
Myobject m = new Myobject();
public void bar (Myobject j){
}
bar(m)
A copy of the value of the reference m will be made and assigned to j. Both m and j will now point to the same object.
The difference here, is that a1 and b1 are not the objects themselves but references to those objects. So, if you modify the object referenced by a1 the object referenced by b1 (which is the same object) will change too. If however you tell a1 to point to another instance (or null in this case) it will no longer reference the same object so changes to that won't effect b1.
To go into a little more detail: Java is pass by value. However when you try to pass an object (rather than a primitive value) you're actually passing the value of the reference (also sometimes called the handle). That's why it can sometimes be a bit confusing when trying to determine whether Java is pass by handle or pass by reference.
Check this image. A2/A3 are REFERENCES to bojects. In first case these are references to Set (a1 on image). When one reference modifies object second reference sees the same change
On the other hand if you just set reference = null then you "erase one arrow" from the picture. The reference stops pointing to object, but the other reference still points to it.
You just noticed that java is completly pass by value, in which even references to other objects are passed as value.
If you null an object you are actually not nulling objects, which methods would be executed on the same object. You are only nulling the value of the reference to the object.
Check this example, you are just nulling the value of the reference on list 1, while you are still able to execute the methods on the original list.
ArrayList<Integer> list1 = new ArrayList<Integer>(0);
list1.add(1);
list1.add(2);
ArrayList<Integer> list2 = list1;
list1.clear();
list1 = null;
System.out.println(list2.size());
System.out.println(list1.size()); // will cause an nullpointerexception
The calls of methods on list1 does also effect list2, but nulling the object list1 wont affect the list 1
My answer is an additional element to #I.K. answer. Primitives (copy of value) and Non-Primitives (copy of reference values) - that's the key understanding.
Look at this code where I have put the comments (hosted on IDEONE):
import java.io.*;
/* Name of the class has to be "Main" only if the class is public. */
class Ideone
{
public static void main (String[] args) throws java.lang.Exception
{
String a1 = "blablabla";
String b1 = a1;
System.out.println(b1);
System.out.println(a1);
System.out.println(b1.hashCode());
System.out.println(a1.hashCode());
a1 = "wow";
System.out.println(b1.hashCode());
System.out.println(a1.hashCode());
System.out.println(b1);
System.out.println(a1);
Set<String> a = new HashSet<String>();
Set<String> b = a;
a.add("hey");
a.add("HellO");
System.out.println(b.size());
System.out.println(b.hashCode()); // e.g. 69712814
System.out.println(a.hashCode()); // same - 69712814 (It's important to know)
a.clear();
System.out.println(a.size()); // same hashcode i.e. gets affected by the change.
System.out.println(b.size());
You can see that they are effectively hashed using the same code.
String class is a bit special in Java as you probably know that they use the String pool for "Intern"ing the values. If you run the code above you can see that as soon as you do a1 = "wow"; it will create a new value "wow" in the pool and therefore, the hashCode() changes.
The problem is on my second switch case 1: I will put the "arrayEmployees[0]." but it doesn't see my methods in the superclass PersonData or the subclass personLocation. My understanding of polymorhpism is a bit shady as well as the internal "Object" possibility as I just began learning about these so perhaps I am referencing them wrong.
I was given these instructions:
Design a new class called PersonTest with a main method that defines a PersonData object and a PersonLocation object (both without arguments) and two more objects with arguments and store all the objects in an Array for retrieval and modification of instantiated objects (i.e. Array of Objects).
My Actual Code
package lab5;
import java.util.InputMismatchException;
import java.util.Scanner;
public class PersonTest
{
public static void main(String args[])
{
Scanner input = new Scanner(System.in);
PersonLocation personLocation = new PersonLocation();
PersonData personData = new PersonData();
PersonLocation personLocationOverLoaded = new PersonLocation("Hamilton");
PersonData personDataOverloaded = new PersonData("Stirling", "905-567-7656");
Object[] arrayEmployees = new Object[4];
arrayEmployees[0] = personLocation;
arrayEmployees[1] = personLocationOverLoaded;
arrayEmployees[2] = personData;
arrayEmployees[3] = personDataOverloaded;
int user = 0;
int menu = 0;
// Get input here, put into variable "user"
switch(user)
{
case 1:
System.out.print("Printing Object Information With Given Values\n\n\t");
arrayEmployees[0]. //Issue
}
}//End Main Method
}//End Class PersonTest
What is Supposed to Happen: I am suppose to be able to reference from my array as shown above (arrayEmployees[0].) and have my methods show up for that particular class.
Object[] arrayEmployees = new Object[4];
arrayEmployees[0] = personLocation;
arrayEmployees[1] = personLocationOverLoaded;
arrayEmployees[2] = personData;
arrayEmployees[3] = personDataOverloaded;
This did exactly what you wanted to do, actually. This is an array that contains objects of both a given type and its superclass.
However, by doing so, you lose some information, as you noticed.
When you create an Object[], you are telling the compiler that "This is an array of Objects". Thus, when you go to retrieve an element of this array, all the compiler knows is that "This array contains Objects". It does not know that the first two elements are PersonLocation instances, and does not know that the last two elements are personData elements. It just knows that the array contains Objects.
This is a fundamental limitation of how collections in Java work in general. Collections in Java have one "overall" type, as in you'll always have a "Collection of Numbers", "array of PersonDatas", "ArrayList of Strings", etc., and not "Collection of Integers and Doubles`" So no matter the actual type of whatever's inside, all the compiler knows that the type of the object you'll get out of the collection is the type of that collection.
For example, say I have List<Number> list = new ArrayList<Number>();. All the compiler knows is that the contents are Numbers. It doesn't know if the first element is an Integer, Double, Long, etc. It's just a Number. And because of that, you can't use a Long-specific method like list.get(0).longValue(), because the compiler can't guarantee that the first element is an Integer. It just knows that list.get(0) returns a Number, and that Numbers don't have a longValue() method.
So how do you solve this?
You have a few options.
Use the instanceof operator to test arrayEmployees[0] for its actual type, then cast as needed and perform desired methods. This is more awkward, but if you must have a single array, you really don't have much of a choice.
Use a separate array for each class, so no information is lost.
Given the assignment you need to do, it seems that multiple arrays aren't an option, as the instructions specify a single array, so you might need to do the test and cast. Here's the general idea:
<variable> instanceof <type>
tests for whether variable is-a <type>. For example:
arrayEmployees[0] instanceof PersonLocation
tests to see if arrayEmployees[0] is a PersonLocation.
This test returns a boolean, so you can use that as the condition for an if statement. Inside that if statement, you can downcast arrayEmployees[0] into a temporary reference:
if (arrayEmployees[0] instanceof PersonLocation) {
PersonLocation temp = (PersonLocation) arrayEmployees[0];
// execute whatever you need on temp
}
Just be warned -- instanceOf will also match subclasses. So both x instanceof Integer and x instanceof Number will both return true if x is declared as Integer x = 1;.
Hopefully that's enough to get you started. Feel free to ask any questions.
I've written a function which. Problem is, the parameters I'm sending, is being manipulated in the main program, though it is not my intention. I just want to have the value inside the function, but while operating, the actual value in the main program is also being changed.
How can I prevent this?
Here is my code:
Tiles[][] MoveRight(Tiles[][] tilesArray) {
Tiles[][] tempTilesArray = new Tiles[3][3];
Tiles[][] tempTilesArrayToSend = new Tiles[3][3];
tempTilesArrayToSend = CopyTilesArrays(tilesArray, tempTilesArrayToSend);
ArrayIndex zeroPos = FindZero(tilesArray);
Tiles zeroTile = GetTile(zeroPos, tilesArray);
if (zeroPos.column != 2) {
ArrayIndex otherPos = new ArrayIndex(zeroPos.row,
zeroPos.column + 1);
tempTilesArray = SwapTilesPositions(zeroTile, GetTile(otherPos,
tilesArray), tempTilesArrayToSend);
}
return tempTilesArray;
}
The array I'm sending inside the SwapPositionFunction is actually modifying the tilesArray itself. Though I've made a new instance of tiles array and then sent it.
Without seeing what is done in
CopyTilesArrays (tilesArray, tempTilesArrayToSend);
we can not say much.
Note, that in Java, there is no pass-by-value or pass-by-reference, but a copy of the reference is passed to the methods. This copy of a reference will - in case of objects and Arrays - point to the same, original object, so if you change the underlying/embedded object, the original object is affected, but if you change the reference, the original object is not affected.
IF you want to pass an independent copy of your array, you have to perform a deep ocpy. Maybe that is, what CopyTilesArrays is supposed to do, but without seeing it, we don't know.
Note too, that there are, or better: that there can be several layers of objects, with different reasons to stay on the surface, to go to the core, or to stay somewhere in between.
For example, to make a deep copy from the Array of Array of Tiles, you could do something like this:
public class TilesCopy {
Tiles[][] copyTilesArrays (Tiles[][] from, int outer, int inner) {
Tiles[][] to = new Tiles[outer][inner];
int o = 0;
for (Tiles [] tiles: from) {
Tiles[] fresh = new Tiles [inner];
int i = 0;
for (Tiles t : tiles)
{
fresh[i] = t.deepCopy ();
i++;
}
to [o] = fresh;
o++;
}
return to;
}
}
Note, that in the innermost loop, the elements aren't just referenced with fresh[i] = t;, but with a deep copy, to keep the objects in the original Array unaffected.
You could copy an array of arrays of Tiles in multiple other ways. For example, you could rearrange the outer array. If the Tiles were
[[A][B][C]]
[[D][E][F]]
[[G][H][I]]
you could copy them, and modify the target to be:
[[G][H][I]]
[[D][E][F]]
[[A][B][C]]
with just copying the outer arrays, and rearranging them. And you could copy the inner arrays, to be:
[[C][B][A]]
[[F][E][D]]
[[I][H][G]]
If you now modify the A to a, the original A will be affected too, without a deep copy:
[[C][B][a]]
[[F][E][D]]
[[I][H][G]]
[[a][B][C]]
[[D][E][F]]
[[G][H][I]]
I wish to confirm which scenario will cause a Garbage Collection on the object myObj:
Scenario 1
ArrayList arList = new ArrayList();
while(someCondition)
{
myObj = new MyObect(); // a custom object
arList.add(myObj);
}
Scenario 2
ArrayList arList = new ArrayList();
while(someCondition)
{
myObj = new MyObect(); // a custom object
arList.add(myObj);
myObj=null;
}
Does explicitly setting an object as null improves garbage collection behavior or will be it same when I reset the object using the new constructor ?
You don't specify the scope of myObj and its important. If its a local variable it almost certainly doesn't matter. If its an instance variable then that could be a long-lived and unnecessary reference in which case setting to null will be useful.
Update: given the updated information that myObj is local to the method, it will be of zero value to set it to null at the end of each iteration of the loop. Consider this example:
public void process(String text) {
String[] lines = text.split("\n");
List<MyObject> list = new ArrayList<MyObject>();
Object myObj;
for (String line : lines) {
myObj = new MyObject(line);
list.add(myObj);
// 1. set myObj = null here
}
list = null; // 2
// 3. do some other stuff
}
public class MyObject {
private final String line;
public MyObject(String line) {
this.line = line;
}
}
Now in this example, let's say that at step 3, it took a long time. Say 10 minutes. During that 10 minutes myObj is pointing to the last line processed. Doesn't sound like a problem? Well it could be. The way substrings work in Java is that they reference the original string. So if you do:
String s = ... // 100 megabytes
String s2 = s.substring(100, 101);
you're actually keeping the entire 100MB in memory because s2 references s.
So in the function I have above, myObj references a line which references the entire file. Changing step 1 to myObj = null; would actually help that because this reference is preventing the object being garbage collected.
Note: step 2 is important here because if you didn't nullify the list all the references would exist anyway.
You just need to think about how references work. An object won't be garbage collected while a reference to it exists. This means clearing long-lived references and keeping variables scoped as tightly as possible. The correct solution for the above is:
for (String line : lines) {
Object myObj = new MyObject(line);
...
}
and then myObj is scoped inside the loop so as soon as the loop ends or another iteration begins it has gone out of scope, which is much better.
Setting it to null will have no effect, since the object is still reachable via arList.
That is, your MyObect instances will live at least as long as arList.
EDIT: Based on your comment, it does sound like myObj is longer-lived. In that case, set it to null after the end of your loop.
I think that this is the root of your misunderstanding.
hmm.. but I don't wish to keep 2 copies of myObj , one in arList and one in the original variable. How can I flush myObj once I add it to arLsit ?
You do NOT "keep two copies of myObj". In your examples, there is only ever one "copy" of each MyObject instance created by the loop. The sequence is:
You create a MyObject instance, assigning its reference to myObj.
You add the reference to the instance to the ArrayList that arList refers to.
You assign null to the reference in myObj.
Note that adding the reference to the list does NOT create a copy of the MyObject instance. It simply means that that you have the reference in two places instead of
one. And when you assign the null you once again have the reference in just one place.
The other thing to note is that assigning null to something will never CAUSE the garbage collector to run. All it does is to (explicitly) remove a potential copy of a reference from consideration the next time the garbage collector is run.
Finally, if we assume that the scoping is as follows, then the line C will have no discernible effect ... unless either line A or line B triggers a garbage collection.
{
MyObject myObj;
ArrayList arList = new ArrayList();
while (someCondition) { // A
myObj = new MyObect(); // B
arList.add(myObj);
myObj = null; // C
}
}
Because it is in a while, myObj is always overwritten (the reference). So in Scenario 1 only one object (the last added in arList) will not be null.
It would be better if you declare it in the while statement:
while(someCondition)
{
MyObect myObj = new MyObect(); // a custom object
arList.add(myObj);
}