将一系列整数分组到函数的答案

时间:2015-05-19 21:08:57

标签: java functional-programming java-8 java-stream

对于一系列整数,我想应用("昂贵的")操作,只滤除那些有趣答案的整数,然后分组答案。

这第一个片段有效,但它在代码和计算中都重复了操作("模数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)

我想映射到一个元组或类似的东西,但是没有找到一个干净的方法来做它。

6 个答案:

答案 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())));

请注意,辅助类可以(也可能应该)是某种嵌套类,甚至是本地类。

为字段命名的一个好处是,它确实可以让您更容易理解正在发生的事情。当我最初写这篇文章时,我无意中颠倒了inputresult在分组操作中的角色,因此我得到了错误的结果。重新阅读代码后,我很容易看到我按input值而不是result值进行分组,并且它也很容易修复。如果我必须使用arr[0]arr[1]tuple.t1tuple.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 

我发现你的表现超出了我的预期,我认为它会或多或少相同。