Stream API przefiltruj - większe od średniej

0

Siema
Da się za pomocą jednego strumienia pobrać wszystkie wartości tablicy większe od średniej wartości tablicy?

w sensie tak ja poniżej, ale bez tej zmiennej dodatkowej avg


int[] arr = new int[] {1, 0, 2, 11, 3, 4, 7, 12, 20, 8};

double avg = Arrays.stream(arr)
    .average()
    .orElse(0.0);

int[] aboveAvg = Arrays.stream(arr)
    .filter(n -> n > avg)
    .toArray();

1

Nie, bo nie da się poznać średniej przed sprawdzeniem wszystkich elementów, więc jednym przejściem przez tablicę nie jest to możliwe. Prosty przykład: 1,1,1,1,1,1,1,1000 ;]

0

Odpowiedź poprawna:

  1. Da się w jednym Streamie
  2. Bez zmiennej się nie da.

Czyli każdy Collector może mieć finisher - UnaryOperator który właśnie pozwoli na takie przefiltrowanie.

W twoim przypadku:

public static void main(String[] args) {
        int[] arr = new int[] {1, 0, 2, 11, 3, 4, 7, 12, 20, 8};

        List<Integer> result = IntStream.of(arr)
                .mapToObj(Integer::valueOf)
                .collect(Collector.of(() -> new ArrayList<Integer>(),
                        (list, value) -> list.add(value),
                        (l1, l2) -> { l1.addAll(l2); return l1; },
                        (list) -> {
                            double avg = list.stream().mapToInt(e -> e.intValue()).average().getAsDouble();

                            return list.stream().filter(v -> v > avg).collect(Collectors.toList());
                        }, Collector.Characteristics.UNORDERED));

        result.forEach(System.out::println);
    }

Oczywiście w twoim przypadku jest to całkowicie bezużyteczne i nieczytelne. Natomiast jestem w stanie wyobrazić sobie przypadek w którym utworzenie Streama może być kosztowne i nie chcesz go otwierać dwa razy - wtedy taka opcja (ładniej sformatowana) może się przydać.

4

@wartek01: robisz sobie jaja, prawda?

Da się w jednym Streamie

A zaraz: IntStream.of(arr) + list.stream() i list.stream() to ja widzę 3 streamy a nie jeden. Wiadomo że można to sobie "ukryć", ba nie trzeba nawet pisać takiego złożonego kodu, równie dobrze mogłeś napisać:

int[] aboveAvg = Arrays.stream(arr)
    .filter(n -> n > Arrays.stream(arr).average().orElse(0.0))
    .toArray();

Wiadomo że się da, ale nie o to było pytanie ;)

0

@Shalom: wszystko się rozbija o znaczenie co to znaczy jeden stream. Dla mnie w tym wypadku to strumień danych źródłowych (czyli IntStream.of(arr)). Żeby uniknąć wszelkiego rodzaju niedopowiedzeń to napisałem, że użycie tego ma dosyć ograniczoną sensowność.

Natomiast jeśli tak naprawdę nam zależy na unikaniu słówka kluczowego Stream to:

    public static void main(String[] args) {
        int[] arr = new int[]{1, 0, 2, 11, 3, 4, 7, 12, 20, 8};
        
        AtomicInteger count = new AtomicInteger(0);
        AtomicInteger sum = new AtomicInteger(0);
        List<Integer> result = IntStream.of(arr)
                .mapToObj(Integer::valueOf)
                .peek(i -> { count.incrementAndGet(); })
                .peek(i -> { sum.addAndGet(i); })
                .collect(Collector.of(
                        () -> new ArrayList<Integer>(),
                        (list, value) -> list.add(value),
                        (list1, list2) -> { list1.addAll(list2); return list1; },
                        (list) -> {
                            double avg = sum.get()/(double) count.get();
    
                            list.removeIf(i -> i <= avg);
                            return list;
                        },
                        Collector.Characteristics.UNORDERED
                ));
    
        result.forEach(System.out::println);
    }
2

wszystko się rozbija o znaczenie co to znaczy jeden stream

W 99% przypadków oznacza to: jak policzyć coś przechodząc przez dane tylko raz. Wyobraź sobie że masz strumień danych i nie masz pamięci żeby sobie to w całości trzymać w pamięci a strumień jest emitowany tylko raz.
Twoje przykłady są bez sensu, bo to tylko użycie nieczytelnej składni do:

  • zapisania wszystkich danych w pamięci
  • przejścia przez dane wielokrotnie żeby policzyć to co chcesz

Użycie takiego czy innego słowa kluczowego w ogóle nie ma znaczenia.

0
Shalom napisał(a):

wszystko się rozbija o znaczenie co to znaczy jeden stream

W 99% przypadków oznacza to: jak policzyć coś przechodząc przez dane tylko raz.

No ja to rozumiem tak: jak policzyć coś konsumując dane źródłowe tylko raz.

Wyobraź sobie że masz strumień danych i nie masz pamięci żeby sobie to w całości trzymać w pamięci a strumień jest emitowany tylko raz.

I to nie jest przypadek o którym pisałem.
Dosyć jasno napisałem, kiedy można coś takiego użyć:

Natomiast jestem w stanie wyobrazić sobie przypadek w którym utworzenie Streama może być kosztowne i nie chcesz go otwierać dwa razy - wtedy taka opcja (ładniej sformatowana) może się przydać.

1 użytkowników online, w tym zalogowanych: 0, gości: 1