Iterator est utile lors du traitement de grandes quantités de données. Surtout lors du traitement d'une grande quantité de données qui coûte beaucoup de temps de calcul et d'utilisation de la mémoire, si toutes les données sont converties à l'avance et converties en une collection telle que List, la mémoire sera insuffisante et OutOfMemory se produira, ou GC Des occurrences fréquentes peuvent ralentir considérablement le processus.
Par exemple, supposons que vous ayez une interface qui reçoit des données de liste d'un magasin de données comme suit: (On suppose que les clés primaires sont disposées dans l'ordre d'enregistrement des enregistrements)
Magasin de données.java
interface MyRepository<E>{
/**
*La valeur de la clé primaire est[fromInclusive, untilInclusive]Renvoie une liste d'enregistrements dans la plage de
*/
List<E> findByIdRange(long fromInclusive,long untilInclusive);
}
Par exemple, lorsque vous souhaitez convertir tous les enregistrements écrits un jour (une plage d'une certaine clé primaire) et plonger dans un autre magasin de données, vous pouvez écrire comme suit à l'aide de l'API Stream, par exemple. ..
Obéissant.java
MyRepository<MyRecord> myRepository;
void extractTransferAndLoad(long fromInclusive,long untilInclusive){
//Image d'un traitement intensif en charge en parallèle
myRepository.findByIdRange(fromInclusive,untilInclusive).parallelStream().map(this::transfer).foreach(this::load);
}
Cependant, si vous avez une grande quantité de données, myRepository.findByIdRange (long, long)
peut provoquer OutOfMemory, ou il peut consommer une grande quantité de mémoire et affecter les performances.
Par conséquent, je vais créer mon propre itérateur. Il y a deux interfaces à retenir pour créer un itérateur.
Interface de maîtrise des itérateurs.java
package java.util;
interface Iterator<A>{
boolean hasNext();
A next();
}
package java.lang;
interface Iterable<A>{
Iterator<A> iterator();
}
Écrivons une classe qui obtient le findByIdRange
ci-dessus séparément en utilisant un itérateur.
Implémentation d'itérateur.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 pour renvoyer une liste
public MyIterableRepository(MyRepository<A> myRepository,long fromInclusive,long untilInclusive,int splitSize){
//Constructeur juste pour DI ... omis
}
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;
}
};
}
}
Vous avez maintenant un itérateur. C'est un modèle simple, donc c'est facile si vous vous y habituez et vous en souvenez. Lorsque Iterable est implémenté, l'appelant peut utiliser l'instruction Extended for pour écrire:
Votre interlocuteur.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){
//Image d'un traitement intensif en charge en parallèle
myDataList.parallelStream().map(this::transfer).forEach(this::load);
}
}
En changeant de cette manière, vous pouvez réduire le nombre d'objets générés à la fois, et à la fin de l'instruction for, myDataList
(et sa destination de référence) peut être modifiée à partir de GC, et vous pouvez éviter de tomber dans OoM. Je vais.
Il y a des moments où vous souhaitez synthétiser un itérateur, par exemple lorsque vous devez récupérer des données à partir de plusieurs sources de données. Par exemple, si vous avez des informations sur l'utilisateur, un historique des achats associé à l'utilisateur et un historique de validation associé à l'utilisateur, et que vous créez une page statique intégrée dans le traitement par lots, double extension pour C'est juste une phrase (ci-dessous, une image du contenu du processus que vous souhaitez réaliser)
Je veux faire ce qui suit en tant que traitement.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( /*Ignorez l'ID que vous avez déjà traité*/ ).map( /*Convertir*/ ).forEach( /*Exportation*/ );
}
}
}
Si vous recevez le processus de réception d'un itérateur et de sa conversion, et que vous devez l'appeler, vous avez des problèmes. (Exemple d'appelé ci-dessous)
Appelé.java
//Méthode appelée
void extractTransferAndLoad2(Iterable<List<UserId>> userIdsListIterable){
for(List<UserId> userIds : userIdsListIterable){
userIds.parallelStream().filter( /*Ignorez l'ID que vous avez déjà traité*/ ).map( /*Convertir*/ ).forEach( /*Exportation*/ );
}
}
//Image de ce que l'appelant veut faire
extractTransferAndLoad2(repositories.flatMap(repository -> new MyIterableRepository2(repository,fromInclusive,untilInclusive,42).getUserIdsIterable()));
//Iterator n'a pas de flatMap, vous ne pouvez donc pas faire cela.
Donc, si vous utilisez la méthode de synthèse de l'itérateur super incroyable que j'ai pensé cette fois
Synthèse d'itérateur.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();
}
};
}
}
Puis
Lors de l'appel.java
extractTransferAndLoad2(IteratorUtils.composedIterator(repositories, repository -> new MyIterableRepository2(repository,fromInclusive,untilInclusive,42).getUserIdsIterable()));
Vous pouvez synthétiser et passer l'itérateur comme! !!
Au fait, je pensais que si j'arrêtais Iterator et retournais tout avec Stream, ce serait un coup avec flatMap, mais il n'y a aucun moyen de créer un Stream fini avec Java autre que de créer Spliterator basé sur Iterator, donc je suis désolé. Cependant, cela n'a pas pu être réalisé.
Recommended Posts