0

here I'm trying to print values using lambda. Is it possible to make it in double colon way?

public class Occurance 
{
    static void occuranceUsingListSet()
    {
        ArrayList<String> listColors = new ArrayList<>(Arrays.asList("Red","blue","green","green"));
        Set<String> setVals = new LinkedHashSet<>(listColors);
        setVals.stream().forEach(s -> System.out.println(s + " : " + Collections.frequency(listColors, s)));
    }
    
}

setVals.stream().forEach(s -> System.out.println(s + " : " + Collections.frequency(listColors, s)));

Is there any possibilities to use double colon?

And is method reference and pass by reference are same?

I can see this double colon is pass method reference. But in this slack answer I can see that there is no such thing like pass by reference in java. https://stackoverflow.com/a/73021/11962586.

S14321K
  • 220
  • 3
  • 19
  • 1
    The answer depends on how willing you are to restructure the code. One idea that comes to my mind would be to forego the explicit frequency calculation, and collect to a map instead, calculating the frequency implicitly through the merge function. [`ideone.com` demo](https://ideone.com/jbzeky) – Turing85 Jun 25 '23 at 15:41
  • 1
    _Method reference_ and _pass by reference_ are two completely different concepts. They have nothing to do with each other. By the way: Java is _pass by value_, always (and not pass by reference). – Seelenvirtuose Jun 25 '23 at 15:53
  • 2
    Why copy as a set? It can be just `listColors.stream().map(s -> s + " : " + Collections.frequency(listColors, s)).forEach(System.out::println);` – DuncG Jun 25 '23 at 16:18
  • 1
    @DuncG, but we are getting output two times. like ```Red : 1 blue : 1 green : 2 green : 2``` – S14321K Jun 25 '23 at 16:29
  • @S14321K Oops, ignore me – DuncG Jun 25 '23 at 16:33
  • 1
    @DuncG haha, no problem. Your soulution worked great. Though I found another solution. ```listColors.stream().map(s -> s + " : " + Collections.frequency(listColors, s)).distinct().forEach(System.out::println);``` Adding distinct() done the job great. – S14321K Jun 25 '23 at 16:37

2 Answers2

4

No, you cannot do that whole operation with a single method reference. You are doing more than a single method call in your lambda.

Although, if you refactor your string creation into a new function, then you can use a method reference to call System.out.println:

static void occuranceUsingListSet() {
    // You don't need to wrap Arrays.asList in a new ArrayList.
    List<String> listColors = Arrays.asList("Red","blue","green","green");
    Set<String> setVals = new LinkedHashSet<>(listColors);

    setVals.stream()
        .map(s -> createFrequencyString(s, listColors);
        .forEach(System.out::println);
}

static String createFrequencyString(String color, List<String> colors) {
    return color + " : " + Collections.frequency(colors, color);
}
marstran
  • 26,413
  • 5
  • 61
  • 67
  • Thanks for your answer. Is method reference and pass by reference are same? I need some clarity in that too. I edited the question. Thanks – S14321K Jun 25 '23 at 15:44
  • 2
    No, those are difference things. Java does not have pass by reference, only pass by value. a method reference is just a syntactic way to refer to a method instead of writing out the lambda expression. – marstran Jun 25 '23 at 16:23
1

With a little reworking of the method you can use a method reference. Since you didn't specify if a set was required, I am using a map as it offers some efficiencies.

List<String> listColors = Arrays.asList("Red", "blue", "green",
        "green");

I am calling the method printOccurrences since that is what you are doing. It takes a Supplier<Stream<String>> as the parameter. The stream is obtained in the method by using get().

printOccurrences(listColors::stream);

prints

Red : 1, blue : 1, green : 2

Using Collections.frequency is ill advised since you must traverse the entire list for each specified object. Using a Map to compute frequencies is a more common technique as it does it in a single pass over the list. For each color string, Collectors.groupingBy along with Collectors.counting will compute the frequency for each map entry pair of <String, Long>. The entries are then streamed to obtain the collected data and joinedinto a string for printing (the format of which can be altered as required). I used a LinkedHashMap supplier to preserve the order of colors as they are processed. If not required or desired, it can be removed.

So in order

  • get the stream and compute the frequencies using groupingBy
  • then stream the entry set, extracting the key(color String) and value(frequency) and format for joining. I am joining each pair with ", ".
  • all of the above is then passed to System.out.println.
static void printOccurrences(Supplier<Stream<String>> stream) {
     System.out.println(stream.get()
             .collect(Collectors.groupingBy(color -> color,
                     LinkedHashMap::new, Collectors.counting()))
             .entrySet().stream().map(e -> e.getKey() + " : " + e.getValue())
             .collect(Collectors.joining(", ")));
}

Some final notes.

  • The method above can also be easily altered to return the string for printing and/or take the list as an argument and just stream it there without using a Supplier.

  • Unless you want "Red" to be different than "red" you should convert to a common case, otherwise they will be treated as different colors. For something like this, title case might be a nice choice so it can be done as follows:

static String toTitleCase(String str) {
     return str.substring(0,1).toUpperCase() + str.substring(1).toLowerCase();
}

Then change a -> a in groupingBy to a->toTitleCase(a)

WJS
  • 36,363
  • 4
  • 24
  • 39