Package org.adoptopenjdk.lambda.tutorial

Source Code of org.adoptopenjdk.lambda.tutorial.Exercise_2_Test

package org.adoptopenjdk.lambda.tutorial;

/*
* #%L
* lambda-tutorial
* %%
* Copyright (C) 2013 Adopt OpenJDK
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program.  If not, see
* <http://www.gnu.org/licenses/gpl-2.0.html>.
* #L%
*/

import org.adoptopenjdk.lambda.tutorial.exercise2.Ballot;
import org.adoptopenjdk.lambda.tutorial.exercise2.ElectoralDistrict;
import org.adoptopenjdk.lambda.tutorial.exercise2.Party;
import org.adoptopenjdk.lambda.tutorial.exercise2.Person;
import org.adoptopenjdk.lambda.tutorial.exercise2.RegisteredVoter;
import org.adoptopenjdk.lambda.tutorial.exercise2.VotingRules;
import org.adoptopenjdk.lambda.tutorial.util.FeatureMatchers;
import org.hamcrest.Matcher;
import org.junit.Test;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static java.util.Arrays.asList;
import static java.util.Arrays.binarySearch;
import static org.adoptopenjdk.lambda.tutorial.exercise2.ElectoralDistrict.HACKNEY;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.everyItem;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;

/**
* Exercise 2 - Filtering and Collecting
* <p>
* Filtering is one of the most popular operations performed on collections. When coupled with mapping/transforming,
* which we'll learn about shortly, it provides a lot of power.
* </p>
* <p>
* Consider how often you have written code like this:
* TODO Bug in the Javdoc processor? It claims the code annotation construct is invalid
* <pre>
* {@code
*     List<String> things = new ArrayList<>();
*     for (String s: otherThings) {
*         if (satisfiesSomeCondition(s)) {
*             things.add(s);
*         }
*     }
*     return things;
* }
* </pre>
* </p>
* <p>
* This is a common pattern, and it's one that is made more concise with lambdas. The common parts of the pattern are:
* <ul>
*     <li>creating an empty, destination collection</li>
*     <li>iterating over the entire source collection</li>
*     <li>deciding whether to include each item, according to some test which evaluates to a boolean
*         <ul>
*             <li>items which pass the test are copied to the destination collection</li>
*             <li>items which fail are ignored</li>
*         </ul>
*     </li>
* </ul>
* </p>
* <p>
* These steps will rarely change, but think of how we represent that in pre JDK 8 code. We use a for (or while) loop,
* and an if statement. Those parts are syntax that until now we couldn't escape, and they're almost always identical.
* </p>
* <p>
* The remaining steps (creating a collection, adding them) do have some variation: which destination collection to
* create? how should an instance be constructed? which method is used to transfer elements to the new list? These
* decisions generally lead to code which differs slightly between examples.
* </p>
* <p>
* In post-JDK 8 code, one potential solution which matches the above problem is:
*
* <pre>
* {@code
*     return otherThings.stream()
*         .filter(s -> satisfiesSomeCondition(s))
*         .collect(Collectors.toList());
* }
* </pre>
* </p>
* <p>
* In this case, unlike the previous example where we used forEach directly from List, in this case we have to "open up"
* the Streams API by calling the <code>stream()</code> method defined on Collection. This provides many operations which take
* advantage of the lambda syntax.
* </p>
* <p>
* One such method available on Stream is <code>filter()</code>. In the first code snippet, we perform our filtering using
* <code>if (satisfiesSomeCondition(s))</code>. The code inside the if condition can be referred to as a "predicate". That is, a
* function which takes some input, and returns a boolean. This is how the <code>filter()</code> method is defined: it takes a
* lambda, which, when given an element from the Stream, returns a boolean. Internally, the filter method has the
* equivalent semantics of the for loop, and the if statement. It will return a new Stream, containing
* all the elements which passed the test (or "satisfied the predicate") from the source Stream.
* </p>
* <p>
* The last piece in this snippet of code (<code>.collect(Collectors.toList())</code>) is what takes the place of constructing
* the initial empty list, and invoking the <code>add()</code> method. The code used in these examples depends on what type of
* list is best suited for the resultant list, and also, how best to transfer elements from the result Stream into
* the new list. The very common case, of creating a new ArrayList, and simply calling <code>add()</code> for each element, is
* fulfilled by Collectors.toList.
* </p>
* <p>
* If you want your result to be a different type of collection, or potentially, something completely outwith the
* collections library, and you wanted to control over how those elements were transferred, you would write your own
* collector. In most cases, and in these examples, we'll use only simple, pre-existing collectors, available in the
* JDK.
* </p>
* <p>
* The tests below can be made to pass using Stream's filter and collect methods. Try to make them pass without using
* a loop, or an if statement.
* </p>
*
* @see Collection#stream()
* @see Stream#filter(Predicate)
* @see Predicate
* @see Collector
* @see Collectors
* @see Collectors#toList()
*
* Lambda Tutorial -- Adopt Open JDK
* @author Graham Allan grundlefleck at gmail dot com
*/
@SuppressWarnings("unchecked")
public class Exercise_2_Test {

    /**
     * Use <code>Stream.filter()</code> to produce a list containing only those
     * Persons eligible to vote.
     *
     * @see Person#getAge()
     */
    @Test
    public void getAllPersonsEligibleToVote() {
        List<Person> potentialVoters =
                new ArrayList<>(asList(new Person("Tom", 24), new Person("Dick", 75), new Person("Harry", 17)));

        int legalAgeOfVoting = 18;
        List<Person> eligibleVoters = VotingRules.eligibleVoters(potentialVoters, legalAgeOfVoting);

        assertThat(eligibleVoters, hasSize(2));
        assertThat(eligibleVoters, containsInAnyOrder(aPersonNamed("Tom"), aPersonNamed("Dick")));

        // ensure we haven't modified the original collection
        assertThat(potentialVoters, hasSize(3));
        assertThat(potentialVoters, containsInAnyOrder(aPersonNamed("Tom"), aPersonNamed("Dick"), aPersonNamed("Harry")));
    }

    /**
     * <p>
     * Use Stream.filter() to find all the voters residing in a given district.
     * </p>
     * <p>
     * The resulting collection is to be used for quick lookups to find if a given
     * voter resides in a district. Performance measurements indicate we should
     * prefer the result to be a java.util.Set. Use Stream.collect() to produce a
     * Set containing the result, rather than a List.
     * </p>
     * <p>
     * HINT: sometimes type inference is "not there yet", in either the IDE or javac,
     * help out the compiler with explicit generic arguments if you have to.
     * </p>
     * @see Stream#collect(java.util.stream.Collector)
     * @see java.util.stream.Collectors#toSet()
     *
     * @see ElectoralDistrict#getPrefix()
     * @see RegisteredVoter#getElectorId()
     */
    @Test public void setOfVotersInDistrict() {
        List<RegisteredVoter> allVoters = new ArrayList<>(asList(
            new RegisteredVoter("CR2345"),
            new RegisteredVoter("HA7654"),
            new RegisteredVoter("HA2213"),
            new RegisteredVoter("BA9987"),
            new RegisteredVoter("CR6203"),
            new RegisteredVoter("ED9876")
            // ... and many more
        ));

        Set<RegisteredVoter> votersInHackney = ElectoralDistrict.votersIn(HACKNEY, allVoters);

        assertThat(votersInHackney, hasSize(2));
        assertThat(votersInHackney, containsInAnyOrder(aVoterWithId("HA7654"), aVoterWithId("HA2213")));
    }

    /**
     * Use Stream.filter() to remove all the ballots that have been spoiled.
     *
     * @see ElectoralDistrict#unspoiledBallots(Set)
     * @see Ballot#isSpoiled
     */
    @Test public void removeAllSpoiledBallots() {
        Set<Ballot> votes = new HashSet<>(asList(
                Ballot.voteFor(Party.LABOUR),
                Ballot.voteFor(Party.CONSERVATIVE),
                Ballot.spoiled(),
                Ballot.voteFor(Party.MONSTER_RAVING_LOONY_PARTY),
                Ballot.voteFor(Party.LIBERAL_DEMOCRATS),
                Ballot.spoiled(),
                Ballot.voteFor(Party.GREEN_PARTY),
                Ballot.voteFor(Party.GREEN_PARTY)
                // ... and many more
        ));

        Set<Ballot> unspoiledBallots = ElectoralDistrict.unspoiledBallots(votes);

        assertThat(unspoiledBallots, hasSize(6));
        assertThat(unspoiledBallots, everyItem(is(not(spoiled()))));
    }

    /**
     * <p>
     * Ensure that the Set returned cannot be modified by callers by wrapping the result
     * in Collections.unmodifiableSet().
     * </p>
     * @throws ClassNotFoundException If the lambdas binary build no longer contains the class
     * @see Stream#collect(java.util.stream.Collector)
     * @see Collectors#collectingAndThen(Collector, Function)
     * @see Collectors#toSet()
     * @see Collections#unmodifiableSet(java.util.Set)
     */
    @Test public void setOfVotersInDistrictInUnmodifiableSet() throws ClassNotFoundException {
        List<RegisteredVoter> allVoters = new ArrayList<>(asList(
            new RegisteredVoter("CR2345"),
            new RegisteredVoter("HA7654"),
            new RegisteredVoter("HA2213"),
            new RegisteredVoter("BA9987"),
            new RegisteredVoter("CR6203"),
            new RegisteredVoter("ED9876")
            // ... and many more
        ));

        Set<RegisteredVoter> votersInHackney = ElectoralDistrict.votersIn(HACKNEY, allVoters);

        assertThat(votersInHackney, instanceOf(Class.forName("java.util.Collections$UnmodifiableSet")));
    }

    // Test helpers

    private static Matcher<Person> aPersonNamed(String name) {
        return FeatureMatchers.from(is(name), "a person", "name", person -> person.getName());
    }

    private static Matcher<RegisteredVoter> aVoterWithId(String name) {
        return FeatureMatchers.from(is(name), "a voter", "electorId", voter -> voter.getElectorId());
    }

    private static Matcher<Ballot> spoiled() {
        return FeatureMatchers.from(equalTo(Boolean.TRUE), "a spoiled ballot", "isSpoiled", ballot -> ballot.isSpoiled());
    }

}
TOP

Related Classes of org.adoptopenjdk.lambda.tutorial.Exercise_2_Test

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.