StringBuilder vs String (in exact situation) [duplicate] - java

This question already has answers here:
StringBuilder vs String concatenation in toString() in Java
(20 answers)
Closed 8 years ago.
I've read about StringBuilder class in Java and I'm wondering, what is more efficient way to do some task:
Using "+" concatenation:
String result = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
"<entry xmlns=\"http://www.w3.org/2005/Atom\" xmlns:y=\"http://api.yandex.ru/yaru/\">"
+ "<title>" + (et_title.getText().toString()) + "</title>" +
"<y:access>"+ privacymode +"</y:access>" +
"<category scheme=\"urn:ya.ru:posttypes\" term=\"link\"/>"
+ "<y:meta>" + "<y:url>" + (et_link.getText().toString()) + "</y:url>" +
"</y:meta>" +
"<content>" + signature_select() + "</content>"
+ "</entry>";
or
StringBuilder sb = new StringBuilder();
sb.append( "<?xml version=\"1.0\" encoding=\"utf-8\"?>")
.append("<entry xmlns=\"http://www.w3.org/2005/Atom\" xmlns:y=\"http://api.yandex.ru/yaru/\">")
.append("<title>")
.append(et_title.getText().toString())
.append("</title>")
.append("<y:access>"+ privacymode +"</y:access>")
.append( "<category scheme=\"urn:ya.ru:posttypes\" term=\"link\"/>")
.append("<y:meta>" + "<y:url>" + (et_link.getText().toString()) + "</y:url>" + "</y:meta>")
.append( "<content>" + signature_select() + "</content>")
.append("</entry>");
String result = sb.toString();

This looks to be worrying about premature optimization. If this is a one-off bit of code and not being called within a tight loop, why worry about optimizing?
Having said that, most often the xml text would be found in a text file and not hard-coded in the program, and then you'll use a loop and a StringBuilder, i.e.,
StringBuilder xmlSb = new StringBuilder();
Scanner xmlScanner = new Scanner(Foo.class.getResourceAsStream(RESOURCE));
while (xmlScanner.hasNextLine()) {
xmlSb.append(xmlScanner.nextLine());
}
if (xmlScanner != null) {
xmlScanner.close();
}
// parse and use your xml text here

Edit: Looks like the Java compiler will optimize the String concatenation (+) version, so both versions will have the same performance, with the advantage to the first one being more readable.
Leaving my previous answer for historic purposes.
Thanks for the comments!
The StringBuilder version is better.
I'd also break the other concatenations you have in the StringBuilder version, like for example:
.append("<y:access>"+ privacymode +"</y:access>")
as:
.append("<y:access>")
.append(privacymode)
.append("</y:access>")
The biggest advantage of using StringBuilder is that you avoid allocating new Strings for each concatenation, whereas StringBuilder will only allocate and resize its internal char array when necessary. It would be even better if you knew the size of the final string. Then you could initialize the StringBuilder with its final size and avoid any extra memory allocation.

Related

Better string manipulation code

I'm looking for an efficient (one line) string manipulation code to achieve this, regex probably.
I have a string, for example, "Calvin" and I need to convert this to "/C/a/l/Calvin".
i.e. take first three characters, separate them using '/' and later append the original string.
This is the code I've come up with and its working fine, just looking for a better one.
String first = StringUtils.substring(prodName, 0, 1);
String second = StringUtils.substring(prodName, 1, 2);
String third = StringUtils.substring(prodName, 2, 3);
String prodPath = path + "/" + first + "/" + second + "/" + third + "/" + prodName + "/" ;
prodName.replaceAll("^(.)(.)(.).*", "/$1/$2/$3/$0")
What is the point of StringUtils.substring(prodName, 0, 1) when the built-in prodName.substring(0, 1) will do the same thing??
Anyway, assuming prodName is always at least 3 characters long (since you didn't give rules for expected output if it is not), this is the fastest way to do it:
String prodPath = path + '/' +
prodName.charAt(0) + '/' +
prodName.charAt(1) + '/' +
prodName.charAt(2) + '/' +
prodName + '/';
Normally, char + char is integer addition, not string concatenation, but since the first value is a String, and the + operator is left-associative, all + operators are string concatenations, not numeric additions.
How about using String.charAt
StringBuilder b = new StringBuilder (path);
b.append ('/').append (prodName.charAt (0))
.append ('/').append(prodName.charAt (1))
.append ('/').append(prodName.charAt (2))
.append ('/').append (prodName).append ('/');
Don't use regex for simple stuff like this. You may save a couple lines, but you loose a lot in readability. Regex usually take some time to understand when reading them.
String s = path;
for (int i = 0; i < 3; i++)
s += prodName.substring(i,i+1) + "/";
s += prodName
You can use MessageFormat.format()
MessageFormat.format("{0}/{1}/{2}/{3}/{4}/", baseDir, name.charAt(0), name.charAt(1), name.charAt(2), name);
imho i would wrap it for readability,
private String getProductionDirectoryPath(String baseDir, String name) {
return MessageFormat.format("{0}/{1}/{2}/{3}/{4}/", baseDir, name.charAt(0), name.charAt(1), name.charAt(2), name);
}
Positive look ahead can be used
public static void main(String[] args) {
String s = "Calvin";
System.out.println(s.replaceAll("(?=^(\\w)(\\w)(\\w))", "/$1/$2/$3/"));
}
O/P:
/C/a/l/Calvin
No use of a regex, but a simple split over nothing =)
String[] firstThree = prodName.split("");
String prodPath = path + "/" + firstThree[0] + "/" + firstThree[1] + "/" + firstThree[2] + "/" + prodName + "/";
Another approach is using charAt():
String prodPath = path + "/" + prodName.charAt(0) + "/" + prodName.charAt(1) + "/"+ prodName.charAt(2) + "/" + prodName + "/";
You said efficient but you maybe meant terse. I doubt either should be an objective, so you have a different problem.
Why do you care that this string transformation requires four lines of code? Are you concerned that something that in your mind is one operation ("create transformed string") is spread over four Java operations? You should extract the four lines of Java into their own method. Then, when you read the code where the operation is needed you have one conceptual operation ("create transformed string") corresponding to one Java operation (call a method). You could call the methid createTransformedString to make the code even clearer.
You can use String Builder:
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 3; i++) {
sb.append("/").append(prodName.charAt(i));
}
sb.append('/').append(prodName);
Or you can put all the code in loop:
int size = 2;
StringBuilder sb = new StringBuilder();
for (int i = 0; i <= size; i++) {
if (i == 0)
sb.append('/');
sb.append(prodName.charAt(i)).append("/");
if (i == size)
sb.append(prodName);
}

Is there any simpler code to separate lines?

I am using this code to separate the next line and giving space.
String sms="Name:"+name+ System.getProperty ("line.separator")+System.getProperty
("line.separator")+"ContactNumber:"+contactnumber+ System.getProperty
("line.separator")+"Quantity:"+quantity+System.getProperty
("line.separator")+"Number.of.Pcs:"+noofpieces+System.getProperty
("line.separator")+"Date and Time:"+dateandtime
+System.getProperty ("line.separator")+"Delivary
Address:"+deliveryaddress;
You could use a StringBuilder instance and then use the new line operator appended to the StringBuilder. For example:
StringBuilder sb = new StringBuilder();
sb.append("Name: ").append(name);
sb.append("\n"); // for a new line.
Whatever the case, I would strongly recommend that you use a StringBuilder to append to a very large String.
In addition, you could also use System.lineSeparator(); However, that may only work in Java with the JVM with Java 7 and not in Android (so I would definitely check that out.)
String sms= "Name:" + name
+ "\nContactNumber:" + contactnumber
+ "\nQuantity:" + quantity
+ "\nNumber.of.Pcs:" + noofpieces
+ "\nDate and Time:" + dateandtime
+ "\nDelivary Address:" + deliveryaddress;
Using System.getProperty("line.separator") is a good practice as it will give you code that could be reused on another platform. To simplify your code, you can use TextUtils.join :
String sms = TextUtils.join(System.getProperty("line.separator"),
new String[] {
"Name:" + name ,
"ContactNumber:" + contactnumber,
...});
You could also use this solution
String format = "Name: %s%n%nContactNumber: %s%nQuantity: %s%nNumber.of.Pcs: %s%nDate and Time: %s%nDelivery Address: %s";
String sms = String.format(format, name, contactnumber, quantity, noofpieces, dateandtime, deliveryaddress);
The explanation of the format placeholders you find in the Javadoc for java.util.Formater

Optimize String += or Concat? [duplicate]

This question already has answers here:
String concatenation: concat() vs "+" operator
(12 answers)
Closed 9 years ago.
I am writing a file with possibly 1000 data points. I have classes for all of these and am currently writing all of the data at the end (the datapoints are taken at 1s intervals). What I am currently doing is written below, and it's very slow. Would I be better off changing how I am writing the string/bytes to the file? Or would I be better off writing this information to some file pointer as the application is running?
Btw, all of the things such as getAccuracy() and such are floats/ints (so it has to convert those also).
fileStr = "";
fileStr += "timestamp,Accuracy,Altitude,Latitude,Longitude,GPSSatelliteEntries\r\n";
for (Iterator<Entry> i = entries.iterator(); i.hasNext(); ) {
Entry item = i.next();
long ts = item.getTs();
DataEntry d = item.getD();
List<GPSSatelliteEntry> satellites = item.getG();
// write stuff
fileStr += ts + ",";
fileStr += d.getAccuracy() + "," + d.getAltitude() + "," + d.getLatittude() + "," + d.getLongitude() + ",";
fileStr += "[";
boolean entered = false;
for (Iterator<GPSSatelliteEntry> j = satellites.iterator(); j.hasNext(); ) {
GPSSatelliteEntry item2 = j.next();
entered = true;
fileStr += "(" + item2.getAzimuth() + "," + item2.getElevation() + "," + item2.getPrn() + "," + item2.getSnr() + "),";
}
// chop off extra ,
if (entered)
fileStr = fileStr.substring(0, fileStr.length() - 1);
fileStr += "]";
fileStr += "\r\n";
}
Everytime you have hard work with Strings, use StringBuilder or StringBuffer to achieve better performance .
Don't forget that String is immutable, and each time you modify String new instance will be created and it costs performance.
Most probably string buffer
A thread-safe, mutable sequence of characters. A string buffer is like a String, but can be modified.
or go for string builder
StringBuilder stuff = new StringBuilder();
stuff.append("PUT YOUR STRINGS HERE");
stuff.append("PUT YOUR STRINGS HERE");
Then you can use 'stuff' to print the strings.
Put it in a loop and iterate over a large number with a timer to see the advantages, it's pretty interesting.

String concatenation not working, or is it a bug

I have written some code and used a string that I concatentated using the += (as I only do it a couple of times.
Later on I used another string and used the concat() function. and the concatenation didn't work.
So I wrote a little method in Junit (with eclipse)...
#Test
public void StingConcatTest(){
String str = "";
str += "hello ";
str += " and goodbye";
String conc="";
conc.concat("hello ");
conc.concat(" and goodbye");
System.out.println("str is: " + str + "\nconc is: "+ conc);
The output is...
str is: hello and goodbye
conc is:
So either I'm going mad, I'm doing something wrong (most likely), there is an issue in JUNIT, or there is a problem with my JRE / eclipse or something.
Note that stringbuilders are working fine.
David.
Ok, we see this question at least couple of times a day.
Strings are immutable, so all operations on String results in new String.
conc= conc.concat("hello "); you need to reassign result to string again
You have to try with:
String conc="";
conc = conc.concat("hello ");
conc = conc.concat(" and goodbye");
System.out.println("str is: " + str + "\nconc is: "+ conc);
For sake of optimization you can write:
String conc="";
conc = conc.concat("hello ").concat(" and goodbye");
System.out.println("str is: " + str + "\nconc is: "+ conc);
If you plan on concatenating multiple Strings you could also use StringBuilder:
StringBuilder builder = new StringBuilder();
builder.append("hello");
builder.append(" blabla");
builder.append(" and goodbye");
System.out.println(builder.toString());
concat returns a String. It doesn't update the original String.
concat() returns the concatenated string.
public static void main(String [] args) {
String s = "foo";
String x = s.concat("bar");
System.out.println(x);
}
String.concat doesn't change the string it is called on - it returns a new string which is the string and the argument concatenated together.
By the way: string concatenation using concat or += is not very performant. You should use the class StringBuilder instead.

How replace String catenation to StringBuilder append?

How can recursively replace all catenations in toString method to StringBuilder for Java? Is there such a plugin in eclipse?
For example:
Replace it:
return "AccountAddresses ["
+ ", corporateAddresses=" + CommonHelper.isNotNull(corporateAddresses)
+ ", corporateDeliveryMinimum=" + corporateDeliveryMinimum
+ ", depot=" + CommonHelper.isNotNull(depot)
+ ", depotDeliveryMinimum=" + depotDeliveryMinimum
+ ", preSelectedId=" + preSelectedId
+ ", residentialAddresses=" + CommonHelper.isNotNull(residentialAddresses)
+ ", residentialDeliveryMinimum=" + residentialDeliveryMinimum
+ "]";
at this:
return new StringBuilder("AccountAddresses [")
.append(", corporateAddresses=").append(CommonHelper.isNotNull(corporateAddresses))
.append(", corporateDeliveryMinimum=").append(corporateDeliveryMinimum)
.append(", depot=").append(CommonHelper.isNotNull(depot))
.append(", depotDeliveryMinimum=").append(depotDeliveryMinimum)
.append(", preSelectedId=").append(preSelectedId)
.append(", residentialAddresses=").append(CommonHelper.isNotNull(residentialAddresses))
.append(", residentialDeliveryMinimum=").append(residentialDeliveryMinimum)
.append("]").toString();
It's a builtin command of Eclipse.
Click on one of the quotation marks (") in your String concatenation.
Bring the Quick Fix menu (Hit Ctrl + 1 on the keyboard).
Select Use 'StringBuilder' for String concatenation.
Magic! Your
return "foo" + "bar";
changed to
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("foo");
stringBuilder.append("bar");
return stringBuilder.toString();
I haven't heard of an Eclipse plugin for this - so feel free to ignore this off topic answer - but IntelliJ has an "intention" that will do it for you (at least in 10.0 it does). There is a community edition available if you want to give it a shot.
Do a regex search and replace :
", ([a-zA-z0-9]+)=" \+ CommonHelper\.isNotNull\(([a-zA-z0-9]+)\) // Find this
append(", $1=").append(CommonHelper.isNotNull($2)) // and replace with this
It is not complete, but you get the idea.
Why dont you override the toString method in your class , and implement the stringbuilder append.
I don't think any plugin would do that for you... this would be useless anyways : the Java compiler will do it better than anyone can (and if "StringBuilder" is ever replaced by something better, the Java compiler will be able to use this "something better" if you do not explicitely use a StringBuilder).

Categories