I am new to Java stream, I was trying to split integer like (12345) to individual value 1, 2, 3 ... and I did it like as bellow.
int number = 123456;
(number+"").chars()
.mapToObj(e -> (char) e)
.map(e -> Integer.parseInt(""+e))
.forEach(System.out::println);
It works for me though but I am not sure, is it the correct way of doing it?
Without multiple cast and mappings :
Stream.of(String.valueOf(number).split("")).forEach(System.out::println);
Related
I have a String array in java as follows.
String[] text = {"hello", "bye"};
Now I need to use Java stream API to calculate the length of each string in this array. I do not want to use for loop. How can I do that?
Update:
I have checked this link and got the following code to get the sum of the length of each string of this array.
Arrays.stream(text)
.filter(Objects::nonNull)
.mapToInt(String::length)
.reduce(0,Integer::sum);
tl;dr
Use code points rather than char & String#length.
Arrays
.stream( inputs ) // A stream of each `String` object in the array.
.mapToLong( s -> s.codePoints().count() ) // Get the count of code points for each string. Returns a `LongStream`.
.toArray() ; // Returns an array of `long` primitives.
Code points
Your code snippet and the other Answers use String#length. Unfortunately that method is legacy, based on the legacy type char.
That approach fails with most characters. Here is a demonstration of such a failure.
String input = "A😷C" ;
int len = input.length() ;
System.out.println( len ) ;
See that code run live at IdeOne.com.
4
Result is incorrect, reporting “4” for a string of 3 characters.
Instead of char, use code point integer numbers when working with individual characters. A code point is the number permanently assigned to each character defined in Unicode.
For each string of our stream of inputs, get an IntStream whose elements are the code points of each character in that string. Get the count of that stream of code points, which returns a long. Report those code point counts as a LongStream. Collect results to an array of long primitive values.
String[] inputs = { "hello" , "bye" , "A😷C" } ;
long[] lengths =
Arrays
.stream( inputs ) // A stream of each `String` object in the array.
.mapToLong( s -> s.codePoints().count() ) // Get the count of code points for each string. Returns a `LongStream`.
.toArray() ;
See this code run live at IdeOne.com.
[5, 3, 3]
You could simply collect after mapping to String::length:
List<Integer> length = Arrays.stream(text)
.filter(Objects::nonNull)
.map(String::length)
.toList();
This would give you a list with the lengths in the same order as defined in the array. (If below JDK 16, you need to use collect(Collectors.toList()) instead of toList()).
Another option would be to collect to a Map:
Map<String, Integer> stringToLength = Arrays.stream(text)
.filter(Objects::nonNull)
.distinct()
.collect(Collectors.toMap(Function.identity(), String::length));
This would give you a map with the strings as keys, and the length as values. distinct() is used in order to protect yourself from multiple copies of the same string, the map collecting would throw an exception in case of duplicates.
Since you are starting with an array you may want to return an array so this will work.
String[] text = {"hello", "bye"};
int[] lengths = Arrays.stream(text)
.mapToInt(String::length)
.toArray();
System.out.println(Arrays.toString(lengths));
prints
[5, 3]
I'm using java stream and I have a problem. I have this code:
clients.stream()
.map(Client::getDescription)
.map(x -> x.substring(x.lastIndexOf("_v")))
.map(x -> x.substring(2))
.mapToInt(Integer::parseInt)
.max()
.getAsInt();
The description for every client could be "John_abc_v1", "Bill_crce_v2", "Joe_ghhj_V3"... and I get the maximum, in this case 3... but the problem is that v could be lowercase or uppercase and I don't know how to resolve that, so this line is not working .map(x -> x.substring(x.lastIndexOf("_v"))) I get String index out of range -1 when "V" is uppercase. How can I resolve that? Any feedback will be apreciated.
There are various approaches, these are just a few:
Lowercase the entire string, as suggested by Sweeper. This means the V would be converted to v, so you could find it with lastIndexOf.
Use regex to replace the parts of the string you don't want, i.e. everything but the trailing digits:
.map(x -> x.replaceAll(".*(\\d+)$", "$1"))
It then doesn't matter about the case of the V.
Write a little method which finds the trailing digits:
String trailingDigits(String x) {
int last = x.length();
while (last > 0 && Character.isDigit(x.charAt(last - 1)) {
--last;
}
return x.substring(last);
}
and call from your map:
.map(x -> trailingDigits(x))
This question already has answers here:
Simple way to repeat a string
(32 answers)
Closed 4 years ago.
There are numerous ways to do this, but using Java 8 streams (likely IntStream), how can I produce a dummy string that is N characters long?
I've seen examples using IntStream.range(), and the various aggregator functions (sum, average), but I don't see a way to do this.
My first random guess looks like this:
IntStream.range(1, 110).map(i -> "x").collect(Collectors.joining());
But that's wrong in a couple of different ways.
You need to use mapToObj() and not map() as you actually use an IntStream and IntStream.map() takes as parameter an IntUnaryOperator, that is an (int->int) function.
For same character dummy (for example "x") :
collect = IntStream.range(1, 110)
.mapToObj(i ->"x")
.collect(Collectors.joining());
Form random dummy :
You could use Random.ints(long streamSize, int randomNumberOrigin, int randomNumberBound).
Returns a stream producing the given streamSize number of pseudorandom
int values, each conforming to the given origin (inclusive) and bound
(exclusive).
To generate a String containing 10 random characters between the 65 and 100 ASCII code :
public static void main(String[] args) {
String collect = new Random().ints(10, 65, 101)
.mapToObj(i -> String.valueOf((char) i))
.collect(Collectors.joining());
System.out.println(collect);
}
If you really want to use a Stream for this, you can utilize Stream#generate, and limit it to n characters:
Stream.generate(() -> "x").limit(110).collect(Collectors.joining());
You are actually almost there:
String s = IntStream.range(40, 110)
.mapToObj(i -> Character.toString((char)i))
.collect(Collectors.joining());
System.out.println(s);
Produces:
()*+,-./0123456789:;<=>?#ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklm
If you want random ordering, with N = 60 for instance:
Random r = new Random();
IntStream.generate(() -> 40 + r.nextInt(70))
.limit(60)
.mapToObj(i -> Character.toString((char)i))
.collect(Collectors.joining()));
Produces
Z>fA+5OY#:HfP;(L:^WKDU21T(*1//#V,F9O-SA2;+),A+V/mLjm<eaE56CH
I want to create a basic program of converting first character of string to uppercase through lambdas
Input
singhakash
Output
Singhakash
I tried
String st = "singhakash";
//approach 1
System.out.print(st.substring(0, 1).toUpperCase());
st.substring(1).codePoints()
.forEach(e -> System.out.print((char) e));
System.out.println();
//approach 2
System.out.print(st.substring(0, 1).toUpperCase());
IntStream.range(0, st.length())
.filter(i -> i > 0)
.mapToObj(st::charAt)
.forEach(System.out::print);
But for both the cases I have to print the first character seperately.Is there any way I can do that without having a seperate print statement?
Note: I can do that normally by loop or any other approach but I am looking for lambdas only solution.
Thanks
You could do it like this:
String st = "singhakash";
IntStream.range(0, st.length())
.mapToObj(i -> i == 0 ? Character.toUpperCase(st.charAt(i)) : st.charAt(i))
.forEach(System.out::print);
The simplest way to do it would be
String result = Character.toUpperCase(st.charAt(0))+st.substring(1);
If you feel like you have to optimize it, i.e. reduce the number of copying operations (instead of letting the JVM do it), you may use:
StringBuilder sb=new StringBuilder(st);
sb.setCharAt(0, Character.toUpperCase(sb.charAt(0)));
String result=sb.toString();
But if it really has to be done using the fancy new Java 8 feature, you can use
String result=IntStream.concat(
IntStream.of(st.codePointAt(0)).map(Character::toUpperCase), st.codePoints().skip(1) )
.collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append)
.toString();
This solution will even handle supplementary code points correctly, so it has even an advantage over the simple solutions (though it would not be too hard to makes these supplementary code point aware too).
If you want to print directly, you can use
IntStream.concat(
IntStream.of(st.codePointAt(0)).map(Character::toUpperCase), st.codePoints().skip(1))
.forEach(cp -> System.out.print(Character.toChars(cp)));
String is immutable in Java. Just uppercase the first character, and append the rest. Something like,
System.out.println(Character.toUpperCase(st.charAt(0)) + st.substring(1));
st.replaceFirst(st.subSequence(0,1).toString(),st.subSequence(0,1).toString().toUpperCase().codePoints().forEach(e -> System.out.print((char)e));
I have a text files that have a lot of string lines in there. If I want to find lines before and after a matching in grep, I will do like this:
grep -A 10 -B 10 "ABC" myfile.txt
How can I implements the equivalent in Java 8 using stream?
If you're willing to use a third party library and don't need parallelism, then jOOλ offers SQL-style window functions as follows
Seq.seq(Files.readAllLines(Paths.get(new File("/path/to/Example.java").toURI())))
.window(-1, 1)
.filter(w -> w.value().contains("ABC"))
.forEach(w -> {
System.out.println("-1:" + w.lag().orElse(""));
System.out.println(" 0:" + w.value());
System.out.println("+1:" + w.lead().orElse(""));
// ABC: Just checking
});
Yielding
-1: .window(-1, 1)
0: .filter(w -> w.value().contains("ABC"))
+1: .forEach(w -> {
-1: System.out.println("+1:" + w.lead().orElse(""));
0: // ABC: Just checking
+1: });
The lead() function accesses the next value in traversal order from the window, the lag() function accesses the previous row.
Disclaimer: I work for the company behind jOOλ
Such scenario is not well-supported by Stream API as existing methods do not provide an access to the element neighbors in the stream. The closest solution which I can think up without creating custom iterators/spliterators and third-party library calls is to read the input file into List and then use indices Stream:
List<String> input = Files.readAllLines(Paths.get(fileName));
Predicate<String> pred = str -> str.contains("ABC");
int contextLength = 10;
IntStream.range(0, input.size()) // line numbers
// filter them leaving only numbers of lines satisfying the predicate
.filter(idx -> pred.test(input.get(idx)))
// add nearby numbers
.flatMap(idx -> IntStream.rangeClosed(idx-contextLength, idx+contextLength))
// remove numbers which are out of the input range
.filter(idx -> idx >= 0 && idx < input.size())
// sort numbers and remove duplicates
.distinct().sorted()
// map to the lines themselves
.mapToObj(input::get)
// output
.forEachOrdered(System.out::println);
The grep output also includes special delimiter like "--" to designate the omitted lines. If you want to go further and mimic such behavior as well, I can suggest you to try my free StreamEx library as it has intervalMap method which is helpful in this case:
// Same as IntStream.range(...).filter(...) steps above
IntStreamEx.ofIndices(input, pred)
// same as above
.flatMap(idx -> IntStream.rangeClosed(idx-contextLength, idx+contextLength))
// remove numbers which are out of the input range
.atLeast(0).less(input.size())
// sort numbers and remove duplicates
.distinct().sorted()
.boxed()
// merge adjacent numbers into single interval and map them to subList
.intervalMap((i, j) -> (j - i) == 1, (i, j) -> input.subList(i, j + 1))
// flatten all subLists prepending them with "--"
.flatMap(list -> StreamEx.of(list).prepend("--"))
// skipping first "--"
.skip(1)
.forEachOrdered(System.out::println);
As Tagir Valeev noted, this kind of problem isn't well supported by the streams API. If you incrementally want to read lines from the input and print out matching lines with context, you'd have to introduce a stateful pipeline stage (or a custom collector or spliterator) which adds quite a bit of complexity.
If you're willing to read all the lines into memory, it turns out that BitSet is a useful representation for manipulating groups of matches. This bears some similarity to Tagir's solution, but instead of using integer ranges to represent lines to be printed, it uses 1-bits in a BitSet. Some advantages of BitSet are that it has a number of built-in bulk operations, and it has a compact internal representation. It can also produce a stream of indexes of the 1-bits, which is quite useful for this problem.
First, let's start out by creating a BitSet that has a 1-bit for each line that matches the predicate:
void contextMatch(Predicate<String> pred, int before, int after, List<String> input) {
int len = input.size();
BitSet matches = IntStream.range(0, len)
.filter(i -> pred.test(input.get(i)))
.collect(BitSet::new, BitSet::set, BitSet::or);
Now that we have the bit set of matching lines, we stream out the indexes of each 1-bit. We then set the bits in the bitset that represent the before and after context. This gives us a single BitSet whose 1-bits represent all of the lines to be printed, including context lines.
BitSet context = matches.stream()
.collect(BitSet::new,
(bs,i) -> bs.set(Math.max(0, i - before), Math.min(i + after + 1, len)),
BitSet::or);
If we just want to print out all the lines, including context, we can do this:
context.stream()
.forEachOrdered(i -> System.out.println(input.get(i)));
The actual grep -A a -B b command prints a separator between each group of context lines. To figure out when to print a separator, we look at each 1-bit in the context bit set. If there's a 0-bit preceding it, or if it's at the very beginning, we set a bit in the result. This gives us a 1-bit at the beginning of each group of context lines:
BitSet separators = context.stream()
.filter(i -> i == 0 || !context.get(i-1))
.collect(BitSet::new, BitSet::set, BitSet::or);
We don't want to print the separator before each group of context lines; we want to print it between each group. That means we have to clear the first 1-bit (if any):
// clear the first bit
int first = separators.nextSetBit(0);
if (first >= 0) {
separators.clear(first);
}
Now, we can print out the result lines. But before printing each line, we check to see if we should print a separator first:
context.stream()
.forEachOrdered(i -> {
if (separators.get(i)) {
System.out.println("--");
}
System.out.println(input.get(i));
});
}