2010-02-28 17 views
36

He creado una lista vinculada en java con genéricos, y ahora quiero poder iterar sobre todos los elementos de la lista. En C# usaría yield return dentro de la lista vinculada mientras repaso la lista de elementos que contiene la lista.Rendimiento de rendimiento en Java

¿Cómo podría hacer para crear una versión java del anterior donde pueda iterar sobre todos los elementos contenidos en la lista vinculada?

que estoy buscando para ser capaz de escribir código ala

LinkedList<something> authors = new LinkedList<something>(); 
for (Iterator<something> i = authors.Values ; i.HasNext()) 
     doSomethingWith(i.Value); 

Y estaba pensando que el valor 'propiedad'/método consistiría en código similar

LinkedListObject<something> current = first; 
While (current != null){ 
yield return current.getValue(); 
current = current.getNext() 
} 

Editar: Tenga en cuenta que yo No me interesa usar ninguna API de terceros. Funcionalidad java incorporada solamente.

+0

http: // stackoverflow.com/questions/1980953/is-there-a-java-equivalent-to-cs-yield-keyword –

+2

no sé C#. Curioso, ¿qué hace el retorno de rendimiento? – bragboy

+0

revise esto: http://msdn.microsoft.com/en-us/library/9k7k7cf0(VS.80).aspx –

Respuesta

-21

¿Me falta algo aquí? Ya existe java.util.LinkedList, está totalmente habilitado para genéricos y tiene un método que devuelve un iterador.

Si realmente desea reinventar la rueda, le sugiero que considere crear una clase LinkedListIterator, probablemente implementando ListIterator.Recordaría su posición actual dentro de la lista enlazada y la avanzará en cada llamada sucesiva.

+2

Las razones para crear mis propias estructuras de datos se deben a la gran necesidad de mejorar el rendimiento más adelante en el proceso (es posible que no se aplique tanto a la lista vinculada como a la tabla hash). Tu solución con un iterador sub-clasificado fue aquella que usé para resolver el problema. –

+22

Creo que puede estar perdiendo algo (como es el OP). 'yield return' en C# /. Net crea solo un iterador. Si el consumidor del iterador termina antes, entonces también lo hace el ciclo que genera la iteración. Un generador infinito es un ejemplo de algo que no es posible sin un iterador que no usa una colección como un paso intermedio. Para el ejemplo que dieron, por supuesto solo devolverían el iterador de 'LinkedList'. –

-1

he tratado de entender lo que el rendimiento hace pero sin la experiencia de C# no estoy seguro si lo tengo, pero voy a intentarlo de todos modos ...

que sugeriría el siguiente ...

Something answer = null; 
for (Something author: authors){ 

    if (author.equals("Tom Jones"){ 
    answer = author; 
    break; 
    } 
} 

Cuando se trata de la devolución de los valores a partir de un método i haría lo siguiente ...

public LinkedList<something> getAuthors(LinkedList<something> list){ 
     LinkedList<something> ret = new LinkedList<something>(); 
     for (something s:list){ 
     if (s.equals("abc")) 
      ret.add(s); 
     } 
     return ret; 
    } 

¿He perdido la trama?

+0

¿dónde llamas getAuthors (LinkedList)? – bragboy

+0

ummm ... llamarías a getAuthors desde cualquier lugar de tu código. si creas una clase llamada Utils (como ejemplo) y haces que el método sea estático, entonces puedes decir Utils.getAuthors (pasa tu lista aquí); y eso te devolverá tu nueva lista. – Paul

+4

Llego tarde, pero lo que necesita comprender es que en C#, la declaración de rendimiento solo se ejecutará una vez que se llame al método "MoveNext()" de IEnumerable. Los elementos de IEnumerable se evalúan de forma diferida en comparación con su ejemplo. – GuiSim

-1

Si desea la funcionalidad completa de yield return, probablemente necesite configurar esto en dos hilos-- uno para el primer método, y uno para el segundo. Entonces, el primer hilo debe wait hasta que el segundo hilo ponga su valor en algún lugar accesible y notify s que esté listo. Entonces, el primer subproceso procesará ese valor, wait para el siguiente valor, etc.

+0

Creo que has confundido el hilo con http://en.wikipedia.org/wiki/Coroutine – MartinP

4

No entiendo por qué las personas están hablando de hilos ... ¿hay algo que desconozca sobre el rendimiento?

En mi opinión, el retorno de rendimiento simplemente guarda la pila de métodos y la restaura en un momento posterior. Para implementar el retorno de rendimiento, solo tiene que guardar el estado manualmente. Consulte las clases de iterador de Java para obtener más información, aunque para una lista vinculada, puede salirse con la suya al guardar el elemento actual. Para una matriz, solo necesitaría el índice.

+0

Esto es correcto. Rendimiento y retorno de rendimiento no usan hilos en C#. Hacen una transformación en tiempo de compilación y crean una máquina de estado, pero esa máquina de estado no usa ningún subproceso adicional (aunque es posible que sea seguro para subprocesos). –

1

Solo para ayudar a los lectores a entender los pequeños detalles.

Si crea una nueva lista que contiene todos los elementos resultantes y devuelve la lista, entonces esta es una buena implementación, lo suficientemente simple como para codificar. Puede tener una estructura de datos tan interesante como lo necesite y, al escanearla para las entradas correctas, simplemente devuelva una lista de todas las coincidencias, y su cliente repetirá en la lista.

Si desea guardar un estado, puede ser más complicado. Tendrás que llegar a donde has estado cada vez que se llame a tu función. Sin mencionar los problemas de reingreso, etc.

La solución con hilos no crea una nueva lista. Y es tan simple como la primera solución. El único problema es que implica una sincronización de subprocesos que es un poco más difícil de codificar y tiene sus penalizaciones de rendimiento.

Por lo tanto, sí, el retorno de rendimiento es excelente y falta en Java. Sin embargo, hay soluciones.

30

Puede devolver una implementación anónima de Iterable. Los efectos son bastante similares, solo que esto es mucho más detallado.

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 
       } 

      }; 
     } 
    }; 
} 
13

"yield return" es un truco de compilación muy sofisticado. Básicamente, le permite implementar declarativa IEnumerable sin ninguno de los molestos detalles de "averiguar" cómo construir su iterador. Lo desafortunado es que no se traduce bien a otros idiomas porque muy pocos compiladores tienen esa capacidad. De alguna manera, el "rendimiento de retorno" es tan condenatorio como revolucionario.

Básicamente en C#, el compilador generará dos implementaciones de IEnumerable e IEnumerator (de T). Lo hace básicamente realizando las variables locales de su "método" como campos de instancia en clases de implementación generadas, así como examinando los cuadros que contienen un artefacto de "rendimiento de retorno". Una vez que sepa esto, debería ser posible que un desarrollador completo logre lo mismo explícitamente ... aunque no de manera concisa. Para demostrar, voy a 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 todo se me permite mi seudo 03 a.m. java ...

Cuestiones relacionadas