Próbuję wymienić zduplikowane elementy na liście liczb całkowitych, powiedzmy np.
List<Integer> numbers = Arrays.asList(new Integer[]{1,2,1,3,4,4});
używając strumieni jdk 8. Czy ktoś próbował. Aby usunąć duplikaty, możemy użyć wyraźnego () api. Ale co ze znalezieniem zduplikowanych elementów? Czy ktoś może mi pomóc?
java
lambda
java-8
java-stream
Siva
źródło
źródło
Odpowiedzi:
Możesz użyć
Collections.frequency
:numbers.stream().filter(i -> Collections.frequency(numbers, i) >1) .collect(Collectors.toSet()).forEach(System.out::println);
źródło
Podstawowy przykład. Pierwsza połowa tworzy mapę częstotliwości, druga połowa redukuje ją do przefiltrowanej listy. Prawdopodobnie nie tak wydajna jak odpowiedź Dave'a, ale bardziej wszechstronna (np. Jeśli chcesz wykryć dokładnie dwa itp.)
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() );
źródło
Potrzebujesz zestawu (
allItems
poniżej) do przechowywania całej zawartości tablicy, ale to jest 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]
źródło
filter()
wymaga predykatu bezpaństwowego. Twoje „rozwiązanie” jest uderzająco podobne do przykładu predykatu stanowego podanego w javadoc: docs.oracle.com/javase/8/docs/api/java/util/stream/ ...sequential()
, prawdopodobnie jest bezpieczny. W bardziej ogólnym przypadku, gdy strumień może byćparallel()
, prawie na pewno pęknie w dziwny sposób.Sposób O (n) wyglądałby jak poniżej:
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());
W tym podejściu złożoność przestrzeni podwoiłaby się, ale przestrzeń ta nie jest marnotrawstwem; w rzeczywistości mamy teraz tylko duplikat tylko jako Zestaw, a także jako inny Zestaw z usuniętymi wszystkimi duplikatami.
źródło
Biblioteka My StreamEx, która ulepsza strumienie Java 8, zapewnia specjalną operację,
distinct(atLeast)
która może zachować tylko elementy pojawiające się co najmniej określoną liczbę razy. Więc twój problem można rozwiązać w następujący sposób:List<Integer> repeatingNumbers = StreamEx.of(numbers).distinct(2).toList();
Wewnętrznie jest podobny do rozwiązania @Dave, zlicza obiekty, obsługuje inne pożądane ilości i jest przyjazny
ConcurrentHashMap
dla równoległości (używa do równoległego strumienia, aleHashMap
dla sekwencyjnego). W przypadku dużych ilości danych można przyspieszyć za pomocą.parallel().distinct(2)
.źródło
Możesz uzyskać duplikat w ten sposób:
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());
źródło
numbers = Arrays.asList(400, 400, 500, 500);
stream
wnętrzastream
jest kosztowne.Myślę, że podstawowe rozwiązania tego pytania powinny wyglądać następująco:
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());
cóż, nie jest zalecane wykonywanie operacji filtrowania, ale dla lepszego zrozumienia użyłem go, ponadto w przyszłych wersjach powinno być trochę niestandardowego filtrowania.
źródło
Zestaw wielozbiorowy to struktura utrzymująca liczbę wystąpień dla każdego elementu. Korzystanie z implementacji Guava:
Set<Integer> duplicated = ImmutableMultiset.copyOf(numbers).entrySet().stream() .filter(entry -> entry.getCount() > 1) .map(Multiset.Entry::getElement) .collect(Collectors.toSet());
źródło
tworzenie dodatkowej mapy lub strumienia jest czasochłonne i przestrzenne…
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]
… I dla którego kwestia jest uważana za [duplikat]
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() ); }
źródło
Jeśli chcesz tylko wykryć obecność duplikatów (zamiast wymieniać je, czego chciał OP), po prostu przekonwertuj je na listę i zestaw, a następnie porównaj rozmiary:
List<Integer> list = ...; Set<Integer> set = new HashSet<>(list); if (list.size() != set.size()) { // duplicates detected }
Podoba mi się to podejście, ponieważ jest mniej miejsc na błędy.
źródło
Myślę, że mam dobre rozwiązanie, jak rozwiązać taki problem - List => Lista z grupowaniem według Something.a & Something.b. Istnieje rozszerzona definicja:
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); } }
klasa A, lista1 to tylko dane przychodzące - magia jest w Objects.hash (...) :)
źródło
Objects.hash
daje tę samą wartość dla(v.a_1, v.b_1, v.c_1, v.d_1)
i(v.a_2, v.b_2, v.c_2, v.d_2)
, to zostaną one uznane za równe i zostaną usunięte jako duplikaty, bez faktycznego sprawdzania, czy a, b, c i d są takie same. Może to być akceptowalne ryzyko lub możesz chcieć użyć funkcji innej niż ta,Objects.hash
która gwarantuje unikalny wynik w całej domenie.Czy musisz używać idiomów Java 8 (steams)? Perphaps prostym rozwiązaniem byłoby przeniesienie złożoności do struktury danych podobnej do mapy, która zawiera liczby jako klucz (bez powtarzania) i czas ich występowania jako wartość. Możesz powtórzyć tę mapę i zrobić coś tylko z tymi liczbami, które pojawiają się> 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()); } } }
źródło
Wypróbuj to rozwiązanie:
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; } }
źródło
A co ze sprawdzaniem indeksów?
źródło