Empresas
Empleos
  • Sobre nosotros
  • Soluciones
    • Publicación de vacantes
      Publica tu vacante y recibe candidatos calificados en 48h.
    • Evaluación de candidatos
      500+ pruebas técnicas y psicológicas, más anti-fraude.
    • Headhunting
      Búsqueda ejecutiva a la medida de principio a fin.
    • Nómina + EOR
      Dispersión de nómina y EOR en más de 15 países de LATAM.
  • Precios
  • Empleos

0

114
Vistas
agrupar objetos usando Stream Collector

Tengo una lista de objetos, digamos de clase Documento:

 class Document { private final String id; private final int length; public Document(String id, int length) { this.id = id; this.length = length; } public int getLength() { return length; } }

La tarea en cuestión es agruparlos en Sobres para que el número de páginas (Documento.longitud) no exceda cierto número.

 class Envelope { private final List<Document> documents = new ArrayList<>(); }

Entonces, por ejemplo, si tuviera la siguiente lista de documentos:

 Document doc0 = new Document("doc0", 2); Document doc1 = new Document("doc1", 5); Document doc2 = new Document("doc2", 5); Document doc3 = new Document("doc3", 5);

y el recuento máximo de páginas en el sobre es digamos 7 , de lo que espero 3 sobres con los siguientes documentos:

 Assert.assertEquals(3, envelopeList.size()); Assert.assertEquals(2, envelopeList.get(0).getDocuments().size()); // doc0, doc1 Assert.assertEquals(1, envelopeList.get(1).getDocuments().size()); // doc2 Assert.assertEquals(1, envelopeList.get(2).getDocuments().size()); // doc3

He implementado esto con el bucle for tradicional y un montón de if, pero la pregunta es: ¿es posible hacerlo de esta manera más elegante con flujos y recopiladores?

Gracias y un saludo

dalibor

over 4 years ago · Santiago Trujillo
1 Respuestas
Responde la pregunta

0

Para agrupar los documentos en lotes según la longitud, necesitamos mantener el estado de las longitudes acumuladas. Streams no son la mejor opción cuando es necesario mantener el estado externo y el bucle personalizado debe ser una opción más simple y eficiente.


Si forzamos el ajuste, las secuencias para este escenario, el DocumentSpliterator cambiaría de la siguiente manera:

 public static List<Couvert> splitDocuments(List<Document> docs) { IntUnaryOperator helper = new IntUnaryOperator() { private int bucketIndex = 0; private int accumulated = 0; public synchronized int applyAsInt(int length) { if (length + accumulated > MAX) { bucketIndex++; accumulated = 0; } accumulated += length; return bucketIndex; } }; return new ArrayList<>(docs.stream() .map(d -> new AbstractMap.SimpleEntry<>(helper.applyAsInt(d.getLength()), d)) .collect(Collectors.groupingBy(AbstractMap.SimpleEntry::getKey, Collector.of(Couvert::new, (c, e) -> c.getDocuments().add(e.getValue()), (c1, c2) -> {c1.getDocuments().addAll(c2.getDocuments());return c1;}))) .values()); }

Explicación:

  • helper mantiene la longitud acumulada y proporciona un nuevo índice de cubo cuando supera el máximo. He usado la interfaz IntUnaryOperator aquí. Alternativamente, podemos usar cualquier interfaz que tome un parámetro int y devuelva un int .
  • En cuanto a la corriente,
    • Document se asigna a una entrada simple de SimpleEntry y Document.
    • El flujo de SimpleEntry se agrupa primero según el bucketIndex. Otro Collector transforma el flujo de Document para un bucketIndex en particular en un Couvert . La salida de collect() es Map<Integer,Couvert>
  • Finalmente, la Collection de Couvert se convierte en una lista y se devuelve.

Nota: Para esta implementación, eliminé el parámetro front y lo incluí como parte de la lista de docs .

over 4 years ago · Santiago Trujillo Denunciar
Responde la pregunta
Encuentra empleos remotos

¡Descubre la nueva forma de encontrar empleo!

Top de empleos
Top categorías de empleo
Empresas
Publicar vacante Precios Comercial
Legal
Términos y condiciones Política de privacidad
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomiéndame algunas ofertas
Necesito ayuda