2015-06-12 2 views
2

Вот некоторые императив код, который я пытаюсь перевести в функциональный программный код:функциональный способ аккумулировать пар в java8

public class Person { 
    String name; 
    Token token; 

    public Person(String name, Token token) { 
     this.name = name; 
     this.token = token; 
    } 
} 
public class Token { 
    String id; 
    boolean isValid; 
    public Token(String id, boolean isValid) { 
     this.id = id; 
     this.isValid = isValid; 
    } 
    public String getId() { return id; } 
    public boolean isValid() {return isValid;} 
} 
public static List<Token> getTokensForPerson(String name) {...} 


public static List<Person> getPeople1 (String[] names) { 

    List<Person> people = new ArrayList<Person>(); 
    for (String name: names) { 
     List<Token> tokens = getTokensForPerson(name); 
     for (Token token: tokens) { 
      if (token.isValid()) { 
       people.add(new Person(name, token)); 
      } 
     } 

    } 
    return people; 
} 

Вот моя попытка сделать это функциональный путь.

public static List<Person> getPeople2 (String[] names) { 

    return Arrays.stream(names).map(name -> getTokensForPerson(name)) 
     .flatMap(tokens -> tokens.stream().filter(token -> token.isValid)) 
     .map(token -> new Person(name, token)) // <== compiler error here. "Cannot resolve symbol 'name'" 
     .collect(Collectors.toList()); 
} 

Однако он не компилируется, так как в последней операции карты мне нужно обратиться к name создать Person объект и name не доступны в то время. Есть идеи?

+0

"императив" лучше :) – ZhongYu

ответ

2

Вы можете перемещать map шаги внутри flatMap:

return Arrays.stream(names) 
     .<Person>flatMap(
       name -> getTokensForPerson(name).stream() 
         .filter(Token::isValid) 
         .map(token -> new Person(name, token))) 
     .collect(Collectors.toList()); 

Таким образом, вы можете получить доступ к name переменной а. Решение

StreamEx основанных короче, хотя он требует третьей стороны библиотеки:

return StreamEx.of(names) 
       .cross(name -> getTokensForPerson(name).stream()) 
       // Here we have the stream of entries 
       // where keys are names and values are tokens 
       .filterValues(Token::isValid) 
       .mapKeyValue(Person::new) 
       .toList(); 
0

было бы возможно создать TokenExtended класс, расширяющий токен, и добавив имя, и возвращает List<TokenExtended> из getTokensForPerson вместо List<Token>?

public class TokenExtended extends Token { 
    private String name; 
    public TokenExtended(String name, String id, boolean isValid) { 
     super(id, isValid); 
     this.name = name; 
    } 
} 

Таким образом, ваш код будет работать

Arrays.stream(names).map(name -> getTokensForPerson(name)).flatMap(tokens -> tokens.stream().filter(token -> token.isValid)) 
      .map(token -> new Person(token.name, token)).collect(Collectors.toList()); 
Смежные вопросы