28

我有一个对象集合,我想将它们划分为两个集合,其中一个通过谓词,另一个通过谓词失败。我希望有一种Guava方法可以做到这一点,但他们最接近的是filter,它不会给我其他集合。

我会想象该方法的签名将是这样的:

public static <E> Pair<Collection<E>, Collection<E>> partition(Collection<E> source, Predicate<? super E> predicate)

我意识到这对自己进行编码非常快,但我正在寻找一种现有的库方法来满足我的需求。

4

6 回答 6

26

使用番石榴的Multimaps.index.

这是一个示例,它将单词列表分成两部分:长度大于 3 的部分和不长度的部分。

List<String> words = Arrays.asList("foo", "bar", "hello", "world");

ImmutableListMultimap<Boolean, String> partitionedMap = Multimaps.index(words, new Function<String, Boolean>(){
    @Override
    public Boolean apply(String input) {
        return input.length() > 3;
    }
});
System.out.println(partitionedMap);

印刷:

false=[foo, bar], true=[hello, world]
于 2012-05-11T08:14:19.550 回答
15

使用新的 java 8 功能(lambda epressions),您可以编写:

List<String> words = Arrays.asList("foo", "bar", "hello", "world");

Map<Boolean, List<String>> partitionedMap =
        words.stream().collect(
                Collectors.partitioningBy(word -> word.length() > 3));

System.out.println(partitionedMap);
于 2016-06-23T09:18:04.360 回答
4

如果您使用的是Eclipse Collections(以前称为 GS Collections),则可以partition在所有RichIterables.

MutableList<Integer> integers = FastList.newListWith(-3, -2, -1, 0, 1, 2, 3);
PartitionMutableList<Integer> result = integers.partition(IntegerPredicates.isEven());
Assert.assertEquals(FastList.newListWith(-2, 0, 2), result.getSelected());
Assert.assertEquals(FastList.newListWith(-3, -1, 1, 3), result.getRejected());

使用自定义类型 ,PartitionMutableList而不是的原因Pair是允许 getSelected() 和 getRejected() 的协变返回类型。例如,对 a 进行分区MutableCollection会给出两个集合而不是列表。

MutableCollection<Integer> integers = ...;
PartitionMutableCollection<Integer> result = integers.partition(IntegerPredicates.isEven());
MutableCollection<Integer> selected = result.getSelected();

如果您的集合不是RichIterable,您仍然可以使用 Eclipse Collections 中的静态实用程序。

PartitionIterable<Integer> partitionIterable = Iterate.partition(integers, IntegerPredicates.isEven());
PartitionMutableList<Integer> partitionList = ListIterate.partition(integers, IntegerPredicates.isEven());

注意:我是 Eclipse Collections 的提交者。

于 2013-04-15T16:54:47.357 回答
2

对于新的 Java 12 来说,这似乎是一份不错的工作Collectors::teeing

var dividedStrings = Stream.of("foo", "hello", "bar", "world")
            .collect(Collectors.teeing(
                    Collectors.filtering(s -> s.length() <= 3, Collectors.toList()),
                    Collectors.filtering(s -> s.length() > 3, Collectors.toList()),
                    List::of
            ));
System.out.println(dividedStrings.get(0)); //[foo, bar]
System.out.println(dividedStrings.get(1)); //[hello, world]

您可以在此处找到更多示例。

于 2019-04-03T15:32:57.097 回答
0

Apache Commons Collections 提供了基于一个或多个谓词对对象IterableUtils进行分区的方法。Iterable(寻找partition(...)方法。)

于 2017-09-28T09:04:35.753 回答
0

请注意,在有限的一组预先知道的分区键的情况下,对于每个分区键再次迭代集合可能会更有效,在每次迭代中跳过所有不同的键项。因为这不会为垃圾收集器分配许多新对象。

LocalDate start = LocalDate.now().with(TemporalAdjusters.firstDayOfYear());
LocalDate endExclusive = LocalDate.now().plusYears(1);
List<LocalDate> daysCollection = Stream.iterate(start, date -> date.plusDays(1))
        .limit(ChronoUnit.DAYS.between(start, endExclusive))
        .collect(Collectors.toList());
List<DayOfWeek> keys = Arrays.asList(DayOfWeek.values());

for (DayOfWeek key : keys) {
    int count = 0;
    for (LocalDate day : daysCollection) {
        if (key == day.getDayOfWeek()) {
            ++count;
        }
    }
    System.out.println(String.format("%s: %d days in this year", key, count));
}

另一种对 GC 友好且封装的方法是使用 Java 8 过滤原始集合周围的包装流:

List<AbstractMap.SimpleEntry<DayOfWeek, Stream<LocalDate>>> partitions = keys.stream().map(
        key -> new AbstractMap.SimpleEntry<>(
                key, daysCollection.stream().filter(
                    day -> key == day.getDayOfWeek())))
        .collect(Collectors.toList());
// partitions could be passed somewhere before being used
partitions.forEach(pair -> System.out.println(
        String.format("%s: %d days in this year", pair.getKey(), pair.getValue().count())));

两个片段都打印了这个:

MONDAY: 57 days in this year
TUESDAY: 57 days in this year
WEDNESDAY: 57 days in this year
THURSDAY: 57 days in this year
FRIDAY: 56 days in this year
SATURDAY: 56 days in this year
SUNDAY: 56 days in this year
于 2018-02-01T08:45:43.660 回答