通过谓词对集合进行分区的库方法

时间:2012-05-11 08:01:18

标签: java collections guava

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

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

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

我意识到这对我自己的代码来说速度非常快,但我正在寻找能够实现我想要的现有库方法。

6 个答案:

答案 0 :(得分:24)

使用番石榴Multimaps.index

这是一个示例,它将单词列表分为两部分:长度为&gt;的部分。 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]

答案 1 :(得分:11)

使用新的java 8功能(streamlambda 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);

答案 2 :(得分:3)

如果您使用的是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()的协变返回类型。例如,对MutableCollection进行分区会产生两个集合而不是列表。

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

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

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

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

答案 3 :(得分:0)

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

答案 4 :(得分:0)

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

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

答案 5 :(得分:0)

对于新的Java 12 "firefox-beta": { "default": "beta", "versions": { "beta": { "image": "my/path/to/firefox:beta", "port": "4444" } } }

来说似乎不错
Collectors::teeing