I know how to separate the string to string[]. In my project, I use t = time.split("-") to split a time into a string array t, and t[0]=DD, t[1]=MM. Now I need to convert the string array t into string time with format DD-MM. Did java have functions for that?
Guava does:
String joined = Joiner.on('-').join(parts);
On the other hand, I'd actually suggest not splitting and joining your string to start with. Instead, parse it into an appropriate date/time type (ideally Joda Time), perform any manipulation you need, and then reformat it using a different format pattern.
This will improve your error detection, and basically make your code really reflect the nature of the data you're working with - instead of just talking about splitting and joining text.
You can use String Utils from Apache Commons like this:
String res = StringUtils.join(myStrings, "-");
If you are not looking to use external frameworks, you can roll your own, like this:
StringBuilder res = new StringBuilder();
boolean isFirst = true;
for (String s : myStrings) {
if (!isFirst) {
res.append('-');
} else {
isFirst = false;
}
res.append(s);
}
public static String join(String[] arr, String separator)
{
StringBuilder b = new StringBuilder();
for(int i = 0; i < arr.length; i++)
{
if(i != 0) b.append(separator);
b.append(arr[i]);
}
return b.toString();
}
For this simple example, what's wrong with this?
String time = t[0]+"-"+t[1];
Yes, use StringBuilder when performance matters, but this is much more concise.
Related
I have a simple method where the goal is to add "[" and "]" before and after every "."
For example:
Input: address = "1.1.1.1"
Output: "1[.]1[.]1[.]1"
Here is the function I've written, the problem here is where I have concatenate, why is this happening?
public static String defangIPaddr(String address) {
String returnStr = "";
for(int i = 0; i < address.length(); i++) {
char c = address.charAt(i);
if(c == '.') {
returnStr.concat("[");
returnStr.concat(".");
returnStr.concat("]");
} else {
returnStr.concat(address);
}
}
return returnStr;
}
Use replace() instead, just like this:
public static String defangIPaddr(String address) {
return address.replace(".", "[.]");
}
As has been explained, concat doesn't change the string, but rather returns a new string which is the concatenation of the two strings.
returnStr = returnStr.concat("[");
However, don't use concat. You rarely (if ever) need to use this. Instead use +=:
returnStr += "[";
But you don't even really want to use this, because it inefficiently creates a new string every time you do this. That's fine if the concatenation is a one-off, but you don't want to do this in a loop.
Use a StringBuilder instead, which allows you to append to the string without creating a new object each time:
String returnStr = new StringBuilder();
for(int i = 0; i < address.length(); i++) {
// ...
returnStr.append("[");
// ...
}
return returnStr.toString();
Or, of course, in this simple case, use replace.
I know there are a lot of questions and answers related to similar questions but I couldn't find an answer to my question. This a small snippet of my code:
private String substitute(String text) {
List<Macro> macros = getMacros();
for (Macro macro : macros) {
text = StringUtils.replace(text, macro.getKey(), macro.getValue());
}
return text;
}
Would this be a good way to substitute multiple macros variables in a text String? This creates a new String object on every loop so I am wondering if there's a better way to do this. Ideally I would have used Apache Commons StrSubstitutor class but I can't because of the format of the tokens/macros (different formats and not between a fixed prefix/suffix). I also don't want to use Regex because of performance issues.
According to some coding rules at work I need to mark the argument as final. I wonder if that's indeed good practice here. I know that Strings are immutable and I know that whenever I call StringUtils.replace() it will return me a new String object. But I am wondering if the String argument here should be marked as final as suggested and in the method do something like this:
String result = text;
for (Macro macro : macros) {
result = StringUtils.replace(result, macro.getKey(), macro.getValue());
}
I just don't like this.
Any help would be appreciated. Thanks.
You can use apache velocity to replace a string with keys with the equivalent string with values.
Your concern seems to be valid. String is immutable so it creates multiple objects. You should you either use StringBuilder or StringBuffer.
I wrote a sample for you. Build from here
private static String substitute(String text) {
List<Macro> macros = getMacros();
StringBuffer st = new StringBuffer(text);
for (Macro macro : macros) {
int start = st.indexOf(macro.getKey());
if (start != -1) {
st.replace(start, start + macro.getKey().length(), macro.getValue());
}
}
return st.toString();
}
Cheers!!
If you have some concerns about performance you could use a StringBuilder, which allows you to declare the text param as final:
private String substitute(final String text) {
List<Macro> macros = getMacros();
StringBuilder stringBuilder=new StringBuilder(text);
for(Macro macro: macros) {
int index=stringBuilder.indexOf(macro.getKey());
if (index!=-1) {
stringBuilder.replace(index, index+macro.getKey().length(), macro.getValue());
}
}
return stringBuilder.toString();
}
When doing concatenating lots of strings, I have been recommended to do it using a StringBuilder as such:
StringBuilder someString = new StringBuilder("abc");
someString.append("def");
someString.append("123");
someString.append("moreStuff");
as opposed to
String someString = "abc";
someString = someString + "def";
someString = someString + "123";
someString = someString + "moreStuff";
which would result in the creation of quite a few Strings, as opposed to one.
Now, I need to do a similar thing, but instead of using concatenation I use the replace method of String as such:
String someString = SOME_LARGE_STRING_CONSTANT;
someString = someString.replace("$VARIABLE1", "abc");
someString = someString.replace("$VARIABLE2", "def");
someString = someString.replace("$VARIABLE3", "123");
someString = someString.replace("$VARIABLE4", "moreStuff");
To accomplish the same thing using StringBuilder, I have to do this, just for one replace:
someString.replace(someString.indexOf("$VARIABLE1"), someString.indexOf("$VARIABLE1")+10, "abc");
So my question is: "Is it better to use String.replace and have lots of extra Strings created, or to use StringBuilder still, and have lots of long winded lines such as the one above?"
It is true that StringBuilder tends to be better than concatenating or modifying Strings manually, since StringBuilder is mutable, while String is immutable and you need to create a new String for each modification.
Just to note, though, the Java compiler will automatically convert an example like this:
String result = someString + someOtherString + anotherString;
into something like:
String result = new StringBuilder().append(someString).append(someOtherString).append(anotherString).toString();
That said, unless you're replacing a whole lot of Strings, go for whichever is more readable and more maintainable. So if you can keep it cleaner by having a sequence of 'replace' calls, go ahead and do that over the StringBuilder method. The difference will be negligible compared to the stress you save from dealing with the sad tragedy of micro-optimizations.
PS
For your code sample (which, as OscarRyz pointed out, won't work if you have more than one "$VARIABLE1" in someString, in which case you'll need to use a loop), you could cache the result of the indexOf call in:
someString.replace(someString.indexOf("$VARIABLE1"), someString.indexOf("$VARIABLE1")+10, "abc");
With
int index = someString.indexOf("$VARIABLE1");
someString.replace(index, index+10, "abc");
No need to search the String twice :-)
Guess what? If you are running with Java 1.5+ the concatenation works the same with string literals
String h = "hello" + "world";
and
String i = new StringBuilder().append("hello").append("world").toString();
Are the same.
So, the compiler did the work for you already.
Of course better would be:
String j = "hellworld"; // ;)
As for the second, yeap, that's preferred, but should't be that hard, with the power of "search and replace" and a bit of regex foo
For instance you can define a method like the one in this sample:
public static void replace( String target, String replacement,
StringBuilder builder ) {
int indexOfTarget = -1;
while( ( indexOfTarget = builder.indexOf( target ) ) >= 0 ) {
builder.replace( indexOfTarget, indexOfTarget + target.length() , replacement );
}
}
And your code currently looks like this:
someString = someString.replace("VARIABLE1", "abc");
someString = someString.replace("VARIABLE2", "xyz");
All you have to do is grab text editor an trigger something like this vi search and replace:
%s/^.*("\(.*\)".\s"\(.*\)");/replace("\1","\2",builder);
That read: "take anything in parenthesis and that looks like a string literal, and put it in this other string".
And your code will look from this:
someString = someString.replace("VARIABLE1", "abc");
someString = someString.replace("VARIABLE2", "xyz");
to this:
replace( "VARIABLE1", "abc", builder );
replace( "VARIABLE2", "xyz", builder );
In no time.
Here's a working demo:
class DoReplace {
public static void main( String ... args ) {
StringBuilder builder = new StringBuilder(
"LONG CONSTANT WITH VARIABLE1 and VARIABLE2 and VARIABLE1 and VARIABLE2");
replace( "VARIABLE1", "abc", builder );
replace( "VARIABLE2", "xyz", builder );
System.out.println( builder.toString() );
}
public static void replace( String target, String replacement,
StringBuilder builder ) {
int indexOfTarget = -1;
while( ( indexOfTarget = builder.indexOf( target ) ) > 0 ) {
builder.replace( indexOfTarget, indexOfTarget + target.length() ,
replacement );
}
}
}
I would say go for using StringBuilder but simply write a wrapper that facilitates making the code more readable and thus more maintainable, while still maintaining efficiency. =D
import java.lang.StringBuilder;
public class MyStringBuilder
{
StringBuilder sb;
public MyStringBuilder()
{
sb = new StringBuilder();
}
public void replace(String oldStr, String newStr)
{
int start = -1;
while ((start = sb.indexOf(oldStr)) > -1)
{
int end = start + oldStr.length();
sb.replace(start, end, newStr);
}
}
public void append(String str)
{
sb.append(str);
}
public String toString()
{
return sb.toString();
}
//.... other exposed methods
public static void main(String[] args)
{
MyStringBuilder sb = new MyStringBuilder();
sb.append("old old olD dudely dowrite == pwn");
sb.replace("old", "new");
System.out.println(sb);
}
}
OUTPUT:
new new olD dudely dowrite == pwn
Now you can just use the new version that is one easy liner
MyStringBuilder mySB = new MyStringBuilder();
mySB.append("old dudley dowrite == pwn");
mySB.replace("old", "new"):
Instead of having long lines like that, you could just write a method for replacing parts of StringBuilder strings, something along the lines of this:
public StringBuilder replace(StringBuilder someString, String replaceWhat, String replaceWith) {
return someString.replace(someString.indexOf(replaceWhat), someString.indexOf(replaceWhat)+replaceWhat.length(), replaceWith);
}
May be the String Class internally uses
indexOf
method to find index of old string and replace it with new string.
And also StringBuilder is not threadsafe so it executes much faster.
If your string really is large and you're worried about performance I would recommend writing a class which takes your template text and a list of variables, then reads over the source string character by character and builds the result using StringBuilder. That should be the most efficient both in terms of CPU and memory usage. Also, if you are reading this template text from a file I wouldn't load it all into memory up front. Process it in chunks as you read it from the file.
If you're just looking for a nice way to build a string that's not quite as efficient as StringBuilder but more efficient than appending strings over and over you can use String.format(). It works like sprintf() in C. MessageFormat.format() is an option too but it uses StringBuffer.
There is another related question here: Inserting a Java string in another string without concatenation?
All guys' codes have a bug .try yourReplace("x","xy").It will loop infinitely
Jam Hong is correct - the above solutions all contain the potential to loop infinitely. I guess the lesson to take away here is that micro optimisations can often cause all sorts of horrible issues and don't really save you much. Still, be that as it may - here is a solution that will not infinite loop.
private static void replaceAll(StringBuilder builder, String replaceWhat, String replaceWith){
int occuranceIndex = builder.indexOf(replaceWhat);
int lastReplace = -1;
while(occuranceIndex >= 0){
if(occuranceIndex >= lastReplace){
builder.replace(occuranceIndex, occuranceIndex+replaceWhat.length(), replaceWith);
lastReplace = occuranceIndex + replaceWith.length();
occuranceIndex = builder.indexOf(replaceWhat);
}else{
break;
}
}
}
while it's true that micro optimizations can be problematic, it sometimes depends on the context, for instance, if your replace happens to run inside of a loop with 10000 iterations, your will see a significant performance difference from the "useless" optimizations.
in most cases however, it's best to err on the side of readability
What is the most elegant way to convert a hyphen separated word (e.g. "do-some-stuff") to the lower camel-case variation (e.g. "doSomeStuff") in Java?
Use CaseFormat from Guava:
import static com.google.common.base.CaseFormat.*;
String result = LOWER_HYPHEN.to(LOWER_CAMEL, "do-some-stuff");
With Java 8 there is finally a one-liner:
Arrays.stream(name.split("\\-"))
.map(s -> Character.toUpperCase(s.charAt(0)) + s.substring(1).toLowerCase())
.collect(Collectors.joining());
Though it takes splitting over 3 actual lines to be legible ツ
(Note: "\\-" is for kebab-case as per question, for snake_case simply change to "_")
The following method should handle the task quite efficient in O(n). We just iterate over the characters of the xml method name, skip any '-' and capitalize chars if needed.
public static String toJavaMethodName(String xmlmethodName) {
StringBuilder nameBuilder = new StringBuilder(xmlmethodName.length());
boolean capitalizeNextChar = false;
for (char c:xmlMethodName.toCharArray()) {
if (c == '-') {
capitalizeNextChar = true;
continue;
}
if (capitalizeNextChar) {
nameBuilder.append(Character.toUpperCase(c));
} else {
nameBuilder.append(c);
}
capitalizeNextChar = false;
}
return nameBuilder.toString();
}
Why not try this:
split on "-"
uppercase each word, skipping the first
join
EDIT: On second thoughts... While trying to implement this, I found out there is no simple way to join a list of strings in Java. Unless you use StringUtil from apache. So you will need to create a StringBuilder anyway and thus the algorithm is going to get a little ugly :(
CODE: Here is a sample of the above mentioned aproach. Could someone with a Java compiler (sorry, don't have one handy) test this? And benchmark it with other versions found here?
public static String toJavaMethodNameWithSplits(String xmlMethodName)
{
String[] words = xmlMethodName.split("-"); // split on "-"
StringBuilder nameBuilder = new StringBuilder(xmlMethodName.length());
nameBuilder.append(words[0]);
for (int i = 1; i < words.length; i++) // skip first
{
nameBuilder.append(words[i].substring(0, 1).toUpperCase());
nameBuilder.append(words[i].substring(1));
}
return nameBuilder.toString(); // join
}
If you don't like to depend on a library you can use a combination of a regex and String.format. Use a regex to extract the starting characters after the -. Use these as input for String.format. A bit tricky, but works without a (explizit) loop ;).
public class Test {
public static void main(String[] args) {
System.out.println(convert("do-some-stuff"));
}
private static String convert(String input) {
return String.format(input.replaceAll("\\-(.)", "%S"), input.replaceAll("[^-]*-(.)[^-]*", "$1-").split("-"));
}
}
Here is a slight variation of Andreas' answer that does more than the OP asked for:
public static String toJavaMethodName(final String nonJavaMethodName){
final StringBuilder nameBuilder = new StringBuilder();
boolean capitalizeNextChar = false;
boolean first = true;
for(int i = 0; i < nonJavaMethodName.length(); i++){
final char c = nonJavaMethodName.charAt(i);
if(!Character.isLetterOrDigit(c)){
if(!first){
capitalizeNextChar = true;
}
} else{
nameBuilder.append(capitalizeNextChar
? Character.toUpperCase(c)
: Character.toLowerCase(c));
capitalizeNextChar = false;
first = false;
}
}
return nameBuilder.toString();
}
It handles a few special cases:
fUnnY-cASe is converted to funnyCase
--dash-before-and--after- is converted to dashBeforeAndAfter
some.other$funky:chars? is converted to someOtherFunkyChars
For those who has com.fasterxml.jackson library in the project and don't want to add guava you can use the jaskson namingStrategy method:
new PropertyNamingStrategy.SnakeCaseStrategy.translate(String);
get The Apache commons jar for StringUtils. Then you can use the capitalize method
import org.apache.commons.lang.StringUtils;
public class MyClass{
public String myMethod(String str) {
StringBuffer buff = new StringBuffer();
String[] tokens = str.split("-");
for (String i : tokens) {
buff.append(StringUtils.capitalize(i));
}
return buff.toString();
}
}
As I'm not a big fan of adding a library just for one method, I implemented my own solution (from camel case to snake case):
public String toSnakeCase(String name) {
StringBuilder buffer = new StringBuilder();
for(int i = 0; i < name.length(); i++) {
if(Character.isUpperCase(name.charAt(i))) {
if(i > 0) {
buffer.append('_');
}
buffer.append(Character.toLowerCase(name.charAt(i)));
} else {
buffer.append(name.charAt(i));
}
}
return buffer.toString();
}
Needs to be adapted depending of the in / out cases.
In case you use Spring Framework, you can use provided StringUtils.
import org.springframework.util.StringUtils;
import java.util.Arrays;
import java.util.stream.Collectors;
public class NormalizeUtils {
private static final String DELIMITER = "_";
private NormalizeUtils() {
throw new IllegalStateException("Do not init.");
}
/**
* Take name like SOME_SNAKE_ALL and convert it to someSnakeAll
*/
public static String fromSnakeToCamel(final String name) {
if (StringUtils.isEmpty(name)) {
return "";
}
final String allCapitalized = Arrays.stream(name.split(DELIMITER))
.filter(c -> !StringUtils.isEmpty(c))
.map(StringUtils::capitalize)
.collect(Collectors.joining());
return StringUtils.uncapitalize(allCapitalized);
}
}
Iterate through the string. When you find a hypen, remove it, and capitalise the next letter.
How to insert a string enclosed with double quotes in the beginning of the StringBuilder and String?
Eg:
StringBuilder _sb = new StringBuilder("Sam");
I need to insert the string "Hello" to the beginning of "Sam" and O/p is "Hello Sam".
String _s = "Jam";
I need to insert the string "Hello" to the beginning of "Jam" and O/p is "Hello Jam".
How to achieve this?
The first case is done using the insert() method:
_sb.insert(0, "Hello ");
The latter case can be done using the overloaded + operator on Strings. This uses a StringBuilder behind the scenes:
String s2 = "Hello " + _s;
Other answers explain how to insert a string at the beginning of another String or StringBuilder (or StringBuffer).
However, strictly speaking, you cannot insert a string into the beginning of another one. Strings in Java are immutable1.
When you write:
String s = "Jam";
s = "Hello " + s;
you are actually causing a new String object to be created that is the concatenation of "Hello " and "Jam". You are not actually inserting characters into an existing String object at all.
1 - It is technically possible to use reflection to break abstraction on String objects and mutate them ... even though they are immutable by design. But it is a really bad idea to do this. Unless you know that a String object was created explicitly via new String(...) it could be shared, or it could share internal state with other String objects. Finally, the JVM spec clearly states that the behavior of code that uses reflection to change a final is undefined. Mutation of String objects is dangerous.
Sure, use StringBuilder.insert():
_sb.insert(0, _s);
You can add a string at the front of an already existing one. for example, if I have a name string name, I can add another string name2 by using:
name = name2 + name;
Don't know if this is helpful or not, but it works. No need to use a string builder.
private static void appendZeroAtStart() {
String strObj = "11";
int maxLegth = 5;
StringBuilder sb = new StringBuilder(strObj);
if (sb.length() <= maxLegth) {
while (sb.length() < maxLegth) {
sb.insert(0, '0');
}
} else {
System.out.println("error");
}
System.out.println("result: " + sb);
}
import java.lang.StringBuilder;
public class Program {
public static void main(String[] args) {
// Create a new StringBuilder.
StringBuilder builder = new StringBuilder();
// Loop and append values.
for (int i = 0; i < 5; i++) {
builder.append("abc ");
}
// Convert to string.
String result = builder.toString();
// Print result.
System.out.println(result);
}
}
It is better if you find quotation marks by using the indexof() method and then add a string behind that index.
string s="hai";
int s=s.indexof(""");