2017-08-01 30 views
18

Chciałbym zapętlić się nad ogromną tablicą i wykonać skomplikowany zestaw instrukcji, który zajmuje dużo czasu. Jednakże, jeśli minęło więcej niż 30 sekund, chcę się poddać.Strumienie Java 8 - limit czasu?

np.

final long start = System.currentTimeMillis(); 
myDataStructure.stream() 
    .while(() -> System.currentTimeMillis() <= start + 30000) 
    .forEach(e -> 
    { 
     ... 
    }); 

chcę uniknąć po prostu mówiąc return wewnątrz zaproszenia forEach gdy pewien warunek jest spełniony.

+2

https://stackoverflow.com/questions/41392286/java-8-completablefuture-stream-and-timeouts Oto próbka odpowiedź, może ci pomaga. – utkusonmez

+1

Jeśli obliczałeś zmiany efektów ubocznych i stosowałeś je później (zakładając, że obliczenia są znacznie droższe niż stosowanie), możesz być w stanie użyć specjalnej implementacji 'Collector', która przestaje zbierać po osiągnięciu limitu czasu. – SpaceTrucker

+2

Powiązane: https://stackoverflow.com/questions/20746429/limit-a-stream-by-a-predicate –

Odpowiedz

14

Jeśli w tym przypadku powtarzanie strumienia lub macierzy jest tanie w porównaniu do faktycznego wykonywania operacji, wystarczy użyć predykatu i odfiltrować, czy upłynął czas, czy nie.

final long end = System.nanoTime() + TimeUnit.SECONDS.toNanos(30L); 
myDataStructure.stream() 
    .filter(e -> System.nanoTime() <= end) 
    .forEach(e -> 
    { 
     ... 
    }); 

Pytanie dotyczy tego, czy trzeba wiedzieć, które elementy zostały przetworzone, czy nie. W związku z powyższym należy sprawdzić, czy efekt uboczny miał miejsce dla określonego elementu później.

+0

Och, nie myślałem, że użyję "filtru" takiego, jak ten – Hatefiend

+11

Dobry pomysł, ale jeśli strumień jest ogromny, może być marnotrawstwem, aby ocenić 'System.currentTimeMillis() <= start + 30000L' dla każdego z elementów po Minęło 30 sekund. – Eran

+1

@Eran tak, wygląda na niestandardową pulę z 'get' byłoby znacznie bardziej odpowiednie – Eugene

15

Od StreamforEach nie posiada break, myślę, że można tworzyć niestandardowy wyjątek za to break pętlę:

myDataStructure.stream() 
    .forEach(e -> 
    { 
     if (System.currentTimeMillis() <= start + 30000) { 
      throw new MyTimeOutException() 
     } 
    }); 

i można złapać ten wyjątek dla połowu to.

+0

Niestety, wyjątki mają nieco zbyt duży narzut dla tej aplikacji. – Hatefiend

+14

@Hatefiend Wyjątek jest zgłaszany tylko raz po 30 sekundach. Myślę, że narzut tworzenia i wyrzucania wyjątku jest znikomy. – SpaceTrucker

+0

jest to najbardziej skuteczny sposób robienia tego. jednorazowy wyjątek jest znacznie skuteczniejszy niż sprawdzanie warunku dla każdego niepotrzebnego elementu ogromnego zbioru danych. – nafas

5

można wykorzystywać fakt, że .allMatch() jest operatorem zwarcie do wypowiedzenia strumieniowe:

final long start = System.currentTimeMillis(); 
myDataStructure.stream() 
    .allMatch(e -> 
    { 
     // your task here 
     return System.currentTimeMillis() <= start + 30000; 
    }); 
+0

To zadziała, ale 'allMatch' zakończy strumień. Używam obecnie 'anyMatch', a dwa nie łączą się ze sobą dobrze. Nie mogę spieszyć się z 'anyMatch' albo. – Hatefiend

+1

@Hatefiend Jest to rozwiązanie problemu opisanego w pytaniu. Jeśli masz ograniczenia/warunki brzegowe/dodatkowe wymagania, które opisujesz teraz, dobrze byłoby wymienić te w pytaniu/opisie problemu. –

17

Chciałbym utworzyć pulę niestandardową, że coś takiego:

ForkJoinPool forkJoinPool = new ForkJoinPool(1); 
    try { 
     forkJoinPool.submit(() -> 
     IntStream.range(1, 1_000_000).filter(x -> x > 2).boxed().collect(Collectors.toList())) 
       .get(30, TimeUnit.MILLISECONDS); 
    } catch (TimeoutException e) { 
     // job not done in your interval 
    } 
+0

O ile rozumiem, powinno to nastąpić wcześniej w przypadku przekroczenia limitu czasu, ale w jaki sposób widełki dołączą do puli przestać przetwarzać, aby nie zużywać więcej zasobów procesora niż to konieczne? Według mnie będzie on nadal przetwarzał strumień w tle. Również gdzie jest operacja OP w twoim kodzie? – SpaceTrucker

+2

@SpaceTrucker można jawnie wywołać 'shutDown' lub' shutDownNow' po osiągnięciu limitu czasu. o kodzie - to był tylko przykład ... – Eugene

+0

Myślę, że twoje rozwiązanie jest najlepszym rozwiązaniem. +1 –

3

jak to, co komentarze powiedziane w ramach PO, TakeWhile/dropWhile zostały pominięte w Javie 8 (zostaną dodane w Javie 9). Nie ma żadnego powodu, aby próbować implementować logikę przez wyjątek lub inne kody, ponieważ kod wygląda tak brzydko, a całkowite nonscenes nawet to tylko dla praktyki. Myślę, że przy użyciu 3rd biblioteki Strona jest o wiele lepsze rozwiązanie, na przykład StreamEx

StreamEx(source).takeWhile(() -> System.currentTimeMillis() <= start + 30000) 
       .forEach(e -> { ... }); 
Powiązane problemy