how can I convert my multiline if statement into a single stream? - java

I have an array of Strings:
String[] myArrayString = ...
It may contain elements or be null. I wrote the following method:
String myName = "";
if(myArrayString != null) {
List<String> temp = new ArrayList<>();
for(String tagID : myArrayString) {
Tag tag = tagManager.resolve(tagID);
if (tag != null) {
temp.add(resolveTagName(tag.getName()));
}
}
myName = temp.stream().map(Object::toString).collect(joining(" "));
myName = myName.substring(myName.lastIndexOf("/") + 1);
return myName;
}
I have a feeling it is not efficient enough and might be done in one single stream (or possibly two). Could you help me with that?

Your code can indeed be converted to use one single stream. The for loop simply transforms each element of the string array (map) and filters the null ones (filter), then performs another transformation (map):
String myName = "";
if(myArrayString != null) {
myName = Arrays.stream(myArrayString)
.map(tagManager::resolve)
.filter(Objects::nonNull)
.map(x -> resolveTagName(x.getName()))
.collect(joining(" "));
myName = myName.substring(myName.lastIndexOf("/") + 1);
return myName;
}
Also note that streams are not necessarily more efficient than a for loop, as it has some overheads as well. To measure performance, you use a profiler, not just by reading the code.

Related

How to convert values to string? [duplicate]

I want the Java code for converting an array of strings into an string.
Java 8+
Use String.join():
String str = String.join(",", arr);
Note that arr can also be any Iterable (such as a list), not just an array.
If you have a Stream, you can use the joining collector:
Stream.of("a", "b", "c")
.collect(Collectors.joining(","))
Legacy (Java 7 and earlier)
StringBuilder builder = new StringBuilder();
for(String s : arr) {
builder.append(s);
}
String str = builder.toString();
Alternatively, if you just want a "debug-style" dump of an array:
String str = Arrays.toString(arr);
Note that if you're really legacy (Java 1.4 and earlier) you'll need to replace StringBuilder there with StringBuffer.
Android
Use TextUtils.join():
String str = TextUtils.join(",", arr);
General notes
You can modify all the above examples depending on what characters, if any, you want in between strings.
DON'T use a string and just append to it with += in a loop like some of the answers show here. This sends the GC through the roof because you're creating and throwing away as many string objects as you have items in your array. For small arrays you might not really notice the difference, but for large ones it can be orders of magnitude slower.
Use Apache commons StringUtils.join(). It takes an array, as a parameter (and also has overloads for Iterable and Iterator parameters) and calls toString() on each element (if it is not null) to get each elements string representation. Each elements string representation is then joined into one string with a separator in between if one is specified:
String joinedString = StringUtils.join(new Object[]{"a", "b", 1}, "-");
System.out.println(joinedString);
Produces:
a-b-1
I like using Google's Guava Joiner for this, e.g.:
Joiner.on(", ").skipNulls().join("Harry", null, "Ron", "Hermione");
would produce the same String as:
new String("Harry, Ron, Hermione");
ETA: Java 8 has similar support now:
String.join(", ", "Harry", "Ron", "Hermione");
Can't see support for skipping null values, but that's easily worked around.
From Java 8, the simplest way I think is:
String[] array = { "cat", "mouse" };
String delimiter = "";
String result = String.join(delimiter, array);
This way you can choose an arbitrary delimiter.
You could do this, given an array a of primitive type:
StringBuffer result = new StringBuffer();
for (int i = 0; i < a.length; i++) {
result.append( a[i] );
//result.append( optional separator );
}
String mynewstring = result.toString();
Try the Arrays.deepToString method.
Returns a string representation of the "deep contents" of the specified
array. If the array contains other arrays as elements, the string
representation contains their contents and so on. This method is
designed for converting multidimensional arrays to strings
Try the Arrays.toString overloaded methods.
Or else, try this below generic implementation:
public static void main(String... args) throws Exception {
String[] array = {"ABC", "XYZ", "PQR"};
System.out.println(new Test().join(array, ", "));
}
public <T> String join(T[] array, String cement) {
StringBuilder builder = new StringBuilder();
if(array == null || array.length == 0) {
return null;
}
for (T t : array) {
builder.append(t).append(cement);
}
builder.delete(builder.length() - cement.length(), builder.length());
return builder.toString();
}
public class ArrayToString
{
public static void main(String[] args)
{
String[] strArray = new String[]{"Java", "PHP", ".NET", "PERL", "C", "COBOL"};
String newString = Arrays.toString(strArray);
newString = newString.substring(1, newString.length()-1);
System.out.println("New New String: " + newString);
}
}
You want code which produce string from arrayList,
Iterate through all elements in list and add it to your String result
you can do this in 2 ways: using String as result or StringBuffer/StringBuilder.
Example:
String result = "";
for (String s : list) {
result += s;
}
...but this isn't good practice because of performance reason. Better is using StringBuffer (threads safe) or StringBuilder which are more appropriate to adding Strings
String[] strings = new String[25000];
for (int i = 0; i < 25000; i++) strings[i] = '1234567';
String result;
result = "";
for (String s : strings) result += s;
//linear +: 5s
result = "";
for (String s : strings) result = result.concat(s);
//linear .concat: 2.5s
result = String.join("", strings);
//Java 8 .join: 3ms
Public String join(String delimiter, String[] s)
{
int ls = s.length;
switch (ls)
{
case 0: return "";
case 1: return s[0];
case 2: return s[0].concat(delimiter).concat(s[1]);
default:
int l1 = ls / 2;
String[] s1 = Arrays.copyOfRange(s, 0, l1);
String[] s2 = Arrays.copyOfRange(s, l1, ls);
return join(delimiter, s1).concat(delimiter).concat(join(delimiter, s2));
}
}
result = join("", strings);
// Divide&Conquer join: 7ms
If you don't have the choise but to use Java 6 or 7 then you should use Divide&Conquer join.
String array[]={"one","two"};
String s="";
for(int i=0;i<array.length;i++)
{
s=s+array[i];
}
System.out.print(s);
Use Apache Commons' StringUtils library's join method.
String[] stringArray = {"a","b","c"};
StringUtils.join(stringArray, ",");
When we use stream we do have more flexibility, like
map --> convert any array object to toString
filter --> remove when it is empty
join --> Adding joining character
//Deduplicate the comma character in the input string
String[] splits = input.split("\\s*,\\s*");
return Arrays.stream(splits).filter(StringUtils::isNotBlank).collect(Collectors.joining(", "));
If you know how much elements the array has, a simple way is doing this:
String appendedString = "" + array[0] + "" + array[1] + "" + array[2] + "" + array[3];

as I find a string in another java

I wish you can help me I want to do is I have two variables of type string
String text = "HELLO HOW ARE YOU";
String value = "abc";
I want to do is see if any character of the variable values in text and contains tested but I only detects a single character but not
if(text.toUpperCase().contains(value.toUpperCase()))throw new Exception("NOT LETTERS");
You could use the List API...
String text = "HELLO HOW ARE YOU";
String value = "abc";
List<String> left = new ArrayList<>(Arrays.asList(text.toUpperCase().split("")));
List<String> right = new ArrayList<>(Arrays.asList(value.toUpperCase().split("")));
boolean hasAny = left.removeAll(right);
Basically this creates a List of each word then removes all the matches from the second in the first. A return value of true means the first List was modified, meaning it had matching values. You might even be able to compare the difference in size to determine how many matches there were.
You could also use Java 8's Stream API...
String text = "HELLO HOW ARE YOU";
String value = "abc";
List<String> left = new ArrayList<>(Arrays.asList(text.toUpperCase().split("")));
List<String> right = new ArrayList<>(Arrays.asList(value.toUpperCase().split("")));
boolean anyMatch = left.stream().anyMatch((String t) -> {
return right.contains(t);
});
Again, this will simply return true if the first List contains ANY of the values in the second List
Now, if you wanted to know which values actually matched, you might be able to use something like...
Set<String> collect = right.stream().filter((String t) -> {
return left.contains(t);
}).collect(Collectors.toSet());
System.out.println(collect);
Which in you example, would print
[A]
You can try like this:
public static boolean findLetters() {
String text = "HELLO HOW ARE YOU";
String value = "abc";
for (int i = 0; i < value.length(); i++) {
if (text.indexOf(value.toUpperCase().charAt(i)) == -1) {
return false;
}
}
return true;
}
Not sure if I understand the question quite well, but you may try this:
String text = "HELLO HOW ARE YOU";
String value = "abc";
for(char c : value.toUpperCase().toCharArray()) {
if (text.indexOf(c) != -1) {
throw new Exception("NOT LETTERS");
}
}

docx4j find and replace

I have docx document with some placeholders. Now I should replace them with other content and save new docx document. I started with docx4j and found this method:
public static List<Object> getAllElementFromObject(Object obj, Class<?> toSearch) {
List<Object> result = new ArrayList<Object>();
if (obj instanceof JAXBElement) obj = ((JAXBElement<?>) obj).getValue();
if (obj.getClass().equals(toSearch))
result.add(obj);
else if (obj instanceof ContentAccessor) {
List<?> children = ((ContentAccessor) obj).getContent();
for (Object child : children) {
result.addAll(getAllElementFromObject(child, toSearch));
}
}
return result;
}
public static void findAndReplace(WordprocessingMLPackage doc, String toFind, String replacer){
List<Object> paragraphs = getAllElementFromObject(doc.getMainDocumentPart(), P.class);
for(Object par : paragraphs){
P p = (P) par;
List<Object> texts = getAllElementFromObject(p, Text.class);
for(Object text : texts){
Text t = (Text)text;
if(t.getValue().contains(toFind)){
t.setValue(t.getValue().replace(toFind, replacer));
}
}
}
}
But that only work rarely because usually the placeholders splits across multiple texts runs.
I tried UnmarshallFromTemplate but it work rarely too.
How this problem could be solved?
You can use VariableReplace to achieve this which may not have existed at the time of the other answers.
This does not do a find/replace per se but works on placeholders eg ${myField}
java.util.HashMap mappings = new java.util.HashMap();
VariablePrepare.prepare(wordMLPackage);//see notes
mappings.put("myField", "foo");
wordMLPackage.getMainDocumentPart().variableReplace(mappings);
Note that you do not pass ${myField} as the field name; rather pass the unescaped field name myField - This is rather inflexible in that as it currently stands your placeholders must be of the format ${xyz} whereas if you could pass in anything then you could use it for any find/replace. The ability to use this also exists for C# people in docx4j.NET
See here for more info on VariableReplace or here for VariablePrepare
Good day, I made an example how to quickly replace text to something you need
by regexp. I find ${param.sumname} and replace it in document.
Note, you have to insert text as 'text only'!
Have fun!
WordprocessingMLPackage mlp = WordprocessingMLPackage.load(new File("filepath"));
replaceText(mlp.getMainDocumentPart());
static void replaceText(ContentAccessor c)
throws Exception
{
for (Object p: c.getContent())
{
if (p instanceof ContentAccessor)
replaceText((ContentAccessor) p);
else if (p instanceof JAXBElement)
{
Object v = ((JAXBElement) p).getValue();
if (v instanceof ContentAccessor)
replaceText((ContentAccessor) v);
else if (v instanceof org.docx4j.wml.Text)
{
org.docx4j.wml.Text t = (org.docx4j.wml.Text) v;
String text = t.getValue();
if (text != null)
{
t.setSpace("preserve"); // needed?
t.setValue(replaceParams(text));
}
}
}
}
}
static Pattern paramPatern = Pattern.compile("(?i)(\\$\\{([\\w\\.]+)\\})");
static String replaceParams(String text)
{
Matcher m = paramPatern.matcher(text);
if (!m.find())
return text;
StringBuffer sb = new StringBuffer();
String param, replacement;
do
{
param = m.group(2);
if (param != null)
{
replacement = getParamValue(param);
m.appendReplacement(sb, replacement);
}
else
m.appendReplacement(sb, "");
}
while (m.find());
m.appendTail(sb);
return sb.toString();
}
static String getParamValue(String name)
{
// replace from map or something else
return name;
}
I created a library to publish my solution because it's quite a lot of code: https://github.com/phip1611/docx4j-search-and-replace-util
The workflow is the following:
First step:
// (this method was part of your question)
List<Text> texts = getAllElementFromObject(docxDocument.getMainDocumentPart(), Text.class);
This way we get all actual Text-content in the correct order but without style markup in-between. We can edit the Text-objects (by setValue) and keep styles.
Resulting problem: Search-text/placeholders can be split accoss multiple Text-instances (because there can be style markup that is invisble in-between in original document), e.g. ${FOOBAR}, ${ + FOOBAR}, or $ + {FOOB + AR}
Second step:
Concat all Text-objects to a full string / "complete string"
Optional<String> completeStringOpt = texts.stream().map(Text::getValue).reduce(String::concat);
Third step:
Create a class TextMetaItem. Each TextMetaItem knows for it's Text-object where it's content begins and ends in the complete string. E.g. If the Text-objects for "foo" and "bar" results in the complete string "foobar" than indices 0-2 belongs to "foo"-Text-object and 3-5 to "bar"-Text-object. Build a List<TextMetaItem>
static List<TextMetaItem> buildMetaItemList(List<Text> texts) {
final int[] index = {0};
final int[] iteration = {0};
List<TextMetaItem> list = new ArrayList<>();
texts.forEach(text -> {
int length = text.getValue().length();
list.add(new TextMetaItem(index[0], index[0] + length - 1, text, iteration[0]));
index[0] += length;
iteration[0]++;
});
return list;
}
Fourth step:
Build a Map<Integer, TextMetaItem> where the key is the index/char in the complete string. This means the map's length equals completeString.length()
static Map<Integer, TextMetaItem> buildStringIndicesToTextMetaItemMap(List<Text> texts) {
List<TextMetaItem> metaItemList = buildMetaItemList(texts);
Map<Integer, TextMetaItem> map = new TreeMap<>();
int currentStringIndicesToTextIndex = 0;
// + 1 important here!
int max = metaItemList.get(metaItemList.size() - 1).getEnd() + 1;
for (int i = 0; i < max; i++) {
TextMetaItem currentTextMetaItem = metaItemList.get(currentStringIndicesToTextIndex);
map.put(i, currentTextMetaItem);
if (i >= currentTextMetaItem.getEnd()) {
currentStringIndicesToTextIndex++;
}
}
return map;
}
interim result:
Now you have enough metadata to delegate every action you want to do on the complete string to the corresponding Text object! (To change the content of Text-objects you just need to call (#setValue()) That's all what's needed in Docx4J to edit text. All style info etc will be preserved!
last step: search and replace
build a method that finds all occurrences of your possible placeholders. You should create a class like FoundResult(int start, int end) that stores begin and end indices of a found value (placeholder) in the complete string
public static List<FoundResult> findAllOccurrencesInString(String data, String search) {
List<FoundResult> list = new ArrayList<>();
String remaining = data;
int totalIndex = 0;
while (true) {
int index = remaining.indexOf(search);
if (index == -1) {
break;
}
int throwAwayCharCount = index + search.length();
remaining = remaining.substring(throwAwayCharCount);
list.add(new FoundResult(totalIndex + index, search));
totalIndex += throwAwayCharCount;
}
return list;
}
using this I build a new list of ReplaceCommands. A ReplaceCommand is a simple class and stores a FoundResult and the new value.
next you must order this list from the last item to the first (order by position in complete string)
now you can write a replace all algorithm because you know what action needs to be done on which Text-object. We did (2) so that replace operations won't invalidate indices of other FoundResults.
3.1.) find Text-object(s) that needs to be changed
3.2.) call getValue() on them
3.3.) edit the string to the new value
3.4.) call setValue() on the Text-objects
This is the code that does all the magic. It executes a single ReplaceCommand.
/**
* #param texts All Text-objects
* #param replaceCommand Command
* #param map Lookup-Map from index in complete string to TextMetaItem
*/
public static void executeReplaceCommand(List<Text> texts, ReplaceCommand replaceCommand, Map<Integer, TextMetaItem> map) {
TextMetaItem tmi1 = map.get(replaceCommand.getFoundResult().getStart());
TextMetaItem tmi2 = map.get(replaceCommand.getFoundResult().getEnd());
if (tmi2.getPosition() - tmi1.getPosition() > 0) {
// it can happen that text objects are in-between
// we can remove them (set to null)
int upperBorder = tmi2.getPosition();
int lowerBorder = tmi1.getPosition() + 1;
for (int i = lowerBorder; i < upperBorder; i++) {
texts.get(i).setValue(null);
}
}
if (tmi1.getPosition() == tmi2.getPosition()) {
// do replacement inside a single Text-object
String t1 = tmi1.getText().getValue();
int beginIndex = tmi1.getPositionInsideTextObject(replaceCommand.getFoundResult().getStart());
int endIndex = tmi2.getPositionInsideTextObject(replaceCommand.getFoundResult().getEnd());
String keepBefore = t1.substring(0, beginIndex);
String keepAfter = t1.substring(endIndex + 1);
tmi1.getText().setValue(keepBefore + replaceCommand.getNewValue() + keepAfter);
} else {
// do replacement across two Text-objects
// check where to start and replace
// the Text-objects value inside both Text-objects
String t1 = tmi1.getText().getValue();
String t2 = tmi2.getText().getValue();
int beginIndex = tmi1.getPositionInsideTextObject(replaceCommand.getFoundResult().getStart());
int endIndex = tmi2.getPositionInsideTextObject(replaceCommand.getFoundResult().getEnd());
t1 = t1.substring(0, beginIndex);
t1 = t1.concat(replaceCommand.getNewValue());
t2 = t2.substring(endIndex + 1);
tmi1.getText().setValue(t1);
tmi2.getText().setValue(t2);
}
}
This can be a problem. I cover how to mitigate broken-up text runs in this answer here: https://stackoverflow.com/a/17066582/125750
... but you might want to consider content controls instead. The docx4j source site has various content control samples here:
https://github.com/plutext/docx4j/tree/master/src/samples/docx4j/org/docx4j/samples

How to avoid string repetition

I have two different string String A="example"; String B="example";
if concat both the string i am getting examplexample. Is there any possibility to avoid repetition of string with same name..??
How about this ?
if(!a.equals(b)){// or if needed use contains() , equalIgnoreCase() depending on your need
//concat
}
The Strings are not different, the same String object is assigned to two different variables ("two pointers to the same memory address").
Consider dumping all strings to a Set before concatenating, this avoids duplicates in the concatenated sequence:
Set<String> strings = new HashSet<String>();
StringBuilder resultBuilder = new StringBuilder();
for (String s:getAllStrings()) { // some magic to get all your strings
if (strings.contains(s))
continue; // has been added already
resultBuilder.append(s); // concatenate
strings.add(s); // put string to set
}
String result = resultBuilder.toString();
You can. Try something like this
private String concatStringExample1(String firstString, String secondString) {
if(firstString.equalsIgnoreCase(secondString)) { // String matched
return firstString; // or return secondString
} else { // Not matched
return firstString.concat(secondString);
}
}
or
private String concatStringExample2(String firstString, String secondString) {
if(firstString != null && firstString != null ) {
if(firstString.toLowerCase().indexOf(secondString.toLowerCase()) >= 0)
return firstString;
else if(secondString.toLowerCase().indexOf(firstString.toLowerCase()) >= 0)
return secondString;
else
return firstString.concat(secondString);
} else {
return "";
}
}
Just create a Set (It has mathematics set behaviour, it won't accept the duplicate objects)
Set<String> strings = new HashSet<String>();
//Fill this set with all the String objects
strings.add(A)
Strings.add(B)
//Now iterate this set and create a String Object
StringBuilder resultBuilder = new StringBuilder();
for(String string:Strings){
resultBuilder.append(string);
}
return resultBuilder.toString()
`

Convert array of strings into a string in Java

I want the Java code for converting an array of strings into an string.
Java 8+
Use String.join():
String str = String.join(",", arr);
Note that arr can also be any Iterable (such as a list), not just an array.
If you have a Stream, you can use the joining collector:
Stream.of("a", "b", "c")
.collect(Collectors.joining(","))
Legacy (Java 7 and earlier)
StringBuilder builder = new StringBuilder();
for(String s : arr) {
builder.append(s);
}
String str = builder.toString();
Alternatively, if you just want a "debug-style" dump of an array:
String str = Arrays.toString(arr);
Note that if you're really legacy (Java 1.4 and earlier) you'll need to replace StringBuilder there with StringBuffer.
Android
Use TextUtils.join():
String str = TextUtils.join(",", arr);
General notes
You can modify all the above examples depending on what characters, if any, you want in between strings.
DON'T use a string and just append to it with += in a loop like some of the answers show here. This sends the GC through the roof because you're creating and throwing away as many string objects as you have items in your array. For small arrays you might not really notice the difference, but for large ones it can be orders of magnitude slower.
Use Apache commons StringUtils.join(). It takes an array, as a parameter (and also has overloads for Iterable and Iterator parameters) and calls toString() on each element (if it is not null) to get each elements string representation. Each elements string representation is then joined into one string with a separator in between if one is specified:
String joinedString = StringUtils.join(new Object[]{"a", "b", 1}, "-");
System.out.println(joinedString);
Produces:
a-b-1
I like using Google's Guava Joiner for this, e.g.:
Joiner.on(", ").skipNulls().join("Harry", null, "Ron", "Hermione");
would produce the same String as:
new String("Harry, Ron, Hermione");
ETA: Java 8 has similar support now:
String.join(", ", "Harry", "Ron", "Hermione");
Can't see support for skipping null values, but that's easily worked around.
From Java 8, the simplest way I think is:
String[] array = { "cat", "mouse" };
String delimiter = "";
String result = String.join(delimiter, array);
This way you can choose an arbitrary delimiter.
You could do this, given an array a of primitive type:
StringBuffer result = new StringBuffer();
for (int i = 0; i < a.length; i++) {
result.append( a[i] );
//result.append( optional separator );
}
String mynewstring = result.toString();
Try the Arrays.deepToString method.
Returns a string representation of the "deep contents" of the specified
array. If the array contains other arrays as elements, the string
representation contains their contents and so on. This method is
designed for converting multidimensional arrays to strings
Try the Arrays.toString overloaded methods.
Or else, try this below generic implementation:
public static void main(String... args) throws Exception {
String[] array = {"ABC", "XYZ", "PQR"};
System.out.println(new Test().join(array, ", "));
}
public <T> String join(T[] array, String cement) {
StringBuilder builder = new StringBuilder();
if(array == null || array.length == 0) {
return null;
}
for (T t : array) {
builder.append(t).append(cement);
}
builder.delete(builder.length() - cement.length(), builder.length());
return builder.toString();
}
public class ArrayToString
{
public static void main(String[] args)
{
String[] strArray = new String[]{"Java", "PHP", ".NET", "PERL", "C", "COBOL"};
String newString = Arrays.toString(strArray);
newString = newString.substring(1, newString.length()-1);
System.out.println("New New String: " + newString);
}
}
You want code which produce string from arrayList,
Iterate through all elements in list and add it to your String result
you can do this in 2 ways: using String as result or StringBuffer/StringBuilder.
Example:
String result = "";
for (String s : list) {
result += s;
}
...but this isn't good practice because of performance reason. Better is using StringBuffer (threads safe) or StringBuilder which are more appropriate to adding Strings
String[] strings = new String[25000];
for (int i = 0; i < 25000; i++) strings[i] = '1234567';
String result;
result = "";
for (String s : strings) result += s;
//linear +: 5s
result = "";
for (String s : strings) result = result.concat(s);
//linear .concat: 2.5s
result = String.join("", strings);
//Java 8 .join: 3ms
Public String join(String delimiter, String[] s)
{
int ls = s.length;
switch (ls)
{
case 0: return "";
case 1: return s[0];
case 2: return s[0].concat(delimiter).concat(s[1]);
default:
int l1 = ls / 2;
String[] s1 = Arrays.copyOfRange(s, 0, l1);
String[] s2 = Arrays.copyOfRange(s, l1, ls);
return join(delimiter, s1).concat(delimiter).concat(join(delimiter, s2));
}
}
result = join("", strings);
// Divide&Conquer join: 7ms
If you don't have the choise but to use Java 6 or 7 then you should use Divide&Conquer join.
String array[]={"one","two"};
String s="";
for(int i=0;i<array.length;i++)
{
s=s+array[i];
}
System.out.print(s);
Use Apache Commons' StringUtils library's join method.
String[] stringArray = {"a","b","c"};
StringUtils.join(stringArray, ",");
When we use stream we do have more flexibility, like
map --> convert any array object to toString
filter --> remove when it is empty
join --> Adding joining character
//Deduplicate the comma character in the input string
String[] splits = input.split("\\s*,\\s*");
return Arrays.stream(splits).filter(StringUtils::isNotBlank).collect(Collectors.joining(", "));
If you know how much elements the array has, a simple way is doing this:
String appendedString = "" + array[0] + "" + array[1] + "" + array[2] + "" + array[3];

Categories