对于一系列整数,我想应用("昂贵的")操作,只滤除那些有趣答案的整数,然后分组答案。
这第一个片段有效,但它在代码和计算中都重复了操作("模数2和#34;):
IntStream.range(1, 10).boxed()
.filter(p -> (p % 2 != 0)) // Expensive computation
.collect(Collectors.groupingBy(p -> (p % 2))); // Same expensive
// computation!
// {1=[1, 3, 5, 7, 9]} (Correct answer)
我首先尝试映射到答案,然后过滤,然后分组 - 但最初的整数当然会在此过程中丢失:
IntStream.range(1, 10).boxed()
.map(p -> p % 2) // Expensive computation
.filter(p -> p != 0)
.collect(Collectors.groupingBy(p -> p));
// {1=[1, 1, 1, 1, 1]} (Of course, wrong answer)
我想映射到一个元组或类似的东西,但是没有找到一个干净的方法来做它。
答案 0 :(得分:4)
好吧,既然我描述了我的所作所为,并且已经对它进行了一些讨论,我想我应该写下我所描述的内容:
class IntPair {
final int input, result;
IntPair(int i, int r) { input = i; result = r; }
}
Map<Integer, List<Integer>> output =
IntStream.range(1, 10)
.mapToObj(i -> new IntPair(i, i % 2))
.filter(pair -> pair.result != 0)
.collect(groupingBy(pair -> pair.result,
mapping(pair -> pair.input, toList())));
请注意,辅助类可以(也可能应该)是某种嵌套类,甚至是本地类。
为字段命名的一个好处是,它确实可以让您更容易理解正在发生的事情。当我最初写这篇文章时,我无意中颠倒了input
和result
在分组操作中的角色,因此我得到了错误的结果。重新阅读代码后,我很容易看到我按input
值而不是result
值进行分组,并且它也很容易修复。如果我必须使用arr[0]
和arr[1]
或tuple.t1
和tuple.t2
,这将更难诊断和修复。
答案 1 :(得分:2)
为什么不对昂贵计算的结果进行分组,然后过滤生成的地图?
IntStream.range(1, 10).boxed()
.collect(groupingBy(x -> x % 2))
.entrySet().stream()
.filter(e -> e.getKey() != 0)
.collect(toMap(Map.Entry::getKey, Map.Entry::getValue));
答案 2 :(得分:1)
如果你想通过单个流实现这个(不收集到中间地图),你可以这样做:
#import "IssueShowcaseViewController.h"
@interface IssueShowcaseViewController () <UIScrollViewDelegate>
{
BOOL _isShowingUpperView;
}
@property (nonatomic, strong) UIScrollView* scrollView;
@property (nonatomic, strong) UIView* upperView;
@property (nonatomic, strong) UIView* lowerView;
@end
@implementation IssueShowcaseViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.scrollView = [[UIScrollView alloc] initWithFrame:self.view.bounds];
self.upperView = [[UIView alloc] initWithFrame:self.scrollView.bounds];
self.lowerView = [[UIView alloc] initWithFrame:CGRectOffset(self.scrollView.bounds, 0, self.scrollView.bounds.size.height)];
[self.upperView setBackgroundColor:[UIColor blueColor]];
[self.lowerView setBackgroundColor:[UIColor greenColor]];
//Add upperView to upper half, and lowerView to lower half:
[self.scrollView addSubview:self.upperView];
[self.scrollView addSubview:self.lowerView];
[self.view addSubview:self.scrollView];
_isShowingUpperView = YES;
self.scrollView.delegate = self;
// Observe scrollView.contentOffset for watching unnormal changes:
[self.scrollView addObserver:self forKeyPath:@"contentOffset" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
// Set contentSize as the sum height of two subviews:
self.scrollView.contentSize = CGSizeMake(self.view.frame.size.width, CGRectGetMaxY(self.lowerView.frame) - CGRectGetMinY(self.upperView.frame));
// But the contentInset is set as to not allow scrolling into lower half:
// 1-pixel more space is necessary, as the scrollView would not be able to scroll downwards without it:
self.scrollView.contentInset = UIEdgeInsetsMake(0, 0, 1-self.lowerView.frame.size.height, 0);
}
- (void) scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
if (_isShowingUpperView)
{
if (scrollView.contentOffset.y > 60)
{
scrollView.contentInset = UIEdgeInsetsMake(1-self.upperView.frame.size.height, 0, 0, 0);
_isShowingUpperView = NO;
}
}
else
{
if (scrollView.contentOffset.y < self.upperView.frame.size.height - 60)
{
scrollView.contentInset = UIEdgeInsetsMake(0, 0, 1-self.lowerView.frame.size.height, 0);
_isShowingUpperView = YES;
}
}
}
#pragma mark Here we can see what happens:
- (void) scrollViewDidScroll:(UIScrollView *)scrollView {
NSLog(@"contentOffsetY = %f", scrollView.contentOffset.y);
}
- (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
if (object == self.scrollView) {
CGPoint oldOffset = [[change objectForKey:@"old"] CGPointValue];
CGPoint newOffset = [[change objectForKey:@"new"] CGPointValue];
if (fabs(newOffset.y - oldOffset.y) > 300) {
NSLog(@"Weird: %@", change);
}
}
}
@end
如果您不介意使用第三方代码,我的StreamEx库具有语法糖,特别是对于此类任务:
IntStream.range(1, 10).boxed()
.map(p -> new AbstractMap.SimpleEntry<>(p % 2, p))
.filter(entry -> entry.getKey() != 0)
.collect(Collectors.groupingBy(Entry::getKey,
Collectors.mapping(Entry::getValue, Collectors.toList())));
在内部,它与第一种解决方案几乎相同。
答案 3 :(得分:1)
一般的解决方案是记住计算结果。例如。与suggested by Stuart Marks类似,但如果您不想引入新类型,则可以改为使用int
数组:
Map<Integer, List<Integer>> map = IntStream.range(1, 10)
.mapToObj(i -> new int[]{i, i % 2})
.filter(pair -> pair[1] != 0)
.collect(groupingBy(pair -> pair[1],
mapping(pair -> pair[0], toList())));
此特殊情况的具体解决方案是通过简单操作i%2
替换昂贵的操作i&1
:
Map<Integer, List<Integer>> map = IntStream.range(1, 10)
.filter(i-> (i&1)!=0)
.boxed().collect(groupingBy(i->i&1));
这个操作太便宜了,我不在乎它的重复。但是,如果你这样做,当然
Map<Integer, List<Integer>> map = IntStream.range(1, 10)
.filter(i-> (i&1)!=0)
.boxed().collect(groupingBy(i->1));
或
Map<Integer, List<Integer>> map = Collections.singletonMap(1,
IntStream.range(1, 10).filter(i-> (i&1)!=0)
.boxed().collect(toList()));
将解决问题。当然,它不是一个可重用的解决方案,但lambda表达式无论如何都是一次性使用的代码片段。
答案 4 :(得分:0)
一种方法是首先收集Map<Integer, Integer>
的数字和答案,然后对条目流进行操作:
IntStream.range(1, 10).boxed()
.collect(toMap(p -> p, p -> p % 2))
.entrySet().stream()
.filter(p -> p.getValue() != 0)
.collect(groupingBy(p -> p.getValue(),
mapping(p -> p.getKey(),
toList())));
虽然不确定性能影响。
答案 5 :(得分:0)
所以这是我的解决方案:)。也许不是最好的。
import java.util.stream.IntStream;
import java.util.stream.Collectors;
import java.util.Map;
import java.util.List;
public class Test {
private static final class Tuple {
private final Integer t1;
private final Integer t2;
private Tuple(final Integer t1, final Integer t2) {
this.t1 = t1;
this.t2 = t2;
}
public Integer getT1() { return t1; }
public Integer getT2() { return t2; }
}
private static String getValues(final List<Tuple> list) {
final StringBuilder stringBuilder = new StringBuilder();
for(Tuple t : list) {
stringBuilder.append(t.getT2()).append(", ");
}
return stringBuilder.toString();
}
public static void main(String []args){
Map<Integer, List<Tuple>> results =
IntStream.range(1, 10).boxed()
.map(p -> new Tuple(p % 2, p)) // Expensive computation
.filter(p -> p.getT1() != 0)
.collect(Collectors.groupingBy(p -> p.getT1()));
results.forEach((k, v) -> System.out.println(k + "=" + getValues(v)));
}
}
输出为1 = 1,3,5,7,9;
关于表现:
sh-4.3# java -Xmx128M -Xms16M Test
Time a1: 231
Time a2: 125
sh-4.3# java -Xmx128M -Xms16M Test
Time a1: 211
Time a2: 127
sh-4.3# java -Xmx128M -Xms16M Test
Time a1: 172
Time a2: 100
A1是你问题中的第一个算法,A2是我的答案。因此,即使使用辅助类也会更快。
以下是性能测量,包括答案中的算法(如A3):
sh-4.3# java -Xmx128M -Xms16M HelloWorld
Time a1: 202
Time a2: 113
Time a2: 170
sh-4.3# java -Xmx128M -Xms16M HelloWorld
Time a1: 195
Time a2: 114
Time a2: 169
我发现你的表现超出了我的预期,我认为它会或多或少相同。