├── .gitignore ├── README.md ├── files ├── mcdonalds.csv ├── movies-mpaa.txt ├── ospd.txt └── words.shakespeare.txt └── src └── org └── paumard ├── jdk8 ├── McDonalds.java ├── MovieV2.java ├── MovieV3.java ├── Movies.java └── Scrabble.java └── model ├── Actor.java ├── McDonald.java └── Movie.java /.gitignore: -------------------------------------------------------------------------------- 1 | /nbproject/ 2 | /manifest.mf 3 | /build.xml 4 | /bin 5 | /.classpath 6 | /.project 7 | /.settings 8 | /build/ 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This repository contains example files I use in my JDK8 / Lambdas / Streams / 2 | Collectors talks. 3 | 4 | The data files mcdonalds.csv, movies-mpaa.txt, ospd.txt and 5 | words.shakespeare.txt can be freely downloaded from Robert Sedgwick page 6 | here: http://introcs.cs.princeton.edu/java/data/. By the way, there are other 7 | very interesting data sets on this page. 8 | 9 | You can find 3 examples here : 10 | - the Scrabble example, or "how good at Scrabble Shakespeare would have been?" 11 | - the MacDonald example, or "Houston, we've got a problem" 12 | - the Movie database example. 13 | 14 | All files are provided under the GPL license. All data sets files are under the 15 | copyright of their authors, and provided for convenience only. 16 | 17 | More on "how good at Scrabble Shakespeare would have been". I realized that the 18 | best words are in fact not playable in Scrabble : not enough letters to 19 | place buzzards and whizzing on a Scrabble board. But there are two blanks 20 | letters in the Scrabble game. So let's take into account that in fact, buzzards 21 | and whizzing are doable with blank letters, and let's change the computation 22 | of the score to take into account that the blank letters score 0. 23 | 24 | Nice little problem, and it turns out that it is still solvable with a 25 | map / filter / reduce approach, thus fully lambda based. Great ! 26 | 27 | More on the Movie database example. 28 | 29 | A nice question I had on this example is : "and how about the most seen duo 30 | of actors". This question is pretty straigthforward if your actors are in 31 | a DB with a basic SQL engine. With the Stream API, it's a bit trickier, and 32 | cant be solved using a brute force method, due to the number of cases to 33 | evaluate. There's no Collector for that, to we have to build our own. And 34 | if we want to go parallel, we need to be careful about using concurrent 35 | structures. 36 | -------------------------------------------------------------------------------- /files/movies-mpaa.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JosePaumard/jdk8-lambda-tour/a423b13d208979185ef20d7b3fc69026efe43ee0/files/movies-mpaa.txt -------------------------------------------------------------------------------- /src/org/paumard/jdk8/McDonalds.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 José Paumard 3 | * 4 | * This program is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU General Public License 6 | * as published by the Free Software Foundation; either version 2 7 | * of the License, or (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program; if not, write to the Free Software 16 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 17 | */ 18 | 19 | package org.paumard.jdk8; 20 | 21 | import java.nio.file.Files; 22 | import java.nio.file.Paths; 23 | import java.util.List; 24 | import java.util.Map; 25 | import java.util.stream.Collectors; 26 | import java.util.stream.Stream; 27 | 28 | import org.paumard.model.McDonald; 29 | 30 | /** 31 | * 32 | * @author José Paumard 33 | */ 34 | public class McDonalds { 35 | 36 | public static void main(String... args) throws Exception { 37 | 38 | Stream lines = Files.lines(Paths.get("files", "mcdonalds.csv")) ; 39 | List mcdos = lines.map(s -> { 40 | // -149.95038,61.13712,"McDonalds-Anchorage,AK","3828 W Dimond Blvd, Anchorage,AK, (907) 248-0597" 41 | // -72.84817,41.27988,"McDonalds-Branford,CT","424 W Main St, Branford,CT, (203) 488-9353" 42 | String [] strings = s.split(",") ; 43 | McDonald mdo = new McDonald() ; 44 | mdo.setLatitude(Double.parseDouble(strings[0])) ; 45 | mdo.setLongitude(Double.parseDouble(strings[1])) ; 46 | mdo.setName(strings[2].substring(1) + strings[3].substring(0, strings[3].length() - 1)) ; 47 | mdo.setAddress(strings[4].substring(1)) ; 48 | mdo.setCity(strings[5].trim()) ; 49 | mdo.setState(strings[6].trim()) ; 50 | if (mdo.state().endsWith("\"")) { 51 | mdo.setState(mdo.state().substring(0, mdo.state().length() - 1)) ; 52 | } 53 | if (mdo.state().contains(" ")) { 54 | mdo.setState(mdo.state().substring(0, mdo.state().indexOf(" "))) ; 55 | } 56 | if (mdo.state().length() > 2) { 57 | mdo.setState(strings[7].trim()) ; 58 | } 59 | return mdo ; 60 | }).collect(Collectors.toList()) ; 61 | 62 | System.out.println("# of McDos = " + mcdos.size()) ; 63 | 64 | // The number of cities that have a McDonald 65 | long nTowns = 66 | mcdos.stream() 67 | .map(McDonald::city) 68 | .collect(Collectors.toSet()) 69 | .size() ; 70 | System.out.println("The number of cities that have a McDonald : " + nTowns) ; 71 | 72 | // The city has the most MacDonald 73 | Map.Entry entry = 74 | mcdos.stream() 75 | .collect( 76 | Collectors.groupingBy( 77 | McDonald::city, 78 | Collectors.counting() 79 | ) 80 | ) 81 | .entrySet() 82 | .stream() 83 | .max(Map.Entry.comparingByValue()) 84 | .get() ; 85 | System.out.println("The city has the most MacDonald : " + entry) ; 86 | 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/org/paumard/jdk8/MovieV2.java: -------------------------------------------------------------------------------- 1 | package org.paumard.jdk8; 2 | 3 | import java.nio.charset.Charset; 4 | import java.nio.file.Files; 5 | import java.nio.file.Paths; 6 | import java.util.Comparator; 7 | import java.util.HashMap; 8 | import java.util.HashSet; 9 | import java.util.Map; 10 | import java.util.Set; 11 | import java.util.concurrent.atomic.AtomicLong; 12 | import java.util.function.Function; 13 | import java.util.stream.Collector; 14 | import java.util.stream.Collectors; 15 | import java.util.stream.Stream; 16 | 17 | import org.paumard.model.Actor; 18 | import org.paumard.model.Movie; 19 | 20 | public class MovieV2 { 21 | 22 | public static void main(String... args) throws Exception { 23 | 24 | Set movies = new HashSet<>() ; 25 | 26 | Stream lines = 27 | Files.lines( 28 | Paths.get("files", "movies-mpaa.txt"), 29 | Charset.forName("windows-1252") 30 | ) ; 31 | 32 | lines.forEach( 33 | (String line) -> { 34 | String[] elements = line.split("/") ; 35 | String title = elements[0].substring(0, elements[0].toString().lastIndexOf("(")).trim() ; 36 | String releaseYear = elements[0].substring(elements[0].toString().lastIndexOf("(") + 1, elements[0].toString().lastIndexOf(")")) ; 37 | 38 | if (releaseYear.contains(",")) { 39 | // Movies with a coma in their title are discarded 40 | return ; 41 | } 42 | 43 | Movie movie = new Movie(title, Integer.valueOf(releaseYear)) ; 44 | 45 | for (int i = 1 ; i < elements.length ; i++) { 46 | String [] name = elements[i].split(", ") ; 47 | String lastName = name[0].trim() ; 48 | String firstName = "" ; 49 | if (name.length > 1) { 50 | firstName = name[1].trim() ; 51 | } 52 | 53 | Actor actor = new Actor(lastName, firstName) ; 54 | movie.addActor(actor) ; 55 | } 56 | 57 | movies.add(movie) ; 58 | } 59 | ) ; 60 | 61 | Set actors = 62 | movies.stream() 63 | .flatMap(movie -> movie.actors().stream()) 64 | .collect(Collectors.toSet()) ; 65 | 66 | System.out.println("# actors = " + actors.size()) ; 67 | System.out.println("# movies = " + movies.size()) ; 68 | 69 | // Actor that played in the greatest number of movies 70 | // Much faster version than the one in Movies, thanks to Celine ! 71 | Map.Entry entry3 = 72 | movies.stream() 73 | .flatMap(movie -> movie.actors().stream()) 74 | .collect( 75 | Collectors.groupingBy( 76 | Function.identity(), 77 | Collectors.counting() 78 | ) 79 | ) 80 | .entrySet() 81 | .stream() 82 | .max(Map.Entry.comparingByValue()) 83 | .get() ; 84 | System.out.println("Actor that played in the greatest number of movies : " + entry3) ; 85 | 86 | // Actor that played in the greatest number of movies during a year 87 | // Much faster version than the one in Movies 88 | Map.Entry> entry4 = 89 | movies.stream() 90 | .collect( 91 | Collectors.groupingBy( 92 | movie -> movie.releaseYear(), 93 | // Collector 94 | Collector.of( 95 | () -> new HashMap(), 96 | (map, movie) -> { 97 | movie.actors().forEach( 98 | actor -> map.computeIfAbsent(actor, a -> new AtomicLong()).incrementAndGet() 99 | ) ; 100 | }, 101 | (map1, map2) -> { 102 | map2.entrySet().stream().forEach( 103 | entry -> map1.computeIfAbsent(entry.getKey(), a -> new AtomicLong()).addAndGet(entry.getValue().get()) 104 | ) ; 105 | return map1 ; 106 | }, 107 | new Collector.Characteristics [] { 108 | Collector.Characteristics.CONCURRENT.CONCURRENT 109 | } 110 | ) 111 | ) 112 | ) 113 | .entrySet() 114 | .stream() 115 | .collect( 116 | Collectors.toMap( 117 | entry5 -> entry5.getKey(), 118 | entry5 -> entry5.getValue() 119 | .entrySet() 120 | .stream() 121 | .max(Map.Entry.comparingByValue(Comparator.comparing(l -> l.get()))) 122 | .get() 123 | ) 124 | ) 125 | .entrySet() 126 | .stream() 127 | .max(Comparator.comparing(entry -> entry.getValue().getValue().get())) 128 | .get() ; 129 | System.out.println("Actor that played in the greatest number of movies during a year : " + entry4) ; 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /src/org/paumard/jdk8/MovieV3.java: -------------------------------------------------------------------------------- 1 | package org.paumard.jdk8; 2 | 3 | import java.nio.charset.Charset; 4 | import java.nio.file.Files; 5 | import java.nio.file.Paths; 6 | import java.util.Comparator; 7 | import java.util.HashSet; 8 | import java.util.IntSummaryStatistics; 9 | import java.util.Map; 10 | import java.util.NavigableSet; 11 | import java.util.Set; 12 | import java.util.concurrent.ConcurrentHashMap; 13 | import java.util.concurrent.ConcurrentSkipListSet; 14 | import java.util.concurrent.atomic.AtomicLong; 15 | import java.util.function.Supplier; 16 | import java.util.stream.Collectors; 17 | import java.util.stream.Stream; 18 | 19 | import org.paumard.model.Actor; 20 | import org.paumard.model.Movie; 21 | 22 | public class MovieV3 { 23 | 24 | public static void main(String... args) throws Exception { 25 | 26 | Set movies = new HashSet<>() ; 27 | 28 | Stream lines = 29 | Files.lines( 30 | Paths.get("files", "movies-mpaa.txt"), 31 | Charset.forName("windows-1252") 32 | ) ; 33 | 34 | lines.forEach( 35 | (String line) -> { 36 | String[] elements = line.split("/") ; 37 | String title = elements[0].substring(0, elements[0].toString().lastIndexOf("(")).trim() ; 38 | String releaseYear = elements[0].substring(elements[0].toString().lastIndexOf("(") + 1, elements[0].toString().lastIndexOf(")")) ; 39 | 40 | if (releaseYear.contains(",")) { 41 | // with skip movies with a coma in their title 42 | return ; 43 | } 44 | 45 | Movie movie = new Movie(title, Integer.valueOf(releaseYear)) ; 46 | 47 | for (int i = 1 ; i < elements.length ; i++) { 48 | String [] name = elements[i].split(", ") ; 49 | String lastName = name[0].trim() ; 50 | String firstName = "" ; 51 | if (name.length > 1) { 52 | firstName = name[1].trim() ; 53 | } 54 | 55 | Actor actor = new Actor(lastName, firstName) ; 56 | movie.addActor(actor) ; 57 | } 58 | 59 | movies.add(movie) ; 60 | } 61 | ) ; 62 | 63 | Set actors = 64 | movies.stream() 65 | .flatMap(movie -> movie.actors().stream()) 66 | .collect(Collectors.toSet()) ; 67 | 68 | System.out.println("# actors = " + actors.size()) ; 69 | System.out.println("# movies = " + movies.size()) ; 70 | 71 | // number of production years 72 | int annees = 73 | movies.stream() 74 | .map(movie -> movie.releaseYear()) 75 | .collect(Collectors.toSet()) 76 | .size() ; 77 | System.out.println("# années = " + annees) ; 78 | 79 | // interval of production years 80 | IntSummaryStatistics stats = 81 | movies.stream() 82 | .mapToInt(movie -> movie.releaseYear()) 83 | .summaryStatistics() ; 84 | System.out.println("From " + stats.getMin() + " to " + stats.getMax()) ; 85 | 86 | long debut = System.currentTimeMillis() ; 87 | 88 | // Duo actor who played in more movies 89 | NavigableSet keyActors = 90 | new ConcurrentSkipListSet<>( 91 | // I love this new way of creating comparators ! 92 | Comparator.comparing(Actor::lastName).thenComparing(Actor::firstName) 93 | ) ; 94 | keyActors.addAll(actors) ; 95 | 96 | System.out.println("# Key actors = " + keyActors.size()) ; 97 | 98 | Supplier>> mapSupplier = () -> new ConcurrentHashMap<>() ; 99 | 100 | Map> map3 = 101 | keyActors.stream().parallel() 102 | .collect( 103 | mapSupplier, 104 | (Map> map, Actor actor) -> { 105 | NavigableSet valueActors = keyActors.tailSet(actor, false) ; 106 | movies.stream() 107 | .filter(movie -> movie.actors().contains(actor)) 108 | .forEach(movie -> {movie.actors().stream() 109 | .filter(actor1 -> valueActors.contains(actor1)) 110 | .forEach(actor2 -> { 111 | Map subMap = map.computeIfAbsent( 112 | actor, 113 | a -> new ConcurrentHashMap<>() 114 | ) ; 115 | subMap.computeIfAbsent( 116 | actor2, 117 | actor3 -> new AtomicLong(1L) 118 | ) 119 | .incrementAndGet() ; 120 | }) ; 121 | }) ; 122 | }, 123 | (Map> map1, Map> map2) -> { 124 | map2.entrySet().stream() 125 | .forEach( 126 | (Map.Entry> entry) -> { 127 | Map map11 = 128 | map1.computeIfAbsent(entry.getKey(), actor -> new ConcurrentHashMap<>()) ; 129 | Map map21 = 130 | entry.getValue() ; 131 | map21.entrySet().stream() 132 | .forEach( 133 | (Map.Entry entry21) -> { 134 | map11.merge( 135 | entry21.getKey(), entry21.getValue(), 136 | (AtomicLong l1, AtomicLong l2) -> { 137 | l1.addAndGet(l2.get()) ; 138 | return l1 ; 139 | } 140 | ) ; 141 | }) ; 142 | } 143 | ) ; 144 | } 145 | ) ; 146 | 147 | System.out.println("map 3 : " + map3.size()) ; 148 | 149 | Map.Entry> e = 150 | map3.entrySet().stream().parallel() 151 | .filter(entry -> !entry.getValue().entrySet().isEmpty()) 152 | .collect( 153 | Collectors.toMap( 154 | entry -> entry.getKey(), 155 | entry -> entry.getValue().entrySet().stream() 156 | .max( 157 | Map.Entry.comparingByValue( 158 | Comparator.comparingLong(AtomicLong::get) 159 | ) 160 | ) 161 | .get() 162 | ) 163 | ) 164 | .entrySet() 165 | .stream() 166 | .max( 167 | Map.Entry.comparingByValue( 168 | Comparator.comparingLong( 169 | entry -> entry.getValue().get() 170 | ) 171 | ) 172 | ) 173 | .get() ; 174 | System.out.println("Most seen actor duo = " + e) ; 175 | 176 | long fin = System.currentTimeMillis() ; 177 | System.out.println("T = " + (fin - debut) + "ms"); 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /src/org/paumard/jdk8/Movies.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 José Paumard 3 | * 4 | * This program is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU General Public License 6 | * as published by the Free Software Foundation; either version 2 7 | * of the License, or (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program; if not, write to the Free Software 16 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 17 | */ 18 | 19 | package org.paumard.jdk8; 20 | 21 | import java.nio.charset.Charset; 22 | import java.nio.file.Files; 23 | import java.nio.file.Paths; 24 | import java.util.Comparator; 25 | import java.util.HashSet; 26 | import java.util.IntSummaryStatistics; 27 | import java.util.Map; 28 | import java.util.Set; 29 | import java.util.function.Function; 30 | import java.util.stream.Collectors; 31 | import java.util.stream.Stream; 32 | import org.paumard.model.Actor; 33 | import org.paumard.model.Movie; 34 | 35 | /** 36 | * 37 | * @author José 38 | */ 39 | public class Movies { 40 | 41 | 42 | public static void main(String... args) throws Exception { 43 | 44 | Set movies = new HashSet<>() ; 45 | 46 | Stream lines = 47 | Files.lines( 48 | Paths.get("files", "movies-mpaa.txt"), 49 | Charset.forName("windows-1252") 50 | ) ; 51 | 52 | lines.forEach( 53 | (String line) -> { 54 | String[] elements = line.split("/") ; 55 | String title = elements[0].substring(0, elements[0].toString().lastIndexOf("(")).trim() ; 56 | String releaseYear = elements[0].substring(elements[0].toString().lastIndexOf("(") + 1, elements[0].toString().lastIndexOf(")")) ; 57 | 58 | if (releaseYear.contains(",")) { 59 | // Movies with a coma in their title are discarded 60 | return ; 61 | } 62 | 63 | Movie movie = new Movie(title, Integer.valueOf(releaseYear)) ; 64 | 65 | for (int i = 1 ; i < elements.length ; i++) { 66 | String [] name = elements[i].split(", ") ; 67 | String lastName = name[0].trim() ; 68 | String firstName = "" ; 69 | if (name.length > 1) { 70 | firstName = name[1].trim() ; 71 | } 72 | 73 | Actor actor = new Actor(lastName, firstName) ; 74 | movie.addActor(actor) ; 75 | } 76 | 77 | movies.add(movie) ; 78 | } 79 | ) ; 80 | 81 | Set actors = 82 | movies.stream() 83 | .flatMap(movie -> movie.actors().stream()) 84 | .collect(Collectors.toSet()) ; 85 | 86 | System.out.println("# actors = " + actors.size()) ; 87 | System.out.println("# movies = " + movies.size()) ; 88 | 89 | // number of release years 90 | int annees = 91 | movies.stream() 92 | .map(movie -> movie.releaseYear()) 93 | .collect(Collectors.toSet()) 94 | .size() ; 95 | System.err.println("Number of release years = " + annees) ; 96 | 97 | // Min and max of release years 98 | IntSummaryStatistics stats = 99 | movies.stream() 100 | .mapToInt(movie -> movie.releaseYear()) 101 | .summaryStatistics() ; 102 | System.err.println("From " + stats.getMin() + " to " + stats.getMax()) ; 103 | 104 | // Movie in which the greatest number of actors have played 105 | Map.Entry entry1 = 106 | movies.stream() 107 | .collect( 108 | Collectors.groupingBy( 109 | movie -> movie.actors().size(), 110 | Collectors.counting() 111 | ) 112 | ) 113 | .entrySet() 114 | .stream() 115 | .max(Map.Entry.comparingByValue()) 116 | .get() ; 117 | System.out.println("Movie in which the greatest number of actors have played : " + entry1) ; 118 | 119 | // Year with the greatest number of movies released 120 | Map.Entry entry2 = 121 | movies.stream() 122 | .collect( 123 | Collectors.groupingBy( 124 | movie -> movie.releaseYear(), 125 | Collectors.counting() 126 | ) 127 | ) 128 | .entrySet() 129 | .stream() 130 | .max(Map.Entry.comparingByValue()) 131 | .get() ; 132 | 133 | long debut = System.currentTimeMillis() ; 134 | System.out.println("Year with the greatest number of movies released : " + entry2) ; 135 | 136 | // Actor that played in the greatest number of movies 137 | Map.Entry entry3 = 138 | actors.stream().parallel() 139 | .collect( 140 | Collectors.toMap( 141 | Function.identity(), 142 | actor -> movies.stream().filter(movie -> movie.actors().contains(actor)).count() 143 | ) 144 | ) 145 | .entrySet() 146 | .stream() 147 | .max(Map.Entry.comparingByValue()) 148 | .get() ; 149 | System.out.println("Actor that played in the greatest number of movies : " + entry3) ; 150 | 151 | // Actor that played in the greatest number of movies during a year 152 | Map.Entry> entry4 = 153 | actors.stream().parallel() 154 | .collect( 155 | Collectors.toMap( 156 | Function.identity(), 157 | actor -> movies.stream() 158 | .filter(movie -> movie.actors().contains(actor)) 159 | .collect( 160 | Collectors.groupingBy( 161 | Movie::releaseYear, 162 | Collectors.counting() 163 | ) 164 | ) 165 | .entrySet() 166 | .stream() 167 | .max(Map.Entry.comparingByValue()) 168 | .get() 169 | ) 170 | ) 171 | .entrySet() 172 | .stream() 173 | .max(Comparator.comparing(entry -> entry.getValue().getValue())) 174 | .get() ; 175 | System.out.println("Actor that played in the greatest number of movies during a year : " + entry4) ; 176 | 177 | 178 | long fin = System.currentTimeMillis() ; 179 | System.err.println((fin - debut) + "ms"); 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /src/org/paumard/jdk8/Scrabble.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 José Paumard 3 | * 4 | * This program is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU General Public License 6 | * as published by the Free Software Foundation; either version 2 7 | * of the License, or (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program; if not, write to the Free Software 16 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 17 | */ 18 | 19 | package org.paumard.jdk8; 20 | 21 | import java.io.IOException; 22 | import java.nio.file.Files; 23 | import java.nio.file.Paths; 24 | import java.util.ArrayList; 25 | import java.util.Comparator; 26 | import java.util.HashMap; 27 | import java.util.HashSet; 28 | import java.util.List; 29 | import java.util.Map; 30 | import java.util.Set; 31 | import java.util.TreeMap; 32 | import java.util.function.Function; 33 | import java.util.function.IntUnaryOperator; 34 | import java.util.function.Predicate; 35 | import java.util.function.Supplier; 36 | import java.util.stream.Collectors; 37 | import java.util.stream.Stream; 38 | 39 | /** 40 | * 41 | * @author José 42 | */ 43 | @SuppressWarnings("unused") 44 | public class Scrabble { 45 | 46 | private static final int [] scrabbleENScore = { 47 | // a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z 48 | 1, 3, 3, 2, 1, 4, 2, 4, 1, 8, 5, 1, 3, 1, 1, 3, 10, 1, 1, 1, 1, 4, 4, 8, 4, 10} ; 49 | 50 | private static final int [] scrabbleENDistribution = { 51 | // a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z 52 | 9, 2, 2, 1, 12, 2, 3, 2, 9, 1, 1, 4, 2, 6, 8, 2, 1, 6, 4, 6, 4, 2, 2, 1, 2, 1} ; 53 | 54 | 55 | private static final int [] scrabbleFRScore = { 56 | // a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z 57 | 1, 3, 3, 2, 1, 4, 2, 4, 1, 8, 10, 1, 2, 1, 1, 3, 8, 1, 1, 1, 1, 4, 10, 10, 10, 10} ; 58 | 59 | private static final int [] scrabbleFRDistribution = { 60 | // a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z 61 | 9, 2, 2, 3, 15, 2, 2, 2, 8, 1, 1, 5, 3, 6, 6, 2, 1, 6, 6, 6, 6, 2, 1, 1, 1, 1} ; 62 | 63 | private static final IntUnaryOperator scrabbleLetterValueEN = 64 | letter -> scrabbleENScore[letter - 'a'] ; 65 | 66 | private static final IntUnaryOperator scrabbleLetterValueFR = 67 | letter -> scrabbleFRScore[letter - 'a'] ; 68 | 69 | 70 | public static void main(String... args) { 71 | 72 | Set scrabbleWords = null ; 73 | Set shakespeareWords = null ; 74 | try (Stream scrabbleWordsStream = Files.lines(Paths.get("files", "ospd.txt")) ; 75 | Stream shakespeareWordsStream = Files.lines(Paths.get("files", "words.shakespeare.txt")) ; 76 | ) { 77 | scrabbleWords = scrabbleWordsStream.map(String::toLowerCase).collect(Collectors.toSet()) ; 78 | shakespeareWords = shakespeareWordsStream.map(String::toLowerCase).collect(Collectors.toSet()) ; 79 | } catch (IOException e) { 80 | e.printStackTrace(); 81 | } 82 | 83 | System.out.println("# of words in the Scrabble dictionnary : " + scrabbleWords.size()) ; 84 | 85 | // number of words used by Shakespeare 86 | Long nWords1 = shakespeareWords.stream().count() ; 87 | System.out.println("# of words used by Shakespeare : " + nWords1) ; 88 | 89 | // number of words used by Shakespeare and allowed at Scrabble 90 | long count = 91 | shakespeareWords.stream() 92 | .filter(scrabbleWords::contains) 93 | .count() ; 94 | System.out.println("# number of words used by Shakespeare and allowed at Scrabble = " + count); 95 | 96 | // words of Shakespeare grouped by their length 97 | Map map1 = 98 | shakespeareWords.stream() 99 | .collect( 100 | Collectors.groupingBy( 101 | String::length, 102 | () -> new TreeMap(Comparator.naturalOrder().reversed()), 103 | Collectors.counting() 104 | ) 105 | ) ; 106 | System.out.println("Words of Shakespeare grouped by their length = " + map1) ; 107 | 108 | // words of Shakespeare of 16 letters and more 109 | Map> map2 = 110 | shakespeareWords.stream() 111 | .filter(word -> word.length() > 15) 112 | .collect( 113 | Collectors.groupingBy( 114 | String::length, 115 | () -> new TreeMap>(Comparator.naturalOrder().reversed()), 116 | Collectors.toList() 117 | ) 118 | ) ; 119 | System.out.println("Words of Shakespeare of 16 letters and more = " + map2) ; 120 | 121 | // # of words of Shakespeare grouped by their Scrabble score 122 | // in descending order 123 | Function score = word -> word.chars().map(scrabbleLetterValueEN).sum() ; 124 | Map map3 = 125 | shakespeareWords.stream() 126 | .filter(scrabbleWords::contains) 127 | .collect( 128 | Collectors.groupingBy( 129 | score, 130 | () -> new TreeMap(Comparator.naturalOrder().reversed()), 131 | Collectors.counting() 132 | ) 133 | ) ; 134 | System.out.println("# of words of Shakespeare grouped by their Scrabble score = " + map3) ; 135 | 136 | // words of Shakespeare grouped by their Scrabble score, with a score greater than 28 137 | // in ascending order 138 | Predicate scoreGT28 = word -> score.apply(word) > 28 ; 139 | Map> map4 = 140 | shakespeareWords.stream() 141 | .map(String::toLowerCase) 142 | .filter(scrabbleWords::contains) 143 | .filter(scoreGT28) 144 | .collect( 145 | Collectors.groupingBy( 146 | score, 147 | TreeMap::new, 148 | Collectors.toList() 149 | ) 150 | ) ; 151 | System.out.println("Words of Shakespeare grouped by their Scrabble score = " + map4) ; 152 | 153 | // histogram of the letters in a given word 154 | Function> lettersHisto = 155 | word -> word.chars() 156 | .boxed() 157 | .collect( 158 | Collectors.groupingBy( 159 | Function.identity(), 160 | Collectors.counting() 161 | ) 162 | ) ; 163 | 164 | // Predicate to check if a word can be written without the use of blanks 165 | Predicate noBlank = 166 | word -> lettersHisto.apply(word) 167 | .entrySet() 168 | .stream() // Map.Entry 169 | .allMatch( 170 | entry -> entry.getValue() <= 171 | scrabbleENDistribution[entry.getKey() - 'a'] 172 | ) ; 173 | System.out.println("Can we write buzzards without blanks? " + noBlank.test("buzzards")) ; 174 | System.out.println("Can we write whizzings without blanks? " + noBlank.test("whizzings")) ; 175 | 176 | // score of a given word, taking into account that the given word 177 | // might contain blank letters 178 | Function scoreWithBlanks = 179 | word -> lettersHisto.apply(word) 180 | .entrySet() 181 | .stream() // Map.Entry 182 | .mapToInt( 183 | entry -> scrabbleENScore[entry.getKey() - 'a']* 184 | (int)Long.min(entry.getValue(), scrabbleENDistribution[entry.getKey() - 'a']) 185 | ) 186 | .sum() ; 187 | 188 | // number of blanks used for the given word 189 | Function blanksUsed = 190 | word -> lettersHisto.apply(word) 191 | .entrySet() 192 | .stream() // Map.Entry 193 | .mapToInt( 194 | entry -> (int)Long.max(0L, entry.getValue() - scrabbleENDistribution[entry.getKey() - 'a']) 195 | ) 196 | .sum() ; 197 | 198 | System.out.println("Number of blanks in [buzzards] = " + blanksUsed.apply("buzzards")) ; 199 | System.out.println("Real score of [buzzards] = " + scoreWithBlanks.apply("buzzards")) ; 200 | System.out.println("Number of blanks in [whizzing] = " + blanksUsed.apply("whizzing")) ; 201 | System.out.println("Real score of [whizzing] = " + scoreWithBlanks.apply("whizzing")) ; 202 | 203 | // best words of Shakespeare and their scores 204 | Map> map = 205 | shakespeareWords.stream() 206 | .filter(scrabbleWords::contains) 207 | .filter(word -> blanksUsed.apply(word) <= 2L) 208 | .filter(word -> scoreWithBlanks.apply(word) >= 24) 209 | .collect( 210 | Collectors.groupingBy( 211 | scoreWithBlanks, 212 | Collectors.toList() 213 | ) 214 | ) ; 215 | System.out.println("Best words of Shakespeare : " + map) ; 216 | 217 | // best word that Shakespeare could have played as a first move 218 | // scoring function 219 | Function scoreOnBoard = 220 | word -> 2*( // the first word scores double at Scrabble 221 | scoreWithBlanks.apply(word) + 222 | Stream.of( 223 | word.chars().skip(4), 224 | word.chars().limit(Integer.max(0, word.length() - 4)) 225 | ) 226 | .flatMapToInt(Function.identity()) 227 | .map(scrabbleLetterValueEN) 228 | .max() 229 | .orElse(0) 230 | ) + 231 | (word.length() == 7 ? 50 : 0) ; // there is a 50 pts bonus for a 7 letters word 232 | Map> mapOnBoard = 233 | shakespeareWords.stream() 234 | .filter(scrabbleWords::contains) 235 | .filter(word -> blanksUsed.apply(word) <= 2L) 236 | .filter(word -> scoreOnBoard.apply(word ) >= 114) 237 | .collect( 238 | Collectors.groupingBy( 239 | scoreOnBoard, 240 | () -> new TreeMap>(Comparator.naturalOrder().reversed()), 241 | Collectors.toList() 242 | ) 243 | ) ; 244 | System.out.println("Best words of Shakespeare played as first move : " + mapOnBoard) ; 245 | 246 | } 247 | } 248 | -------------------------------------------------------------------------------- /src/org/paumard/model/Actor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 José Paumard 3 | * 4 | * This program is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU General Public License 6 | * as published by the Free Software Foundation; either version 2 7 | * of the License, or (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program; if not, write to the Free Software 16 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 17 | */ 18 | 19 | package org.paumard.model; 20 | 21 | import java.util.Objects; 22 | 23 | /** 24 | * 25 | * @author José 26 | */ 27 | public class Actor { 28 | public String lastName, firstName ; 29 | 30 | public Actor(String lastName, String firstName) { 31 | this.lastName = lastName; 32 | this.firstName = firstName; 33 | } 34 | 35 | public String lastName() { 36 | return this.lastName ; 37 | } 38 | 39 | public String firstName() { 40 | return this.firstName ; 41 | } 42 | 43 | @Override 44 | public int hashCode() { 45 | int hash = 7; 46 | hash = 67 * hash + Objects.hashCode(this.lastName); 47 | hash = 67 * hash + Objects.hashCode(this.firstName); 48 | return hash; 49 | } 50 | 51 | @Override 52 | public boolean equals(Object obj) { 53 | if (obj == null) { 54 | return false; 55 | } 56 | if (getClass() != obj.getClass()) { 57 | return false; 58 | } 59 | final Actor other = (Actor) obj; 60 | if (!Objects.equals(this.lastName, other.lastName)) { 61 | return false; 62 | } 63 | return Objects.equals(this.firstName, other.firstName); 64 | } 65 | 66 | @Override 67 | public String toString() { 68 | return "Actor{" + "lastName=" + lastName + ", firstName=" + firstName + '}'; 69 | } 70 | } 71 | 72 | -------------------------------------------------------------------------------- /src/org/paumard/model/McDonald.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 José Paumard 3 | * 4 | * This program is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU General Public License 6 | * as published by the Free Software Foundation; either version 2 7 | * of the License, or (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program; if not, write to the Free Software 16 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 17 | */ 18 | 19 | package org.paumard.model; 20 | 21 | /** 22 | * 23 | * @author José 24 | */ 25 | public class McDonald { 26 | private double latitude, longitude ; 27 | private String name, address, city, state ; 28 | 29 | 30 | public void setLatitude(double latitude) { 31 | this.latitude = latitude; 32 | } 33 | 34 | public void setLongitude(double longitude) { 35 | this.longitude = longitude; 36 | } 37 | 38 | public void setName(String name) { 39 | this.name = name; 40 | } 41 | 42 | public void setAddress(String address) { 43 | this.address = address; 44 | } 45 | 46 | public void setCity(String city) { 47 | this.city = city; 48 | } 49 | 50 | public void setState(String state) { 51 | this.state = state; 52 | } 53 | 54 | 55 | 56 | public String state() { 57 | return this.state ; 58 | } 59 | 60 | public String city() { 61 | return this.city ; 62 | } 63 | 64 | public double latitude() { 65 | return latitude; 66 | } 67 | 68 | public double longitude() { 69 | return longitude; 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /src/org/paumard/model/Movie.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 José Paumard 3 | * 4 | * This program is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU General Public License 6 | * as published by the Free Software Foundation; either version 2 7 | * of the License, or (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program; if not, write to the Free Software 16 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 17 | */ 18 | 19 | package org.paumard.model; 20 | 21 | import java.util.HashSet; 22 | import java.util.Set; 23 | 24 | /** 25 | * 26 | * @author José 27 | */ 28 | public class Movie { 29 | private String title ; 30 | private int releaseYear ; 31 | 32 | private Set actors = new HashSet<>() ; 33 | 34 | public Movie(String title, int releaseYear) { 35 | this.title = title; 36 | this.releaseYear = releaseYear; 37 | } 38 | 39 | public String title() { 40 | return this.title ; 41 | } 42 | 43 | public int releaseYear() { 44 | return this.releaseYear ; 45 | } 46 | 47 | public void addActor(Actor actor) { 48 | this.actors.add(actor) ; 49 | } 50 | 51 | public Set actors() { 52 | return this.actors ; 53 | } 54 | 55 | @Override 56 | public String toString() { 57 | return "Movie{" + "title=" + title + ", releaseYear=" + releaseYear + ", actors=" + actors + '}'; 58 | } 59 | } 60 | --------------------------------------------------------------------------------