Okay, I have had a question, my code has seemed to work, but I have not tested it particuarly well.
I'm trying to set an element in an ArrayList.
ArrayList<StringBuilder> g=new ArrayList<StringBuilder>();
//set the array contents
g.get(2).append("Something");
I know that doing something like
StringBuilder q=g.get(2);
q.append("something else?");
g.set(2,q);
works, and is probably the right way to do it, but it seems like such long way of doing it.
Am I doing this right, if I'm not, then is the second way I've mentioned the only way?
If you have a list of StringBuilder, which are modified in place, then the first method is fine. If you have a list of some immutable type (like String), then since you cannot change the object that you get out, you have to use a variant of the second method, because the result is not the same object.
e.g.
List<StringBuilder> widgets = new ArrayList<StringBuilder>();
// ...
StringBuilder widget = widgets.get(0);
widget.append(" version 2");
vs
List<String> widgets = new ArrayList<String>();
// ...
String widget = widgets.get(0);
widgets.set(2, widget + " version 2");
When calling List#get() method you are retrieving a reference to your StringBuilder object. StringBuilder is a mutable object. So if you want to modify your StringBuilder's contents at index 2 then you don't need to set a reference again after you modify it. Hence the following is enough:
g.get(2).append("Something");
g.get(2).append("Something"); works if you initialized the list values with StringBuilder instances. Otherwise you will get a NullPointerException.
Note that in your code you define an ArrayList of type StringBuilder but then you try to assign an ArrayList of type Integer to it, which does not work
Related
Pretty new to Java here. I am coming from Python. There are similar questions on SO talking about remove or add element while iterating a Java Set. What I would like to know is to modify the elements containing in the Set. For instance, ["apple", "orange"] to ["iapple", "iorange"]. In addition, I would like to do it in place, i.e., not creating another set and put the modified element into the new set while iterating it.
Apparently a simple for loop doesn't work, as the following:
import java.util.Arrays;
import java.util.Set;
import java.util.HashSet;
class Test {
public static void main (String[] args) {
Set<String> strs = new HashSet<String>(Arrays.asList("apple", "orange"));
for (String str : strs) {
str = "i" + str;
}
}
}
The issue with what you've written is you don't do anything with the computed value. The for-each loop sets the value of str to each element. Within the for loop you are changing the value of str, but not doing anything else with it.
This would be easy to do in a linkedlist or any data structure which supports indexing, but with a set it can be tricky. Just removing the old element and adding the new one will likely screw up the iteration, especially because you're dealing with a hash set.
A simple way to do this is to convert to a list and back:
class Test {
public static void main (String[] args) {
Set<String> strs = new HashSet<String>(Arrays.asList("apple", "orange"));
//Convert to list
LinkedList<String> strsList = new LinkedList<String>();
strsList.addAll(strs);
//Do modification
for (int i = 0; i < strsList.size(); i++) {
String str = strsList.get(i);
strsList.set(i,"i" + str);
}
//Convert back to set
strs.clear();
strs.addAll(strsList);
}
}
This is clearly a bit more work than you would expect, but if mass-replacing is behavior you anticipate then probably don't use a set.
I'm interested to see what other answers pop up as well.
You cannot modify a String in java, they are immutable.
While it is theoretically possible to have mutable elements in a Set and mutate them in place, it is a terrible idea if the mutation effects hashcode (and equals).
So the answer to your specific question is no, you cannot mutate a String value in a Set without removing then adding entries to that Set.
The problem is that in the for loop, str is merely a reference to a String. References, when reassigned, don't change the actual object it refers to. Additionally, strings are immutable anyway, so calling any method on them will give you a new String instead of modifying the original. What you want to do is store all the new Strings somewhere, take out the old ones, and then add the new ones.
EDIT: My original code would have thrown a ConcurrentModificationException.
There are 3 problems in your approach to solve the problem.
1st - You can't modify the contents of a Set while you are iterating it. You would need to create a new Set with the new values.
"But I'm not modifying the set, I am modifying the objects within it", which leads to problem two
2nd - In your code you are modifying a reference to a string, not the string itself.
To modify the string you would need to call a method over it, like string.changeTo("this"), or modify a field, string.value = "new value".
Which leads to problem three.
3rd - Strings are immutable. when you construct a string, say new String("hello"), you can't further modify it's inner value.
The solutions:
First option is the simpler one, create a new set.
The second option is to use string builders instead of strings, which are mutable string creators/placeholders.
http://docs.oracle.com/javase/7/docs/api/java/lang/StringBuilder.html
today i dealt with a Java problem that really confused me.
I have the following code:
List<ObjectXY> someList = obj.getListOfObjectsXY(); // getter returns 2 elements
someList.add(new ObjectXY());
obj.getListOfObjectsXY(); // getter now returns 3 elements
When i add an element to a list, the getter gets some kind of overwritten. Is this because someList acts like a reference on the result of the getter in this case? Or what else causes this effect?
I solved the problem with the following code by using another list:
List<ObjectXY> someList = obj.getListOfObjectsXY(); // result: 2 elements
List<ObjectXY> someOtherList = new ArrayList<ObjectXY>();
someOtherList.addAll(someList);
someOtherList.add(new ObjectXY());
obj.getListOfObjectsXY(); // result: 2 elements
But i am still some kind of confused because i didn't expect Java to behave this way.
Can anyone explain to me what i did wrong and why it is so?
Thanks in advance!
The returned result is indeed just a copy of a reference to the same object as you are using internally. Counting on the caller to not modify the object is error-prone.
One solution is to return a reference to an unmodifiable list wrapping your list. See Collections.unmodifiableList(). The getter caller will be unable to modify your list.
Is this because someList acts like a reference on the result of the
getter in this case?
Yes. The list you received was just a reference to the same, original list you had. Any changes made on this variable would be reflected on the original list.
By adding the list's values to a new list you explicitly constructed a new object and thus they are separated.
In your case, obj.getListOfObjectsXY() everytime return you the same object and in Java object references are pass-by-value. So, when you do a someList.add(new ObjectXY());, it's actually setting the property of the object someList which is poiting to obj.getListOfObjectsXY().
And in the latter case, you are just copying someList to someOtherList. Then you added one more element to the same someOtherList but not to the someList. So, you getting 2 elements in
obj.getListOfObjectsXY(); is perfectly valid.
I've looked around and cannot figure this one out: I have a object that implemens an observer pattern and a List implementation that allows listeners to be added on the list whenever a change event is triggered on any object in the list to avoid manual add/remove listeners to each object int the list.
The problem I have is when creating a new instance of the same List implementation and adding objects from existing lists the object changes are not getting triggered from beans added to the new list. My thought was that when adding an object to a Collection it just adds a pointer to the existing object which given this case the change notifications would be getting triggered on objects in the new list but this is not happening. Can anyone help me figure out what the problem might be? I have seen similar questions but none that can help me solve this problem.
The use case, is a stock scanner where one list has all the stocks in the market I'm watching and the scanner list only has the stocks that pass the criteria but the scanner is not getting updates like price, volume etc that get triggered using the observation pattern. - Duncan
Your understanding was correct; collections hold references to objects. For example, this:
final StringBuilder stringBuilder = new StringBuilder();
final List<StringBuilder> stringBuilderList = new ArrayList<StringBuilder>();
stringBuilderList.add(stringBuilder);
stringBuilderList.add(stringBuilder);
stringBuilder.append("yes");
System.out.println(stringBuilderList);
will print this:
[yes, yes]
because there was only a single StringBuilder instance, so the appended "yes" is in every element of the list.
But note that the collections hold those references by value, not by reference. For example, this:
StringBuilder stringBuilder = new StringBuilder("yes");
final List<StringBuilder> stringBuilderList = new ArrayList<StringBuilder>();
stringBuilderList.add(stringBuilder);
stringBuilder = new StringBuilder("no");
// now stringBuilder refers to a different object than before
stringBuilderList.add(stringBuilder);
System.out.println(stringBuilderList);
will print this:
[yes, no]
because the two elements of the list refer to different objects, even though both objects were identified by the same variable.
For more help in figuring out what's going wrong with your code, I think you'll have to post a minimal program that demonstrates the issue.
By value, always!
When it comes to objects, the value passed is the value of the reference, but not the reference it self.
See most of these links
Languages where pass by reference is supported ( Java doesn't support this ) can perform the following:
Foo foo = new Foo();//create a new object
foo.name("Old foo"); // label it
modify( foo ); // try to modify it
// In a language that supports byRef will print "New foo".
// In Java will print "Old foo" always
println( foo );
...
void modify( Foo foo ) {
foo = new Foo(); // reference assigned a new different object
foo.name("New foo");
}
So, languages that support pass by reference, will put the new object created inside the method to the reference passed to them ( they receive the reference after all ). Languages like C++ and VB can do this..
Languages that doesn't support pass by reference ( like Java ) won't assign the new object to the original reference, Java will assigned it to the copy of the reference ( the one created in the argument passing --> void modify( Foo foo ) { ) But the original one, the one created before the method will remain intact and thus still with Old foo.
As per java spec, everything passed as value in Java. This SO discussion has very good example explanation on how it works. Specifically read second answer.
Yes, in Java every class (but not simple types) is been passed by reference.
To solve your problem, you can use clone, if your objects support it.
Java objects are always passed by reference.
So, unless your Collection's "add" method does something else (like cloning the object or using it as a prototype, etc) everything should be working.
I think your issue is more on the notification's logic than within the collection itself. I suggest you paste some code.
I'm iterating through an ArrayList, modifying the string, and trying to add it to a new list. It doesn't change the original list. Within a foreach loop in Java, is it creating a copy of the object so I can add it?
List<String> newString = new ArrayList<String>();
for (String s : lineOfWords { // lineOfWords is a String that has text in it
s = s.replaceAll("\\b(\\w+)\\b(\\s+\\1)+\\b", "$1");
newString.add(s);
}
Thanks in advance!
EDIT: I don't mean for it to change the original string, but to add the new string, in this case s, to my newString ArrayList.
Yes, Strings are immutable. So with the call to...
s = s.replaceAll("\\b(\\w+)\\b(\\s+\\1)+\\b", "$1");
after this line executes, s is an entirely new String, not the one you started with.
Update
I hope lineOfWords is an array of String objects? Not "a String that has text in it."
Yes, you are creating a new String object each time you call s.replaceAll. You are then assigning that new String object to the temporary variable s, which has no effect on any strings that you have previously added to the List or on any strings in the original List.
You original list is unchanged.
Your "s" variable is local to the loop, on entry to the body loop it refers to the string in the original list, but then the s.replace() is returning a reference to a new String.
Yes as String is immuatable any operation on it that modifies it actually creates a copy of it and in your loop assigning it to 's' will not change the original contents of the list.
You are creating a new string object when you perform the replace operation - as strings in Java are immutable.
You can see for yourself when objects are actually the same or not using the "==" operator, which compares references when used on objects. Since String is an object it has this reference comparison behaviour, but it is sometimes confused due to java interning strings to that in many cases two equal strings of characters actually refer to the same object.
I have a Vector that holds a number of objects. My code uses a loop to add objects to the Vector depending on certain conditions. My question is, when I add the object to the Vector, is the original object reference added to the vector or does the Vector make a new instance of the object and adds that?
For example, in the following code:
private Vector numbersToCalculate;
StringBuffer temp = new StringBuffer();
while(currentBuffer.length() > i) {
//Some other code
numbersToCalculate.add(temp);
temp.setLength(0); //resets the temp StringBuffer
}
What I'm doing is adding the "temp" StringBuffer to the numbersToCalculate Vector. Should I be creating a new StringBuffer within the loop and adding that or will this code work? Thanks for the help!
Eric
You need to create a new StringBuffer each time. Each item item in the Vector is just a pointer to the same StringBuffer object, so each time through the loop you are resetting the single instance of stringbuffer and adding the same reference to the Vector.
Just replace the temp.setLength(0); with temp = new StringBuffer();
If you have to have an independent object added to the Vector, create a new one each time.
You're adding references to the vector. If the state of an object changes, then all references to it see the change.
It uses the same object each time. You should add a temp = new StringBuffer(); to the end of your loop. (The result of your loop will be a Vector of pointers to the same single empty StringBuffer.)
The Vector will store the reference you give it, it won't create its own copy of the object. So if you want the Vector to have buffers separate from the one you're continuing to use, as you said you'll need to create those separately by creating a new buffer instead of setting the old one's length to zero.
Off-topic side note: Vector is fairly out of date. you're probably better off with ArrayList (or one of the other classes implementing List, if you don't need an array backing it).
Inserting an element into a collection does not, and can not, make a copy of an object, because Java has no formalized notion of a copy-constructor or operator overloading for user-defined types. That is, a general purpose collection can not know how to copy the contained objects.
Java's assignment operator always copies the pointer, never the contents, of a user-defined type.
As most of the answers here say, a Vector stores references to objects of type Object. If you change the underlying Object each time you will end up with a Vector containing lots of references to one object, which now contains the last value you gave it.
Based on the name of your variables, I'm guessing you actually want to be storing numbers in your Vector.
In an ideal world you would just add object of type int into the Vector. Unfortunately, in Java an int is a 'primitive type', and Vector can only store objects of type Object. This means you can only put Integer objects into the Vector instead of int objects.
So your code will look something like:
// Put a number at index 0
Integer inputNumber = new Integer(7);
numbersToCalculate.add(0, inputNumber);
// Some time later read out the value at index 0
Object objFromVector = numbersToCalculate.elementAt(0);
// Cast the Object to an Integer
Integer numberToProcess = (Integer)objFromVector;
This code will throw an IllegalCastException if the Vector contains something that isn't an Integer object. If you are worried about that you can encompass it in a try catch statement.
In your example you will presumably want to just loop through all the numbers in the Vector. You also might want to be more prescriptive about what objects your Vector can contain (called 'Generics', which is similar to C templating). Here's what it might look like:
Vector<Integer> myVector = new Vector<Integer>();
// Add stuff to the Vector
for (Integer number : myVector)
{
// Do stuff with the number
}
The foreach and Generics constructs were only added in Java SDK 1.5, so you can't use them if you want to run on an earlier Java SDK.