I am trying to split a string using Splitter class. For example:
List<String> resultList = Splitter.on("|").splitToList(stringToSplit);
My question is: can resultList ever be empty?
I cannot think of any cases that it would be empty. But if I directly use resultList.get(0), it seems buggy. Can I directly call resultList.get(0) without worries?
Well, that depends on how you configure your splitter instance. Here you have to very similar examples, both as input take an empty string.
First case uses "raw" splitter returns a list with only one element -- an empty string:
#Test
public void shouldReturnOneEmptyStringForRegularSplitter() {
//given
String input = "";
Splitter splitter = Splitter.on(',');
//when
List<String> result = splitter.splitToList(input);
//then
assertThat(result).containsOnly("");
}
In the second example the input is the same, but splitter is additionally configured to omit empty strings from a resulting list, which as a result returns an empty list:
#Test
public void shouldReturnOneEmptyStringForCustomized() {
//given
String input = "";
Splitter splitter = Splitter.on(',').omitEmptyStrings();
//when
List<String> result = splitter.splitToList(input);
//then
assertThat(result).isEmpty();
}
The behavior is documented:
For separator-based splitters that do not use omitEmptyStrings, an input string containing n occurrences of the separator naturally yields an iterable of size n + 1. So if the separator does not occur anywhere in the input, a single substring is returned containing the entire input. Consequently, all splitters split the empty string to [""] (note: even fixed-length splitters).
Splitter has couple more options you could configure and change the result base on that.
In your case, if you only use Splitter.on("|") without any additional options, you're guaranteed to always have at least one value in resulting list.
You can use this without checking if empty or not
resultList.stream().findFirst().orElseGet(null)
this will return the first object in your list otherwise it will return null
Base on code of the method Splitter#splitToList here,
It will return an empty ArrayList in case the given string could not be split
(In your case, it happens if the string does not contain "|")
407 public List<String> splitToList(CharSequence sequence) {
408 checkNotNull(sequence);
409
410 Iterator<String> iterator = splittingIterator(sequence);
411 List<String> result = new ArrayList<String>();
412
413 while (iterator.hasNext()) {
414 result.add(iterator.next());
415 }
416
417 return Collections.unmodifiableList(result);
418 }
Thus, you have to ensure the result is not empty or an IndexOutOfBoundsException would be thrown when calling resultList.get(0)
how about checking that stringToSplit contains "|" before call Splitter.on.splitToList
Related
I have a Collection of strings.
I need to replace some characters with given relevant characters.
I was tried this. But my code but doesn't work correctly.
public class MyPattern {
public static void main(String[] args)
{
String items;
Collection<String> arr = new ArrayList<String>();
arr.add("I00.30"); //Expected output: 100.30
arr.add("8B4.99"); //Expected output: 884.99
arr.add("B37.2B"); //Expected output: 837.28
arr.add("292.b4"); //Expected output: 262.64
arr.add("24|.7O"); //Expected output: 241.70
arr.add("B55.I4"); //Expected output: 855.14
arr.add("444.07"); //Expected output: 444.07
for(String item:arr)
{
System.out.println(item);
items = item.toUpperCase().replaceAll("l", "1")
.replaceAll("l", "1")
.replaceAll("i", "1")
.replaceAll("|", "1")
.replaceAll("G", "6")
.replaceAll("b", "6")
.replaceAll("B", "8")
.replaceAll("O", "0");
System.out.println(items);
}
}
}
The letters passed in the replaceAll() method has to be replaced in every item in the collection.
Is there any way to find the irrelevant character and replace it with the number (as shown in the code above) ?
Your code is resigning the variable item, it will not affect the list contents.
To be able to do that, you might change the type of variable arr to List. With that, you can iterate over it by using the traditional (index based) for loop.
List<String> arr = // initializing the list
for (int i = 0; i < arr.size(); i++) {
String item = replace(arr.get(i)); // method replace provided below
arr.set(i, item);
}
Another option is to use Java 8 replaceAll() method, which expects a function that will be applied to every element in the collection.
arr.replaceAll(str -> replace(str));
public static String replace(String source) {
return source.toUpperCase()
.replaceAll("[LI|]", "1")
.replaceAll("[GB]", "6")
.replace("O", "0");
}
Note that method replaceAll() that expect a regular expression is more expensive than replace(). Hence, when you don't need a regex, its better to use any flavor of replace() (with either char or String arguments).
Here you can benefit from replaceAll() by processing characters L, L and | in one go with a regular expression "[LI|]".
For more information on regular expressions take a look at this tutorial
There's also a minor issue in your code:
After toUpperCase() has been applied, it doesn't make sense to try to replace lowercase letters like 'l' or 'i'.
There's a clash "b", "6" and "B", "8".
I hope with all these hints you'll be able to manage to get it working.
Instead of using the for loop. You can use, stream.
For example, if you want to change i to 1 in every element, you can get it using the following code. And you want to
Collection<String> arr_new = arr.stream().map(String::toLowerCase).map(t->t.replaceAll("i",1)).map(String::toUpperCase);
Here, the first map converts the String to lower-case, the second map do replace what you need and the third map convert it to the upper case.
I hope this is what you are looking for.
I have a loop which update an String object:
String result = "";
for (SomeObject obj: someObjectList) {
result = someMetohd(obj, result);
}
An implementation of someMethod is irrelevant:
private String someMethod(SomeObject obj, String result) {
result = result.concat(obj.toString());
return result;
}
And I want to use Stream instead a loop. How to implement it with Stream?
#SuppressWarnings("OptionalGetWithoutIsPresent")
String result = Stream.concat(Stream.of(""), someObjectList.stream())
.reduce(this::someMethod)
.get();
Your someMethod should be associative as specified in the documentation, however this is only important for parallel streams, while your code is explicitly sequential
As you always add to the result, you can consider it a first element of the stream and then use reduce method which will always merge first two elements - current result and next element
result has to be the first parameter of your someMethod
Because all elements in the stream have to be of the same type, while you have String result and SomeObject elements, you need to change the signature of someMethod to accept two Objects (and do the casts inside the method): private String someMethod(Object result, Object obj). This is the most ugly part of this solution.
You can inline the initial value of the result - no need to define result upfront
You might want to change this::someMethod depending on where this method is declared
Finally, you don't need to worry about handling Optional result, because the stream always has at least one element so it's safe to just call get()
final StringBuilder resultBuilder = new StringBuilder();
someObjectList.stream().map(SomeObject::toString).forEach(resultBuilder::append);
final String result = resultBuilder.toString();
To know more about Streams, you can check this page: http://winterbe.com/posts/2014/07/31/java8-stream-tutorial-examples/, I think it's very helpful.
Although the functional equivalent of what you're trying to achieve here is possible with streams, it's worth reminding you that functional and iterative ways of thinking are not necessarily compatible.
Generally you think of each element on its own, and you don't have visibility over other elements, unless you're using a special function like reduce.
Here's something that does what you've asked for:
final List<Object> objectList = Arrays.asList("a", "b", "c", "d");
String concatString = objectList.stream()
.map(e -> e.toString())
.reduce((result, element) -> result.concat(e))
.get();
Map turns the entire stream into a list, but with the toString function called separately on every element. Reduce is more complex. It can be described as an accumulation. It executes a function between the result, and the current element. In this case, it takes the first element, and concatenates it to the second. It then takes the first/second concatenation, and applies the same function to the third. And so on.
Instead of dealing with lambdas, you can also pass in methods directly, to tighten up your code a bit:
String result = objectList.stream()
.map(Object::toString)
.reduce(String::concat)
.get();
I have the following:
[[Statistics (pH)], [ Upright Normal Recumbent Normal Total]]
I want to split the first element of the second element on whitespace so that I end up with:
[[Statistics (pH)], [Upright,Normal,Recumbent,Normal,Total]]
My code so far:
for (ArrayList<List<String>> row2 : StatspH) {
row2.get(1).get(0).split("\\s");
}
but nothing happens
Java Strings are immutable, so you need to store the return value of split("\\s") in the correct List.
I recommend something like
for (ArrayList<List<String>> row2 : StatspH) {
List<String> stats = row2.get(1);
// remove() returns the object that was removed
String allStats = stats.remove(0);
Collections.addAll(stats, allStats.split("\\s"));
}
Note that we're removing the original string first, then adding all of the 'split' values.
A newly created ArrayList without adding any elements to it looks just like an ArrayList with an empty string added in it.
This makes me feel that a newly created ArrayList even without adding any elements in it has a default empty string or element added in it.
package cracking;
import java.util.ArrayList;
public class Ask
{
public static void main(String[] args)
{
ArrayList<String> al = new ArrayList<String>();
System.out.println(al.size());//0
System.out.println(al);//[]
al.add("");
System.out.println(al.size());//1
System.out.println(al);//[]
}
}
However, the discrepancy between the size of ArrayList in the two scenarios makes them inconsistent, especially since the two ArrayList when printed out look just the same.
To maintain consistency, I feel, either of two would've been good:
The size of the ArrayList which is just created, i.e., even before
adding any element to it should show 1 implying empty string or
element, since even adding to the ArrayList makes it look the same.
Printing out a newly created ArrayList should not be allowed, it
should just print NULL or something instead of showing [] like it
shows now
You are being tricked by the toString implementation of the AbstractCollection. See here:
public String More ...toString() {
Iterator<E> i = iterator();
if (! i.hasNext())
return "[]";
StringBuilder sb = new StringBuilder();
sb.append('[');
for (;;) {
E e = i.next();
sb.append(e == this ? "(this Collection)" : e);
if (! i.hasNext())
return sb.append(']').toString();
sb.append(", ");
}
}
Your collection has a size of 1, but it's not printing anything, including the comma character , because it only has size one. Try adding multilpe emptry Strings and you'll see the result more clearly.
al.add("");
al.add("");
al.add("");
al.add("");
al.add("");
System.out.println(al); // Prints [, , , , ]
[] is representative of an empty list. This is by convention, but I think you'll find this reasonable.
And printing the ArrayList uses toString on each of the objects that it contains to show a printed representation of those contents. When a string is printed (using toString) the quotes aren't included.
Consider the following amendment to your code:
ArrayList<String> al = new ArrayList<String>();
al.add("");
al.add("");
al.add("");
System.out.println(al);//[]
The output is:
[, , ]
We might just as well complain about the use of the brackets when printing an ArrayList, but note, that the output of toString is often only useful for debugging, and shouldn't be relied on programatically.
From the docs for toString:
In general, the toString method returns a string that "textually represents" this object. The result should be a concise but informative representation that is easy for a person to read.
In the case of your example, this does break down a bit: printing the ArrayList<strin> with a single empty string isn't particularly informative. If you need to check/validate the contents of your collection, e.g. for debugging, you should also print the length.
ArrayList is using the toString of the object that you place into it as part of its toString call. The reason this appears odd to you is due to the fact that printing an empty string prints nothing.
Ideally, one would not elect to store an empty string in the list, and would instead do a check to ensure that only non-empty strings ever made their way into it.
if(!"".equals(value)) {
al.add(value);
}
I'm getting weird results from Apache Commons Lang's StringUtils.join. Let's say I have:
List<String> values = new LinkedList<>();
values.add("120");
values.add("123456789");
values.add("9000");
values.add("en");
byte[] data = StringUtils.join(values, new char[] {1}).getBytes();
I expected to have 31323001313233343536373839013930303001656e, which is 120.123456789.9000.en, with . as 0x01. But what confuses me is that I'm getting
5b3132302c203132333435363738392c20393030302c20656e5d5b4340333664303437 instead, which converts to [120, 123456789, 9000, en][C#36d047. Is there a gotcha in the way I'm doing this that's causing the weird value?
You're using the following method:
public static <T> String join(T... elements)
Joins the elements of the provided array into a single String containing the provided list of elements.
No separator is added to the joined String. Null objects or empty strings within the array are represented by empty strings.
So this method calls toString() on the list of Strings and on the char array, and joins the results.
You want to pass a char or String separator as second argument instead:
StringUtils.join(values, '.').getBytes();