FUNDAMENTOS DE PROGRAMACIÓN Versión: 0.3.0 Tema 10. Órdenes, predicados y funciones Autores: Juan A. Nepomuceno Revisiones: Antonia M. Reina Tiempo estimado: 4 horas 1. Introducción ................................................................................................................................................ 1 2. El tipo Ordering ........................................................................................................................................... 2 3. El tipo Predicate .......................................................................................................................................... 5 4. La clase de utilidad Predicates ................................................................................................................... 6 5. El tipo Function ........................................................................................................................................... 8 6. La clase de utilidad Functions ..................................................................................................................... 9 7. Acciones .................................................................................................................................................... 10 8. Ejercicios propuestos ................................................................................................................................ 10 Anexo ................................................................................................................................................................ 13 1. Introducción Nos marcamos como objetivo resolver cada tratamiento secuencial visto en el primer cuatrimestre mediante la llamada a un método implementado previamente que lo resuelva. De esta forma, utilizando código ya verificado, reducimos las posibilidades de cometer errores de programación. Uno de los mecanismos más importantes de la Programación Orientada a Objetos para la reutilización de código es el mecanismo de abstracción. Dicho mecanismo consiste en programar teniendo en cuenta “qué se hace” en lugar del “cómo se hace” y, de esta forma, se puede programar de forma generalizada. Veámoslo con un ejemplo: un método que calcule, dados dos objetos, el mayor de ellos según un orden determinado. Nos centramos en “qué significa orden” y no en “cómo se ordena”. De esta forma, el método public static <T> T mayor(T o1, T o2, Comparator<T> cmp) { T res; if (cmp.compare(o1, o2) > 0) { res = o1; } else { res = o2; } return res; } sirve para cualquier criterio de ordenación y para objetos de cualquier tipo. Cuando se quiera utilizar este método, bastará pasarle como parámetro de entrada no sólo los dos objetos a ordenar, sino también un objeto comparador que abstrae la idea del criterio de ordenación que se vaya a utilizar. En el tema 3 vimos 2 Fundamentos de Programación cómo implementar comparadores, que son clases que implementan la interfaz Comparator<T> presente en la API de Java en el paquete java.util1. De esta forma, cuando se quiera abstraer la información de un criterio de ordenación, lo que se hará será implementar un comparador que indique cómo se ordenan los objetos dos a dos. Así se pueden reutilizar métodos genéricos que calculan, por ejemplo, máximos o mínimos. De una manera análoga a los comparadores, en este tema nos abstraeremos de expresiones lógicas concretas usando la interfaz Predicate<T> de la librería Guava y de expresiones sobre un tipo F que devuelven un resultado de tipo T mediante la interfaz Function<F,T>, también de la librería Guava. El abstraernos de expresiones concretas nos permitirá implementar métodos genéricos que requieran una condición de filtrado o expresiones generales que transformen objetos de un tipo en otro. En este tema veremos, en primer lugar, la clase Ordering, una clase de la librería Guava que, a la vez, hace de factoría y proporciona métodos para calcular máximos, mínimos y ordenar, entre otras utilidades. En segundo lugar, introduciremos el tipo Predicate como una forma de abstracción de las expresiones lógicas, y la clase de utilidad Predicates, que contiene métodos para combinar predicados. En tercer lugar, se verá el tipo Function, como una forma de abstraerse de una expresión sobre un tipo, y la clase de utilidad Functions, que contiene métodos para combinar expresiones. Finalmente, se introducirá el tipo Accion, que es propio de la asignatura y no está soportado por Guava, como una forma de abstraerse de una expresión que modifica alguna propiedad de un objeto de un tipo concreto. 2. El tipo Ordering La clase Ordering2 pertenece al paquete com.google.common.collect y en ella se puede encontrar toda la funcionalidad relacionada con órdenes: máximos, mínimos, ordenaciones, etc. Los métodos que proporciona se pueden clasificar en tres grandes grupos: (1) métodos para crear objetos de tipo Ordering (o métodos de factoría); (2) métodos para combinar criterios de ordenación (o métodos de manipulación de objetos de tipo Ordering); y (3) métodos para aplicar criterios de ordenación. Veamos algunos de los métodos más importantes de estas tres categorías: 1. Métodos de factoría. Los métodos de este grupo sirven para crear un objeto de tipo Ordering que será utilizado posteriormente como objeto base o pivote que invoque otras funcionalidades. Note que todos los métodos de esta categoría son estáticos. Los principales son: static <T> Ordering<T> from(Comparator<T> comparator) Crea un objeto de tipo Ordering usando el orden inducido por el comparador que se pasa como parámetro. static <C extends Comparable> Ordering<C> 1 2 natural() Crea un objeto de tipo Ordering usando como criterio de ordenación el orden natural. Ver: http://docs.oracle.com/javase/7/docs/api/ http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Ordering.html 10. Órdenes, predicados y funciones static Ordering<Object> usingToString() Crea un objeto de tipo Ordering usando como criterio de ordenación el orden alfabético de la representación como cadena de los objetos. Note que, a diferencia de los métodos natural y from, el método usingToString devuelve un objeto de tipo Ordering<Object>. 2. Métodos de manipulación de objetos de tipo Ordering. Los métodos de este grupo se utilizan para obtener nuevos objetos de tipo Ordering derivados de los objetos de tipo Ordering sobre los que se invocan. Pueden verse como una forma de componer objetos de tipo Ordering. Note que los métodos de este grupo no son estáticos, por lo que para utilizarlos es necesario haber creado previamente un objeto de tipo Ordering con los métodos factoría. Algunos de los métodos de este grupo son: <S extends T> Ordering<S> reverse() <S extends T> Ordering<S> nullsLast() <S extends T> Ordering<S> nullsFirst() <U extends T> Ordering<U> compound(Comparator<? super U> secondaryComparator) <F> Ordering<F> onResultOf(Function<F,? extends T> function) Devuelve un objeto de tipo Ordering que especifica el criterio de ordenación inverso al del objeto de tipo Ordering con el que se invoca al método. Devuelve un objeto de tipo Ordering que trata a los nulos como elementos mayores que el resto. Los elementos no nulos se ordenan según el criterio definido por el objeto de tipo Ordering con el que se invoca al método. Devuelve un objeto de tipo Ordering que trata a los nulos como elementos menores que el resto. Los elementos no nulos se ordenan según el criterio definido por el objeto de tipo Ordering con el que se invoca al método. Devuelve un objeto de tipo Ordering que usa el criterio de ordenación definido por el objeto de tipo Ordering con el que se invoca, pero que en caso de empate usa el comparador secundario para desempatar. Devuelve un objeto de tipo Ordering sobre F que ordena los elementos primero aplicándoles la función3, y luego según el criterio definido por el objeto de tipo Ordering que lo invoca. 3. Métodos de aplicación de criterios de ordenación. Los métodos de este grupo se utilizan para aplicar criterios de ordenación a valores o colecciones de elementos. Los métodos de este grupo tampoco son estáticos, por lo que para utilizarlos es necesario 3 El concepto de función se verá más adelante en el tema. 3 4 Fundamentos de Programación haber creado previamente un objeto de tipo Ordering. Algunos de los métodos más populares de este grupo son: <E extends T> E max(E a, E b) Devuelve el mayor de los dos valores según el criterio de ordenación inducido por el objeto de tipo Ordering con el que se invoca. <E extends T> E max(E a, E b, E c, E... rest) Devuelve el mayor de los valores especificados según el criterio de ordenación inducido por el objeto de tipo Ordering con el que se invoca. <E extends T> E max(Iterable<E> iterable) Devuelve el mayor de los elementos del iterable según el criterio de ordenación inducido por el objeto de tipo Ordering con el que se invoca. <E extends T> E min(E a, E b) Devuelve el menor de los dos valores según el criterio de ordenación inducido por el objeto de tipo Ordering con el que se invoca. <E extends T> E min(E a, E b, E c, E... rest) Devuelve el menor de los valores especificados según el criterio de ordenación inducido por el objeto de tipo Ordering con el que se invoca. <E extends T> E min(Iterable<E> iterable) Devuelve el mayor de los elementos del iterable según el criterio de ordenación inducido por el objeto de tipo Ordering con el que se invoca. <E extends T> List<E> sortedCopy(Iterable<E> iterable) int binarySearch(List<? extends T> sortedList, T key) Devuelve una lista que es una copia del objeto iterable que se pasa como parámetro, ordenada según el criterio inducido por el objeto de tipo Ordering con el que se invoca. Busca en la lista sortedList el elemento key utilizando el algoritmo de búsqueda binaria. La lista debe estar ordenada según el criterio inducido por el objeto de tipo Ordering con el que se invoca. Devuelve el índice del elemento buscado en la lista, o un número negativo si el elemento no se encuentra. Para utilizar los métodos de Ordering en el cálculo de un máximo o un mínimo procederemos de la siguiente forma: 1. Se crea un objeto del tipo Ordering utilizando alguno de los métodos de la factoría (natural, si se quiere ordenar según el criterio de ordenación natural; from, junto con un comparador, si se quiere ordenar por un criterio de ordenación alternativo; usingToString, si se quiere ordenar según la representación como cadena). 2. Con el objeto del tipo Ordering creado en el apartado anterior, se invoca a max, si se quiere calcular un máximo, o a min, si se quiere calcular un mínimo. 10. Órdenes, predicados y funciones Tenga en cuenta que los dos pasos anteriores se pueden implementar en una sola línea de código. Algunos ejemplos de uso de Ordering son: Ejemplo 1 Dada una lista de libros se desea saber cuál es el mayor libro según el orden natural del tipo Libro. public static Libro libroMayor(List<Libro> lstLibro) { return Ordering.natural().max(lstLibro); } Listado 1. Cálculo del máximo según el criterio de ordenación natural usando Ordering Ejemplo 2 Dada una lista de libros se desea saber cuál es el más caro. public static Libro libroMasCaro(List<Libro> lstLibro) { return Ordering.from(new ComparadorLibroPorPrecio()).max(lstLibro); } Listado 2. Cálculo del máximo según el criterio definido por un comparador usando Ordering Para lo cual es necesario tener previamente implementado el comparador ComparadorLibroPorPrecio. public class ComparadorLibroPorPrecio implements Comparator<Libro> { public int compare(Libro l1, Libro l2){ int res = l1.getPrecio().compareTo(l2.getPrecio()); if (res == 0) { res = l1.compareTo(l2); } return res; } } Listado 3. Clase ComparadorLibroPorPrecio 3. El tipo Predicate La interfaz Predicate<T>4 se encuentra en el paquete com.google.common.base y nos abstrae del concepto de expresión lógica. Predicate tiene dos métodos: apply y equals. Su descripción es la siguiente: boolean apply(T input) Devuelve un valor de tipo lógico resultado de aplicar una expresión lógica sobre input. boolean equals(Object object) Devuelve true si el objeto object es un predicado igual al predicado con el que se invoca al método. 4 http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/base/Predicate.html 5 6 Fundamentos de Programación Es aconsejable que la expresión lógica del método apply no tenga efectos laterales y sea consistente con equals. El método equals generalmente no lo implementaremos, sino que utilizaremos el que se hereda de Object. Un ejemplo de uso del tipo Predicate es el siguiente: Ejemplo 3 Escriba un predicado que determine si un libro es un best seller. public class PredicadoLibroEsBestSeller implements Predicate<Libro> { public boolean apply(Libro l) { return l.getEsBestSeller(); } } Listado 4. Clase PredicadoLibroEsBestSeller. Los objetos de tipo predicado serán pasados como parámetros de entrada a los métodos genéricos que los requieran. Así se consigue la abstracción de condiciones de filtrado en una búsqueda, la abstracción de propiedades en tratamientos secuenciales de existencia, etc. En general serán deseables predicados que abstraigan pequeñas funcionalidades de modo que se puedan combinar entre sí y obtener de esta forma piezas de código de fácil combinación y reutilización. 4. La clase de utilidad Predicates La clase de utilidad Predicates5 está en el paquete com.google.common.base, y proporciona dos tipos de métodos: (1) métodos de factoría; y (2) métodos para componer predicados. 1. Métodos de factoría. Los métodos de este grupo sirven para crear predicados de uso común. Algunos métodos de este grupo son: static <T> Predicate<T> alwaysFalse() Crea un predicado que siempre se evalúa a falso. static <T> Predicate<T> alwaysTrue() Crea un predicado que siempre se evalúa a cierto. static <T> Predicate<T> equalTo(T target) Crea un predicado que se evalúa a cierto si el objeto pasado como parámetro, target, y el objeto referencia son iguales, según el equals, o bien si los dos son nulos. static <T> Predicate<T> isNull() Crea un predicado que se evalúa a cierto si el objeto referencia que está siendo comprobado es nulo. static <T> Predicate<T> 5 notNull() http://docs.guava-librariees.googlecode.com/git-history/release/javadoc/com/google/common/base/Predicates.html 10. Órdenes, predicados y funciones Crea un predicado que se evalúa a cierto si el objeto referencia que está siendo comprobado no es nulo. static <T> Predicate<T> in(Collection<? extends T> target) Crea un predicado que se evalúa a cierto si el objeto referencia es miembro de la colección target. 2. Métodos de composición de predicados. Los métodos de este grupo devuelven un predicado basado en otro o en otros básicos. Algunos métodos de este grupo son: static <T> Predicate<T> and(Predicate<? super T> first, Predicate<? super T> second) Devuelve un predicado que se evalúa a cierto si ambos componentes (first y second) se evalúan a cierto. static <T> Predicate<T> and(Predicate<? super T>... components) Devuelve un predicado que se evalúa a cierto si todos los componentes se evalúan a cierto. static <T> Predicate<T> and(Iterable<? extends Predicate<? super T>> components) Devuelve un predicado que se evalúa a cierto si todos los predicados del iterable se evalúan a cierto. static <T> Predicate<T> or(Predicate<? super T> first, Predicate<? super T> second) Devuelve un predicado que se evalúa a cierto si alguno de los dos componentes (first o second) se evalúa a cierto. static <T> Predicate<T> or(Predicate<? super T>... components) Devuelve un predicado que se evalúa a cierto si alguno de los componentes se evalúa a cierto. static <T> Predicate<T> or(Iterable<? extends Predicate<? super T>> components) Devuelve un predicado que se evalúa a cierto si alguno de los predicados del iterable se evalúa a cierto. static <T> Predicate<T> not(Predicate<T> predicate) Devuelve un predicado que se evalúa a cierto si el predicado predicate se evalúa a falso. static <A,B> Predicate<A> compose(Predicate<B> predicate, Function<A,? extends B> function) Devuelve un predicado resultado de la composición de una función6 y un predicado. Un ejemplo de uso de los métodos de Predicates es el siguiente: 6 En el siguiente apartado se verá el concepto de función. 7 8 Fundamentos de Programación Ejemplo 4 Se pretende obtener un predicado que indique si un libro tiene un autor en concreto, un número de páginas determinado y un precio concreto. Predicate<Libro> Predicate<Libro> Predicate<Libro> Predicate<Libro> pr1 = new PredicadoLibroNombreAutorIgualA("Amos"); pr2 = new PredicadoLibroNumPaginasIgualA(100); pr3 = new PredicadoLibroPrecioIgualA(25.0); resultado = Predicates.and(pr1,pr2,pr3); Listado 5. Combinación de predicados mediante Predicates.and En el Listado 5 se supone que previamente se han implementado las clases PredicadoLibroPrecioIgualA, PredicadoLIbroNumPaginasIgualA y PredicadoLibroNombreAutorIgualA. 5. El tipo Function De manera análoga a como trabajamos con Predicate<T>, trabajaremos con la interfaz Function<F,T>7, que abstrae una función que se aplica a un objeto de tipo F y cuyo resultado es un objeto de tipo T. Las funciones se pueden ver como una generalización del concepto de predicado, ya que un predicado Predicate<T> puede considerarse una función de tipo Function<T, Boolean>. Al igual que Predicate, tiene dos métodos: apply y equals. Su descripción es la siguiente: T apply(F input) Devuelve un objeto de tipo T resultado de aplicar la función al objeto input. boolean equals(Object object) Devuelve true si el objeto object es una función igual a la función con la que se invoca al método. Al igual que ocurría con Predicate, es aconsejable que la expresión del método apply no tenga efectos laterales y sea consistente con equals. El método equals generalmente no lo implementaremos, sino que utilizaremos el que se hereda de Object. Un ejemplo de uso del tipo Function es el siguiente: Ejemplo 5 Escriba una función que dado un libro devuelva su precio. public class FuncionPrecioLibro implements Function<Libro, Double> { public Double apply(Libro l) { return l.getPrecio(); } } Listado 6. Clase FuncionPrecioLibro 7 http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/base/Function.html 10. Órdenes, predicados y funciones 6. La clase de utilidad Functions La clase de utilidad Functions8 está en el paquete com.google.common.base y, de forma similar a la clase Predicates, proporciona dos tipos de métodos: (1) métodos de factoría; y (2) métodos para componer funciones. 1. Métodos de factoría. Los métodos de este grupo sirven para crear funciones de uso común. Algunos métodos de este grupo son: static <E> Function<Object,E> constant(E value) static <E> Function<E,E> identity() static Function<Object,String> toStringFunction() static <T> Function<T,Boolean> forPredicate(Predicate<T> predicate) static <K,V> Function<K,V> forMap(Map<K,V> map) Crea una función que devuelve value para cualquier entrada. Devuelve la función identidad. Devuelve una función que a cada objeto le hace corresponder su representación como cadena. Crea una función que devuelve el mismo resultado booleano que el predicado predicate. Devuelve una función que hace una búsqueda en el map. Si el elemento buscado no está en el map, se lanza la excepción IllegalArgumentException. 2. Métodos de composición de funciones. Los métodos de este grupo devuelven una función basada en otra u otras básicas. El único método de este grupo es: static <A,B,C> Function<A,C> compose(Function<B,C> g, Function<A,? extends B> f) Devuelve una función resultado de componer las funciones f y g. Un ejemplo de uso de los métodos de Functions es el siguiente: Ejemplo 6 Suponga definida la aplicación ap y la clase llamada FuncionContarElementosLista. La aplicación ap relaciona un autor con el conjunto de libros que ha escrito. La clase FuncionContarElementosLista, cuyo código se muestra en el Listado 7, define una función que asocia a una lista el número de elementos que tiene. Cree una función que asocie un autor con el número de libros que ha escrito. 8 http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/base/Functions.html 9 10 Fundamentos de Programación public class FuncionContarElementosConjunto<T> implements Function<Set<T>, Integer> { public Integer apply(Set<T> conjunto) { return conjunto.size(); } } Listado 7. Clase FuncionContarElementosLista Map<Persona, Set<Libro>> ap = ... Function<Persona, Set<Libro>> f = Functions.forMap (ap); Function<Set<Libro>,Integer> g = new FuncionContarElementosConjunto<Libro>(); Function<Persona, Integer> resultado = Functions.compose(g,f); Listado 8. Composición de funciones mediante Functions.compose 7. Acciones En determinados casos se necesitarán funciones que en lugar de obtener un objeto a partir de otro lo que hagan es una modificación de dicho objeto. Dichas funciones se llaman acciones9. Una acción no es más que una función que no devuelve nada y que tiene efectos laterales. Para definir las acciones nos basamos en el tipo Function y decimos que una acción es de tipo Function<F,Void>. El tipo Void es el tipo envoltura de void. Cuando trabajamos con tipos genéricos no se pueden usar tipos básicos, por eso es necesario usar Void. Un ejemplo de acción es el siguiente: Ejemplo 7 Escriba una acción que ponga el precio de un libro a cero. public class AccionLibroGratis implements Function<Libro, Void> { public Void apply(Libro l) { l.setPrecio(0.0); return null; } } Listado 9. Clase AccionLibroGratis Observe que es necesario poner return null porque el compilador obliga a poner un return cuando se trabaja con objetos y, en este caso, el método apply trabaja con Void. 8. Ejercicios propuestos Ejercicio 1 Dados los tipos Libro y Persona, vistos en temas anteriores y cuyas interfaces se muestran a continuación, se pide: 9 El concepto de acción es propio de la asignatura y no está definido en la librería Guava. 10. Órdenes, predicados y funciones public interface Libro extends Comparable<Libro> { String getISBN(); String getTitulo(); void setTitulo(String titulo); Persona getAutor(); void setAutor(Persona p); Integer getNumPaginas(); void setNumPaginas(Integer n); Double getPrecio(); void setPrecio(Double precio); Boolean getEsBestSeller(); void setEsBestSeller(Boolean b); } public interface Persona extends Comparable<Persona> { String getDNI(); String getNombre(); void setNombre(String nombre); String getApellidos(); void setApellidos(String apellidos); } a) En la clase de utilidad Libros, añada cuatros métodos tales que, dada una lista de libros, respondan a las siguientes preguntas: ¿cuál es el menor según el orden natural? ¿cuál es el que tiene más páginas? ¿cuál es el más barato? Ordene dicha lista según el apellido del autor que ha escrito el libro b) En la clase de utilidad Personas, cree un método que dado un conjunto de personas y un Map<Persona,Set<Libro>> que asocia un autor con el conjunto de libros que ha escrito, indique cuál es el autor que ha escrito más libros. Ejercicio 2 Escriba los siguientes métodos en una clase de utilidad llamada EjerciciosTema10: a) Un método que a partir del nombre de un fichero de texto y una cadena linea que representa una línea del fichero, devuelva el índice que ocuparía esa línea si el fichero estuviera ordenado alfabéticamente por líneas. Si linea no fuera una línea del fichero, el método devolverá un número negativo. Para resolverlo: (1) obtenga una lista de las líneas del fichero de texto, (2) ordénela alfabéticamente, y (3) haga una búsqueda binaria en el fichero. b) Un método que a partir de una lista de libros devuelva el libro más caro. c) Repita el ejercicio anterior, pero teniendo en cuenta que puede haber objetos null en la lista de libros. d) Obtenga el libro menor si la lista se ordena según su representación como cadena. Ejercicio 3 Construya los siguientes predicados sobre objetos de tipo Libro: a) Un predicado que indique si un libro tiene un precio inferior a uno dado. b) Un predicado que indique si un libro tiene un número de páginas superior o igual a uno dado como parámetro. c) Un predicado que indique si un libro está escrito por un autor de nombre dado. d) Suponiendo definido un Map<Persona,Set<Libro>> que asocia un autor con el conjunto de libros que ha escrito, se pide definir un predicado que indique si un autor ha escrito más de un número de libros dado. 11 12 Fundamentos de Programación e) Un predicado que indique si un libro pertenece a un conjunto de libros dado. f) Un predicado que indique si un libro tiene un número de páginas mayor o igual que 100 y menor de 500. g) Un predicado que indique si un libro tiene menos de 100 páginas o cuesta menos de 9.90 euros. Ejercicio 4 Construya las siguientes funciones sobre objetos de tipo Libro: a) Una función que dado un libro devuelva el nombre del autor que lo ha escrito. b) Una función que dado un libro devuelva el número de páginas que tiene. c) Suponiendo definido un Map<Persona,Set<Libro>> que asocia un autor con el conjunto de libros que ha escrito, escriba una función que dado un autor devuelva el conjunto de libros que ha escrito. d) Escriba una función que dado un conjunto de libros devuelva cuál es el más caro. e) Suponiendo definido un Map<Persona,Set<Libro>> que asocia un autor con el conjunto de libros que ha escrito, escriba una función que dado un autor devuelva cuál es el libro más caro de los que ha escrito. Ejercicio 5 Sin hacer uso de los métodos de las clases Predicates y/o Functions, escriba el código de las siguientes clases: a) Una clase de tipo Predicate<T> llamada PredicadoY, que dados dos predicados, pred1 y pred2, de tipo Predicate<T>, realice la conjunción de los mismos. b) Una clase de tipo Function<A,C> llamada FuncionCompuesta, que dados dos funciones, func1 (de tipo Function<A,B>) y func2, de tipo Function<B,C>, realice la composición de las dos funciones. 10. Órdenes, predicados y funciones Anexo public class Ordering { // Métodos de creación de objetos Ordering public static <T> Ordering<T> from(Comparator<T> comparator) {…} public static <C extends Comparable> Ordering<C> natural (){…} public static Ordering<Object> usingToString (){…} //Métodos de manipulación de objetos Ordering public <S extends T> Ordering<S> reverse(){…} public <S extends T> Ordering<S> nullsFirst(){…} public <S extends T> Ordering<S> nullsLast(){…} public <U extends T> Ordering<U> compound(Comparator<? super U> secondaryComparator) {…} public <F> Ordering<F> onResultOf(Function<F,? extends T> function) {…} // Métodos de aplicación de criterios de ordenación mediante Ordering public int binarySearch(List<? extends T> sortedList, T key) {…} public <E extends T> List<E> sortedCopy(Iterable<E> iterable) {…} public <E extends T> E max(Iterable<E> iterable) {…} public <E extends T> E max(E a, E b, E c, E... rest) {…} public <E extends T> E max(E a, E b) {…} public <E extends T> E min(Iterable<E> iterable) {…} public <E extends T> E min(E a, E b, E c, E... rest) {…} public <E extends T> E min(E a, E b) {…} … } public interface Predicate<T> { public interface Function<F, T> { boolean apply (T o); T apply (F o); boolean equals (Object o); boolean equals (Object o); } } public class Predicates { public static <T> Predicate<T> alwaysTrue(){…} public static <T> Predicate<T> alwaysFalse(){…} public static <T> Predicate<T> equalTo(T target){…} public static <T> Predicate<T> isNull(){…} public static <T> Predicate<T> notNull(){…} public static <T> Predicate<T> in(Collection<? extends T> target) {…} public static <T> Predicate<T> not(Predicate<T> predicate) {…} public static <T> Predicate<T> and(Predicate<? super T>... components) {…} public static <T> Predicate<T> and(Iterable<? extends Predicate<? super T>> components) {…} public static <T> Predicate<T> or(Predicate<? super T>... components) {…} public static <T> Predicate<T> or(Iterable<? extends Predicate<? super T>> components) {…} public static <A,B> Predicate<A> compose(Predicate<B> predicate, Function<A,? extends B> function) {…} …} public class Functions { public static <E> Function<Object,E> constant(E value) {…} public static Function<Object,String> toStringFunction(){…} public static <E> Function<E,E> identity(){…} public static <T> Function<T,Boolean> forPredicate(Predicate<T> predicate) {…} public static <K,V> Function<K,V> forMap(Map<K,V> map) {…} public static <A,B,C> Function<A,C> compose(Function<B,C> g, Function<A,? extends B> f) {…} } 13