3

i wonder if this code that using StringBuilder is best efficient

or it still creating alot of temporry strings by concatenating the " " with current item?

if so can you suggest for better code?

public String toString() {
    StringBuilder out = new StringBuilder();
    for (long item : someListOfNumbers) {
        out.append( " " + item);
    }
    return out.toString();
}
user10776203
  • 181
  • 3
  • 9

4 Answers4

4

For this particular use case you probably want to use StringJoiner, unless your goal is to have a leading space in the result.

public String toString() {
    StringJoiner joiner = new StringJoiner(" ");
    someListOfNumbers.forEach(l -> joiner.add(Long.toString(l)));
    return joiner.toString();
}

or use Collectors.joining():

public String toString() {
    returns someListOfNumbers.stream()
        .map(String::valueOf)
        .collect(Collectors.joining(" "));
}

StringBuilder will be a more efficient when building the text but one can argue that above is more readable. Definitely one should avoid + operator when using StringBuilder and use methods like append() instead.

If you take a look at this or this or other answers you will see that String concatenation is additionally optimized by the javac compiler e.g. if constants are concatenated with + operator then the compiler can insert the resulting constant into bytecode.

Karol Dowbecki
  • 43,645
  • 9
  • 78
  • 111
  • 2
    `joiner::add` won't work if `someListOfNumbers` is actually a list of numbers. – Andy Turner Jun 03 '19 at 09:24
  • 1
    StringJoiner needs you to turn all the numbers into intermediate Strings first, whereas StringBuilder can directly output the numbers into its internal buffer, so I'd use StringBuilder (does not make the code shorter anyway). – Thilo Jun 03 '19 at 09:26
3
out.append( " " + item);

In above statement there seems a problem here " " + item as its combining string with a long. As string is immutable so + operator for string creates a new copy of String in memory. An optimal solution could be using

out.append(' ').append(item);

Or even

out.append(" ").append(item);

In the both above scenarios, as StringBuilder is Mutable so extra memory space is not require to save new concatinated string. Hence its more optimal.

Asad Ali Choudhry
  • 4,985
  • 4
  • 31
  • 36
1

A modern way would leave the manipulation of the string builder to a library method:

public String toString() {
    return someListOfNumbers.stream()
            .map(Number::toString)
            .collect(Collectors.joining(" "));
}

I have assumed that someListOfNumbers is a List<Long>. My method doesn’t give the exact same result as yours: Your result has a leading space that I have left out. If you need that space, you may either prepend it yourself or use:

            .collect(Collectors.joining(" ", " ", ""));

The three-arg joining expects delimiter, prefix and suffix in the mentioned order (I am giving the empty string as suffix).

Side note: I had wanted to use .map(Long::toString), but it doesn’t compile because it is ambiguous. Here toString may refer to either the no-arg Long.toString or to the static one-arg Long.toString(long). I solved it referring to it via the superclass of Long: Number::toString. Another solution would be .map(String::valueOf) as in Karol Dowbecki’s answer.

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
0

In order to get rid of a trailing or leading zero, you have to check the index of the item that is currently handled in the loop.

Here are three examples which build a String that does not have anything to trim afterwards:

public String toString() {
    StringBuilder out = new StringBuilder();
    // iterate in a classic for-loop checking for item at index 0
    for (int i = 0; i < someListOfNumbers.size(); i++) {
        long item = someListOfNumbers.get(i);
        if (i == 0) {
            // in this case, avoid a leading zero
            out.append(item);
        } else {
            out.append(" ").append(item);
        }
    }
    return out.toString();
}

public String toString() {
    StringBuilder out = new StringBuilder();
    // iterate in a classic for-loop checking for item at last index
    for (int i = 0; i < someListOfNumbers.size(); i++) {
        long item = someListOfNumbers.get(i);
        if (i == someListOfNumbers.size() - 1) {
            out.append(item).append(" ");
        } else {
            // this case avoids a trailing zero
            out.append(item);
        }
    }
    return out.toString();
}

public String toString() {
    StringBuilder out = new StringBuilder();
    // add the first element and iterate from 1 to end afterwards 
    // (this requires a check for an empty list)
    out.append(someListOfNumbers.get(0));
    for (int i = 1; i < someListOfNumbers.size(); i++) {
        out.append(" ").append(someListOfNumbers.get(i));
    }
    return out.toString();
}

You could even use you own method, but with an enhanced for-loop, you would have to use indexOf and then perform the check. I wouldn't want that, but it is your decision.

deHaar
  • 17,687
  • 10
  • 38
  • 51
  • 1
    I prefer the third of the three examples. Having a special case inside a `for` loop is a bit, just a little bit of an antipattern, and in this case it is easily avoided. – Ole V.V. Jun 03 '19 at 20:19
  • @OleV.V.sure, antipatterns should be avoided in every case, no matter how *big* they are ;-) But in this case, it is about correctly working code, not code quality, I think. – deHaar Jun 04 '19 at 06:42
  • I agree about antipatterns, and I may also have used a strong word here. I tend not to agree about what it’s about: to me it’s always also about code quality. Thanks for your comment. – Ole V.V. Jun 04 '19 at 06:44
  • @OleV.V. I don't doubt it is always about code quality to you now, which is great. But hadn't there been a time when you did not know about code quality and just wanted to learn the basics? I can't really tell what OP wants, but it appears not to be mainly code quality but code that meets some functional requirements. – deHaar Jun 04 '19 at 06:50
  • It could be a long and interesting discussion, but there isn’t room for it here. When I teach programming to beginners, I almost from day one tell them that programming is about writing code that “the next programmer” can read, understand and maintain. – Ole V.V. Jun 04 '19 at 06:54
  • 1
    @OleV.V that's definitely **the** right way to teach programming. Good for your students, I had several teachers who thought differently... – deHaar Jun 04 '19 at 07:04