J'ai un List<String> list et je veux obtenir la première et la dernière chaîne de cette liste par ordre alphabétique. Je veux résoudre ce problème en utilisant la puissance de Java 8 et des flux avec des collecteurs.

Cela ne fonctionne pas:

String first = list.stream().collect(Collectors.minBy(String.CASE_INSENSITIVE_ORDER));

Cela me donne une erreur de compilation:

Incompatibilité de type: impossible de convertir de facultatif en chaîne

Pouvez-vous expliquer pourquoi et me montrer la meilleure façon de faire ce que je veux faire?

3
principal-ideal-domain 28 déc. 2015 à 23:41

3 réponses

Meilleure réponse

{{ X0}} et Collectors.maxBy renvoie un Optional: si le Stream est vide, un Optional vide est renvoyé; sinon, un Optional contenant le résultat est renvoyé.

Si vous souhaitez avoir une valeur par défaut (ou simplement null) lorsque le Stream est vide, vous pouvez appeler orElse.

String first = list.stream().collect(minBy(String.CASE_INSENSITIVE_ORDER)).orElse(null);

De plus, si vous êtes sûr que le Stream n'est pas vide, vous pouvez appeler directement get() et récupérer la valeur.


En remarque, vous pouvez également renvoyer la valeur minimale en appelant Collections.min (resp. Collections.max):

String first = Collections.min(list, String.CASE_INSENSITIVE_ORDER);

Sans avoir besoin de créer un pipeline Stream. Notez que cela lèvera une exception si la liste est vide.

7
Tunaki 28 déc. 2015 à 20:45

Le message d'erreur est assez clair. Il en va de même pour le javadoc: Collector.minBy() produit un résultat de type Optional<T>, c'est-à-dire Optional<String> dans votre cas.

Pourquoi? Parce qu'il ne peut retourner aucune chaîne si le flux est vide. Vous devez donc obtenir la valeur String de Facultatif renvoyé par collect().

Notez que get () lèvera une exception si le flux était vide, et une valeur minimale coult donc introuvable. Si c'est ce que vous voulez parce qu'une liste vide n'est jamais censée se produire, alors très bien. Sinon, vous devez utiliser orElse () ou orElseThrow () pour renvoyer une autre valeur par défaut, ou lever une autre exception.

5
JB Nizet 28 déc. 2015 à 21:29

Vous mentionnez que vous voulez à la fois min et max de la liste. Il n'y a pas de collecteur standard qui fait cela, mais vous pouvez créer le vôtre:

class ExtremesCollector<T> {
    private final Comparator<T> comparator;
    private Optional<T> min = Optional.empty();
    private Optional<T> max = Optional.empty();

    public static Collector<T> collector(Comparator<T> comparator) {
        return Collector.of(() -> new ExtremesCollector(comparator),
            ExtremesCollector::accept, ExtremesCollector::combine);
    }

    public Optional<T> getMin() {
        return min;
    }

    public Optional<T> getMax() {
        return max;
    }

    private ExtremesCollector(Comparator<T> comparator) {
        this.comparator = comparator;
    }

    private void accept(T value) {
        if (!min.isPresent() || comparator.compare(min.get(), value) < 0)
            min = Optional.of(value):
        if (!max.isPresent() || comparator.compare(max.get(), value) > 0)
            max = Optional.of(value):
    }

    private ExtremesCollector combine(ExtremesCollector other) {
        if (other.min.isPresent())
            accept(other.min.get());
        if (other.max.isPresent())
            accept(other.max.get());
    }
}

Ceci est utilisé comme vous vous en doutez:

ExtremesCollector extremes = myList.parallelStream()
    .collect(ExtremesCollector.collector(String.CASE_INSENSITIVE_ORDER));
extremes.getMax().ifPresent(System.out::println);
extremes.getMin().ifPresent(System.out::println);

Le seul avantage de ceci par rapport à l'utilisation des méthodes intégrées min et max est l'efficacité.

0
sprinter 28 déc. 2015 à 23:43