Java根据布尔条件将列表分类为多个列表

时间:2017-11-20 08:56:09

标签: java java-8

我在Java 8中有一个列表,并且有一些过滤函数,如果列表中的项满足该函数的条件,则返回布尔结果。我想根据条件将列表拆分成几个列表。目前我的代码看起来像 -

public void classifyItems(List<SomeType> mylist) {
    //declare and initialize list1, list2, list3
    //these lists will store items as per filtering conditions

    mylist.forEach(item -> {
        if (filteringFunction1(item) {
            list1.add(item);
        }
        else if (filteringFunction2(item) {
            list2.add(item);
        }
        else if (filteringFunction3(item) {
            list3.add(item)
        }
    });

    //other operations on filtered lists
}

是否有更清晰/更好的方法在Java 8中使用流或其他构造编写上述逻辑?我对长期的if-else梯子不太满意。我需要单独的列表来满足不同条件的项目。

4 个答案:

答案 0 :(得分:2)

接受谓词列表(或使用 varargs 方法)将允许可扩展的解决方案,如

public void classifyItems(List<SomeType> mylist, List<Predicate<SomeType>> filterFuncs) {
    int ffSize = filterFuncs.size();
    Map<Integer,List<SomeType>> classified = mylist.stream()
            .collect(Collectors.groupingBy(item ->
                IntStream.range(0, ffSize)
                         .filter(ix -> filterFuncs.get(ix).test(item))
                         .findFirst().orElse(ffSize)));

    //other operations on filtered lists
}

地图键对应filterFuncs列表中的谓词位置,因此classified.get(0)会让您“list1”,classified.get(1)会让您“list2” ,依此类推,classified.get(ffSize)将为您提供与任何谓词不匹配的所有项目的列表。

它保留原始代码的逻辑,匹配列表中较早的谓词优先于后续谓词。

答案 1 :(得分:1)

您当前的代码实际上非常易读,而且我认为它可以改进很多,但您可以考虑使用groupingBy收集器:

Map<String, List<SomeType>> grouped =  mylist.stream().collect(
    Collectors.groupingBy(it -> {
        if (filteringFunction1(item))
            return "CASE 1";

        if (filteringFunction2(item))
            return "CASE 2";

        if (filteringFunction3(item))
            return "CASE 3";

        return "DEFAULT";
    }));

这为您提供了MapList个实例,其中关键是每个值被归类为的情况。

您的程序可能包含比包含案例名称的字符串更好的地图键的候选者,也许像OldCurmudgeon的回答中的enum一样?

答案 2 :(得分:0)

为了便于使用,您可以使用带过滤器的基本流。这很容易理解,并且保持代码大小不变,但确实有三次迭代列表的缺点。

public void classifyItems(List<String> mylist) {
    List<String> filter1 = mylist.stream().filter(i -> filteringFunction1(i)).collect(Collectors.toList());
    List<String> filter2 = mylist.stream().filter(i -> filteringFunction2(i)).collect(Collectors.toList());
    List<String> filter3 = mylist.stream().filter(i -> filteringFunction3(i)).collect(Collectors.toList());
}

答案 3 :(得分:0)

使用流时,您应该尝试在使用流和编码逻辑之间找到平衡点。我可能会这样做。

enum Types {
    Type1(list1) {
        @Override
        boolean filter(SomeType it) {
            return false;
        }
    },
    Type2(list2) {
        @Override
        boolean filter(SomeType it) {
            return true;
        }
    },
    Type3(list3) {
        @Override
        boolean filter(SomeType it) {
            return false;
        }
    };

    final List<SomeType> theList;
    Types(List<SomeType> theList) {
        this.theList = theList;
    }

    List<SomeType> getList() {
        return theList;
    }

    abstract boolean filter (SomeType it);
}

public void classifyItems(List<SomeType> mylist) {
    mylist.stream().forEach(
            // walk all types.
            i -> Arrays.stream(Types.values())
                    // Choose only applicable types.
                    .filter(t -> t.filter(i))
                    // Add it to the list.
                    .forEach(t -> t.getList().add(i))
    );
}

这会对SomeType列表的属性进行编码,方法是提供filter来检测此SomeType是否适用以及与之相关的list

然后我只使用stream来表达它的意图,使用每个元素来处理流和做事。