Java 8 is a game changer. There is no question about that. Java 7 barely made a splash, but Java 8 is like Java 5 which introduced generics, annotations, and so on. I may be a little late to the party on Java 8 but in corporate America, things become relevant a little slower. One of the ways Java 8 has changed things is with streams and how it has changed the way we can do loops.
Here is a little code to show you how things can be different with streams. I will go into more detail but this is just to start the discussion with code.
public void doStuff() throws Exception {
List<BlogPost > posts = new ArrayList<>();
BlogPost post1 = new BlogPost();
post1.setTile("num1");
post1.setTags(Arrays.asList("A", "B", "C"));
posts.add(post1);
BlogPost post2 = new BlogPost();
post2.setTile("num1");
post2.setTags(Arrays.asList("C", "D", "E"));
posts.add(post2);
BlogPost result1 = getRelatedPostLoop(posts, "D");
Optional<BlogPost > result2 =
getRelatedPostStream(posts, "D");
if (result1 != result2.get()) {
throw new Exception("error");
}
BlogPost result3 = getRelatedPostLoop(posts, "F");
Optional<Blogpost> result4 =
getRelatedPostStream(posts, "F");
if (result3 != null || result4.isPresent()) {
throw new Exception("error");
}
}
private BlogPost getRelatedPostLoop(List<BlogPost> posts,
String tag) {
for (BlogPost post : posts) {
if (post.getTags().contains(tag)) {
return post;
}
}
return null;
}
private Optional<BlogPost> getRelatedPostStream(
List<BlogPost > posts, String tag) {
return posts.stream()
.filter(post -> post.getTags().contains(tag))
.findFirst();
}
private class BlogPost {
private String tile;
private StringBuffer body;
private List<String> tags;
public String getTile() {
return tile;
}
public void setTile(String tile) {
this.tile = tile;
}
public StringBuffer getBody() {
return body;
}
public void setBody(StringBuffer body) {
this.body = body;
}
public List<string> getTags() {
return tags;
}
public void setTags(List<String> tags) {
this.tags = tags;
}
}
Pretty cool, right? Single pass loops can be made a lot simpler with the use of streams. The list is being used as a stream which does the same as a for
loop. The filter operation does exactly what you would expect; it filters each element in the list by a condition passed in through the argument. Lastly, findFirst pretty clearly from its name again returns the first element from the stream or null
if the stream is empty.
Other hidden gems in that code thanks to Java 8:
-
List<BlogPost > posts = new ArrayList<>();
This is a simple thing but reduces frustration. How many times have you been frustrated because you had to cut and paste the same generic on both sides of an assignment? You are writing the assignment inline why does the compiler really need you to specify it twice! Well, now you don't have to.
-
Optional<BlogPost>
Optional
is a new class which helps make it clear that a null
object response can be returned and adds in some helper methods to work with that possibly null
value cleanly.
More examples of how streams can be done to make the code a little cleaner:
- Here is a simple way to return the filtered matches as a collection. There is no need to create the in scope list variable to be used in the response.
private List<BlogPost> getAllRelatedPosts(
List<BlogPost > posts, String tag) {
return posts.stream()
.filter(post -> post.getTags().contains(tag))
.collect(Collectors.toList());
}
- Also, here is a simple way to return the filtered matches but only the distinct matches. Small change but showing the power here.
private Set<BlogPost> getAllRelatedPosts(
List<BlogPost> posts, String tag) {
return posts.stream()
.filter(post -> post.getTags().contains(tag))
.collect(Collectors.toSet());
}
- Even performing operations to say get a sum total based upon a loop variable method is easy:
private int getTagCount(List<BlogPost> posts) {
return posts.stream()
.mapToInt(
post ->
post.getTags().size())
.sum();
}
- The magic continues on and on but those are things you really should explore on your own
Potentially helpful link: