Create a char count array for a String using java8 features - java

I wish to create a int[] of count for a particular String (comprising of only lowercase English Alphabets) using Java 8 stream API. Where arr[i] denotes the count of i-th character of English dictionary (e.g. arr[0] = count of 'a' in String str while arr[2] = count of 'c' in String str. This can be simply done by:
int[] arr = new int[26];
for(char c : str.toCharArray())
arr[c-'a']++;
Or using IntSream in the 2nd way:
int[] arr = IntStream.range('a','z'+1).map(i -> (int)str.chars().filter(c -> c == i).count()).toArray();
But the problem with the second approach is that the String is traversed 26 times for each of the characters from 'a' to 'z'
Can you suggest a better way of achieving the same using java8-stream API?
PS: I know this can be done using Map but I need int[]

int[] r = str.chars()
.boxed()
.reduce(new int[26],
(a, c) -> { ++a[c - 'a']; return a; },
(a1, a2) -> a1);
You know the former is simpler and better. My answer just proves it's feasible with the Stream API, and doesn't suggest that you should go with it. Personally, I would choose the map approach as the most intuitive one.
As pointed out by #Holger, collect is a better option here
str.chars()
.map(c -> c - 'a')
.collect(() -> new int[26],
(a, i)-> a[i]++,
(a1, a2) -> /* left as an exercise to the reader*/);

if you want to use streams and keep the iterative approach, you could do it as well like this:
final int count[] = new int[26];
test.chars().forEach(c -> count[c-'a']++);

Related

Convert a String into a set<Character> using a Stream java 8

private static final String ALPHABET = "abcdefghijklmnopqrstuvwxyz";
SortedSet<Character> set= new TreeSet<Character>();
for (int i = 0; i < ALPHABET.length(); i++) {
set.add(new Character(ALPHABET.charAt(i)));
}
I would like to convert this for loop in Java 8 way. It could be better if using a stream. Output will be the "set" object with contains the Character.
String has a method which will give you a stream of characters. It's actually an IntStream so we just need to convert them to Characters and then collect to a set.
"foo".chars()
.mapToObj(chr -> (char) chr) // autoboxed to Character
.collect(Collectors.toSet());
or use TreeSet::new as others have shown if you need the set to be sorted.
IntStream.range(0, ALPHABET.length())
.mapToObj(ALPHABET::charAt)
.collect(Collectors.toCollection(TreeSet::new));
I think this is the simplest way, preserving the requirement of using a TreeSet. Notice that there's no need to iterate over the input string using indexes, you can directly iterate over its characters.
SortedSet<Character> set =
ALPHABET.chars()
.mapToObj(c -> (char) c)
.collect(Collectors.toCollection(TreeSet::new));

Produce a dummy string N characters long with java 8 streams [duplicate]

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

Java 8 Streams parsing to Integer

Does it exist better way to parse String to Integer using stream than this :
String line = "1 2 3 4 5";
List<Integer> elements = Arrays.stream(line.split(" ")).mapToInt(x -> Integer.parseInt(x))
.boxed().collect(Collectors.toList());
You can eliminate one step if you parse the String directly to Integer:
String line = "1 2 3 4 5";
List<Integer> elements = Arrays.stream(line.split(" ")).map(Integer::valueOf)
.collect(Collectors.toList());
Or you can stick to primitive types, which give better performance, by creating an int array instead of a List<Integer>:
int[] elements = Arrays.stream(line.split(" ")).mapToInt(Integer::parseInt).toArray ();
You can also replace
Arrays.stream(line.split(" "))
with
Pattern.compile(" ").splitAsStream(line)
I'm not sure which is more efficient, though.
There's one more way to do it that will be available since java-9 via Scanner#findAll:
int[] result = scan.findAll(Pattern.compile("\\d+"))
.map(MatchResult::group)
.mapToInt(Integer::parseInt)
.toArray();

Finding 1st free "index" using java streams

I need to find 1st free index in my file system having stream of names as source.
Consider list: ["New2", "New4", "New0", "New1", ...]
1st unused index of those will be 3.
int index = 0;
try (IntStream indexes = names.stream()
.filter(name -> name.startsWith("New"))
.mapToInt(Integer::parseInt)
.distinct()
.sorted())
{
// I was thinking about making possible indexes stream, removing existig ones from try-with-resource block, and getting .min().
IntStream.rangeClosed(0, 10)... // Idk what to do.
}
I am asking someone to help me find right syntax for my idea or propose better solution.
The most efficient way is to collect into a BitSet:
int first = names.stream()
.filter(name -> name.startsWith("New"))
.mapToInt(s -> Integer.parseInt(s.substring(3)))
.collect(BitSet::new, BitSet::set, BitSet::or).nextClearBit(0);
Note that the bits are intrinsically sorted and distinct. Also, there will always be a “free” index. If there is no gap between 0 and the maximum number, the next free will be maximum+1, if there are no matching elements at all, the next free will be zero.
Starting with Java 9, we can do even more efficient with
int first = names.stream()
.filter(name -> name.startsWith("New"))
.mapToInt(s -> Integer.parseInt(s, 3, s.length(), 10))
.collect(BitSet::new, BitSet::set, BitSet::or).nextClearBit(0);
which parses the relevant part of the string directly, saving the substring operation.
You could:
Extract the numeric part from each name
Store the used indexes in a set
Iterate over the range from 0 until the size of the list
The first index not in the used set is available
For example like this:
List<String> names = Arrays.asList("New2", "New4", "New0", "New1");
Set<Integer> taken = names.stream()
.map(s -> s.replaceAll("\\D+", ""))
.map(Integer::parseInt)
.collect(Collectors.toSet());
int first = IntStream.range(0, names.size())
.filter(index -> !taken.contains(index))
.findFirst()
.orElse(names.size());
For the fun of it, if you know you have up to 63 entries...
private static int firstMissing(List<Long> input) {
if (!input.contains(0L)) {
return 0;
}
long firstMissing = Long.lowestOneBit(~input.stream().reduce(1L, (i, j) -> i | 1L << j));
int result = 0;
while (firstMissing != 0) {
++result;
firstMissing = firstMissing >> 1;
}
return result - 1;
}
That's what #Holger did (+1 from me), but without the extra penalty of using BitSet.

Converting Char Array to List in Java

Can anyone help me and tell how to convert a char array to a list and vice versa.
I am trying to write a program in which users enters a string (e.g "Mike is good") and in the output, each whitespace is replaced by "%20" (I.e "Mike%20is%20good"). Although this can be done in many ways but since insertion and deletion take O(1) time in linked list I thought of trying it with a linked list. I am looking for someway of converting a char array to a list, updating the list and then converting it back.
public class apples
{
public static void main(String args[])
{
Scanner input = new Scanner(System.in);
StringBuffer sb = new StringBuffer(input.nextLine());
String s = sb.toString();
char[] c = s.toCharArray();
//LinkedList<char> l = new LinkedList<char>(Arrays.asList(c));
/* giving error "Syntax error on token " char",
Dimensions expected after this token"*/
}
}
So in this program the user is entering the string, which I am storing in a StringBuffer, which I am first converting to a string and then to a char array, but I am not able to get a list l from s.
I would be very grateful if someone can please tell the correct way to convert char array to a list and also vice versa.
In Java 8 and above, you can use the String's method chars():
myString.chars().mapToObj(c -> (char) c).collect(Collectors.toList());
And if you need to convert char[] to List<Character>, you might create a String from it first and then apply the above solution. Though it won't be very readable and pretty, it will be quite short.
Because char is primitive type, standard Arrays.asList(char[]) won't work. It will produce List<char[]> in place of List<Character> ... so what's left is to iterate over array, and fill new list with the data from that array:
public static void main(String[] args) {
String s = "asdasdasda";
char[] chars = s.toCharArray();
// List<Character> list = Arrays.asList(chars); // this does not compile,
List<char[]> asList = Arrays.asList(chars); // because this DOES compile.
List<Character> listC = new ArrayList<Character>();
for (char c : chars) {
listC.add(c);
}
}
And this is how you convert List back to array:
Character[] array = listC.toArray(new Character[listC.size()]);
Funny thing is why List<char[]> asList = Arrays.asList(chars); does what it does: asList can take array or vararg. In this case char [] chars is considered as single valued vararg of char[]! So you can also write something like
List<char[]> asList = Arrays.asList(chars, new char[1]); :)
Another way than using a loop would be to use Guava's Chars.asList() method. Then the code to convert a String to a LinkedList of Character is just:
LinkedList<Character> characterList = new LinkedList<Character>(Chars.asList(string.toCharArray()));
or, in a more Guava way:
LinkedList<Character> characterList = Lists.newLinkedList(Chars.asList(string.toCharArray()));
The Guava library contains a lot of good stuff, so it's worth including it in your project.
Now I will post this answer as a another option for all those developers that are not allowed to use any lib but ONLY the Power of java 8:)
char[] myCharArray = { 'H', 'e', 'l', 'l', 'o', '-', 'X', 'o', 'c', 'e' };
Stream<Character> myStreamOfCharacters = IntStream
.range(0, myCharArray.length)
.mapToObj(i -> myCharArray[i]);
List<Character> myListOfCharacters = myStreamOfCharacters.collect(Collectors.toList());
myListOfCharacters.forEach(System.out::println);
You cannot use generics in java with primitive types, why?
If you really want to convert to List and back to array then dantuch's approach is the correct one.
But if you just want to do the replacement there are methods out there (namely java.lang.String's replaceAll) that can do it for you
private static String replaceWhitespaces(String string, String replacement) {
return string != null ? string.replaceAll("\\s", replacement) : null;
}
You can use it like this:
StringBuffer s = new StringBuffer("Mike is good");
System.out.println(replaceWhitespaces(s.toString(), "%20"));
Output:
Mike%20is%20good
All Operations can be done in java 8 or above:
To the Character array from a Given String
char[] characterArray = myString.toCharArray();
To get the Character List from given String
ArrayList<Character> characterList
= (ArrayList<Character>) myString.chars().mapToObj(c -> (char)c).collect(Collectors.toList());
To get the characters set from given String
Note: sets only stores unique value. so if you want to get only unique characters from a string, this can be used.
HashSet<Character> abc =
(HashSet<Character>) given.chars().mapToObj(c -> (char)c).collect(Collectors.toSet());
To get Characters in a specific range from given String :
To get the character whose unicode value is greater than 118.
https://unicode-table.com/en/#basic-latin
ASCII Code value for characters
* a-z - 97 - 122
* A-Z - 65 - 90
given.chars().filter(a -> a > 118).mapToObj(c -> (char)c).forEach(a -> System.out.println(a));
It will return the characters: w,x, v, z
you ascii values in the filter you can play with characters. you can do operations on character in filter and then you can collect them in list or set as per you need
I guess the simplest way to do this would be by simply iterating over the char array and adding each element to the ArrayList of Characters, in the following manner:
public ArrayList<Character> wordToList () {
char[] brokenStr = "testing".toCharArray();
ArrayList<Character> result = new ArrayList<Character>();
for (char ch : brokenStr) {
result.add(ch);
}
return result;
}
List strList = Stream.of( s.toCharArray() ).map( String::valueOf ).collect( Collectors.toList() );
Try Java Streams.
List<Character> list = s.chars().mapToObj( c -> (char)c).collect(Collectors.toList());
Generic arguments cannot be primitive type.
if you really want to convert char[] to List, you can use .chars() to make your string turns into IntStream, but you need to convert your char[] into String first
List<Character> charlist = String.copyValueOf(arrChr)
.chars()
.mapToObj(i -> (char) i)
.collect(Collectors.toList());
Try this solution
List<Character> characterList = String.valueOf(chars).chars().mapToObj(i -> (char) i).toList();

Categories