首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >优化Java 8函数算法与过程计数器的比较

优化Java 8函数算法与过程计数器的比较
EN

Stack Overflow用户
提问于 2015-07-14 16:12:54
回答 2查看 208关注 0票数 4

我正在尝试优化Java 8中的功能操作,而不是与其过程等效的操作,但我遇到了一些严重的性能问题。

情况

我必须从给定枚举的值解析Headers String, List<String>,该枚举将HeaderName映射到许多可能的变体String, Set<String>

示例

给定以下HttpHeaders

代码语言:javascript
复制
public static final Map<String, List<String>> httpHeaders = new HashMap<>();

httpHeaders.put("Content-Type", Arrays.asList("application/json", "text/x-json"));
httpHeaders.put("SID", Arrays.asList("ABC123"));
httpHeaders.put("CORRELATION-ID", Arrays.asList("ZYX666"));

以及我的自定义枚举:

LogHeaders

代码语言:javascript
复制
protected final String key;
protected final Set<String> variation;

SESSION_ID("_sid", Arrays.asList("SESSION-ID", "SID"));    
CORRELATION_ID("cid", Arrays.asList("CORRELATION-ID", "CID")),


  private LogHeaders(final String logKey, final List<String> logKeyVariations) {

        this.logKey = logKey;
        this.logKeyVariations = new HashSet<>(logKeyVariations);
    }

@Override
    public String toString() {

        return this.logKey;
    }

结果应该是"LogHeaders.key“的映射,其中包含来自HttpHeaders的相应变体的值集。对于给定的标题,只有一个变化是可能的:

代码语言:javascript
复制
// {LogHeaders.key : HttpHeaderValue>
{
_sid=[ABC123], 
_cid=[ZYX666]
}

程序代码

代码语言:javascript
复制
final Map<String, List<String>> logHeadersToValue = new HashMap<>();

for (final LogHeaders header : LogHeaders.values()) {
  for (final String variation : header.getLogKeyVariations()) {
    final List<String> headerValue = httpHeaders.get(variation);
      if (headerValue != null) {
        logHeadersToValue.put(header.logKey, headerValue);
        break;
      }
  }
}

功能代码

代码语言:javascript
复制
final Map<String, List<String>> logHeadersToValue =
EnumSet.allOf(LogHeaders.class)
  .stream()
  .collect(Collectors.toMap(
    LogHeaders::toString,
    logHeader -> logHeader.getLogKeyVariations().stream()
      .map(variation -> httpHeaders.get(variation)).filter(Objects::nonNull)
      .collect(singletonCollector())));


public static <T> Collector<T, ?, T> singletonCollector() {

  return Collectors.collectingAndThen(Collectors.toList(), list -> {
      if (list.size() < 1) {
        return null;
      }
      return list.get(0);
    });
}

现行基准

代码语言:javascript
复制
FunctionalParsing : 0.086s

ProceduralParsing : 0.001s

你知道我如何优化我的功能部分吗?

谢谢

更新基准

我使用@Tagir代码运行了一个100 k热身+100 k迭代:

代码语言:javascript
复制
FunctionalParsing : 0.040s

ProceduralParsing : 0.010s

更新基准#2

我用@Misha代码运行了100 k热身+100 k迭代:

代码语言:javascript
复制
FunctionalParsing : 0.025s

ProceduralParsing : 0.017s
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2015-07-15 03:01:51

您的功能代码与原始代码不一样。如果其中一个LogHeader不能匹配一个标头,那么旧代码就会跳过它,而函数代码则会抛出一个NullPointerException

将原始代码直接转换为流如下所示:

代码语言:javascript
复制
Map<String, List<String>> logHeadersToValue =  Arrays.stream(LogHeaders.values())
    .collect(
        HashMap::new,
        (map, logHeader) -> logHeader.getLogKeyVariations().stream()
            .filter(httpHeaders::containsKey)
            .findAny()
            .ifPresent(x -> map.put(logHeader.key, httpHeaders.get(x))),
        Map::putAll
    );

如果您想让它更具表现力,并且易于阅读,请考虑预先计算对其键的每个变体的Map<String,String>。您可以通过这样修改enum来做到这一点:

代码语言:javascript
复制
enum LogHeaders {

    SESSION_ID("_sid", "SESSION-ID", "SID"),
    CORRELATION_ID("cid", "CORRELATION-ID", "CID");

    final String key;
    final Map<String, String> variations;

    private LogHeaders(final String key, String... variation) {
        this.key = key;
        variations = Arrays.stream(variation).collect(collectingAndThen(
                toMap(x -> x, x -> key),
                Collections::unmodifiableMap
        ));
    }

    // unmodifiable map of every variation to its key
    public final static Map<String, String> variationToKey =
        Arrays.stream(LogHeaders.values())
            .flatMap(lh -> lh.variations.entrySet().stream())
            .collect(collectingAndThen(
                            toMap(Map.Entry<String, String>::getKey, Map.Entry<String, String>::getValue),
                            Collections::unmodifiableMap
            ));  // will throw if 2 keys have the same variation
}

如果有重复的变体,这种方法的优点是快速失败。现在,代码变得非常简单:

代码语言:javascript
复制
Map<String, List<String>> logHeadersToValue = LogHeaders.variationToKey.keySet().stream()
    .filter(httpHeaders::containsKey)
    .collect(toMap(LogHeaders.variationToKey::get, httpHeaders::get));
票数 2
EN

Stack Overflow用户

发布于 2015-07-14 16:35:31

我绝对相信你做得不对。很可能你只执行了一次。你不在乎你的程序是工作0.001秒还是0.086秒,对吗?它还是比你眨眼还快。因此,您可能想要多次运行此代码。但是,您似乎只测量了一次时间,并且错误地假设每次连续运行所花费的时间大致相同。在第一次启动期间,代码主要由解释器执行,而它将在稍后进行JIT编译,并将运行得更快。这对于与流相关的代码来说是非常关键的。

至于您的代码,似乎没有必要使用自定义收集器。您可以这样实现它:

代码语言:javascript
复制
final Map<String, List<String>> logHeadersToValue =
        EnumSet.allOf(LogHeaders.class)
          .stream()
          .collect(Collectors.toMap(
            LogHeaders::toString,
            logHeader -> logHeader.getLogKeyVariations().stream()
              .map(httpHeaders::get).filter(Objects::nonNull)
              .findFirst().orElse(null)));

这个解决方案也可能更快,因为它不会读取一个以上的http头(就像在过程代码中通过break完成的那样)。

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/31411960

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档