Estou tentando listar elementos duplicados na lista de inteiros, por exemplo,
List<Integer> numbers = Arrays.asList(new Integer[]{1,2,1,3,4,4});
usando Streams de jdk 8. Alguém já experimentou. Para remover as duplicatas, podemos usar a API distinta (). Mas e quanto a encontrar os elementos duplicados? Alguém pode me ajudar?
java
lambda
java-8
java-stream
Siva
fonte
fonte
Respostas:
Você pode usar
Collections.frequency
:numbers.stream().filter(i -> Collections.frequency(numbers, i) >1) .collect(Collectors.toSet()).forEach(System.out::println);
fonte
Exemplo básico. A primeira parte constrói o mapa de frequência, a segunda metade reduz a uma lista filtrada. Provavelmente não tão eficiente quanto a resposta de Dave, mas mais versátil (como se você quiser detectar exatamente dois etc.)
List<Integer> duplicates = IntStream.of( 1, 2, 3, 2, 1, 2, 3, 4, 2, 2, 2 ) .boxed() .collect( Collectors.groupingBy( Function.identity(), Collectors.counting() ) ) .entrySet() .stream() .filter( p -> p.getValue() > 1 ) .map( Map.Entry::getKey ) .collect( Collectors.toList() );
fonte
Você precisa de um conjunto (
allItems
abaixo) para conter todo o conteúdo da matriz, mas este é O (n):Integer[] numbers = new Integer[] { 1, 2, 1, 3, 4, 4 }; Set<Integer> allItems = new HashSet<>(); Set<Integer> duplicates = Arrays.stream(numbers) .filter(n -> !allItems.add(n)) //Set.add() returns false if the item was already in the set. .collect(Collectors.toSet()); System.out.println(duplicates); // [1, 4]
fonte
filter()
requer um predicado sem estado. Sua "solução" é notavelmente semelhante ao exemplo de um predicado com estado fornecido no javadoc: docs.oracle.com/javase/8/docs/api/java/util/stream/…sequential()
, provavelmente é seguro. No caso mais geral em que o fluxo pode estarparallel()
, é praticamente garantido que ele quebrará de maneiras estranhas.Uma forma O (n) seria a seguinte:
List<Integer> numbers = Arrays.asList(1, 2, 1, 3, 4, 4); Set<Integer> duplicatedNumbersRemovedSet = new HashSet<>(); Set<Integer> duplicatedNumbersSet = numbers.stream().filter(n -> !duplicatedNumbersRemovedSet.add(n)).collect(Collectors.toSet());
A complexidade do espaço dobraria nesta abordagem, mas esse espaço não é um desperdício; na verdade, agora temos a duplicata sozinha apenas como um Conjunto, bem como outro Conjunto com todas as duplicatas removidas também.
fonte
A biblioteca My StreamEx , que aprimora os fluxos Java 8, oferece uma operação especial
distinct(atLeast)
que pode reter apenas os elementos que aparecem pelo menos o número especificado de vezes. Portanto, seu problema pode ser resolvido assim:List<Integer> repeatingNumbers = StreamEx.of(numbers).distinct(2).toList();
Internamente é semelhante à solução @Dave, conta objetos, para suportar outras quantidades desejadas e é compatível com paralelismo (usa
ConcurrentHashMap
para fluxo paralelizado, masHashMap
para sequencial). Para grandes quantidades de dados, você pode obter uma aceleração usando.parallel().distinct(2)
.fonte
Você pode obter o duplicado assim:
List<Integer> numbers = Arrays.asList(1, 2, 1, 3, 4, 4); Set<Integer> duplicated = numbers .stream() .filter(n -> numbers .stream() .filter(x -> x == n) .count() > 1) .collect(Collectors.toSet());
fonte
numbers = Arrays.asList(400, 400, 500, 500);
stream
dentrostream
é caro.Acho que as soluções básicas para a questão devem ser as seguintes:
Supplier supplier=HashSet::new; HashSet has=ls.stream().collect(Collectors.toCollection(supplier)); List lst = (List) ls.stream().filter(e->Collections.frequency(ls,e)>1).distinct().collect(Collectors.toList());
bem, não é recomendável realizar uma operação de filtro, mas para melhor compreensão, tenho usado, além disso, deve haver alguma filtragem customizada em versões futuras.
fonte
Um multiset é uma estrutura que mantém o número de ocorrências para cada elemento. Usando a implementação de Guava:
Set<Integer> duplicated = ImmutableMultiset.copyOf(numbers).entrySet().stream() .filter(entry -> entry.getCount() > 1) .map(Multiset.Entry::getElement) .collect(Collectors.toSet());
fonte
a criação de um mapa ou fluxo adicional consome tempo e espaço ...
Set<Integer> duplicates = numbers.stream().collect( Collectors.collectingAndThen( Collectors.groupingBy( Function.identity(), Collectors.counting() ), map -> { map.values().removeIf( cnt -> cnt < 2 ); return( map.keySet() ); } ) ); // [1, 4]
… E para a pergunta de qual é reivindicado ser um [duplicado]
public static int[] getDuplicatesStreamsToArray( int[] input ) { return( IntStream.of( input ).boxed().collect( Collectors.collectingAndThen( Collectors.groupingBy( Function.identity(), Collectors.counting() ), map -> { map.values().removeIf( cnt -> cnt < 2 ); return( map.keySet() ); } ) ).stream().mapToInt( i -> i ).toArray() ); }
fonte
Se você só precisa detectar a presença de duplicatas (em vez de listá-las, que é o que o OP queria), basta convertê-las em Lista e Conjunto e, em seguida, compare os tamanhos:
List<Integer> list = ...; Set<Integer> set = new HashSet<>(list); if (list.size() != set.size()) { // duplicates detected }
Gosto dessa abordagem porque tem menos lugares para erros.
fonte
Acho que tenho uma boa solução para resolver um problema como este - List => List com agrupamento por Something.a & Something.b. Existe uma definição estendida:
public class Test { public static void test() { class A { private int a; private int b; private float c; private float d; public A(int a, int b, float c, float d) { this.a = a; this.b = b; this.c = c; this.d = d; } } List<A> list1 = new ArrayList<A>(); list1.addAll(Arrays.asList(new A(1, 2, 3, 4), new A(2, 3, 4, 5), new A(1, 2, 3, 4), new A(2, 3, 4, 5), new A(1, 2, 3, 4))); Map<Integer, A> map = list1.stream() .collect(HashMap::new, (m, v) -> m.put( Objects.hash(v.a, v.b, v.c, v.d), v), HashMap::putAll); list1.clear(); list1.addAll(map.values()); System.out.println(list1); } }
classe A, lista1 são apenas dados de entrada - a magia está no Objects.hash (...) :)
fonte
Objects.hash
produzir o mesmo valor para(v.a_1, v.b_1, v.c_1, v.d_1)
e(v.a_2, v.b_2, v.c_2, v.d_2)
, então eles serão considerados iguais e removidos como duplicatas, sem realmente verificar se os a's, b's, c's e d's são iguais. Este pode ser um risco aceitável, ou você pode querer usar uma função diferente daquelaObjects.hash
que é garantida para produzir um resultado único em seu domínio.Você tem que usar o idioma java 8 (steams)? Talvez uma solução simples seja mover a complexidade para uma estrutura de dados semelhante a um mapa que mantém os números como chave (sem repetir) e as vezes em que ocorre como um valor. Você poderia iterar esse mapa e fazer algo apenas com os números que ocorrem> 1.
import java.lang.Math; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.HashMap; import java.util.Iterator; public class RemoveDuplicates { public static void main(String[] args) { List<Integer> numbers = Arrays.asList(new Integer[]{1,2,1,3,4,4}); Map<Integer,Integer> countByNumber = new HashMap<Integer,Integer>(); for(Integer n:numbers) { Integer count = countByNumber.get(n); if (count != null) { countByNumber.put(n,count + 1); } else { countByNumber.put(n,1); } } System.out.println(countByNumber); Iterator it = countByNumber.entrySet().iterator(); while (it.hasNext()) { Map.Entry pair = (Map.Entry)it.next(); System.out.println(pair.getKey() + " = " + pair.getValue()); } } }
fonte
Experimente esta solução:
public class Anagramm { public static boolean isAnagramLetters(String word, String anagramm) { if (anagramm.isEmpty()) { return false; } Map<Character, Integer> mapExistString = CharCountMap(word); Map<Character, Integer> mapCheckString = CharCountMap(anagramm); return enoughLetters(mapExistString, mapCheckString); } private static Map<Character, Integer> CharCountMap(String chars) { HashMap<Character, Integer> charCountMap = new HashMap<Character, Integer>(); for (char c : chars.toCharArray()) { if (charCountMap.containsKey(c)) { charCountMap.put(c, charCountMap.get(c) + 1); } else { charCountMap.put(c, 1); } } return charCountMap; } static boolean enoughLetters(Map<Character, Integer> mapExistString, Map<Character,Integer> mapCheckString) { for( Entry<Character, Integer> e : mapCheckString.entrySet() ) { Character letter = e.getKey(); Integer available = mapExistString.get(letter); if (available == null || e.getValue() > available) return false; } return true; } }
fonte
E quanto à verificação de índices?
fonte