Comment obtenir une carte paginée triée par deux conditions à l'aide de flux java 8


J'ai un objet modèle Account avec une liste de champs, dans le cas simple avec deux champs String name et double margin

Et j'ai besoin d'obtenir la page exacte de ces comptes qui dépasse une certaine limite de marge, triée par marge et nom. Chaque enregistrement doit avoir margin comme clé et Account comme valeur.

J'ai fait cet exemple minimal de code, mais il semble que le tri ne fonctionne pas bien pour moi. Je l'enveloppe donc dans TreeMap, mais cela coûtera de la mémoire supplémentaire et je le déteste. Devrait probablement attendre pour résoudre ce problème à l'intérieur de flux et ajouter losted trier par nom en cas de marge égale

@Data
class Account {
    private final String name;
    private final double margin;

    private final static double MARGIN_LIMIT = 100;
    private final static int PAGE_SIZE = 3;

    private static Set<Account> accounts = new HashSet<>();

    public static void main(String[] args) {
        accounts.add(new Account("user1", 200));
        accounts.add(new Account("user2", 100));
        accounts.add(new Account("user3", 150));
        accounts.add(new Account("user4", 175));
        accounts.add(new Account("user5", 75));
        accounts.add(new Account("user6", 110));

        Map<Double,Account> val = new TreeMap<Double,Account>(Comparator.reverseOrder());
        val.putAll(getClientsForClosing(2));
        System.out.println(val);
    }

    private static Map<Double, Account> getClientsForClosing(int page) {
        return accounts.stream()
                .filter(account -> account.getMargin() >= MARGIN_LIMIT)
                .sorted(Comparator.comparingDouble(Account::getMargin).reversed())
                .skip(PAGE_SIZE * (page - 1))
                .limit(PAGE_SIZE)
                .collect(Collectors.toMap(Account::getMargin, o -> o));
    }
}
Author: Batiaev, 2017-11-15

3 answers

Votre question et votre solution sont en quelque sorte contradictoires, d'une part vous montrez du code qui trie vos entrées par margin - qui est un double; dans ce cas, il existe un moyen plus simple:

accounts.stream()
        .filter(account -> account.getMargin() >= MARGIN_LIMIT)
        .skip(PAGE_SIZE * (page - 1))
        .limit(PAGE_SIZE)
        .collect(Collectors.groupingBy(
               Account::getMargin,
               () -> new TreeMap<Double, List<Account>>(Comparator.reverseOrder()),
               Collectors.toList()));

, Si vous voulez trier par les deux margin et name et toujours conserver la définition de Map<Double, List<Account>>, vous allez avoir à coller avec un LinkedHashMap:

accounts.stream()
        .filter(account -> account.getMargin() >= MARGIN_LIMIT)
        .sorted(Comparator.comparingDouble(Account::getMargin)
                          .reversed()
                          .thenComparing(Comparator.comparing(Account::getName)))
        .skip(PAGE_SIZE * (page - 1))
        .limit(PAGE_SIZE)
        .collect(Collectors.toMap(
              Account::getMargin,
              x -> {
                 List<Account> list = new ArrayList<>();
                 list.add(x);
                 return list;
              },
              (left, right) -> {
                  left.addAll(right);
                  return left;
              },
              LinkedHashMap::new));
 4
Author: Eugene, 2017-11-14 22:21:33

Semble que cela fixe exactement ce que vous cherchez

private static Map<Double, Account> getClientsForClosing(int page) {
    return accounts.stream()
            .filter(account -> account.getMargin() >= MARGIN_LIMIT)
            .sorted(Comparator.comparingDouble(Account::getMargin)
                    .reversed().thenComparing(Comparator.comparing(Account::getName)))
            .skip(PAGE_SIZE * (page - 1))
            .limit(PAGE_SIZE)
            .collect(Collectors.toMap(Account::getMargin, o -> o,
                    (oldVal, newVal) -> oldVal, LinkedHashMap::new));
}
 4
Author: , 2017-11-14 21:40:52

D'autres réponses sont correctes, mais je pense que TreeSet (au lieu de TreeMap) pourrait vous être d'une grande aide:

TreeSet<Account> accounts = new TreeSet<>(
    Comparator.comparingDouble(Account::getMargin).reversed()
        .thenComparing(Account::getName));

// Then add all the accounts

Map<Double, List<Account>> result = accounts
    .headSet(new Account("zzzz", MARGIN_LIMIT)) // zzzz is the greatest string 
    .stream()                                   // I could think of at this time
    .skip(PAGE_SIZE * (page - 1))               // (it's quite late here)
    .limit(PAGE_SIZE)
    .collect(Collectors.groupingBy(Account::getMargin));

Cette solution utilise le TreeSet.headSet méthode, qui va comme un gant pour ce que vous essayez de faire. Ensuite, nous passons au flux pour ignorer et limiter les éléments et enfin collecter à la structure de données souhaitée.

 2
Author: Federico Peralta Schaffner, 2017-11-15 02:10:48