Статьи

Использование потоков JDK 8 для преобразования между коллекциями обернутых объектов и коллекциями объектов-оберток

Я обнаружил, что декораторы и адаптеры время от времени полезны, так как я работал с приложениями на основе Java. Эти «обертки» хорошо работают в различных ситуациях и довольно просты для понимания и реализации, но все может стать немного сложнее, когда нужно обернуть иерархию объектов, а не один объект. В этой записи блога я расскажу о том, как потоки Java 8 облегчают преобразование между коллекциями объектов и коллекциями объектов, которые обертывают эти объекты.

Для этого обсуждения я применю два простых Java-класса, представляющих класс Movie и класс, который «оборачивает» этот класс с именем MovieWrapper . Класс Movie использовался в моем посте об улучшениях JDK 8 для коллекций Java. Класс Movie и класс, который его оборачивает, показаны далее.

Movie.java

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
package dustin.examples.jdk8.streams;
 
import java.util.Objects;
 
/**
 * Basic characteristics of a motion picture.
 *
 * @author Dustin
 */
public class Movie
{
   /** Title of movie. */
   private final String title;
 
   /** Year of movie's release. */
   private final int yearReleased;
 
   /** Movie genre. */
   private final Genre genre;
 
   /** MPAA Rating. */
   private final MpaaRating mpaaRating;
 
   /** imdb.com Rating. */
   private final int imdbTopRating;
 
   public Movie(final String newTitle, final int newYearReleased,
                final Genre newGenre, final MpaaRating newMpaaRating,
                final int newImdbTopRating)
   {
      this.title = newTitle;
      this.yearReleased = newYearReleased;
      this.genre = newGenre;
      this.mpaaRating = newMpaaRating;
      this.imdbTopRating = newImdbTopRating;
   }
 
   public String getTitle()
   {
      return this.title;
   }
 
   public int getYearReleased()
   {
      return this.yearReleased;
   }
 
   public Genre getGenre()
   {
      return this.genre;
   }
 
   public MpaaRating getMpaaRating()
   {
      return this.mpaaRating;
   }
 
   public int getImdbTopRating()
   {
      return this.imdbTopRating;
   }
 
   @Override
   public boolean equals(Object other)
   {
      if (!(other instanceof Movie))
      {
         return false;
      }
      final Movie otherMovie = (Movie) other;
      return   Objects.equals(this.title, otherMovie.title)
            && Objects.equals(this.yearReleased, otherMovie.yearReleased)
            && Objects.equals(this.genre, otherMovie.genre)
            && Objects.equals(this.mpaaRating, otherMovie.mpaaRating)
            && Objects.equals(this.imdbTopRating, otherMovie.imdbTopRating);
   }
 
   @Override
   public int hashCode()
   {
      return Objects.hash(this.title, this.yearReleased, this.genre, this.mpaaRating, this.imdbTopRating);
   }
 
   @Override
   public String toString()
   {
      return "Movie: " + this.title + " (" + this.yearReleased + "), " + this.genre + ", " + this.mpaaRating + ", "
            + this.imdbTopRating;
   }
}

MovieWrapper.java

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
package dustin.examples.jdk8.streams;
 
/**
 * Wraps a movie like a Decorator or Adapter might.
 *
 * @author Dustin
 */
public class MovieWrapper
{
   private Movie wrappedMovie;
 
   public MovieWrapper(final Movie newMovie)
   {
      this.wrappedMovie = newMovie;
   }
 
   public Movie getWrappedMovie()
   {
      return this.wrappedMovie;
   }
 
   public void setWrappedMovie(final Movie newMovie)
   {
      this.wrappedMovie = newMovie;
   }
 
   public String getTitle()
   {
      return this.wrappedMovie.getTitle();
   }
 
   public int getYearReleased()
   {
      return this.wrappedMovie.getYearReleased();
   }
 
   public Genre getGenre()
   {
      return this.wrappedMovie.getGenre();
   }
 
   public MpaaRating getMpaaRating()
   {
      return this.wrappedMovie.getMpaaRating();
   }
 
   public int getImdbTopRating()
   {
      return this.wrappedMovie.getImdbTopRating();
   }
 
   @Override
   public String toString()
   {
      return this.wrappedMovie.toString();
   }
}

С классами Movie и MovieWrapper определенными выше, я теперь смотрю на преобразование коллекции одного из них в коллекцию другого. До JDK 8 типичный подход к преобразованию коллекции объектов Movie в коллекцию объектов MovieWrapper в MovieWrapper исходной коллекции объектов Movie и добавлении каждого из них в новую коллекцию объектов MovieWrapper . Это продемонстрировано в следующем листинге кода.

Преобразование коллекции обернутых объектов в коллекцию объектов-оберток

1
2
3
4
5
6
// movies previously defined as Set<Movie>
final Set<MovieWrapper> wrappedMovies1 = new HashSet<>();
for (final Movie movie : movies)
{
   wrappedMovies1.add(new MovieWrapper(movie));
}

С потоками JDK 8 вышеприведенная операция теперь может быть реализована, как показано в следующем листинге кода.

Преобразование коллекции обернутых объектов в коллекцию объектов-обёрток — JDK 8

1
2
3
// movies previously defined as Set<Movie>
final Set<MovieWrapper> wrappedMovies2 =
   movies.stream().map(movie -> new MovieWrapper(movie)).collect(Collectors.toSet());

Преобразование другого направления (из коллекции объектов-оболочек в коллекцию объектов-оболочек) можно сравнить аналогичным образом, чтобы продемонстрировать, как JDK 8 меняет это. Следующие два списка кода показывают старый путь и путь JDK 8.

Преобразование коллекции объектов-оболочек в коллекцию обернутых объектов

1
2
3
4
5
final Set<Movie> newMovies1 = new HashSet();
for (final MovieWrapper wrappedMovie : wrappedMovies1)
{
   newMovies1.add(wrappedMovie.getWrappedMovie());
}

Преобразование коллекции объектов-оболочек в коллекцию обернутых объектов — JDK 8

1
2
final Set<Movie> newMovies2 =
   wrappedMovies2.stream().map(MovieWrapper::getWrappedMovie).collect(Collectors.toSet());

Как и в некоторых примерах из моего поста « Потоковая функциональность коллекций» в JDK 8 , примеры в этом посте демонстрируют мощь агрегатных операций, представленных в JDK 8. Преимущества этих агрегатных операций по сравнению с традиционной итерацией включают в себя большую краткость в коде, возможно, (возможно, в конечном итоге) улучшенная читаемость и преимущества внутренней итерации (в том числе упрощение параллелизации с поддержкой потенциальных потоков ). Хороший пример использования потоков и более сложных функций для преобразования между коллекциями менее связанных объектов показан в объекте Transform в другой тип с Java 8 .