Java Stream API: Lambda, Functional Interfaces, Map, Filter, Reduce & Collect
Este artículo es una guía completa sobre la Java Stream API, incluyendo expresiones lambda, Functional Interfaces, map, filter, reduce y collect con ejemplos prácticos.
Resumen rápido
Java Stream API permite procesamiento funcional de datos mediante expresiones lambda. map transforma, filter selecciona, reduce agrega y collect recopila resultados en contenedores.
Descripción técnica concisa
Java Stream API es una API funcional para procesar colecciones de datos. Soporta programación declarativa con expresiones lambda y Functional Interfaces.
Conceptos clave:
Expresiones lambda
- Sintaxis:
(parámetro) -> expresióno(parámetro) -> { sentencias } - Inferencia de tipos: El tipo se deduce del contexto
- Efectivamente final: Las variables deben ser finales o efectivamente finales
- Referencias a métodos: Notación más compacta para expresiones lambda
Functional Interfaces
- **Predicate<T>**: boolean test(T t) - Prueba
- **Function<T,R>**: R apply(T t) - Transformación
- **Consumer<T>**: void accept(T t) - Consumición
- **Supplier<T>**: T get() - Generación
- **UnaryOperator<T>**: T apply(T t) - Operación unaria
- **BinaryOperator<T>**: T apply(T t1, T t2) - Operación binaria
Operaciones con streams
- Intermedias: map, filter, sorted, distinct, limit, skip
- Terminales: forEach, collect, reduce, count, anyMatch, allMatch
- Cortocircuito: findFirst, findAny, anyMatch, allMatch, noneMatch
Puntos clave para pruebas
- Stream API: Procesamiento funcional de datos en Java 8+
- Expresiones lambda: Funciones anónimas con sintaxis compacta
- Functional Interfaces: Interfaces con un único método abstracto
- Map: Transformación de elementos
- Filter: Selección basada en predicados
- Reduce: Agregación de elementos del stream
- Collect: Recopilación de resultados en contenedores
- Relevancia IHK: Java moderno, programación funcional
Componentes principales
- Expresiones lambda: Literales de función compactos
- Functional Interfaces: Definiciones de función tipificadas
- Creación de streams: Colecciones, arrays, E/S, generadores
- Operaciones intermedias: Lazy, encadenables
- Operaciones terminales: Eager, finalizan el procesamiento del stream
- Collectors: Operaciones de recopilación especializadas
- Parallel Streams: Procesamiento paralelo de datos
- Optional: Contenedor seguro para valores que pueden ser nulos
Ejemplos prácticos
1. Operaciones básicas con streams
import java.util.*;
import java.util.function.*;
import java.util.stream.*;
public class StreamGrundlagen {
public static void main(String[] args) {
System.out.println("=== Stream API Grundlagen ===");
// Datenquelle
List<String> namen = Arrays.asList("Alice", "Bob", "Charlie", "Diana", "Eve");
List<Integer> zahlen = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// Filter Demo
filterDemo(namen, zahlen);
// Map Demo
mapDemo(namen, zahlen);
// Reduce Demo
reduceDemo(zahlen);
// Collect Demo
collectDemo(namen, zahlen);
// Methodenreferenzen
methodenreferenzenDemo();
}
private static void filterDemo(List<String> namen, List<Integer> zahlen) {
System.out.println("\n--- Filter Demo ---");
// Lambda-Ausdruck für Filter
List<String> langeNamen = namen.stream()
.filter(name -> name.length() > 4)
.collect(Collectors.toList());
System.out.println("Namen mit > 4 Buchstaben: " + langeNamen);
// Mehrere Filter
List<Integer> gefilterteZahlen = zahlen.stream()
.filter(zahl -> zahl % 2 == 0) // Gerade Zahlen
.filter(zahl -> zahl > 3) // Größer als 3
.collect(Collectors.toList());
System.out.println("Gerade Zahlen > 3: " + gefilterteZahlen);
// Komplexes Prädikat
Predicate<String> komplexesPraedikat = name ->
name.startsWith("A") && name.length() <= 5;
List<String> gefilterteNamen = namen.stream()
.filter(komplexesPraedikat)
.collect(Collectors.toList());
System.out.println("Namen mit 'A' und ≤5 Buchstaben: " + gefilterteNamen);
}
private static void mapDemo(List<String> namen, List<Integer> zahlen) {
System.out.println("\n--- Map Demo ---");
// String zu Integer (Länge)
List<Integer> namenslaengen = namen.stream()
.map(name -> name.length())
.collect(Collectors.toList());
System.out.println("Namenslängen: " + namenslaengen);
// Integer zu String (Quadrate)
List<String> quadrate = zahlen.stream()
.map(zahl -> zahl * zahl)
.map(quad -> "Quadrat: " + quad)
.collect(Collectors.toList());
System.out.println("Quadrate: " + quadrate);
// FlatMap für verschachtelte Strukturen
List<List<Integer>> verschachtelt = Arrays.asList(
Arrays.asList(1, 2, 3),
Arrays.asList(4, 5),
Arrays.asList(6, 7, 8, 9)
);
List<Integer> flach = verschachtelt.stream()
.flatMap(list -> list.stream())
.collect(Collectors.toList());
System.out.println("Flachgemacht: " + flach);
// Map mit Objekten
List<Person> personen = Arrays.asList(
new Person("Alice", 25),
new Person("Bob", 30),
new Person("Charlie", 35)
);
List<String> personenInfo = personen.stream()
.map(person -> person.getName() + " (" + person.getAlter() + ")")
.collect(Collectors.toList());
System.out.println("Personen-Info: " + personenInfo);
}
private static void reduceDemo(List<Integer> zahlen) {
System.out.println("\n--- Reduce Demo ---");
// Summe mit Reduce
Optional<Integer> summe = zahlen.stream()
.reduce((a, b) -> a + b);
System.out.println("Summe: " + summe.orElse(0));
// Produkt mit Reduce
Optional<Integer> produkt = zahlen.stream()
.reduce((a, b) -> a * b);
System.out.println("Produkt: " + produkt.orElse(1));
// Maximum mit Reduce
Optional<Integer> maximum = zahlen.stream()
.reduce(Integer::max);
System.out.println("Maximum: " + maximum.orElse(0));
// Reduce mit Identitätswert
int summeMitIdentitaet = zahlen.stream()
.reduce(0, Integer::sum);
System.out.println("Summe mit Identität: " + summeMitIdentitaet);
// String-Verkettung
List<String> woerter = Arrays.asList("Java", "Stream", "API");
Optional<String> verkettet = woerter.stream()
.reduce((a, b) -> a + " " + b);
System.out.println("Verkettet: " + verkettet.orElse(""));
}
private static void collectDemo(List<String> namen, List<Integer> zahlen) {
System.out.println("\n--- Collect Demo ---");
// To List
List<String> grossgeschrieben = namen.stream()
.map(String::toUpperCase)
.collect(Collectors.toList());
System.out.println("Großgeschrieben: " + grossgeschrieben);
// To Set
Set<Integer> quadrate = zahlen.stream()
.map(zahl -> zahl * zahl)
.collect(Collectors.toSet());
System.out.println("Quadrate als Set: " + quadrate);
// To Map
Map<String, Integer> namenMap = namen.stream()
.collect(Collectors.toMap(
name -> name, // Key-Mapper
name -> name.length() // Value-Mapper
));
System.out.println("Namen-Map: " + namenMap);
// Grouping By
Map<Integer, List<String>> nachLaengeGruppiert = namen.stream()
.collect(Collectors.groupingBy(String::length));
System.out.println("Nach Länge gruppiert: " + nachLaengeGruppiert);
// Partitioning By
Map<Boolean, List<Integer>> geradeUngerade = zahlen.stream()
.collect(Collectors.partitioningBy(zahl -> zahl % 2 == 0));
System.out.println("Partitioniert: " + geradeUngerade);
// Joining
String namensliste = namen.stream()
.collect(Collectors.joining(", ", "[", "]"));
System.out.println("Namensliste: " + namensliste);
// Summarizing
IntSummaryStatistics statistik = zahlen.stream()
.collect(Collectors.summarizingInt(Integer::intValue));
System.out.println("Statistik: " + statistik);
}
private static void methodenreferenzenDemo() {
System.out.println("\n--- Methodenreferenzen Demo ---");
List<String> namen = Arrays.asList("alice", "bob", "charlie");
// Statische Methodenreferenz
List<String> grossgeschrieben = namen.stream()
.map(String::toUpperCase)
.collect(Collectors.toList());
System.out.println("Statische Referenz: " + grossgeschrieben);
// Instanzmethodenreferenz
List<Integer> laengen = namen.stream()
.map(String::length)
.collect(Collectors.toList());
System.out.println("Instanz-Referenz: " + laengen);
// Konstruktorreferenz
List<Person> personen = namen.stream()
.map(name -> new Person(name, 20 + name.length()))
.collect(Collectors.toList());
System.out.println("Konstruktor-Referenz: " +
personen.stream()
.map(Person::getName)
.collect(Collectors.toList()));
}
// Hilfsklasse
static class Person {
private String name;
private int alter;
public Person(String name, int alter) {
this.name = name;
this.alter = alter;
}
public String getName() { return name; }
public int getAlter() { return alter; }
}
}
2. Operaciones avanzadas con Streams
import java.util.*;
import java.util.stream.*;
import java.util.function.*;
public class FortgeschritteneStreams {
public static void main(String[] args) {
System.out.println("=== Operaciones avanzadas con Streams ===");
// Datos para las demostraciones
List<Student> studenten = Arrays.asList(
new Student("Alice", "Informatik", 85, 3),
new Student("Bob", "Mathematik", 92, 2),
new Student("Charlie", "Informatik", 78, 4),
new Student("Diana", "Physik", 88, 1),
new Student("Eve", "Informatik", 95, 2),
new Student("Frank", "Mathematik", 73, 3)
);
// Ordenamiento
sortierungDemo(studenten);
// Limit y Skip
limitSkipDemo(studenten);
// Distinct
distinctDemo();
// Operaciones Match
matchDemo(studenten);
// Operaciones Find
findDemo(studenten);
// Manejo de Optional
optionalDemo(studenten);
// Parallel Streams
parallelStreamDemo(studenten);
}
private static void sortierungDemo(List<Student> studenten) {
System.out.println("\n--- Demo de Ordenamiento ---");
// Ordenar por nota
List<Student> nachNote = studenten.stream()
.sorted(Comparator.comparing(Student::getNote))
.collect(Collectors.toList());
System.out.println("Por nota ascendente:");
nachNote.forEach(s -> System.out.println(" " + s.getName() + ": " + s.getNote()));
// Ordenar por nota descendente
List<Student> nachNoteAbsteigend = studenten.stream()
.sorted(Comparator.comparing(Student::getNote).reversed())
.collect(Collectors.toList());
System.out.println("\nPor nota descendente:");
nachNoteAbsteigend.forEach(s -> System.out.println(" " + s.getName() + ": " + s.getNote()));
// Ordenamiento por múltiples criterios
List<Student> mehrkriterium = studenten.stream()
.sorted(Comparator
.comparing(Student::getFach)
.thenComparing(Student::getNote)
.thenComparing(Student::getName))
.collect(Collectors.toList());
System.out.println("\nPor materia, nota y nombre:");
mehrkriterium.forEach(s -> System.out.println(" " + s.getFach() + " - " +
s.getName() + ": " + s.getNote()));
}
private static void limitSkipDemo(List<Student> studenten) {
System.out.println("\n--- Demo de Limit y Skip ---");
// Primeros 3 estudiantes
List<Student> ersteDrei = studenten.stream()
.limit(3)
.collect(Collectors.toList());
System.out.println("Primeros 3 estudiantes:");
ersteDrei.forEach(s -> System.out.println(" " + s.getName()));
// Omitir los primeros 2
List<Student> nachUeberspringen = studenten.stream()
.skip(2)
.collect(Collectors.toList());
System.out.println("\nDespués de omitir los primeros 2:");
nachUeberspringen.forEach(s -> System.out.println(" " + s.getName()));
// Paginación (página 2, 2 elementos por página)
int seite = 2;
int groesse = 2;
List<Student> paginiert = studenten.stream()
.skip((seite - 1) * groesse)
.limit(groesse)
.collect(Collectors.toList());
System.out.println("\nPágina " + seite + " (tamaño " + groesse + "):");
paginiert.forEach(s -> System.out.println(" " + s.getName()));
}
private static void distinctDemo() {
System.out.println("\n--- Demo de Distinct ---");
List<Integer> zahlenMitDuplikaten = Arrays.asList(1, 2, 2, 3, 4, 4, 4, 5, 1);
List<Integer> eindeutigeZahlen = zahlenMitDuplikaten.stream()
.distinct()
.collect(Collectors.toList());
System.out.println("Con duplicados: " + zahlenMitDuplikaten);
System.out.println("Únicos: " + eindeutigeZahlen);
// Distinct con objetos
List<String> faecher = Arrays.asList("Informatik", "Mathematik", "Informatik",
"Physik", "Mathematik", "Informatik");
List<String> eindeutigeFaecher = faecher.stream()
.distinct()
.collect(Collectors.toList());
System.out.println("\nMaterias con duplicados: " + faecher);
System.out.println("Materias únicas: " + eindeutigeFaecher);
}
private static void matchDemo(List<Student> studenten) {
System.out.println("\n--- Demo de Match ---");
// All Match - todos cumplen la condición
boolean alleBestanden = studenten.stream()
.allMatch(s -> s.getNote() >= 50);
System.out.println("Todos aprobaron: " + alleBestanden);
boolean alleInformatik = studenten.stream()
.allMatch(s -> s.getFach().equals("Informatik"));
System.out.println("Todos Informatik: " + alleInformatik);
// Any Match - al menos uno cumple la condición
boolean jemandInformatik = studenten.stream()
.anyMatch(s -> s.getFach().equals("Informatik"));
System.out.println("Alguien estudia Informatik: " + jemandInformatik);
boolean jemandSehrGut = studenten.stream()
.anyMatch(s -> s.getNote() >= 90);
System.out.println("Alguien muy bien: " + jemandSehrGut);
// None Match - ninguno cumple la condición
boolean niemandDurchgefallen = studenten.stream()
.noneMatch(s -> s.getNote() < 50);
System.out.println("Nadie reprobó: " + niemandDurchgefallen);
}
private static void findDemo(List<Student> studenten) {
System.out.println("\n--- Demo de Find ---");
// Find First - primer elemento
Optional<Student> erster = studenten.stream()
.findFirst();
erster.ifPresent(s -> System.out.println("Primer estudiante: " + s.getName()));
// Find Any - cualquier elemento (especialmente útil en Parallel Streams)
Optional<Student> irgendeinInformatiker = studenten.stream()
.filter(s -> s.getFach().equals("Informatik"))
.findAny();
irgendeinInformatiker.ifPresent(s ->
System.out.println("Un estudiante de Informatik: " + s.getName()));
// Find con predicado complejo
Optional<Student> besterMathematiker = studenten.stream()
.filter(s -> s.getFach().equals("Mathematik"))
.max(Comparator.comparing(Student::getNote));
besterMathematiker.ifPresent(s ->
System.out.println("Mejor estudiante de Mathematik: " + s.getName() + " (" + s.getNote() + ")"));
}
private static void optionalDemo(List<Student> studenten) {
System.out.println("\n--- Demo de Manejo de Optional ---");
// Optional con Map
Optional<String> ersterName = studenten.stream()
.findFirst()
.map(Student::getName);
ersterName.ifPresent(name -> System.out.println("Primer nombre: " + name));
// Optional con Filter
Optional<Student> besterStudent = studenten.stream()
.max(Comparator.comparing(Student::getNote));
String besterName = besterStudent
.filter(s -> s.getNote() >= 90)
.map(Student::getName)
.orElse("Ninguno con 90+ puntos");
System.out.println("Mejor estudiante (90+): " + besterName);
// Optional Chaining
Optional<String> fachDesBesten = studenten.stream()
.max(Comparator.comparing(Student::getNote))
.flatMap(s -> Optional.ofNullable(s.getFach()))
.map(String::toUpperCase);
fachDesBesten.ifPresent(fach ->
System.out.println("Materia del mejor: " + fach));
// Optional con Supplier
String defaultValue = studenten.stream()
.filter(s -> s.getName().equals("NichtExistent"))
.findFirst()
.map(Student::getName)
.orElseGet(() -> "Estudiante-Predeterminado");
System.out.println("Valor por defecto: " + defaultValue);
}
private static void parallelStreamDemo(List<Student> studenten) {
System.out.println("\n--- Demo de Parallel Stream ---");
// Procesamiento paralelo
long startZeit = System.currentTimeMillis();
List<String> namenParallel = studenten.parallelStream()
.filter(s -> s.getNote() > 80)
.map(Student::getName)
.sorted()
.collect(Collectors.toList());
long endZeit = System.currentTimeMillis();
System.out.println("Resultado paralelo: " + namenParallel);
System.out.println("Tiempo paralelo: " + (endZeit - startZeit) + "ms");
// Comparación con procesamiento secuencial
startZeit = System.currentTimeMillis();
List<String> namenSequentiell = studenten.stream()
.filter(s -> s.getNote() > 80)
.map(Student::getName)
.sorted()
.collect(Collectors.toList());
endZeit = System.currentTimeMillis();
System.out.println("\nResultado secuencial: " + namenSequentiell);
System.out.println("Tiempo secuencial: " + (endZeit - startZeit) + "ms");
// Información de threads en Parallel Stream
System.out.println("\nInformación de threads en Parallel Stream:");
studenten.parallelStream()
.forEach(s -> System.out.println(s.getName() + " en " +
Thread.currentThread().getName()));
}
// Clase Student
static class Student {
private String name;
private String fach;
private int note;
private int semester;
public Student(String name, String fach, int note, int semester) {
this.name = name;
this.fach = fach;
this.note = note;
this.semester = semester;
}
public String getName() { return name; }
public String getFach() { return fach; }
public int getNote() { return note; }
public int getSemester() { return semester; }
}
}
3. Collectors especializados y operaciones personalizadas
import java.util.*;
import java.util.stream.*;
import java.util.function.*;
public class CollectorsEspecializados {
public static void main(String[] args) {
System.out.println("=== Demo de Collectors Especializados ===");
// Datos de prueba
List<Producto> productos = Arrays.asList(
new Producto("Laptop", "Electrónica", 999.99, 5),
new Producto("Ratón", "Electrónica", 29.99, 15),
new Producto("Teclado", "Electrónica", 79.99, 8),
new Producto("Libro", "Libros", 19.99, 20),
new Producto("Bolígrafo", "Oficina", 2.99, 50),
new Producto("Papel", "Oficina", 9.99, 30)
);
// Agrupación con agregación
agrupacionConAgregacion(productos);
// Agrupación multinivel
agrupacionMultinivel(productos);
// Collector personalizado
demoCollectorPersonalizado();
// Collectors descendentes
demoCollectorsDescendentes(productos);
// Streams primitivos
demoStreamsPrivativos();
}
private static void agrupacionConAgregacion(List<Producto> productos) {
System.out.println("\n--- Agrupación con Agregación ---");
// Agrupación por categoría con estadísticas
Map<String, DoubleSummaryStatistics> estadisticasPrecios = productos.stream()
.collect(Collectors.groupingBy(
Producto::getCategoria,
Collectors.summarizingDouble(Producto::getPrecio)
));
estadisticasPrecios.forEach((categoria, estadistica) -> {
System.out.println(categoria + ":");
System.out.println(" Promedio: " + estadistica.getAverage());
System.out.println(" Mínimo: " + estadistica.getMin());
System.out.println(" Máximo: " + estadistica.getMax());
System.out.println(" Suma: " + estadistica.getSum());
});
// Agrupación con mapeo
Map<String, Set<String>> categoriasNombres = productos.stream()
.collect(Collectors.groupingBy(
Producto::getCategoria,
Collectors.mapping(Producto::getNombre, Collectors.toSet())
));
System.out.println("\nCategorías con nombres de productos:");
categoriasNombres.forEach((categoria, nombres) ->
System.out.println(categoria + ": " + nombres));
// Agrupación con filtro
Map<String, List<Producto>> productosCaros = productos.stream()
.collect(Collectors.groupingBy(
Producto::getCategoria,
Collectors.filtering(p -> p.getPrecio() > 50, Collectors.toList())
));
System.out.println("\nProductos caros (>50€):");
productosCaros.forEach((categoria, listaProductos) -> {
if (!listaProductos.isEmpty()) {
System.out.println(categoria + ": " +
listaProductos.stream().map(Producto::getNombre).collect(Collectors.toList()));
}
});
}
private static void agrupacionMultinivel(List<Producto> productos) {
System.out.println("\n--- Agrupación Multinivel ---");
// Agrupar productos por categorías de precio
Map<String, Map<String, List<Producto>>> multinivel = productos.stream()
.collect(Collectors.groupingBy(
p -> p.getPrecio() < 50 ? "Económico" : "Premium",
Collectors.groupingBy(Producto::getCategoria)
));
System.out.println("Agrupación multinivel:");
multinivel.forEach((categoriaPrecios, mapaCategoria) -> {
System.out.println(categoriaPrecios + ":");
mapaCategoria.forEach((categoria, listaProductos) -> {
System.out.println(" " + categoria + ": " +
listaProductos.stream().map(Producto::getNombre).collect(Collectors.toList()));
});
});
}
private static void demoCollectorPersonalizado() {
System.out.println("\n--- Demo de Collector Personalizado ---");
List<String> palabras = Arrays.asList("Java", "Stream", "API", "Functional", "Programming");
// Collector personalizado para concatenación de strings con separador y prefijo/sufijo
Collector<String, StringBuilder, String> collectorStringPersonalizado = Collector.of(
StringBuilder::new, // Supplier
(builder, str) -> { // Accumulator
if (builder.length() > 0) {
builder.append(" | ");
}
builder.append(str.toUpperCase());
},
StringBuilder::append, // Combiner
StringBuilder::toString, // Finisher
Characteristics.IDENTITY_FINISH
);
String resultado = palabras.stream().collect(collectorStringPersonalizado);
System.out.println("Resultado del collector personalizado: " + resultado);
// Collector personalizado para estadísticas
Collector<Integer, int[], Double> collectorPromedio = Collector.of(
() -> new int[2], // [sum, count]
(acc, num) -> {
acc[0] += num; // sum
acc[1]++; // count
},
(acc1, acc2) -> {
acc1[0] += acc2[0];
acc1[1] += acc2[1];
return acc1;
},
acc -> acc[1] == 0 ? 0 : (double) acc[0] / acc[1] // average
);
List<Integer> numeros = Arrays.asList(10, 20, 30, 40, 50);
double promedio = numeros.stream().collect(collectorPromedio);
System.out.println("Promedio personalizado: " + promedio);
}
private static void demoCollectorsDescendentes(List<Producto> productos) {
System.out.println("\n--- Demo de Collectors Descendentes ---");
// GroupingBy con Counting
Map<String, Long> cantidadPorCategoria = productos.stream()
.collect(Collectors.groupingBy(
Producto::getCategoria,
Collectors.counting()
));
System.out.println("Cantidad por categoría:");
cantidadPorCategoria.forEach((categoria, cantidad) ->
System.out.println(categoria + ": " + cantidad));
// GroupingBy con Summing
Map<String, Integer> inventarioPorCategoria = productos.stream()
.collect(Collectors.groupingBy(
Producto::getCategoria,
Collectors.summingInt(Producto::getInventario)
));
System.out.println("\nInventario por categoría:");
inventarioPorCategoria.forEach((categoria, cantidad) ->
System.out.println(categoria + ": " + cantidad));
// GroupingBy con maxBy
Map<String, Optional<Producto>> productoMasCaroPorCategoria = productos.stream()
.collect(Collectors.groupingBy(
Producto::getCategoria,
Collectors.maxBy(Comparator.comparing(Producto::getPrecio))
));
System.out.println("\nProducto más caro por categoría:");
productoMasCaroPorCategoria.forEach((categoria, opcional) ->
opcional.ifPresent(p -> System.out.println(categoria + ": " + p.getNombre())));
// CollectingAndThen para resultados postprocesados
Map<String, String> infoCategoria = productos.stream()
.collect(Collectors.groupingBy(
Producto::getCategoria,
Collectors.collectingAndThen(
Collectors.toList(),
lista -> lista.size() + " productos, " +
String.format("%.2f€",
lista.stream().mapToDouble(Producto::getPrecio).average().orElse(0))
)
));
System.out.println("\nInfo de categoría:");
infoCategoria.forEach((categoria, info) -> System.out.println(categoria + ": " + info));
}
private static void demoStreamsPrivativos() {
System.out.println("\n--- Demo de Streams Primitivos ---");
// IntStream
IntStream numeros = IntStream.range(1, 10);
int suma = numeros.sum();
System.out.println("Suma 1-9: " + suma);
// Con boxed para convertir a stream de objetos
List<Integer> listaNumeros = IntStream.rangeClosed(1, 5)
.boxed()
.collect(Collectors.toList());
System.out.println("Números como List: " + listaNumeros);
// DoubleStream con cálculos
double[] precios = {19.99, 29.99, 99.99, 149.99};
DoubleSummaryStatistics estadisticasPrecios = Arrays.stream(precios)
.summaryStatistics();
System.out.println("\nEstadísticas de precios:");
System.out.println(" Cantidad: " + estadisticasPrecios.getCount());
System.out.println(" Suma: " + estadisticasPrecios.getSum());
System.out.println(" Promedio: " + estadisticasPrecios.getAverage());
System.out.println(" Mín: " + estadisticasPrecios.getMin());
System.out.println(" Máx: " + estadisticasPrecios.getMax());
// LongStream para números grandes
long factorial = LongStream.rangeClosed(1, 10)
.reduce(1, (a, b) -> a * b);
System.out.println("\n10! = " + factorial);
// Stream primitivo con filtro
long numerosParas = IntStream.rangeClosed(1, 20)
.filter(n -> n % 2 == 0)
.count();
System.out.println("Números pares 1-20: " + numerosParas);
// MapToObj para transformación
List<String> numerosComoStrings = IntStream.rangeClosed(1, 5)
.mapToObj(n -> "Número " + n)
.collect(Collectors.toList());
System.out.println("Números como strings: " + numerosComoStrings);
}
// Clase Producto
static class Producto {
private String nombre;
private String categoria;
private double precio;
private int inventario;
public Producto(String nombre, String categoria, double precio, int inventario) {
this.nombre = nombre;
this.categoria = categoria;
this.precio = precio;
this.inventario = inventario;
}
public String getNombre() { return nombre; }
public String getCategoria() { return categoria; }
public double getPrecio() { return precio; }
public int getInventario() { return inventario; }
}
}
Vista General de Interfaces Funcionales
| Interface | Método | Descripción | Ejemplo |
|---|---|---|---|
Predicate<T> | boolean test(T t) | Prueba una condición | s -> s.length() > 5 |
Function<T,R> | R apply(T t) | Transforma valores | s -> s.toUpperCase() |
Consumer<T> | void accept(T t) | Consume valores | System.out::println |
Supplier<T> | T get() | Proporciona valores | () -> new Random() |
UnaryOperator<T> | T apply(T t) | Operación unaria | x -> x * x |
BinaryOperator<T> | T apply(T t1, T t2) | Operación binaria | (a, b) -> a + b |
Vista General de Operaciones en Streams
Operaciones Intermedias (Lazy)
// Filter
stream.filter(x -> x > 0)
// Map
stream.map(x -> x * 2)
// FlatMap
stream.flatMap(list -> list.stream())
// Sorted
stream.sorted()
stream.sorted(Comparator.reverseOrder())
// Distinct
stream.distinct()
// Limit/Skip
stream.limit(10)
stream.skip(5)
// Peek (para depuración)
stream.peek(System.out::println)
Operaciones Terminales (Eager)
// ForEach
stream.forEach(System.out::println)
// Collect
stream.collect(Collectors.toList())
// Reduce
stream.reduce((a, b) -> a + b)
// Count
stream.count()
// Min/Max
stream.min(Comparator.naturalOrder())
stream.max(Comparator.reverseOrder())
// Match
stream.anyMatch(x -> x > 0)
stream.allMatch(x -> x > 0)
stream.noneMatch(x -> x > 0)
// Find
stream.findFirst()
stream.findAny()
Tipos de Referencias a Métodos
Referencia a Método Estático
// Lambda: s -> Integer.parseInt(s)
// Referencia: Integer::parseInt
list.stream().map(Integer::parseInt)
Referencia a Método de Instancia
// Lambda: s -> s.toUpperCase()
// Referencia: String::toUpperCase
list.stream().map(String::toUpperCase)
Referencia a Constructor
// Lambda: name -> new Person(name)
// Referencia: Person::new
list.stream().map(Person::new)
Consideraciones de Rendimiento
Cuándo Usar Streams
- Procesamiento complejo de datos: Operaciones de filtrado, transformación y reducción
- Legibilidad: Código declarativo en lugar de bucles imperativos
- Paralelización: Cambio sencillo a procesamiento paralelo
- Programación funcional: Estructuras de datos inmutables
Cuándo Evitar Streams
- Operaciones simples: Los bucles tradicionales suelen ser más rápidos
- Código crítico en rendimiento: El overhead de los streams puede ser relevante
- Arrays primitivos: Las operaciones especializadas funcionan mejor
- Colecciones muy pequeñas: El overhead supera los beneficios
Ventajas e Inconvenientes
Ventajas de Stream API
- Legibilidad: Sintaxis declarativa y expresiva
- Composición: Operaciones encadenables con facilidad
- Paralelización: Cambio sencillo a procesamiento paralelo
- Funcional: Soporte para programación funcional
- Lazy Evaluation: Procesamiento eficiente
Inconvenientes
- Rendimiento: Overhead en operaciones simples
- Depuración: Más difícil que bucles imperativos
- Curva de aprendizaje: Conceptos y sintaxis nuevos
- Memoria: Las colecciones intermedias pueden consumir memoria
Preguntas Frecuentes en Exámenes
-
¿Cuál es la diferencia entre operaciones intermedias y terminales? Las intermedias son lazy y devuelven un stream, mientras que las terminales son eager y finalizan el procesamiento.
-
¡Explica las expresiones lambda! Son funciones anónimas con sintaxis compacta:
(parámetro) -> expresióno(parámetro) -> { sentencias }. -
¿Cuándo usas referencias a métodos? Como alternativa más corta a expresiones lambda cuando ya existe un método que se ajusta perfectamente.
-
¿Cuál es la ventaja de los streams paralelos? Procesamiento automático en paralelo en sistemas multi-núcleo para mejor rendimiento.
Fuentes Principales
- https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html
- https://docs.oracle.com/javase/tutorial/collections/streams/
- https://www.baeldung.com/java-8-streams



