2017-09-26 55 views
4

我想知道有没有办法用Stream的API来重写这样与流API排除结果的记录的更好的办法

public static void main(String[] args) { 
    final List<String> dataCollection = Collections.emptyList(); 
    final Set<String> someValues = new HashSet<>(); 
    final Iterator<String> iterator = dataCollection.iterator(); 
    while (iterator.hasNext()) { 
     final String dataItem = iterator.next(); 
     // imagine some calculations 
     String calculatedData = dataItem; 
     if (!someValues.contains(calculatedData)) { 
      logger.error("Skipped data {} because of ...#1", dataItem); 
      iterator.remove(); 
      continue; 
     } 

     for (char element : dataItem.toCharArray()) { 
      // imagine some other calculations 
      if (element > 100) { 
       logger.error("Skipped data {} because of ...#2", dataItem); 
       iterator.remove(); 
       break; 
      } 
     } 
    } 
} 

代码,以便排除过滤元素被记录后。 peek()在这种情况下不起作用,因为它要么在过滤器之前执行每个元素的操作,要么在剩余项之后执行操作。

到目前为止,我设法使用lambda中的日志记录来设计它,但它看起来冗长,笨拙,并且像副作用一样。我们可以用一些方法来包装它,但它只会隐藏该代码

public static void main(String[] args) { 
    final List<String> dataCollection = Collections.emptyList(); 
    final Set<String> someValues = new HashSet<>(); 
    final Iterator<String> iterator = dataCollection.iterator(); 

    dataCollection.stream() 
      .filter(byCondition1(someValues)) 
      .filter(byCondition2()) 
      .collect(Collectors.toList()); 
} 

private static Predicate<String> byCondition1(Set<String> someValues) { 
    return dataItem -> { 
     final boolean remain = someValues.contains(dataItem); 
     if (!remain) { 
      logger.error("Skipped data {} because of ...#1", dataItem); 
     } 
     return remain; 
    }; 
} 

private static Predicate<String> byCondition2() { 
    return dataItem -> { 
     for (char element : dataItem.toCharArray()) { 
      // imagine some other calculations 
      if (element > 100) { 
       logger.error("Skipped data {} because of element {}...#2", dataItem, element); 
       return false; 
      } 
     } 
     return true; 
    }; 
} 

我希望有更好的方法。

+0

*可能* Predicate的扩展可能是?像'LoggingPredicate extends Predicate ...'和静态方法'Static LoggingPredicate(Logger log)'一样,它会创建它并覆盖'test'方法。只是一个想法... – Eugene

+0

是的,但是......这是包装此代码的另一种方式。当然,它具有更好的可重用性,比我的 – WeGa

+0

我不认为有任何其他的方式来做到这一点,因为流API没有任何东西,除了'peek' – Eugene

回答

3

假定跳跃是不平凡的条件下,这可能是可行的

List<String> dataCollection = Arrays.asList("FOO", "hello", "VALID", "123", "BAR", "bla"); 
Set<String> someValues = new HashSet<>(Arrays.asList("FOO", "BAR")); 

Predicate<String> firstPredicate = string -> !someValues.contains(string); 
Predicate<String> secondPredicate = string -> string.chars().noneMatch(c -> c>100); 

List<String> result; 

if(!logger.isLoggable(Level.WARNING)) { 
    result = dataCollection.stream() 
      .filter(firstPredicate) 
      .filter(secondPredicate) 
      .collect(Collectors.toList()); 
} 
else { 
    Map<Boolean, List<String>> map = dataCollection.stream() 
     .collect(Collectors.partitioningBy(firstPredicate)); 
    if(!map.get(false).isEmpty()) 
     logger.log(Level.WARNING, "Skipped data {0} because of ...#1", map.get(false)); 
    map = map.get(true).stream() 
     .collect(Collectors.partitioningBy(secondPredicate)); 
    if(!map.get(false).isEmpty()) 
     logger.log(Level.WARNING, "Skipped data {0} because of ...#2", map.get(false)); 
    result = map.get(true); 
} 

此代码使用标准java.util.logging API,如果日志记录指定的电平被禁用将不执行任何记录相关操作。另一方面,如果启用并遇到要跳过的项目,则会按条件一次报告所有项目,而不是每个元素的条目填充日志。使用上面所示的采样数据和默认日志处理程序,它印

Sep 26, 2017 11:00:46 AM LoggingExample main 
WARNUNG: Skipped data [FOO, BAR] because of ...#1 
Sep 26, 2017 11:00:46 AM LoggingExample main 
WARNUNG: Skipped data [hello, bla] because of ...#2 

结果为[VALID, 123],是否记录该级别启用与否。

+0

关于将流分成两部分的好主意。它是数据流动的反应方式。可悲的是,在(至少在java中)分裂是终端操作,并且会破坏流懒惰的评估潜力。现在还有2个代码块计算结果,其实实际上是重复商业算法 – WeGa

2

首先,我认为OP中的代码并不算太坏。其次,还有一点点的改进,我们可以通过原有的代码做:

final Predicate<String> loggingFilter = dataItem -> { 
    final String calculatedData = dataItem; // imagine some calculations 
    if (!someValues.contains(calculatedData)) { 
     logger.error("Skipped data {} because of ...#1", dataItem); 
     return false; 
    }   
    final OptionalInt element = dataItem.chars().filter(ch -> ch > 100).findAny(); 
    if (element.isPresent()) { 
     logger.error("Skipped data {} because of element {}...#2", dataItem, element.getAsInt()); 
     return false; 
    } 
    return true; 
}; 

dataCollection.stream().filter(loggingFilter).collect(Collectors.toList()); 

我不知道,如果它是:“冗长,笨拙,就像副作用”给你或其他人。对我来说,没有重复的代码或副作用。这几乎是所有我们可以通过任何语言,除了一些语言可能会提供链操作API删除if(...); if(...)