validar jdate from datechooser chooser java collections

java - jdate - ¿Cómo iterar más elegantemente a través de colecciones paralelas?



jdate java (8)

Digamos que tengo 2 colecciones paralelas, por ejemplo: una lista de nombres de personas en una List<String> y una lista de su edad en una List<Int> en el mismo orden (para que cualquier índice dado en cada colección se refiera a la misma persona )

Quiero iterar a través de ambas colecciones al mismo tiempo y buscar el nombre y la edad de cada persona y hacer algo con ella. Con arreglos esto se hace fácilmente con:

for (int i = 0; i < names.length; i++) { do something with names[i] .... do something with ages[i]..... }

¿Cuál sería la forma más elegante (en términos de legibilidad y velocidad) de hacer esto con las colecciones?


Acabo de publicar esta función en esta pregunta similar (que @Nils von Barth afirma no es un duplicado;)), pero es igualmente aplicable aquí:

public static <L,R,M> List<M> zipLists( BiFunction<L,R,M> factory, Iterable<L> left, Iterable<R> right) { Iterator<L> lIter = left.iterator(); Iterator<R> rIter = right.iterator(); ImmutableList.Builder<M> builder = ImmutableList.builder(); while (lIter.hasNext() && rIter.hasNext()) { builder.add(factory.apply(lIter.next(), rIter.next())); } // Most of the existing solutions fail to enforce that the lists are the same // size. That is a *classic* source of bugs. Always enforce your invariants! checkArgument(!lIter.hasNext(), "Unexpected extra left elements: %s", ImmutableList.copyOf(lIter)); checkArgument(!rIter.hasNext(), "Unexpected extra right elements: %s", ImmutableList.copyOf(rIter)); return builder.build(); }

A continuación, puede proporcionar una operación de fábrica para BiFunction , como el constructor de un tipo de valor:

List<Person> people = zipLists(Person::new, names, ages);

Si realmente desea iterar sobre ellos y realizar alguna operación, en lugar de construir una nueva colección, puede cambiar la BiFunction para un BiConsumer y hacer que la función vuelva a estar void .


Crearía un nuevo objeto que encapsula los dos. Tíralo en la matriz e itera sobre eso.

List<Person>

Dónde

public class Person { public string name; public int age; }


Podría crear una interfaz para ello:

public interface ZipIterator<T,U> { boolean each(T t, U u); } public class ZipUtils { public static <T,U> boolean zip(Collection<T> ct, Collection<U> cu, ZipIterator<T,U> each) { Iterator<T> it = ct.iterator(); Iterator<U> iu = cu.iterator(); while (it.hasNext() && iu.hasNext()) { if (!each.each(it.next(), iu.next()) { return false; } } return !it.hasNext() && !iu.hasNext(); } }

Y luego tienes:

Collection<String> c1 = ... Collection<Long> c2 = ... zip(c1, c2, new ZipIterator<String, Long>() { public boolean each(String s, Long l) { ... } });


Según lo sugerido por jeef3, modelar el dominio verdadero en lugar de mantener Listas separadas, implícitamente acopladas es el camino correcto a seguir ... cuando esta es una opción.

Existen varios motivos por los cuales es posible que no pueda adoptar este enfoque. Si es así...

R. Puedes usar un enfoque de devolución de llamada, como lo sugirió cletus.

B. Todavía puede optar por exponer un iterador que expone el elemento de objeto de dominio para cada instancia compuesta. Este enfoque no te obliga a mantener una estructura de listas paralelas.

private List<String> _names = ...; private List<Integer> _ages = ...; Iterator<Person> allPeople() { final Iterator<String> ni = _names.iterator(); final Iterator<Integer> ai = _ages.iterator(); return new Iterator() { public boolean hasNext() { return ni.hasNext(); } public Person next() { return new Person(ni.next(), ai.next()); } public void remove() { ni.remove(); ai.remove(); } }; }

C. Puede usar una variación de esto y usar una API de cursor de estilo RowSet. Digamos que IPerson es una interfaz que describe Persona. Entonces podemos hacer:

public interface IPerson { String getName(); void setName(String name); ... } public interface ICursor<T> { boolean next(); T current(); } private static class PersonCursor implements IPerson, ICursor<IPerson> { private final List<String> _names; ... private int _index = -1; PersonCursor(List<String> names, List<Integer> ages) { _names = names; ... } public boolean next() { return ++_index < _names.size(); } public Person current() { return this; } public String getName() { return _names.get(_index); } public void setName(String name) { _names.set(0, name); } ... } private List<String> _names = ...; private List<Integer> _ages = ...; Cursor<Person> allPeople() { return new PersonCursor(_names, _ages); }

Tenga en cuenta que el enfoque B también se debe hacer para admitir actualizaciones a la lista introduciendo una interfaz de Dominio, y haciendo que el iterador devuelva objetos "activos".


Si bien las soluciones presentadas son correctas, prefiero la siguiente porque sigue las guías del elemento efectivo de Java 57: minimizar el alcance de las variables locales:

for (Iterator<String> i = lst1.iterator(), ii = lst2.iterator(); i.hasNext() && ii.hasNext(); ) { String e1 = i.next(); String e2 = ii.next(); .... }


Tomé el comentario de @cletus y lo mejoré, y eso es lo que uso:

public static <T,U> void zip(Collection<T> ct, Collection<U> cu, BiConsumer<T, U> consumer) { Iterator<T> it = ct.iterator(); Iterator<U> iu = cu.iterator(); while (it.hasNext() && iu.hasNext()) { consumer.accept(it.next(), iu.next()); } }

Uso:

zip(list1, list2, (v1, v2) -> { // Do stuff });


for (int i = 0; i < names.length; ++i) { name = names.get(i); age = ages.get(i); // do your stuff }

Realmente no importa. Su código no obtendrá puntos por elegancia. Solo hazlo para que funcione. Y por favor no hinches.


it1 = coll1.iterator(); it2 = coll2.iterator(); while(it1.hasNext() && it2.hasNext()) { value1 = it1.next(); value2 = it2.next(); do something with it1 and it2; }

Esta versión finaliza cuando se agota la colección más corta; alternativamente, puede continuar hasta que el más largo se agote, estableciendo value1 resp. value2 a null.