Printing certain values using Collections.frequency()

北城以北 提交于 2020-01-23 06:34:39

问题


I have an array as follows:

int[] array = {11, 14, 17, 11, 48, 33, 29, 11, 17, 22, 11, 48, 18};

What I wanted to do was to find the duplicate values, and print them.

So my way of doing this was to convert to ArrayList, then to Set and use a stream on the Set.

ArrayList<Integer> list = new ArrayList<>(array.length);
for (int i = 0; i < array.length; i++) {
    list.add(array[i]);
}

Set<Integer> dup = new HashSet<>(list);

I then used a stream to loop through it and print the values using Collections.frequency.

dup.stream().forEach((key) -> {
            System.out.println(key + ": " + Collections.frequency(list, key));
        });

Which will of course print them all, even if the count is one.

I thought adding in if(key > 1) but it's the value I want not the key.

How can I get the value in this instance to print only where value > 2.

I could probably put in:

int check = Collections.frequency(list, key);
            if (check > 1) {

but this then duplicates Collections.frequency(list, key) in the stream and is quite ugly.


回答1:


Probably you can to use filter to get only the values great than 2 :

dup.stream()
       .filter(t -> Collections.frequency(list, t) > 2)
       .forEach(key -> System.out.println(key + ": " + Collections.frequency(list, key)));

result in your case is :

11: 4

Edit

Another solution :

No need to use a Set or Collections.frequency you can just use :

Integer[] array = {11, 14, 17, 11, 48, 33, 29, 11, 17, 22, 11, 48, 18};
Arrays.stream(array).collect(Collectors.groupingBy(p -> p, Collectors.counting()))
        .entrySet().stream().filter(t -> t.getValue() > 1)
        .forEach(key -> System.out.println(key.getKey() + ": " + key.getValue()));

Output

48: 2
17: 2
11: 4



回答2:


The complete example without initialising dup in a loop and calling Collections.frequency for every unique element once:

Integer[] array = {11, 14, 17, 11, 48, 33, 29, 11, 17, 22, 11, 48, 18};
List<Integer> list = Arrays.asList(array);
Arrays.stream(array).collect(Collectors.toSet())
  .stream()
  .map(v -> new SimpleEntry<>(v, Collections.frequency(list, v)))
  .filter(v -> v.getValue() > 1)
  .forEach(v -> System.out.println(v.getKey() + ":" + v.getValue()));



回答3:


package various;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

public class Frequency {
    public static void main(String[] args) {
        int[] array = {11, 14, 17, 11, 48, 33, 29, 11, 17, 22, 11, 48, 18};
        ArrayList<Integer> list = new ArrayList<>(array.length);
        for (int i = 0; i < array.length; i++) {
            list.add(array[i]);
        }

        Set<Integer> dup = new HashSet<>(list);

        dup.stream().forEach((key) -> {
            System.out.print((Collections.frequency(list, key) > 2) ? key + ": " + Collections.frequency(list, key) +"\n" : "");
        });
    }
}

Output:

11: 4



回答4:


HashMap<Integer, Integer> hashmap = new HashMap<>();
for (int i : array) {
    if (hashmap.containsKey(i)) {
        hashmap.put(i, hashmap.get(i) + 1);
    } else {
        hashmap.put(i, 1);
    }
}

Stores the value and its frequency.

{48=2, 17=2, 33=1, 18=1, 22=1, 11=4, 29=1, 14=1}

EDIT: if you want to print the duplicates;

hashmap.entrySet().stream().filter((entry) -> ((int) entry.getValue() >= 2)).forEach((entry) -> {
    System.out.println(entry.getKey() + ":" + entry.getValue());
});

Gives you:

48:2
17:2
11:4



回答5:


This should work:

int[] array = {11, 14, 17, 11, 48, 33, 29, 11, 17, 22, 11, 48, 18};

List<Integer> list = Arrays.stream(array).boxed().collect(Collectors.toList());

// print values appearing more than once
list.stream().filter(i -> Collections.frequency(list, i) >1)
        .collect(Collectors.toSet()).forEach(System.out::println);

Would print:

48  
17  
11  

If you also would like to get the occurences you could do something like this:

int[] array = {11, 14, 17, 11, 48, 33, 29, 11, 17, 22, 11, 48, 18};

List<Integer> list = Arrays.stream(array).boxed().collect(Collectors.toList());

list.stream()
    .filter(i -> Collections.frequency(list, i) >1)
    .collect(Collectors.toMap(identity(), v -> 1, Integer::sum))
    .forEach((x, y) -> System.out.println("Key: " + x +", occurence: "+ y));

which prints

Key: 48, occurence: 2
Key: 17, occurence: 2
Key: 11, occurence: 4



回答6:


To implement the desired functionality, we can facilitate Java Streams, in particular, the Stream.collect method:

<R> R collect(Supplier<R> supplier,
    BiConsumer<R,? super T> accumulator,
    BiConsumer<R,R> combiner)

Please consider the following draft implementation:

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Consumer;
import java.util.stream.Collectors;

public class Program {
    public static void main(final String[] args) {
        // Input.
        final int[] inputArray = {11, 14, 17, 11, 48, 33, 29, 11, 17, 22, 11, 48, 18};

        // Processing.
        final DuplicateCounter<Integer> duplicateCounter = Arrays.stream(inputArray)
            .collect(
                DuplicateCounter<Integer>::new,
                DuplicateCounter<Integer>::accept,
                DuplicateCounter<Integer>::combine
            );

        // Output.
        System.out.println(duplicateCounter.getDuplicateCountMap());
    }

    private static final class DuplicateCounter<T> implements Consumer<T> {
        private final Map<T, Integer> countMap = new HashMap<>();

        public void accept(final T obj) {
            this.incrementCounter(obj, 1);
        }

        public void combine(final DuplicateCounter<T> other) {
            other.countMap.entrySet()
                .forEach(entry -> this.incrementCounter(entry.getKey(), entry.getValue()));
        }

        public Map<T, Integer> getDuplicateCountMap() {
            return this.countMap.entrySet()
                .stream()
                .filter(entry -> entry.getValue() > 1)
                .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
        }

        private void incrementCounter(final T obj, int increment) {
            final Integer counter = this.countMap.getOrDefault(obj, 0);
            this.countMap.put(obj, counter + increment);
        }
    }
}

It outputs:

{48=2, 17=2, 11=4}

Additional references:

  1. Reduction (The Java™ Tutorials > Collections > Aggregate Operations).
  2. Java 8 Streams - collect vs reduce.



回答7:


And yet another functional way:

HashMap<Integer, Long> result = Arrays.stream(array)
            .boxed()
            .collect(Collectors.collectingAndThen(Collectors.groupingBy(
                    Function.identity(),
                    HashMap::new,
                    Collectors.counting()),
                    map -> {
                        map.entrySet().removeIf(x -> x.getValue() == 1);
                        return map;
                    }));

Btw the accepted answer - is probably the worst here, like every single suggestion to use Collections.frequency for each element (even if unique)




回答8:


The problem with Collections.frequency is that it has to traverse all the collection to find the frequency of the given element. If you do this for every element of the collection, then your solution is O(n^2), i.e. extremely inefficient, because the processing time increases as the square of the number of elements in the collection.

Instead, you could use a stream to create a map that counts the occurrences of each element, as follows:

int[] array = {11, 14, 17, 11, 48, 33, 29, 11, 17, 22, 11, 48, 18};

Map<Integer, Long> occurrences = Arrays.stream(array)
    .boxed()
    .collect(Collectors.groupingBy(
        Function.identity(), 
        Collectors.counting()));

Now, if you want to only keep the values with more than 1 occurrence, you can simply remove the entries of the map whose value is equal to 1:

occurrences.values().removeIf(v -> v == 1);

Finally, if you print the map:

System.out.println(occurrences);

You'll get the following output:

{48=2, 17=2, 11=4}

Or, to get the output in the format you expect:

occurrences.forEach((k, v) -> System.out.println(k + ": "+ v));

Another way, much shorter and without the overhead of streams:

Map<Integer, Long> occurrences = new HashMap<>();
for (int n : array) occurrences.merge(n, 1L, Long::sum);

Then, you remove entries for unique elements as shown before:

occurrences.values().removeIf(v -> v == 1);



回答9:


You can use Filter :

dup.stream().filter(key -> Collections.frequency(list,key)>2).forEach(System.out::println);



回答10:


You don't have to convert it to ArrayList at all. Take a look at this:

HashSet<Integer> set = new HashSet<Integer>();
for (int i = 0; i < array.length; i++) {
    if(set.contains(array[i]))
        System.out.println(array[i]);
    else
        set.add(array[i]);
}


来源:https://stackoverflow.com/questions/47353687/printing-certain-values-using-collections-frequency

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!