I'm adding three different objects to an ArrayList, but the list contains three copies of the last object I added.
For example:
for (Foo f : list) {
System.out.println(f.getValue());
}
Expected:
0
1
2
Actual:
2
2
2
What mistake have I made?
Note: this is designed to be a canonical Q&A for the numerous similar issues that arise on this site.
This problem has two typical causes:
Static fields used by the objects you stored in the list
Accidentally adding the same object to the list
Static Fields
If the objects in your list store data in static fields, each object in your list will appear to be the same because they hold the same values. Consider the class below:
public class Foo {
private static int value;
// ^^^^^^------------ - Here's the problem!
public Foo(int value) {
this.value = value;
}
public int getValue() {
return value;
}
}
In that example, there is only one int value which is shared between all instances of Foo because it is declared static. (See "Understanding Class Members" tutorial.)
If you add multiple Foo objects to a list using the code below, each instance will return 3 from a call to getValue():
for (int i = 0; i < 4; i++) {
list.add(new Foo(i));
}
The solution is simple - don't use the static keywords for fields in your class unless you actually want the values shared between every instance of that class.
Adding the Same Object
If you add a temporary variable to a list, you must create a new instance of the object you are adding, each time you loop. Consider the following erroneous code snippet:
List<Foo> list = new ArrayList<Foo>();
Foo tmp = new Foo();
for (int i = 0; i < 3; i++) {
tmp.setValue(i);
list.add(tmp);
}
Here, the tmp object was constructed outside the loop. As a result, the same object instance is being added to the list three times. The instance will hold the value 2, because that was the value passed during the last call to setValue().
To fix this, just move the object construction inside the loop:
List<Foo> list = new ArrayList<Foo>();
for (int i = 0; i < 3; i++) {
Foo tmp = new Foo(); // <-- fresh instance!
tmp.setValue(i);
list.add(tmp);
}
Your problem is with the type static which requires a new initialization every time a loop is iterated. If you are in a loop it is better to keep the concrete initialization inside the loop.
List<Object> objects = new ArrayList<>();
for (int i = 0; i < length_you_want; i++) {
SomeStaticClass myStaticObject = new SomeStaticClass();
myStaticObject.tag = i;
// Do stuff with myStaticObject
objects.add(myStaticClass);
}
Instead of:
List<Object> objects = new ArrayList<>();
SomeStaticClass myStaticObject = new SomeStaticClass();
for (int i = 0; i < length; i++) {
myStaticObject.tag = i;
// Do stuff with myStaticObject
objects.add(myStaticClass);
// This will duplicate the last item "length" times
}
Here tag is a variable in SomeStaticClass to check the validity of the above snippet; you can have some other implementation based on your use case.
Had the same trouble with the calendar instance.
Wrong code:
Calendar myCalendar = Calendar.getInstance();
for (int days = 0; days < daysPerWeek; days++) {
myCalendar.add(Calendar.DAY_OF_YEAR, 1);
// In the next line lies the error
Calendar newCal = myCalendar;
calendarList.add(newCal);
}
You have to create a NEW object of the calendar, which can be done with calendar.clone();
Calendar myCalendar = Calendar.getInstance();
for (int days = 0; days < daysPerWeek; days++) {
myCalendar.add(Calendar.DAY_OF_YEAR, 1);
// RIGHT WAY
Calendar newCal = (Calendar) myCalendar.clone();
calendarList.add(newCal);
}
Every time you add an object to an ArrayList, make sure you add a new object and not already used object. What is happening is that when you add the same 1 copy of object, that same object is added to different positions in an ArrayList. And when you make change to one, because the same copy is added over and over again, all the copies get affected.
For example,
Say you have an ArrayList like this:
ArrayList<Card> list = new ArrayList<Card>();
Card c = new Card();
Now if you add this Card c to list, it will be added no problem. It will be saved at location 0. But, when you save the same Card c in the list, it will be saved at location 1. So remember that you added same 1 object to two different locations in a list. Now if you make a change that Card object c, the objects in a list at location 0 and 1 will also reflect that change, because they are the same object.
One solution would be to make a constructor in Card class, that accepts another Card object. Then in that constructor, you can set the properties like this:
public Card(Card c){
this.property1 = c.getProperty1();
this.property2 = c.getProperty2();
... //add all the properties that you have in this class Card this way
}
And lets say you have the same 1 copy of Card, so at the time of adding a new object, you can do this:
list.add(new Card(nameOfTheCardObjectThatYouWantADifferentCopyOf));
It can also consequence of using the same reference instead of using a new one.
List<Foo> list = new ArrayList<Foo>();
setdata();
......
public void setdata(int i) {
Foo temp = new Foo();
tmp.setValue(i);
list.add(tmp);
}
Instead of:
List<Foo> list = new ArrayList<Foo>();
Foo temp = new Foo();
setdata();
......
public void setdata(int i) {
tmp.setValue(i);
list.add(tmp);
}
Related
I am using two classes in javascript.
class Challenge and class Modules, that extends Challenge.
in Challenge i have this constructor:
constructor() {
this.handles = [];
}
and this method:
bla() {
let elmnts = document.querySelectorAll("p"); // 5x
for(let i=0; i < elmnts.length; i++) {
let elmnt = elmnts[i];
this.handles[elmnt['id']] = elmnt; // saves the handles to each element in separate array for later use
}
}
in the child Class i am trying to use this "this.handles" array, but it is always empty.
It is not "undefined" but it is an empty array as i defined it in the constructor. its like the entries have never been set... but they are (as console.log() shows, when i insert it directly after the for()-loop)
console.log(this.handles); // --> []
Why does this happen?
this.handles is an array. So when you are doing this.handles[elmnt['id']], elmnt['id'] returns a string, whereas an array expects (positive) integer for index. So the elements are not set in the array.
Maybe you meant this.handles to be an object?
class Ex1 {
constructor() {
this.handles = {};
}
bla() {
let elmnts = document.querySelectorAll("p"); // 5x
for (let i = 0; i < elmnts.length; i++) {
let elmnt = elmnts[i];
this.handles[elmnt['id']] = elmnt; // saves the handles to each element in separate array for later use
}
}
}
class Ex2 extends Ex1 {
constructor(){
super();
}
m1(){
console.log(this.handles);
}
}
let p1 = new Ex2();
p1.m1();
p1.bla();
p1.m1();
<p id="one">1</p>
<p id="two">2</p>
<p id="three">3</p>
<p id="four">4</p>
<p id="five">5</p>
You need to understand the following things:
In Javascript...
... Arrays are always index-based (0 pointing to the first array element).
... you append elements to an array using arr.push(el).
... Arrays are also objects, so adding arbitrary properties also works (which is what you are doing by using the p element id as a key and assigning it the element reference). See this example:
If you need an array (which preserves order and can easily be iterated later on), just spread the NodeList you got from querySelectorAll('p') into the array using the ES6 spread syntax ...:
this.handles = [...document.querySelectorAll("p")];
I am trying to be able to make a list of all instances of a Constructor, so I can check if all of these fit criteria in an IF statement
For Example:
function People() {
People.allInstances = [];
People.allInstances.push(this);
}
var Carry = new People();
var Gary = new People();
var Parry = new People();
console.log(People.allInstances);
However, I seem to lose all data except for the last instance I created. How can I make that list/array, and then use that array to test if any of them has a certain property?
The constructor runs every time an instance is constructed with it, but you only want to create an empty array once:
function People() {
People.allInstances.push(this);
}
People.allInstances = [];
In the following code, I create a car object and modify an element by increasing the count. Each time I modify the car, I push it onto an array called yada. At the end, I access the 3rd car that gets put onto the yada array to see what the count is, but it reports a count of 6 instead of what I expected to be a count of 3.
<html>
<body>
<script>
var car = {limit:4, count:0, trigger:0};
var yada = new Array();
for(var i=0; i<6; i++)
{
car.count += 1;
if(car.count >= car.limit)
{
car.trigger = 1;
}
yada.push(car);
alert(car.count+" "+car.limit+" "+car.trigger);
};
alert(yada[2].count + " TEST ");
</script>
</body>
</html>
It is important to understand the difference between pass by value and pass by reference: Is JavaScript a pass-by-reference or pass-by-value language?.
Javascript's default is always pass by value, but for objects the value of the variable is a reference. Because of this, when you pass an object and change its members, those changes persist outside of the function.
You could adapt by making a new car object within your for loop:
<html>
<body>
<script>
var yada = new Array();
for(var i=0; i<6; i++)
{
let car = {limit:4, count:0, trigger:0};
car.count = i+1;
if(car.count >= car.limit)
{
car.trigger = 1;
}
yada.push(car);
alert(car.count+" "+car.limit+" "+car.trigger);
};
alert(yada[2].count + " TEST ");
</script>
</body>
</html>
Because you declare the car a local variable, it doesn't persist outside the loop and you create a new one every looprun that you can then store in your array.
This works for me.
There is only one car object which you push everytime into your array, so all 6 entries in yada point to the same car object, and change in any one of them is reflected across all the "pointers" or array entries.
You loop through and increment car.count 6 times and when you are done with the loop you access the count of car in the array, which points to the same car object which has now car.count equal to 6 and that's what you see. No matter which array index you access, you will get the same result.
Perhaps, you want to create multiple car objects, have a look at https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/new
Your constructor function could look something like this:
function Car() {
this.limit = 4;
this.count = 0;
this.trigger = 0;
}
Then, to create a new car object you can just say :
var myCar = new Car();
There is only one car with multiple references to it in your array. You need to create a new object from the current car.
var car = {limit:4, count:0, trigger:0};
var yada = new Array();
for(var i=0; i<6; i++)
{
car.count += 1;
if(car.count >= car.limit)
{
car.trigger = 1;
}
yada.push(Object.assign({},car) );
};
console.log(yada);
I am trying to create a array with multiple fields in it.
For Example:
var person1 = {firstname:"Bob", lastname:"Smith", middlename:"happy"};
The problem I have is that I have 5000 variables I want to create so it would become:
var person1 = {firstname:"Bob", lastname:"Smith", middlename:"happy"};
var person2 = {firstname:"John", lastname:"Jones", middlename:"Long"};
..
var person5000 = {firstname:"Jim", lastname:"Cook", middlename:"Shorty"};
I think it would be silly to have 5000 lines of code to declare the variables.
So I want to be able to declare the variables on page load and then later assign the values to each.
I was trying to do this using the following code but I am guessing I am doing something wrong.
(I am loading some dummy data into the variables for testing)
<!DOCTYPE html>
<html>
<body>
<script>
var person = new Array (firstName:"", lastName:"", middleName:"");
for (var i = 0; i < 5000; ++i) {
person[i] = {firstName:"First"+i, lastName:"Last"+i, middlename:"Middle"+i};
}
alert(person1["firstName"]); // should alert First1
alert(person6["lastname"]); // should alert Last6
</script>
</body>
</html>
I was hoping to later in my code set the value using:
(I am pretty sure this code should work, but can't test it since I can't declare the variables correctly)
person1[firstname] = "Terry"; // should replace First1 with Terry
And then to receive a value using:
alert(person1[firstname]); // should alert Terry since it was changed
Anyone know what is wrong with my code since it's not returning the value ?
I am guessing I am declaring the variables wrong? If so how should I declare them ?
You appear to be confused about the difference between arrays and objects in Javascript. Arrays have numeric indexes, objects have named properties. So the initialization
new Array(firstName:"", lastName:"", middleName:"");
makes no sense. Not to mention, it's not valid Javascript syntax; property: value pairs can only be used in object literals, not in argument lists. If you use new Array(...), the argument should either be a single number, which is the size of the array to allocate, or a list of initial array element (with no property: prefixes. But the preferred way to create a new array is simply with the [] literal for an empty array; it will grow as necessary when you assign to it.
When you create an array, you don't get separate variables for each element. You access them using array[n] notation.
// Create an empty array
var person = [];
// Fill up the array
for (var i = 0; i < 5000; ++i) {
person[i] = {firstName:"First"+i, lastName:"Last"+i, middlename:"Middle"+i};
}
// Access elements
alert(person[1].firstName);
alert(person[6].middleName);
// Change elements
person[1].firstName = "Terry";
I believe this should work as you intended:
var person = new Array();
for (var i = 0; i < 5000; ++i) {
person[i] = {firstName:"First"+i, lastName:"Last"+i, middleName:"Middle"+i};
}
alert(person[1]["firstName"]);
alert(person[6]["lastName"]);
As pointed out by others, the person array is filled with objects, not arrays. You can use either property or associative array syntax with them.
Is there a way to define array size inside objects in Java Script?
Like we do in C or C++, when we want user to enter values in array, we define a size and populate it using indexes according to user. Display is done using something like n[4] (assuming n is an array.)
function activity(id, name, description, prior, post, start, end, current, status) {
this.id = id; //activity id
this.name = name; //activity name
this.description = description; //activity description
**this.prior = prior[]; // prior activities
this.post = post[]; //post activities**
this.start = start; //activity start date
this.end = end; //activity end date
this.currentWork = currentWork; //current work that has been done
this.status = status; //current status
}
I want prior and post to be arrays of size 3. I am making 18 instances of above object, each one with different value. Tell me how to access these values how to enter values in this array?
You can create an array of a specified size with:
this.prior = new Array(3);
However, Javascript arrays are not a fixed size -- if you assign beyond the end, it will automatically grow. So normally just just create an empty array:
this.prior = [];
and then assign elements or use this.prior.push(newElement) to add to it.
see this
array in javascript grows dynamically so according to that post you can just store the length in some variable and use it as for looping. like this
var data = [];
var arrlength = 10 ; // user-defined length
for(var i = 0; i < arrlength; i++) {
}