as it mentioned in the title, I have this code
String a = flett("AM ","L","GEDS","ORATKRR","","R TRTE","IO","TGAUU");
public static String flett(String... s){
StringBuilder merge = new StringBuilder();
for (int i = 0; i < s.length; i++) {
merge.append(s.charAt(i));
}
return merge;
}
I got an error at chartAt(i) ?
how for example I can call every character in the array s and save them into merge or call an specific character like the first character from each one and save them into merge ?
s[i].charAt(j);
where i - the index of an array, j - the index of a letter within a String.
A Java 8 method that collects the first letter of each array's element might look like
public String flett(String... s) {
return Arrays.stream(s)
.map(i -> i.length() > 0 ? String.valueOf(i.charAt(0)) : "")
.collect(Collectors.joining());
}
For the array "AM ","L","GEDS","ORATKRR","","R TRTE","IO","TGAUU", it results in "ALGORIT".
You have to use a variable amount of String parameters, then concatenate all first characters of non empty Strings of the parameters and return the concatenated object:
public static void main(String[] args) {
String s = flett("AM ","L","GEDS","ORATKRR","","R TRTE","IO","TGAUU", "HOLA", "MMMMH");
System.out.println(s);
}
// Please note the parameter, it takes a various amount of Strings
public static String flett(String ... values) {
// create something that concatenates Strings (other options possible)
StringBuilder sb = new StringBuilder();
// the parameters are now an array of Strings, which you can "foreach"
for (String s : values) {
// check for empty ones and skip those
if (!s.equals("")) {
// append the first character of a valid parameter
sb.append(s.charAt(0));
}
}
return sb.toString();
}
Be surprised by the output…
This method get some Strings and Create String from the first character of each String.
public static String flett(String... s) {
StringBuilder res = new StringBuilder(s.length);
for (String a : s) {
if (!a.isEmpty()) {
res.append(a.charAt(0));
}
}
return res.toString();
}
Can I do this with streams?
StringBuilder text = new StringBuilder();
StringBuilder dupText = new StringBuilder();
String lastLetter = "";
for (Container cont : containersList) {
String letter = cont.getLetter();
text.append(letter);
if (letter.equals(lastLetter) == false) {
dupText.append(letter);
}
lastLetter = letter;
}
System.out.println(text);
System.out.println(dupText);
I go over list of continers, each one has a char.
I need to assemble two strings - one is all the chars combine, and the other one is all the chars but without coupled duplicates (ABABAAAB -> ABABAB)
Can this be done with streams?
I tried doing it like this:
Optional<String> text = containersList.stream()
.map(Container::getLetter)
.reduce((letter,accumalator) -> accumalator += letter);
Optional<String> dupText = session.containersList().stream()
.map(Container::getLetter)
.reduce((letter, accumalator) ->{
if ((accumalator.endsWith(letter) == false)) {
accumalator += letter;
}
return accumalator;
});
Using StreamEx library
You can do this in a single Stream pipeline using the StreamEx library.
List<Container> containersList = Arrays.asList(new Container("A"), new Container("B"), new Container("A"), new Container("A"), new Container("B"));
String[] result =
StreamEx.of(containersList)
.map(Container::getLetter)
.groupRuns(Object::equals)
.collect(MoreCollectors.pairing(
MoreCollectors.flatMapping(List::stream, Collectors.joining()),
MoreCollectors.mapping(l -> l.get(0), Collectors.joining()),
(s1, s2) -> new String[] { s1, s2 }
));
System.out.println(result[0]);
System.out.println(result[1]);
This code creates a Stream of the containers and maps each of those to their letter.
Then, the method groupRuns collapses into a List the successive elements that matches the given predicate. In this case, the predicate is the equality of the String: so if you start with the stream [A, A, B], this method will collapse it into the Stream [List(A, A), List(B)] (the first element is the list of 2 A successive elements in the input).
Finally, this is collected with the pairing collector that allows to collect into two different collector. The first one joins the flat map result of each list while the second one joins only the first element of the list (hence removing the successive elements).
The result is stored inside an array which just serves as a holder for two values.
Output:
ABAAB
ABAB
Using the Stream API directly
If you want to stay with the current API and not using a library, your best bet would be to write a custom Collector:
public static void main(String[] args) {
List<Container> containersList = Arrays.asList(new Container("A"), new Container("B"), new Container("A"), new Container("A"), new Container("B"));
String[] result = containersList.stream().parallel().map(Container::getLetter).collect(ContainerCollector.collector());
System.out.println(result[0]);
System.out.println(result[1]);
}
private static final class ContainerCollector {
private StringBuilder text = new StringBuilder();
private StringBuilder dupText = new StringBuilder();
private void accept(String letter) {
text.append(letter);
if (dupText.indexOf(letter, dupText.length() - letter.length()) < 0) {
dupText.append(letter);
}
}
private ContainerCollector combine(ContainerCollector other) {
text.append(other.text);
other.dupText.codePoints().forEach(i -> {
String letter = new String(Character.toChars(i));
if (dupText.indexOf(letter, dupText.length() - letter.length()) < 0) {
dupText.append(letter);
}
});
return this;
}
private String[] finish() {
return new String[] { text.toString(), dupText.toString() };
}
private static Collector<String, ?, String[]> collector() {
return Collector.of(ContainerCollector::new, ContainerCollector::accept, ContainerCollector::combine, ContainerCollector::finish);
}
}
This custom collector builds the text and dupText when each letter is accepted. For the text String, the letter is always appended. For the dupText, the letter is only appended if the last one is different.
The combiner code (ran in case of parallel execution) is a bit tricky for the dupText: the second one is appended if it does not start with the end of the first one. Otherwise, the first letter is dropped and the rest is appended.
The output is the same.
I would make it in two separate operations. First, to get the text with duplicates:
String dupText = containersList.stream()
.map(Container::getLetter)
.collect(Collectors.joining());
And the second to remove the duplicates using the regexp:
String text = dupText.replaceAll("(.)\\1+", "$1");
While it's technically two-pass solution, it does not traverse input container twice and, I believe, it should be quite fast, at least not slower than other proposed solutions. And it's simple and does not require third-party libraries.
Using streams is the right choice for unpacking containers. Removing repeated characters, however, is easier with loops.
I'd recommend to use the best out of both worlds:
import java.util.ArrayList;
import java.util.Collection;
import java.util.stream.Collectors;
class Container {
private char letter;
public String getLetter() {
return Character.toString(letter);
}
public static Container of(char letter) {
Container container = new Container();
container.letter = letter;
return container;
}
}
public class T {
public static void main(String[] args) {
Collection<Container> containersList = new ArrayList<>();
containersList.add(Container.of('A'));
containersList.add(Container.of('B'));
containersList.add(Container.of('A'));
containersList.add(Container.of('B'));
containersList.add(Container.of('A'));
containersList.add(Container.of('A'));
containersList.add(Container.of('A'));
containersList.add(Container.of('B'));
// at first join characters, don't bother about duplicates
String text = containersList.stream()
.map(Container::getLetter)
.collect(Collectors.joining());
// afterwards remove duplicates
StringBuilder dupText = new StringBuilder();
Character lastLetter = null;
for (Character c : text.toCharArray()) {
if (c.equals(lastLetter))
continue;
dupText.append(c);
lastLetter = c;
}
System.out.println(text);
System.out.println(dupText);
}
}
A solution without loops could look like this:
// at first join characters, don't bother about duplicates
String text = containersList.stream()
.map(Container::getLetter)
.collect(Collectors.joining());
// afterwards remove duplicates
String dupText = text.chars()
.mapToObj(i -> Character.toString((char)i))
.reduce((left,right) -> {
if (left.endsWith(right))
return left;
return left+right;
})
.get();
If you must not iterate twice, use this:
MyBuilder myBuilder = new MyBuilder();
containersList.stream()
.map(Container::getLetter)
.forEachOrdered(myBuilder::accept);
System.out.println(myBuilder.text);
System.out.println(myBuilder.dupText);
with a builder like this:
class MyBuilder {
StringBuilder text = new StringBuilder();
StringBuilder dupText = new StringBuilder();
String lastLetter;
void accept(String letter) {
text.append(letter);
if (letter.equals(lastLetter) == false) {
dupText.append(letter);
}
lastLetter = letter;
}
}
Another solution using my StreamEx library:
Collector<Entry<String, Long>, ?, String[]> collector = MoreCollectors.pairing(
Collectors.mapping(e -> StreamEx.constant(e.getKey(), e.getValue()).joining(),
Collectors.joining()),
Collectors.mapping(e -> e.getKey(), Collectors.joining()),
(s1, s2) -> new String[] { s1, s2 }
);
String[] result = StreamEx.of(containersList).map(Container::getLetter)
.runLengths().collect(collector);
System.out.println(result[0]);
System.out.println(result[1]);
It should be more performant than solution proposed by #Tunaki when long series of equal letters appear: instead of collecting them to lists (via groupRuns()) this solution just counts them (via runLengths())
This question already has answers here:
What's the best way to build a string of delimited items in Java?
(37 answers)
Closed 4 years ago.
I am looking for a method to combine an array of strings into a delimited
String. An opposite to split().
Wanted to ask the forum before I try writing my own (since the JDK has everything)
There's no method in the JDK for this that I'm aware of. Apache Commons Lang has various overloaded join() methods in the StringUtils class that do what you want.
There has been an open feature request since at least 2009. The long and short of it is that it will part of the functionality of JDK 8's java.util.StringJoiner class. http://download.java.net/lambda/b81/docs/api/java/util/StringJoiner.html
Here is the Oracle issue if you are interested.
http://bugs.sun.com/view_bug.do?bug_id=5015163
Here is an example of the new JDK 8 StringJoiner on an array of String
String[] a = new String[]{"first","second","third"};
StringJoiner sj = new StringJoiner(",");
for(String s:a) sj.add(s);
System.out.println(sj); //first,second,third
A utility method in String makes this even simpler:
String s = String.join(",", stringArray);
You can sneak this functionality out of the Arrays utility package.
import java.util.Arrays;
...
String delim = ":",
csv_record = "Field0:Field1:Field2",
fields[] = csv_record.split(delim);
String rebuilt_record = Arrays.toString(fields)
.replace(", ", delim)
.replaceAll("[\\[\\]]", "");
I got the following example here
/*
7) Join Strings using separator >>>AB$#$CD$#$EF
*/
import org.apache.commons.lang.StringUtils;
public class StringUtilsTrial {
public static void main(String[] args) {
// Join all Strings in the Array into a Single String, separated by $#$
System.out.println("7) Join Strings using separator >>>"
+ StringUtils.join(new String[] { "AB", "CD", "EF" }, "$#$"));
}
}
Google also provides a joiner class in their Google Collections library:
Joiner API
Google Collections
There are several examples on DZone Snippets if you want to roll your own that works with a Collection. For example:
public static String join(AbstractCollection<String> s, String delimiter) {
if (s == null || s.isEmpty()) return "";
Iterator<String> iter = s.iterator();
StringBuilder builder = new StringBuilder(iter.next());
while( iter.hasNext() )
{
builder.append(delimiter).append(iter.next());
}
return builder.toString();
}
If you have an int[], Arrays.toString() is the easiest way.
Based on all the previous answers:
public static String join(Iterable<? extends Object> elements, CharSequence separator)
{
StringBuilder builder = new StringBuilder();
if (elements != null)
{
Iterator<? extends Object> iter = elements.iterator();
if(iter.hasNext())
{
builder.append( String.valueOf( iter.next() ) );
while(iter.hasNext())
{
builder
.append( separator )
.append( String.valueOf( iter.next() ) );
}
}
}
return builder.toString();
}
For Android: in
android.text.TextUtils
there are methods:
public static String join (CharSequence delimiter, Iterable tokens)
public static String join (CharSequence delimiter, Object[] tokens)
Returns a string containing the tokens joined by delimiters.
tokens -- an array objects to be joined. Strings will be formed from the objects by calling object.toString().
Since JDK8 I love Streams and Lambdas a lot, so I would suggest:
public static String join( String delimiter, String[] array )
{
return Arrays.asList( array ).stream().collect( Collectors.joining( delimiter ) );
}
For the sake of completeness, I'd like to add that you cannot reverse String#split in general, as it accepts a regular expression.
"hello__world".split("_+"); Yields ["hello", "world"].
"hello_world".split("_+"); Yields ["hello", "world"].
These yield identical results from a different starting point. splitting is not a one-to-one operation, and is thus non-reversible.
This all being said, if you assume your parameter to be a fixed string, not regex, then you can certainly do this using one of the many posted answers.
This one is not bad too :
public static String join(String delimitor,String ... subkeys) {
String result = null;
if(null!=subkeys && subkeys.length>0) {
StringBuffer joinBuffer = new StringBuffer(subkeys[0]);
for(int idx=1;idx<subkeys.length;idx++) {
joinBuffer.append(delimitor).append(subkeys[idx]);
}
result = joinBuffer.toString();
}
return result;
}
I wrote this one:
public static String join(Collection<String> col, String delim) {
StringBuilder sb = new StringBuilder();
Iterator<String> iter = col.iterator();
if (iter.hasNext())
sb.append(iter.next());
while (iter.hasNext()) {
sb.append(delim);
sb.append(iter.next());
}
return sb.toString();
}
Collection isn't supported by JSP, so for TLD I wrote:
public static String join(List<?> list, String delim) {
int len = list.size();
if (len == 0)
return "";
StringBuilder sb = new StringBuilder(list.get(0).toString());
for (int i = 1; i < len; i++) {
sb.append(delim);
sb.append(list.get(i).toString());
}
return sb.toString();
}
and put to .tld file:
<?xml version="1.0" encoding="UTF-8"?>
<taglib version="2.1" xmlns="http://java.sun.com/xml/ns/javaee"
<function>
<name>join</name>
<function-class>com.core.util.ReportUtil</function-class>
<function-signature>java.lang.String join(java.util.List, java.lang.String)</function-signature>
</function>
</taglib>
and use it in JSP files as:
<%#taglib prefix="funnyFmt" uri="tag:com.core.util,2013:funnyFmt"%>
${funnyFmt:join(books, ", ")}
Below code gives a basic idea. This is not best solution though.
public static String splitJoin(String sourceStr, String delim,boolean trim,boolean ignoreEmpty){
return join(Arrays.asList(sourceStr.split(delim)), delim, ignoreEmpty);
}
public static String join(List<?> list, String delim, boolean ignoreEmpty) {
int len = list.size();
if (len == 0)
return "";
StringBuilder sb = new StringBuilder(list.get(0).toString());
for (int i = 1; i < len; i++) {
if (ignoreEmpty && !StringUtils.isBlank(list.get(i).toString())) {
sb.append(delim);
sb.append(list.get(i).toString().trim());
}
}
return sb.toString();
}
If you use jdk8 see #Nathaniel Johnson's answer as that is better.
I think that many of the answer here are complex or not easily read. With branch prediction so efficient why you simply not use a if statement?
public static String join(List<String> fooList){
if (fooList.isEmpty()) return "";
StringBuilder sb = null;
for (String element : fooList) {
if (sb == null) {
sb = new StringBuilder();
} else {
sb.append(", ");
}
sb.append(element);
}
return sb.toString();
}
Something that should be mentioned is that Apache uses a simple for loop with an if statement inside it like this (it uses an array as its index to know the first element), and openjdk 8 does the same but in separated methods calls instead of a simple loop.
I like this better:
public String join(Collection<String> strCollection, String delimiter) {
String joined = "";
int noOfItems = 0;
for (String item : strCollection) {
joined += item;
if (++noOfItems < strCollection.size())
joined += delimiter;
}
return joined;
}
It is the neatest solution I have found so far.
(Don't worry about the use of raw String objects instead of StringBuilder. Modern Java compilers use StringBuilder anyway, but this code is more readable).
While working in a Java app, I recently needed to assemble a comma-delimited list of values to pass to another web service without knowing how many elements there would be in advance. The best I could come up with off the top of my head was something like this:
public String appendWithDelimiter( String original, String addition, String delimiter ) {
if ( original.equals( "" ) ) {
return addition;
} else {
return original + delimiter + addition;
}
}
String parameterString = "";
if ( condition ) parameterString = appendWithDelimiter( parameterString, "elementName", "," );
if ( anotherCondition ) parameterString = appendWithDelimiter( parameterString, "anotherElementName", "," );
I realize this isn't particularly efficient, since there are strings being created all over the place, but I was going for clarity more than optimization.
In Ruby, I can do something like this instead, which feels much more elegant:
parameterArray = [];
parameterArray << "elementName" if condition;
parameterArray << "anotherElementName" if anotherCondition;
parameterString = parameterArray.join(",");
But since Java lacks a join command, I couldn't figure out anything equivalent.
So, what's the best way to do this in Java?
Pre Java 8:
Apache's commons lang is your friend here - it provides a join method very similar to the one you refer to in Ruby:
StringUtils.join(java.lang.Iterable,char)
Java 8:
Java 8 provides joining out of the box via StringJoiner and String.join(). The snippets below show how you can use them:
StringJoiner
StringJoiner joiner = new StringJoiner(",");
joiner.add("01").add("02").add("03");
String joinedString = joiner.toString(); // "01,02,03"
String.join(CharSequence delimiter, CharSequence... elements))
String joinedString = String.join(" - ", "04", "05", "06"); // "04 - 05 - 06"
String.join(CharSequence delimiter, Iterable<? extends CharSequence> elements)
List<String> strings = new LinkedList<>();
strings.add("Java");strings.add("is");
strings.add("cool");
String message = String.join(" ", strings);
//message returned is: "Java is cool"
You could write a little join-style utility method that works on java.util.Lists
public static String join(List<String> list, String delim) {
StringBuilder sb = new StringBuilder();
String loopDelim = "";
for(String s : list) {
sb.append(loopDelim);
sb.append(s);
loopDelim = delim;
}
return sb.toString();
}
Then use it like so:
List<String> list = new ArrayList<String>();
if( condition ) list.add("elementName");
if( anotherCondition ) list.add("anotherElementName");
join(list, ",");
In the case of Android, the StringUtils class from commons isn't available, so for this I used
android.text.TextUtils.join(CharSequence delimiter, Iterable tokens)
http://developer.android.com/reference/android/text/TextUtils.html
The Google's Guava library has com.google.common.base.Joiner class which helps to solve such tasks.
Samples:
"My pets are: " + Joiner.on(", ").join(Arrays.asList("rabbit", "parrot", "dog"));
// returns "My pets are: rabbit, parrot, dog"
Joiner.on(" AND ").join(Arrays.asList("field1=1" , "field2=2", "field3=3"));
// returns "field1=1 AND field2=2 AND field3=3"
Joiner.on(",").skipNulls().join(Arrays.asList("London", "Moscow", null, "New York", null, "Paris"));
// returns "London,Moscow,New York,Paris"
Joiner.on(", ").useForNull("Team held a draw").join(Arrays.asList("FC Barcelona", "FC Bayern", null, null, "Chelsea FC", "AC Milan"));
// returns "FC Barcelona, FC Bayern, Team held a draw, Team held a draw, Chelsea FC, AC Milan"
Here is an article about Guava's string utilities.
In Java 8 you can use String.join():
List<String> list = Arrays.asList("foo", "bar", "baz");
String joined = String.join(" and ", list); // "foo and bar and baz"
Also have a look at this answer for a Stream API example.
in Java 8 you can do this like:
list.stream().map(Object::toString)
.collect(Collectors.joining(delimiter));
if list has nulls you can use:
list.stream().map(String::valueOf)
.collect(Collectors.joining(delimiter))
it also supports prefix and suffix:
list.stream().map(String::valueOf)
.collect(Collectors.joining(delimiter, prefix, suffix));
You can generalize it, but there's no join in Java, as you well say.
This might work better.
public static String join(Iterable<? extends CharSequence> s, String delimiter) {
Iterator<? extends CharSequence> iter = s.iterator();
if (!iter.hasNext()) return "";
StringBuilder buffer = new StringBuilder(iter.next());
while (iter.hasNext()) buffer.append(delimiter).append(iter.next());
return buffer.toString();
}
Use an approach based on java.lang.StringBuilder! ("A mutable sequence of characters. ")
Like you mentioned, all those string concatenations are creating Strings all over. StringBuilder won't do that.
Why StringBuilder instead of StringBuffer? From the StringBuilder javadoc:
Where possible, it is recommended that this class be used in preference to StringBuffer as it will be faster under most implementations.
I would use Google Collections. There is a nice Join facility.
http://google-collections.googlecode.com/svn/trunk/javadoc/index.html?com/google/common/base/Join.html
But if I wanted to write it on my own,
package util;
import java.util.ArrayList;
import java.util.Iterable;
import java.util.Collections;
import java.util.Iterator;
public class Utils {
// accept a collection of objects, since all objects have toString()
public static String join(String delimiter, Iterable<? extends Object> objs) {
if (objs.isEmpty()) {
return "";
}
Iterator<? extends Object> iter = objs.iterator();
StringBuilder buffer = new StringBuilder();
buffer.append(iter.next());
while (iter.hasNext()) {
buffer.append(delimiter).append(iter.next());
}
return buffer.toString();
}
// for convenience
public static String join(String delimiter, Object... objs) {
ArrayList<Object> list = new ArrayList<Object>();
Collections.addAll(list, objs);
return join(delimiter, list);
}
}
I think it works better with an object collection, since now you don't have to convert your objects to strings before you join them.
Apache commons StringUtils class has a join method.
Java 8
stringCollection.stream().collect(Collectors.joining(", "));
Java 8 Native Type
List<Integer> example;
example.add(1);
example.add(2);
example.add(3);
...
example.stream().collect(Collectors.joining(","));
Java 8 Custom Object:
List<Person> person;
...
person.stream().map(Person::getAge).collect(Collectors.joining(","));
Use StringBuilder and class Separator
StringBuilder buf = new StringBuilder();
Separator sep = new Separator(", ");
for (String each : list) {
buf.append(sep).append(each);
}
Separator wraps a delimiter. The delimiter is returned by Separator's toString method, unless on the first call which returns the empty string!
Source code for class Separator
public class Separator {
private boolean skipFirst;
private final String value;
public Separator() {
this(", ");
}
public Separator(String value) {
this.value = value;
this.skipFirst = true;
}
public void reset() {
skipFirst = true;
}
public String toString() {
String sep = skipFirst ? "" : value;
skipFirst = false;
return sep;
}
}
You can use Java's StringBuilder type for this. There's also StringBuffer, but it contains extra thread safety logic that is often unnecessary.
And a minimal one (if you don't want to include Apache Commons or Gauva into project dependencies just for the sake of joining strings)
/**
*
* #param delim : String that should be kept in between the parts
* #param parts : parts that needs to be joined
* #return a String that's formed by joining the parts
*/
private static final String join(String delim, String... parts) {
StringBuilder builder = new StringBuilder();
for (int i = 0; i < parts.length - 1; i++) {
builder.append(parts[i]).append(delim);
}
if(parts.length > 0){
builder.append(parts[parts.length - 1]);
}
return builder.toString();
}
Why not write your own join() method? It would take as parameters collection of Strings and a delimiter String. Within the method iterate over the collection and build up your result in a StringBuffer.
If you are using Spring MVC then you can try following steps.
import org.springframework.util.StringUtils;
List<String> groupIds = new List<String>;
groupIds.add("a");
groupIds.add("b");
groupIds.add("c");
String csv = StringUtils.arrayToCommaDelimitedString(groupIds.toArray());
It will result to a,b,c
If you're using Eclipse Collections, you can use makeString() or appendString().
makeString() returns a String representation, similar to toString().
It has three forms
makeString(start, separator, end)
makeString(separator) defaults start and end to empty strings
makeString() defaults the separator to ", " (comma and space)
Code example:
MutableList<Integer> list = FastList.newListWith(1, 2, 3);
assertEquals("[1/2/3]", list.makeString("[", "/", "]"));
assertEquals("1/2/3", list.makeString("/"));
assertEquals("1, 2, 3", list.makeString());
assertEquals(list.toString(), list.makeString("[", ", ", "]"));
appendString() is similar to makeString(), but it appends to an Appendable (like StringBuilder) and is void. It has the same three forms, with an additional first argument, the Appendable.
MutableList<Integer> list = FastList.newListWith(1, 2, 3);
Appendable appendable = new StringBuilder();
list.appendString(appendable, "[", "/", "]");
assertEquals("[1/2/3]", appendable.toString());
If you can't convert your collection to an Eclipse Collections type, just adapt it with the relevant adapter.
List<Object> list = ...;
ListAdapter.adapt(list).makeString(",");
Note: I am a committer for Eclipse collections.
You should probably use a StringBuilder with the append method to construct your result, but otherwise this is as good of a solution as Java has to offer.
Why don't you do in Java the same thing you are doing in ruby, that is creating the delimiter separated string only after you've added all the pieces to the array?
ArrayList<String> parms = new ArrayList<String>();
if (someCondition) parms.add("someString");
if (anotherCondition) parms.add("someOtherString");
// ...
String sep = ""; StringBuffer b = new StringBuffer();
for (String p: parms) {
b.append(sep);
b.append(p);
sep = "yourDelimiter";
}
You may want to move that for loop in a separate helper method, and also use StringBuilder instead of StringBuffer...
Edit: fixed the order of appends.
With Java 5 variable args, so you don't have to stuff all your strings into a collection or array explicitly:
import junit.framework.Assert;
import org.junit.Test;
public class StringUtil
{
public static String join(String delim, String... strings)
{
StringBuilder builder = new StringBuilder();
if (strings != null)
{
for (String str : strings)
{
if (builder.length() > 0)
{
builder.append(delim).append(" ");
}
builder.append(str);
}
}
return builder.toString();
}
#Test
public void joinTest()
{
Assert.assertEquals("", StringUtil.join(",", null));
Assert.assertEquals("", StringUtil.join(",", ""));
Assert.assertEquals("", StringUtil.join(",", new String[0]));
Assert.assertEquals("test", StringUtil.join(",", "test"));
Assert.assertEquals("foo, bar", StringUtil.join(",", "foo", "bar"));
Assert.assertEquals("foo, bar, x", StringUtil.join(",", "foo", "bar", "x"));
}
}
For those who are in a Spring context their StringUtils class is useful as well:
There are many useful shortcuts like:
collectionToCommaDelimitedString(Collection coll)
collectionToDelimitedString(Collection coll, String delim)
arrayToDelimitedString(Object[] arr, String delim)
and many others.
This can be helpful if you are not already using Java 8 and you are already in a Spring context.
I prefer it against the Apache Commons (although very good as well) for the Collection support which is easier like this:
// Encoding Set<String> to String delimited
String asString = org.springframework.util.StringUtils.collectionToDelimitedString(codes, ";");
// Decoding String delimited to Set
Set<String> collection = org.springframework.util.StringUtils.commaDelimitedListToSet(asString);
You can try something like this:
StringBuilder sb = new StringBuilder();
if (condition) { sb.append("elementName").append(","); }
if (anotherCondition) { sb.append("anotherElementName").append(","); }
String parameterString = sb.toString();
So basically something like this:
public static String appendWithDelimiter(String original, String addition, String delimiter) {
if (original.equals("")) {
return addition;
} else {
StringBuilder sb = new StringBuilder(original.length() + addition.length() + delimiter.length());
sb.append(original);
sb.append(delimiter);
sb.append(addition);
return sb.toString();
}
}
Don't know if this really is any better, but at least it's using StringBuilder, which may be slightly more efficient.
Down below is a more generic approach if you can build up the list of parameters BEFORE doing any parameter delimiting.
// Answers real question
public String appendWithDelimiters(String delimiter, String original, String addition) {
StringBuilder sb = new StringBuilder(original);
if(sb.length()!=0) {
sb.append(delimiter).append(addition);
} else {
sb.append(addition);
}
return sb.toString();
}
// A more generic case.
// ... means a list of indeterminate length of Strings.
public String appendWithDelimitersGeneric(String delimiter, String... strings) {
StringBuilder sb = new StringBuilder();
for (String string : strings) {
if(sb.length()!=0) {
sb.append(delimiter).append(string);
} else {
sb.append(string);
}
}
return sb.toString();
}
public void testAppendWithDelimiters() {
String string = appendWithDelimitersGeneric(",", "string1", "string2", "string3");
}
Your approach is not too bad, but you should use a StringBuffer instead of using the + sign. The + has the big disadvantage that a new String instance is being created for each single operation. The longer your string gets, the bigger the overhead. So using a StringBuffer should be the fastest way:
public StringBuffer appendWithDelimiter( StringBuffer original, String addition, String delimiter ) {
if ( original == null ) {
StringBuffer buffer = new StringBuffer();
buffer.append(addition);
return buffer;
} else {
buffer.append(delimiter);
buffer.append(addition);
return original;
}
}
After you have finished creating your string simply call toString() on the returned StringBuffer.
Instead of using string concatenation, you should use StringBuilder if your code is not threaded, and StringBuffer if it is.
You're making this a little more complicated than it has to be. Let's start with the end of your example:
String parameterString = "";
if ( condition ) parameterString = appendWithDelimiter( parameterString, "elementName", "," );
if ( anotherCondition ) parameterString = appendWithDelimiter( parameterString, "anotherElementName", "," );
With the small change of using a StringBuilder instead of a String, this becomes:
StringBuilder parameterString = new StringBuilder();
if (condition) parameterString.append("elementName").append(",");
if (anotherCondition) parameterString.append("anotherElementName").append(",");
...
When you're done (I assume you have to check a few other conditions as well), just make sure you remove the tailing comma with a command like this:
if (parameterString.length() > 0)
parameterString.deleteCharAt(parameterString.length() - 1);
And finally, get the string you want with
parameterString.toString();
You could also replace the "," in the second call to append with a generic delimiter string that can be set to anything. If you have a list of things you know you need to append (non-conditionally), you could put this code inside a method that takes a list of strings.
//Note: if you have access to Java5+,
//use StringBuilder in preference to StringBuffer.
//All that has to be replaced is the class name.
//StringBuffer will work in Java 1.4, though.
appendWithDelimiter( StringBuffer buffer, String addition,
String delimiter ) {
if ( buffer.length() == 0) {
buffer.append(addition);
} else {
buffer.append(delimiter);
buffer.append(addition);
}
}
StringBuffer parameterBuffer = new StringBuffer();
if ( condition ) {
appendWithDelimiter(parameterBuffer, "elementName", "," );
}
if ( anotherCondition ) {
appendWithDelimiter(parameterBuffer, "anotherElementName", "," );
}
//Finally, to return a string representation, call toString() when returning.
return parameterBuffer.toString();
So a couple of things you might do to get the feel that it seems like you're looking for:
1) Extend List class - and add the join method to it. The join method would simply do the work of concatenating and adding the delimiter (which could be a param to the join method)
2) It looks like Java 7 is going to be adding extension methods to java - which allows you just to attach a specific method on to a class: so you could write that join method and add it as an extension method to List or even to Collection.
Solution 1 is probably the only realistic one, now, though since Java 7 isn't out yet :) But it should work just fine.
To use both of these, you'd just add all your items to the List or Collection as usual, and then call the new custom method to 'join' them.