-2

I've got a string s, and I'm trying to map each letter in s to the number of times it appears in s, using the Stream API.
For example, giraffe -> {1, 1, 1, 1, 2, 2, 1}.
The code I've tried using is:

String s = "giraffe";
int[] occurences = s.chars().map(c -> count(s, c)).toArray();

public int count(String text, char ch) {
    return (int) text.chars().filter(c -> c == ch).count();
}

but I get incompatible types: possible lossy conversion from int to char on the line that starts with int[] occurences = ... I've tried some different variations but nothing has worked. Any ideas? Thanks

5 Answers5

2

The problem

The real cause of your problem is that there isn’t any CharStream class. For this reason s.chars() gives you an IntStream. For this reason in turn in map(c -> count(s, c)), c has type int. So you are trying to pass an int to your count method where it expects a char. This gives you your error message. An int can be converted to a char, but some bits will be discarded, which is why the conversion is necessarily lossy.

The two obvious solutions

Solution 1: Tell Java that you mean to make this lossy conversion. Since you know that c comes from a char from your string, converting it back to a char again won’t do any harm.

    int[] occurences = s.chars().map(c -> count(s, (char) c)).toArray();

Solution 2:

You may declare your method to accept an int:

public int count(String text, int ch) {

Now no conversion (cast) is needed. The comparison c == ch still works fine. It is comparing two ints anyway, so no harm is done.

PS There are other fine solutions and refinements in the other answers. Personally I’d be tempted to preprocess the string into a map of counts so I need not count again for each letter. This is not necessary, your code works fine with just one of the two changes mentioned.

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
1

I have modified your count method to accept int instead of char and it works

import java.util.Arrays;
class Main {
  public static void main(String[] args) {
    
    String s = "giraffe";
int[] occurences = s.chars().map(c -> count(s, c)).toArray();

 System.out.println(Arrays.toString(occurences));


  }
  public static int count(String text, int ch) {
    return (int) text.chars().filter(c -> c == ch).count();
}
}

See repl

Shubham Dixit
  • 9,242
  • 4
  • 27
  • 46
1

Using loops, I would do it like this.

  • allocate an array to hold all the characters
  • then only print the ones that are non-zero
int[] count = new int[256]; // default to all 0's
for (char c : s.toCharArray()) {
    count[c]++;  // use the character to index the array and update the count
}

for (int i = 0; i < count.size; i++) {
   if (count[i] > 0) {
      System.out.println((char)i + " " + count[i]);
    }
}

You can also use a stream to convert to a map. The order will be different but the frequency counts will be the same. It works as follows:

  • split the string and stream the characters.
  • group them by character
  • and apply the counting collector when a key is found to update the count.
String s = "giraffe";
Map<String, Long> freq = Arrays.stream(s.split("")).collect(
        Collectors.groupingBy(c -> c, Collectors.counting()));

freq.entrySet().forEach(System.out::println);

prints


a=1
r=1
e=1
f=2
g=1
i=1
WJS
  • 36,363
  • 4
  • 24
  • 39
1

There can be many ways to do it. One of the ways is to use \X as the regex which specifies any Unicode extended grapheme cluster and group by the captured Matcher groups.

Demo:

import java.util.LinkedHashMap;
import java.util.Map;
import java.util.regex.MatchResult;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class Main {
    public static void main(String[] args) {
        // Test
        Stream.of(
                    "giraffe", 
                    "Hello"
                ).forEach(s -> System.out.println(getFrequencyMap(s)));
    }

    static Map<Object, Long> getFrequencyMap(String s) {
        return Pattern.compile("\\X").matcher(s).results()
                .collect(Collectors.groupingBy(MatchResult::group, LinkedHashMap::new, Collectors.counting()));
    }
}

Output:

{g=1, i=1, r=1, a=1, f=2, e=1}
{H=1, e=1, =1, l=2, o=1}
Arvind Kumar Avinash
  • 71,965
  • 6
  • 74
  • 110
0

Sorry, but I think Streams is NOT A GOOD for this. The easies way is to use Map:

public static Map<Character, Integer> histogram(String str) {
    Map<Character, Integer> map = new TreeMap<>();

    for (int i = 0; i < str.length(); i++) {
        char ch = Character.toLowerCase(str.charAt(i));
        map.put(ch, map.getOrDefault(ch, 0) + 1);
    }

    return map;
}
Oleg Cherednik
  • 17,377
  • 4
  • 21
  • 35