Iterator ist nützlich bei der Verarbeitung großer Datenmengen. Insbesondere bei der Verarbeitung einer großen Datenmenge, die eine hohe Berechnungszeit und Speicherauslastung erfordert, ist der Speicher nicht ausreichend, wenn alle Daten im Voraus konvertiert und in eine Sammlung wie List konvertiert werden, und OutOfMemory oder GC wird ausgeführt Häufige Vorkommnisse können den Prozess erheblich verlangsamen.
Angenommen, Sie haben eine Schnittstelle, die Listendaten aus einem Datenspeicher wie folgt empfängt: (Es wird davon ausgegangen, dass die Primärschlüssel in der Reihenfolge der Datensatzregistrierung angeordnet sind.)
Datenspeicher.java
interface MyRepository<E>{
/**
*Der Wert des Primärschlüssels ist[fromInclusive, untilInclusive]Gibt eine Liste von Datensätzen im Bereich von zurück
*/
List<E> findByIdRange(long fromInclusive,long untilInclusive);
}
Wenn Sie beispielsweise alle an einem Tag geschriebenen Datensätze (einen Bereich eines bestimmten Primärschlüssels) konvertieren und in einen anderen Datenspeicher eintauchen möchten, können Sie beispielsweise mithilfe der Stream-API wie folgt schreiben. ..
Gehorsam.java
MyRepository<MyRecord> myRepository;
void extractTransferAndLoad(long fromInclusive,long untilInclusive){
//Bild der parallelen Ausführung einer lastintensiven Verarbeitung
myRepository.findByIdRange(fromInclusive,untilInclusive).parallelStream().map(this::transfer).foreach(this::load);
}
Wenn Sie jedoch über eine große Datenmenge verfügen, kann "myRepository.findByIdRange (long, long)" OutOfMemory verursachen oder viel Speicherplatz beanspruchen und die Leistung beeinträchtigen.
Daher werde ich meinen eigenen Iterator erstellen. Es gibt zwei Schnittstellen, an die Sie denken müssen, um einen Iterator zu erstellen.
Schnittstelle zum Mastering von Iteratoren.java
package java.util;
interface Iterator<A>{
boolean hasNext();
A next();
}
package java.lang;
interface Iterable<A>{
Iterator<A> iterator();
}
Schreiben wir eine Klasse, die den obigen findByIdRange
separat mit einem Iterator erhält.
Iterator-Implementierung.java
class MyIterableRepository<A> implements Iterable<List<A>>{
final private MyRepository<A> myRepository;
final private long fromInclusive;
final private long untilInclusive;
final private int splitSize; //split by splitSize, um eine Liste zurückzugeben
public MyIterableRepository(MyRepository<A> myRepository,long fromInclusive,long untilInclusive,int splitSize){
//Konstruktor nur für DI ... weggelassen
}
public Iterator<List<A>> iterator() {
return new Iterator<List<A>>() {
long currentFromInclusive = fromInclusive;
public boolean hasNext() {
return untilInclusive >= currentFromInclusive;
}
public List<A> next() {
long currentUntilInclusive = Math.min(untilInclusive, nextFromInclusive+splitSize-1);
List<A> ret = myRepository.findById(currentFromInclusive,currentUntilInclusive);
currentFromInclusive = currentUntilInclusive+1;
return ret;
}
};
}
}
Jetzt haben Sie einen Iterator. Es ist ein einfaches Muster, also ist es einfach, wenn Sie sich daran gewöhnen und sich daran erinnern. Wenn Iterable implementiert ist, kann der Aufrufer die erweiterte for-Anweisung verwenden, um Folgendes zu schreiben:
Anrufer.java
MyRepository<MyRecord> myRepository;
void extractTransferAndLoad(long fromInclusive,long untilInclusive){
MyIterableRepository<MyData> myIterableRepository = new MyIterableRepository(myRepository,fromInclusive,untilInclusive, MagicNumbers.SPLIT_SIZE);
for(List<MyData> myDataList : myIterableRepository){
//Bild der parallelen Ausführung einer lastintensiven Verarbeitung
myDataList.parallelStream().map(this::transfer).forEach(this::load);
}
}
Durch diese Änderung können Sie die Anzahl der gleichzeitig generierten Objekte reduzieren. Am Ende der for-Anweisung kann myDataList
(und sein Referenzziel) über GC geändert werden, und Sie können vermeiden, in OoM zu fallen. Ich werde.
Es gibt Zeiten, in denen Sie einen Iterator synthetisieren möchten, z. B. wenn Sie Daten aus mehreren Datenquellen abrufen müssen. Wenn Sie beispielsweise über Benutzerinformationen, einen mit dem Benutzer verknüpften Kaufverlauf und einen mit dem Benutzer verknüpften Buchungsverlauf verfügen und eine integrierte statische Seite in der Stapelverarbeitung erstellen, doppelte Erweiterung für Es ist nur ein Satz (unten ein Bild des Inhalts des Prozesses, den Sie realisieren möchten)
Ich möchte Folgendes als Verarbeitung ausführen.java
Iterable<MyRepository2> repositories;
void extractTransferAndLoad(MyTime fromInclusive,MyTime untilInclusive){
for(MyRepository2 repository: repositories){
Iterable<List<UserId>> ittrable = new MyIterableRepository2(repository,fromInclusive,untilInclusive,42).getUserIdsIterable();
for(List<UserId> userIds : ittrable){
userIds.parallelStream().filter( /*Ignorieren Sie die ID, die Sie bereits verarbeitet haben*/ ).map( /*Konvertieren*/ ).forEach( /*Export*/ );
}
}
}
Wenn Sie den Prozess erhalten, einen Iterator zu empfangen und zu konvertieren, und Sie ihn aufrufen müssen, sind Sie in Schwierigkeiten. (Beispiel eines Angerufenen unten)
Angerufene.java
//Aufgerufene Methode
void extractTransferAndLoad2(Iterable<List<UserId>> userIdsListIterable){
for(List<UserId> userIds : userIdsListIterable){
userIds.parallelStream().filter( /*Ignorieren Sie die ID, die Sie bereits verarbeitet haben*/ ).map( /*Konvertieren*/ ).forEach( /*Export*/ );
}
}
//Bild von dem, was der Anrufer tun möchte
extractTransferAndLoad2(repositories.flatMap(repository -> new MyIterableRepository2(repository,fromInclusive,untilInclusive,42).getUserIdsIterable()));
//Iterator hat keine flatMap, daher können Sie dies nicht tun.
Also, wenn Sie die Methode verwenden, den super erstaunlichen Iterator zu synthetisieren, den ich diesmal dachte
Iteratorsynthese.java
public class IteratorUtils {
public static <A,B> Iterator<B> composedIterator(Iterator<A> aittr, Function<A,Iterator<B>> func){
return new Iterator<B>(){
Iterator<B> bittr = Collections.emptyIterator();
public boolean hasNext() {
while(!bittr.hasNext() && aittr.hasNext()){
bittr = func.apply(aittr.next());
}
return bittr.hasNext();
}
public B next() {
while(!bittr.hasNext() && aittr.hasNext()){
bittr = func.apply(aittr.next());
}
return bittr.next();
}
};
}
}
Dann
Beim Anruf.java
extractTransferAndLoad2(IteratorUtils.composedIterator(repositories, repository -> new MyIterableRepository2(repository,fromInclusive,untilInclusive,42).getUserIdsIterable()));
Sie können den Iterator als synthetisieren und übergeben! !!
Übrigens dachte ich, wenn ich Iterator stoppen und alles mit Stream zurückgeben würde, wäre es eine Aufnahme mit flatMap, aber es gibt keine andere Möglichkeit, einen endlichen Stream mit Java zu erstellen, als Spliterator auf Iterator-Basis zu erstellen. Es tut mir leid. Es konnte jedoch nicht realisiert werden.
Recommended Posts