I have used linked lists before with Strings, doubles, etc., and they always worked exactly as expected. But now I am forming a linked list of items, and whenever I add a new element to the list, all objects in the list apparently become equal to the last object.
The essential code is as below:
import java.util.*;
public class Global
{
static public LinkedList<StockInfo> DiaryStocks = new LinkedList<StockInfo>();
static public class StockInfo //info related to each stock in diary
{
String recordDate;
String ticker;
int status;
String buyDate;
String sellDate;
double buyPrice;
double sellPrice;
double nmbrShares;
}//StockInfo
//The following function places the Diary data for a stock in the arraylist
static public void AddDiaryData(StockInfo thisdata)
{
String tckr;
int i;
DiaryStocks.add(thisdata);
for (i = 0; i < DiaryStocks.size(); i++) //this is debug code
{
tckr = DiaryStocks.get(i).ticker;
}
}
}
As I said, when single stepping through the debug code near the bottom, each time I add a new item to the list, the list size grows as it should, but the tckr item only corresponds to the last item added.
Any insights into this puzzle would be greatly appreciated.
John Doner
The problem is outside the code your provide. It is most likely that you are adding the same instance of StockInfo. Perhaps you have something like:
StockInfo info = new StockInfo();
for (...) {
info.setFoo(..);
info.setBar(..);
AddDiaryData(info);
}
You should not reuse instances like that. You should create a new instance each time.
As a sidenote - method names in Java should start with lowercase letter.
From the symptoms you are describing, it seems as if you are always adding a reference to the same StockInfo object instance to your list, rather than a reference to a new copy each time.
When that object is updated with the contents of the new entry, all list entries appear to change to reflect that latest entry.
This problem lies outside the code snippet that you posted, perhaps in the caller of the AddDiaryData method.
Ooops.
Deep Copy please search it
DiaryStocks.add(thisdata);
you should create new StockInfo() then add to the list otherwise you add the reference and it equalize all the reference of items to the last one
Related
I have three lists in my program.
ONE A global list of all items. ALL
TWO A list showing all items that are available to the user. VISIBLE
THREE A list of all items currently visible on the screen. SHOWING
The lists were declared as so:
ONE = external source
TWO = ONE
THREE = ONE
I have this issue when responding to the user filtering specific items. The function will iterate over THREE and List.remove(index) items that match the filtered item. The function does not interact with TWO. Despite this TWO is modified to be equivalent to THREE despite the initial declarations not being run again (I checked with the debugger and can't work out when TWO is ever modified).
I tried changing TWO to be a Collections.unmodifiableList() but it was still modified?
I fixed the issue with some serious jankiness.
Object[] temp = new Object[ONE.size()];
temp=ONE.toArray(temp);
TWO = Arrays.asList(temp);
Awful I know... It works because Arrays.asList is immutable but surely the unmodifiable list is also immutable?
Added Code for those requesting.
Initialisation Code:
protected void onPostExecute(List<SpaceObject> visibleObjects){
hideLoading();
SpaceObject[] atom = new SpaceObject[visibleObjects.size()];
atom=visibleObjects.toArray(atom);
VISIBLE_OBJECTS = Arrays.asList(atom);
SHOWING_OBJECTS = visibleObjects;
addToView(visibleObjects);
}
Responding to removing or adding items.
private void showSpecificItems(String itemType, Boolean remove){
int i = 0;
if (remove) {
while (i < SHOWING_OBJECTS.size()){
if(SHOWING_OBJECTS.get(i).getType().toLowerCase().matches(itemType.toLowerCase())){
SHOWING_OBJECTS.remove(i);
}else{
i++;
}
}
}else{
System.out.println("remove");
while(i < VISIBLE_OBJECTS.size()){
if(VISIBLE_OBJECTS.get(i).getType().toLowerCase().matches(itemType.toLowerCase())){
SHOWING_OBJECTS.add(VISIBLE_OBJECTS.get(i));
}
i++;
}
SHOWING_OBJECTS = sortBrightest(SHOWING_OBJECTS);
}
addToView(SHOWING_OBJECTS);
}
If you have code like this
List<Object> ONE = someMagic();
List<Object> TWO = ONE;
List<Object> THREE = ONE;
Then you have three things all pointing at the same list.
If you want them to be different, you could try something like this:
List<Object> ONE = someMagic();
List<Object> TWO = ONE.clone();
List<Object> THREE = ONE.clone();
This way they make actual different lists.
protected void onPostExecute(List<SpaceObject> visibleObjects){
hideLoading();
// this line is useless - you allocate an array only to immediately toss it away?!
SpaceObject[] atom = new SpaceObject[visibleObjects.size()];
atom=visibleObjects.toArray(atom);
// this uses atom as its backing array, which comes from visibleObjects
VISIBLE_OBJECTS = Arrays.asList(atom);
// this obviously atom as a backing array
SHOWING_OBJECTS = visibleObjects;
addToView(visibleObjects);
}
So to fix this, you need to remove the dependency on that backing array
protected void onPostExecute(List<SpaceObject> visibleObjects){
hideLoading();
VISIBLE_OBJECTS = visibleObjects.clone();
SHOWING_OBJECTS = visibleObjects.clone();
addToView(visibleObjects); // probably bad form, but I doubt this will keep a reference, so it's "acceptable"
}
if clone is not available for you, you can do it a little more complicated
protected void onPostExecute(List<SpaceObject> visibleObjects){
hideLoading();
VISIBLE_OBJECTS = new ArrayList(visibleObjects);
SHOWING_OBJECTS = new ArrayList(visibleObjects);
addToView(visibleObjects); // probably bad form, but I doubt this will keep a reference, so it's "acceptable"
}
I tried changing TWO to be a Collections.unmodifiableList() but it was still modified?
The javadocs say this about unmodifiable views as created by that method:
An unmodifiable view collection is a collection that is unmodifiable and that is also a view onto a backing collection. Its mutator methods throw UnsupportedOperationException, as described above, while reading and querying methods are delegated to the backing collection. The effect is to provide read-only access to the backing collection. ...
Note that changes to the backing collection might still be possible, and if they occur, they are visible through the unmodifiable view. Thus, an unmodifiable view collection is not necessarily immutable.
In short, unmodifiableList() does not have the properties that you expect. If you want an immutable snapshot of a mutable list, you need to copy it.
Background
I am trying to create an immutable object that contains a list of immutable objects, as well as object type totals within the list.
I created a slightly sudo gist to try and show what I mean.
Gist - Adjusting an immutable object that contains an immutable object list.
Explanation
My example shows how I'm currently doing it, it does work. However not for all cases.
My VeggieCartView will have a recyclerview that gets filled with a new/saved VeggieCart.
Each VeggieCart has a list of veggies. Veggie totals etc...
I then have a helper class VeggieChanger, it contains an rx.Consumer<Veggie[]> that gets set and accepts any 1:1 veggie changes from the veggie views.
The VeggieCartView sets the consumer so when any one veggie changes, it creates an updated cart using the changes' corresponding cart factory method. The adapter is used to change/retrieve its list.
Working and not
This works well for changing one at a time, however batching changes is throwing concurrency exceptions.
I realize my gist is not runnable and doesn't show most boilerplate, and that I may be fundamentally wrong with some or all of my approaches. With that said I still hope someone can give me advice on how to better implement what I'm trying to do.
If more information is needed to understand, please ask. Thank you for anybody who does have help to offer,
Jon.
I ended up figuring out my main issue.
By adding this to my VeggieCartView:
public void bagAllCanned() {
final Veggie[] canned = new Veggie[cart.canTotal()];
final Veggie[] bagged = new Veggie[canned.length];
int t = 0;
final List<Veggie> veggies = cart.veggies();
for (int i = 0; i < veggies.size(); i++) {
final Veggie veggie = veggies.get(i);
if (veggie.canned()) {
canned[t] = veggie;
if (veggie instanceof Potato)
bagged[t] = Potato.can(veggie);
else if (veggie instanceof Tomato)
bagged[t] = Tomato.can(veggie);
t++;
}
}
for (int i = 0; i < canned.length; i++) {
veggieChange(canned[i], bagged[i]);
}
}
It fixes the concurrency errors.
I'm still unsure if my approach is correct or not. So even though the question is mostly answered, opinions are still VERY welcome.
I'm POSITIVE that my title for this topic is not appropriate. Let me explain. The purpose of this is to duplicate a "Profile" application, where I have a profile and so would you. We both have our own followers and in this example, we both follow each other. What this method is needed to return is a cross reference based on whom you follow that I do not. I need this method to return to me a recommended Profile object that I do not already have in my array. Right now I'm having a difficult time with one line of code within a particular method.
One of my classes is a Set class that implements a SetInterface (provided by my professor) and also my Profile class that implements a ProfileInterface which was also provided. In my code for the Profile class, I have the following object: private Set<ProfileInterface> followBag = new Set<ProfileInterface>(); which utilizes the Array bag methods from my Set class with the ProfileInterface methods I've made.
Here is the method (not complete but can't move further without my problem being explained):
public ProfileInterface recommend(){
Set<ProfileInterface> recommended;
ProfileInterface thisProfile = new Profile();
for(int index = 0; index < followBag.getCurrentSize(); index++){
Set<ProfileInterface> follows = followBag[index].toArray();
for(int followedFollowers = 0; followedFollowers < follows.getCurrentSize(); followedFollowers++) {
if()
//if Profile's do not match, set recommended == the Profile
}
}
return recommended;
}
The purpose of this method is to parse through an array (Profile as this example) and then take each of those sub-Profiles and do a similar action. The reason for this much like "Twitter", "Facebook", or "LinkedIn"; where each Profile has followers. This method is meant to look through the highest Profiles follows and see if those subProfiles have any followers that aren't being followed by the highest one. This method is then meant to return that Profile as a recommended one to be followed. This is my first dealing with Array Bag data structures, as well as with generics. Through "IntelliJ", I'm receiving errors with the line Set<ProfileInterface> follows = followBag[index].toArray();. Let me explain the reason for this line. What I'm trying to do is take "my" profile (in this example), and see who I'm following. For each followed profile (or followBag[index]) I wish to see if followBag[index][index] == followBag[index] and continue to parse the array to see if it matches. But, due to my confusion with generics and array bag data structures, I'm having major difficulties figuring this out.
I'd like to do the following:
//for all of my followers
//look at a particular followed profile
//look at all of that profile's followers
//if they match one of my followers, do nothing
//else
//if they don't match, recommend that profile
//return that profile or null
My problem is that I do not know how to appropriately create an object of a Profile type that will allow me to return this object
(in my method above, the line Set<ProfileInterface> follows = followBag[index].toArray();)
I'm trying to make an index of my Profile set to an object that can later be compared where my difficulties are. I'd really appreciate any insight into how this should be done.
Much appreciated for all help and Cheers!
When you do:
Set<ProfileInterface> follows = followBag[index].toArray();
you're trying to use Set as Array. But you can't.
Java will not allow, because Set and Array are different classes, and Set does not support [] syntax.
That is why you get error. For usefollowBag as Array you have to convert it:
ProfileInterface[] profileArray = followBag.toArray(new ProfileInterface[followBag.size()]);
for(int i=0; i<profileArray.length; i++){
ProfileInterface profile = profileArray[i];
//do what you would like to do with array item
}
I believe, in your case, you don't need assign Set object to generic Array at all. Because you can enumerate Set as is.
public class Profile {
private Set<ProfileInterface> followBag = new HashSet<Profile>();
...
public Set<ProfileInterface> recommended(){
Set<ProfileInterface> recommendSet = new HashSet<ProfileInterface>();
for(Profile follower : followBag){
for(Profile subfollower : follower.followBag){
if(!this.followBag.contains(subfollower)){
recommendSet.add(subfollower);
}
}
}
return recommendSet;
}
}
I also added possibility of returning list of recommended profiles, because there is may be several.
I am a poor programmer but I need some help for an app I have been procrastinating to build. (note sorry for the lack of detail on the first try
I have created an ArrayList of myObjects that have their own properties. When I created the myObject class I created an initializer so that I could add in myObject into an ArrayList of them. I got it working but I am having problems as the properties of the objects are being overwritten as I loop through my code. Here is a simplified example:
myOjbect newMyObject = new myObject
List<myOject> listOfObjects = new ArrayList<myObjet>();
try {
// go through a text file, set some properties of my object...
myArrayValue = some text input //(sorry i didnt want to put the whole code as its sloppy, but it does return an array)
myObject.matrix = myArrayValue; // this value changes as I go through the text file, but in the listOfObects, only the last value is saved to each item in the list
SetStartDate(somestring1); // another constructor/initializer (sorry i forget the correct terminology) I added to the 'myObject' class. This property sets correctly in the list
listOfObjects.add(new myObject(newMyObject));
Then in my class of myObject is have this initializer:
public myObject(myObject other){
matrix = other.matrix;
startDate = other.startDate;
// TODO add all the properties here, so that they get copied
}
public SetStartDate(string inputText){
startdate = inputText // or something like that, I dont have the code on this computer
}
So the startDate property is working, when I loop through the list of items but when I set the matrix property, I always end up with the last property value in my main script as the property value for each item in the list.
Any ideas why the startDate property works fine but not the matrix (which is an array variable)?
thanks
To copy the array elements instead of saving a reference of the array object, you can do this.
matrix = Arrays.copyOf(other.matrix, other.matrix.length);
I've put together a few methods that are suppose to delete a searched item from an array and the data from the array is also being put into a JTable through a method called createLoginTable().
When my delete button actionListener Method is carried out the element or login is successfully deleted from the array: 'listOfLogins' but the element does not appear to be deleted from the JTable as it is still there.
Here are the methods starting with the actionListener:
if(e.getSource()==deleteLoginButton)
{
int loopNo = list.nextLogin; ///Variables used in the 'removeLogin' Method
String foundLogin = list.listOfLogins[foundLocation].toString();
Login[] loginList = list.listOfLogins;
LoginList list = new LoginList(); //The 'list' is wiped
list.removeLogin(loginList, foundLogin, loopNo);
list.writeLoginsToFile(); //Writes logins to file (not integral to the array)
String[][] loginTableLogins = new String[50][2]; //Wipes the JTable Array
createLoginsTable(); //Creates the JTable
searchLoginButton.setEnabled(true);
editLoginButton.setEnabled(false);
deleteLoginButton.setEnabled(false);
addLoginButton.setEnabled(true);
}
This is the 'removeLogin' Method (This is in a seperate 'list' class):
public void removeLogin(Login[] array, String unwantedLogin, int loop)
{
for(int i=0;i<loop;i++)
{
String currentLogin = array[i].toString();
if(!currentLogin.equals(unwantedLogin))
{
Login login = new Login();
addLogin(array[i]);
}
}
}
plus 'addLogin' Method (although i am assured this is not the source of my issue):
public void addLogin(Login tempLogin)
{
listOfLogins[nextLogin] = tempLogin;
System.out.println(listOfLogins[nextLogin]);
nextLogin++;
System.out.println(nextLogin);
}
And the 'createLoginsTable' method:
public void createLoginsTable()
{
for(int i=0;list.nextLogin>i;i++)
{
loginTableLogins[i] = list.listOfLogins[i].toArray();
System.out.println(list.listOfLogins[i].toString());
}
JTable loginsTable = new JTable(loginTableLogins, loginTableTitles);
JScrollPane loginsScrollPane = new JScrollPane(loginsTable);
loginsScrollPane.setBounds(400, 200, 200, 250);
testPanel.add(loginsScrollPane);
}
I have used 'System.out.println's so I am 99% certain that the element has been removed from the array (it is also apparent through my writeLoginsToFile Method) So I hope this information helps.
Your code is a little bit hard to decipher, next time maybe put in also the enclosing class, or some details about that class. What does the following line do:
LoginList list = new LoginList(); //The 'list' is wiped
You say the list is wiped, I think what is does is: it declares a list local variable and assigns a new object to it (and it masks the other list variable which you used a few lines earlier). Now, in the createLoginsTable() method you don't have this local variable, you have the "list" which I guess is a public field in your class. Now what you can do, is or pass the local list variable to the above function as a parameter createLoginsTable(list) or try the wiping line without the declaration so only:
list = new LoginList(); //The 'list' is wiped
Anyway, your code seams a little bit troubled it, maybe you should refactor it a little bit. Hope it helps.
You're not returning the table after you delete the item.
When you call the method to delete it and write out the table, that table is not returned after you remake the table.
Take this:
JScrollPane loginsScrollPane = new JScrollPane(loginsTable);
Bring it outside of your method. What I think might be happening is when you create your loginsScrollPane locally inside the method, it's not being added properly to your testPanel.
I think what might be happening is when you add it, and the method ends it's loosing that data that is contained. Declare your scrollpane, and your jtable where you declare your frame.