Check condition and retrieve data with single pass in the stream

时间:2016-12-09 12:54:02

标签: java lambda java-stream

I have some data:

    List<String> constData = new ArrayList<>();
    constData.add("data10115");
    constData.add("data8134");
    constData.add("data8933");
    constData.add("data8206");

    String strJSON = "[{\"type1\":\"data8933,data10115\",\"type2\":\"data8134,data8135,data9931\"},{\"type1\":\"data8204,data10115\",\"type2\":\"data8773,data8206,data9931\"}]";

    Gson gson = new GsonBuilder().create();
    List<Map<String, String>> mappedJSON = gson.fromJson(strJSON, new TypeToken<List<Map<String, String>>>(){}.getType());

Now I need to check if constData have some intersections with mappedJSON with some rules:

    System.out.println(
            mappedJSON.stream()
                .anyMatch(f -> f.entrySet().stream()
                        .allMatch(
                                e -> constData.stream()
                                        .anyMatch(CollectionUtils.emptyIfNull(new ArrayList<>(Arrays.asList(e.getValue().split(","))))::contains)
                        ))
    );

Now I want not just to check the condition, but also have the array of the interceptions. Is there any way to process this (check condition and retrieve intersections) with single pass?

I only have such variant:

   Set<String> result1 = new HashSet<>();
    for(Map<String, String> mapdata : mappedJSON) {
        Map<String, List<String>> mapNode = new HashedMap<>();
        for (Map.Entry<String, String> entry : mapdata.entrySet()) {
            List<String> listNode = new ArrayList<>();
            constData.stream()
                    .filter(CollectionUtils.emptyIfNull(new ArrayList<>(Arrays.asList(entry.getValue().split(","))))::contains)
                    .collect(Collectors.toList()).forEach(l -> listNode.add(l));
            if(listNode.size() > 0) {
                mapNode.putIfAbsent(entry.getKey(), listNode);
            }
        }
        if(mapNode.entrySet().size() == mapdata.entrySet().size()) {
            result1.addAll(mapNode.entrySet().stream().map(x -> x.getValue()).flatMap(List::stream).collect(Collectors.toList()));
        }
    }
    System.out.println(result1);

This code works as I need - I can check condition (set will be empty if condition is false) and get data with the single pass. But it's look "heavy")

Or I can use lambdas to check and after that to get data:

    if(
            mappedJSON.stream()
                .anyMatch(f -> f.entrySet().stream()
                        .allMatch(
                                e -> constData.stream()
                                        .anyMatch(CollectionUtils.emptyIfNull(new ArrayList<>(Arrays.asList(e.getValue().split(","))))::contains)
                        ))
    ) {

            List<String> result0 = new ArrayList<>();
            mappedJSON.stream()
                    .forEach(
                            m -> m.entrySet().stream()
                                    .forEach(
                                            s -> constData.stream()
                                                    .filter(CollectionUtils.emptyIfNull(new ArrayList<>(Arrays.asList(s.getValue().split(","))))::contains)
                                                    .collect(Collectors.toList()).forEach(l -> result0.add(l))
                                    )
                    );
            System.out.println(result0);
    }

This is also works, but here I need two passes.

Is there any other "light and beautiful" way?

More shorter version of my question: Main logic expression is here:

   mappedJSON.stream().anyMatch(f -> f.entrySet().stream().allMatch(e -> constData.stream().anyMatch(Arrays.asList(s.getValue().split(","))::contains)))

Is there any "easy" way to get intersections from inner part of this expression (I mean this part: anyMatch(Arrays.asList(s.getValue().split(","))::contains)) ?

0 个答案:

没有答案