Should one use StringBuilder in a recursive function? - java

I understand that the purpose of StringBuilder (usually) is to avoid creating objects over and over in Java when iterating over strings, especially in a loop.
I'm wondering if it's worth it to use it in a recursive function that returns a string. In other words, which of the following is more efficient?
public String recursive(int n) {
String retStr = "s";
if (n==0) {
return retStr;
}
else {
return retStr + recursive(n-1);
}
}
or
public String recursive(int n) {
String retStr = "s";
StringBuilder sb = new StringBuilder();
if (n==0) {
return retStr;
}
else {
return sb.append(retStr).append(recursive(n-1)).toString();
}
}
If I had to guess, the first one seems less complex, because either way you have to create a new object, be it a String or a StringBuilder, every time, but I could be wrong.

You can add StringBuilder to recursive method:
public String recursive(int n, StringBuilder sb) {
String retStr = "s";
if (n==0) {
return retStr;
}
else {
return sb.append(retStr).append(recursive(n-1, sb)).toString();
}
}
and call it
recursive(100, new StringBuilder());

if you are using java 8,
Since variables are in scope I think there is no need for StringBuilder.
In summary, Java 8 seems not to introduce new optimizations for String
concatenation with the + operator. It means that using StringBuilder
manually is still required for specific cases where the compiler or
the JIT is not applying magic tricks. For instance, when lot of
substrings are concatenated to a String variable defined outside the
scope of a loop.
See more in pellegrino
and dzone

User7294900's idea to pass the StringBuilder is fine, but he is adding much too much.
public String recursive (int n, StringBuilder sb) {
String retStr = "s";
if (n==0) {
return sb.toString ();
}
else {
return recursive (n-1, sb.append (retStr));
}
}
And - you should look this up, I'm unsure - doesn't the StringBuilder take hints, how big it will grow?
recursive (100, new StringBuilder (101));

Related

Java - Calling a method that returns a string of ints from an array

I am working with a program in which I need to use a method call to return a String from an int array. This is what I have of the method so far (I am required to use a method with this header and parameters)
public String toString()
{
for (int i=0;i<NUM_POCKETS;i++)
{
this.getPocketCount(i);
}
}
I basically need the loop to go through all of my "pockets" (array items) and return the stored values into a String to be returned.
I could be missing something very obvious, but for the life of me I do not understand how this information would be stored and returned to the Driver as a String. I know the loop logic is there, but how do I store each increment of i into a String as the loop progresses?
Thanks in advance for any help!
"I am working with a program in which I need to use a method call to return a String from an int array."
If this isn't a homework problem, you can simply use Arrays.toString(int[] array).
String myString = Arrays.toString(myIntArray);
Otherwise, maybe you can do something like this:
String getStringFromIntArray(int[] array) {
StringBuilder builder = new StringBuilder();
for (int num : array)
builder.append(num)
return builder.toString();
}
Try looking into the StringBuilder class. The specification for Java 6 is here.
http://docs.oracle.com/javase/6/docs/api/java/lang/StringBuilder.html
You would need a StringBuilder object and just append the value to the object using the .append() function.
as long as this.getPocketCount(i); gives you the value of the array on position i:
public String toString() {
String returnstring= ""; //init empty string
for (int i = 0; i < NUM_POCKETS; i++) {
returnstring += this.getPocketCount(i)+" "; //append(concat) to string
}
returnstring = returnstring.substring(0, returnstring.length()-1); //remove the last " "
return returnstring; //return string
}
the + sign appends the next string
"Hello"+" "+"World" becomes "Hello World"
Edit:
public String toString() {
String returnstring= ""; //init empty string
for (int i = 0; i < NUM_POCKETS-1; i++) { //-1 to place last later
returnstring += this.getPocketCount(i)+" "; //append to string
}
returnstring += this.getPocketCount(NUM_POCKETS-1) //append the last value
return returnstring; //return string
}
Assuming you specifically want to build your String from within the loop, you could use a StringBuilder. It is more verbose than the one-liner offered by Arrays.toString(), but you asked for it:
e.g.
public String toString() {
StringBuilder sb = new StringBuilder(NUM_POCKETS);
for (int i = 0; i < NUM_POCKETS; i++) {
sb.append(this.getPocketCount(i));
}
return sb.toString();
}
Using a StringBuilder within a loop is faster than performing concatenation of each individual element. See: when to use StringBuilder in java

String concatenation - Boolean hard-coded Vs Boolean Concatenation with String

I need a advice (both in java & .net) for the following piece of code.
public void method(bool value)
{
String someString;
//some code
if (value)
{
//some code
...
someString = "one" + value;
}
else
{
//some code
...
someString = "two" + value;
}
}
Which one is advisable and why? either code like above or code like
someString = "onetrue";
someString = "twofalse";
After compilation and optimization by JDK, method will look like:
public static String method(boolean value) {
String someString;
if (value) {
StringBuilder sb = new StringBuilder();
sb.append("one");
sb.append(value);
someString = sb.toString();
} else {
StringBuilder sb = new StringBuilder();
sb.append("two");
sb.append(value);
someString = sb.toString();
}
return someString;
}
If this code is invoked very frequently, it could bring a performance impact, compared to the second version. In each case a new StringBuilder is constructed and three methods are invoked on it. And boolean should be converted to an object before calling append. While in the second version we just return constant. Everything depends on how often this code is called.
Neither will make any difference it's purely style.
Since you have // some other code I'd just stick with the first. If you only had one line in each branch then either is ok.
At a high level they both are the same but if you look down at lower levels, I would advise to using the method:
someString = "onetrue";
someString = "twofalse";
This is because when you do "one" + value, the value is actually a bool and the toString() method of the bool object will be called to add to the string. Basically just adding another step opposed to just specifying what to add to the string.

Highest performance for finding substrings

I have an array of strings (keywords), and I need to check how many of those strings existing within a larger string (text read from file). I need the check to be case insensitive.
At this moment what I do is this:
private void findKeywords() {
String body = email.getMessage();
for (String word : keywords) {
if (body.toLowerCase().contains(word.toLowerCase())) {
//some actions }
if (email.getSubject().contains(word)) {
//some actions
}
}
}
From reading questions in here another solution came up:
private void findKeywords() {
String body = email.getMessage();
for (String word : keywords) {
boolean body_match = Pattern.compile(Pattern.quote(word), Pattern.CASE_INSENSITIVE).matcher(body).find();
boolean subject_match = Pattern.compile(Pattern.quote(word), Pattern.CASE_INSENSITIVE).matcher(email.getSubject()).find();
if (body_match) {
rating++;
}
if (subject_match) {
rating++;
}
}
}
Which of these solutions is more efficient? Also is there another way to do this that is better? Any accepted solutions must be simple to implement(on par with the above) and preferably without external libraries as this is not very important issue in this case.
Both of the solutions seem viable to me. One improvement I would suggest is moving functions out of the loop. In your current code you are repeatedly doing actions such as toLowerCase() and Pattern.compile which you only need to do once.
Obviously there are much faster methods to solve this problem, but they require much more complex code than these 5-liners.
Better: build a single pattern with all keywords. Then search on that pattern. Assuming your keywords do not contain meta-characters (characters with special meanings in patterns), then use:
StringBuilder keywordRegex = new StringBuilder();
for (String w : keywords) {
keywordRegex.append("|"+w);
}
Pattern p = Pattern.compile(keywordRegex.substring(1));
Matcher m = new p.matcher(textToMatch);
while (m.find()) {
// match is at m.start(); word is m.group(0);
}
Much more efficient than iterating through all keywords: pattern compilation (once) will have generated an automata that looks for all keywords at once.
I think the explicit regex solution you mentioned would be more efficient since it doesn't have the toLowerCase operation, which would copy the input string in memory and make chars lowercase.
Both solutions should be practical and your question is mostly academic, but I think the regexes provide cleaner code.
If your email bodies are very large, writing a specialized case-insensitive contains may be justified, because you can avoid calling toUpperCase() on big strings:
static bool containsIgnoreCase(String big, String small) {
if (small == null || big == null || small.length() > big.length()) {
return false;
}
String smallLC = small.toLowerCase();
String smallUC = small.toUpperCase();
for (int i = 0; i < big.length(); ++i) {
if (matchesAt(big, i, smallLC, smallUC)) {
return true;
}
}
return false;
}
private static bool matchesAt(String big, int index, String lc, String uc) {
if (index + lc.length() > big.length()) {
return false;
}
for (int i = 0; i < lc.length(); ++i) {
char c = big.charAt(i + index);
if ((c != lc.charAt(i)) && (c != uc.charAt(i))) {
return false;
}
}
return true;
}

Should the toString() method be added?

This is the piece of code.
List<BDDObject> childlist = savingObject.getChildren("TherapyAreaReference");
if (childlist.size() > 1) {
for (int i = 0; i < childlist.size() - 1; i++) {
String newMedcondRefChild = ((String) childlist
.get(i)
.getValue( IDDConstants.IDD_THERAPY_AREA_REF_VALUE))
.toLowerCase()
.trim()
.concat(((String) childlist
.get(i)
.getValue(IDDConstants.IDD_THERAPY_AREA_REF_TYPE_NAME))
.toLowerCase().trim());
}
}
IDDConstants has public static final strings defined in it. As StringBuffer is more effective, how can it be incorporated for the concat operations?
I'm guessing that the intention is to generate a list of 'reports', one for each BDDObject record found. Based on that idea, your code should look more like this:
public List<String> getReport(List<BDDObject> records) {
List<String> reports = new ArrayList<String>(record.size());
for (BDDObject record:records) {
String newMedcondRefChild = String.valueOf(record.getValue( IDDConstants.IDD_THERAPY_AREA_REF_VALUE))
.toLowerCase()
.trim() + String.valueOf(record.getValue(IDDConstants.IDD_THERAPY_AREA_REF_TYPE_NAME)))
.toLowerCase().trim());
reports.add(newMedcondRefChild);
}
return reports;
}
Regarding the question on whether toString() would be helpful, the only place where I see it fitting, would be on the BDDObject itself. It would look something like this:
class BDDObject {
...
#Override
public String toString() {
return String.valueOf(getValue(IDDConstants.IDD_THERAPY_AREA_REF_VALUE)).toLowerCase().trim() +
String.valueOf(getValue(IDDConstants.IDD_THERAPY_AREA_REF_TYPE_NAME)).toLowerCase().trim());
}
In which case, the function to create the report becomes trivial:
public List<String> getReport(List<BDDObject> records) {
List<String> reports = new ArrayList<String>(record.size());
for (BDDObject record:records) {
reports.add(record.toString());
}
return reports;
}
In case that what you want is a looooong string with all the values concatenated to it, you can use StringBuilder, like this:
public String getReport(List<BDDObject> records) {
StringBuilder sb = new StringBuilder();
for (BDDObject record:records) {
sb.append(String.valueOf(record.getValue( IDDConstants.IDD_THERAPY_AREA_REF_VALUE))
.toLowerCase()
.trim());
sb.append(String.valueOf(record.getValue(IDDConstants.IDD_THERAPY_AREA_REF_TYPE_NAME))
.toLowerCase().trim()));
}
return sb.toString();
}
This will return all the records appended after each other. I doubt its readability, but you I hope you get the idea. StringBuilder is helpful when you need to build a string iteratively (like in the previous example). StringBuilder should not be used to replace single String operations like : String a = b.get() + c.get(); given that the compiler implicitly creates a StringBuilder in these cases and therefore there's no actual performance improvement to be achieved.
In the code in your question, StringBuffer/StringBuilder will not give you any performance gains, because you concatenate only two strings. However, the question does not state what you are doing with the string in newMedconfRefChild. If your actual goal is to concatenate the strings of each loop iteration, then you should use a StringBuilder (use StringBuffer only when it is really necessary, prefer StringBuilder).

Java: StringBuffer & Concatenation

I'm using StringBuffer in Java to concat strings together, like so:
StringBuffer str = new StringBuffer();
str.append("string value");
I would like to know if there's a method (although I didn't find anything from a quick glance at the documentation) or some other way to add "padding".
Let me explain; every time I append something to the string, I want to add a space in the end, like so:
String foo = "string value";
str.append(foo + " ");
and I have several calls to append.. and every time, I want to add a space. Is there a way to set the object so that it will add a space automatically after each append?
EDIT --
String input
StringBuffer query = new StringBuffer();
Scanner scanner = new Scanner(System.in);
scanner.UseDelimiter("\n");
do {
System.out.println("sql> ");
input = scanner.next();
if (!empty(input)) query.append(input);
if (query.toString().trim().endsWith(";")) {
//run query
}
}
while (!input.equalsIgnoreCase("exit");
I'll use StringBuilder though as grom suggested, but that's how the code looks right now
I think this is handled easier either with a helper method (untested code):
public String myMethod() {
StringBuilder sb = new StringBuilder();
addToBuffer(sb, "Hello").addToBuffer("there,");
addToBuffer(sb, "it").addToBuffer(sb, "works");
}
private StringBuilder addToBuffer(StringBuilder sb, String what) {
return sb.append(what).append(' '); // char is even faster here! ;)
}
Or even using a Builder pattern with a fluent interface (also untested code):
public String myMethod() {
SBBuilder builder = new SBBuilder()
.add("Hello").add("there")
.add("it", "works", "just", "fine!");
for (int i = 0; i < 10; i++) {
builder.add("adding").add(String.valueOf(i));
}
System.out.println(builder.build());
}
public static class SBBuilder {
private StringBuilder sb = new StringBuilder();
public SBBuilder add(String... parts) {
for (String p : parts) {
sb.append(p).append(' '); // char is even faster here! ;)
}
return this;
}
public String build() {
return sb.toString();
}
}
Here's an article on the subject.
Hope it helps! :)
You should be using StringBuilder.
Where possible, it is recommended that this class be used in preference to StringBuffer as it will be faster under most implementations.
StringBuffer is final. You cannot derive from it.
The Best solution really is to add the padding for yourself. Write a method for it and use a PADDING-Constant so that you can easily change it, or better put it in a parameter.
Can you not create a new class which wraps around StringBuffer and add an appendWithTrailingSpace() method?
CustomStringBuffer str = new CustomStringBuffer();
str.appendWithTrailingSpace("string value");
(Although you may want to call your method something a little shorter.)
Just add the space yourself, it's easy enough, as per your own example.
Another possibility is that StringBuilder objects return themselves when you call append, meaning you can do:
str.append("string value").append(" ");
Not quite as slick, but it is probably an easier solution than the + " " method.
Another possibility is to build a wrapper class, like PaddedStringBuilder, that provides the same methods but applies the padding you want, since you can't inherit.

Categories

Resources