web-dev-qa-db-fra.com

utiliser l'instruction if-else dans l'expression lambda en Java-8

J'ai un for-statement en Java-7 et tout fonctionne bien:

Character cha = new Character(',');
String ncourseIds = null;
String pastCourseIds = null;
for (EquivalentCourse equivalentCourse : equivalentCourses) {
  if(equivalentCourse.getNcourse() != null){
    ncourseIds += equivalentCourse.getNcourse().getId()+ ",";   
  } else if(equivalentCourse.getPastCourse() != null) {
    pastCourseIds +=equivalentCourse.getPastCourse().getId()+","; 
  }
}
if(!ncourseIds.isEmpty() &&cha.equals(ncourseIds.charAt(ncourseIds.length()-1))) {
  ncourseIds = ncourseIds.substring(0, ncourseIds.length()-1);
}
if(!pastCourseIds.isEmpty()&& cha.equals(pastCourseIds.charAt(pastCourseIds.length()-1))) {
  pastCourseIds = pastCourseIds.substring(0,pastCourseIds.length()-1);
}

Maintenant, je veux convertir mon code en Stream & collect en Java-8, j'implémente la moitié de mes activités concernant le filtre non null Ncourse:

equivalentCourses.stream().filter(obj -> obj.getNcourse() != null )
                 .map(obj -> obj.getNcourse().getId()).collect(Collectors.joining(",")); 

mais je ne sais pas pour implémenter c'est else-statement. de l'aide?

4
Shayan Mirzaee

Comme une chaîne d'appels de flux est complexe, créez deux flux en évitant les branches conditionnelles.

String ncourseIds = equivalentCourses.stream()
   .filter(equivalentCourse -> equivalentCourse.getNcourse() != null)
   .map(EquivalentCourse::getNcourse)
   .map(x -> String.valueOf(x.getId()))
   .collect(Collectors.joining(", "));

String pastCourseIds = equivalentCourses.stream()
   .filter(equivalentCourse -> equivalentCourse.getNcourse() == null
          && equivalentCourse.getPastCourse() != null)
   .map(EquivalentCourse::getPastCourse)
   .map(x -> String.valueOf(x.getId()))
   .collect(Collectors.joining(", "));

C'est aussi un code qui se concentre sur les deux chaînes résultantes, avec une jonction efficace.

À propos, s'il s'agit d'une chaîne SQL, vous pouvez utiliser un PreparedStatement avec un Array .


Embellissement commenté par @Holger:

String ncourseIds = equivalentCourses.stream()
   .map(EquivalentCourse::getNcourse)
   .filter(Objects::nonNull)
   .map(NCourse::getId)
   .map(String::valueOf)
   .collect(Collectors.joining(", "));

String pastCourseIds = equivalentCourses.stream()
   .filter(equivalentCourse -> equivalentCourse.getNcourse() == null)
   .map(EquivalentCourse::getPastCourse)
   .filter(Objects::nonNull)
   .map(EquivalentCourse::getPastCourse)
   .map(PastCourse::getId)
   .map(String::valueOf)
   .collect(Collectors.joining(", "));
4
Joop Eggen

Vous pouvez regrouper par condition, puis remapper:

public void booleanGrouping() throws Exception {
    List<String> strings = new ArrayList<>();
    strings.add("ala");
    strings.add("ela");
    strings.add("jan");

    strings.stream()
            .collect(
                    Collectors.groupingBy(s -> s.endsWith("a")) // using function Obj -> Bool not predicate
            ).entrySet()
            .stream()
            .collect(
                    Collectors.toMap(
                            e -> e.getKey() ? "Present" : "Past",
                            e -> e.getValue().stream().collect(Collectors.joining(""))
                    )
            );
}

Premier groupe de flux par condition, vous devez utiliser equivalentCourse.getNcourse() != null second remapper les collections d'une valeur à une chaîne. Vous pouvez présenter:

enum PresentPast{
    Present, Past
    PresentPast is(boolean v){
         return v ? Present : Past
    }
}

et changez e -> e.getKey() ? "Present" : "Past" en solution basée sur enum. 

Modifier:

Solution pour else if:

public Map<Classifier, String> booleanGrouping() throws Exception {
    List<String> strings = new ArrayList<>();
    strings.add("ala");
    strings.add("ela");
    strings.add("jan");
    // our ifs:
    /*
        if(!string.endsWith("n")){
        }else if(string.startsWith("e")){}

        final map should contains two elements
        endsWithN -> ["jan"]
        startsWithE -> ["ela"]
        NOT_MATCH -> ["ala"]

     */
    return strings.stream()
            .collect(
                    Collectors.groupingBy(Classifier::apply) // using function Obj -> Bool not predicate
            ).entrySet()
            .stream()
            .collect(
                    Collectors.toMap(
                            e -> e.getKey(),
                            e -> e.getValue().stream().collect(Collectors.joining(""))
                    )
            );
}

enum Classifier implements Predicate<String> {
    ENDS_WITH_N {
        @Override
        public boolean test(String s) {
            return s.endsWith("n");
        }
    },
    STARTS_WITH_E {
        @Override
        public boolean test(String s) {
            return s.startsWith("e");
        }
    }, NOT_MATCH {
        @Override
        public boolean test(String s) {
            return false;
        }
    };

    public static Classifier apply(String s) {
        return Arrays.stream(Classifier.values())
                .filter(c -> c.test(s))
                .findFirst().orElse(NOT_MATCH);
    }
}
1
Koziołek

Mettre à jour

Pour ajouter une alternative, voici à quoi le code ressemblerait pour effectuer le travail avec deux opérations filter(). Notez que cela a pour effet d’itérer une seconde fois sur l’ensemble de la collection, ce qui peut avoir un impact sur les performances s’il s’agit d’une vaste collection.

Je suis aussi allé de l'avant et j'ai simplifié une partie de la logique en ce qui concerne la jonction de chaînes. Corrigez-moi si quelque chose me manquait.

final List<String> courseIdList = new ArrayList<>();
final List<String> pastCourseIdList = new ArrayList<>();

equivalentCourses.stream().filter((current) -> current.getNcourse() != null)
                .forEach((current) -> courseIdList.add(current.getNcourse().getId()));

equivalentCourses.stream().filter((current) -> current.getNcourse() != null && current.getPastCourse() != null)
                .forEach((current) -> pastCourseIdList.add(current.getPastCourse().getId()));

String ncourseIds = String.join(",", courseIdList);
String pastCourseIds = String.join(",", pastCourseIdList);

Réponse originale

Dans votre cas d'utilisation, il peut sembler judicieux d'utiliser la fonction forEach() lambda. Ce sera le moyen le plus simple de faire la traduction.

Java.lang.Character cha = new Java.lang.Character(',');

final StringBuilder ncourseIdBuilder = new StringBuilder();
final StringBuilder pastCourseIdBuilder = new StringBuilder();
equivalentCourses.stream().forEach((equivalentCourse) -> {
    if (equivalentCourse.getNcourse() != null) {
        ncourseIdBuilder.append(equivalentCourse.getNcourse().getId()).append(",");
    } else if (equivalentCourse.getPastCourse() != null) {
        pastCourseIdBuilder.append(equivalentCourse.getPastCourse().getId()).append(",");
    }
});

String ncourseIds = ncourseIdBuilder.toString();
String pastCourseIds = pastCourseIdBuilder.toString();

if (!ncourseIds.isEmpty() && cha.equals(ncourseIds.charAt(ncourseIds.length() - 1))) {
    ncourseIds = ncourseIds.substring(0, ncourseIds.length() - 1);
}
if (!pastCourseIds.isEmpty() && cha.equals(pastCourseIds.charAt(pastCourseIds.length() - 1))) {
    pastCourseIds = pastCourseIds.substring(0, pastCourseIds.length() - 1);
}

Vous pouvez réécrire le code en utilisant des expressions filter(), mais cela nécessitera un réusinage plus important de la logique dans les conditions, ce qui présente le risque de casser quelque chose si cela n’est pas bien testé. Les changements de logique sont exactement ce que @Holger et @Ole V.V. référence dans leurs commentaires à la question initiale.

Que vous utilisiez forEach() ou les filtres, lambdas ne peut pas accéder aux variables non finales dans l'expression, d'où la raison pour laquelle j'ai ajouté la variable final StringBuilder en dehors du périmètre de la boucle.

0
Cuga