我需要通过谓词将列表拆分为两个列表,其中限制元素将转到true
部分。
例如。假设我有这样的列表:A = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
我希望按谓词o -> o % 2 == 0
和限制3
拆分它。
我想让Map<Boolean, List<Integer>>
在哪里:
true -> [2, 4, 6] // objects by predicate and with limit (actually, order is not important)
false -> [1, 3, 5, 7, 8, 9, 10] // All other objects
Java 8具有通过谓词 - Collectors.partitioningBy(...)
拆分流的收集器,但它不支持限制。是否可以使用java 8 streams / guava / apache执行此操作,还是应该创建自己的此函数实现?
MultiValuedMap
是可选的,可以替换为Map
。
private <E> MultiValuedMap<Boolean, E> partitioningByWithLimit(Predicate<E> predicate, List<E> src, int limit) {
MultiValuedMap<Boolean, E> result = new ArrayListValuedHashMap<>();
Iterator<E> iterator = src.iterator();
while (iterator.hasNext()) {
E next = iterator.next();
if (limit > 0 && predicate.test(next)) {
result.put(true, next);
iterator.remove();
limit--;
}
}
result.putAll(false, src);
return result;
}
答案 0 :(得分:6)
以下是基于自定义收集器的方法:
public static <E> Collector<E, ?, Map<Boolean, List<E>>> partitioningByWithLimit(
Predicate<E> predicate,
int limit) {
class Acc {
Map<Boolean, List<E>> map = new HashMap<>();
Acc() {
map.put(true, new ArrayList<>());
map.put(false, new ArrayList<>());
}
void add(E elem) {
int size = map.get(true).size();
boolean key = size < limit && predicate.test(elem);
map.get(key).add(elem);
}
Acc combine(Acc another) {
another.map.get(true).forEach(this::add);
another.map.get(false).forEach(this::add);
return this;
}
}
return Collector.of(Acc::new, Acc::add, Acc::combine, acc -> acc.map));
}
我使用包裹地图的本地Acc
类并公开逻辑以累积并将流的元素组合到地图中。此映射根据提供的谓词和限制进行分区。
最后,我使用Collector.of
收集信息流。
用法:
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
Map<Boolean, List<Integer>> map = list.stream()
.collect(partitioningByWithLimit(n -> n % 2 == 0, 3));
输出是:
{false=[1, 3, 5, 7, 8, 9, 10], true=[2, 4, 6]}
这种方法的主要优点是它还支持并行流。
答案 1 :(得分:4)
没有干净的Stream解决方案,因为任务依赖于有状态谓词。
所以你的循环不错,但它可以清理一下:
// Start catching the output
ob_start();
// Run code that echoes unwanted stuff under the hood
$Connection = new Connection("...");
$array = $Connection->arrCol("SELECT IT FROM global", "IT");
// Clear the output buffer and stop buffering (i.e. discard that echo)
ob_end_clean();
// Output JSON
header('Content-Type: application/json');
echo json_encode($array);
如果您真的希望在达到限制时感觉快一点,可以使用
private <E> MultiValuedMap<Boolean, E> partitioningByWithLimit(
Predicate<E> predicate, List<E> src, int limit) {
MultiValuedMap<Boolean, E> result = new ArrayListValuedHashMap<>();
for(E next: src) {
boolean key = limit>0 && predicate.test(next);
result.put(key, next);
if(key) limit--;
}
return result;
}
这可以避免重新检查限制甚至是其余元素的地图查找,但是,我不会期望有很大的性能差异。第一个变体更简单。
使用更多Java 8功能的另一种选择是
private <E> MultiValuedMap<Boolean, E> partitioningByWithLimit(
Predicate<E> predicate, List<E> src, int limit) {
MultiValuedMap<Boolean, E> result = new ArrayListValuedHashMap<>();
for(Iterator<E> iterator = src.iterator(); iterator.hasNext(); ) {
E next = iterator.next();
boolean key = predicate.test(next);
result.put(key, next);
if(key && --limit==0) iterator.forEachRemaining(result.get(false)::add);
}
return result;
}
答案 2 :(得分:1)
这个怎么样:
namespace projectname.Models
{
public class ModelNameViewModel
{
public List<ListOfModel> { get; set; }
}
}
打印:{false = [1,3,5,7,8,9,10],true = [2,4,6]}
答案 3 :(得分:1)
怎么样:
list.stream().collect(Collectors.partitioningBy(withLimit(i -> i % 2 == 0, 3)));
public static <E> Predicate<E> withLimit(final Predicate<E> predicate, final int limit) {
Objects.requireNonNull(predicate);
final AtomicInteger counter = new AtomicInteger(limit);
return e -> predicate.test(e) && counter.decrementAndGet() > 0;
}