0

I have two Strings, each one of them can be empty (blank or empty by StringUtils definition.) I need to display them in a concatenated fashion having a simple character in between, e.g. dash, comma, etc.

For example, a person's name consists of LastName and FirstName. PrintName method should print in this fashion:

case a: both not empty

print LastName + ", " FirstName

case b: FirstName is empty

print LastName

case c: LastName is empty

print FirstName

case d: both are empty

print empty string

This is just a simple exercise, but I'm curious if there's a most efficient method in Java that requires minimum variables/memory allocation, fewer lines of code, so on and so forth... My definition of efficiency is a bit vague so if you could write down why you think it's more efficient, that would be nice.

Pshemo
  • 122,468
  • 25
  • 185
  • 269
Poly Bug
  • 1,514
  • 1
  • 14
  • 27
  • 1
    See http://stackoverflow.com/a/1752807/1820501 – Florent Bayle Oct 16 '14 at 09:06
  • I read the answer on that link, does Joiner check for blanks? – Poly Bug Oct 16 '14 at 09:13
  • Yes it does, but not as straightforward as null check. – govin Oct 16 '14 at 09:29
  • Do you have to handle nulls? Also could you say something more about "*blank or empty by StringUtils definition*"? Some examples of values which should be skipped would be nice. – Pshemo Oct 16 '14 at 09:58
  • blank or empty = null, "", " " (as defined in StringUtils) https://commons.apache.org/proper/commons-lang/javadocs/api-2.6/org/apache/commons/lang/StringUtils.html#isBlank(java.lang.String) – Poly Bug Oct 16 '14 at 10:05

10 Answers10

2

You could design your pritning method like follows:

public class Main {
    private final static Predicate<String> notEmpty = s -> s != null;
    private final static Predicate<String> notBlank = s -> !s.equals("");

    // ...

    private static void printName(final String firstName, final String lastName) {
        final boolean firstNameOk = notEmpty.and(notBlank).test(firstName);
        final boolean lastNameOk = notEmpty.and(notBlank).test(lastName);
        final StringBuilder result = new StringBuilder();

        // Old version:
        //result
        //    .append(lastNameOk ? lastName : "")
        //    .append(lastNameOk && firstNameOk ? ", " : "")
        //    .append(firstNameOk ? firstName : "");
        // End of old Version:

        // New version:
        if (lastNameOk) {
            result.append(lastName);
        }

        if (firstNameOk) {
            if (lastNameOk) {
                result.append(", ");
            }

            result.append(firstName);
        }
        // End of new version:

        System.out.println(result);
    }
}

Then some example calls:

public static void main(String[] args) {
    printName("James", "Bond");
    printName("James", null);
    printName(null, "Bond");
    printName(null, null);
}

Will print:

Bond, James
James
Bond
ifloop
  • 8,079
  • 2
  • 26
  • 35
  • May I ask why is your code efficient? It looks so complex... >.< Nvm, I understood it. Just wasn't familiar with Predicates. – Poly Bug Oct 16 '14 at 09:15
  • I used a StringBuilder which is more efficient than string concatenation, so far. You could also inline all 3 checks instead of using an `Predicate` or you could make that `Predicate` an static class member. – ifloop Oct 16 '14 at 09:18
  • You make 3 calls to append, then for each append, you also have conditionals and a check for existing length. I don't think it's efficient enough. It's quite logical though. – Poly Bug Oct 16 '14 at 09:19
  • You could store the result of notBlankOrEmpty in local variables and replace the length check with a reference to notBlankOrEmpty(lastName), but you'll need to evaluate if a parameter is null or empty at least once – ftr Oct 16 '14 at 09:26
  • @ftr Thanks! I edited my answer taking into account your suggestions. – ifloop Oct 16 '14 at 09:36
  • @ifLoop you could replace `result.length() > 0`with `lastNameOk`, thus getting rid of the length check. – ftr Oct 16 '14 at 10:24
  • @ftr I took your last comment to heart and reworked the whole logic. Now the call to `result.length()` is obsolete and the calls to `StringBuilder#append()` only happen if the conditions match and not as it was before: The three calls happen every time but the argument is condition checked. – ifloop Oct 16 '14 at 12:12
2

If it's just two strings, and they are empty, not null, I'd go with

System.out.println(
    lastName
    + (( firstName.length() > 0 && lastName.length() > 0 ) ? ", " : "")
    + firstName
);

Explanation: the middle expression will be ", " only if both strings are non-empty.

In all other cases both the middle part and one or both of the others are empty. Thus, only the full side is printed.

If you want to go by StringUtils's definitions, the code is:

System.out.println(
    StringUtils.stripToEmpty(lastName)
    + (( StringUtils.isNotBlank(firstName) && StringUtils.isNotBlank(lastName) ) ? ", " : "")
    + StringUtils.stripToEmpty(firstName)
);
RealSkeptic
  • 33,993
  • 7
  • 53
  • 79
  • Yes, it's only two strings. Personally, I found your answer the most clever so far. – Poly Bug Oct 16 '14 at 09:55
  • 1
    Thank you. I've noticed that you do want to handle strings that may be null or have spaces in them, so I added a version with `StringUtil` methods for that. – RealSkeptic Oct 16 '14 at 10:43
1
return (firstName == null ? "" : firstname + ", " + lastName==null ? "" : lastName).replaceFirst("^, |, $","");

this will result in the firstname + ", " + lastname string, and in case the ", " string is at the beginning or the end of the string, it will be erased, therefore you get exactly what you want.

Attila Neparáczki
  • 466
  • 1
  • 6
  • 14
1

Only one boolean variable, but still four branches:

public void p(String f, String l) {
    boolean e=l==null||l.isEmpty();
    System.out.println(f==null||f.isEmpty()?e?"":l:e?f:f+", "+l);
}

This is surely not the best way and I would also recommend using Guava or another library like Apache commons.

stevecross
  • 5,588
  • 7
  • 47
  • 85
  • Yeah, I use `StringUtils.isBlank()` to check for null/empty/blanks. I can replace the `f==null||f.isEmpty()` with that call. – Poly Bug Oct 16 '14 at 09:50
0

You can use apache commons api class to validate it in single line.

GenericValidator.isBlankOrNull(value); 

For that you need to use apache commons jar and import this class

import org.apache.commons.validator.GenericValidator;
Qadir Hussain
  • 1,263
  • 1
  • 10
  • 26
0

Try this one:

    String LastName = "First";
    String FirstName = "Last";
    boolean cond1, cond2;
    cond1 = FirstName == null || "".equals(FirstName);
    cond2 = LastName == null || "".equals(LastName);
    String DesiredName = (cond2 ? "" :  LastName) +
      ((!cond2 && !cond1) ? ", " : "") +
      (cond1 ? "" : FirstName);
     System.out.println(DesiredName);
Dr. Debasish Jana
  • 6,980
  • 4
  • 30
  • 69
0

Use Google's guava and its Joiner method. It is the most elegant solution as far as I know.

https://code.google.com/p/guava-libraries/wiki/StringsExplained

Joiner joiner = Joiner.on(", ").skipNulls();
return joiner.join(firstName, lastName);

To skips nulls + empty strings, store firstName, lastName... in an array or list and then do

return Joiner.on(", ").join(Iterables.filter(listOfStrings, StringPredicates.notEmpty()));

You can look at the Joiner source code to see how its implemented. It is certainly not the most efficient but in this case the ignorable efficiency gain is worth trading off with code clarity and readability.

govin
  • 6,445
  • 5
  • 43
  • 56
0

Clean, no extra conditions/libraries. This is of course specific to your requirement. You can use Guava's Joiner for more complex requirements.

public static void main(String[] args) throws Exception {
    String firstName = "Stack";
    String lastName = "Overflow";
    System.out.println(printName(firstName, lastName));
}

private static String printName(String firstName, String lastName) {
    StringBuilder buffer = new StringBuilder();
    String cleanFirstName = avoidNull(firstName, "");
    String cleanLastName = avoidNull(lastName, "");
    if (!cleanLastName.isEmpty()) {
        buffer.append(cleanLastName);
    }
    if (!cleanFirstName.isEmpty()) {
        if (!cleanLastName.isEmpty()) {
            buffer.append(", ");
        }
        buffer.append(cleanFirstName);
    }
    return buffer.toString();
}

private static String avoidNull(String str, String alternate) {
    if (str == null || str.isEmpty()) {
        return alternate;
    }
    return str;
}

You can remove the alternate parameter in the avoidNull() method if you don't want it.

private static String avoidNull(String str) {
    return str == null ? "" : str;
}
Sergio
  • 438
  • 2
  • 6
0

I believe you shouldn't focus on performance this much, but on clean code.

If you have only two strings, you could create simple method as this one (I assume you are using Java 8).

public static String combine(String s1, String s2) {
    StringJoiner sj = new StringJoiner(", ");

    if (!StringUtils.isBlank(s1)) sj.add(s1);
    if (!StringUtils.isBlank(s2)) sj.add(s2);

    return sj.toString();
}

You can replace !StringUtils.isBlank(s1) with s1!=null %% !s1.trim().isEmpty()


If you want to create more generic method in which you could decide which delimiter to use, and which would be able to accept more than two Strings you could use something like

public static String joinWithourEmptyOrNull(String delimiter, String... words) {
    StringJoiner sj = new StringJoiner(delimiter);
    for (String s : words) {
        if (!StringUtils.isBlank(s))
            sj.add(s);
    }
    return sj.toString();
}

You can also rewrite this code into something more readable (at least for someone familiar with streams and lambdas - you don't have to be guru :D )

public static String joinWithourEmptyOrNull(String delimiter, String... words) {
    return Arrays.stream(words)
            .filter(s -> !StringUtils.isBlank(s))
            .collect(Collectors.joining(delimiter));
}

Demo:

System.out.println(joinWithourEmptyOrNull(", ", "firstName", null, "  ",
        "secondName"));

result firstName, secondName

Pshemo
  • 122,468
  • 25
  • 185
  • 269
-1

A simple "fix" would be

String toPrint = "";
if(lastName.length() > 0 && firstName.length() > 0){
   toPrint = lastName + ", " - firstName;
}else if(!lastName.length() > 0 && firstName.length() > 0){
   toPrint = firstName;
}else if(lastName.length() > 0 && !firstName.length() > 0){
   toPrint = lastName;
}
System.out.println(toPrint)
Lars Nielsen
  • 2,005
  • 2
  • 25
  • 48