¿Existe un equivalente en Java a la palabra clave 'rendimiento' de C #?

112

Sé que no hay un equivalente directo en Java en sí, pero ¿quizás un tercero?

Es realmente conveniente. Actualmente, me gustaría implementar un iterador que produzca todos los nodos en un árbol, que son aproximadamente cinco líneas de código con rendimiento.

destripador234
fuente
6
Sé que sé. Pero creo que saber más idiomas es más poderoso. Además, el desarrollo de backend (que estoy haciendo) en la empresa para la que trabajo en este momento se realiza en Java, por lo que realmente no puedo elegir el idioma :(
ripper234

Respuestas:

91

Las dos opciones que conozco son la biblioteca de colecciones de infomantes de Aviad Ben Dov de 2007 y la biblioteca YieldAdapter de Jim Blackler de 2008 (que también se menciona en la otra respuesta).

Ambos le permitirán escribir código con una yield returnconstrucción similar en Java, por lo que ambos satisfarán su solicitud. Las diferencias notables entre los dos son:

Mecánica

La biblioteca de Aviad utiliza la manipulación de códigos de bytes, mientras que la de Jim utiliza subprocesos múltiples. Dependiendo de sus necesidades, cada uno puede tener sus propias ventajas y desventajas. Es probable que la solución de Aviad sea más rápida, mientras que la de Jim sea más portátil (por ejemplo, no creo que la biblioteca de Aviad funcione en Android).

Interfaz

La biblioteca de Aviad tiene una interfaz más limpia; aquí hay un ejemplo:

Iterable<Integer> it = new Yielder<Integer>() {
    @Override protected void yieldNextCore() {
        for (int i = 0; i < 10; i++) {
            yieldReturn(i);
            if (i == 5) yieldBreak();
        }
    }
};

Mientras que el de Jim es mucho más complicado, requiriendo adeptun genérico Collectorque tenga un collect(ResultHandler)método ... uf. Sin embargo, podría usar algo como este envoltorio alrededor del código de Jim de Zoom Information que simplifica enormemente eso:

Iterable<Integer> it = new Generator<Integer>() {
    @Override protected void run() {
        for (int i = 0; i < 10; i++) {
            yield(i);
            if (i == 5) return;
        }
    }
};

Licencia

La solución de Aviad es BSD.

La solución de Jim es de dominio público, al igual que su envoltorio mencionado anteriormente.

Roble
fuente
3
Fantástica respuesta. No solo respondió totalmente la pregunta, sino que lo hizo de una manera muy clara. Además, me gusta el formato de su respuesta y cómo incluyó la información de la licencia. ¡Sigan con la respuesta increíble! :)
Malcolm
1
No te olvides de Guayaba AbstractIterator.
shmosel
Acabo de publicar otra solución (con licencia del MIT) aquí, que lanza un hilo separado para el productor y establece una cola limitada entre el productor y el consumidor: github.com/lukehutch/Producer
Luke Hutchison
Tenga en cuenta que yield(i)se romperá a partir de JDK 13, ya que yieldse agregará como una nueva declaración de Java / palabra clave reservada.
Luke Hutchison
14

Ambos enfoques se pueden hacer un poco más limpios ahora que Java tiene Lambdas. Puedes hacer algo como

public Yielderable<Integer> oneToFive() {
    return yield -> {
        for (int i = 1; i < 10; i++) {
            if (i == 6) yield.breaking();
            yield.returning(i);
        }
    };
}

Expliqué un poco más aquí.

benjiweber
fuente
4
Yielderable? ¿No debería ser así Yieldable? (el verbo es simplemente 'ceder', no 'ceder' o 'ceder' o lo que sea)
Jochem Kuijpers
yield -> { ... }se romperá a partir de JDK 13, ya que yieldse agregará como una nueva declaración de Java / palabra clave reservada.
Luke Hutchison
1

Sé que es una pregunta muy antigua aquí, y hay dos formas descritas anteriormente:

  • manipulación del código de bytes que no es tan fácil durante la migración;
  • basado en hilos yieldque obviamente tiene costos de recursos.

Sin embargo, existe otra forma, la tercera y probablemente la más natural, de implementar el yieldgenerador en Java que es la implementación más cercana a lo que hacen los compiladores de C # 2.0+ para la yield return/breakgeneración: lombok-pg . Está completamente basado en una máquina de estado y requiere una estrecha cooperación javacpara manipular el código fuente AST. Desafortunadamente, el soporte de lombok-pg parece estar descontinuado (sin actividad de repositorio durante más de un año o dos), y el Proyecto Lombok original lamentablemente carece de la yieldfunción (aunque tiene un mejor IDE como Eclipse, soporte IntelliJ IDEA).

Lyubomyr Shaydariv
fuente
1

Stream.iterate (seed, seedOperator) .limit (n) .foreach (action) no es lo mismo que el operador de rendimiento, pero puede ser útil escribir sus propios generadores de esta manera:

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
    }
}
Andrey Lavrukhin
fuente
0

También sugiero que si ya está usando RXJava en su proyecto, use un Observable como un "productor". Se puede usar de manera similar si crea su propio Observable.

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
    }
}

Los observables se pueden transformar en iteradores, por lo que incluso puede usarlos en bucles for más tradicionales. Además, RXJava te brinda herramientas realmente poderosas, pero si solo necesitas algo simple, quizás esto sea una exageración.

Győri Sándor
fuente
0

Acabo de publicar otra solución (con licencia del MIT) aquí , que lanza el productor en un hilo separado y configura una cola limitada entre el productor y el consumidor, lo que permite el almacenamiento en búfer, el control de flujo y la canalización en paralelo entre el productor y el consumidor (por lo que que el consumidor puede estar trabajando en consumir el artículo anterior mientras que el productor está trabajando en producir el siguiente artículo).

Puede utilizar este formulario de clase interna anónima:

Iterable<T> iterable = new Producer<T>(queueSize) {
    @Override
    public void producer() {
        produce(someT);
    }
};

por ejemplo:

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);
}

O puede usar la notación lambda para reducir el texto estándar:

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);
}
Luke Hutchison
fuente