Concatenate two or more optional string in Java 8 - java

I have a rather simple question for you guys. In Java 8 it was introduced the Optional type. I have two objects of type Optional<String> and I want to know which is the more elegant way to concatenate them.
Optional<String> first = Optional.ofNullable(/* Some string */);
Optional<String> second = Optional.ofNullable(/* Some other string */);
Optional<String> result = /* Some fancy function that concats first and second */;
In detail, if one of the two original Optional<String> objects was equal to Optional.empty(), I want the whole concatenation to be empty too.
Please, note that I am not asking how to concatenate the evaluation of two Optionals in Java, but how to concatenate two Strings that are inside some Optional.
Thanks in advance.

The solution I found is the following:
first.flatMap(s -> second.map(s1 -> s + s1));
which can be cleaned using a dedicated method, such the following:
first.flatMap(this::concat);
Optional<String> concat(String s) {
second.map(s1 -> s + s1);
}
However, I think that something better can be found.
If we want to generalize to a list or an array of Optional<String>, then we can use something similar to the following.
Optional<String> result =
Stream.of(Optional.of("value1"), Optional.<String>empty())
.reduce(Optional.of(""), this::concat);
// Where the following method id used
Optional<String> concat(Optional<String> first, Optional<String> second) {
return first.flatMap(s -> second.map(s1 -> s + s1));
}
Note that in order to compile the above code, we have to manually bind the type variable of Optional.empty() to String.

You can stream the Optionals and reduce them with a concat.
Optional<String> first = Optional.of("foo");
Optional<String> second = Optional.of("bar");
Optional<String> result = Stream.of(first, second).flatMap(Optional::stream).reduce(String::concat);
If you are using Java 8 replace the flatMap operator with filter(Optional::isPresent).map(Optional::get).
Consider also to use the joining collectors: this will return String, not an Optional<String>.

You can use something like :
Optional<String> result;
result = first.isPresent() && second.isPresent() ? Optional.of(first.get() + second.get()) : Optional.empty();

Any solution that requires a flexible number of optional strings must explicitly use a StringBuilder, rather than rely on the compiler to generate one for you.
String concatThem(Stream<String> stringsin) {
StringBuilder sb = new StringBuilder();
stringsin.forEach(s -> sb.append(s));
return sb.toString();
}
If you have a Stream<Optional<String>> then it becomes:
String concatThem(Stream<Optional<String>> stringsin) {
StringBuilder sb = new StringBuilder();
stringsin.filter(Optional::isPresent).forEach(s -> sb.append(s.get()));
return sb.toString();
}
Otherwise if you have N optional strings you end-up with a heavy cycle of creation and destruction of N-1 single-use StringBuilder objects (generated at compile time) and N-1 strings.
Edit: I had misread, so here's how to do it if any of them is missing to clear it all:
String concatThem(Stream<Optional<String>> stringsin) {
StringBuilder sb = new StringBuilder();
try {
stringsin.forEach(s -> {
if (!s.isPresent()) throw new IllegalArgumentException();
sb.append(s.get())
});
}
catch(IllegalArgumentException ex) {
sb.setLength(0);
}
return sb.toString();
}
This is of course if you insist on using the new API that's light on the syntax and heavy on the execution.

#SafeVarargs
public final Optional<String> concat(Optional<String>... inputs)
{
return Arrays.stream(inputs)
.reduce((left, right) -> left.flatMap(leftValue -> right.map(rightValue -> leftValue + rightValue)))
.get();
}
#Test
public void shouldReturnEmptyIfFirstItemIsEmpty()
{
assertThat(concat(Optional.empty(), Optional.of("B")), is(Optional.empty()));
}
#Test
public void shouldReturnEmptyIfSecondItemIsEmpty()
{
assertThat(concat(Optional.of("A"), Optional.empty()), is(Optional.empty()));
}
#Test
public void shouldConcatIfNoItemIsEmpty()
{
assertThat(concat(Optional.of("A"), Optional.of("B")), is(Optional.of("AB")));
}
Here's an implementation using the reduce method on Stream.

Here's another pretty way:
#Value.Immutable
public abstract class Person {
public Optional<String> firstName() {
return Optional.of("John");
}
public Optional<String> lastName() {
return Optional.of("Smith");
}
public Optional<String> location() {
return Optional.of("Paris");
}
#Value.Lazy
public String concat() {
return Stream.of(firstName(), lastName(), location())
.filter(Optional::isPresent)
.map(Optional::get)
.filter(StringUtils::isNotBlank)
.reduce((first, second) -> first + '.' + second)
.orElse("");
}
}
Note that, as mentioned in other comments, the concat() method performs string concatenations without using a StringBuilder (which might not be performant if you call the method a lot of times). To fix this, in the above example we're using Immutables' [1] #Value.Lazy, which makes sure the concat() method is called once and the result is cached for further calls. Works great!
[1] https://immutables.github.io

Related

I got a optional object, maybe it is null, so how can we use or judge it?

code like this
Optional<String> str = getString();
but str maybe is null.
so how can we use this object? Cause I met an NPE exception when I use.
str.isPresent()
You could wrap the getString() method call in Optional
Optional<String> str = Optional.ofNullable(getString());
if (str.isPresent()) {
// some logic
}
Best way to use Optional depends what you want to with data.
1.) If you just want to consume then do like this.
`
Optional<String> data = getString();
data.ifPresent(name->{
System.out.println(name);
});
`
2.) If you want to transform or manipute then do
data.map(name-> {
return name+"sample";
});
3.) If you using java 9 then ,having else with consumer
data.ifPresentOrElse(name-> {
System.out.println(name);
},()->{
System.out.println("default");
});
It seems that your getString() method is returning null when it should return Optional.empty(). From your comments you suggest that you cannot refactor the getString() method.
Your best option here would be to wrap an Optional inside an Optional to satisfy types like so:
Optional<Optional<String> string = Optional.ofNullable(getString());
Here you may then use:
final boolean getStringReturnedValue = string.isPresent();
if (getStringReturnedValue) {
final Optional<String> returnedValue = string.get();
if (returnedValue.isPresent()) {
final String value = returnedValue.get();
// value usage...
} else {}
} else {}

Private Sorting Rule in a Stream Java

Hey if anyone has an idea I would be really thankfull.
I'm in a Java stream and i would like to sort my list that i'll be returning.
I need to sort the list via TradPrefis ( MyObject::getTradPrefix ).
But this would be way too easy. Because i want to sort following the number at the end of TradPrefix exampleTradPrefix_[NUMBER TO SORT]
Exemple : hello_1
test_2
...
still_there_22
Here is a piece of code so you can imagine easier.
public LinkedHashSet<WsQuestion> get(String quizId, String companyId) {
LinkedHashSet<QuizQuestionWithQuestion> toReturn = quizQuestionRepository.findAllQuizQuestionWithQuestionByQuizId(quizId);
return (toReturn.stream()
.map(this::createWsQuestion)
.sorted(comparing(WsQuestion::getTradPrefix.toString().length()))
.collect(Collectors.toCollection(LinkedHashSet::new)));
}
One method would simply be to split getTradPrefix().toString() by _ and parse the rightmost value as an int, and use it to sort the Stream:
public LinkedHashSet<WsQuestion> get(String quizId, String companyId) {
LinkedHashSet<QuizQuestionWithQuestion> toReturn = quizQuestionRepository.findAllQuizQuestionWithQuestionByQuizId(quizId);
return toReturn.stream()
.map(this::createWsQuestion)
.sorted(Comparator.comparingInt(question -> {
String[] args = question.getTradPrefix().toString().split("_");
return Integer.parseInt(args[args.length - 1]);
}))
.collect(Collectors.toCollection(LinkedHashSet::new));
}
If I where you I would simply put a method on the WsQuestion class, let's call it sort order:
public int getSortOrder() {
return Integer.valueOf(tradPrefix.substring(tradPrefix.lastIndexOf("_") + 1));
}
The Integer parse is needed since comparing strings would give "11" < "2" (thanks Holger for pointing this out). The lastIndexOf() makes sure that any number of underscores are allowed in tradPrefix, as long as there is at least one.
Then simply create a comparotor by using Comparator.comparingInt()
public LinkedHashSet<WsQuestion> get(String quizId, String companyId) {
LinkedHashSet<QuizQuestionWithQuestion> toReturn = quizQuestionRepository.findAllQuizQuestionWithQuestionByQuizId(quizId);
return (toReturn.stream()
.map(this::createWsQuestion)
.sorted(comparingInt(WsQuestion::getSortOrder))
.collect(Collectors.toCollection(LinkedHashSet::new)));
}
You can make a small Comparator like this:
private static final Comparator<String> questionComparator = Comparator.comparingInt(s -> {
String[] pieces = s.split("_");
return Integer.parseInt(pieces[pieces.length-1]);
});
Then use it in your sorted().
Having a separate Comparator will make your code more readable too, since you will be separating concerns.
return toReturn.stream()
.map(this::createWsQuestion)
.sorted(questionComparator)
.collect(Collectors.toCollection(LinkedHashSet::new));

Return Lambda from Method in Java 8?

I have just begun to use Java 8 and I am wondering if there is a way to write a method that returns a Function?
Right now I have method like below:
Function<Integer, String> getMyFunction() {
return new Function<Integer, String>() {
#Override public String apply(Integer integer) {
return "Hello, world!"
}
}
}
Is there a way to write that more succinctly in Java 8? I was hoping this would work but it does not:
Function<Integer, String> getMyFunction() {
return (it) -> { return "Hello, world: " + it }
}
Get rid of your return statement inside of your function definition:
Function<Integer, String> getMyFunction() {
return (it) -> "Hello, world: " + it;
}
You are missing semi colons:
return (it) -> { return "Hello, world: " + it; };
Although as noted it can be shortened to:
return it -> "Hello, world: " + it;
I would like to point out that it might be more appropriate to use the built-in IntFunction in this case:
IntFunction<String> getMyFunction() {
return it -> "Hello, world: " + it;
}
IntFunction is a part of the standard API for functional interfaces which defines a range of good to have interfaces, mostly related to Java primitives.
You could write it simply like that:
Function<Integer, String> function = n -> "Hello, world " + n;
So, the answer for 99% of the cases has been given by #assylias
You are missing semi colons:
return (it) -> { return "Hello, world: " + it; }; Although as noted it
can be shortened to:
return it -> "Hello, world: " + it;
Yet, I think that it's worth it to add that, if you want to assign your lambda to a variable (to use later).
You can do so by typing:
Callable<YourClass> findIt = () -> returnInstanceOfYourClass();
And then you can easily use it, one example of such a use:
if(dontNeedzToWrap()) {
return findIt.call();
}
return Wrapp.withTransaction(() -> findIt.call());
Given, things can be even made simpler if the Wrapp.withTransaction() method accepts the same kind of Callable's as parameters.
(I use this for JPA atm)

Collect HashSet / Java 8 / Regex Pattern / Stream API

Recently I change version of the JDK 8 instead 7 of my project and now I overwrite some code snippets using new features that came with Java 8.
final Matcher mtr = Pattern.compile(regex).matcher(input);
HashSet<String> set = new HashSet<String>() {{
while (mtr.find()) add(mtr.group().toLowerCase());
}};
How I can write this code using Stream API ?
A Matcher-based spliterator implementation can be quite simple if you reuse the JDK-provided Spliterators.AbstractSpliterator:
public class MatcherSpliterator extends AbstractSpliterator<String[]>
{
private final Matcher m;
public MatcherSpliterator(Matcher m) {
super(Long.MAX_VALUE, ORDERED | NONNULL | IMMUTABLE);
this.m = m;
}
#Override public boolean tryAdvance(Consumer<? super String[]> action) {
if (!m.find()) return false;
final String[] groups = new String[m.groupCount()+1];
for (int i = 0; i <= m.groupCount(); i++) groups[i] = m.group(i);
action.accept(groups);
return true;
}
}
Note that the spliterator provides all matcher groups, not just the full match. Also note that this spliterator supports parallelism because AbstractSpliterator implements a splitting policy.
Typically you will use a convenience stream factory:
public static Stream<String[]> matcherStream(Matcher m) {
return StreamSupport.stream(new MatcherSpliterator(m), false);
}
This gives you a powerful basis to concisely write all kinds of complex regex-oriented logic, for example:
private static final Pattern emailRegex = Pattern.compile("([^,]+?)#([^,]+)");
public static void main(String[] args) {
final String emails = "kid#gmail.com, stray#yahoo.com, miks#tijuana.com";
System.out.println("User has e-mail accounts on these domains: " +
matcherStream(emailRegex.matcher(emails))
.map(gs->gs[2])
.collect(joining(", ")));
}
Which prints
User has e-mail accounts on these domains: gmail.com, yahoo.com, tijuana.com
For completeness, your code will be rewritten as
Set<String> set = matcherStream(mtr).map(gs->gs[0].toLowerCase()).collect(toSet());
Marko's answer demonstrates how to get matches into a stream using a Spliterator. Well done, give that man a big +1! Seriously, make sure you upvote his answer before you even consider upvoting this one, since this one is entirely derivative of his.
I have only a small bit to add to Marko's answer, which is that instead of representing the matches as an array of strings (with each array element representing a match group), the matches are better represented as a MatchResult which is a type invented for this purpose. Thus the result would be a Stream<MatchResult> instead of Stream<String[]>. The code gets a little simpler, too. The tryAdvance code would be
if (m.find()) {
action.accept(m.toMatchResult());
return true;
} else {
return false;
}
The map call in his email-matching example would change to
.map(mr -> mr.group(2))
and the OP's example would be rewritten as
Set<String> set = matcherStream(mtr)
.map(mr -> mr.group(0).toLowerCase())
.collect(toSet());
Using MatchResult gives a bit more flexibility in that it also provides offsets of match groups within the string, which could be useful for certain applications.
I don't think you can turn this into a Stream without writing your own Spliterator, but, I don't know why you would want to.
Matcher.find() is a state changing operation on the Matcher object so running each find() in a parallel stream would produce inconsistent results. Running the stream in serial wouldn't have better performance that the Java 7 equivalent and would be harder to understand.
What about Pattern.splitAsStream ?
Stream<String> stream = Pattern.compile(regex).splitAsStream(input);
and then a collector to get a set.
Set<String> set = stream.map(String::toLowerCase).collect(Collectors.toSet());
What about
public class MakeItSimple {
public static void main(String[] args) throws FileNotFoundException {
Scanner s = new Scanner(new File("C:\\Users\\Admin\\Desktop\\TextFiles\\Emails.txt"));
HashSet<String> set = new HashSet<>();
while ( s.hasNext()) {
String r = s.next();
if (r.matches("([^,]+?)#([^,]+)")) {
set.add(r);
}
}
set.stream().map( x -> x.toUpperCase()).forEach(x -> print(x));
s.close();
}
}
Here is the implementation using Spliterator interface.
// To get the required set
Set<String> result = (StreamSupport.stream(new MatcherGroupIterator(pattern,input ),false))
.map( s -> s.toLowerCase() )
.collect(Collectors.toSet());
...
private static class MatcherGroupIterator implements Spliterator<String> {
private final Matcher matcher;
public MatcherGroupIterator(Pattern p, String s) {
matcher = p.matcher(s);
}
#Override
public boolean tryAdvance(Consumer<? super String> action) {
if (!matcher.find()){
return false;
}
action.accept(matcher.group());
return true;
}
#Override
public Spliterator<String> trySplit() {
return null;
}
#Override
public long estimateSize() {
return Long.MAX_VALUE;
}
#Override
public int characteristics() {
return Spliterator.NONNULL;
}
}

Can I decorate Joiner class of Guava

I have a List<String> and we are using Joiner to get the comma separated presentation of that List but now we need to do little enhancement, We need to capitalize the values in the List. Now the code was -
String str = Joiner.on(',').skipNulls().join(myValueList);
But now as I need to capitalize the Strings present in values I need to iterate it first to capitalize and then pass to Joiner to join, but I den't think this is a good approach as it'll iterate the List twice, one to capitalize and then Joiner will iterate to Join.
Is there any other utility method that I'm missing which may do this in one iteration.
How will you do it with Guava?
You can use Iterables.transform()
Iterable<String> upperStrings = Iterables.transform(myValueList, new Function<String,String>() {
public String apply(String input) {
// any transformation possible here.
return (input == null) ? null : input.toUpperCase();
}
});
Str str = Joiner.on(',').skipNulls().join(upperStrings);
About Joachim Sauer's answer:
it can be made a lot less verbose if you move the Function to a place where it can be re-used, in Guava the typical scenario would be to use an enum:
public enum StringTransformations implements Function<String, String>{
LOWERCASE{
#Override
protected String process(final String input){
return input.toLowerCase();
}
},
UPPERCASE{
#Override
protected String process(final String input){
return input.toUpperCase();
}
}
// possibly more transformations here
;
#Override
public String apply(final String input){
return input == null ? null : process(input);
}
protected abstract String process(String input);
}
Now the client code looks like this:
String str =
Joiner
.on(',')
.skipNulls()
.join(
Iterables.transform(myValueList,
StringTransformations.UPPERCASE));
Which I'd call much more readable.
Of course it would be even better (in terms of both memory usage and performance) if you introduced a constant for your Joiner:
private static final Joiner COMMA_JOINER = Joiner.on(',').skipNulls();
// ...
String str = COMMA_JOINER.join(
Iterables.transform(myValueList,
StringTransformations.UPPERCASE));
How about the following?
Joiner.on(',').skipNulls().join(myValueList).toUpperCase()

Categories