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.
Related
I have a question about the difference between Arraylist.forEach() and Arraylist.replaceAll(). I know that Arraylist.forEach() is used to perform an action for each element in the list, but it seems that Arraylist.replaceAll() does the exact same thing. But for some reason, when you decide to do a.forEach(e -> e.concat("test"); assuming a is an Arraylist of Strings, the Arraylist does not concatenate "test" to the end of each String in the Arraylist. However, if you do a.replaceAll(e -> e.concat("test");, the concatenation works. Why is this the case? An example of what I am talking about is in this image:
.
The answer is B, but C seems to make the most sense to me. Thanks for the help.
concat returns a new string, but forEach ignores the return value of the lambda expression. (Its declared parameter is a Consumer, which has no return value.)
replaceAll puts the return value from its function back into the list. That is what it is for.
Remember that strings are immutable. You are not assigning the return value of concatto anything when you call forEach(), but replaceAll() does since it overwrites the current element each time.
.forEach() takes each individual element of the list and then applies the function. Normally you would call a function of an object. In this case you throw the returned value away and does not change the object.
.replaceAll() takes each individual element of the list and applices a function. After that the returned value will be set on the specific place.
.concat() does not change the string reference due to the string immutability, but it will return the changed string in a new reference.
The names explain exactly what the methods do.
forEach pass each element to the function, but it doesn't modify the array member as long as the object is of an immutable type. Consequently it expects no return value (void) of the function. The array always remain unchanged. String type is immutable, so any change doesn't influence the object being held in the array.
replaceAll pass all array members to the function and checks the object return by that function, and of course store that object back to the array, meaning replace. Note that in this method the function must return an object. In case of String.concat the concatenated String is returned thus that concatenated String appears in the array.
I think it happens because
a.forEach iterates over each element in the arrayList, then Concat returns a new string. It does not change the original string in the arrayList.
a.Replaceall will replace the original string in the arrayList.
I'm returning to Java after 2 years working in PHP.
Sorry if this seem silly:
This is the code (Depth-First Traversal of a graph):
public List<List<Edge>> paths = new ArrayList<>();
public void traverse(Edge edge, List<Edge> currentPath){
String vertex = graph.getEdgeTarget(edge);
if(edge!=null) currentPath.add(edge);
if(vertex=="TARGET_VERTEX"){
System.out.println(currentPath); // prints fine
paths.add(currentPath); // elements are empty at end of reccursion
if(edge!=null) currentPath.remove(edge);
return;
}
for(Edge e : graph.outgoingEdgesOf(vertex)){
traverse(e, currentPath);
}
if(edge!=null) path.remove(edge);
}
public void search(){
//graph is initalized, vertices and edges are added
for(Edge e : graph.outgoingEdgesOf("START_VERTEX")){
traverse(e, new ArrayList<Edge>());
}
System.out.println("############################");
System.out.println(paths);
System.out.println(paths.size());
}
Can someone explain why at the end of the recursion paths has empty elements, and how to make it contain the paths I need?
Seems like passing by reference makes me a problem ...
ArrayList has a shallow clone() method, which won't copy the elements (as per JavaDoc).
Do I need to create a temporary variable which will be a manual copying of the currentPath (iterating through the values)?
I'm still a bit confused about passing by value and by reference in Java, which in PHP is easily distinguished by using pass by reference (&variable.)
Edited so I won't get complains about string comparison
The line if(edge!=null) currentPath.remove(edge); removes the element of your List.
This is likely to cause your problem, as currentPath is recursed.
On an unrelated matter, you are comparing String with == instead of using equals, which is bad practice. (See here for more explanations).
These two lines are causing the issue:
paths.add(currentPath);
currentPath.remove(edge);
When you add currentPath to paths, it adding the reference of currentPath. In the end, currentPath is empty and hence paths is left with empty references.
To avoid the issue, create a copy of currentPath and add the copy in paths.
Also update the line below:
if(vertex=="TARGET_VERTEX"){
as
if("TARGET_VERTEX".equals(vertex)){
to use the proper string equality check and avoid NullPointerException.
If you want to ignore the case whily checking then use equalsIgnoreCase() method.
You're adding and removing from paths. It seems that it is doing it wrongly, you can try debugging the app.
Java is pass-by-value. What this means is that a method like
public void myMethod(MyObject instance) {...}
receives a copy of the value of the reference to instance. If within the method you did
instance.setField(newValue);
then you are accessing the same object that you passed because the reference has the same value. However, if you did this within the method
instance = new MyObject(newValue);
then the object that was used to call the method will remain unchanged. This happens because you changed the value of the reference in a copy, not in the original
You can see it explained with more detail at javadude.
Finally, you should compare Strings and other objects with .equals method instead of using ==. This answer should help you.
Doing a bird view of your code, I would correct it to this (didn't try):
by
Create constants for string constants.
//These two lines, minor improvements
public static final String TARGETV= "TARGET_VERTEX";
public static final String STARTV= "START_VERTEX";
Change
if(vertex=="TARGET_VERTEX"){
to
if(vertex.equals(TARGETV)){
About printing the paths variable, System.out.println will print a String, you're passing an Object (a List of Lists of edges). Each object has a toString() method that is call automatically when the Object is required as String. As stated in the docs, by default:
The toString method for class Object returns a string consisting of
the name of the class of which the object is an instance, the at-sign
character `#', and the unsigned hexadecimal representation of the hash
code of the object.
So instead you could:
Create a new class (that implements internally a List<List<Edge>> and override the toString() method) or you could implement a method like this:
public static String printPath(List<List<Edge>> paths){
StringBuffer sb = new StringBuffer();
for(List<Edge> le : paths){
for(Edge e: le){
sb.append(le); //or similar method to print edges to String
}
}
return sb.toString();
}
And instead of:
System.out.println(paths);
do this:
System.out.println(printPaths(paths));
The first thing you need to know is that objects are not values in Java. The only types in Java are primitive types and reference types, so the only values in Java are primitives and references. A "reference" is a pointer to an Object. paths, currentPath, edge, etc. in your code are all references. The elements of paths and of currentPath are also references. When you assign or pass a reference, you get another reference that points to the same object. A new object can basically only be created when you do new ....
So from this it should become more apparent what is going on. The only place where a list of edges is ever created in your code is in the search() function, when it calls the traverse() function. The traverse() function does not contain any object creation expressions. So in all the recursion and all that, it is working with and modifying the same list object. Each call to traverse() adds and then removes an element. So at the end of the recursion, there will be no elements in the list. This is the same list whose reference you added to paths, so of course you see references to empty lists in paths at the end.
You said you worked in PHP. In PHP5, objects work in the same way -- objects are not values in PHP, and they are only manipulated through pointers to objects. However, arrays in PHP (which are not objects) are different; arrays in PHP are values, and so when assigned or passed, the array is copied.
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
Basically I need a different string created each time a loop repeats. I need a new string each time the loop repeats so I can store a whole bunch of different names and recall them as needed.
Thanks for any help I can get, I know its a little vague sorry bout that.
Use the java Collections framework:
List<String> strings = new ArrayList<String>();
while(someCondition()) {
// Call add() as much as you like - the List will grow as needed
strings.add("some new string"); // create whatever String you like
}
Now variable strings contains a bunch of String objects.
Later, you can iterate over the strings to do something with them:
for(String string : strings) {
System.out.println(string); // or whatever
}
Create an array or a list outside the loop and add a new string every time.
I guess you'll have to store your strings in a Map, or similar.
What you need is an array.
An array is like a list of variables. So you can make a new array called mystring, and then mystring[0], mystring[1], mystring[2] are all different variables. In your loop you can use your index value i to do mystring[i] and get a new variable for each time the loop repeats.
http://download.oracle.com/javase/tutorial/java/nutsandbolts/arrays.html
How-Do/Can I set the value of a String object in Java (without creating a new String object)?
There are no "set" methods on String. Strings are immutable in Java. To change the value of a String variable you need to assign a different string to the variable. You can't change the existing string.
(without creating a new String object)
Assigning doesn't create a new object - it copies the reference. Note that even if you write something like this:
s = "hello";
it won't create a new string object each time it is run. The string object will come from the string pool.
Actually there is no way to do that in Java, the String objects are immutable by default.
In fact, that's one of the reason why using the "+" concatenation operator like "str1" + "str2" is terribly inefficient, because what it does is copy every string in order to produce a third one.
Depending on your need you should consider using StringBuilder
Strings are immutable so you cannot change the value of an already created string.
The String object is immutable in Java so any changes create a new String object. Use a StringBuilder if you want to make changes to a string like object without creating new objects. As a bonus the StringBuilder allows you to preallocate additional memory if you know something about the eventual length of your string.
I stumbled across this question because i have to set a string within an "enclosing type" - an anonymous type. But all variables i want to set inside and use outside must be final.
The simple solution is to use StringBuilder - it's an mutable String.
It depends a bit on your definition of object. If you mean the reference, no. A reference is always created. If you mean the memory used by the characters, sure.
Strings are interned (if possible) which means that in an assignment:
String s1 = "Hello";
String s2 = "Hello";
there are 2 references (pointers to a memory location), but Hello is in memory on only 1 place. This is one of the reasons Strings can't be modified.
Sure you can access the internal char array via reflection. But it's usually a bad idea to do so. More on http://www.eclipsezone.com/eclipse/forums/t16714.html.