J'ai créé une liste chaînée en Java en utilisant des génériques, et maintenant je veux pouvoir parcourir tous les éléments de la liste. En C # j'utiliserais yield return
à l'intérieur de la liste chaînée en parcourant la liste des éléments contenus dans la liste.
Comment pourrais-je créer une Java de ce qui précède où je peux parcourir tous les éléments contenus dans la liste liée?
Je cherche à pouvoir écrire du code ala
LinkedList<something> authors = new LinkedList<something>();
for (Iterator<something> i = authors.Values ; i.HasNext())
doSomethingWith(i.Value);
Et pensait que la "propriété"/méthode Value consisterait en un code ressemblant à
LinkedListObject<something> current = first;
While (current != null){
yield return current.getValue();
current = current.getNext()
}
Edit: notez que je ne suis pas intéressé à utiliser des API tierces. Built-in Java fonctionnalité uniquement.
Vous pouvez renvoyer une implémentation anonyme d'Iterable. Les effets sont assez similaires, juste que c'est beaucoup plus verbeux.
public Iterable<String> getStuff() {
return new Iterable<String>() {
@Override
public Iterator<String> iterator() {
return new Iterator<String>() {
@Override
public boolean hasNext() {
// TODO code to check next
}
@Override
public String next() {
// TODO code to go to next
}
@Override
public void remove() {
// TODO code to remove item or throw exception
}
};
}
};
}
"yield return" est une astuce de compilation très sophistiquée. Il vous permet essentiellement d'implémenter déclarativement IEnumerable sans aucun détail gênant pour "comprendre" comment construire votre itérateur. Le malheur est qu'il ne se traduit pas bien dans d'autres langues car très peu de compilateurs ont une telle capacité. À certains égards, le "rendement du rendement" est aussi accablant que révolutionnaire.
Fondamentalement en C #, le compilateur générera deux implémentations de IEnumerable et IEnumerator (de T). Il le fait en réalisant essentiellement les variables locales de votre "méthode" en tant que champs d'instance dans les classes d'implémentation générées ainsi qu'en examinant les trames contenant un artefact "return return". Une fois que vous le savez, il devrait être possible pour un développeur bien équilibré d'accomplir la même chose explicitement ... mais pas de manière aussi concise. Pour démontrer, je vais CONCAT!
public static <T> Iterable<T> concat(Iterable<T> x, Iterable<T> y)
{
for(T e: x)
{
yield return e;
}
for(T e: y)
{
yield return e;
}
}
// becomes ....
public static <E> Iterator<E> concat_(Iterable<E> x, Iterator<E> y)
{
T e1, e2;
Iterator<E> i1, i2;
Iterator<E> s;
Iterator<E> s4 = new Iterator<E>()
{
public bool hasNext()
{
return false;
}
public E next()
{
throw ... ;
}
public void remove()
{
throw ... ;
}
}
Iterator<E> s3 = new Iterator<E>()
{
Iterator<E> act()
{
if(i2.hasNext())
{
return i2;
}
i2 = y.iterator();
return (s = s4);
}
public bool hasNext()
{
return act().hasNext();
}
public E next()
{
return act().next();
}
public void remove()
{
return i2.remove();
}
}
Iterator<E> s2 = new Iterator<E>()
{
Iterator<E> act()
{
if(i1.hasNext())
{
return i1;
}
i2 = y.iterator();
return (s = s3);
}
public bool hasNext()
{
return act().hasNext();
}
public E next()
{
return act().next();
}
public void remove()
{
return i1.remove();
}
};
Iterator<E> s1 = new Iterator<E>()
{
Iterator<E> act()
{
i1 = x.iterator();
return s = s2;
}
public bool hasNext()
{
return act().hasNext();
}
public E next()
{
return act().next();
}
public void remove()
{
return act().remove();
}
};
s = s1;
return new Iterator<T>()
{
public bool hasNext()
{
return s.hasNext();
}
public E next()
{
return s.next();
}
public void remove()
{
return s.remove();
}
};
}
public static <T> Iterable<T> concat(Iterable<T> x, Iterable<T> y)
{
return new Iterable<T>()
{
public Iterator<T> iterator()
{
return concat_(x, y)
}
};
}
// tada!
Si vous pardonnez tous à mon pseudo Java 3 heures du matin ...
essaye ça
consultez également cet article pour un exemple d'implémentation:
Je ne comprends pas pourquoi les gens parlent de threads ... y a-t-il quelque chose que je ne sais pas sur le rendement?
À ma connaissance, return return enregistre simplement la pile de méthodes et la restaure ultérieurement. Pour implémenter le retour de rendement, il vous suffit d'enregistrer l'état manuellement. Voir les classes d'itérateur Java pour plus de détails, mais pour une liste chaînée, vous pouvez simplement vous en sortir en enregistrant l'élément en cours. Pour un tableau, vous avez juste besoin de l'index.
Juste pour aider les lecteurs à comprendre les petits détails.
Si vous créez une nouvelle liste contenant tous les éléments résultants et renvoyez la liste, alors c'est une bonne implémentation, assez simple à coder. Vous pouvez avoir une structure de données aussi intéressante que vous le souhaitez, et lors de la numérisation pour les bonnes entrées, renvoyez simplement une liste de toutes les correspondances, et votre client itérera sur la liste.
Si vous souhaitez enregistrer un état, cela peut être plus compliqué. Vous devrez vous rendre là où vous vous êtes rendu chaque fois que votre fonction est appelée. Sans parler des problèmes de rentrée, etc.
La solution avec les threads ne crée pas de nouvelle liste. Et c'est aussi simple que la première solution. Le seul problème est que vous impliquez une synchronisation de threads qui est un peu plus difficile à coder et qui a ses inconvénients de performances.
Donc, oui, le rendement est excellent et manque à Java. Pourtant, il existe des solutions de contournement.
une opération de rendement peut être considérée comme
par conséquent, je l'implémente en tant que classe de machine à états, coroutine. dans ce mécanisme, chaque instruction a son pointeur d'instruction, son index et l'instruction peut avoir une étiquette avec elle, nous pouvons donc utiliser jmp (étiquette) pour passer à l'étiquette.
par exemple:
public class FibbonaciCoroutine implements Iterator<BigInteger> {
BigInteger[] bucket = { new BigInteger("1"), new BigInteger("1"), new BigInteger("0") };
int idx = 2;
Coroutine coroutine = new Coroutine((pthis) -> {
pthis.addInstruction("_label1", (me) -> {
int p1 = idx - 2;
int p2 = idx - 1;
if (p1 < 0)
p1 += 3;
if (p2 < 0)
p2 += 3;
bucket[idx] = bucket[p1].add(bucket[p2]);
idx = (idx + 1) % bucket.length;
me.yield(bucket[idx]);
});
// goto
pthis.addInstruction((me) -> {
me.jmp("_label1");
});
pthis.start();
});
@Override
public boolean hasNext() {
return !coroutine.isStopped();
}
@Override
public BigInteger next() {
while (coroutine.exec())
;
return coroutine.getYieldValue();
}
public static void main(String[] argv) {
FibbonaciCoroutine cor = new FibbonaciCoroutine();
for (int i = 0; i < 100 && cor.hasNext(); ++i) {
System.out.printf("%d ", cor.next());
}
}
}