W wielu innych językach, np. Haskell, łatwo jest wielokrotnie powtórzyć wartość lub funkcję, np. aby otrzymać listę 8 kopii wartości 1:
take 8 (repeat 1)
ale nie znalazłem tego jeszcze w Javie 8. Czy jest taka funkcja w JDK Java 8?
Lub alternatywnie coś równoważnego z zakresem takim jak
[1..8]
Wydawałoby się, że jest oczywistym zamiennikiem pełnej instrukcji w Javie
for (int i = 1; i <= 8; i++) {
System.out.println(i);
}
mieć coś takiego
Range.from(1, 8).forEach(i -> System.out.println(i))
chociaż ten konkretny przykład nie wygląda właściwie na bardziej zwięzły ... ale miejmy nadzieję, że jest bardziej czytelny.
Odpowiedzi:
W tym konkretnym przykładzie możesz zrobić:
Jeśli potrzebujesz kroku innego niż 1, możesz użyć funkcji mapowania, na przykład dla kroku 2:
Lub utwórz niestandardową iterację i ogranicz jej rozmiar:
źródło
IntStream.rangeClosed(1, 8).forEach(i -> methodNoArgs());
), ale IMO wprowadza w błąd i w takim przypadku pętla wydaje się wskazywać.Oto inna technika, z którą spotkałem się tamtego dnia:
Collections.nCopies
Wezwanie tworzyList
zawierającychn
kopie cokolwiek wartość podać. W tym przypadku jest toInteger
wartość w ramce 1. Oczywiście w rzeczywistości nie tworzy listy zn
elementami; tworzy „zwirtualizowaną” listę, która zawiera tylko wartość i długość, a każde wywołanieget
w zakresie po prostu zwraca wartość. TanCopies
metoda istnieje od czasu wprowadzenia struktury kolekcji w JDK 1.2. Oczywiście możliwość tworzenia strumienia z jego wyniku została dodana w Javie SE 8.Wielka sprawa, inny sposób na zrobienie tego samego w tej samej liczbie linii.
Jednak technika ta jest szybsza niż
IntStream.generate
iIntStream.iterate
podejść, i zaskakująco, ale także szybciej niżIntStream.range
podejścia.Dla
iterate
agenerate
wynik nie jest może zbyt zaskakujące. Struktura strumieni (tak naprawdę Spliteratory dla tych strumieni) jest zbudowana przy założeniu, że lambdy będą potencjalnie generować różne wartości za każdym razem i będą generować nieograniczoną liczbę wyników. To sprawia, że równoległe rozłupywanie jest szczególnie trudne.iterate
Metoda jest również problematyczne w tym przypadku, ponieważ każde wywołanie wymaga wynik poprzedniego. Zatem strumienie używającegenerate
iiterate
nie radzą sobie zbyt dobrze z generowaniem powtarzających się stałych.range
Zaskakujące jest stosunkowo słabe działanie programu . To również jest zwirtualizowane, więc w rzeczywistości nie wszystkie elementy istnieją w pamięci, a rozmiar jest znany z góry. Powinno to zapewnić szybki i łatwy równolegle rozdzielacz. Ale, co zaskakujące, nie wyszło zbyt dobrze. Być może powodem jest to, żerange
musi obliczyć wartość dla każdego elementu zakresu, a następnie wywołać na nim funkcję. Ale ta funkcja po prostu ignoruje swoje dane wejściowe i zwraca stałą, więc jestem zaskoczony, że nie jest ona wstawiana i zabijana.Collections.nCopies
Technika musi zrobić boks / unboxing w celu obsługi wartości, ponieważ nie istnieją prymitywne specjalizacjeList
. Ponieważ wartość jest za każdym razem taka sama , w zasadzie jest ona zapakowana raz i to pudełko jest wspólne dla wszystkichn
kopii. Podejrzewam, że boxing / unboxing jest wysoce zoptymalizowany, a nawet zintensyfikowany i można go dobrze wprowadzić.Oto kod:
A oto wyniki JMH: (2,8 GHz Core2Duo)
W wersji ncopies występuje spora rozbieżność, ale ogólnie wydaje się, że jest ona 20 razy szybsza niż wersja z zakresu. (Chociaż byłbym skłonny uwierzyć, że zrobiłem coś złego).
Jestem zaskoczony, jak dobrze
nCopies
działa ta technika. Wewnętrznie nie robi to zbyt wiele, ponieważ strumień zwirtualizowanej listy jest po prostu implementowany za pomocąIntStream.range
! Spodziewałem się, że konieczne będzie stworzenie wyspecjalizowanego rozdzielacza, aby to działało szybko, ale już wydaje się, że jest całkiem niezły.źródło
nCopies
rzeczywistości nic nie kopiuje , a wszystkie „kopie” wskazują na ten jeden obiekt. Zawsze jest bezpiecznie, jeśli ten obiekt jest niezmienny , na przykład w tym przykładzie prymityw w pudełku. Nawiązujesz do tego w swoim stwierdzeniu „raz w pudełku”, ale miło byłoby wyraźnie wskazać tutaj zastrzeżenia, ponieważ takie zachowanie nie jest specyficzne dla auto-boksu.LongStream.range
to, że jest znacznie wolniejszy niżIntStream.range
? Więc dobrze, że pomysł nie oferowaniaIntStream
(ale używaniaLongStream
dla wszystkich typów liczb całkowitych) został odrzucony. Zauważ, że w przypadku użycia sekwencyjnego nie ma żadnego powodu, aby używać strumienia:Collections.nCopies(8, 1).forEach(i -> System.out.println(i));
robi to samo,Collections.nCopies(8, 1).stream().forEach(i -> System.out.println(i));
ale może być nawet bardziej wydajneCollections.<Runnable>nCopies(8, () -> System.out.println(1)).forEach(Runnable::run);
LongStream.range
działa gorzej, ponieważ ma dwie mapy zLongFunction
wnętrzem, podczas gdyncopies
ma trzy mapy zIntFunction
,ToLongFunction
aLongFunction
więc wszystkie lambdy są monomorficzne. Przeprowadzenie tego testu na profilu typu wstępnie zanieczyszczonego (który jest bliższy rzeczywistej sytuacji) pokazuje, żencopies
jest on 1,5 raza wolniejszy.for
pętlą. Chociaż twoje rozwiązanie jest szybsze niżStream
kod, przypuszczam, żefor
pętla pokonałaby którekolwiek z nich ze znacznym marginesem.Dla kompletności, a także dlatego, że nie mogłem się powstrzymać :)
Generowanie ograniczonej sekwencji stałych jest dość zbliżone do tego, co można zobaczyć w Haskell, tylko w przypadku gadatliwości na poziomie Java.
źródło
() -> 1
wygeneruje tylko jedynki, czy to jest zamierzone? Więc wynik byłby1 1 1 1 1 1 1 1
.take 8 (repeat 1)
. assylias prawie obejmowały wszystkie pozostałe przypadki.Stream<T>
ma również ogólnągenerate
metodę uzyskiwania nieskończonego strumienia innego typu, który można ograniczyć w ten sam sposób.Gdy funkcja powtarzania jest gdzieś zdefiniowana jako
Możesz go używać od czasu do czasu w ten sposób, np .:
Aby uzyskać i odpowiednik Haskella
Możesz pisać
źródło
Runnable
to,Function<Integer, ?>
a następnie używającf.apply(i)
.To jest moje rozwiązanie do implementacji funkcji czasów. Jestem junior, więc przyznaję, że mogłoby to nie być idealne. Z przyjemnością usłyszę, czy to nie jest dobry pomysł z jakiegokolwiek powodu.
Oto kilka przykładów użycia:
źródło