Eu sei que não há equivalente direto no próprio Java, mas talvez um terceiro?
É muito conveniente. Atualmente, gostaria de implementar um iterador que produza todos os nós em uma árvore, que tem cerca de cinco linhas de código com rendimento.
Eu sei que não há equivalente direto no próprio Java, mas talvez um terceiro?
É muito conveniente. Atualmente, gostaria de implementar um iterador que produza todos os nós em uma árvore, que tem cerca de cinco linhas de código com rendimento.
Respostas:
As duas opções que conheço são a biblioteca infomancers-collection de Aviad Ben Dov de 2007 e a biblioteca YieldAdapter de Jim Blackler de 2008 (que também é mencionada na outra resposta).
Ambos permitirão que você escreva código com a yield return
construção -like em Java, então ambos irão satisfazer sua solicitação. As diferenças notáveis entre os dois são:
A biblioteca do Aviad está usando manipulação de bytecode, enquanto a do Jim usa multithreading. Dependendo de suas necessidades, cada um pode ter suas próprias vantagens e desvantagens. É provável que a solução do Aviad seja mais rápida, enquanto a do Jim é mais portátil (por exemplo, não acho que a biblioteca do Aviad funcione no Android).
A biblioteca do Aviad tem uma interface mais limpa - aqui está um exemplo:
Iterable<Integer> it = new Yielder<Integer>() {
@Override protected void yieldNextCore() {
for (int i = 0; i < 10; i++) {
yieldReturn(i);
if (i == 5) yieldBreak();
}
}
};
Enquanto Jim's é muito mais complicado, exigindo adept
um genérico Collector
que tem um collect(ResultHandler)
método ... ugh. No entanto, você pode usar algo como este wrapper em torno do código de Jim por Zoom Information, que simplifica muito isso:
Iterable<Integer> it = new Generator<Integer>() {
@Override protected void run() {
for (int i = 0; i < 10; i++) {
yield(i);
if (i == 5) return;
}
}
};
A solução da Aviad é BSD.
A solução de Jim é de domínio público, assim como seu invólucro mencionado acima.
AbstractIterator
.
yield(i)
será interrompido a partir do JDK 13, já que yield
está sendo adicionado como uma nova instrução Java / palavra-chave reservada.
Ambas as abordagens podem ser um pouco mais claras agora que o Java tem Lambdas. Você pode fazer algo como
public Yielderable<Integer> oneToFive() {
return yield -> {
for (int i = 1; i < 10; i++) {
if (i == 6) yield.breaking();
yield.returning(i);
}
};
}
Yielderable
? Não deveria ser Yieldable
? (o verbo sendo apenas 'render', não 'yielder' ou 'yielderate' ou qualquer outro)
yield -> { ... }
será interrompido a partir do JDK 13, pois yield
está sendo adicionado como uma nova instrução Java / palavra-chave reservada.
Eu sei que é uma questão muito antiga aqui, e existem duas maneiras descritas acima:
yield
que obviamente tem custos de recursos.No entanto, existe outra maneira, a terceira e provavelmente a mais natural, de implementar o yield
gerador em Java que é a implementação mais próxima do que os compiladores C # 2.0+ fazem para yield return/break
geração: lombok-pg . É totalmente baseado em uma máquina de estado e requer estreita cooperação com a javac
para manipular o código-fonte AST. Infelizmente, o suporte lombok-pg parece ter sido descontinuado (sem atividade de repositório por mais de um ano ou dois), e o Projeto Lombok original infelizmente não tem o yield
recurso (ele tem um IDE melhor como Eclipse, suporte IntelliJ IDEA, entretanto).
Stream.iterate (seed, seedOperator) .limit (n) .foreach (action) não é o mesmo que operador de rendimento, mas pode ser útil escrever seus próprios geradores desta maneira:
import java.util.stream.Stream;
public class Test01 {
private static void myFoo(int someVar){
//do some work
System.out.println(someVar);
}
private static void myFoo2(){
//do some work
System.out.println("some work");
}
public static void main(String[] args) {
Stream.iterate(1, x -> x + 1).limit(15).forEach(Test01::myFoo); //var1
Stream.iterate(1, x -> x + 1).limit(10).forEach(item -> myFoo2()); //var2
}
}
Eu também sugiro se você já estiver usando RXJava em seu projeto, use um Observable como um "rendimento". Ele pode ser usado de maneira semelhante se você fizer seu próprio Observável.
public class Example extends Observable<String> {
public static void main(String[] args) {
new Example().blockingSubscribe(System.out::println); // "a", "b", "c", "d"
}
@Override
protected void subscribeActual(Observer<? super String> observer) {
observer.onNext("a"); // yield
observer.onNext("b"); // yield
observer.onNext("c"); // yield
observer.onNext("d"); // yield
observer.onComplete(); // finish
}
}
Os observáveis podem ser transformados em iteradores para que você possa até mesmo usá-los em loops for mais tradicionais. Além disso, RXJava oferece ferramentas realmente poderosas, mas se você só precisa de algo simples, talvez isso seja um exagero.
Acabei de publicar outra solução (licenciada pelo MIT) aqui , que inicia o produtor em um thread separado e configura uma fila limitada entre o produtor e o consumidor, permitindo buffer, controle de fluxo e pipelining paralelo entre produtor e consumidor (então que o consumidor pode estar trabalhando no consumo do item anterior enquanto o produtor está trabalhando na produção do próximo item).
Você pode usar este formulário de classe interna anônimo:
Iterable<T> iterable = new Producer<T>(queueSize) {
@Override
public void producer() {
produce(someT);
}
};
por exemplo:
for (Integer item : new Producer<Integer>(/* queueSize = */ 5) {
@Override
public void producer() {
for (int i = 0; i < 20; i++) {
System.out.println("Producing " + i);
produce(i);
}
System.out.println("Producer exiting");
}
}) {
System.out.println(" Consuming " + item);
Thread.sleep(200);
}
Ou você pode usar a notação lambda para reduzir o clichê:
for (Integer item : new Producer<Integer>(/* queueSize = */ 5, producer -> {
for (int i = 0; i < 20; i++) {
System.out.println("Producing " + i);
producer.produce(i);
}
System.out.println("Producer exiting");
})) {
System.out.println(" Consuming " + item);
Thread.sleep(200);
}