Colección tipo parcial con límite y costumbre Comparador

frizzle:

Quiero ordenar un ArrayList llamada imageList como esto:

Collections.sort(imageList, new MapComparator(Function.KEY_TIMESTAMP, "dsc"));

Esto funciona bien, pero ahora yo quiero ser capaz de establecer un límite (sólo mostrará las últimas 100 imágenes, en el que el ArrayList es sin clasificar, por lo que la simple creación de una lista secundaria no funcionará) por razones de rendimiento.

Mi clase MapComparator se ve así:

class MapComparator implements Comparator<HashMap<String, String>>
{
    private final String key;
    private final String order;

    public MapComparator(String key, String order)
    {
        this.key = key;
        this.order = order;
    }

    public int compare(HashMap<String, String> first,
                       HashMap<String, String> second)
    {
        String firstValue = first.get(key);
        String secondValue = second.get(key);
        if(this.order.toLowerCase().contentEquals("asc"))
        {
            return firstValue.compareTo(secondValue);
        }else{
            return secondValue.compareTo(firstValue);
        }

    }
}

¿Alguien sabe cómo poner en práctica eso? ¡Gracias por adelantado!

Stuart Marcas:

No estoy al tanto de un nombre oficial para este tipo de problema, pero ocurre con una frecuencia razonable, y que a menudo llama algo así como un top- k o greatest- k problema.

Por supuesto que tiene que procesar todos los elementos en la entrada, debido a que el último elemento puede pertenecer en el "top  k conjunto" y no se sabe hasta que haya procesado hasta el último elemento. Sin embargo, usted no tiene que ordenar la entrada completa. Hacer algo como la clasificación y luego tomando una sublista, o con una corriente, llamando sorted()seguido de limit(), potencialmente puede ser muy costoso, ya que con los elementos de entrada N, clasificación es O (N log N). Sin embargo, es posible reducir la complejidad del tiempo de O (N) simplemente hacer el seguimiento de los mayores k elementos vistos hasta el momento ya que se corre a través de la lista.

La guayaba tiene un colector que hace exactamente esto: Comparators.greatest (k, comparador) .

Si no desea utilizar la guayaba, que no es demasiado difícil de construir su propio colector que es más o menos equivalente. Una PriorityQueuees bastante útil para este propósito. He aquí un primer corte en él:

static <T> Collector<T,PriorityQueue<T>,List<T>> topK(int k, Comparator<? super T> comp) {
    return Collector.of(
        () -> new PriorityQueue<>(k+1, comp),
        (pq, t) -> {
            pq.add(t);
            if (pq.size() > k)
                pq.poll();
        },
        (pq1, pq2) -> {
            pq1.addAll(pq2);
            while (pq1.size() > k)
                pq1.poll();
            return pq1;
        },
        pq -> {
            int n = pq.size();
            @SuppressWarnings("unchecked")
            T[] a = (T[])new Object[n];
            while (--n >= 0)
                a[n] = pq.poll();
            return Arrays.asList(a);
        },
        Collector.Characteristics.UNORDERED);
}

Este utiliza una PriorityQueuecomo una estructura de datos intermedia. A medida que se añaden elementos, el elemento más pequeño se recorta cuando la cola excede k en tamaño. Al final, los elementos se extraen de la cola y se ponen en una lista en orden inverso, por lo que la lista resultante se ordena de mayor a menor.

Por ejemplo, dada una List<Integer>que contiene

[920, 203, 880, 321, 181, 623, 496, 576, 854, 323,
 339, 100, 795, 165, 857, 935, 555, 648, 837, 975]

uno puede hacer

List<Integer> out = input.stream()
                         .collect(topK(5, Comparator.naturalOrder()));

Resultando en

[979, 936, 890, 875, 831]

Como acotación al margen, es posible crear un comparador mapa mucho más simplemente usando los métodos de combinadores en la Comparatorclase. Por ejemplo, supongamos que su apariencia de entrada como esta:

    List<Map<String, String>> input =
        List.of(Map.of("name", "map1", "timestamp", "00017"),
                Map.of("name", "map2", "timestamp", "00192"),
                Map.of("name", "map3", "timestamp", "00001"),
                Map.of("name", "map4", "timestamp", "00072"),
                Map.of("name", "map5", "timestamp", "04037"));

Usted puede ordenar fácilmente los mapas de marca de tiempo de esta manera:

    input.stream()
         .sorted(Comparator.comparing(map -> map.get("timestamp")))
         .forEach(System.out::println);

O recogerlos en una lista, o una especie en el lugar usando sort(comparator), o lo que sea. Puede invertir la clase haciendo:

    input.stream()
         .sorted(Comparator.comparing(map -> map.get("timestamp"), Comparator.reverseOrder()))
         .forEach(System.out::println);

La salida de este último será entonces:

{name=map5, timestamp=04037}
{name=map2, timestamp=00192}
{name=map4, timestamp=00072}
{name=map1, timestamp=00017}
{name=map3, timestamp=00001}

Supongo que te gusta

Origin http://43.154.161.224:23101/article/api/json?id=210113&siteId=1
Recomendado
Clasificación