问题
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:
- Reduction (The Java™ Tutorials > Collections > Aggregate Operations).
- 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