Annotated Solution Guide forThinking in Java Fourth Edition TIJ4 published February, 2006 Solution Guide published September 2007 Bruce Eckel President, MindView, Inc. Ervin Varga Ph.D. Computer Science, University of Novi Sad, Serbia; Faculty of Technical Sciences ©2007, MindView, Inc. All Rights Reserved Copyright & Disclaimer This Annotated Solution Guide for Thinking in Java, Fourth Edition is not freeware. You cannot post it on any website, reproduce or distribute it, display it publicly (such as on overhead slides), or make it the basis of any derivative work. Copyrighted by MindView, Inc., this publication is sold only at www.MindView.net. The Source Code in this book is provided without express or implied warranty of any kind, including any implied warranty of merchantability, fitness for a particular purpose or non-infringement. MindView, Inc. does not warrant that the operation of any program that includes the Source Code will be uninterrupted or error-free. MindView, Inc. makes no representation about the suitability of the Source Code or of any software that includes the Source Code for any purpose. The entire risk as to the quality and performance of any program that includes the Source Code is with the user of the Source Code. The user understands that the Source Code was developed for research and instructional purposes and is advised not to rely exclusively for any reason on the Source Code or any program that includes the Source Code. Should the Source Code or any resulting software prove defective, the user assumes the cost of all necessary servicing, repair, or correction. IN NO EVENT SHALL MINDVIEW, INC., OR ITS PUBLISHER BE LIABLE TO ANY PARTY UNDER ANY LEGAL THEORY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, BUSINESS INTERRUPTION, LOSS OF BUSINESS INFORMATION, OR ANY OTHER PECUNIARY LOSS, OR FOR PERSONAL INJURIES, ARISING OUT OF THE USE OF THIS SOURCE CODE AND ITS DOCUMENTATION, OR ARISING OUT OF THE INABILITY TO USE ANY RESULTING PROGRAM, EVEN IF MINDVIEW, INC., OR ITS PUBLISHER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. MINDVIEW, INC. SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOURCE CODE AND DOCUMENTATION PROVIDED HEREUNDER IS ON AN “AS IS” BASIS, WITHOUT ANY ACCOMPANYING SERVICES FROM MINDVIEW, INC., AND MINDVIEW, INC. HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. i About this Document This is the annotated solution guide for Thinking in Java, Fourth Edition. Thinking in Java, Fourth Edition is available in print from Prentice Hall and for sale electronically from www.mindview.net. This solution guide is only available online as a PDF document along with the associated source code (if printed in the same format as Thinking in Java, this guide would be almost 900 pages). You can download a free sample of this solution guide, up to and including the solutions for the chapters Everything is an Object and Operators, from http://www.mindviewinc.com/Books/TIJ4/Solutions. You should do this before buying the guide to make sure that you can properly install, compile and run the solutions on your system. The complete version of this book including all exercises is only available electronically, for US $25 (credit cards or PayPal), from http://www.mindviewinc.com/Books/TIJ4/Solutions/. Unpacking the Distribution The Annotated Solutions Guide is distributed as a zipped file containing the book in Adobe Acrobat PDF format, along with a source-code tree in the code.zip file. Please make sure your computer can unzip files before purchasing the guide. There are free unzip utilities for virtually every platform, available by searching on the Internet. Linux/Unix (including Mac OSX) Users: Unzip the file using InfoZip (pre-installed on many Linux distributions, or available at http://www.infozip.org/). Unzip the package for Linux/Unix using the –a option to correct for the difference between DOS and Unix newlines, like this: unzip –a TIJ4-solutions.zip All Users: This guide uses the Ant build system, and includes Ant build.xml files in each subdirectory of the code tree, which compile and run the code examples using the javac compiler in the Sun JDK (available at http://java.sun.com/j2se/). Ant, the standard build tool for Java projects, is an open-source tool. The full download, installation and configuration instructions, along with the Ant executable and documentation are available at http://ant.apache.org/. iii Once you install and configure Ant on your computer, you can type ant at the command prompt of the guide’s source-code root directory to build and test all the code. You can also choose to build and test the code for a particular chapter. For example, to build and test the code from the Polymorphism chapter, enter the polymorphism sub-directory and type ant from there. Full detailed instructions for installation can be found after the table of contents. No Exercises in Chapter 1 The chapter Introduction to Objects has no exercises. Additional exercises This guide features additional exercises not included in Thinking in Java, for which solutions are not provided, as a challenge to the reader. iv Thinking in Java, 4th Edition Annotated Solution Guide Contents Copyright & Disclamer About this Document i iii Unpacking the Distribution.................. iii No Exercises in Chapter 1.. iv Additional exercises........... iv Installing the Code Packages & IDEs Left to the Reader Everything is an Object 1 5 7 9 Using Eclipse....................... 3 Exercise 1............................. 9 Exercise 2 ............................ 9 Exercise 3 ...........................10 Exercise 4 ...........................10 Exercise 5 ........................... 11 Exercise 6 ........................... 11 Exercise 7 ...........................12 Exercise 8 ...........................13 Exercise 9 ...........................13 Exercise 10 .........................14 Exercise 11 ..........................16 Exercise 12 .........................16 Exercise 13.......................... 17 Exercise 14 ......................... 17 Exercise 15..........................18 Exercise 16 .........................18 Operators 21 Exercise 1............................21 Exercise 2 ...........................21 Exercise 3 .......................... 22 Exercise 4 .......................... 23 Exercise 5 .......................... 24 Exercise 6 .......................... 24 Exercise 7 .......................... 26 v Exercise 8 .......................... 27 Exercise 9 .......................... 27 Exercise 10 ........................ 28 Exercise 11 ......................... 29 Exercise 12 .........................31 Exercise 13......................... 33 Exercise 14 ........................ 33 Controlling Execution 35 Exercise 1........................... 35 Exercise 2 .......................... 35 Exercise 3 .......................... 37 Exercise 4 .......................... 38 Exercise 5 .......................... 39 Exercise 6 ...........................41 Exercise 7 .......................... 42 Exercise 8 .......................... 43 Exercise 9 .......................... 45 Exercise 10 ........................ 46 Initialization & Cleanup 49 Exercise 1........................... 49 Exercise 2 .......................... 49 Exercise 3 .......................... 50 Exercise 4 ...........................51 Exercise 5 ...........................51 Exercise 6 .......................... 52 Exercise 7 .......................... 53 Exercise 8 .......................... 54 Exercise 9 .......................... 54 Exercise 10 ........................ 55 Exercise 11 ......................... 55 Exercise 12 ........................ 56 Exercise 13..........................57 Exercise 14 ........................ 58 Exercise 15......................... 58 Exercise 16 ........................ 59 Exercise 17......................... 60 Exercise 18 .........................61 Exercise 19 .........................61 Exercise 20........................ 62 Exercise 21 ........................ 63 Exercise 22 ........................ 63 vi Thinking in Java, 4th Edition Annotated Solution Guide Access Control 65 Exercise 1........................... 65 Exercise 2 .......................... 65 Exercise 3 .......................... 66 Exercise 4 .......................... 67 Exercise 5 .......................... 68 Exercise 6 .......................... 70 Exercise 7 .......................... 70 Exercise 8 ........................... 71 Exercise 9 ...........................75 Reusing Classes 77 Exercise 1............................77 Exercise 2 .......................... 78 Exercise 3 .......................... 79 Exercise 4 .......................... 80 Exercise 5 ...........................81 Exercise 6 ...........................81 Exercise 7 .......................... 82 Exercise 8 .......................... 83 Exercise 9 .......................... 84 Exercise 10 ........................ 85 Exercise 11 ......................... 86 Exercise 12 ........................ 87 Exercise 13......................... 89 Exercise 14 ........................ 90 Exercise 15..........................91 Exercise 16 ........................ 92 Exercise 17......................... 92 Exercise 18 ........................ 93 Exercise 19 ........................ 94 Exercise 20........................ 95 Exercise 21 ........................ 96 Exercise 22 ........................ 96 Exercise 23 ........................ 97 Exercise 24 ........................ 98 Polymorphism 99 Exercise 1........................... 99 Exercise 2 ........................ 100 Exercise 3 ........................ 102 Exercise 4 ........................ 104 Exercise 5 .........................105 Exercise 6 ........................ 106 Contents vii Exercise 7 ........................ 108 Exercise 8 ........................ 109 Exercise 9 ..........................111 Exercise 10 ....................... 113 Exercise 11 ........................ 114 Exercise 12 ....................... 115 Exercise 13........................ 116 Exercise 14 ....................... 118 Exercise 15........................ 121 Exercise 16 .......................122 Exercise 17........................123 Interfaces 125 Exercise 1..........................125 Exercise 2 .........................126 Exercise 3 .........................127 Exercise 4 ........................ 128 Exercise 5 .........................129 Exercise 6 ........................ 130 Exercise 7 ........................ 130 Exercise 8 .........................132 Exercise 9 .........................133 Exercise 10 .......................135 Exercise 11 ........................136 Exercise 12 .......................138 Exercise 13........................139 Exercise 14 ...................... 140 Exercise 15........................142 Exercise 16 .......................143 Exercise 17........................145 Exercise 18 .......................146 Exercise 19 .......................147 Inner Classes 149 Exercise 1..........................149 Exercise 2 .........................149 Exercise 3 .........................150 Exercise 4 ......................... 151 Exercise 5 .........................152 Exercise 6 .........................152 Exercise 7 .........................153 Exercise 8 .........................154 Exercise 9 .........................155 Exercise 10 .......................156 viii Thinking in Java, 4th Edition Annotated Solution Guide Exercise 11 ........................ 157 Exercise 12 .......................158 Exercise 13........................158 Exercise 14 .......................159 Exercise 15....................... 160 Exercise 16 ....................... 161 Exercise 17........................162 Exercise 18 .......................163 Exercise 19 .......................164 Exercise 20.......................165 Exercise 21 .......................166 Exercise 22 .......................167 Exercise 23 ...................... 168 Exercise 24 .......................170 Exercise 25 .......................174 Exercise 26 ....................... 175 Holding Your Objects 179 Exercise 1..........................179 Exercise 2 ........................ 180 Exercise 3 ........................ 180 Exercise 4 ......................... 181 Exercise 5 .........................183 Exercise 6 .........................185 Exercise 7 .........................187 Exercise 8 ........................ 188 Exercise 9 ........................ 189 Exercise 10 ...................... 190 Exercise 11 ........................ 191 Exercise 12 .......................192 Exercise 13........................193 Exercise 14 .......................197 Exercise 15........................197 Exercise 16 ...................... 198 Exercise 17........................199 Exercise 18 ......................200 Exercise 19 ...................... 201 Exercise 20...................... 202 Exercise 21 ...................... 203 Exercise 22 ...................... 204 Exercise 23 ...................... 206 Exercise 24 ......................208 Exercise 25 ...................... 209 Exercise 26 ...................... 210 Contents ix Exercise 27 .......................212 Exercise 28.......................214 Exercise 29 .......................214 Exercise 30.......................215 Exercise 31........................217 Exercise 32 .......................219 Error Handling with Exceptions 221 Exercise 1..........................221 Exercise 2 .........................221 Exercise 3 ........................ 222 Exercise 4 ........................ 223 Exercise 5 ........................ 224 Exercise 6 ........................ 225 Exercise 7 ........................ 226 Exercise 8 ........................ 227 Exercise 9 ........................ 228 Exercise 10 ...................... 229 Exercise 11 ....................... 229 Exercise 12 ...................... 230 Exercise 13........................231 Exercise 14 ...................... 232 Exercise 15....................... 233 Exercise 16 ...................... 234 Exercise 17....................... 235 Exercise 18 ...................... 237 Exercise 19 ...................... 238 Exercise 20...................... 239 Exercise 21 .......................241 Exercise 22 ...................... 242 Exercise 23 ...................... 243 Exercise 24 ...................... 245 Exercise 25 ...................... 246 Exercise 26 ...................... 247 Exercise 27 ...................... 248 Exercise 28...................... 248 Exercise 29 ...................... 249 Exercise 30...................... 250 Strings 253 Exercise 1......................... 253 Exercise 2 ........................ 254 Exercise 3 ........................ 255 x Thinking in Java, 4th Edition Annotated Solution Guide Exercise 4 ........................ 256 Exercise 5 ........................ 257 Exercise 6 ........................ 260 Exercise 7 .........................261 Exercise 8 ........................ 262 Exercise 9 ........................ 262 Exercise 10 ...................... 263 Exercise 11 ....................... 266 Exercise 12 ...................... 267 Exercise 13....................... 268 Exercise 14 ...................... 270 Exercise 15....................... 270 Exercise 16 ...................... 272 Exercise 17....................... 273 Alternative A ...............................274 Alternative B ...............................275 Exercise 18 ...................... 278 Alternative A ...............................278 Alternative B ...............................279 Exercise 19 ......................280 Alternative A .............................. 280 Alternative B ...............................281 Exercise 20...................... 282 Type Information 285 Exercise 1......................... 285 Exercise 2 ........................ 286 Exercise 3 ........................288 Exercise 4 ........................288 Exercise 5 ........................ 289 Exercise 6 ........................ 290 Exercise 7 ........................ 293 Exercise 8 ........................ 294 Exercise 9 ........................ 295 Exercise 10 ...................... 298 Exercise 11 ....................... 299 Exercise 12 ...................... 303 Exercise 13....................... 303 Exercise 14 ...................... 304 Exercise 15....................... 306 Exercise 16 ...................... 309 Exercise 17........................312 Exercise 18 .......................313 Exercise 19 .......................315 Contents xi Exercise 20.......................316 Exercise 21 .......................317 Exercise 22 .......................318 Exercise 23 ...................... 320 Exercise 24 .......................321 Exercise 25 ...................... 324 Exercise 26 ...................... 325 Generics 329 Exercise 1......................... 329 Exercise 2 ........................ 329 Exercise 3 ........................ 330 Exercise 4 .........................331 Exercise 5 ........................ 332 Exercise 6 ........................ 333 Exercise 7 ........................ 334 Exercise 8 ........................ 335 Exercise 9 ........................ 337 Exercise 10 ...................... 338 Exercise 11 ....................... 338 Exercise 12 ...................... 339 Exercise 13....................... 340 Exercise 14 .......................341 Exercise 15....................... 342 Exercise 16 ...................... 343 Exercise 17....................... 344 Exercise 18 ...................... 346 Exercise 19 ...................... 347 Exercise 20...................... 349 Exercise 21 ...................... 350 Exercise 22 .......................351 Exercise 23 ...................... 352 Exercise 24 ...................... 353 Exercise 25 ...................... 354 Exercise 26 ...................... 355 Exercise 27 ...................... 356 Exercise 28...................... 356 Exercise 29 ...................... 357 Exercise 30...................... 359 Exercise 31....................... 360 Exercise 32 ...................... 360 Exercise 33 .......................361 Exercise 34 ...................... 363 Exercise 35 ...................... 364 xii Thinking in Java, 4th Edition Annotated Solution Guide Exercise 36 ...................... 365 Exercise 37 ...................... 367 Exercise 38 ...................... 368 Exercise 39 ...................... 370 Exercise 40...................... 370 Exercise 41 ...................... 372 Exercise 42 ...................... 373 Arrays 377 Exercise 1..........................377 Exercise 2 ........................ 378 Exercise 3 ........................ 378 Exercise 4 ........................380 Exercise 5 ........................ 383 Exercise 6 ........................ 384 Exercise 7 ........................ 384 Exercise 8 ........................ 385 Exercise 9 ........................ 386 Exercise 10 ...................... 387 Exercise 11 ....................... 387 Exercise 12 ...................... 388 Exercise 13....................... 388 Exercise 14 ...................... 389 Exercise 15....................... 390 Exercise 16 ...................... 392 Exercise 17....................... 395 Exercise 18 ...................... 396 Exercise 19 ...................... 397 Exercise 20...................... 398 Exercise 21 ...................... 399 Exercise 22 ...................... 401 Exercise 23 ...................... 402 Exercise 24 ...................... 403 Exercise 25 ...................... 404 Containers in Depth 407 Exercise 1......................... 407 Exercise 2 ........................408 Exercise 3 ........................ 409 Exercise 4 ........................ 409 Exercise 5 ........................ 409 Exercise 6 ......................... 411 Exercise 7 .........................413 Exercise 8 .........................414 Contents xiii .................................. 458 Exercise 30.................... 476 Exercise 37 .............. 418 Exercise 10 ... 439 Exercise 22 .........................................................................Exercise 9 ..... 430 Exercise 17............ 426 Exercise 14 ................. 464 Exercise 32 ....... 443 Exercise 25 ... 434 Exercise 18 ...................... 428 Exercise 15............ 434 Exercise 19 ..... 466 Exercise 33 .. 449 Exercise 27 ........ 489 Exercise 41 .................................. 499 Exercise 4 ..........500 Exercise 5 ........... 424 Exercise 12 ............... 486 Exercise 40...........451 Exercise 28............................................. 472 Exercise 35 ................ 435 Exercise 20.................. 481 Exercise 38 .................................................. 492 Exercise 42 ........................ 425 Exercise 13.................. 497 Exercise 2 .... 467 Exercise 34 .441 Exercise 24 .......419 Exercise 11 ........ 498 Exercise 3 ........................ 504 xiv Thinking in Java..................... 4th Edition Annotated Solution Guide ........................................................... 484 Exercise 39 .............. 502 Exercise 7 ...... 440 Exercise 23 .......... 494 I/O 497 Exercise 1...........................................501 Exercise 6 ............................................ 429 Exercise 16 . 453 Exercise 29 ............ 503 Exercise 8 ................................................................................... 436 Exercise 21 ............ 462 Exercise 31............................................................................................................ 445 Exercise 26 ....... 474 Exercise 36 . .................... 525 Exercise 25 ................. 524 Exercise 24 .............................................. 522 Exercise 23 ..... 578 Exercise 3 ..575 Exercise 2 .............................. 543 Exercise 33 .. 548 Exercise 3 ...............514 Exercise 16 ................................................................. 537 Exercise 31............. 559 Exercise 10 ................ 584 Contents xv ............ 506 Exercise 12 ................513 Exercise 15............... 552 Exercise 6 .. 505 Exercise 11 ...............531 Exercise 28............................. 554 Exercise 7 ................................................. 505 Exercise 10 ..... 535 Exercise 30...... 555 Exercise 9 ......................................................512 Exercise 14 ...... 544 Enumerated Types 547 Exercise 1............................... 554 Exercise 8 ............................... 530 Exercise 27 ..... 517 Exercise 18 ........................................518 Exercise 19 ................................ 550 Exercise 5 .... 520 Exercise 20........................ 562 Exercise 11 ......... 522 Exercise 22 ....................... 540 Exercise 32 ........ 567 Annotations 575 Exercise 1.......................................581 Exercise 4 ........ 511 Exercise 13..................................... 549 Exercise 4 ....................................................515 Exercise 17.......... 525 Exercise 26 .............................................................................................. 547 Exercise 2 .......... 533 Exercise 29 .......Exercise 9 ................521 Exercise 21 ........ .............. 590 Concurrency 597 Exercise 1.................................................................................................... 4th Edition Annotated Solution Guide ...... 650 Exercise 31................................... 640 Exercise 28............................................................... 655 Exercise 33 ....................................................... 637 Exercise 27 ..609 Exercise 12 .. 603 Exercise 7 . 585 Exercise 7 .......................................... 623 Exercise 19 ............................................... 628 Exercise 23 ................................. 611 Exercise 13.................. 597 Exercise 2 . 588 Exercise 10 .................614 Exercise 16 ............ 635 Exercise 26 ................ 587 Exercise 9 ............ 599 Exercise 4 ................................612 Exercise 14 .. 657 Exercise 34 ....................................... 652 Exercise 32 .......................... 645 Exercise 30.....613 Exercise 15............ 589 Exercise 11 .... 664 xvi Thinking in Java...... 598 Exercise 3 .. 604 Exercise 8 ............................................... 605 Exercise 9 . 643 Exercise 29 ............ 626 Exercise 22 ................. 585 Exercise 6 ......... 625 Exercise 21 ..........600 Exercise 5 ...................................621 Exercise 18 ... 605 Exercise 10 ............ 607 Exercise 11 ............................................................................................................. 630 Exercise 24 ....... 623 Exercise 20....... 662 Exercise 35 ........Exercise 5 .......617 Exercise 17................. 632 Exercise 25 ......................................... 586 Exercise 8 ........... 601 Exercise 6 .......... ................. 698 Exercise 4 ................................................................................... 697 Exercise 2 .. 715 Exercise 16 ..... 745 Exercise 33 ................... 708 Exercise 11 ....... 743 Exercise 31.... 676 Exercise 38 .................................. 694 Graphical User Interfaces 697 Exercise 1........................................ 749 Contents xvii ...........................714 Exercise 15..................... 704 Exercise 9 .......... 702 Exercise 8 .............................................................737 Exercise 27 ..........719 Exercise 19 ..................................................... 697 Exercise 3 .............................. 734 Exercise 26 ................................................................................... 682 Exercise 39 ....... 740 Exercise 29 .......................................... 738 Exercise 28......... 699 Exercise 5 ......................... 746 Exercise 34 ... 728 Exercise 23 ................712 Exercise 14 .........................Exercise 36 ..... 742 Exercise 30.......... 668 Exercise 37 .......716 Exercise 17......................... 724 Exercise 21 ............690 Exercise 41 ........................................... 692 Exercise 42 .......... 729 Exercise 24 ......... 744 Exercise 32 ............................................. 705 Exercise 10 .................... 709 Exercise 12 ................ 700 Exercise 6 .731 Exercise 25 ....................... 726 Exercise 22 ....701 Exercise 7 ................. 720 Exercise 20....................718 Exercise 18 ..............710 Exercise 13........ 687 Exercise 40............... ............. 751 Exercise 36 ....... 754 Exercise 42 .... 751 Exercise 37 .............. 754 Exercise 41 ..................................... 752 Exercise 38 .......................................755 Exercise 43 ......... 4th Edition Annotated Solution Guide ...................................Exercise 35 . 758 xviii Thinking in Java............. 753 Exercise 39 .......... 753 Exercise 40..................... add the bin directory from your Java installation into your 3. 5... If there is no “CLASSPATH” variable. you should see numerous subdirectories in the C:\TIJ4-Solutions\code directory. Using WinZip or some other zip utility (if one is not preinstalled. To verify that your classpath has been set. double click it and add .Installing the Code Detailed instructions for installing.. click the “New” button and enter CLASSPATH In the “Variable name” box. but for PATH instead of CLASSPATH.” look to see if there’s already a “CLASSPATH” variable. but they will also act as a guide for OSX and Linux installations. 1 . For Windows machines.. You’ll also eventually want the documentation. In the “Variable value” box. right-click on the “My Computer” icon and select “Properties. search the web for a free utility).sun.com/javase/downloads/index. Using the same technique as in Step 4.. 1. Set the CLASSPATH in your computer’s environment. When you’re done. Create a directory called C:\TIJ4-Solutions\code. Install the Java SE Development Kit (JDK). These instructions also work with the free demo version of the guide. which is available from the same site. then click “OK”. from the download site at Sun (http://java. to the end of the current entry.” Then select the “Advanced” tab and click the “Environment Variables” button at the bottom. start a command prompt (see below). and . 2. then enter set and look for the CLASSPATH information in the output. extract the zip file containing the code that you received when you purchased the guide. Under “System Variables. 4. configuring and testing the source code..C:\TIJ4-Solutions\code... If there is. version 5 or newer.C:\TIJ4-Solutions\code.jsp).. Unzip it into the C:\TIJ4-Solutions\code directory. These instructions describe a Windows installation. including subdirectories corresponding to the chapters in the solution guide. Start a command prompt in the C:\TIJ4. Install the Ant 1.eclipse.jar (download here: http://sourceforge. after you put the javassist. Click on the most recent build number. under the heading “Installing SWT. but you must explicitly add it to your classpath. To compile a file called MyProgram.java.java. as described in Step 4) using the javac command-line compiler that was installed when you completed the steps 3 and 5.cafeconleche. which is actually part of the JDK. Create a directory called C:\jars. the default JDK installation path is under “C:\Program Files” and because this has spaces. also part of the JDK. Once you successfully run ‘ant build’ in the root directory.system’s PATH environment variable. You must explicitly add each of the Jar files to your CLASSPATH.jar. then select “Run” and type “cmd” and press “OK. press the “Start” button. you must quote that directory when adding it to the PATH: C:\"Program Files"\Java\bin.php?group_id=22866.apache. For example. the associated CLASSPATH entry is C:\jars\javassist. xom. you may need to search for it).. 9.Solutions\code directory.jar.jar. However. in the /jre/lib/ directory. Note: Ant is required in order to compile the examples in the book.jar. • • • • 7.” tools.” then type 8. you must also include the name of the Jar file in the CLASSPATH entry. To do this in Windows. then scroll down to “SWT Binary and Source” and select the file corresponding to your platform. On Windows.org/eclipse/downloads/).org/.jar from the Eclipse SWT library (http://download.net/project/showfiles. 6.jar file into the C:\jars\ directory. (The default is C:\"Program Files"\Java\lib). you type javac MyProgram. javaws. 4th Edition Annotated Solution Guide . Further details about finding the jar file are in Thinking in Java. 2 Thinking in Java. Place the following files into this directory: • javassist. You’ll find it in the lib directory wherever you installed the JDK on your machine.org/XOM/. available from http://www. you can also compile each example individually (once you have the CLASSPATH set. following the directions in Step 4. swt. 4th Edition.7 (or newer) build tool by following the instructions you will find in the Ant download at http://ant. you do not need to perform any of the following steps.” Press the “Browse” button and navigate to C:\TIJ4Solutions\code. see the following section for instructions on how to use the code with Eclipse.eclipse.org/).cd C:\TIJ4-Solutions\code into the resulting command window. 11. Certain programs (primarily those that use hashing) can produce different output from one version to the next. Note: If you are installing the demo version of the solution guide. At the prompt. type ant build The build should successfully compile all the chapters in the solution guide.org/). Installing the Code 3 . This code is designed to work without an IDE.eclipse.jedit. 2. but it has also been tested with Eclipse (free at http://www. 12. instead. you can use the code inside the Eclipse development environment as follows: 1. Using Eclipse Once you’ve followed the above instructions. Different IDEs have different requirements and you might find it’s more trouble than it’s worth right now. 13. 10. then choose File | New | Java Project from the main menu. choose a version for Java developers and follow the installation instructions. Start Eclipse. Install Eclipse from http://www. Enter “TIJ4-Solutions” as the project name and press the “Finish” button. you may want to begin with a more basic editor like JEdit (free at http://www. If you want to use this code inside other IDEs you might need to make appropriate adjustments.” select “Create Project from Existing Source. 3. In the ensuing dialog box. under “Contents. Once you’ve run ant build in the root directory. you can also move into individual chapters and type ant (to compile and execute the code in that chapter) or ant build (to compile the code only).org/downloads/. Note: The output for the programs has been verified for Java 6. Click on “Included” and press the “Remove” button. When you press OK to close the dialog box.” add the following files.jar that are described in step 6 of the previous section. Click on the “Libraries” tab.java reusing/E21_FinalMethod.java interfaces/E02_Abstract. 8. Add tools.java arrays/E11_AutoboxingWithArrays.” Click on “Excluded” and press “Edit. 4 Thinking in Java. the project should rebuild without any errors. 6.jar. then click on “Excluded” and press “Remove. which are not intended to compile. After you add the files. then the “Add External Jars” button. In the “Package Explorer” pane. press the “Finish” button. right click on “TIJ4-Solutions” and select “Properties” (at the bottom). javassist. swt. select “Java Build Path.4. Eclipse will work for awhile and then present you with a “problems” pane containing a lot of errors and warnings. 4th Edition Annotated Solution Guide . In the left column of the ensuing dialog box.” Select the “Source” tab.jar and xom. The warnings that you see refer to code that is intentional for those solutions. • • • • • • • access/E04_ForeignClass.java reusing/E06_ChessWithoutDefCtor.java reusing/E20_OverrideAnnotation.” Under “Exclusion Patterns. in order to demonstrate features and issues of the language. The default Eclipse configuration may have chosen to exclude and include some files. 9.java reusing/E22_FinalClass.java 5. We’ll remove the errors in the following steps. 7.jar. Because of the prevalence of IDEs. If you have solved the problems in those chapters without using package statements. IDE support has gotten so good (and many prevalent IDEs are free) that it’s less and less likely that you’ll develop in Java – or even learn the language – without an IDE. but it still prefers packages). however. 5 . Packages. even for chapters before Access Control. between IDEs and the way that Thinking in Java attempts to teach the language: One step at a time. so you typically used a text editor and the command-line compiler. are not introduced until the Access Control chapter. however. An IDE like Eclipse (from www. Over the years. we have chosen to include package statements for all the code in this book. your solutions are still correct. using language features only after they’ve been introduced.Packages & IDEs When Java first appeared there was no Integrated Development Environment (IDE) support.Eclipse.org) likes to have all its code in packages (later versions have become more tolerant of unpackaged code. There’s a conflict. . and 43 from the chapter Graphical User Interfaces 7 . The exercises left to the reader include: • • • • • • • Exercises 12 & 13 from the chapter Everything Is an Object Exercise 13 from the chapter Initialization & Cleanup Exercise 2 from the chapter Access Control Exercise 15 from the chapter Generics Exercise 8 from the chapter Arrays Exercise 21 from the chapter Containers in Depth Exercise 35. 36.Left to the Reader We have left only a few exercises to the reader. 39. the solution typically requires some configuration on your own computer. For these. 38. . public class E01_DefaultInitialization { int i. char c.java /****************** Exercise 1 ***************** * Create a class containing an int and a char * that are not initialized. your solutions are still correct. } public static void main(String[] args) { new E01_DefaultInitialization(). we have included package statements for chapters before Access Control.println("c = [" + c + ']').java 9 . If you have solved the problems in this chapter without using package statements.println("i = " + i). ***********************************************/ package object. and a “space” for the char. System.out. Print their values * to verify that Java performs default * initialization.Everything is an Object To satisfy IDEs like Eclipse.out. } } /* Output: i = 0 c = [ ] *///:~ When you run the program you’ll see that both variables are given default values: 0 for the int. Exercise 2 //: object/E02_HelloWorld. public E01_DefaultInitialization() { System. Exercise 1 //: object/E01_DefaultInitialization. java /****************** Exercise 3 ****************** * Turn the code fragments involving ATypeName * into a program that compiles and * runs. public class E03_ATypeName { public static void main(String[] args) { E03_ATypeName a = new E03_ATypeName(). 10 Thinking in Java. 4th Edition Annotated Solution Guide . * Compile the program with javac and run it using * java.println("Hello.java /****************** Exercise 4 ****************** * Turn the DataOnly code fragments into a * program that compiles and runs. ************************************************/ package object. You need only a * single method in your class (the "main" one that * executes when the program starts). } } /* Output: Hello.out. public class E02_HelloWorld { public static void main(String[] args) { System.java example in this * chapter to create a "hello. If you are using a different development * environment than the JDK. world! *///:~ Exercise 3 //: object/E03_ATypeName. ************************************************/ package object. world" program that * simply displays that statement./****************** Exercise 2 ****************** * Follow the HelloDate. } } ///:~ Exercise 4 //: object/E04_DataOnly. Remember * to make it static and to include the argument * list (even though you don't use it). learn how to compile * and run programs in that environment. world!"). ************************************************/ package object. d. System.i = 47 d. d. public class E05_DataOnly2 { public static void main(String[] args) { E04_DataOnly d = new E04_DataOnly(). public static void main(String[] args) { E04_DataOnly d = new E04_DataOnly().println("d.i).out.java /****************** Exercise 6 ****************** Everything is an Object 11 .b). double d. d.1.d = " + d.d = 1.1 d. public class E04_DataOnly { int i. d.println("d.java /****************** Exercise 5 ****************** * Modify Exercise 4 so the values * of the data in DataOnly are assigned to and * printed in main().d).println("d. boolean b. System.b = false. } } /* Output: d.out.1.b = " + d.d = 1.i = " + d. System. } } ///:~ Exercise 5 //: object/E05_DataOnly2.d = 1. d.i = 47.b = false *///:~ Exercise 6 //: object/E06_Storage. d.i = 47.out.b = false.************************************************/ package object. } public class E07_Incrementable { static void increment() { StaticTest. } } ///:~ 12 Thinking in Java.java /****************** Exercise 7 ****************** * Turn the Incrementable code fragments into a * working program. } public static void main(String[] args) { E07_Incrementable sf = new E07_Incrementable().i++. class StaticTest { static int i = 47. World!".* Write a program that includes and calls the * storage() method defined as a code fragment in * this chapter. 4th Edition Annotated Solution Guide . increment(). ************************************************/ package object. int storage(String s) { return s. E07_Incrementable.increment().println("storage(s) = " + storage(s)).out.length() * 2. } void print() { System.print(). } } /* Output: storage(s) = 26 *///:~ Exercise 7 //: object/E07_Incrementable.increment(). public class E06_Storage { String s = "Hello. sf. } public static void main(String[] args) { E06_Storage st = new E06_Storage(). ************************************************/ package object. st. You can call increment( ) by itself. } } /* Output: 47 == 47 48 == 48 *///:~ The output shows that both instances of E08_StaticTest share the same static field.out. E08_StaticTest st2 = new E08_StaticTest(). ************************************************/ package object. st1. public class E09_AutoboxingTest { public static void main(String[] args) { Everything is an Object 13 . because a static method (main( ).java /****************** Exercise 8 ****************** * Write a program to demonstrate that no * matter how many objects you create of a * particular class. public static void main(String[] args) { E08_StaticTest st1 = new E08_StaticTest().println(st1.i). Exercise 8 //: object/E08_StaticTest. System. public class E08_StaticTest { static int i = 47.i + " == " + st2.out. there is only one instance * of a particular static field in that class. System.i). in this case) can call another static method without qualification.i++.i + " == " + st2.java /****************** Exercise 9 ****************** * Write a program to demonstrate that * autoboxing works for all the primitive types * and their wrappers. Exercise 9 //: object/E09_AutoboxingTest. We incremented the shared field through the first instance and the effect was visible in the second instance.println(st1. ************************************************/ package object. Exercise 10 //: object/E10_ShowArgs. System. System.out. System.0 double = 1. The only difference is the direction of the conversion: autoboxing converts from the primitive type to the wrapper object.0 *///:~ The terms Autoboxing and Autounboxing appear often in the literature.println("int = " + i). } } /* Output: byte = 1 short = 1 int = 1 long = 1 boolean = true char = x float = 1. and autounboxing converts from the wrapped type to the primitive type. long l = lo. short s = sh. Double db = 1. char c = ch. Integer in = 1.out. System.0d. boolean b = bo. Character ch = 'x'.0f.out.java // {Args: A B C} /****************** Exercise 10 **************** 14 Thinking in Java.out.println("long = " + l).println("boolean = " + b). Float fl = 1. byte bt = by. System. Boolean bo = true.println("char = " + c).out. int i = in. System. System.out. Long lo = 1L.println("short = " + s).Byte by = 1.println("double = " + d). 4th Edition Annotated Solution Guide .println("float = " + f). System.out. float f = fl.println("byte = " + bt). Short sh = 1.out. double d = db. } } /* Output: A B C *///:~ Remember.* Write a program that prints three arguments * taken from the command line. * You'll need to index into the command-line * array of Strings.out. System.length < 3) { System. You can test for the length of the command-line argument array like this: //: object/E10_ShowArgs2. } System.println(args[0]).println(args[2]).println(args[0]).java // {Args: A B C} package object. } } /* Output: A B Everything is an Object 15 . args[0] is the first command-line argument and not the name of the program (as it is in C). public class E10_ShowArgs2 { public static void main(String[] args) { if(args. System. System.println(args[2]). ***********************************************/ package object. when you want to get an argument from the command line: • • • Arguments are provided in a String array. System. System.err. You’ll cause a runtime exception if you run the program without enough arguments.out.out.exit(1).out.out.out.println("Need 3 arguments"). public class E10_ShowArgs { public static void main(String[] args) { System.println(args[1]).println(args[1]). Exercise 11 //: object/E11_AllTheColorsOfTheRainbow. ************************************************/ package object.java. the simple comment* documentation example. a non-zero status code indicates that the program execution failed. Execute Javadoc on the * file and view the results with your Web browser. } } ///:~ 16 Thinking in Java. public class E12_LeftToReader { public static void main(String args[]) { System. (With most operating systems.out.) Typically.exit( ) terminates the program and passes its argument back to the operating system as a status code. ************************************************/ package object. all.println("Exercise left to reader"). } } ///:~ Exercise 12 //: object/E12_LeftToReader.C *///:~ System. } public static void main(String[] args) { E11_AllTheColorsOfTheRainbow all = new E11_AllTheColorsOfTheRainbow(). void changeTheHueOfTheColor(int newHue) { anIntegerRepresentingColors = newHue. as shown above. public class E11_AllTheColorsOfTheRainbow { int anIntegerRepresentingColors.java /****************** Exercise 12 ***************** * Find the code for the second version of * HelloDate.java /****************** Exercise 11 ***************** * Turn the AllTheColorsOfTheRainbow example into * a program that compiles and runs.changeTheHueOfTheColor(27). 4th Edition Annotated Solution Guide . you send error messages to System.err. java /****************** Exercise 13 ***************** * Run Documentation1.java /****************** Exercise 14 ***************** * Add an HTML list of items to the documentation * in Exercise 13.Note that Javadoc doesn’t automatically create the destination directory.println("Exercise left to reader"). * </pre> */ public class E14_DocTest { /** A variable comment */ public int i. Verify * the resulting documentation with your Web * browser.out. * and Documentation3.println(new Date()). Consult the Javadoc reference in the JDK documentation to learn the many uses of Javadoc.out. public class E13_LeftToReader { public static void main(String args[]) { System.java. ************************************************/ package object.java. Exercise 13 //: object/E13_LeftToReader. Documentation2. } } ///:~ Exercise 14 //: object/E14_DocTest. ************************************************/ package object. /** A class comment * <pre> * System.java through Javadoc. /** A method comment * You can <em>even</em> insert a list: * <ol> * <li> Item one * <li> Item two * <li> Item three Everything is an Object 17 . java /****************** Exercise 15 ***************** * Add comment documentation to the program in Exercise 2.* </ol> */ public void f() {} } ///:~ We simply added the HTML code fragments from the chapter examples. 18 Thinking in Java. * Extract it into an HTML file using Javadoc * and view it with your Web browser.out. } } /* Output: Hello. ************************************************/ package object.println("Hello. * Demonstrates the basic class * structure and the creation of a * <code>main()</code> method.java example. * Extract it into an HTML file using Javadoc * and view it with your Web browser. Exercise 15 //: object/E15_HelloWorldDoc. ************************************************/ package object. @param args array passed from the command-line */ public static void main(String[] args) { System. world! *///:~ Exercise 16 //: object/E16_OverloadingDoc. /** A first example from <i>sTIJ4</i>. add * Javadoc documentation to the Overloading. 4th Edition Annotated Solution Guide . */ public class E15_HelloWorldDoc { /** The <code>main()</code> method which is called when the program is executed by saying <code>java E15_HelloWorldDoc</code>. world!").java /****************** Exercise 16 ***************** * In the Initialization and Cleanup chapter. // 0 by default /** Plant a seedling. } // Overloaded constructor: new Tree(). } } /* Output: Creating new Tree that is 0 feet tall Tree is 0 feet tall overloaded method: Tree is 0 feet tall Creating new Tree that is 1 feet tall Tree is 1 feet tall overloaded method: Tree is 1 feet tall Everything is an Object 19 .out. } /** Transplant an existing tree with a given height. t. t.info().println("Planting a seedling"). */ public static void main(String[] args) { for(int i = 0.out. } /** Produce information about this unit.info("overloaded method"). */ int height. Assume height can be considered as zero./** Model of a single arboreal unit.out. } } /** Simple test code for Tree class */ public class E16_OverloadingDoc { /** Creates <b>Tree</b> objects and exercises the two different <code>info()</code> methods. */ void info() { System.out. */ class Tree { /** Current vertical aspect to the tip. */ void info(String s) { System. */ Tree() { System. i < 5.println("Creating new Tree that is " + i + " feet tall"). height = i. */ Tree(int i) { System. } /** Produce information with optional message. i++) { Tree t = new Tree(i).println("Tree is " + height + " feet tall").println(s + ": Tree is " + height + " feet tall"). 4th Edition Annotated Solution Guide . Statements that control the execution flow of the program appear in a later chapter of TIJ4. 20 Thinking in Java. which should be greater than zero.Creating new Tree that is 2 feet tall Tree is 2 feet tall overloaded method: Tree is 2 feet tall Creating new Tree that is 3 feet tall Tree is 3 feet tall overloaded method: Tree is 3 feet tall Creating new Tree that is 4 feet tall Tree is 4 feet tall overloaded method: Tree is 4 feet tall Planting a seedling *///:~ The one-argument constructor does not check the input argument. } 21 .*. your solutions are still correct. it's: " + currDate).util. Exercise 1 //: operators/E01_PrintStatements.java /****************** Exercise 1 ***************** * Write a program that uses the "short" and * normal form of print statement. we have included package statements for chapters before Access Control. import static net.java /****************** Exercise 2 ***************** * Create a class containing a float and use it to * demonstrate aliasing. import java. ***********************************************/ package operators.Print.Print. ***********************************************/ package operators.Operators To satisfy IDEs like Eclipse.mindview. class Integral { float f.println("Hello. public class E01_PrintStatements { public static void main(String[] args) { Date currDate = new Date(). it's: " + currDate).Date. If you have solved the problems in this chapter without using package statements.util.out.mindview. System. print("Hello. import static net. } } /* Output: (Sample) Hello. it's: Wed Mar 30 17:39:26 CEST 2005 *///:~ Exercise 2 //: operators/E02_Aliasing. it's: Wed Mar 30 17:39:26 CEST 2005 Hello.util.*. f: " + x.f + ".0 *///:~ 22 Thinking in Java.util. n2.0 3: n1.f).f = 27f.0.f = 47f.mindview.0f.java /****************** Exercise 3 ***************** * Create a class containing a float and use it * to demonstrate aliasing during method calls. n2. Exercise 3 //: operators/E03_Aliasing2.f). n2.0f.f: " + n1. } } /* Output: 1: n1. import static net.f: " + n2.f).f: 47.f = 2.f: 47. } public static void main(String[] args) { Integral x = new Integral().Print. n1. n1.0 2: x. public class E03_Aliasing2 { static void f(Integral y) { y.f: " + n1.0 *///:~ You can see the effect of aliasing after n2 is assigned to n1: they both point to the same object. x. n2. print("2: n1.0. Integral n2 = new Integral().f + ".f). ***********************************************/ package operators.f = 1.f: " + n2.f: 27.f: 47. f(x).f: " + n1.f + ". print("1: x. print("1: n1. 4th Edition Annotated Solution Guide .*.f: 2.f). n2. n1 = n2.0 2: n1. } } /* Output: 1: x.f: 27.public class E02_Aliasing { public static void main(String[] args) { Integral n1 = new Integral().f: " + n2.f: 9.f = 9f. n2.f: 1. n2.0.f: " + x. print("3: n1. print("2: x. parseFloat(args[1]). the latter terminates the current line by writing the line separator string. // Change the next line if you want to use a different // unit for 'distance' System.length < 2) { System.err.out. Note the difference between System.53125 m/s *///:~ Here we take the distance and time values from the command line.print( ) and System.print(distance / time).parseFloat(args[0]). System.out.2} /****************** Exercise 4 ***************** * Write a program that calculates velocity * using a constant distance and a constant time. Even when you don’t actually see changes being made to the code you’re writing or the method you’re calling. public class E04_Velocity { public static void main(String[] args) { if(args. Operators 23 . System. Exercise 4 //: operators/E04_Velocity. } } /* Output: Velocity = 9.print("Velocity = "). use the static parseFloat( ) method of class Float.out.out. } float distance = Float. you must remember either “parse” or that it’s part of class Float. float time = Float.This exercise emphasizes that you’re always passing references around.exit(1). thus you’re always aliasing. ***********************************************/ package operators.5 3.java // {Args: 30. This can be difficult to locate using the JDK HTML documentation.println(" m/s"). System.println( "Usage: java E04_Velocity distance time"). if you need a float instead.println( ).out. that code or method could be calling other methods that modify the object. Arguments come in as a String array. } } /* Output: spot says ruff! scruffy says wurf! *///:~ This walks you through the basics of objects.name = "scruffy".says). and demonstrates that each object has its own distinct storage space. String says. Then display their names and * what they say.println(dog1. Dog dog2 = new Dog().mindview. dog1.println(dog2.name = "spot". 24 Thinking in Java. dog2. Exercise 6 //: operators/E06_DogsComparison.util. a new Dog * reference to spot's object.java /****************** Exercise 6 ***************** * Following Exercise 5 assign. import static net.name + " says " + dog2.out.Print.says). class Dog { String name. ***********************************************/ package operators. create two dogs.java /****************** Exercise 5 ***************** * Create a class called Dog with two Strings: * name and says.*. In main(). dog2. "Wurf!". System. dog1. System. and "scruffy" who * says. ***********************************************/ package operators.name + " says " + dog1. } public class E05_Dogs { public static void main(String[] args) { Dog dog1 = new Dog().says = "wurf!". 4th Edition Annotated Solution Guide .Exercise 5 //: operators/E05_Dogs. * "spot" who says.out. "Ruff!". Test for comparison * using == and equals() for all references.says = "ruff!". dog2. print( "equals() on names: " + dog1.equals(dog2.").. == on top references: true equals() on top references: true == on names: true equals() on names: true == on says: true equals() on says: true Comparing dog2 and dog3 objects.equals(dog2..name. dog3). } } /* Output: Comparing dog1 and dog2 objects."). compare(dog1.says = "ruff!". dog2)..name = "scruffy". print("\nComparing dog1 and dog3 objects.. compare(dog1.public class E06_DogsComparison { static void compare(Dog dog1.says) ).. print( "equals() on says: " + dog1...says)). == on top references: false equals() on top references: false == on names: false equals() on names: false == on says: false equals() on says: false Comparing dog1 and dog3 objects.name = "spot". dog1.equals(dog2) ).says. print("Comparing dog1 and dog2 objects.name) ). print("== on says: " + (dog1..says = "wurf!". == on top references: false Operators 25 . // "Aliased" reference dog1.name == dog2."). print( "equals() on top references: " + dog1... } public static void main(String[] args) { Dog dog1 = new Dog().says == dog2.name)).. print("== on names: " + (dog1. print("\nComparing dog2 and dog3 objects. Dog dog3 = dog1. compare(dog2. dog3). Dog dog2 = new Dog(). dog2.. Dog dog2) { print("== on top references: " + (dog1 == dog2)). ***********************************************/ package operators. 4th Edition Annotated Solution Guide .print("OUTCOME: ").out.Random. boolean flip = rand.nextBoolean(). The program uses a ternary if-else operator to produce output.com). After familiarizing yourself with the HTML documentation for the JDK (downloadable from java. System.out.name == dog2. (See the Ternary if-else Operator section in TIJ4 for more information.sun.) NOTE: You will normally create a Random object with no arguments to produce different output for each execution of the program.equals() on top references: false == on names: false equals() on names: false == on says: false equals() on says: false *///:~ Guess whether the following line compiles: print("== on top references: " + dog1 == dog2).util.java /****************** Exercise 7 ***************** * Write a program that simulates coin-flipping. Otherwise it can 26 Thinking in Java. System.println(flip ? "HEAD" : "TAIL"). } } /* Output: OUTCOME: HEAD *///:~ This is partly an exercise in Standard Java Library usage.name). Exercise 7 //: operators/E07_CoinFlipping. select “R” at the JDK index to see various ways to generate random numbers. import java.) Apply the same reasoning to the next case and explain why the comparison always results in false: print("== on says: " + dog1. public class E07_CoinFlipping { public static void main(String[] args) { Random rand = new Random(47). Why or why not? (Hint: Read the Precedence and String operator + and += sections in TIJ4. toBinaryString() to display * the results.util. public class E09_MinMaxExponents { public static void main(String[] args) { print("Float MIN: " + Float.Print. // Hexadecimal (lowercase) print("l1: " + Long. ***********************************************/ package operators.util. Use Long.mindview. import static net.mindview. we use the seed value of 47 to make the output identical.java /****************** Exercise 9 ***************** * Display the largest and smallest numbers for * both float and double exponential notation.*.toBinaryString( ) does not print leading zeroes.toBinaryString(l2)).java /****************** Exercise 8 ***************** * Show that hex and octal notations work with long * values. In this exercise and throughout the book. } } /* Output: l1: 101111 l2: 101111 l3: 1111111 *///:~ Note that Long.Print. public class E08_LongLiterals { public static void main(String[] args) { long l1 = 0x2f. import static net. ***********************************************/ package operators. Operators 27 . long l2 = 0X2F. thus verifiable.MAX_VALUE). // Hexadecimal (uppercase) print("l2: " + Long. // Octal (leading zero) print("l3: " + Long. print("Float MAX: " + Float. long l3 = 0177.toBinaryString(l1)). for each run.MIN_VALUE). Exercise 9 //: operators/E09_MinMaxExponents.*.toBinaryString(l3)). Exercise 8 //: operators/E08_LongLiterals.hardly be called a simulator. toBinaryString(i1 print("i1 ^ i2 = " + Integer.toBinaryString(i2)). i1)). print("i2 = " + Integer.toBinaryString(i1)).*. import static net. i2)).toBinaryString(i1 } } /* Output: i1 = 10101010101010101010101010101010 i2 = 1010101010101010101010101010101 ~i1 = 1010101010101010101010101010101 & | ^ & | ^ i1)). with * a zero in the least-significant digit.toBinaryString(i1 print("i1 & i2 = " + Integer. 4th Edition Annotated Solution Guide . print("~i1 = " + Integer. i2)). print("i1 & i1 = " + Integer.) Combine * these two values every way possible using the * bitwise operators. print("i1 = " + Integer.print("Double MIN: " + Double. also alternating. ************************************************/ package operators.4E-45 Float MAX: 3. i1)).MIN_VALUE).toBinaryString(~i2)). print("~i2 = " + Integer.MAX_VALUE). } } /* Output: Float MIN: 1.toBinaryString(i1 print("i1 | i2 = " + Integer. Display the results using * Integer.4028235E38 Double MIN: 4.util.toBinaryString(i1 print("i1 | i1 = " + Integer.7976931348623157E308 *///:~ Exercise 10 //: operators/E10_BitwiseOperators. int i2 = 0x55555555. and the * second. public class E10_BitwiseOperators { public static void main(String[] args) { int i1 = 0xaaaaaaaa.toBinaryString(). print("Double MAX: " + Double. (Hint: It's easiest to * use hexadecimal constants for this.toBinaryString(i1 print("i1 ^ i1 = " + Integer. i2)).mindview.toBinaryString(~i1)).9E-324 Double MAX: 1. one * with alternating binary ones and zeroes.Print.java /****************** Exercise 10 ***************** * Write a program with two constant values. 28 Thinking in Java. with a one in the * least-significant digit. mindview.toBinaryString(i)). print(Integer. print(Integer. print(Integer.toBinaryString(i)).toBinaryString(i)).toBinaryString(i)). print(Integer. i >>= 1. i >>= 1. i >>= 1. print(Integer. * Display each result using Integer.toBinaryString(i)). i >>= 1. print(Integer.toBinaryString(i)). print(Integer.toBinaryString(i)).toBinaryString(i)). print(Integer. public class E11_SignedRightShift { public static void main(String[] args) { int i = 0x80000000. print(Integer.toBinaryString(i)).toBinaryString( ) does not print leading zeroes. Operators 29 .toBinaryString().toBinaryString(i)). i >>= 1.*.java /****************** Exercise 11 ***************** * Start with a number that has a binary one in * the most significant position. print(Integer.) Use the signed * right-shift operator to right shift your * number through all its binary positions. i >>= 1.toBinaryString(i)).toBinaryString(i)). i >>= 1. i >>= 1. print(Integer. import static net. print(Integer. i >>= 1. print(Integer. print(Integer. print(Integer. ************************************************/ package operators.Print.toBinaryString(i)). i >>= 1.~i2 = 10101010101010101010101010101010 i1 & i1 = 10101010101010101010101010101010 i1 | i1 = 10101010101010101010101010101010 i1 ^ i1 = 0 i1 & i2 = 0 i1 | i2 = 11111111111111111111111111111111 i1 ^ i2 = 11111111111111111111111111111111 *///:~ Note that Integer. print(Integer.toBinaryString(i)). i >>= 1. print(Integer. i >>= 1.util.toBinaryString(i)). i >>= 1.toBinaryString(i)). i >>= 1. i >>= 1. (Hint: Use a * hexadecimal constant. i >>= 1. Exercise 11 //: operators/E11_SignedRightShift. i >>= 1. print(Integer.toBinaryString(i)).toBinaryString(i)). toBinaryString(i)). 4th Edition Annotated Solution Guide . 1.toBinaryString(i)). print(Integer. print(Integer. 1. print(Integer. 1. print(Integer. print(Integer. 1. } } /* Output: 10000000000000000000000000000000 11000000000000000000000000000000 11100000000000000000000000000000 11110000000000000000000000000000 11111000000000000000000000000000 11111100000000000000000000000000 11111110000000000000000000000000 11111111000000000000000000000000 11111111100000000000000000000000 11111111110000000000000000000000 11111111111000000000000000000000 11111111111100000000000000000000 11111111111110000000000000000000 11111111111111000000000000000000 11111111111111100000000000000000 11111111111111110000000000000000 11111111111111111000000000000000 11111111111111111100000000000000 11111111111111111110000000000000 11111111111111111111000000000000 11111111111111111111100000000000 11111111111111111111110000000000 11111111111111111111111000000000 11111111111111111111111100000000 11111111111111111111111110000000 11111111111111111111111111000000 11111111111111111111111111100000 11111111111111111111111111110000 11111111111111111111111111111000 11111111111111111111111111111100 11111111111111111111111111111110 30 Thinking in Java. 1.toBinaryString(i)). 1. print(Integer. 1.toBinaryString(i)). 1.toBinaryString(i)). print(Integer. 1. 1. 1. print(Integer.toBinaryString(i)).toBinaryString(i)).toBinaryString(i)). print(Integer.toBinaryString(i)). print(Integer. print(Integer.i i i i i i i i i i i i i i >>= >>= >>= >>= >>= >>= >>= >>= >>= >>= >>= >>= >>= >>= 1.toBinaryString(i)).toBinaryString(i)).toBinaryString(i)). 1.toBinaryString(i)). print(Integer. 1. print(Integer.toBinaryString(i)). print(Integer. *. i >>>= 1. print(Integer. i >>>= 1. i >>>= 1.toBinaryString(i)).util. print(Integer.toBinaryString(i)).toBinaryString(i)). i >>>= 1.toBinaryString(i)). Exercise 12 //: operators/E12_UnsignedRightShift. Operators 31 . i >>>= 1.toBinaryString(i)).toBinaryString(i)). print(Integer.toBinaryString(i)).toBinaryString(i)). i >>>= 1. i >>>= 1. i >>>= 1.toBinaryString(i)).toBinaryString(i)). print(Integer.toBinaryString(i)). i >>>= 1.toBinaryString(i)). print(Integer.toBinaryString(i)). then use the unsigned right-shift * operator to right shift through all of its binary * positions. i >>>= 1. print(Integer. * Left shift it. print(Integer. ************************************************/ package operators. print(Integer.toBinaryString(i)). print(Integer. print(Integer. print(Integer.toBinaryString(i)).toBinaryString(i)). print(Integer.toBinaryString(i)). i >>>= 1. i >>>= 1. print(Integer.toBinaryString(i)). i >>>= 1. print(Integer. i >>>= 1. import static net. i >>>= 1. print(Integer.toBinaryString(i)). i >>>= 1.toBinaryString(i)). print(Integer. print(Integer. i >>>= 1.toBinaryString(i)). print(Integer. i >>>= 1.11111111111111111111111111111111 *///:~ This exercise required duplicating the line of code (the “brute force” approach).toBinaryString(i)).java /****************** Exercise 12 ***************** * Start with a number that is all binary ones. i >>>= 1.toBinaryString(). print(Integer.mindview.toBinaryString(i)).Print. In the next chapter you learn to use looping structures to greatly reduce the amount of code you see above. i >>>= 1. i >>>= 1. i >>>= 1. print(Integer. print(Integer. print(Integer. Display each result using * Integer. public class E12_UnsignedRightShift { public static void main(String[] args) { int i = -1 << 1. print(Integer. toBinaryString(i)). print(Integer. } } /* Output: 11111111111111111111111111111110 1111111111111111111111111111111 111111111111111111111111111111 11111111111111111111111111111 1111111111111111111111111111 111111111111111111111111111 11111111111111111111111111 1111111111111111111111111 111111111111111111111111 11111111111111111111111 1111111111111111111111 111111111111111111111 11111111111111111111 1111111111111111111 111111111111111111 11111111111111111 1111111111111111 111111111111111 11111111111111 1111111111111 111111111111 11111111111 1111111111 111111111 11111111 1111111 111111 11111 1111 111 11 1 *///:~ See the note for Exercise 11. 1. 1.toBinaryString(i)). print(Integer. print(Integer.toBinaryString(i)).toBinaryString(i)). 1. 1. print(Integer. print(Integer. print(Integer. 1. 1.i i i i i i i i i >>>= >>>= >>>= >>>= >>>= >>>= >>>= >>>= >>>= 1.toBinaryString(i)).toBinaryString(i)). print(Integer.toBinaryString(i)). print(Integer. 4th Edition Annotated Solution Guide . 32 Thinking in Java.toBinaryString(i)). 1.toBinaryString(i)). print(Integer. 1. println(s + ": " + b). String rval) { System.toBinaryString('A')).java /****************** Exercise 13 ***************** * Write a method to display char values in * binary form. //! p("lval < rval: " + lval < rval). Perform the equals() test for the == and * !=. print("x: " + Integer.out. Demonstrate it using several * different characters. print("!: " + Integer.Exercise 13 //: operators/E13_BinaryChar.*. print("7: " + Integer. } } /* Output: A: 1000001 !: 100001 x: 1111000 7: 110111 *///:~ Exercise 14 //: operators/E14_CompareStrings. boolean b) { System.java /****************** Exercise 14 ***************** * Write a method that compares two String arguments * using all the Boolean comparisons and print the * results.util.toBinaryString('!')). import static net. Operators 33 . ************************************************/ package operators.println("lval: " + lval + " rval: " + rval).toBinaryString('x')).mindview. } public static void compare(String lval.Print.toBinaryString('7')).out. call your method with different * String objects. public class E13_BinaryChar { public static void main(String[] args) { print("A: " + Integer. public class E14_CompareStrings { public static void p(String s. //! p("lval > rval: " + lval > rval). ************************************************/ package operators. In main(). Be wary of any comparison that uses ==. + lval <= rval). Because String objects are immutable (you cannot change their contents). lval p("lval.equals(rval): false *///:~ The only comparisons that actually compile are == and !=.equals(rval)". which actually compares content. s). Remember that quoted character arrays also produce references to String objects. In the first case. However. "Goodbye").//! p("lval <= rval: " //! p("lval >= rval: " p("lval == rval". // Force creation of separate object: String s = new String("Hello"). lval. when you create a separate String s you also create a distinct object with the same contents.equals(rval)). so == returns true in that case. they point to the same object). == rval). This (slightly tricky) exercise highlights the critical difference between the == and != operators.equals(rval): true lval: Hello rval: Goodbye lval == rval: false lval != rval: true lval.equals(rval): true lval: Hello rval: Hello lval == rval: false lval != rval: true lval. } public static void main(String[] args) { compare("Hello". compare("Hello". compare("Hello". 4th Edition Annotated Solution Guide . The only reliable way to compare objects for equality is with equals( ). + lval >= rval). which compare references. 34 Thinking in Java. therefore the == returns false. lval p("lval != rval". } } /* Output: lval: Hello rval: Hello lval == rval: true lval != rval: false lval. the compiler recognizes that the two strings actually contain the same values. and equals( ). the compiler can merge the two String objects into one. which always and only compares two references to see if they are identical (that is. != rval). "Hello"). i <= 100. i++) System. } } /* Output: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 *///:~ 19 39 59 79 99 20 21 40 41 60 61 80 81 100 22 42 62 82 23 43 63 83 This is the most trivial use of a for loop. public class E01_To100 { public static void main(String[] args) { for(int i = 1. If you have solved the problems in this chapter without using package statements. or * equal to a second randomly generated value.out. Use an if-else statement for each value * to classify it as greater than. Exercise 2 //: control/E02_RandomInts. ************************************************/ package control.Controlling Execution To satisfy IDEs like Eclipse.java /****************** Exercise 1 ****************** * Write a program to print values from one to * 100. your solutions are still correct.java /****************** Exercise 2 ****************** * Write a program to generate 25 random int * values. Exercise 1 //: control/E01_To100. ************************************************/ package control. Try to simplify Exercises 11 and 12 in the previous chapter using a for loop.print(i + " "). less than. we have included package statements for chapters before Access Control. 35 . b = -1916708780 a > b a = -2016789463.nextInt(). } public static void main(String[] args) { for(int i = 0. i++) compareRand(). i < 25.*.out. 4th Edition Annotated Solution Guide . else if(a > b) System. b = 1072754767 a > b a = -846991883. b = -1812486437 a > b a = 809509736. b = " + b). b = 1791060401 a < b a = -2076178252. int b = r. b = -1946952740 a > b a = -843035300. b = 1717241110 a < b a = -2014573909. System. b = 491149179 a > b a = 218473618. b = 865149722 a < b a = -1021916256. } } /* Output: a = -1172028779. else System. public static void compareRand() { int a = r. b = 229403722 a < b a = 688081923. b = -1128074882 a < b a = 1150476577.out.println("a < b"). b = 1703464645 a < b a = 2092435409.nextInt(). if(a < b) System.out.util.println("a > b").out.println("a = b"). public class E02_RandomInts { static Random r = new Random(47). b = 674708281 a < b a = -2020372274.println("a = " + a + ". b = -210207040 a > b a = 1122537102. b = 488201151 36 Thinking in Java.import java. b = 849275653 a < b a = 2078628644. you may have created all the code inline. inside main( ). b = -855894611 a > b a = -1612351948. In your solution. * typically with Control-C. b = -955419343 a < b *///:~ In the solution above. b = 1891197608 a < b a = -56789395. b = 1920737378 a < b a = -1278072925. then call that method 25 times. Exercise 3 //: control/E03_RandomInts2. we create a method that generates and compares the random numbers. } Controlling Execution 37 . b = 281473985 a < b a = -1439435803.a < b a = 100996820. public class E03_RandomInts2 { public static void main(String[] args) { while(true) E02_RandomInts. ************************************************/ package control. b = -914835675 a > b a = 1169976606. b = 875665968 a < b a = 1738084688.compareRand(). b = -1004355271 a > b a = -541407364. b = 1947946283 a < b a = 691554276. It * will then run until you interrupt it.java // {RunByHand} /****************** Exercise 3 ****************** * Modify Exercise 2 so your code is * surrounded by an "infinite" while loop. b = -1099465504 a > b a = 39716067. ************************************************/ package control.java /****************** Exercise 4 ****************** * Write a program to detect and print prime numbers * (integers evenly divisible only by themselves * and 1). i < max. 4th Edition Annotated Solution Guide . using two nested for loops and the * modulus operator (%).print(i + " "). but a well-designed program is usually easier to get running in the first place.} ///:~ A method outside of main( ) did most of the work in the previous exercise. so this solution requires only a minor change to main( ). // Get the max value from the command line. } } } /* Output: 1 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97 *///:~ Note that the program includes 1 as a prime number even though 2 is ordinarily considered the smallest prime. if(prime) System. 38 Thinking in Java. for(int i = 1. j++) if(i % j == 0) prime = false.out. public class E04_FindPrimes { public static void main(String[] args) { int max = 100.length != 0) max = Integer. j < i. Exercise 4 //: control/E04_FindPrimes. for(int j = 2. i++) { boolean prime = true. Structure a program properly and it requires fewer code changes during its lifetime. // if the argument has been provided: if(args.parseInt(args[0]). The benefit may lie in reducing the maintenance costs of the software rather than the cost of the initial release. *. i += 2) if(!sieve[i]) printnb(i + " "). if(max > 1) printnb(2 + " ").Print.java package control. since 2 is the only even prime. The search continues until you have crossed out all numbers divisible by max . // if the argument has been provided: if(args.java Controlling Execution 39 . printnb(1 + " "). import static java.Math. The following program uses a boolean array to mark prime numbers. // Print prime numbers for(int i = 3. j <= max.util. // Detect prime numbers for(int i = 3. //: control/E04_FindPrimes2.parseInt(args[0]). boolean[] sieve = new boolean[max + 1].lang. where ⎣x ⎦ is the floor function. i <= max. Additional optimizations are left as exercises.mindview. i += 2) if(!sieve[i]) for(int j = 2 * i. // Get the max value from the command line. import static net. int limit = (int)floor(sqrt(max)). i <= limit. The program uses a max + 1 sized array to make indexing easier.length != 0) max = Integer. } } /* Output: 1 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97 *///:~ You need only test odd numbers. j += i) sieve[j] = true.*. public class E04_FindPrimes2 { public static void main(String[] args) { int max = 100. ⎣ ⎦ Exercise 5 //: control/E05_BitwiseOperators2.One of the fastest ways of finding prime numbers is called the Sieve of Eratosthenes. printnb("i2 = "). i2). printnb("i1 = "). 4th Edition Annotated Solution Guide . i2). do { buffer[--bufferPosition] = ((i & 0x01) != 0) ? '1' : '0'.Print. ************************************************/ package control. i2). i >>>= 1. 40 Thinking in Java. } while (i != 0). toBinaryString(i1 printnb("i1 | i1 = "). print(). public class E05_BitwiseOperators2 { private static void toBinaryString(int i) { char[] buffer = new char[32]. i1). int i2 = 0x55555555.*.toBinaryString() to display * the ones and zeroes. toBinaryString(i1 printnb("i1 ^ i1 = "). int bufferPosition = 32. toBinaryString(i2). import static net. toBinaryString(i1 } } /* Output: i1 = 10101010101010101010101010101010 i2 = 1010101010101010101010101010101 ~i1 = 1010101010101010101010101010101 ~i2 = 10101010101010101010101010101010 i1 & i1 = 10101010101010101010101010101010 i1 | i1 = 10101010101010101010101010101010 i1 ^ i1 = 0 i1 & i2 = 0 i1 | i2 = 11111111111111111111111111111111 i1 ^ i2 = 11111111111111111111111111111111 & | ^ & | ^ i1). toBinaryString(i1). i1).util. toBinaryString(~i1). } public static void main(String[] args) { int i1 = 0xaaaaaaaa. toBinaryString(~i2).mindview. toBinaryString(i1 printnb("i1 & i2 = "). j < 32. toBinaryString(i1 printnb("i1 | i2 = "). for(int j = bufferPosition. printnb("~i1 = "). * but use the ternary operator and a bitwise test * instead of Integer. j++) printnb(buffer[j])./****************** Exercise 5 ****************** * Repeat Exercise 10 from the previous chapter. printnb("i1 & i1 = "). toBinaryString(i1 printnb("i1 ^ i2 = "). printnb("~i2 = "). out. Note that by using return in the following program. public class E06_RangeTest { static boolean test(int testval.println(test(10. System.println(test(5. int begin. if(testval >= begin && testval <= end) result = true.toBinaryString( ). 5. 5)).*///:~ The private static method toBinaryString( ) behaves like Integer. 5. Exercise 6 //: control/E06_RangeTest. begin and end. System. ************************************************/ package control.java // No intermediate 'result' value necessary: package control. 15)).out. int end) { boolean result = false. 15)). so we changed the return value to Boolean. using buffer to hold the binary digits because printing out the digits as encountered would produce an inverted output.java /****************** Exercise 6 ****************** * Modify the two test() methods in the previous * two programs so they take two extra * arguments.println(test(5. return result. 10. } public static void main(String[] args) { System. Controlling Execution 41 . } } /* Output: true false true *///:~ The test( ) methods are now only testing for two conditions.out. no intermediate result variable is necessary: //: control/E06_RangeTest2. and so testval is * tested to see if it is within the range between * (and including) begin and end. 15)).out. System. public class E07_To98 { public static void main(String[] args) { for(int i = 1.out. ************************************************/ package control.out. 5)). return false. } } } /* Output: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 *///:~ 19 39 59 79 20 40 60 80 21 41 61 81 22 42 62 82 23 43 63 83 There is no observable difference whether you use break or return in this program.public class E06_RangeTest2 { static boolean test(int testval. i <= 100. Try using * return instead.java /****************** Exercise 7 ****************** * Modify Exercise 1 so the program exits by * using the break keyword at value 99. } } /* Output: true false true *///:~ Exercise 7 //: control/E07_To98. System. 15)).println(test(10. System.out. int begin. 5. 10. i++) { if(i == 99) break. int end) { if(testval >= begin && testval <= end) return true. 4th Edition Annotated Solution Guide .print(i + " "). 42 Thinking in Java. } public static void main(String[] args) { System.println(test(5. 5.println(test(5. case 4: System. Put * a break after each case and test it.out. break.println("case 4"). i++) switch(i) { case 1: System. i < 7. } } } /* Output: default case 1 case 2 case 3 case 4 case 5 default *///:~ As a demonstration.println("case 2").println("default"). break.Exercise 8 This straightforward exercise demonstrates all the behaviors of switch. Here’s the version with breaks: //: control/E08_SwitchDemo.out.out. You can see that anything that doesn’t match one of the cases goes to the default statement. break. we allowed the value of i to go out of bounds. Here’s the same program with the breaks removed: Controlling Execution 43 . then see * what happens when you remove the breaks. case 3: System.out.out.println("case 5"). break. ***********************************************/ package control.println("case 3"). one with breaks and one without. break. case 5: System.java /****************** Exercise 8 ***************** * Create a switch statement inside a for loop * that tries each case and prints a message. case 2: System.println("case 1").out. default: System. We wrote two programs. public class E08_SwitchDemo { public static void main(String[] args) { for(int i = 0. println("default").out.println("case 1"). case 2: System.//: control/E08_SwitchDemo2. i++) switch(i) { case 1: System. package control. i < 7. 44 Thinking in Java. so you’ll almost always want a break at the end of each case. public class E08_SwitchDemo2 { public static void main(String[] args) { for(int i = 0. } } } /* Output: default case 1 case 2 case 3 case 4 case 5 default case 2 case 3 case 4 case 5 default case 3 case 4 case 5 default case 4 case 5 default case 5 default default *///:~ Without the break.println("case 5"). case 4: System.out.out.out.java with the breaks removed. 4th Edition Annotated Solution Guide .println("case 2"). case 5: System. case 3: System. When you select case 1 you get all the other cases as well. default: System. each case falls through to the next one.out.out.println("case 4").println("case 3").java // E08_SwitchDemo. java // {Args: 20} /****************** Exercise 9 ********************** * A Fibonacci sequence is the sequence of numbers 1. * 1. 144.parseInt(args[0]). ****************************************************/ package control. 6765. *///:~ This problem. 2584. 377. 1. 5.. 2. if(n < 0) { System. 3. 8. 3. If. 55. 5. etc. return.out.. 34. public class E09_Fibonacci { static int fib(int n) { if (n <= 2) return 1. } } /* Output: 1. 8. commonly presented in introductory programming classes. you run java * Fibonacci 5 (where Fibonacci is the name of the * class) the output will be: 1. As an additional exercise. 21. ").) Controlling Execution 45 . 89. 13. e. (Hint: Use Binet’s formula for the nth Fibonacci number. where each * number (from the third on) is the sum of the previous * two.Exercise 9 //: control/E09_Fibonacci. i++) System. uses recursion. meaning that a function calls itself until it reaches a bottoming-out condition and returns.print(fib(i) + ". Create a method that takes an integer as an * argument and displays that many Fibonacci numbers * starting from the beginning. 2. 987. 1. 21.out. 233. return fib(n-1) + fib(n-2). 610. 1597. } for(int i = 1. rewrite the solution without relying on recursion.g. 2. 5. 4181. 3.println("Cannot use negative numbers"). i <= n. 13. } public static void main(String[] args) { // Get the max value from the command line: int n = Integer. 34. productDigit[3] = product % 1000 % 100 % 10. startDigit[0] = num1 / 10. productDigit[0] = product / 1000. productDigit[2] = product % 1000 % 100 / 10. * Pairs of trailing zeroes are not allowed. int[] productDigit = new int[4]. startDigit[3] = num2 % 10. Examples * include: * 1260 = 21 * 60 * 1827 = 21 * 87 * 2187 = 27 * 81 * Write a program that finds all the 4-digit vampire * numbers. 4th Edition Annotated Solution Guide . (Suggested by Dan Forhan. num1++) for(int num2 = num1. The digits * are taken from the original number in any order. startDigit[1] = num1 % 10. startDigit[2] = num2 / 10. y++) { if(productDigit[x] == startDigit[y]) { count++. public class E10_Vampire { public static void main(String[] args) { int[] startDigit = new int[4]. productDigit[1] = (product % 1000) / 100. int product = num1 * num2.Exercise 10 //: control/E10_Vampire. y < 4. x < 4. startDigit[y] = -2. for(int num1 = 10.java /****************** Exercise 10 ********************* * A vampire number has an even number of digits and * is formed by multiplying a pair of numbers containing * half the number of digits of the result. num1 <= 99. productDigit[x] = -1. num2++) { // Pete Hartley's theoretical result: // If x·y is a vampire number then // x·y == x+y (mod 9) if((num1 * num2) % 9 != (num1 + num2) % 9) continue. int count = 0.) ****************************************************/ package control. num2 <= 99. if(count == 4) 46 Thinking in Java. for(int x = 0. x++) for(int y = 0. and is optimized by using Pete Hartley’s theoretical result (see the comment inside main( )). } } } } } /* Output: 15 * 93 : 1395 21 * 60 : 1260 21 * 87 : 1827 27 * 81 : 2187 30 * 51 : 1530 35 * 41 : 1435 80 * 86 : 6880 *///:~ The program does not produce duplicates. Controlling Execution 47 .out.System.println(num1 + " * " + num2 + " : " + product). . System.java /****************** Exercise 01 **************** * Create a class with an uninitialized * String reference. What is the * difference between the two approaches? ***********************************************/ package initialization. 49 . public class E01_StringRefInitialization { String s. we have included package statements for chapters before Access Control. your solutions are still correct. Demonstrate that this * reference is initialized by Java to null.println("sri.s = " + sri. public static void main(String args[]) { E01_StringRefInitialization sri = new E01_StringRefInitialization().out. If you have solved the problems in this chapter without using package statements. and another one * initialized by the constructor.Initialization & Cleanup To satisfy IDEs like Eclipse. } } /* Output: sri.s). ***********************************************/ package initialization.java /****************** Exercise 2 ***************** * Create a class with a String field initialized * at the point of definition.s = null *///:~ Exercise 2 //: initialization/E02_StringInitialization. Exercise 1 //: initialization/E01_StringRefInitialization. println("Default constructor").out. System. so there is no need to create and hold a reference to the object. which is set to null as the object is created. ************************************************/ package initialization. The more flexible s2 field lets you choose what value to give it when you call the constructor.println("si.s2). public class E03_DefaultConstructor { E03_DefaultConstructor() { System. } public static void main(String args[]) { new E03_DefaultConstructor().println("si. 4th Edition Annotated Solution Guide .public class E02_StringInitialization { String s1 = "Initialized at definition".java /****************** Exercise 3 ****************** * Create a class with a default constructor (one * that takes no arguments) that prints a * message.s1 = " + si. so is the s2 field.s1 = Initialized at definition si.out. public E02_StringInitialization(String s2i) { s2 = s2i.out. Create an object of this class. System. } public static void main(String args[]) { E02_StringInitialization si = new E02_StringInitialization( "Initialized at construction"). whereas s1 always has the same value.s1). technically.s2 = " + si. 50 Thinking in Java. Exercise 3 //: initialization/E03_DefaultConstructor. } } /* Output: Default constructor *///:~ Here we create the E03_DefaultConstructor object for the side effects of the constructor call. } } /* Output: si.s2 = Initialized at construction *///:~ The s1 field is initialized before the constructor is entered. String s2. In practice. } } /* Output: Default constructor String arg constructor Overloaded *///:~ Exercise 5 //: initialization/E05_OverloadedDog.out..java /****************** Exercise 5 ***************** * Create a class called Dog with an overloaded * bark() method. Write a main() that calls Initialization & Cleanup 51 . depending on which overloaded * version is called. (See The meaning of static section in TIJ4 for more information. and should print different types of barking.println("Default constructor"). then a static utility method is more appropriate. etc. } E04_OverloadedConstructor(String s) { System.println(s). System.out. // Call overloaded version: new E04_OverloadedConstructor("Overloaded").) Exercise 4 //: initialization/E04_OverloadedConstructor. Your method should be * overloaded based on various primitive data * types.println("String arg constructor").java /****************** Exercise 4 ***************** * Add an overloaded constructor to Exercise 3 that * takes a String argument and prints it along with * your message.out. public class E04_OverloadedConstructor { E04_OverloadedConstructor() { System. when an operation doesn’t actually require an object. ***********************************************/ package initialization. } public static void main(String args[]) { // Call default version: new E04_OverloadedConstructor(). * howling. 1).* all the different versions. 4th Edition Annotated Solution Guide . (This is similar to some questions on the Sun Java Certification Exam. dog.bark(). class Dog { public void bark() { System. Verify that this works. } // Etc.println("Default bark!"). dog.. but in reversed order relative to each * other.java /****************** Exercise 6 ****************** * Modify Exercise 5 so two of the overloaded * methods have two arguments of two different * types..bark(1. ***********************************************/ package initialization. } public class E05_OverloadedDog { public static void main(String args[]) { Dog dog = new Dog(). . ************************************************/ package initialization.out.bark(1). dog.out. class Dog2 { 52 Thinking in Java.println("double bark = yip"). Now overload the method to return a long.out. } } /* Output: Default bark! int bark = howl double bark = yip *///:~ As an additional challenge.) Exercise 6 //: initialization/E06_SwappedArguments. } public void bark(int i) { System. write a class with a method boolean print(int) that prints a value and returns a boolean. } public void bark(double d) { System.println("int bark = howl"). public class E06_SwappedArguments { public static void main(String args[]) { Dog2 dog = new Dog2(). 2. double } public void bark(double d. } } /* Output: int.bark(1.public void bark(int i. In the example. Initialization & Cleanup 53 .println("double.2). you know it was created. then * create an object of that class in main() to * verify that the default constructor is * automatically synthesized. { bark"). even if you can’t see it. int i) System.println("int. Exercise 7 //: initialization/E07_SynthesizedConstructor. dog. int } } { bark"). 1).out. double bark double. double d) System. public class E07_SynthesizedConstructor { public static void main(String args[]) { // Call the synthesized default constructor // for this class: new E07_SynthesizedConstructor(). ************************************************/ package initialization. int bark *///:~ Note that not only the type of arguments but also their order distinguish an overloaded method. dog.out.java /****************** Exercise 7 ****************** * Create a class without a constructor.bark(2. } } ///:~ Because it’s possible to call the constructor. the two versions of bark( ) are unique.2. Use the this. the first time without using this.println("b() called"). public class E09_ThisConstructorCall { public E09_ThisConstructorCall(String s) { System.b(). * (You should not use this form in practice. otherwise you risk confusing the reader/maintainer of your code. } } /* Output: b() called b() called *///:~ This exercise shows that this refers to the current object. Exercise 9 //: initialization/E09_ThisConstructorCall. } public void b() { System.b( ) style of method call only when necessary. 54 Thinking in Java. public class E08_ThisMethodCall { public void a() { b().out.java /****************** Exercise 8 ***************** * Create a class with two methods. call the second * constructor inside the first one.) ***********************************************/ package initialization. * and the second time using this. Using this.java /****************** Exercise 9 ***************** * Create a class with two (overloaded) * constructors. Within the * first method call the second method twice to * see it work. ***********************************************/ package initialization. this. 4th Edition Annotated Solution Guide .Exercise 8 //: initialization/E08_ThisMethodCall.a().out.println("s = " + s). } public static void main(String args[]) { new E08_ThisMethodCall(). Exercise 10 //: initialization/E10_FinalizeCall. Explain the behavior of your * program. public class E10_FinalizeCall { protected void finalize() { System. In main(). ************************************************/ package initialization. } } ///:~ You probably won’t see the finalizer called because the program doesn’t usually generate enough garbage for the collector to run.java /****************** Exercise 10 ***************** * Create a class with a finalize() method that * prints a message. ***********************************************/ Initialization & Cleanup 55 .} public E09_ThisConstructorCall(int i) { this("i = " + i). } public static void main(String args[]) { new E09_ThisConstructorCall("String call").java /****************** Exercise 11 **************** * Modify Exercise 10 so your * finalize() will always be called. new E09_ThisConstructorCall(47). } public static void main(String args[]) { new E10_FinalizeCall().out. create an object * of your class.println("finalize() called"). Exercise 11 //: initialization/E11_FinalizeAlwaysCalled. } } /* Output: s = String call s = i = 47 *///:~ Here’s a situation where you are forced to use the this keyword. runFinalization(). System. public Tank() { System. Ultimately.package initialization.println("Tank " + id + " created"). boolean full. } } /* Output: finalize() called *///:~ Calling System. } public static void main(String args[]) { new E11_FinalizeAlwaysCalled(). } public void empty() { full = false. In main().out. class Tank { static int counter. test the possible * scenarios that can occur when you use Tank. System.runFinalization( ) in sequence will probably but not necessarily call your finalizer (The behavior of finalize has been uncertain from one version of JDK to another.gc(). int id = counter++. * Write a finalize() that verifies this termination * condition.gc( ) and System.println( 56 Thinking in Java. nothing guarantees that finalize( ) will be called. with a termination condition that it * must be empty when the object is cleaned up. ******************************************************/ package initialization.) The call to these methods is just a request. 4th Edition Annotated Solution Guide . } protected void finalize() { if(full) System. it doesn’t ensure the finalizer will actually run.out. full = true.java /********************* Exercise 12 ******************** * Create a class called Tank that can be filled * and emptied.out. public class E11_FinalizeAlwaysCalled { protected void finalize() { System.println("finalize() called"). Exercise 12 //: initialization/E12_TankWithTerminationCondition. java /****************** Exercise 13 ***************** * Comment the line marked (1) in * ExplicitStatic.gc( ) was called so they wouldn’t be cleaned up. Another option is to set references to zero when you want them to be garbage-collected. You can never be sure finalizers will be called. } } public class E12_TankWithTerminationCondition { public static void main(String args[]) { new Tank(). } public String toString() { return "Tank " + id. for example. to ensure they’ve been cleaned up properly.java and verify that the static * initialization clause is not called. } } /* Output: Tank 0 created Tank 1 created Error: tank 1 must be empty at cleanup Tank 0 cleaned up OK *///:~ We created no references to the two instances of type Tank. new Tank()."Error: tank " + id + " must be empty at cleanup"). so their utility is limited. // Don't empty the second one System. check the state of objects when it does run.gc(). because those references would be in scope when System.empty(). Modify the above example to try this method. Exercise 13 //: initialization/E13_LeftToReader. Now * uncomment one of the lines marked (2) and * verify that the static initialization clause * is called. A finalizer can. Finally. Initialization & Cleanup 57 .out.println("Tank " + id + " cleaned up OK"). thus they wouldn’t be finalized. else System. ************************************************/ package initialization.runFinalization(). uncomment the other line marked * (2) and verify that static initialization * occurs only once. // Force finalization? System. Add a static method that prints both * fields and demonstrates that they are both * initialized before they are used. ************************************************/ package initialization.out." ***********************************************/ package initialization.println("s1 = " + s1).out. System.out.println("Exercise left to reader"). Exercise 15 //: initialization/E15_StringInstanceInitialization. 4th Edition Annotated Solution Guide . static String s2.public class E13_LeftToReader { public static void main(String args[]) { System. } } ///:~ Exercise 14 //: initialization/E14_StaticStringInitialization. } public static void main(String args[]) { System.java /****************** Exercise 14 ***************** * Create a class with a static String field that * is initialized at the point of definition. and * another one initialized by the static * block. } } /* Output: s1 = Initialized at definition s2 = Initialized in static block *///:~ main( ) is a static method. static { s2 = "Initialized in static block".java /****************** Exercise 15 **************** * Create a class with a String that is * initialized using "instance initialization. so we used it to print the values. public class E14_StaticStringInitialization { static String s1 = "Initialized at definition".println("s2 = " + s2). public class E15_StringInstanceInitialization { 58 Thinking in Java. length. sa1[3] = "strings". for(int i = 0. // Using aggregate initialization to // make it easier: String sa2[] = { "These". Print the array using a for loop. s = " + s). sa1[1] = "are".out.println("int constructor. s = " + s).out. i < sa2. sa1[0] = "These". s = 'instance initialization' int constructor. ************************************************/ package initialization. } public E15_StringInstanceInitialization(int i) { System. "some". you’ll see that instance initialization occurs before either of the two constructors. "strings" }.java /****************** Exercise 16 ***************** * Assign a string to each element of an array of * String objects. i < sa1. for(int i = 0. sa1[2] = "some". } } /* Output: Default constructor. } public static void main(String args[]) { new E15_StringInstanceInitialization(). "are".String s. Exercise 16 //: initialization/E16_StringArray. } public E15_StringInstanceInitialization() { System.println(sa1[i]). s = 'instance initialization' *///:~ When you run this program.out.println("Default constructor. public class E16_StringArray { public static void main(String args[]) { // Doing it the hard way: String sa1[] = new String[4]. i++) System. i++) System. new E15_StringInstanceInitialization(1). Initialization & Cleanup 59 . { s = "'instance initialization'".println(sa2[i]).out.length. public static void main(String args[]) { // Or as a temporary inside main: Test[] array2 = new Test[5]. notice * whether the initialization messages from the * constructor calls are printed. ************************************************/ package initialization. Create an array of object * references to this class. When you run the program. but don't * create objects to assign into the * array. } } ///:~ 60 Thinking in Java. During construction.java /****************** Exercise 17 ***************** * Create a class with a constructor that takes * a String argument. s = " + s).println("String constructor. 4th Edition Annotated Solution Guide .} } /* Output: These are some strings These are some strings *///:~ The solution shows both ways to solve the exercise: you can either create the array object and assign a string into each slot by hand.out. } } public class E17_ObjectReferences { // You can define the array as a field in the class: Test[] array1 = new Test[5]. Exercise 17 //: initialization/E17_ObjectReferences. which creates and initializes the array object in a single step. class Test { Test(String s) { System. or use aggregate initialization. print * the argument. public class E19_VarargStringArray { Initialization & Cleanup 61 . ************************************************/ package initialization. } } /* Output: String constructor. s = 0 String constructor.) Exercise 19 //: initialization/E19_VarargStringArray.This code creates only the array.toString(i)). for(int i = 0. not the objects that go into it. s = 1 String constructor. public class E18_ObjectArray { public static void main(String args[]) { Test[] array = new Test[5]. You don’t see initialization messages in Test’s constructors because no instances of that class exist. s = 2 String constructor.toString( ) returns a String object representing the specified integer. Can you find a way to convert an integer into a String without using this utility method? (Hint: Recall previous discussions of the overloaded String operator +. Exercise 18 //: initialization/E18_ObjectArray. i++) array[i] = new Test(Integer. s = 4 *///:~ Integer.java /****************** Exercise 18 ***************** * Create objects to attach to the array of * references for Exercise 17.java /****************** Exercise 19 ***************** * Write a method that takes a vararg String * array. i < array. Verify that you can pass either a * comma-separated list of Strings or a * String[] into this method. s = 3 String constructor.length. ************************************************/ package initialization. strings *///:~ 62 Thinking in Java.. } public static void main(String args[]) { printStrings("These". "some".static void printStrings(String. } } /* Output: These are some strings These are some strings *///:~ Exercise 20 //: initialization/E20_VarargMain.out. Print all the * elements in the resulting args array. "are". public class E20_VarargMain { public static void main(String.java // {Args: These. ************************************************/ package initialization. Test it * with various numbers of command-line * arguments. } } /* Output: These.. 4th Edition Annotated Solution Guide . some. strs) { for(String s : strs) System. are.. "are". "strings"). strings} /****************** Exercise 20 ***************** * Create a main() that uses varargs instead * of the ordinary main() syntax.. are. args) { E19_VarargStringArray. some.println(s). printStrings( new String[] { "These". "strings" } ).printStrings(args). "some". switch(pct) { case ONE: print("George Washington").Print. Loop through the values() * and print each value and its ordinal(). public class E22_PaperCurrencyTypesEnum2 { static void describe(PaperCurrencyTypes pct) { printnb(pct + " has a portrait of "). ordinal 2 TEN.println(s + ". ordinal 4 FIFTY. import static net. FIVE. For each case. ordinal " + s.mindview. ***********************************************/ package initialization. Initialization & Cleanup 63 . FIFTY } public class E21_PaperCurrencyTypesEnum { public static void main(String args[]) { for(PaperCurrencyTypes s : PaperCurrencyTypes.values()) System. TEN.java /****************** Exercise 21 **************** * Create an enum of the six lowest denominations * of paper currency. ***********************************************/ package initialization. ordinal 3 TWENTY. TWO. output a * description of that particular currency. ordinal 1 FIVE.ordinal()). enum PaperCurrencyTypes { ONE. ordinal 0 TWO.out.Exercise 21 //: initialization/E21_PaperCurrencyTypesEnum. } } /* Output: ONE.*. TWENTY.util.java /****************** Exercise 22 **************** * Write a switch statement for the enum in * Exercise 21. ordinal 5 *///:~ Exercise 22 //: initialization/E22_PaperCurrencyTypesEnum2. break. } } /* Output: ONE has a portrait of George Washington TWO has a portrait of Thomas Jefferson FIVE has a portrait of Abraham Lincoln TEN has a portrait of Alexander Hamilton TWENTY has a portrait of Andrew Jackson FIFTY has a portrait of U. case TEN: print("Alexander Hamilton"). break.S. } } public static void main(String args[]) { for(PaperCurrencyTypes s : PaperCurrencyTypes. case FIFTY: print("U. 4th Edition Annotated Solution Guide . break. Grant *///:~ 64 Thinking in Java.values()) describe(s).S. case FIVE: print("Abraham Lincoln"). break. break. break. Grant"). case TWENTY: print("Andrew Jackson").case TWO: print("Thomas Jefferson"). ***********************************************/ package access.Access Control Exercise 1 //: access/local/E01_PackagedClass.java /****************** Exercise 1 **************** * Create a class in a package. public class E02_LeftToReader { public static void main(String args[]) { System. public class E01_ForeignClass { public static void main(String[] args) { new access. then continues into access/local. public class E01_PackagedClass { } ///:~ Create the above file in the access/local subdirectory. Create an * instance of your class outside of that package.java package access.out. } 65 . (Be sure the file is in a directory that starts at a CLASSPATH location.E01_PackagedClass().) Then create the following file in the access directory.local. which is above the local directory and thus outside of the access. and verify that * collisions do in fact occur. } } ///:~ Exercise 2 //: access/E02_LeftToReader.java /****************** Exercise 2 ****************** * Turn the code fragments in the "Collisions" * section into a program.local.local package: //: access/E01_ForeignClass.println("Exercise left to reader"). ***********************************************/ package access. E03_Debug.*. public class E03_ReleaseApp { public static void main(String[] args) { 66 Thinking in Java.java package access.debugoff. ***********************************************/ package access. * Import the class into a test program * using a static import line.out.println("Message: " + msg). public class E03_Debug { public static void debug(String msg) {} } ///:~ //: access/E03_DebugApp.java /****************** Exercise 3 **************** * Create two packages: debug and debugoff. the second does nothing.debug.*. import static access. } } /* Output: Message: DEBUG VERSION *///:~ //: access/E03_ReleaseApp.java package access. } } ///:~ //: access/debugoff/E03_Debug. 4th Edition Annotated Solution Guide . The first version displays its String * argument to the console. and demonstrate * the conditional compilation effect. public class E03_DebugApp { public static void main(String[] args) { debug("DEBUG VERSION").E03_Debug.java package access. import static access.debug.debugoff.} ///:~ Exercise 3 //: access/debug/E03_Debug. public class E03_Debug { public static void debug(String msg) { System. * containing an identical class with a debug() * method. Exercise 4 //: access/local/E04_PackagedClass. ***********************************************/ package access.greeting().println("Hello client programmer!").debug("RELEASE VERSION").local.out. } } ///:~ The release version E03_ReleaseApp prints no message. public class E04_ConsumerInSamePackage { public static void main(String[] args) { E04_PackagedClass.local. public class E04_PackagedClass { protected static void greeting() { System. } } ///:~ //: access/local/E04_ConsumerInSamePackage. } } ///:~ Explain why the compiler generates an error for E04_ForeignClass. public class E04_ForeignClass { public static void main(String[] args) { access.java.local package make a difference? Access Control 67 .E04_PackagedClass.java // {CompileTimeError} to see results package access.local. } } /* Output: Hello client programmer! *///:~ //: access/E04_ForeignClass.java /****************** Exercise 4 **************** * Show that protected methods have package * access but are not public. Would making the E04_ForeignClass class part of the access.java package access.greeting(). (See Exercises 6 & 9.f1(). * protected.b = 2.f3(). // No problem accessing everything inside // of main() for this class.local.local.d = 4.local. test. test.java /****************** Exercise 5 ****************** * Create a class with public. } } ///:~ 68 Thinking in Java. since main() // is a member and therefore has access: test. ***********************************************/ package access. it would share the same package as E04_PackagedClass. test. // Package access public void f1() {} private void f2() {} protected void f3() {} void f4() {} // Package access public static void main(String args[]) { E05_AccessControl test = new E05_AccessControl().Solution: E04_PackagedClass is in its own package.a = 1. test. and package-access fields and * method members. test. int d. private. protected int c. test.f4(). 4th Edition Annotated Solution Guide .f2(). and greeting( ) is not a public method so is generally unavailable outside of the package access.) Exercise 5 //: access/local/E05_AccessControl. and so could access it.c = 3. test. private int b. If E04_ForeignClass were included in access. public class E05_AccessControl { public int a. * Remember that classes in the same directory * are part of the "default" package.greeting( ). Create an object of this class * and see what kind of compiler messages you get * when you try to access all the class members. f1().a = 1. test. // Can't access: package } } ///:~ Access Control 69 .local.f1(). //! test.E05_AccessControl. //! test. test. // Can't access: package test. If you create a separate class within the same package. that class cannot access the private members: //: access/local/E05_Other.f4().local.f3(). public class E05_Other { public E05_Other() { E05_AccessControl test = new E05_AccessControl(). // Can't access: private //! test.a = 1.b = 2. test.d = 4.d = 4. //! test. // Can't access: private test.f2().c = 3. // Can't access: protected //! test. // Can't access: protected //! test.java // A separate class in the same package cannot // access private elements: package access. protected and package elements: package access.You can see that main( ) is a member and so has access to everything. // Can't access: private //! test.b = 2. test.c = 3.f2(). test. } } ///:~ When you create a class in a separate package (by either using a package statement or putting it in a different directory) then it can access only public members: //: access/E05_Other. public class E05_Other { public E05_Other() { E05_AccessControl test = new E05_AccessControl().f3(). // Can't access: private test.f4(). import access. //! test.java // A separate class in the other package cannot // access private. 4th Edition Annotated Solution Guide . You can always access protected fields within the same package. } 70 Thinking in Java. } public class E06_ProtectedManipulation { public static void main(String args[]) { WithProtected wp = new WithProtected().i = " + wp.Exercise 6 //: access/E06_ProtectedManipulation.out.i = 47 *///:~ This exercise shows that protected also means “package access” (a. As a further exercise.Widget.e07.i = 47. Exercise 7 //: access/e07/E07_Widget.k.java /****************** Exercise 6 ***************** * Create one class with protected data.java /****************** Exercise 7 ***************** * Create the library according to the code * fragments describing access and Widget.i). and a * second class in the same file with a method * that manipulates that protected data. import access. ***********************************************/ package access. class WithProtected { protected int i. ***********************************************/ package access. public class E07_Widget { public static void main(String args[]) { new Widget().println("wp. add a protected method to WithProtected and access it from within E06_ProtectedManipulation. } } /* Output: wp.a. wp. “friendly”). Create * a Widget in a class that is not part of the * access package. System. } } ///:~ Exercise 8 //: access/E08_ConnectionManager.println(c).getConnection(). * ConnectionManager returns a null reference when * it runs out of objects. c = ConnectionManager.*. import access.getConnection(). c. but only get them * via a static method in ConnectionManager. * The client programmer must not be able to * create Connection objects. while(c != null) { System. * create a class called ConnectionManager that * manages a fixed array of Connection objects.java.println("Making a Widget").connection.doSomething().out.java /****************** Exercise 8 ***************** * Following the form of the example Lunch.} /* Output: Making a Widget *///:~ //: access/Widget. ***********************************************/ package access.java package access. } } } /* Output: Connection 0 Connection 1 Connection 2 Connection 3 Connection 4 Connection 5 Connection 6 Access Control 71 . public class Widget { public Widget() { System. public class E08_ConnectionManager { public static void main(String args[]) { Connection c = ConnectionManager. Test the classes in main().out. length) return pool[counter++].connection. i < pool. static { for(int i = 0.connection. Connection also has a doSomething( ) method to indicate the task for which you created the Connection object. } public void doSomething() {} } ///:~ //: access/connection/ConnectionManager. return null. public class Connection { private static int counter = 0. which produces the identifier as part of its toString( ) representation.length. The only way to get Connection objects is through the ConnectionManager. } } ///:~ The Connection class identifies each Connection object with a static int called counter. private int id = counter++. } // Very simple -. Connection() {} public String toString() { return "Connection " + id. 4th Edition Annotated Solution Guide . Note that the constructor for Connection has package access. so the client programmer cannot access it to make instances of Connection directly.just hands out each one once: public static Connection getConnection() { if(counter < pool.java package access. it is unavailable outside of this package. private static int counter = 0. i++) pool[i] = new Connection().java package access. public class ConnectionManager { private static Connection[] pool = new Connection[10]. 72 Thinking in Java.Connection 7 Connection 8 Connection 9 *///:~ //: access/connection/Connection. *. // Use up all the connections for(int i = 0.checkIn().connection2. System. i++) { ca[i].java package access. c. } } } /* Output: null Connection 0 Connection 0 Connection 0 Connection 0 Connection 0 *///:~ //: access/connection2/Connection. import access.checkIn(). package access.out. // Return connections.out. // Should produce "null" since there are no // more connections: System.println(ConnectionManager. c. private int id = counter++. Connection() {} public String toString() { Access Control 73 . (TIJ4 covers static clauses in detail.) Here is a more sophisticated connection manager that allows the client programmer to “check in” a connection when finished with it: //: access/E08_ConnectionManager2.getConnection()).println(c). public class E08_ConnectionManager2 { public static void main(String args[]) { Connection[] ca = new Connection[10]. public class Connection { private static int counter = 0.getConnection().doSomething().connection2. i < 10.ConnectionManager initializes a static array of objects inside the static clause that is called only once when the class loads. i++) ca[i] = ConnectionManager. then get them out: for(int i = 0.getConnection(). Connection c = ConnectionManager. i < 5.java // Connections that can be checked in. java package access. When the client programmer is done with the connection. i++) if(pool[i] != null) { Connection c = pool[i]. // None left } public static void checkIn(Connection c) { for(int i = 0. i < pool. } } ///:~ //: access/connection2/ConnectionManager. public class ConnectionManager { private static Connection[] pool = new Connection[10]. // Indicates "in use" return c. } public void doSomething() {} public void checkIn() { ConnectionManager. 4th Edition Annotated Solution Guide . } } } ///:~ When a Connection is checked out.net). there are all kinds of potential problems with this approach.length. i < pool.checkIn(this). i++) if(pool[i] == null) { pool[i] = c.length. or checks in more than once? We address the problem of the connection pool more thoroughly in Thinking in Patterns with Java (available from www. 74 Thinking in Java. What if a client checks in a Connection and then continues to use it. i++) pool[i] = new Connection().MindView. its slot in pool is set to null. static { for(int i = 0. pool[i] = null.connection2.length. i < pool.return "Connection " + id. checkIn( ) returns it to the connection pool by assigning it to a null slot in pool. } return null. However. // Check it back in return. } // Produce the first available connection: public static Connection getConnection() { for(int i = 0. local.Exercise 9 (See TIJ4 for problem description. so is unavailable outside of package access.local.) Solution: PackagedClass is in its own package and is not a public class. then it would be in the same package as PackagedClass and would have access to it. Access Control 75 . If Foreign were also part of access. . mindview. public Second(String si) { s = si.Reusing Classes Exercise 1 //: reusing/E01_Composition. } private Simple lazy() { if(simple == null) { print("Creating Simple"). } public String toString() { return s. simple = new Simple(s). ***********************************************/ package reusing. } return simple.Print. import static net. Inside a second class. class Simple { String s. } public Simple getSimple() { return lazy(). * define a reference to an object of the first * class.*. public Simple(String si) { s = si. } public String toString() { 77 . } public void setString(String sNew) { s = sNew. Use lazy initialization to instantiate * this object. else print("initialized"). } } class Second { Simple simple. String s. // 'simple' not initialized } public void check() { if(simple == null) print("not initialized").java /****************** Exercise 1 ***************** * Create a simple class.util. * Override scrub() and add a new method called * sterilize().toString(). We added print statements to show when initialization occurs.check(). The lazy( ) method is called by all the other methods to access the Simple object.setSimple("New String"). } public void setSimple(String sNew) { lazy(). print(second). print(second). 4th Edition Annotated Solution Guide . print(second.java /****************** Exercise 2 ***************** * Inherit a new class from class Detergent. Exercise 2 //: reusing/E02_NewDetergent. which creates (if it hasn’t been) the Simple object and then returns it. ***********************************************/ package reusing. and that it happens only once.getSimple()). } } /* Output: not initialized Creating Simple Init String initialized Init String New String *///:~ The Simple class has some data and methods. second.check(). The Second class performs lazy initialization through the lazy( ) method. class NewDetergent extends Detergent { public void scrub() { 78 Thinking in Java. } } public class E01_Composition { public static void main(String args[]) { Second second = new Second("Init String"). second. // toString() call second.setString(sNew).return lazy(). class CartoonWithDefCtor extends Drawing { //! CartoonWithDefCtor() { //! System.java /****************** Exercise 3 ****************** * Even if you don't create a constructor for * Cartoon(). // Doesn't have to be first } public void sterilize() { append(" sterilize()"). } } /* Output: Cleanser dilute() NewDetergent.scrub() scrub() sterilize() *///:~ Exercise 3 //: reusing/E03_CartoonWithDefCtor. nd. super.sterilize().out. } } /* Output: Art constructor Drawing constructor *///:~ Reusing Classes 79 .dilute().scrub()"). System. } } public class E02_NewDetergent { public static void main(String args[]) { NewDetergent nd = new NewDetergent(). ***********************************************/ package reusing.scrub() Detergent.scrub().println(nd). the compiler will synthesize a * default constructor that calls the base-class * constructor. nd.out. nd.scrub().println("CartoonWithDefCtor constructor").append(" NewDetergent. //! } } public class E03_CartoonWithDefCtor { public static void main(String args[]) { new CartoonWithDefCtor (). Prove that assertion. Exercise 4 //: reusing/E04_ConstructorOrder. a default constructor is not synthesized if you define any constructors but not the default. The compiler ensures that a constructor is called.out. in which it calls the base class Drawing default constructor. however. it calls the default constructor if available. } } class Derived1 extends Base1 { public Derived1() { System.java /****************** Exercise 4 ****************** * Prove that base-class constructors are (a) * always called and (b) called before * derived-class constructors.out.println("Derived1").We commented out the CartoonWithDefCtor constructor above.out.println("Derived2"). } } /* Output: Base1 Derived1 Derived2 *///:~ 80 Thinking in Java. If you don’t call a constructor.println("Base1"). } } public class E04_ConstructorOrder { public static void main(String args[]) { new Derived2(). 4th Edition Annotated Solution Guide . which in turn calls the base class Art default constructor. class Base1 { public Base1() { System. TIJ4 shows this output by printing in the constructor. ***********************************************/ package reusing. } } class Derived2 extends Derived1 { public Derived2() { System. The compiler synthesizes the default CartoonWithDefCtor constructor. } } class B { public B() { System. then the member object constructors. class A { public A() { System.println("B()").println("A()"). ***********************************************/ package reusing. Inherit a new class * called C from A.java // {CompileTimeError} /****************** Exercise 6 ***************** * If you don’t call the base-class constructor * in BoardGame(). and create a member of class * B inside C. } } class C extends A { B b = new B().out. Do not create a constructor for C.Exercise 5 //: reusing/E05_SimpleInheritance. } } /* Output: A() B() *///:~ Here the compiler synthesizes a constructor for C. the compiler will respond Reusing Classes 81 . Exercise 6 //: reusing/E06_ChessWithoutDefCtor. first calling the base-class constructor.out. } public class E05_SimpleInheritance { public static void main(String args[]) { new C(). * Create an object of class C and observe the * results.java /****************** Exercise 5 ***************** * Create classes A and B with default * constructors (empty argument lists) that * announce themselves. } } ///:~ BoardGame has no default constructor for ChessWithoutDefCtor. The {CompileTimeError} directive takes the compilation out of the build process because this compilation fails.* that it can’t find a constructor of the form * Game(). class ChessWithoutDefCtor extends BoardGame { //ChessWithoutDefCtor () { // System. The call to the base-class * constructor must be the first thing you do * in the derived-class constructor. so the program won’t compile. so the compiler cannot generate a default. ***********************************************/ package reusing.java to prove those assertions.println("A2(): " + s). 4th Edition Annotated Solution Guide . //} } public class E06_ChessWithoutDefCtor { public static void main(String args[]) { new ChessWithoutDefCtor(). } 82 Thinking in Java. class A2 { public A2(String s) { System.out. the compiler will demand that you first call the base-class constructor in the derived-class constructor. ***********************************************/ package reusing.out. // super(11).println("ChessWithoutDefCtor constructor"). (The compiler * will remind you if you get it wrong. Moreover. BoardGame defines a constructor that takes an argument.java /****************** Exercise 7 ****************** * Modify Exercise 5 so A and B have * constructors with arguments instead of default * constructors.) * Use Chess. Write a constructor for C and * perform all initialization within it. nor its own default constructor that the compiler can use to synthesize one. if you uncomment the default constructor definition for ChessWithoutDefCtor. Exercise 7 //: reusing/E07_SimpleInheritance2. ***********************************************/ package reusing. } } /* Output: A2(): Init string 2B(): Init string *///:~ We added the 2’s to keep class names in the same directory from clashing. } } class C2 extends A2 { B2 b.out.} class B2 { public B2(String s) { System. b = new B2(s). } Reusing Classes 83 . * Call the base-class constructor in the * derived-class constructors. Remember that super calls the base-class constructor and must be the first call in a derived-class constructor. public C2(String s) { super(s). Exercise 8 //: reusing/E08_CallBaseConstructor. } } public class E07_SimpleInheritance2 { public static void main(String args[]) { new C2("Init string"). and a derived class with both a * default (no-arg) and non-default constructor.println("2B(): " + s).java /****************** Exercise 8 ***************** * Create a base class with only a non-default * constructor. class BaseNonDefault { public BaseNonDefault(int i) {} } class DerivedTwoConstructors extends BaseNonDefault { public DerivedTwoConstructors() { super(47). Component2 c2 = new Component2().public DerivedTwoConstructors(int i) { super(i). Component3 c3 = new Component3(). new Component3(). } } public class E08_CallBaseConstructor { public static void main(String args[]) { new DerivedTwoConstructors().println("Component2").out. class Component1 { public Component1() { System.out.println("Component1"). ***********************************************/ package reusing.println("Component3"). System.out. 84 Thinking in Java. } class Stem extends Root { Component1 c1 = new Component1()." * Default constructors for each class should * print a message about that class.println("Root").out. } } class Component2 { public Component2() { System. Component2. } } class Root { Component1 c1 Component2 c2 Component3 c3 public Root() } = = = { new Component1(). and * Component3. } } ///:~ Exercise 9 //: reusing/E09_ConstructorOrder2. Component1. } } class Component3 { public Component3() { System. new Component2().java /****************** Exercise 9 ***************** * Create a class called Root and an instance of * each of three classes. Derive a class Stem from Root that * also contains an instance of each "component. 4th Edition Annotated Solution Guide . new DerivedTwoConstructors(74). println("Component3b " + i).out.java /****************** Exercise 10 ***************** * Modify Exercise 9 so each class only has * non-default constructors. ***********************************************/ package reusing.out.out. class Component1b { public Component1b(int i) { System.public Stem() { System. } } public class E09_ConstructorOrder2 { public static void main(String args[]) { new Stem().out.println("Component1b " + i). } } class Component2b { public Component2b(int i) { System.println("Stem"). } } class Component3b { public Component3b(int i) { System. } } class Rootb { Reusing Classes 85 .println("Component2b " + i). } } /* Output: Component1 Component2 Component3 Root Component1 Component2 Component3 Stem *///:~ Exercise 10 //: reusing/E10_ConstructorOrder3. public Stemb(int i) { super(i). System. import static net.out. ***********************************************/ package reusing. Component3b c3 = new Component3b(6). } } public class E10_ConstructorOrder3 { public static void main(String args[]) { new Stemb(47).util. Exercise 11 //: reusing/E11_Delegation. } class Stemb extends Rootb { Component1b c1 = new Component1b(4).java /****************** Exercise 11 ***************** * Modify Detergent. 4th Edition Annotated Solution Guide . Component2b c2 = new Component2b(5). new Component2b(2).println("Stemb"). } } /* Output: Component1b 1 Component2b 2 Component3b 3 Rootb Component1b 4 Component2b 5 Component3b 6 Stemb *///:~ We display the “Component” argument to clarify the order of constructor calls. // Delegated methods: public void append(String a) { cleanser.java so it uses delegation.println("Rootb"). i) { System. class DetergentDelegation { private Cleanser cleanser = new Cleanser(). } 86 Thinking in Java.Print.mindview.Component1b c1 = Component2b c2 = Component3b c3 = public Rootb(int } new Component1b(1).append(a).*.out. new Component3b(3). println("Component1c dispose"). class Component1c { public Component1c(int i) { System. } public void dispose() { System.out. } } public class E11_Delegation { public static void main(String[] args) { DetergentDelegation.toString(). x.scrub(). } } /* Output: Cleanser dilute() apply() DetergentDelegation.scrub(). Reusing Classes 87 . print("Testing base class:"). } public void scrub() { append(" DetergentDelegation.dilute().out. x. } public void foam() { append(" foam()").main(args).foam(). } public String toString() { return cleanser.scrub() scrub() foam() Testing base class: Cleanser dilute() apply() scrub() *///:~ Exercise 12 //: reusing/E12_Dispose. print(x). cleanser. ***********************************************/ package reusing.java /****************** Exercise 12 ***************** * Add a proper hierarchy of dispose() methods to * all the classes in Exercise 9. } public void apply() { cleanser.apply(). x.dilute().scrub()"). x.println("Component1c"). Cleanser.apply().public void dilute() { cleanser. } public static void main(String[] args) { DetergentDelegation x = new DetergentDelegation().main(args). out. public Rootc(int i) { System. 88 Thinking in Java. c3.out.} } class Component2c { public Component2c(int i) { System. Component2c c2 = new Component2c(2).println("Stemc"). Component3c c3 = new Component3c(6). } public void dispose() { System.out.println("Component2c"). } public void dispose() { System. c1.out. c3.dispose().dispose().println("Rootc").dispose(). System. } } class Component3c { public Component3c(int i) { System. 4th Edition Annotated Solution Guide . public Stemc(int i) { super(i).println("Component3c").println("Stemc dispose"). } } class Rootc { Component1c c1 = new Component1c(1).println("Component2c dispose").out. c2.dispose(). } public void dispose() { System. Component3c c3 = new Component3c(3).println("Component3c dispose").out. c1. Component2c c2 = new Component2c(5).out. c2.dispose().dispose(). } } class Stemc extends Rootc { Component1c c1 = new Component1c(4).out. } public void dispose() { System.println("Rootc dispose"). } } /* Output: Component1c Component2c Component3c Rootc Component1c Component2c Component3c Stemc Stemc dispose Component3c dispose Component2c dispose Component1c dispose Rootc dispose Component3c dispose Component2c dispose Component1c dispose *///:~ Remember. } } public class E12_Dispose { public static void main(String args[]) { new Stemc(47).java /****************** Exercise 13 ***************** * Create a class with a method that is * overloaded three times.super. and show * that all four methods are available in the * derived class. Inherit a new class. ***********************************************/ package reusing. it’s important to call the dispose( ) methods in the reverse order of initialization.out. class ThreeOverloads { public void f(int i) { System. * add a new overloading of the method.println("f(int i)"). Exercise 13 //: reusing/E13_InheritedOverloading. } Reusing Classes 89 .dispose().dispose(). java /****************** Exercise 14 ***************** * In Car.java add a service() method to Engine * and call this method in main(). } } class MoreOverloads extends ThreeOverloads { public void f(String s) { System. } } public class E13_InheritedOverloading { public static void main(String args[]) { MoreOverloads mo = new MoreOverloads().println("f(double d)").println("f(char c)"). public Wheel[] wheel = new Wheel[4].out.f(1).f("Hello"). mo.f(1. } public void f(double d) { System. public Door 90 Thinking in Java. ***********************************************/ package reusing.out. mo.public void f(char c) { System. } } /* Output: f(int i) f(char c) f(double d) f(String s) *///:~ Exercise 14 //: reusing/E14_ServicableEngine. mo. 4th Edition Annotated Solution Guide .f('c'). mo.println("f(String s)"). class ServicableEngine extends Engine { public void service() {} } class ServicableCar { public ServicableEngine engine = new ServicableEngine().out.1). // 2-door public ServicableCar() { for(int i = 0.window. i < 4.left = new Door(). } } public class E14_ServicableEngine { public static void main(String[] args) { ServicableCar car = new ServicableCar(). // Accessible in derived class } } public class E15_ProtectedTest { public static void main(String args[]) { Reusing Classes 91 .java package reusing.engine. right = new Door().protect. car.*. } } ///:~ Exercise 15 //: reusing/protect/E15_Protected. import reusing.java /****************** Exercise 15 ***************** * Create a class with a protected method inside * a package. public class E15_Protected { protected void f() {} } ///:~ //: reusing/E15_ProtectedTest. ***********************************************/ package reusing.service(). * Now inherit from your class and call the * protected method from inside a method of your * derived class.left.inflate(72).protect. Try to call the protected method * outside the package. car.rollup(). i++) wheel[i] = new Wheel(). and explain the results.wheel[0]. class Derived extends E15_Protected { public void g() { f(). car. java 92 Thinking in Java. a. // Cannot access new Derived().f().moveInWater().out. * inherit a class from it called Frog. 4th Edition Annotated Solution Guide . Exercise 16 //: reusing/E16_Frog. From it.out. Put * appropriate methods in the base class. } } /* Output: Moving in Water Moving on Land *///:~ Exercise 17 //: reusing/E17_Frog2. In * main().println("Moving in Water"). create a Frog.//! new E15_Protected().println("Moving on Land"). a.g(). } } class Frog extends Amphibian {} public class E16_Frog { public static void main(String args[]) { Amphibian a = new Frog(). * and demonstrate that all the methods still work. ***********************************************/ package reusing. you can only access a protected member inside an inherited class.java /****************** Exercise 16 ***************** * Create a class called Amphibian.moveOnLand(). } public void moveOnLand() { System. class Amphibian { public void moveInWater() { System. } } ///:~ Outside the package. upcast it to Amphibian. } } /* Output: Frog swimming Frog jumping *///:~ Since the compiler has a reference to an Amphibian./****************** Exercise 17 ***************** * Modify Exercise 16 so Frog overrides the * method definitions from the base class * (provides new definitions using the same * method signatures). Exercise 18 //: reusing/E18_FinalFields. it calls the Frog2 methods.out. Note what happens in * main(). Instead.println("Frog swimming").println("Frog jumping"). class SelfCounter { private static int count. } public void moveOnLand() { System. Since a is indeed a reference to a Frog2.java /****************** Exercise 18 ***************** * Create a class with a static final field and a * final field and demonstrate the difference * between the two. } } public class E17_Frog2 { public static void main(String args[]) { Amphibian a = new Frog2(). ***********************************************/ package reusing. class Frog2 extends Amphibian { public void moveInWater() { System. a. Reusing Classes 93 . a. this is the appropriate result. That’s polymorphism: The right behavior happens even if you are talking to a base-class reference. you might guess it will call the Amphibian methods.moveInWater().out. ***********************************************/ package reusing.moveOnLand(). whereas the regular final’s values are different for each instance.out.java /****************** Exercise 19 ***************** * Create a class with a blank final reference to * an object. System. it has the same value in both instances of WithFinalFields. Perform initialization of the * blank final inside all constructors.out.println(new WithFinalFields()). } } class WithFinalFields { final SelfCounter scf = new SelfCounter(). Exercise 19 //: reusing/E19_BlankFinalField. public String toString() { return "scf = " + scf + "\nscsf = " + scsf.println(new WithFinalFields()). static final SelfCounter scsf = new SelfCounter(). ***********************************************/ package reusing. * Demonstrate that the final must * be initialized before use.out. System. } } /* Output: First object: scf = SelfCounter 1 scsf = SelfCounter 0 Second object: scf = SelfCounter 2 scsf = SelfCounter 0 *///:~ Because class loading initializes the static final. public String toString() { return "SelfCounter " + id.println("Second object:").out. } } public class E18_FinalFields { public static void main(String args[]) { System.private int id = count++. and cannot * be changed once initialized.println("First object:"). 4th Edition Annotated Solution Guide . System. class WithBlankFinalField { 94 Thinking in Java. import static net. } } class OverridingPrivate2E20 extends OverridingPrivateE20 { @Override public final void f() { Reusing Classes 95 .g()"). } } /* Output: 10 *///:~ Exercise 20 //: reusing/E20_OverrideAnnotation.f()").java // {CompileTimeError} /****************** Exercise 20 **************** * Show that the @Override annotation solves the * problem from the "final and private" section. return i.geti()). you'll get a compiler error: // "variable i might not have been initialized" public WithBlankFinalField(int ii) { i = new Integer(ii).util.out. } public Integer geti() { // This won't compile. // Without this constructor. } @Override private void g() { print("OverridingPrivateE20.*. } } public class E19_BlankFinalField { public static void main(String args[]) { WithBlankFinalField wbff = new WithBlankFinalField(10). class OverridingPrivateE20 extends WithFinals { @Override private final void f() { print("OverridingPrivateE20.Print. The error is: // "cannot assign a value to final variable i" // if(i == null) // i = new Integer(47). ***********************************************/ package reusing.private final Integer i. System.mindview.println(wbff. 4th Edition Annotated Solution Guide .g().f(). class WithFinalMethod { final void f() {} } public class E21_FinalMethod extends WithFinalMethod { void f() {} public static void main(String args[]) {} } ///:~ Exercise 22 //: reusing/E22_FinalClass.f()"). ***********************************************/ package reusing. } } public class E20_OverrideAnnotation { public static void main(String[] args) { OverridingPrivate2E20 op2 = new OverridingPrivate2E20(). } @Override public void g() { print("OverridingPrivate2E20. 96 Thinking in Java. op2. ***********************************************/ package reusing. } } ///:~ Exercise 21 //: reusing/E21_FinalMethod.print("OverridingPrivate2E20.java // {CompileTimeError} /****************** Exercise 22 **************** * Create a final class and attempt to inherit * from it. Inherit * from that class and attempt to override that * method.java // {CompileTimeError} /****************** Exercise 21 **************** * Create a class with a final method.g()"). op2. Remember.java /****************** Exercise 23 **************** * Prove that class loading takes place only * once and may be caused by either the creation * of the first instance of that class or by * accessing a static member.final class FinalClass {} public class E22_FinalClass extends FinalClass { public static void main(String args[]) {} } ///:~ Exercise 23 //: reusing/E23_ClassLoading. LoadTest.println("Calling static member"). a constructor is a static method. } } /* Output: Calling static member Loading LoadTest Creating an object *///:~ Now modify the code so object creation occurs before the static member call to see that object creation loads the object.out. Reusing Classes 97 . System.println("Creating an object"). ***********************************************/ package reusing.out.println("Loading LoadTest"). } static void staticMember() {} } public class E23_ClassLoading { public static void main(String args[]) { System.staticMember(). new LoadTest(). even though you don’t define it using the static keyword. class LoadTest { // The static clause is executed // upon class loading: static { System.out. inherit a specific type of * beetle from class Beetle. then the next-derived class.Print. and finally the most-derived class. } static int x3 = printInit("static JapaneseBeetle. The base class loads first. 98 Thinking in Java. also starting at the root class.k initialized k = 47 j = 39 JapaneseBeetle. print("j = " + j).java.x2 initialized static JapaneseBeetle. } public class E24_JapaneseBeetle { public static void main(String args[]) { new JapaneseBeetle().*. 4th Edition Annotated Solution Guide . import static net.java /****************** Exercise 24 **************** * In Beetle.x3 initialized"). This creates the object and initializes the non-static members. j = 0 Beetle.m initialized"). following the same * format as the existing classes. } } /* Output: static Insect. class JapaneseBeetle extends Beetle { int m = printInit("JapaneseBeetle. JapaneseBeetle() { print("m = " + m). Trace and * explain the output.x1 initialized static Beetle. ***********************************************/ package reusing.mindview.Exercise 24 //: reusing/E24_JapaneseBeetle.util.x3 initialized i = 9.m initialized m = 47 j = 39 *///:~ Loading the class initializes the static variables. and Tricycle. ***********************************************/ package polymorphism. Demonstrate * that an instance of each type can be upcast * to Cycle via a ride() method.cycle.java package polymorphism.java package polymorphism.java package polymorphism. public class E01_Upcasting { public static void ride(Cycle c) {} public static void main(String[] args) { ride(new Cycle()).cycle. // Upcast ride(new Tricycle()).cycle. 99 . public class Unicycle extends Cycle { } ///:~ //: polymorphism/cycle/Bicycle.java package polymorphism. import polymorphism.cycle. // Upcast ride(new Bicycle()).cycle. // No upcasting ride(new Unicycle()). public class Cycle { } ///:~ //: polymorphism/cycle/Unicycle. // Upcast } } ///:~ //: polymorphism/cycle/Cycle.Polymorphism Exercise 1 //: polymorphism/E01_Upcasting.*.java /****************** Exercise 1 **************** * Create a Cycle class. public class Bicycle extends Cycle { } ///:~ //: polymorphism/cycle/Tricycle. Bicycle. with subclasses * Unicycle. public static void main(String[] args) { Shape[] shapes = new Shape[9].next().draw() Triangle. import polymorphism. ***********************************************/ package polymorphism. // Make polymorphic method calls: for(Shape shape : shapes) shape.java /****************** Exercise 2 **************** * Add the @Override annotation to the shapes * example.shape.draw() Triangle. package polymorphism.length. // Fill up the array with shapes: for(int i = 0.draw() Circle.Shape.oshape. import polymorphism.draw() Square.oshape.shape. i < shapes.draw().draw() Square.draw() Triangle.draw() Square.draw() *///:~ //: polymorphism/oshape/RandomShapeGenerator. public class E02_Shapes { private static RandomShapeGenerator gen = new RandomShapeGenerator(). 4th Edition Annotated Solution Guide .*. import polymorphism.draw() Triangle.java // A "factory" that randomly creates shapes. 100 Thinking in Java. Exercise 2 //: polymorphism/E02_Shapes.public class Tricycle extends Cycle { } ///:~ We created the classes in individual files because they are used in a later example. i++) shapes[i] = gen. } } /* Output: Triangle.Shape. import polymorphism.util.erase()"). } } } ///:~ //: polymorphism/oshape/Square.util.java package polymorphism.draw()"). public class Triangle extends Shape { @Override public void draw() { print("Triangle. } @Override public void erase() { print("Square.erase()").java package polymorphism. } } ///:~ //: polymorphism/oshape/Circle. case 2: return new Triangle().erase()").*. public class Square extends Shape { @Override public void draw() { print("Square.Print.import java. import static net.oshape. public class RandomShapeGenerator { private Random rand = new Random(47).*. import polymorphism. public class Circle extends Shape { @Override public void draw() { print("Circle. } @Override public void erase() { print("Circle.Shape. } } ///:~ //: polymorphism/oshape/Triangle.Shape.*.Shape.shape. import polymorphism. } @Override public void erase() { print("Triangle.mindview.oshape.*.oshape.shape.Print.nextInt(3)) { default: case 0: return new Circle().mindview.draw()").java package polymorphism.util.mindview.util.shape.Print. import static net. case 1: return new Square(). public Shape next() { switch(rand.draw()"). import static net. } } ///:~ Polymorphism 101 . ***********************************************/ package polymorphism. public class E03_NewShapeMethod { public static void main(String args[]) { Shape[] shapes = { new Circle().erase().draw() Triangle. 4th Edition Annotated Solution Guide . Now override it in only one of the * derived classes and see what happens.util. } } } /* Output: Circle.draw() Square.draw() Circle.erase() Circle.Print. but don't * override it in the derived classes.mindview.newshape. Finally. new Square().java that prints a message. new Triangle(). shape. } 102 Thinking in Java.newshape.*. Explain * what happens. import static net.java /****************** Exercise 3 ***************** * Add a new method in the base class of * Shapes.draw(). // Make polymorphic method calls: for(Shape shape : shapes) { shape.msg() Triangle.erase() Square.Exercise 3 //: polymorphism/E03_NewShapeMethod.java package polymorphism.msg() Square.*.msg() *///:~ //: polymorphism/newshape/Shape. shape. * override it in all the derived classes. public class Shape { public void draw() {} public void erase() {} public void msg() { print("Base class msg()").erase() Triangle. import polymorphism. }.msg(). *.java package polymorphism. public class Circle extends Shape { public void draw() { print("Circle. public class Square extends Shape { public void draw() { print("Square.erase()").util. Defining print( ) in only the base class results in: Circle.erase() Base class msg() Nothing overrides the base-class definition.erase()").newshape.util.Print.mindview. } } ///:~ //: polymorphism/newshape/Square. } } ///:~ //: polymorphism/newshape/Triangle.draw() Square.draw() Triangle. Overriding print( ) in Circle produces: Polymorphism 103 .msg()").Print.msg()").mindview. public class Triangle extends Shape { public void draw() { print("Triangle.util.draw() Circle.java package polymorphism.*. } } ///:~ This final version overrides print( ) in all classes.draw()").Print.newshape.draw()").erase()"). } public void msg() { print("Circle.*. import static net.newshape. } public void erase() { print("Triangle.} ///:~ //: polymorphism/newshape/Circle. import static net. } public void erase() { print("Circle. import static net.erase() Base class msg() Square.java package polymorphism.msg()"). } public void msg() { print("Triangle.erase() Base class msg() Triangle. } public void erase() { print("Square. so it is used everywhere.draw()"). } public void msg() { print("Square.mindview. new Triangle(). } } } /* Output: Circle.java /****************** Exercise 4 ***************** * Add a new type of Shape to Shapes.msg().erase().msg() Square. new Square().msg() Square.draw() Triangle. as in Circle. import polymorphism. otherwise we use the default base-class version.erase() Circle. ***********************************************/ package polymorphism. 4th Edition Annotated Solution Guide . we use that version. Exercise 4 //: polymorphism/E04_NewShapeType.erase() Base class msg() Anywhere we override.draw() Circle.erase() Base class msg() Triangle.draw().draw() Circle.Circle.java and * verify in main() that polymorphism works for * your new type as it does in the old types.newshape.draw() Square.draw() Square.erase() Square.erase() Circle.draw() Triangle.msg() Triangle. shape.*. new Tetrahedron() }. // Make polymorphic method calls: for(Shape shape : shapes) { shape. public class E04_NewShapeType { public static void main(String args[]) { Shape[] shapes = { new Circle().erase() 104 Thinking in Java. shape. println("Num. } } /* Output: Num.java /****************** Exercise 5 **************** * Starting from Exercise 1. which returns the number of * wheels.mindview.util.*.draw()").erase() Tetrahedron.msg()").out.msg() Tetrahedron. } public void msg() { print("Tetrahedron. of wheels: 1 Num. of wheels: " + c.Print.msg() *///:~ //: polymorphism/newshape/Tetrahedron. public class E05_Wheels { public static void ride(Cycle c) { System.java package polymorphism.newshape.cycle2. } } ///:~ The other shape definitions are in the same package so we just add the new shape and override the methods. import static net. of wheels: 3 *///:~ Polymorphism 105 .draw() Tetrahedron. public class Tetrahedron extends Shape { public void draw() { print("Tetrahedron. import polymorphism.*. Exercise 5 //: polymorphism/E05_Wheels. The code in the for loop is unchanged from the previous example.erase()"). } public static void main(String[] args) { ride(new Unicycle()). Modify ride() to call wheels() and * verify that polymorphism works. ride(new Tricycle()). of wheels: 2 Num. ***********************************************/ package polymorphism.Triangle. } public void erase() { print("Tetrahedron. ride(new Bicycle()). add a wheels() * method in Cycle.wheels()). util. 4th Edition Annotated Solution Guide . public class Bicycle extends Cycle { public int wheels() { return 2. ***********************************************/ package polymorphism. import polymorphism.cycle2. class Instrument { void play(Note n) { print("Instrument.println() (without * any casting).Print. } 106 Thinking in Java.play() " + n). Print the Instrument * objects using System.Note.java package polymorphism.java package polymorphism. } public String toString() { return "Instrument".java so what() becomes the root * Object method toString(). public class Unicycle extends Cycle { public int wheels() { return 1.java /****************** Exercise 6 ***************** * Change Music3. In later chapters of TIJ4.out. } } ///:~ //: polymorphism/cycle2/Unicycle.java package polymorphism.cycle2.cycle2.//: polymorphism/cycle2/Cycle. } } ///:~ //: polymorphism/cycle2/Bicycle.java package polymorphism.music.mindview. public class Cycle { public int wheels() { return 0.cycle2. you’ll learn better ways to specify wheels( ) than using a dummy implementation.*. } } ///:~ //: polymorphism/cycle2/Tricycle. import static net. } } ///:~ Cycle defines wheels( ) to return 0. public class Tricycle extends Cycle { public int wheels() { return 3. Exercise 6 //: polymorphism/E06_MusicToString. } public String toString () { return "Stringed".play() " + n).play() " + n). } public static void main(String args[]) { printAll(orchestra).void adjust() {} } class Wind extends Instrument { void play(Note n) { print("Wind. } } class Brass extends Wind { void play(Note n) { print("Brass. } } public class E06_MusicToString { static Instrument[] orchestra = { new Wind(). } } class Woodwind extends Wind { void play(Note n) { print("Woodwind. new Woodwind() }. } public String toString () { return "Woodwind". public static void printAll(Instrument[] orch) { for(Instrument i : orch) System.out. new Percussion(). } } class Stringed extends Instrument { void play(Note n) { print("Stringed. } public String toString () { return "Percussion". new Brass().play() " + n).play() " + n).play() " + n). } public String toString () { return "Wind". new Stringed(). } } class Percussion extends Instrument { void play(Note n) { print("Percussion. } } /* Output: Wind Percussion Stringed Polymorphism 107 .println(i). } void adjust() { print("Brass.adjust()"). new Woodwind().java /****************** Exercise 7 ***************** * Add a new type of Instrument to Music3.Print.util. ***********************************************/ package polymorphism. print(i). i. public static void main(String args[]) { for(Instrument i : orchestra) { i.adjust().play() MIDDLE_C Wind Percussion. 4th Edition Annotated Solution Guide . } } } /* Output: Wind.play() MIDDLE_C Stringed Brass.play() MIDDLE_C Brass. import static net. } public String toString() { return "Electronic". } } public class E07_NewInstrument { static Instrument[] orchestra = { new Wind().MIDDLE_C). import polymorphism.mindview.play() " + n). new Electronic() }.Note.adjust() 108 Thinking in Java. new Stringed().Wind Woodwind *///:~ Exercise 7 //: polymorphism/E07_NewInstrument.play(Note. new Percussion().java * and verify that polymorphism works for your * new type. class Electronic extends Instrument { void play(Note n) { print("Electronic.*.play() MIDDLE_C Percussion Stringed. new Brass().music. case 1: return new Percussion(). case 4: return new Woodwind().play() MIDDLE_C Electronic *///:~ Exercise 8 //: polymorphism/E08_RandomInstruments.java does.java /****************** Exercise 8 ***************** * Modify Music3.util.Wind Woodwind.next()). i < 20. case 2: return new Stringed().util.Random(47).java so it randomly creates * Instrument objects the way Shapes. case 5: return new Electronic(). case 3: return new Brass().play() MIDDLE_C Woodwind Electronic. } } /* Output: Stringed Electronic Percussion Electronic Percussion Electronic Woodwind Stringed Polymorphism 109 . } } } public class E08_RandomInstruments { public static void main(String args[]) { InstrumentGenerator gen = new InstrumentGenerator().Random gen = new java. public Instrument next() { switch(gen. ***********************************************/ package polymorphism.out.println(gen. class InstrumentGenerator { java. i++) System. for(int i = 0.nextInt(6)) { default: case 0: return new Wind(). Electronic.Wind Percussion Wind Wind Wind Percussion Electronic Woodwind Woodwind Percussion Stringed Woodwind *///:~ A generator produces a new value each time you call it. features of the Class object enable us to index an array that itself generates various instruments.class.class. Stringed. as in this more elegant solution: //: polymorphism/E08_RandomInstruments2. return (Instrument) instruments[idx].newInstance().class.class. However. class InstrumentGenerator2 { java.Random(47). The generator nextInt( ) produces a random value within the array between zero (inclusive) and the value of the argument (exclusive).abs(gen.class.class.util. } } } 110 Thinking in Java. Class<?> instruments[] = { Wind. package polymorphism.util.java // A more sophisticated solution using features // you'll learn about in later chapters. Woodwind.length)). e). } catch(Exception e) { throw new RuntimeException("Cannot Create". Brass. public Instrument next() { try { int idx = Math. 4th Edition Annotated Solution Guide . }. though you don’t give it any parameters. Percussion.Random gen = new java. Managing the case statement with a random generator can be error-prone.nextInt(instruments. but it can throw exceptions. for(int i = 0.next()). i < 20. the rest of the code takes care of itself. } } /* Output: Stringed Electronic Percussion Electronic Percussion Electronic Woodwind Stringed Wind Percussion Wind Wind Wind Percussion Electronic Woodwind Woodwind Percussion Stringed Woodwind *///:~ The . Here. (We cover exceptions in detail in the chapter Error Handling with Exceptions. so your code doesn’t have to catch such exceptions. You can embed the cause of an error inside a thrown exception to pass detailed information about the condition to a client programmer.newInstance( ) creates an object of the class it is called for.class syntax in the array definition produces references to Class objects for each type of instrument.public class E08_RandomInstruments2 { public static void main(String args[]) { InstrumentGenerator2 gen = new InstrumentGenerator2().java /****************** Exercise 9 ***************** * Create an inheritance hierarchy of Rodent: Polymorphism 111 . we create and throw a RuntimeException for this programming error.) The benefit of this design is that you can add a new type to the system by only adding it to the Class array. i++) System. Class.out. Exercise 9 //: polymorphism/E09_Rodents.println(gen. provide methods that are common to all * Rodents. } public void reproduce() { print("Making more Gerbils"). } } class Mouse extends Rodent { public void hop() { print("Mouse hopping"). import static net. } public void scurry() { print("Gerbil scurrying"). } public void scurry() { print("Rodent scurrying"). class Rodent { public void hop() { print("Rodent hopping").util. * Create an array of Rodent.Print. } public void reproduce() { print("Making more Mice"). Gerbil. } public void scurry() { print("Hamster scurrying"). new Gerbil(). } public String toString() { return "Mouse".* Mouse. } public String toString() { return "Rodent". ***********************************************/ package polymorphism.*. 112 Thinking in Java. } public String toString() { return "Hamster".mindview. } } class Gerbil extends Rodent { public void hop() { print("Gerbil hopping"). Hamster. and call * your base-class methods to see what happens. In the base * class. fill it with * different specific types of Rodents. } public void reproduce() { print("Making more Rodents"). } public void reproduce() { print("Making more Hamsters"). }. etc. new Hamster(). } public String toString() { return "Gerbil". and override these in the derived * classes to perform different behaviors * depending on the specific type of Rodent. } public void scurry() { print("Mouse scurrying"). } } public class E09_Rodents { public static void main(String args[]) { Rodent[] rodents = { new Mouse(). } } class Hamster extends Rodent { public void hop() { print("Hamster hopping"). 4th Edition Annotated Solution Guide . } } } /* Output: Mouse hopping Mouse scurrying Making more Mice Mouse Gerbil hopping Gerbil scurrying Making more Gerbils Gerbil Hamster hopping Hamster scurrying Making more Hamsters Hamster *///:~ Exercise 10 //: polymorphism/E10_MethodCalls. print(r). ***********************************************/ package polymorphism. upcast it to * the base type. Inherit * a class and override the second method. In the * first method. Create * an object of the derived class. } } class Inherited extends TwoMethods { Polymorphism 113 . calling m2").out.scurry().hop(). and call the first method.println("Inside m2"). r. class TwoMethods { public void m1() { System. r. } public void m2() { System. * Explain what happens.for(Rodent r : rodents) { r.java /****************** Exercise 10 ***************** * Create a base class with two methods. m2().reproduce().println("Inside m1. call the second method.out. m2 *///:~ The first method isn’t overridden. x. but it calls the second method.Print. } } class Sandwich2 extends PortableLunch { Bread b = new Bread(). calling m2 Inside Inherited. which is.MindView.public void m2() { System. } 114 Thinking in Java. import static net.out.net. this is very powerful (and may surprise the unaware). } } public class E10_MethodCalls { public static void main(String args[]) { TwoMethods x = new Inherited().util. Lettuce l = new Lettuce(). } } public class E11_Pickle { public static void main(String args[]) { new Sandwich2(). Sandwich2() { print("Sandwich()").mindview. ***********************************************/ package polymorphism. (See Thinking in Patterns with Java at www. The Template Method design pattern makes heavy use of polymorphism. class Pickle { Pickle() { print("Pickle()").m1(). Cheese c = new Cheese().println("Inside Inherited.m2").java /****************** Exercise 11 ***************** * Add class Pickle to Sandwich. } } /* Output: Inside m1.*. Java always uses the most-derived method for the object type. 4th Edition Annotated Solution Guide . Pickle p = new Pickle().java.) Exercise 11 //: polymorphism/E11_Pickle. } public void hop() { print("Mouse hopping").Print. } public void hop() { print("Rodent hopping"). } public String toString() { return "Rodent". } } class Mouse2 extends Rodent2 { Member m1 = new Member("m1"). and show * the order in which their initialization occurs * during construction. m2 = new Member("r2"). Exercise 12 //: polymorphism/E12_RodentInitialization. ***********************************************/ package polymorphism.*. public Mouse2() { print("Mouse constructor"). class Member { public Member(String id) { print("Member constructor " + id). after the other member objects. } public void scurry() { print("Rodent scurrying"). } } class Rodent2 { Member m1 = new Member("r1").java /****************** Exercise 12 ***************** * Modify Exercise 9 so it demonstrates the * order of initialization of the base classes * and derived classes.mindview.} /* Output: Meal() Lunch() PortableLunch() Bread() Cheese() Lettuce() Pickle() Sandwich() *///:~ Notice that Pickle appears in the correct order. } Polymorphism 115 . m2 = new Member("m2"). Now add member objects to * both the base and derived classes. } public void reproduce() { print("Making more Rodents"). import static net.util. public Rodent2() { print("Rodent constructor"). public Hamster2() { print("Hamster constructor").) 116 Thinking in Java. } public void scurry() { print("Gerbil scurrying"). Exercise 13 //: polymorphism/E13_VerifiedRefCounting.java * to verify the termination condition. } public void scurry() { print("Hamster scurrying"). } public String toString() { return "Mouse". } public void reproduce() { print("Making more Gerbils").java /****************** Exercise 13 ***************** * Add a finalize() method to ReferenceCounting. } public void reproduce() { print("Making more Mice"). starting with its member objects. starting with the member objects in order of definition. } public void hop() { print("Gerbil hopping"). m2 = new Member("h2"). then the derived class. } } /* Output: Member constructor r1 Member constructor r2 Rodent constructor Member constructor h1 Member constructor h2 Hamster constructor *///:~ We initialize the base class first. } } public class E12_RodentInitialization { public static void main(String args[]) { new Hamster2(). } public String toString() { return "Hamster". } public void hop() { print("Hamster hopping"). } } class Gerbil2 extends Rodent2 { Member m1 = new Member("g1"). } } class Hamster2 extends Rodent2 { Member m1 = new Member("h1"). } public void reproduce() { print("Making more Hamsters"). public Gerbil2() { print("Gerbil constructor"). m2 = new Member("g2").public void scurry() { print("Mouse scurrying"). } public String toString() { return "Gerbil". (See * the Initialization & Cleanup chapter. 4th Edition Annotated Solution Guide . shared. public Composing(Shared shared) { print("Creating " + this). Polymorphism 117 . private int id = counter++. private static int counter = 0. } protected void finalize() { if(refcount != 0) print("Error: object is not properly cleaned-up!").dispose().*. new Composing(shared).mindview. } protected void dispose() { if(--refcount == 0) print("Disposing " + this). new Composing(shared) }. } protected void dispose() { print("disposing " + this). new Composing(shared). this.dispose(). public Shared() { print("Creating " + this). } public void addRef() { refcount++. import static net. private static int counter = 0.***********************************************/ package polymorphism.shared = shared. this. } public String toString() { return "Shared " + id. shared. for(Composing c : composing) c. private int id = counter++. class Shared { private int refcount = 0.Print. } } public class E13_VerifiedRefCounting { public static void main(String[] args) { Shared shared = new Shared(). Composing[] composing = { new Composing(shared).addRef(). new Composing(shared).util. } } class Composing { private Shared shared. } public String toString() { return "Composing " + id. java /****************** Exercise 14 ***************** * Modify Exercise 12 so one of the member * objects is a shared object with reference * counting. System. so you can see the termination condition report the mistake. // Verify failure: new Composing(new Shared()).gc(). and demonstrate that it works * properly.util. ***********************************************/ package polymorphism. } } class SharedMember { private int refcount. 4th Edition Annotated Solution Guide . Exercise 14 //: polymorphism/E14_SharedRodentInitialization.System.mindview.Print. 118 Thinking in Java. class NonSharedMember { public NonSharedMember(String id) { print("Non shared member constructor " + id). import static net.gc().*. } } /* Output: Creating Shared 0 Creating Composing 0 Creating Composing 1 Creating Composing 2 Creating Composing 3 Creating Composing 4 disposing Composing 0 disposing Composing 1 disposing Composing 2 disposing Composing 3 disposing Composing 4 Disposing Shared 0 Creating Shared 1 Creating Composing 5 Error: object is not properly cleaned-up! *///:~ We kept our last Composing( ) object alive. public void addRef() { print("Number of references " + ++refcount). m2 = new NonSharedMember("r2"). public Rodent3(SharedMember sm) { print("Rodent constructor"). } protected void dispose() { print("Disposing " + this). m = sm. } protected void dispose() { if(--refcount == 0) print("Disposing " + this).dispose(). } public String toString() { return "Mouse". } } class Gerbil3 extends Rodent3 { private SharedMember m. } public SharedMember() { print("Shared member constructor"). } public String toString() { return "Shared member". } public void hop() { print("Rodent hopping").addRef(). m2 = new NonSharedMember("m2"). } public void reproduce() { print("Making more Mice"). m. } } class Rodent3 { private SharedMember m. public Mouse3(SharedMember sm) { super(sm). } public String toString() { return "Rodent". } public void hop() { print("Mouse hopping"). } public void scurry() { print("Mouse scurrying"). } public void reproduce() { print("Making more Rodents"). Polymorphism 119 . } public void scurry() { print("Rodent scurrying"). print("Mouse constructor"). m. } } class Mouse3 extends Rodent3 { NonSharedMember m1 = new NonSharedMember("m1"). NonSharedMember m1 = new NonSharedMember("r1"). new Gerbil3(sm). m2 = new NonSharedMember("h2"). m2 = new NonSharedMember("g2"). print("Gerbil constructor"). } public String toString() { return "Gerbil". Rodent3[] rodents = { new Hamster3(sm). print("Hamster constructor"). } public void hop() { print("Hamster hopping"). } public void scurry() { print("Hamster scurrying"). } public void hop() { print("Gerbil hopping"). } } public class E14_SharedRodentInitialization { public static void main(String args[]) { SharedMember sm = new SharedMember().dispose(). } public void reproduce() { print("Making more Hamsters").NonSharedMember m1 = new NonSharedMember("g1"). } } class Hamster3 extends Rodent3 { private SharedMember m. } public String toString() { return "Hamster". for(Rodent3 r : rodents) r. public Gerbil3(SharedMember sm) { super(sm). NonSharedMember m1 = new NonSharedMember("h1"). 4th Edition Annotated Solution Guide . } } /* Output: Shared member constructor Non shared member constructor r1 Non shared member constructor r2 Rodent constructor Number of references 1 120 Thinking in Java. new Mouse3(sm). public Hamster3(SharedMember sm) { super(sm). } public void reproduce() { print("Making more Gerbils"). }. } public void scurry() { print("Gerbil scurrying"). When we instantiate a new concrete rodent. The shared member is automatically disposed after release of the last reference.height = height. width = " + Polymorphism 121 .Non shared member constructor Non shared member constructor Hamster constructor Non shared member constructor Non shared member constructor Rodent constructor Number of references 2 Non shared member constructor Non shared member constructor Gerbil constructor Non shared member constructor Non shared member constructor Rodent constructor Number of references 3 Non shared member constructor Non shared member constructor Mouse constructor Disposing Hamster Disposing Gerbil Disposing Mouse Disposing Shared member *///:~ h1 h2 r1 r2 g1 g2 r1 r2 m1 m2 All types of rodents share one member object. this. Exercise 15 //: polymorphism/E15_PolyConstructors2. class RectangularGlyph extends Glyph { private int width = 4. int height) { this.Print. the reference counter of the shared member is incremented. import static net.*.util. When a rodent is destroyed.width = width.java /****************** Exercise 15 ***************** * Add a RectangularGlyph to PolyConstructors. ***********************************************/ package polymorphism.RectangularGlyph(). print("RectangularGlyph. private int height = 5.mindview.java * and demonstrate the problem described in this * section. the reference counter is decremented. RectangularGlyph(int width. draw(). though this isn’t obvious in the code. new RectangularGlyph(2. } } /* Output: Glyph() before draw() RoundGlyph. } } class RedAlertStatus extends AlertStatus { 122 Thinking in Java. height = " + height). class AlertStatus { public String getStatus() { return "None". radius = 5 Glyph() before draw() RectangularGlyph. width = 2.RectangularGlyph().java /****************** Exercise 16 ***************** * Following the example in Transmogrify.2). Exercise 16 //: polymorphism/E16_Starship. you see output of area = 0 because the base-class constructor calls draw( ).RoundGlyph(). ***********************************************/ package polymorphism. area = " + width * height). } } public class E15_PolyConstructors2 { public static void main(String[] args) { new RoundGlyph(5).draw(). radius = 0 Glyph() after draw() RoundGlyph. even before completely initializing the object.draw(). During construction. 4th Edition Annotated Solution Guide .java. } void draw() { print("RectangularGlyph.width + ". * create a Starship class containing an * AlertStatus reference that can indicate three * different states. height = 2 *///:~ This section shows how polymorphism calls the most-derived method. Include methods to change * the states. area = 0 Glyph() after draw() RectangularGlyph. eprise.println(eprise). } class GreenAlertStatus extends AlertStatus { public String getStatus() { return "Green". } class YellowAlertStatus extends AlertStatus { public String getStatus() { return "Yellow". You switch the setStatus( ) reference from one state to another to change the behavior of the Starship. eprise.out.setStatus(new RedAlertStatus()). }. System. } } /* Output: Green Yellow Red *///:~ This is an example of the State design pattern. The three states of an AlertStatus object. } class Starship { private AlertStatus status = new GreenAlertStatus(). represent behaviors of the Starship.println(eprise).println(eprise). } public String toString() { return status.setStatus(new YellowAlertStatus()).out. Exercise 17 //: polymorphism/E17_RTTI. to which the Starship class holds a reference.public String getStatus() { return "Red".java // {ThrowsException} /****************** Exercise 17 **************** Polymorphism 123 .out. System.getStatus(). }. System. wherein the object’s behavior is state-dependent. } } public class E16_Starship { public static void main(String args[]) { Starship eprise = new Starship(). }. public void setStatus(AlertStatus istatus) { status = istatus. import polymorphism. new Tricycle() }. import polymorphism. // Downcast/RTTI ((Unicycle)cycles[2]). import polymorphism.cycle. 4th Edition Annotated Solution Guide .cycle. public class Unicycle extends Cycle { public void balance() {} } ///:~ //: polymorphism/cycle3/Bicycle. public class Bicycle extends Cycle { public void balance() {} } ///:~ 124 Thinking in Java.balance().cycle3. ((Unicycle)cycles[0]).balance(). // Compile time: method not found in Cycle: // cycles[0]. // Exception thrown } } ///:~ //: polymorphism/cycle3/Unicycle. Downcast and call * balance() and observe what happens.balance(). new Bicycle(). // Downcast/RTTI ((Bicycle)cycles[1]).cycle3. // cycles[2].cycle3.*. using the Cycle hierarchy * from Exercise 1.cycle. public class E17_RTTI { public static void main(String[] args) { Cycle[] cycles = new Cycle[]{ new Unicycle().Tricycle. ***********************************************/ package polymorphism.balance().java package polymorphism.balance(). import polymorphism.Cycle.cycle. import polymorphism. Try to call * balance() on each element of the array and * observe the results.balance().java package polymorphism.Cycle.* Add a balance() method to Unicycle and Bicycle * but not to Tricycle. // cycles[1].Cycle. Upcast instances of all * three types to an array of Cycle. } public String toString() { return "Gerbil". } public String toString() { return "Hamster". } public void scurry() { print("Mouse scurrying").Print. } public void reproduce() { print("Making more Gerbils"). } public void reproduce() { print("Making more Hamsters"). class Mouse extends Rodent { public void hop() { print("Mouse hopping"). } public String toString() { return "Mouse". } } class Gerbil extends Rodent { public void hop() { print("Gerbil hopping"). abstract void reproduce(). } public void reproduce() { print("Making more Mice").*. ***********************************************/ package interfaces.java /****************** Exercise 1 ***************** * Modify Exercise 9 in the previous chapter so * Rodent is an abstract class. abstract public public public } class Rodent { abstract void hop(). abstract void scurry(). } } public class E01_AbstractRodent { public static void main(String args[]) { 125 .Interfaces Exercise 1 //: interfaces/E01_AbstractRodent.util. } public void scurry() { print("Hamster scurrying").mindview. import static net. Make the * methods of Rodent abstract whenever possible. } public void scurry() { print("Gerbil scurrying"). } } class Hamster extends Rodent { public void hop() { print("Hamster hopping"). 4th Edition Annotated Solution Guide . r. } } public class E02_Abstract { 126 Thinking in Java.Rodent[] rodents = { new Mouse(). new Gerbil(). Exercise 2 //: interfaces/E02_Abstract. r.hop(). ***********************************************/ package interfaces.reproduce(). for(Rodent r : rodents) { r.java // {CompileTimeError} /****************** Exercise 2 ***************** * Create a class as abstract without including * any abstract methods. abstract class NoAbstractMethods { void f() { System.toString( ) can be left out of the abstract base class. }.out. and verify that you * cannot create any instances of that class.println("f()"). print(r). } } } /* Output: Mouse hopping Mouse scurrying Making more Mice Mouse Gerbil hopping Gerbil scurrying Making more Gerbils Gerbil Hamster hopping Hamster scurrying Making more Hamsters Hamster *///:~ Note that the root class method Object.scurry(). new Hamster(). The code Interfaces 127 . Create an object of * the derived type in main(). Define this variable with a * nonzero value.print(). dp. Explain the results. public void print() { System. } } public class E03_Initialization { public static void main(String args[]) { DerivedWithPrint dp = new DerivedWithPrint(). } public abstract void print().out. producing a default value for i before any other initialization occurs.public static void main(String args[]) { new NoAbstractMethods(). * The overridden version of the method prints * the value of an int variable defined in the * derived class. abstract class BaseWithPrint { public BaseWithPrint() { print(). ***********************************************/ package interfaces. } } /* Output: i = 0 i = 47 *///:~ The java virtual machine zeroes the bits of the object after it allocates storage. Call print() in the * base-class constructor.java /****************** Exercise 3 ***************** * Create a base class with an abstract print() * method that is overridden in a derived class. } class DerivedWithPrint extends BaseWithPrint { int i = 47. } } ///:~ Exercise 3 //: interfaces/E03_Initialization.println("i = " + i). then call its * print() method. abstract class NoMethods {} class Extended1 extends NoMethods { public void f() { System. The safest approach is to set the object into a known good state as simply as possible. * Eliminate the need for the downcast by moving * the abstract declaration to the base class. 4th Edition Annotated Solution Guide . for example). The danger of calling a method inside a constructor is when that method depends on a derived initialization. this is not true with all languages – C++.f"). Exercise 4 //: interfaces/E04_AbstractBase. the object may be in an unexpected state (in Java. } } abstract class WithMethods { abstract public void f(). } } public class E04_AbstractBase { public static void test1(NoMethods nm) { // Must downcast to access f(): 128 Thinking in Java.calls the base-class constructor before running the derived-class initialization.out.out. and then perform any other operations outside the constructor.println("Extended1. ***********************************************/ package interfaces. } class Extended2 extends WithMethods { public void f() { System. Create a * static method that downcasts a reference from * the base class to the derived class and calls * the method. Demonstrate that it works in main().f"). Before the derived-class constructor is called. at least that state is defined.println("Extended2. * Derive a class and add a method. so we see the zeroed value of i as the initial output.java /****************** Exercise 4 ***************** * Create an abstract class with no methods. ***********************************************/ package interfaces. import interfaces. test1(nm).f().f().*.f Extended2. Implement the interface in a * different package.g().ownpackage.h().f"). imp. } } /* Output: Extended1. } } public class E05_ImplementInterface { public static void main(String args[]) { ImplementInterface imp = new ImplementInterface(). import static net.h"). } } /* Output: Interfaces 129 .((Extended1)nm). test2(wm).mindview.*. imp. imp. } public static void main(String args[]) { NoMethods nm = new Extended1(). } public static void test2(WithMethods wm) { // No downcast necessary: wm. } public void h() { print("ImplementInterface.f *///:~ test1( ) needs the downcast to call f( ). while test2( ) doesn’t need a downcast because f( ) is defined in the base class. Exercise 5 //: interfaces/E05_ImplementInterface.f().Print. class ImplementInterface implements AnInterface { public void f() { print("ImplementInterface. } public void g() { print("ImplementInterface.g"). WithMethods wm = new Extended2().java /****************** Exercise 5 ****************** * Create an interface with three methods in its * own package.util. void g().ownpackage. ***********************************************/ package interfaces. // stating that you cannot reduce the access of the // base class public method in a derived class.ownpackage.java 130 Thinking in Java.java /****************** Exercise 6 ****************** * Prove that all the methods in an interface are * automatically public. public interface AnInterface { void f(). 4th Edition Annotated Solution Guide . until we declare it public for use outside its package: //: interfaces/ownpackage/AnInterface. void h().*. public class E06_InterfacePublicMethods implements AnInterface { // Each of these produces a compile-time error message.h *///:~ The elements of an interface are public but the interface itself is not.f ImplementInterface.java package interfaces. } ///:~ Exercise 6 //: interfaces/E06_InterfacePublicMethods.ImplementInterface. //! void f() {} //! void g() {} //! void h() {} // Compiles OK: public void f() {} public void g() {} public void h() {} public static void main(String args[]) {} } ///:~ Exercise 7 //: interfaces/E07_RodentInterface.g ImplementInterface. import interfaces. } public void reproduce() { print("Making more Gerbils"). } public String toString() { return "Gerbil". } class Mouse2 implements Rodent2 { public void hop() { print("Mouse hopping"). interface Rodent2 { void hop(). } public void scurry() { print("Gerbil scurrying"). } } class Gerbil2 implements Rodent2 { public void hop() { print("Gerbil hopping"). } public void reproduce() { print("Making more Mice"). } public void scurry() { print("Hamster scurrying"). for(Rodent2 r : rodents) { r.Print. void scurry(). ***********************************************/ package interfaces./****************** Exercise 7 ****************** * Change Rodent to an interface in Exercise 9 of * the Polymorphism chapter. print(r). void reproduce(). } } public class E07_RodentInterface { public static void main(String args[]) { Rodent2[] rodents = { new Mouse2(). }. new Gerbil2(). } public String toString() { return "Mouse".util. } public void reproduce() { print("Making more Hamsters").scurry(). } } class Hamster2 implements Rodent2 { public void hop() { print("Hamster hopping"). r. } public void scurry() { print("Mouse scurrying"). } public String toString() { return "Hamster".mindview. import static net. } Interfaces 131 . r.reproduce().hop(). new Hamster2().*. void gobble().util.Print. } public void gobble() { print("Chomp! Snort! Gobble!").java.mindview. } } public class E08_FastFood { public static void main(String args[]) { FastSandwich burger = new FastSandwich(). and change Sandwich * so it also implements FastFood. import polymorphism. interface FastFood { void rushOrder(). } class FastSandwich extends Sandwich implements FastFood { public void rushOrder() { print("Rushing your sandwich order").java /****************** Exercise 8 ****************** * Create an interface called FastFood (with * appropriate methods) in * polymorphism.*.} } /* Output: Mouse hopping Mouse scurrying Making more Mice Mouse Gerbil hopping Gerbil scurrying Making more Gerbils Gerbil Hamster hopping Hamster scurrying Making more Hamsters Hamster *///:~ Exercise 8 //: interfaces/E08_FastFood. 132 Thinking in Java. ***********************************************/ package interfaces. 4th Edition Annotated Solution Guide . print("Super Size?").Sandwich.Sandwich. print("Fries with that?"). import static net. adjust()"). import polymorphism. } } /* Output: Meal() Lunch() PortableLunch() Bread() Cheese() Lettuce() Sandwich() Fries with that? Super Size? Rushing your sandwich order Chomp! Snort! Gobble! *///:~ Exercise 9 //: interfaces/E09_AbstractMusic5. burger. } class Wind extends Instrument { public String toString() { return "Wind".rushOrder(). abstract class Instrument { public void play(Note n) { print(this + ". ***********************************************/ package interfaces.music.mindview.play() " + n). Percussion and Stringed into * an abstract class.java by moving the common * methods in Wind. import static net.Print.java /****************** Exercise 9 ****************** * Refactor Music5.*. } } Interfaces 133 . } // Forces implementation in derived class: public abstract String toString().burger. } } class Percussion extends Instrument { public String toString() { return "Percussion".util.Note.gobble(). } public void adjust() { print(this + ". new Percussion(). } static void tuneAll(Instrument[] e) { for(Instrument i : e) tune(i).play() MIDDLE_C Woodwind.MIDDLE_C). new Woodwind() }.adjust() Woodwind. Without the redefinition of 134 Thinking in Java.play() MIDDLE_C Brass.adjust().adjust() Stringed.class Stringed extends Instrument { public String toString() { return "Stringed".adjust() Percussion.adjust() Wind. i.adjust() Brass.play(Note. so all classes that implement Instrument provide a definition for it. new Stringed().play() MIDDLE_C Percussion.play() MIDDLE_C Stringed. } public static void main(String[] args) { Instrument[] orchestra = { new Wind(). } } class Brass extends Wind { public String toString() { return "Brass". 4th Edition Annotated Solution Guide . } } public class E09_AbstractMusic5 { static void tune(Instrument i) { i.play() MIDDLE_C *///:~ We eliminate code duplication. new Brass(). } } class Woodwind extends Wind { public String toString() { return "Woodwind". toString( ) is now an abstract method. tuneAll(orchestra). moving common functionality into the abstract base class. } } /* Output: Wind. Note.util. } } class Percussion2 implements Instrument2.music. * Change tune() so it takes a Playable instead * of an Instrument.toString( ). } class Wind2 implements Instrument2.Print. Exercise 10 //: interfaces/E10_PlayableMusic5.*.java /****************** Exercise 10 ****************** * Add a Playable to Modify Music5.adjust()"). ***********************************************/ package interfaces.java. } interface Playable { void play(Note n). all Instruments would otherwise use the original. import static net.adjust()"). Interfaces 135 . non-abstract toString( ) from the root class Object. Playable { public void play(Note n) { print(this + ". Playable { public void play(Note n) { print(this + ". } public void adjust() { print(this + ".play() " + n).play() " + n). Include Playable in the implements * list to add it to the derived classes. import polymorphism.play() " + n). } public String toString() { return "Wind". } public void adjust() { print(this + ". interface Instrument2 { void adjust(). } } class Stringed2 implements Instrument2. Playable { public void play(Note n) { print(this + ". } public String toString() { return "Percussion". and move * the play() declaration from Instrument to * Playable.mindview. play() MIDDLE_C Stringed. } static void tuneAll(Playable[] e) { for(Playable p : e) tune(p).adjust()"). tuneAll(orchestra). } public static void main(String[] args) { Playable[] orchestra = { new Wind2(). } } public class E10_PlayableMusic5 { static void tune(Playable p) { p. and don’t inherit from any concrete classes.} public String toString() { return "Stringed".java /****************** Exercise 11 ****************** * Create a class with a method that takes a String 136 Thinking in Java.MIDDLE_C).play() MIDDLE_C Brass. new Woodwind2() }.play() MIDDLE_C *///:~ We make Playable a concrete class to eliminate code duplication. 4th Edition Annotated Solution Guide . } } class Woodwind2 extends Wind2 { public String toString() { return "Woodwind".play(Note. new Stringed2().play() MIDDLE_C Woodwind. new Brass2(). Exercise 11 //: interfaces/E11_Swapper. } } class Brass2 extends Wind2 { public String toString() { return "Brass".play() MIDDLE_C Percussion. new Percussion2(). and Stringed2 only use interfaces. Percussion2. as Wind2. } public void adjust() { print(this + ". } } /* Output: Wind. setCharAt(i + 1. i += 2) { char c1 = sb. } return sb.swap((String)input). import interfaces. ***********************************************/ package interfaces.process(new SwapperAdapter().getSimpleName().process(). } } class SwapperAdapter implements Processor { public String name() { return CharacterPairSwapper. (See the J2SE5 API documentation for details. Adapt the * class to work with * interfaceprocessor.length() .*.charAt(i).interfaceprocessor.) CharacterPairSwapper has a completely different interface and cannot be directly integrated into the rest of Interfaces 137 . } public String process(Object input) { return CharacterPairSwapper. c1). } } public class E11_Swapper { public static void main(String[] args) { Apply. c2).* argument and produces a result that swaps each * pair of characters in that argument. for(int i = 0. sb. class CharacterPairSwapper { static String swap(String s) { StringBuilder sb = new StringBuilder(s). "1234").toString(). sb.charAt(i + 1). Apply. i < sb.Apply.1.process(new SwapperAdapter(). } } /* Output: Using Processor CharacterPairSwapper 2143 Using Processor CharacterPairSwapper badce *///:~ CharacterPairSwapper uses the methods of the StringBuilder class to access and modify individual characters inside the character sequence. "abcde").setCharAt(i.class. char c2 = sb. // Treat it as an ActionCharacter } } ///:~ 138 Thinking in Java. // Treat it as a CanFight u(h).java /****************** Exercise 12 ****************** * Follow the form of the other * interfaces to add an interface called * CanClimb in Adventure. t(h). CanFly.fight(). } static void z(CanClimb x) { x. 4th Edition Annotated Solution Guide . Refactor the SwapperAdapter class as a Singleton as an additional exercise. // Treat it as a CanFly z(h). Because swap( ) is a static method. // Treat it as a CanSwim v(h).fight(). Exercise 12 //: interfaces/E12_CanClimb. } static void w(ActionCharacter x) { x.java. interface CanClimb { void climb(). The Adapter pattern application solves this problem. } static void u(CanSwim x) { x.climb(). ***********************************************/ package interfaces.fly().swim(). } class Hero2 extends ActionCharacter implements CanFight.the input processing system. you don’t need a new SwapperAdapter instance for each SwapperAdapter. CanClimb { public void swim() {} public void fly() {} public void climb() {} } public class E12_CanClimb { static void t(CanFight x) { x. } static void v(CanFly x) { x. CanSwim. } public static void main(String[] args) { Hero2 h = new Hero2(). // Treat it as a CanClimb w(h). } interface IntermediateInterface1 extends BaseInterface { void f().Exercise 13 //: interfaces/E13_Diamond. } class CombinedImpl implements CombinedInterface { public void f() { System. then multiply-inherit * a third interface from the second two. } interface IntermediateInterface2 extends BaseInterface { void f(). } interface CombinedInterface extends IntermediateInterface1.println("CombinedImpl. inherit two new * interfaces from it.f()"). IntermediateInterface2 { void f(). } } public class E13_Diamond { public static void main(String[] args) { new CombinedImpl(). which eliminates ambiguity about which of two identical members we use when combining implementations of the same base class.f().java /****************** Exercise 13 ****************** * Create an interface. interface BaseInterface { void f().out. } } /* Output: CombinedImpl.f() *///:~ Java allows multiple interface inheritance but not multiple implementation inheritance. ***********************************************/ package interfaces. We replicate f( ) in the interfaces above to demonstrate that Java avoids the “diamond problem” (so called because of the diamond-shaped class diagram produced by Interfaces 139 . interface Interface1 { void f1(). adding * a new method. ***********************************************/ package interfaces. } } class All extends Concrete implements Multiple { 140 Thinking in Java. } interface Interface3 { void f3(). C++ requires extra base-class syntax resolve the ambiguity created by concrete multiple inheritance). each of which takes one * of the four interfaces as an argument. each with two methods.multiple inheritance. void g3().util.s = s. 4th Edition Annotated Solution Guide . Exercise 14 //: interfaces/E14_InterfaceInheritance. Interface3 { void h().Print. } interface Multiple extends Interface1.mindview. * Inherit a new interface from each. Create * an object of your class in main(). Interface2. Use the new interface to create * a class. void g1().*. import static net. } interface Interface2 { void f2().java /****************** Exercise 14 ****************** * Create three interfaces. void g2(). and pass it * to each of the methods. public Concrete(String s) { this. and inherit from a concrete class. * Now write four methods. } class Concrete { String s. All() { super("All"). } static void takesAll(All a) { a. takes2(a). a. i. public void f2() { print("All.f2().g2 All. a.h(). } static void takes3(Interface3 i) { i. takesAll(a). } static void takes2(Interface2 i) { i.f1(). i.g1"). a.f1(). takes1(a).g1(). i.f1 Interfaces 141 .g2").g2().f3().g3(). public void g3() { print("All.h").f1"). } public static void main(String args[]) { All a = new All(). } } /* Output: All. public void g1() { print("All. } public void f1() { print("All.g3 All.g1 All. a.g1(). } } } } } } } public class E14_InterfaceInheritance { static void takes1(Interface1 i) { i.f3"). public void g2() { print("All.f2 All. } public void h() { print("All.f1 All.f2(). takes3(a).f3 All.f3(). a. a.f2").g3(). public void f3() { print("All.g3").g2(). abstract class Abstract { String s.af").f3").g3").h *///:~ Exercise 15 //: interfaces/E15_AbstractsAndInterfaces. } public void g2() { print("All.g2").f2"). 4th Edition Annotated Solution Guide .*. ***********************************************/ package interfaces. } public void g1() { print("All. } public void f2() { print("All. } static void takes2(Interface2 i) { i.f1").Print. } abstract void af().All. } class All2 extends Abstract implements Multiple { All2() { super("All2"). public Abstract(String s) { this.s = s. } public void h() { print("All2.f3 All. } public void f3() { print("All.g1").g2().f1().f2 All.java /****************** Exercise 15 ****************** * Modify Exercise 14 by creating an abstract class * and inheriting it into the derived class.h"). } void af() { print("All.g2 All. import static net.g3 All.mindview. i. i. } } public class E15_AbstractsAndInterfaces { static void takes1(Interface1 i) { i. } public void f1() { print("All.g1().f2(). } static void takes3(Interface3 i) { 142 Thinking in Java.util. } public void g3() { print("All.g1 All. } static void takesAll(All2 a) { a.g1 All.f3(). a. takes2(a). } static void takesAbstract(Abstract a) { a.g3().f3 All.g3().f1 All.f1().h All.af().f3 All.g2 All. i.g2(). a.af *///:~ Exercise 16 //: interfaces/E16_AdaptedCharSequence. takesAbstract(a).f2 All. a. takesAll(a).g1 All.g1().f3().g3 All. a. a.java /****************** Exercise 16 ****************** * Create a class that produces a sequence of chars. a.f1 All. } public static void main(String args[]) { All2 a = new All2().h().g3 All2. } } /* Output: All.g2 All. takes1(a).i.f2(). takes3(a). * Adapt this class so that it can be an input to a Interfaces 143 .f2 All. private static final char[] lowers = "abcdefghijklmnopqrstuvwxyz". import java. private static final char[] capitals = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".*. class CharSequence { private static Random rand = new Random(47). public E16_AdaptedCharSequence(int count) { this. int idx = 0.*.nio.length)].println(s. i < 4. private static final char[] vowels = "aeiou".nextInt(capitals. char[] generate() { char[] buffer = new char[10].* Scanner object.put(buffer). 4th Edition Annotated Solution Guide .toCharArray().nextInt(lowers. } } /* Output: 144 Thinking in Java.toCharArray(). while(s.length. import java.count = count.next()).hasNext()) System.toCharArray(). } } class E16_AdaptedCharSequence extends CharSequence implements Readable { private int count.util. ***********************************************/ package interfaces.length)].out.length)]. buffer[idx++] = lowers[rand. } buffer[idx] = ' '. return buffer. return buffer.== 0) return -1. for(int i = 0. } public int read(CharBuffer cb) { if(count-.nextInt(vowels. i++) { buffer[idx++] = vowels[rand. buffer[idx++] = capitals[rand. // Indicates end of input char[] buffer = generate(). cb. } public static void main(String[] args) { Scanner s = new Scanner(new E16_AdaptedCharSequence(10)). java /****************** Exercise 17 ****************** * Prove that the fields in an interface are * implicitly static and final.RED). } } /* Output: StaticFinalTest. Exercise 17 //: interfaces/E17_ImplicitStaticFinal. this program behaves like RandomWords.RED = Red Interfaces 145 . } class Test implements StaticFinalTest { public Test() { // Compile-time error: cannot assign a value // to final variable RED: //! RED = "Blue".java from the Interfaces chapter. an Adapter is created to provide a read( ) method.Yazeruyac Fowenucor Goeazimom Raeuuacio Nuoadesiw Hageaikux Ruqicibui Numasetih Kuuuuozog Waqizeyoy *///:~ Though structurally different. interface StaticFinalTest { String RED = "Red".RED = " + StaticFinalTest. Because CharSequence alone does not work as an input to a Scanner instance.println("StaticFinalTest.out. } } public class E17_ImplicitStaticFinal { public static void main(String args[]) { // Accessing as a static field: System. ***********************************************/ package interfaces. Bicycle. RED is clearly static because you can access it using static syntax.java /****************** Exercise 18 ****************** * Create a Cycle interface. and code that uses * these factories. Exercise 18 //: interfaces/E18_CycleFactories. and Tricycle. } class Unicycle implements Cycle { public int wheels() { return 1. } } class TricycleFactory implements CycleFactory { public Tricycle getCycle() { return new Tricycle(). } } class BicycleFactory implements CycleFactory { public Bicycle getCycle() { return new Bicycle(). } } class Bicycle implements Cycle { public int wheels() { return 2. interface Cycle { int wheels(). with implementations * Unicycle. } interface CycleFactory { Cycle getCycle(). } 146 Thinking in Java. } } class Tricycle implements Cycle { public int wheels() { return 3. ***********************************************/ package interfaces. } } class UnicycleFactory implements CycleFactory { public Unicycle getCycle() { return new Unicycle().*///:~ The compiler tells you RED is a final variable when you try to assign a value to it. Create factories * for each type of Cycle. 4th Edition Annotated Solution Guide . } class CoinTossing implements Tossing { private int events.out. of wheels: 3 *///:~ This solution has several more classes than Exercise 5 in the Polymorphism chapter. ride(new BicycleFactory ()). To anticipate every possibility. } } Interfaces 147 .java /****************** Exercise 19 ****************** * Create a framework using Factory Methods that * performs both coin tossing and dice tossing. ***********************************************/ package interfaces. private static final int EVENTS = 2. of wheels: 1 Num.} public class E18_CycleFactories { public static void ride(CycleFactory fact) { Cycle c = fact. ride(new TricycleFactory ()). complexity and necessary maintenance make this a wise choice only when you know you’ll be adding new classes. public boolean event() { System. return ++events != EVENTS. programmers often use this interface + factory form to create classes. Exercise 19 //: interfaces/E19_TossingFramework.wheels()).getCycle(). However. } interface TossingFactory { Tossing getTossing().out. of wheels: 2 Num. of wheels: " + c. } } /* Output: Num.println("Coin tossing event " + events). interface Tossing { boolean event().println("Num. System. } public static void main(String[] args) { ride(new UnicycleFactory()). because it allows them to add new classes anywhere. public boolean event() { System. private static final int EVENTS = 6. } } /* Output: Coin tossing event 0 Coin tossing event 1 Dice tossing event 0 Dice tossing event 1 Dice tossing event 2 Dice tossing event 3 Dice tossing event 4 Dice tossing event 5 *///:~ 148 Thinking in Java.class CoinTossingFactory implements TossingFactory { public CoinTossing getTossing() { return new CoinTossing(). return ++events != EVENTS.event()) .println("Dice tossing event " + events). simulate(new DiceTossingFactory()). } } class DiceTossing implements Tossing { private int events. } } public class E19_TossingFramework { public static void simulate(TossingFactory fact) { Tossing t = fact. while(t. } public static void main(String[] args) { simulate(new CoinTossingFactory()).out. } } class DiceTossingFactory implements TossingFactory { public DiceTossing getTossing() { return new DiceTossing().getTossing(). 4th Edition Annotated Solution Guide . java /****************** Exercise 2 ***************** * Create a class that holds a String. In * main(). with a * toString() method that displays this String. 149 .println("Inner created"). class Outer { class Inner { { System. then display them.Inner i = o.java /****************** Exercise 1 ***************** * Write a class named Outer containing an * inner class named Inner. Outer. } } /* Output: Inner created *///:~ Exercise 2 //: innerclasses/E02_SequenceOfStringHolders.out. ***********************************************/ package innerclasses.getInner(). Add a method to Outer * that returns an object of type Inner. } } Inner getInner() { return new Inner(). * Add several instances of your new class to a * Sequence object. ***********************************************/ package innerclasses. create and initialize a reference to * an Inner.Inner Classes Exercise 1 //: innerclasses/E01_ReferenceToInnerClass. } } public class E01_ReferenceToInnerClass { public static void main(String[] args) { Outer o = new Outer(). selector(). Outer2.getInner().java /****************** Exercise 3 ***************** * Modify Exercise 1 so Outer has a private * String field (initialized by the constructor). } } public class E02_SequenceOfStringHolders { public static void main(String[] args) { Sequence sequence = new Sequence(10).out.toString(i))).end()) { System.data = data. while(!selector. for(int i = 0. Selector selector = sequence. i < 10.class StringHolder { private String data. ***********************************************/ package innerclasses. } Inner getInner() { return new Inner(). 150 Thinking in Java.data = data.add(new StringHolder(Integer. * and Inner has a toString() that displays this * field. i++) sequence. class Inner { public String toString() { return data.next(). } } public class E03_InnerAccessingOuter { public static void main(String[] args) { Outer2 o = new Outer2("Inner accessing outer!"). class Outer2 { private final String data. Create an object of type Inner and * display it. } } } /* Output: 0 1 2 3 4 5 6 7 8 9 *///:~ Exercise 3 //: innerclasses/E03_InnerAccessingOuter. } public String toString() { return data.current() + " "). 4th Edition Annotated Solution Guide .Inner i = o. } } Outer2(String data) { this.print(selector. StringHolder(String data) { this. selector. out.length. } public boolean check() { return this == ((SequenceSelector)selector()). } public Object current() { return items[i].System. class Sequence2 { private Object[] items. System. } private class SequenceSelector implements Selector { private int i. } public void next() { if(i < items.java /****************** Exercise 4 ***************** * Add a method to the class Sequence.println(s. } } public Selector selector() { return new SequenceSelector(). } } public class E04_SequenceSelectorToSequence { public static void main(String[] args) { Sequence2 s = new Sequence2(10).length) i++.toString()).println(i. public Sequence2(int size) { items = new Object[size].check()).this. public boolean end() { return i == items.out.length) items[next++] = x. private int next. } } /* Output: Inner accessing outer! *///:~ Exercise 4 //: innerclasses/E04_SequenceSelectorToSequence. ***********************************************/ package innerclasses.SequenceSelector * that produces the reference to the outer class * Sequence. } public void add(Object x) { if(next < items. } } /* Output: Inner Classes 151 . } public Sequence2 sequence() { return Sequence2.sequence(). * in its own package.java /****************** Exercise 6 ***************** * Create an interface with at least one method. so check( ) performs the validation.true *///:~ The private inner class SequenceSelector is inaccessible outside of Sequence2. Exercise 6 //: innerclasses/E06_ProtectedInnerClass. you must fully resolve the name of the inner class Outer3. so the new expression must specify the object that creates the inner class. } } /* Output: Inner created *///:~ To create a reference in the separate class E05_InstanceOfInner. make an instance of the inner * class. class Outer3 { class Inner { { System. Exercise 5 //: innerclasses/E05_InstanceOfInner. The inner class object has a connection to the outer-class object.out. Create a class in a 152 Thinking in Java. } } } public class E05_InstanceOfInner { public static void main(String args[]) { Outer3 o = new Outer3(). see the Inner classes and upcasting section of TIJ4. For more information about this topic. Outer3. ***********************************************/ package innerclasses.Inner. 4th Edition Annotated Solution Guide . In a * separate class.java /****************** Exercise 5 ***************** * Create a class with an inner class.new Inner().Inner i = o.println("Inner created"). return an object of the protected * inner class.*.get(). import innerclasses. ***********************************************/ package innerclasses. } public static void main(String args[]) { new E06_ProtectedInnerClass().exercise6. public class E06_ProtectedInnerClass extends SimpleClass { public SimpleInterface get() { return new Inner().java package innerclasses.f(). import innerclasses.exercise6.*. public class SimpleClass { protected class Inner implements SimpleInterface { // Force constructor to be public: public Inner() {} public void f() {} } } ///:~ You cannot access the synthesized default constructor from E06 ProtectedInnerClass because it has the same protected access as the defining class.exercise6. } } ///:~ //: innerclasses/exercise6/SimpleInterface. Add a protected inner class * that implements the interface. inherit from your class and.java Inner Classes 153 . Exercise 7 //: innerclasses/E07_InnerClassAccess. public interface SimpleInterface { void f().exercise6b. } ///:~ //: innerclasses/exercise6b/SimpleClass.* separate package.exercise6b. upcasting to the interface during * the return.java package innerclasses. import innerclasses. inside a * method.*. In a third * package. println("E07_InnerClassAccess. then show * the effect on the outer-class object. } class Inner { void g() { i++. private void f() { System.f i = 11 *///:~ This exercise shows that inner classes have transparent access to their outerclass objects. public class E07_InnerClassAccess { private int i = 10. Create an inner class with a * method that modifies the outer-class field and * calls the outer-class method.out. in.println("i = " + i). In a second * outer-class method.h(). 4th Edition Annotated Solution Guide . } public static void main(String args[]) { E07_InnerClassAccess ica = new E07_InnerClassAccess().out.f"). ***********************************************/ package innerclasses. ica. even private fields and methods./****************** Exercise 7 ***************** * Create a class with a private field and a * private method. f(). Exercise 8 //: innerclasses/E08_OuterAccessingInner. ***********************************************/ 154 Thinking in Java. } } /* Output: E07_InnerClassAccess.java /****************** Exercise 8 ***************** * Determine whether an outer class has access to * the private elements of its inner class. System. } } public void h() { Inner in = new Inner().g(). create an object of the * inner class and call its method. out. } } Inner Classes 155 .f").j = 47 *///:~ As you can see from the output. System.println("Inner. } } public void testInnerAccess() { Inner i = new Inner(). o.out. private void h() { System. ***********************************************/ package innerclasses. } } public class E08_OuterAccessingInner { public static void main(String args[]) { Outer4 o = new Outer4(). * and implement it by defining an * inner class within a method that returns a * reference to your interface.j = " + j). } } /* Output: Inner.out.println("SI.j = 47.h called Inner.h(). the accessibility goes both ways. public class E09_InnerClassInMethod { public SimpleInterface get() { class SI implements SimpleInterface{ public void f() { System. i.java /****************** Exercise 9 ***************** * Create an interface with at least one method. i.*.println("Inner.testInnerAccess(). class Outer4 { class Inner { private int j. Exercise 9 //: innerclasses/E09_InnerClassInMethod.package innerclasses.h called").exercise6. import innerclasses. exercise6. if not. } } public static void main(String args[]) { SimpleInterface si = new E10_InnerClassInScope(). si. } } /* Output: SI.return new SI().f().get(). si.f"). 4th Edition Annotated Solution Guide .println("SI. public class E10_InnerClassInScope { public SimpleInterface get() { { class SI implements SimpleInterface{ public void f() { System.out. the inner class definition goes out of scope. ************************************************/ package innerclasses. } } /* Output: SI.f *///:~ Exercise 10 //: innerclasses/E10_InnerClassInScope.java /****************** Exercise 10 ***************** * Repeat Exercise 9 but define the inner * class within a scope within a method.get(). } } return new SI().f *///:~ The inner class remains visible only if the return statement is in its scope. } public static void main(String args[]) { SimpleInterface si = new E09_InnerClassInMethod(). import innerclasses. 156 Thinking in Java.*.f(). get2(). Notice that get2( ) returns an object of the private class Inner.out.'Inner' not visible: //! Inner i1 = out. Inner Classes 157 . upcast to SimpleInterface. } public Inner get2() { return new Inner(). import innerclasses. si = out. Show * that the inner class is completely hidden by * trying to downcast to it. However.get2().get(). Thus. class Outer5 { private class Inner implements SimpleInterface { public void f() { System.f"). } } ///:~ The public get( ) method returns the private class Inner instance. upcast to the interface.*. // Won't compile -. } } public class E11_HiddenInnerClass { public static void main(String args[]) { Outer5 out = new Outer5(). } } public SimpleInterface get() { return new Inner(). ************************************************/ package innerclasses.Exercise 11 //: innerclasses/E11_HiddenInnerClass. You can only upcast the return value to a visible base interface. //! Inner i2 = (Inner)si. Outer methods can use the actual type. when you call get2( ) from outside of Outer.java /****************** Exercise 11 ***************** * Create a private inner class that implements a * public interface.exercise6.Inner. you can’t use the return value’s actual type because it’s private and visible only inside the class. Write a method that returns * a reference to an instance of the private * inner class.println("Outer5. while methods of other classes must use the upcast result. SimpleInterface si = out. 4th Edition Annotated Solution Guide . public class E13_AnonymousInnerClassInMethod { public SimpleInterface get() { 158 Thinking in Java.h().Exercise 12 //: innerclasses/E12_AnonymousInnerClassAccess.out. public class E12_AnonymousInnerClassAccess { private int i = 10.println("i = " + i). ica. } }.java /****************** Exercise 12 ***************** * Repeat Exercise 7 using an anonymous inner * class.f"). private void f() { System.java /****************** Exercise 13 ***************** * Repeat Exercise 9 using an anonymous inner * class. } public void h() { new Object() { void g() { i++. } } /* Output: E12_AnonymousInnerClassAccess.println("E12_AnonymousInnerClassAccess. } public static void main(String args[]) { E12_AnonymousInnerClassAccess ica = new E12_AnonymousInnerClassAccess().*.out. import innerclasses. f(). ***********************************************/ package innerclasses.exercise6.f i = 11 *///:~ Exercise 13 //: innerclasses/E13_AnonymousInnerClassInMethod. System.g(). ***********************************************/ package innerclasses. f().u(vlad).get(). HorrorShow.out.f *///:~ Exercise 14 //: innerclasses/E14_HorrorShow2.java /****************** Exercise 14 ****************** * Modify interfaces/HorrorShow.println("SimpleInterface. } } ///:~ Inner Classes 159 . HorrorShow. } public static void main(String args[]) { SimpleInterface si = new E13_AnonymousInnerClassInMethod(). public class E14_HorrorShow2 { public static void main(String[] args) { DangerousMonster barney = new DangerousMonster() { public void menace() {} public void destroy() {} }.u(barney).f"). } }. Vampire vlad = new Vampire() { public void menace() {} public void destroy() {} public void kill() {} public void drinkBlood() {} }.java to implement * DangerousMonster and Vampire using anonymous * classes. ************************************************/ package innerclasses.w(vlad). si. } } /* Output: SimpleInterface. HorrorShow. HorrorShow. HorrorShow.return new SimpleInterface() { public void f() { System.v(barney).v(vlad). f").f Second. Create the object * you return by making an anonymous inner * class inherit from the first class. } } /* Output: NoDefault.java /****************** Exercise 15 ***************** * Create a class with a non-default constructor * (one with arguments) and no default constructor * (no "no-arg" constructor).Exercise 15 //: innerclasses/E15_ReturningAnonymousIC. } } public class E15_ReturningAnonymousIC { public static void main(String args[]) { Second sec = new Second().println("NoDefault.get2(99).f 160 Thinking in Java. class NoDefault { private int i. } public NoDefault get2(int i) { // Overrides f(): return new NoDefault(i) { public void f() { System. ***********************************************/ package innerclasses. } }. nd.get1(47).println("Second.f(). 4th Edition Annotated Solution Guide . nd.i = i. NoDefault nd = sec.get2.out.f().get2.out. } } class Second { public NoDefault get1(int i) { // Doesn't override any methods: return new NoDefault(i) {}.f"). nd = sec. Create a second class * with a method that returns a reference to * an object of the first class. } public void f() { System. public NoDefault(int i) { this. } public static CycleFactory factory = new CycleFactory() { public Tricycle getCycle() { return new Tricycle().java /****************** Exercise 16 ****************** * Use anonymous inner classes to modify the * solution to Exercise 18 from the Interfaces chapter. } public static CycleFactory factory = new CycleFactory() { public Unicycle getCycle() { return new Unicycle().*///:~ In get1( ). } Inner Classes 161 . usually you’ll override a method when you inherit. } class Bicycle implements Cycle { public int wheels() { return 2. Exercise 16 //: innerclasses/E16_AnonymousCycleFactories. } public static CycleFactory factory = new CycleFactory() { public Bicycle getCycle() { return new Bicycle(). interface Cycle { int wheels(). } class Unicycle implements Cycle { public int wheels() { return 1. } }. as in get2( ). you inherit NoDefault in the anonymous inner class without overriding any methods. } class Tricycle implements Cycle { public int wheels() { return 3. } interface CycleFactory { Cycle getCycle(). } }. ***********************************************/ package innerclasses. } }. } class CoinTossing implements Tossing { private int events. return ++events != EVENTS.getCycle().wheels()). } }.out. } public static TossingFactory factory = new TossingFactory() { public CoinTossing getTossing() { return new CoinTossing(). System.java /****************** Exercise 17 ****************** * Use anonymous inner classes to modify the solution * to Exercise 19 from the Interfaces chapter. 4th Edition Annotated Solution Guide . interface Tossing { boolean event(). } public static void main(String[] args) { ride(Unicycle.println("Coin tossing event " + events).out.factory). of wheels: 1 Num. } class DiceTossing implements Tossing { 162 Thinking in Java. public boolean event() { System.factory). of wheels: 2 Num. ***********************************************/ package innerclasses. of wheels: 3 *///:~ Exercise 17 //: innerclasses/E17_AnonymousTossingFramework.public class E16_AnonymousCycleFactories { public static void ride(CycleFactory fact) { Cycle c = fact. ride(Bicycle. ride(Tricycle.factory). private static final int EVENTS = 2. } } /* Output: Num.println("Num. } interface TossingFactory { Tossing getTossing(). of wheels: " + c. } }.factory). return ++events != EVENTS. } } /* Output: Coin tossing event 0 Coin tossing event 1 Dice tossing event 0 Dice tossing event 1 Dice tossing event 2 Dice tossing event 3 Dice tossing event 4 Dice tossing event 5 *///:~ Exercise 18 //: innerclasses/E18_NestedClass.event()) .getTossing(). simulate(DiceTossing. private static final int EVENTS = 6. ************************************************/ package innerclasses. * In main(). } public static void main(String[] args) { simulate(CoinTossing. while(t. } public class E17_AnonymousTossingFramework { public static void simulate(TossingFactory fact) { Tossing t = fact.java /****************** Exercise 18 ***************** * Create a class containing a nested class.out. } public static TossingFactory factory = new TossingFactory() { public DiceTossing getTossing() { return new DiceTossing().factory). create an instance of the nested * class. public boolean event() { System.private int events.println("Dice tossing event " + events). public class E18_NestedClass { Inner Classes 163 . Exercise 19 //: innerclasses/E19_InnerInsideInner.f"). but outside the class. } } class Other { // Specifying the nested type outside // the scope of the class: void f() { E18_NestedClass. 4th Edition Annotated Solution Guide . you must specify the outer class and nested class.out.class files produced by the compiler. } } Inner1 makeInner1() { return new Inner1(). as shown in Other. ne. above. } static class Nested1 { static class Nested2 { void f() {} 164 Thinking in Java.Nested(). } } public static void main(String args[]) { Nested ne = new Nested().static class Nested { void f() { System.java /****************** Exercise 19 ***************** * Create a class containing an inner class that * itself contains an inner class.f().f *///:~ You can refer to just the class name when inside the method of a class with a defined nested (static inner) class. public class E19_InnerInsideInner { class Inner1 { class Inner2 { void f() {} } Inner2 makeInner2() { return new Inner2().Nested ne = new E18_NestedClass.println("Nested. } } /* Output: Nested. Note the names of * the . ***********************************************/ package innerclasses. Repeat this * using static inner classes. Inner Classes 165 . E19_InnerInsideInner x1 = new E19_InnerInsideInner(). public Nested(int i) { this.} void f() {} } public static void main(String args[]) { new E19_InnerInsideInner.i = i. * Implement this interface and create an * instance of the nested class. WithNested. new E19_InnerInsideInner.Nested1.f().out.println("Nested.f().class E19_InnerInsideInner$Inner1. } void f() { System.f").class E19_InnerInsideInner$Inner1$Inner2.Inner1. E19_InnerInsideInner.makeInner1().Nested1().Nested ne = new WithNested.class Exercise 20 //: innerclasses/E20_InterfaceWithNested.class E19_InnerInsideInner$Nested1. x3. } } } class B2 implements WithNested {} public class E20_InterfaceWithNested { public static void main(String args[]) { B2 b = new B2().f(). E19_InnerInsideInner.class E19_InnerInsideInner$Nested1$Nested2.Inner1 x2 = x1. ***********************************************/ package innerclasses. interface WithNested { class Nested { int i.Inner2 x3 = x2.Nested2().makeInner2(). } } ///:~ The class names produced are: E19_InnerInsideInner.java /****************** Exercise 20 ***************** * Create an interface containing a nested class.Nested(5). ne. Exercise 21 //: innerclasses/E21_InterfaceWithNested2. } } /* Output: Nested.Nested.f()"). ***********************************************/ package innerclasses. * Implement your interface and pass an instance of * your implementation to the method. If we define Nested within WithNested. } } } public class E21_InterfaceWithNested2 { public static void main(String[] args) { I impl = new I() { public void f() {} public void g() {} }. class Nested { static void call(I impl) { System. since all elements of an interface are public.g(). I. a nested class defined within it can still be useful.out.f(). } } /* Output: Calling I.println("Calling I.g()").f *///:~ Even if an interface itself has no use.call(impl).println("Calling I. impl.out. Nested has no added access to the elements of WithNested. void g(). interface I { void f(). impl.f() 166 Thinking in Java.java /****************** Exercise 21 ****************** * Create an interface with a nested class * and a static method that calls the methods * of your interface and displays the results. System. 4th Edition Annotated Solution Guide .f(). that just means we locate its name there. length) i++. } } public Selector selector() { return new SequenceSelector(). } private class ReverseSelector implements Selector { int i = objects.length . } public Selector reverseSelector() { return new ReverseSelector().1. } public void add(Object x) { if(next < objects. then define nested classes. It’s generally clearer to list all methods of an interface. } public Object current() { return objects[i].Calling I. } } public class E22_GetRSelector { public static void main(String[] args) { Sequence3 sequence = new Sequence3(10). ***********************************************/ package innerclasses. private int next. public Sequence3(int size) { objects = new Object[size]. } public void next() { if(i < objects. Exercise 22 //: innerclasses/E22_GetRSelector.java. class Sequence3 { private Object[] objects. public boolean end() { return i < 0.length) objects[next++] = x.java /****************** Exercise 22 ***************** * Implement reverseSelector() in Sequence.g() *///:~ Notice that we use an anonymous inner class to implement interface I. } public Object current() { return objects[i]. public boolean end() { return i == objects. } } private class SequenceSelector implements Selector { private int i. Inner Classes 167 . } public void next() { if(i >= 0) i--.length. and a third method * that moves through the array and calls the * methods in U.reverseSelector(). * Create a class A with a method that produces a * reference to a U by building an anonymous * inner class. i < 10.toString(i)).add(Integer. Selector selector = sequence. create a group of A * objects and a single B. interface U { void f(). } } } /* Output: 9 8 7 6 5 4 3 2 1 0 *///:~ This is a copy-and-paste solution with minor logic changes in the ReverseSelector class.out. while(!selector.end()) { System. a second method that sets a * reference in the array (specified by the * method argument) to null. Use the * B to call back into all the A objects.next(). } class A { public U getU() { return new U() { 168 Thinking in Java. Fill the B with U * references produced by the A objects. ***********************************************/ package innerclasses.current() + " "). Remove * some of the U references from the B.java /****************** Exercise 23 ***************** * Create an interface U with three methods.print(selector. void h(). selector. B should have one * method that accepts and stores a reference to * a U in the array. i++) sequence. 4th Edition Annotated Solution Guide . Create a second class B that * contains an array of U. void g(). In main(). Exercise 23 //: innerclasses/E23_UAB.for(int i = 0. // Couldn't find any space } public boolean setNull(int i) { if(i < 0 || i >= ua. return true. System.println("****"). } } } public class E23_UAB { public static void main(String args[]) { A[] aa = { new A(). ua[i]. i++) if(ua[i] != null) { ua[i].length. for(int i = 0. } } class B { U[] ua. b. // Value out of bounds // (Normally throw an exception) ua[i] = null. Inner Classes 169 .f(). } } return false. i++) b.out.length. } public void h() { System.out.length) return false. i < ua.add(aa[i]. } public void g() { System.g"). i < ua.println("A.f").g().getU()). return true.println("A. } }. } public boolean add(U elem) { for(int i = 0. b.println("A.h"). new A() }.h().setNull(0). i++) { if(ua[i] == null) { ua[i] = elem. public B(int size) { ua = new U[size].public void f() { System.out. ua[i].out.length. B b = new B(3).callMethods(). i < aa. } public void callMethods() { for(int i = 0. new A(). f A.g A. public class LightOn extends Event { public LightOn(long delayTime) { super(delayTime).controller.f A. } } /* Output: A.g A.h A. Exercise 24 //: innerclasses/E24_GreenhouseInnerEvent.callMethods().java to use these new * Event objects. import innerclasses. light = true. ***********************************************/ package innerclasses.f A. } public String toString() { return "Light is on".g A.java // {Args: 5000} /****************** Exercise 24 ***************** * Add Event inner classes that turn fans on and * off in GreenhouseControls. } 170 Thinking in Java.b.g A.h A.g A.*.f A. Configure * GreenhouseController.f A.h **** A. 4th Edition Annotated Solution Guide .h *///:~ Notice that we remove the zero element. } public void action() { // Put hardware control code here to // physically turn on the light. class GreenhouseControlsWithFan extends Controller { private boolean light = false.java.h A. } public void action() { // Put hardware control code here. } } private boolean fan = false. } public void action() { // Put hardware control code here to // physically turn off the light. } public String toString() { return "Greenhouse water is on". light = false. public class WaterOn extends Event { public WaterOn(long delayTime) { super(delayTime). } public void action() { // Put hardware control code here to // physically turn on the Fan. } public String toString() { return "Fan is on". } } public class FanOff extends Event { public FanOff(long delayTime) { super(delayTime). water = true. } public void action() { // Put hardware control code here to // physically turn off the Fan. } public String toString() { Inner Classes 171 .} public class LightOff extends Event { public LightOff(long delayTime) { super(delayTime). fan = false. fan = true. } public String toString() { return "Fan is off". } public String toString() { return "Light is off". } } public class WaterOff extends Event { public WaterOff(long delayTime) { super(delayTime). } public void action() { // Put hardware control code here. } } private boolean water = false. public class FanOn extends Event { public FanOn(long delayTime) { super(delayTime). water = false. thermostat = "Night". } } // An example of an action() that inserts a // new one of itself into the event list: public class Bell extends Event { public Bell(long delayTime) { super(delayTime). } } public class Restart extends Event { private Event[] eventList. this.return "Greenhouse water is off". } public String toString() { return "Thermostat on day setting". } } private String thermostat = "Day". } public void action() { for(Event e : eventList) { 172 Thinking in Java. } public String toString() { return "Bing!". public Restart(long delayTime.eventList = eventList. thermostat = "Day". } public void action() { // Put hardware control code here. 4th Edition Annotated Solution Guide . for(Event e : eventList) addEvent(e). public class ThermostatNight extends Event { public ThermostatNight(long delayTime) { super(delayTime). } } public class ThermostatDay extends Event { public ThermostatDay(long delayTime) { super(delayTime). Event[] eventList) { super(delayTime). } public void action() { addEvent(new Bell(delayTime)). } public String toString() { return "Thermostat on night setting". } public void action() { // Put hardware control code here. new LightOn(200). Event[] eventList = { gc. } public void action() { System. gc.addEvent(gc.new WaterOn(600).new ThermostatNight(0). } } } public class E24_GreenhouseInnerEvent { public static void main(String[] args) { GreenhouseControlsWithFan gc = new GreenhouseControlsWithFan().new Restart(2000. gc. gc.new FanOff(500).addEvent(gc.start().run().length == 1) gc. } public String toString() { return "Terminating". gc. gc.new WaterOff(800).new Bell(900)). } start().e.Terminate(new Integer(args[0]))).new ThermostatDay(1400) }. gc. // Rerun this Event addEvent(this).exit(0). gc. } } public static class Terminate extends Event { public Terminate(long delayTime) { super(delayTime). gc. you could parse // configuration information from a text file here: gc. // Rerun each event addEvent(e).new LightOff(400). if(args.addEvent(new GreenhouseControlsWithFan . } } /* Output: Bing! Thermostat on night setting Light is on Fan is on Light is off Fan is off Inner Classes 173 . gc. // Instead of hard-wiring. eventList)). } public String toString() { return "Restarting system".new FanOn(300). } } public class WatermistGeneratorOff extends Event { public WatermistGeneratorOff(long delayTime) { super(delayTime). but it helps ensure that you understand the structure of the program. } public String toString() { return "Water mist generator is on". Write a new version of * GreenhouseController. public class WatermistGeneratorOn extends Event { public WatermistGeneratorOn(long delayTime) { super(delayTime). } public String toString() { 174 Thinking in Java. } public void action() { generator = true. ***********************************************/ package innerclasses.controller. class GreenhouseControlsWithWMG extends GreenhouseControls { private boolean generator = false.java // {Args: 5000} /****************** Exercise 25 ****************** * Inherit from GreenhouseControls in * GreenhouseControls.java to use these new * Event objects. } public void action() { generator = false.Greenhouse water is on Greenhouse water is off Thermostat on day setting Restarting system Terminating *///:~ This is basically a copy and paste exercise.java to add Event inner * classes that turn water mist generators on * and off. 4th Edition Annotated Solution Guide . import innerclasses.*. Exercise 25 //: innerclasses/E25_GreenhouseController. gc.return "Water mist generator is off". gc.new WatermistGeneratorOff(1800) }.new WaterOff(800). gc. gc.addEvent(new GreenhouseControlsWithWMG .addEvent(gc. } } } public class E25_GreenhouseController { public static void main(String[] args) { GreenhouseControlsWithWMG gc = new GreenhouseControlsWithWMG().new Restart(2000.addEvent(gc. // Instead of hard-wiring.run(). if(args. gc.new WaterOn(600). eventList)). } } /* Output: Bing! Thermostat on night setting Light is on Light is off Greenhouse water is on Greenhouse water is off Thermostat on day setting Water mist generator is on Water mist generator is off Restarting system Terminating *///:~ Exercise 26 //: innerclasses/E26_InnerClassInheritance.new LightOn(200). gc.java /****************** Exercise 26 ***************** Inner Classes 175 .new LightOff(400).length == 1) gc.new WatermistGeneratorOn(1600). gc.new ThermostatDay(1400). Event[] eventList = { gc.new Bell(900)).Terminate(new Integer(args[0]))).new ThermostatNight(0). you could parse // configuration information from a text file here: gc. gc. gc. //! } public Inner2(WithNonDefault wnd. so creating a new Inner2 object is doubly complex.println("Inner2.f Inner. 47). the Inner2 object now has an intimate connection with the objects WithNonDefault and 176 Thinking in Java.println("Inner. } public void f() { System. 4th Edition Annotated Solution Guide .i = i.out. Create a second class with an inner * class that inherits from the first inner class. i2.f"). } } } public class E26_InnerClassInheritance { class Inner2 extends WithNonDefault.f"). To create an instance of one inner class inheriting from another. Inner2 i2 = ici. ************************************************/ package innerclasses. we provide the constructor with an instance of the outer base class. public Inner(int i) { this. } public void f() { System. } } public static void main(String args[]) { WithNonDefault wnd = new WithNonDefault(). } } /* Output: Inner2.Inner { // Won't compile -.WithNonDefault not available: //! public Inner2(int i) { //! super(i). However. E26_InnerClassInheritance ici = new E26_InnerClassInheritance().out.f().f *///:~ We use the new expression from the outer class object to create an instance of an inner class.* Create a class with an inner class that has a * non-default constructor (one that takes * arguments). super. int i) { wnd. } public Inner() { i = 47. class WithNonDefault { class Inner { int i.f().new Inner2(wnd.super(i). E26_InnerClassInheritance. (See Mediator in the design patterns literature. this creates a private mediation between the two.) Inner Classes 177 . . class Gerbil { private final int gerbilNumber. import java.Holding Your Objects Exercise 1 //: holding/E01_Gerbil. ***********************************************/ package holding.out.util. } } public class E01_Gerbil { public static void main(String args[]) { ArrayList<Gerbil> gerbils = new ArrayList<Gerbil>(). * Give it a method called hop() that prints out * which gerbil number this is.hop().size().add(new Gerbil(i)). i++) gerbils.*. for(int i = 0. } } /* Output: gerbil 0 is hopping gerbil 1 is hopping gerbil 2 is hopping 179 .gerbilNumber = gerbilNumber. i++) gerbils. } public String toString() { return "gerbil " + gerbilNumber. } public void hop() { System. * Create an ArrayList and add Gerbil objects to * the List.java /****************** Exercise 1 ****************** * Create a new class called Gerbil with an int * gerbilNumber initialized in the constructor. Now use the get() method to move * through the List and call hop() for each Gerbil. i < gerbils. Gerbil(int gerbilNumber) { this. i < 10. for(int i = 0. and that it’s hopping.println(this + " is hopping").get(i). util. 4. "). 8. 7. } } /* Output: 0. ***********************************************/ package holding. 5.java /****************** Exercise 2 ****************** * Modify SimpleCollection. Exercise 3 //: holding/E03_UnlimitedSequence. // Autoboxing for(Integer i : c) System. 3. for(int i = 0.java /****************** Exercise 3 ****************** * Modify innerclasses/Sequence.java to use a Set for c. import java.gerbil gerbil gerbil gerbil gerbil gerbil gerbil *///:~ 3 4 5 6 7 8 9 is is is is is is is hopping hopping hopping hopping hopping hopping hopping Exercise 2 //: holding/E02_SimpleCollection2. i < 10. import java.*.add(i). a Set does not hold elements in insertion order.java so you * can add any number of elements to it. 2. ***********************************************/ package holding.print(i + ". 6. class UnlimitedSequence { private final List<Object> items = new ArrayList<Object>(). 1. *///:~ Generally. 180 Thinking in Java.util. 9. i++) c. 4th Edition Annotated Solution Guide . public class E02_SimpleCollection2 { public static void main(String[] args) { Collection<Integer> c = new HashSet<Integer>().out.*. Use this generator to * fill an array. Selector selector = sequence. } } } /* Output: 0 1 2 3 4 5 6 7 8 9 *///:~ Exercise 4 //: holding/E04_MovieNameGenerator.mindview.util. public boolean end() { return i == items. i++) sequence. } } public Selector selector() { return new SequenceSelector().size()) i++.end()) { System. import net.java /****************** Exercise 4 ****************** * Create a generator class that produces String objects * with the names of characters from your favorite * movie each time you call next().*.util. i < 10.public void add(Object x) { items. an ArrayList.out.print(selector.size().selector(). } public Object current() { return items. then * print each container.add(x). class MovieNameGenerator implements Generator<String> { Holding Your Objects 181 .toString(i)). selector. while(!selector.util. } private class SequenceSelector implements Selector { private int i.Print.get(i).*.next(). a LinkedHashSet. } public void next() { if(i < items. import static net.*. and then loops * around to the beginning of the character list * when it runs out of names.current() + " "). a LinkedList. ***********************************************/ package holding. and a TreeSet. a * HashSet. } } public class E03_UnlimitedSequence { public static void main(String[] args) { UnlimitedSequence sequence = new UnlimitedSequence().mindview. for(int i = 0.add(Integer. import java. i < array. Sleepy. Sleepy. next = (next + 1) % characters. } public static void main(String[] args) { print(Arrays. Happy.next(). } static Collection<String> fill(Collection<String> collection) { for(int i = 0. i < 5. Bashful. Dopey. Sneezy. 4th Edition Annotated Solution Guide . Doc] [Snow White. i++) collection. i++) array[i] = mng. 182 Thinking in Java. "Doc". Sleepy. "Sleepy". Prince. print(fill(new ArrayList<String>())). static String[] fill(String[] array) { for(int i = 0. } } /* Output: [Grumpy. "Prince" }. int next. "Snow White". Snow White.add(mng. Snow White. Doc] [Sneezy. Prince] [Grumpy. print(fill(new HashSet<String>())). Happy. return r. } } public class E04_MovieNameGenerator { private static final MovieNameGenerator mng = new MovieNameGenerator(). Dopey. "Dopey". Happy. return array. "Witch Queen". print(fill(new LinkedList<String>())). public String next() { String r = characters[next]. "Sneezy".length. print(fill(new LinkedHashSet<String>())).next()). Witch Queen] *///:~ All data generators use the simple Generator<T> parameterized interface. Witch Queen. Prince] [Grumpy.length. Sneezy. introduced later in TIJ4. "Bashful".String[] characters = { "Grumpy". "Happy". print(fill(new TreeSet<String>())). Dopey. Doc] [Bashful. Bashful.toString(fill(new String[5]))). return collection. Witch Queen. List<Integer> copy = new ArrayList<Integer>(ints).add(8). 7)). 5.Print. print("2: " + ints). print("sorted subList: " + sub). copy = new ArrayList<Integer>(ints). Collections. print("10: " + ints. rand).get(4)). print("13: " + copy). 6. print("5: " + ints.subList(1.*. 4). ***********************************************/ package holding. 2.sort(sub). 3.containsAll(sub)).Exercise 5 //: holding/E05_IntegerListFeatures.shuffle(sub. public static void main(String[] args) { List<Integer> ints = new ArrayList<Integer>( Arrays. print("3: " + ints.retainAll(sub). print("9: " + ints).get(2).java /****************** Exercise 5 ****************** * Use Integers instead of Pets to modify * ListFeatures. ints.indexOf(j)).containsAll(sub)). print("11: " + ints.add(3.java (remember autoboxing).util. print("subList: " + sub).asList(1. print("4: " + i + " " + ints. print("sub: " + sub). * Explain any difference in results.valueOf(1). List<Integer> sub = ints. import java.indexOf(i)).remove(Integer.valueOf(8)). ints. Holding Your Objects 183 .util.asList(ints. Integer j = Integer. 4. import static net. public class E05_IntegerListFeatures { static Random rand = new Random(47). print("8: " + ints). sub = Arrays. ints.contains(8)).get(1). print("7: " + ints.containsAll(sub)).remove(j)).remove(i)). print("1: " + ints). Integer i = ints. ints. print("12: " + ints.*. copy. print("6: " + ints.mindview. print("shuffled subList: " + sub). 0). Collections. 3. 4. 7] 18: false 19: [] 20: true 21: [1.clear(). 4th Edition Annotated Solution Guide . print("17: " + copy). print("19: " + ints). sub). print("21: " + ints). 6. 4. ints. 3. 7. 6. print("15: " + copy). 2. 4. 5. print("18: " + ints. 7] subList: [4. Object[] o = ints. 6] 14: [2. 5. 9. print("16: " + copy). 6.isEmpty()). print("22: " + ia[3]). 2. 0. 4. 8] 3: true 4: 3 2 5: 0 6: true 7: true 8: [2. 2.isEmpty()). print("20: " + ints.toArray(new Integer[0]).copy. 4)). 5. 4. 7] 15: [2. 3. 6. 5. 5] 12: true sub: [4. ints.remove(2).asList(1.removeAll(sub). copy. 4. 7] 9: [2.addAll(Arrays. 6. Integer[] ia = ints. 5. 5. 6] 13: [4.toArray().set(1. print("22: " + o[3]). copy. 4. } } /* Output: 1: [1. 9. print("14: " + copy). 2. 0] 10: true sorted subList: [0. 5] 11: true shuffled subList: [4. 7] 17: [2. 9). 0. 7] 16: [2.addAll(2. 7] 2: [1. 3. 6. 5. copy. 4] 22: 4 22: 4 184 Thinking in Java. *. print("5: " + strs. "D". print("subList: " + sub).util. If. print("6: " + strs. "B". Be vigilant with overloaded methods like remove( ).add(3. you type remove(2) instead of remove(Integer. "0"). print("9: " + strs).remove(s2)).add("H"). Explain any difference in * results. for example. print("10: " + strs.Print.get(2). String s1 = strs. import static net. "G")). ***********************************************/ package holding. 4). print("2: " + strs).containsAll(sub)).asList("A". public class E06_StringListFeatures { static Random rand = new Random(47). Holding Your Objects 185 . print("8: " + strs).util.*. print("1: " + strs). "E". print("4: " + s1 + " " + strs. "F". import java. strs. List<String> sub = strs.remove(s1)).subList(1. Two Integers are equal if their contents are identical in the output. print("7: " + strs. print("sorted subList: " + sub). strs.mindview. strs.sort(sub).indexOf(s1)).java /****************** Exercise 6 ****************** * Using Strings instead of Pets. Exercise 6 //: holding/E06_StringListFeatures. print("3: " + strs. String s2 = "A". "C". it’s easy to make mistakes due to autoboxing.*///:~ List behavior varies depending on equals( ).valueOf(2)) you remove the third element from the list (as the first element’s index is 0). modify * ListFeatures. Collections.remove("H").contains("H")). public static void main(String[] args) { List<String> strs = new ArrayList<String>( Arrays. as TIJ4 explains.indexOf(s2)). instead of an element whose value is 2.java . D. D.isEmpty()). print("14: " + copy). "B". copy. "I"). print("17: " + copy). copy. E. "C". print("16: " + copy). copy. Object[] o = strs. 4th Edition Annotated Solution Guide . 0] 10: true sorted subList: [0. String[] sa = strs. E. "D")). B.get(4)). sub = Arrays.removeAll(sub).set(1. F. print("20: " + strs. print("18: " + strs. print("21: " + strs). print("12: " + strs. sub). F] 186 Thinking in Java. D. rand). } } /* Output: 1: [A. G] 9: [B. print("sub: " + sub). E] 12: true sub: [D. 0.toArray(new String[0]). F] 13: [D. E. G] 2: [A. print("22: " + o[3]). E.containsAll(sub)). C. G.remove(2). F. C. E.clear(). copy = new ArrayList<String>(strs).get(1).isEmpty()). print("19: " + strs).addAll(Arrays. B. List<String> copy = new ArrayList<String>(strs). D. D. strs.print("11: " + strs.containsAll(sub)). F. print("13: " + copy).toArray(). copy.asList("A". print("shuffled subList: " + sub). strs.shuffle(sub. strs. print("15: " + copy). 0. E] 11: true shuffled subList: [D.addAll(2. H] 3: true 4: C 2 5: 0 6: true 7: true 8: [B.asList(strs. Collections. F. G] subList: [D. copy.retainAll(sub). print("22: " + sa[3]). i < idc. 18: false 19: [] 20: true 21: [A. List<IDClass> subSet = lst. private int count = counter++. be careful when using overloaded methods. Exercise 7 //: holding/E07_TestList.asList(idc)).size()/2). I. ***********************************************/ package holding.out. F. F. Create a subset of your List using * subList(). I. Holding Your Objects 187 . System. for(int i = 0. 22: D 22: D *///:~ E. } } public class E07_TestList { public static void main(String args[]) { IDClass[] idc = new IDClass[10].size()/4. public String toString() { return "IDClass " + count.println("lst = " + lst). E.java /****************** Exercise 7 ***************** * Create a class and make an initialized array * of your class objects. 17: [B. i++) idc[i] = new IDClass(). 15: [B.out. Fill a List from * your array.subList(lst. G] G] G] D.length.println("subSet = " + subSet). class IDClass { private static int counter. 16: [B. G] C. import java. System. D] Again.util. D.*. then remove this subset from * your List. B.14: [B. List<IDClass> lst = new ArrayList<IDClass>( Arrays. lst. it.add(new Gerbil(i)). import java. IDClass 4. IDClass 7.clear().*. for(int i = 0. IDClass 5. for(Iterator<Gerbil> it = gerbils.out. respectively. System. i++) gerbils. avoid errors by creating a separate copy of the returned sublist and use that as an argument to removeAll( ).hasNext(). you get a concurrent modification exception. IDClass 6. IDClass 5. Alternatively. the program operates on the sublist instead of the backing list. IDClass 3.util. IDClass 7.iterator(). it. Exercise 8 //: holding/E08_GerbilIterator. public class E08_GerbilIterator { public static void main(String args[]) { ArrayList<Gerbil> gerbils = new ArrayList<Gerbil>().java /****************** Exercise 8 ****************** * Modify Exercise 1 so it uses an Iterator to * move through the List while calling hop(). } } /* Output: gerbil 0 is hopping gerbil 1 is hopping gerbil 2 is hopping gerbil 3 is hopping 188 Thinking in Java. If you structurally modify the backing list as we did in the commented-out section. ***********************************************/ package holding.println("lst = " + lst). IDClass 9] subSet = [IDClass 2. IDClass 1. IDClass 1. } } /* Output: lst = [IDClass 0. IDClass 9] *///:~ The methods asList( ) and subList( ) return immutable Lists because they are “backed” by the underlying array and list. subSet. i < 10. Therefore. IDClass 3.// The semantics of the sub list become undefined if the // backing list is structurally modified! //! lst. IDClass 2. IDClass 8.hop()).removeAll(subSet).next(). IDClass 4] lst = [IDClass 0. IDClass 8. 4th Edition Annotated Solution Guide . IDClass 6. java /****************** Exercise 9 ****************** * Modify innerclasses/Sequence. } public Object next() { if(hasNext()) return items[i++]. } } public Iterator<Object> iterator() { return new SequenceIterator(). import java. class Sequence2 { private Object[] items. throw new NoSuchElementException(). } public void remove() { throw new UnsupportedOperationException().java so that * Sequence works with an Iterator instead of a * Selector. } } public class E09_SequenceIterator { Holding Your Objects 189 .length) items[next++] = x.gerbil gerbil gerbil gerbil gerbil gerbil *///:~ 4 5 6 7 8 9 is is is is is is hopping hopping hopping hopping hopping hopping Exercise 9 //: holding/E09_SequenceIterator.util. public boolean hasNext() { return i < items.*. private int next. ***********************************************/ package holding. public Sequence2(int size) { items = new Object[size]. } public void add(Object x) { if(next < items.length. } private class SequenceIterator implements Iterator<Object> { private int i. import java. i < 10.java /****************** Exercise 10 ****************** * change Exercise 9 in the Polymorphism chapter * to use an ArrayList to hold the Rodents and an * Iterator to move through their sequence. } public String toString() { return "Mouse". } public void scurry() { print("Rodent scurrying"). for(int i = 0.hasNext(). } } public class E10_RodentIterator { 190 Thinking in Java. 4th Edition Annotated Solution Guide .Print.mindview. } } class Hamster extends Rodent { public void hop() { print("Hamster hopping"). } public void scurry() { print("Hamster scurrying"). } public void scurry() { print("Mouse scurrying").next() + " "). } } class Mouse extends Rodent { public void hop() { print("Mouse hopping").) System.public static void main(String[] args) { Sequence2 sequence = new Sequence2(10).toString(i)). i++) sequence. ***********************************************/ package holding. it. for(Iterator<Object> it = sequence. } public void reproduce() { print("Making more Mice").out. } public String toString() { return "Rodent".util.print(it.*. } public void reproduce() { print("Making more Rodents").add(Integer. class Rodent { public void hop() { print("Rodent hopping"). } public String toString() { return "Hamster". } } /* Output: 0 1 2 3 4 5 6 7 8 9 *///:~ Exercise 10 //: holding/E10_RodentIterator. } public void reproduce() { print("Making more Hamsters").*.util.iterator(). import static net. import java.*.reproduce(). } } } /* Output: Rodent hopping Rodent scurrying Making more Rodents Rodent Mouse hopping Mouse scurrying Making more Mice Mouse Hamster hopping Hamster scurrying Making more Hamsters Hamster *///:~ Exercise 11 //: holding/E11_IterToString.java /****************** Exercise 11 ***************** * Write a method that uses an Iterator to step * through a Collection and print the toString() * of each object in the container. public class E11_IterToString { public static void printToStrings(Iterator<?> it) { while(it.toString()).iterator().asList( new Rodent().println(it. new Hamster())). r. it.out. Fill all the * different types of Collections with objects and * apply your method to each container. new Mouse(). r.next().hop().hasNext()) System.scurry().) { r = it. for(Iterator<Rodent> it = rodents.hasNext(). Holding Your Objects 191 . Rodent r. r.next().public static void main(String args[]) { ArrayList<Rodent> rodents = new ArrayList<Rodent>( Arrays.util. ***********************************************/ package holding. print(r). } } /* Output: Grumpy Happy Sleepy Dopey Doc Sneezy Bashful Snow White Witch Queen Prince Happy Doc Sleepy Grumpy Dopey Bashful Prince Sneezy Snow White Witch Queen *///:~ Exercise 12 //: holding/E12_ListIterators. new LinkedList<String>().} @SuppressWarnings("unchecked") public static void main(String args[]) { List<Collection<String>> ca = Arrays. new TreeSet<String>()). for(Collection<String> c : ca) printToStrings(c. 4th Edition Annotated Solution Guide .java /****************** Exercise 12 ***************** * Create two List<Integer>s of the same size. * and populate one of them. for(Collection<String> c : ca) E04_MovieNameGenerator.iterator()). Use ListIterators * to insert elements from the first List into * the second in reverse order.<Collection<String>>asList( new ArrayList<String>(). new HashSet<String>().fill(c). (You may want to * explore a number of different ways to solve 192 Thinking in Java. List<Integer> dest = new LinkedList<Integer>(src). 8. reverse(dest). fwd. 9. 3.println("source: " + src). 6. public class E12_ListIterators { static void reverse(List<Integer> list) { ListIterator<Integer> fwd = list.listIterator(). Exercise 13 //: holding/E13_GreenhouseLinkedList. ListIterator<Integer> rev = list. 7. Holding Your Objects 193 . 9. 5. 8.size() >> 1. } } /* Output: source: [1.out. then reverse their order using reverse( ). 7. } } public static void main(String[] args) { List<Integer> src = Arrays. 1] *///:~ We fill the destination list with elements from the source list. 3. System.set(rev. 7.util. import java. 3. i < mid. 10] destination: [10.size()).java // {Args: 5000} /****************** Exercise 13 ***************** * In the innerclasses/GreenhouseController.listIterator(list. 4. 8. and * use an Iterator to cycle through the set of events. 5. 3.asList(1.* this problem.println("source: " + src). * Change the code to use a LinkedList instead. 5. 4. 6. 9.println("destination: " + dest). 2.*.out.previous()). 6. rev. 2.out. 7.next().out. int mid = list. System. System. 9. 6. 4. 9.set(tmp). 6. i++) { Integer tmp = fwd.println("destination: " + dest). 2. 4. 7. 4. 10] destination: [1.) ***********************************************/ package holding. for(int i = 0. 2. 10). 5. Practice by finding other solutions. 2.java * example. 5. 8. 3. System. 8. the class Controller uses an ArrayList. 10] source: [1. Event.next(). } public String toString() { return "Light is on". e. it. import java. } public void run() { while(eventList. } public void action() { water = true. public class WaterOn extends Event { public WaterOn(long delayTime) { super(delayTime).util. } public String toString() { return "Greenhouse water is on". public class LightOn extends Event { public LightOn(long delayTime) { super(delayTime).hasNext()) { Event e = it. } public void action() { light = true. if(e.***********************************************/ package holding. } } public class LightOff extends Event { public LightOff(long delayTime) { super(delayTime).remove().size() > 0) { Iterator<Event> it = new LinkedList<Event>(eventList). } } } } } class GreenhouseControls extends Controller { private boolean light = false. } public void action() { light = false. public void addEvent(Event c) { eventList.out. 4th Edition Annotated Solution Guide .controller. class Controller { // List changed to a LinkedList: private List<Event> eventList = new LinkedList<Event>(). } } public class WaterOff extends Event { 194 Thinking in Java. import innerclasses.action().iterator().ready()) { System. } public String toString() { return "Light is off".add(c). while(it.println(e). } } private boolean water = false.*. Holding Your Objects 195 . this. } start(). addEvent(this). } } public class Bell extends Event { public Bell(long delayTime) { super(delayTime).eventList = eventList. } public String toString() { return "Thermostat on day setting". } public void action() { addEvent(new Bell(delayTime)). } } public class Restart extends Event { private Event[] eventList. for(Event e : eventList) addEvent(e). } public void action() { thermostat = "Night". addEvent(e). } public String toString() { return "Thermostat on night setting". public Restart(long delayTime. } public void action() { for(Event e : eventList) { e. public class ThermostatNight extends Event { public ThermostatNight(long delayTime) { super(delayTime). Event[] eventList) { super(delayTime). } public void action() { thermostat = "Day". } } private String thermostat = "Day". } } public class ThermostatDay extends Event { public ThermostatDay(long delayTime) { super(delayTime). } public String toString() { return "Bing!". } public String toString() { return "Greenhouse water is off".start(). } public void action() { water = false.public WaterOff(long delayTime) { super(delayTime). gc.new Bell(900)). gc.addEvent( new GreenhouseControls.Terminate( new Integer(args[0]))).new LightOff(400).new ThermostatDay(1400) }.new Restart(2000. } } public static class Terminate extends Event { public Terminate(long delayTime) { super(delayTime). 4th Edition Annotated Solution Guide . gc.run().length == 1) gc.addEvent(gc. Event[] eventList = { gc. gc.new WaterOff(800).exit(0).new ThermostatNight(0). if(args. only modifying Controller. } public void action() { System. gc.new WaterOn(600).addEvent(gc. gc.new LightOn(200). gc.} public String toString() { return "Restarting system". } public String toString() { return "Terminating". 196 Thinking in Java. } } } public class E13_GreenhouseLinkedList { public static void main(String[] args) { GreenhouseControls gc = new GreenhouseControls(). gc. eventList)). } } /* Output: Bing! Thermostat on night setting Light is on Light is off Greenhouse water is on Greenhouse water is off Thermostat on day setting Restarting system Terminating *///:~ Here we simply copy all the classes from TIJ4 into a single file. if(i % 2 == 0) it. 2] *///:~ You must calibrate the implicit cursor position every second iteration to keep the insertion point at the middle of the list. Exercise 15 //: holding/E15_Evaluator. } } /* Output: [1.java /****************** Exercise 14 ***************** * Create an empty LinkedList<Integer>. import java. 3. add Integers to the List by always * inserting them in the middle of the List. 9. 6. 7. evaluate the following * expression.java /****************** Exercise 15 ***************** * Stacks are often used to evaluate expressions * in programming languages. 8.previous().listIterator(). i++) { it. } System. import net.mindview. i <= 10. 4.Stack. 10. for(int i = 1. where '+' means "push the following * letter onto the stack. Using a * ListIterator.util. ***********************************************/ package holding.Stack.mindview. ListIterator<Integer> it = list.*. Using * net.util.add(i).Exercise 14 //: holding/E14_MiddleInsertion. public class E15_Evaluator { Holding Your Objects 197 . public class E14_MiddleInsertion { public static void main(String[] args) { LinkedList<Integer> list = new LinkedList<Integer>()." and '-' means "pop the * top of the stack and print it": * "+U+n+c---+e+r+t---+a-+i-+n+t+y---+ -+r+u--+l+e+s---" ***********************************************/ package holding.println(list).out.util. 5. private final static Stack<Character> stack = new Stack<Character>(). 'o'.java.toCharArray().util. } } /* Output: cnUtreaiytn ursel *///:~ Of course.*.java /****************** Exercise 16 ***************** * Create a Set of the vowels. } } public static void main(String[] args) { evaluate( "+U+n+c---+e+r+t---+a-+i-+n+t+y---+ -+r+u--+l+e+s---"). 'e'.length.util. import static net.*. int wordVowels.) switch(data[i++]) { case '+' : stack.Print. 'u'. "\\W+")) { 198 Thinking in Java. case '-' : System. Exercise 16 //: holding/E16_Vowels. public class E16_Vowels { private final static Set<Character> vowels = new HashSet<Character>(Arrays. 'I')).util.pop()). 'O'. for(String word : new TextFile("E16_Vowels. 'E'.print(stack. break. private static void evaluate(String expr) { char data[] = expr. to avert deadlock in case of improper input).asList('a'. and also display the * total number of vowels in the input file. Working from * UniqueWords. 'U'.out. 4th Edition Annotated Solution Guide . count and display the number of * vowels in each input word.*.push(data[i++]). ***********************************************/ package holding.. import java. i < data. 'A'. a real-world program also needs some error handling logic (e.g.mindview. for(int i = 0. 'i'.java". int fileVowels = 0.mindview. import net. public static void main(String[] args) { HashSet<String> processedWords = new HashSet<String>(). contains has 3 vowel(s) add has 1 vowel(s) print has 1 vowel(s) has has 1 vowel(s) vowel has 2 vowel(s) s has 0 vowel(s) Total has 2 vowel(s) Total number of vowels in file: 240 *///:~ Exercise 17 //: holding/E17_GerbilMap. * print out the key.. for(char letter : word. } } /* Output: (Sample) holding has 2 vowel(s) E16_Vowels has 3 vowel(s) java has 2 vowel(s) Exercise has 4 vowel(s) 16 has 0 vowel(s) Create has 3 vowel(s) . } print("Total number of vowels in file: " + fileVowels). and associate each Gerbil (the value) * with it's name as a String (the key). * Use an Iterator for the keySet() to move * through the Map. public class E17_GerbilMap { Holding Your Objects 199 .. and tell the Gerbil to hop(). print(word + " has " + wordVowels + " vowel(s)"). ***********************************************/ package holding.add(word).Map.contains(word)) { processedWords.Entry. look up the Gerbil for each key. } fileVowels += wordVowels.*.util.java /****************** Exercise 17 ****************** * Move the Gerbil class from Exercise 1 * into a Map.wordVowels = 0.util. import java. import java. if(!processedWords.contains(letter)) wordVowels++.toCharArray()) if(vowels. public static void main(String args[]) { Map<String. import net. new Gerbil(5)).iterator().String>(Countries. System.put("Heather". Gerbil>> it = map. new Gerbil(3)).sort(keys). new Gerbil(2)). 200 Thinking in Java. Gerbil> entry = it.hop().next(). *******************************************************/ package holding.capitals(25)).print(entry.put("Ted". sort * by key. map. and place the result into a LinkedHashMap. String[] keys = m1. } } } /* Output: Ted: gerbil 4 is hopping Heather: gerbil 5 is hopping Spot: gerbil 2 is hopping Joe: gerbil 3 is hopping Fuzzy: gerbil 1 is hopping *///:~ The hashing function (described in TIJ4) does not store objects in entry order.util.println(m1). Print the results * to show ordering by hash code. map.toArray(new String[0]).getKey() + ": "). Extract the pairs.mindview.put("Spot".out.java /******************* Exercise 18 ************************ * Fill a HashMap with key-value pairs. map. new Gerbil(4)). public class E18_MapOrder { public static void main(String[] args) { Map<String. Exercise 18 //: holding/E18_MapOrder.out.put("Fuzzy".getValue(). Iterator<Entry<String.Gerbil> map = new HashMap<String.entrySet().String> m1 = new HashMap<String. Arrays.*.hasNext()) { Entry<String. so use a LinkedHashMap if you want to maintain that order. map. while(it. 4th Edition Annotated Solution Guide . new Gerbil(1)).put("Joe".Gerbil>(). map.keySet().*. import java.util. entry. * Show that insertion order is maintained. System. System.get(key)). CENTRAL AFRICAN REPUBLIC=Bangui. BENIN=Porto-Novo.*. *************************************************/ package holding.toArray(new String[0]). System. CAMEROON=Yaounde.out. EQUATORIAL GUINEA=Malabo. CAPE VERDE=Praia. EQUATORIAL GUINEA=Malabo. CHAD=N'djamena. BURUNDI=Bujumbura. CONGO=Brazzaville. GABON=Libreville. CENTRAL AFRICAN REPUBLIC=Bangui. import net. m1. ETHIOPIA=Addis Ababa.String>(). COTE D'IVOIR (IVORY COAST)=Yamoussoukro. KENYA=Nairobi. ERITREA=Asmara. ETHIOPIA=Addis Ababa. THE GAMBIA=Banjul. BISSAU=Bissau. BOTSWANA=Gaberone.mindview. GABON=Libreville.String> m2 = new LinkedHashMap<String. for(String key : keys) m2. ERITREA=Asmara. GUINEA=Conakry. CAMEROON=Yaounde. CONGO=Brazzaville. BURKINA FASO=Ouagadougou. EGYPT=Cairo.util. Arrays. EGYPT=Cairo. BOTSWANA=Gaberone. DJIBOUTI=Dijibouti. LESOTHO=Maseru} {ALGERIA=Algiers.names(25)). BISSAU=Bissau.java /******************* Exercise 19 ***************** * Repeat Exercise 18 with a HashSet and * LinkedHashSet. GUINEA=Conakry. CAPE VERDE=Praia.println(m2). String[] elements = s1. BENIN=Porto-Novo. BURKINA FASO=Ouagadougou.util. BURUNDI=Bujumbura.out. COMOROS=Moroni. } } /* Output: {KENYA=Nairobi. THE GAMBIA=Banjul} *///:~ Exercise 19 //: holding/E19_SetOrder. Holding Your Objects 201 .println(s1). COTE D'IVOIR (IVORY COAST)=Yamoussoukro. ALGERIA=Algiers. CHAD=N'djamena. GHANA=Accra. ANGOLA=Luanda.sort(elements). GHANA=Accra.put(key.Map<String. public class E19_SetOrder { public static void main(String[] args) { Set<String> s1 = new HashSet<String>(Countries. COMOROS=Moroni. import java. ANGOLA=Luanda. Set<String> s2 = new LinkedHashSet<String>(). DJIBOUTI=Dijibouti. LESOTHO=Maseru.*. ERITREA. Integer freq = stat. System.Integer>(). EQUATORIAL GUINEA. BURKINA FASO. for(String word : 202 Thinking in Java.for(String element : elements) s2. 'u'.Print. CAMEROON. CAPE VERDE. CHAD.put(ch. CENTRAL AFRICAN REPUBLIC. DJIBOUTI.util. BISSAU. CHAD. BOTSWANA. freq == null ? 1 : freq + 1). KENYA. DJIBOUTI. CAPE VERDE. 'I')). 'e'. 'i'. public class E20_VowelsInfo { private final static Set<Character> vowels = new HashSet<Character>(Arrays. } } /* Output: [KENYA. 'A'.*. ***********************************************/ package holding. CONGO. stat. BOTSWANA. COTE D'IVOIR (IVORY COAST). HashMap<Character.get(ch). ALGERIA. BURUNDI. THE GAMBIA. CONGO. GABON. BENIN.mindview. BURUNDI.Integer>(). static void updateStat(Map<Character. EGYPT. EGYPT.Integer> wordStat = new HashMap<Character.mindview. COMOROS. BISSAU. 'O'. import java. } public static void main(String[] args) { HashMap<Character. 'E'. COTE D'IVOIR (IVORY COAST).util.println(s2).util. EQUATORIAL GUINEA.out. 'U'. char letter) { Character ch = Character.asList('a'. ANGOLA. GHANA. import net. GHANA. HashSet<String> processedWords = new HashSet<String>(). COMOROS. LESOTHO. GABON. GUINEA.*. ETHIOPIA. ETHIOPIA. 'o'.toLowerCase(letter). 4th Edition Annotated Solution Guide . GUINEA. ERITREA.java /****************** Exercise 20 ***************** * Modify Exercise 16 to count the occurrence of * each vowel. CENTRAL AFRICAN REPUBLIC. CAMEROON. LESOTHO] [ALGERIA. THE GAMBIA] *///:~ Exercise 20 //: holding/E20_VowelsInfo.Integer> stat. import static net.*. BENIN.Integer> fileStat = new HashMap<Character.add(element). ANGOLA. BURKINA FASO. ***********************************************/ Holding Your Objects 203 . } if(!processedWords.java to create a program that counts * the occurrence of words in a file. i=1} Vowels in 16: {} Vowels in so: {o=1} . Vowels in Vowels: {o=1. and display the result.java /****************** Exercise 21 ***************** * Using a Map<String. e=3} Vowels in 20: {} Vowels in Modify: {o=1. for(char letter : word. i=56. Sort the * results using Collections..sort() with a second * argument of String. u=15. i=1} Vowels in E20_VowelsInfo: {o=2. processing only the first occurrence of each word.CASE_INSENSITIVE_ORDER (to * produce an alphabetic sort).clear(). a=79.contains(word)) { processedWords. follow the form of * UniqueWords.new TextFile("E20_VowelsInfo.Integer>. "\\W+")) { wordStat. letter).contains(letter)) { updateStat(wordStat. } } /* Output: (Sample) Vowels in holding: {o=1. Exercise 21 //: holding/E21_WordsInfo. e=1} Vowels in in: {i=1} Vowels in whole: {o=1. } } print("*************************"). print("Vowels in " + word + ": " + wordStat).add(word).toCharArray()) if(vowels. e=2} Vowels in java: {a=2} Vowels in Exercise: {i=1. i=1. e=1} Vowels in file: {i=1.java". updateStat(fileStat.. e=96} *///:~ Try to accelerate the code. letter). print("Vowels in the whole file: " + fileStat). e=1} ************************* Vowels in the whole file: {o=49. for(String word : new TextFile("E21_WordsInfo. public class E21_WordsInfo { public static void main(String[] args) { Map<String.util. ***********************************************/ package holding.keySet()).. 4th Edition Annotated Solution Guide . } } /* Output: (Sample) 0 => 1 1 => 2 21 => 1 a => 4 alphabetic => 1 an => 1 and => 1 args => 1 . for(String key : keys) System. } List<String> keys = new ArrayList<String>(wordsStat.Integer> wordsStat = new HashMap<String.*.package holding. String. freq == null ? 1 : freq + 1).get(key)). import net. Collections..println(key + " => " + wordsStat.util. import java. wordsStat.java".put(word.java /****************** Exercise 22 ***************** * Modify the previous exercise so that it uses a * class containing a String and a count field to * store each different word. W => 1 with => 1 word => 3 words => 1 wordsStat => 5 *///:~ Exercise 22 //: holding/E22_WordsInfo2.Integer>().get(word). "\\W+")) { Integer freq = wordsStat.mindview.sort(keys.out.CASE_INSENSITIVE_ORDER).*. 204 Thinking in Java. and a Set of these * objects to maintain the list of words. *.hasNext()) { WordCounter currentWC = it.compareToIgnoreCase(o2.util. } } public static void main(String[] args) { Set<WordCounter> stat = new HashSet<WordCounter>().contains(wc)) updateStat(stat.*. wc). frequency = 1. for(String word : new TextFile("E22_WordsInfo2. } int getFrequency() { return frequency. Holding Your Objects 205 . } void incFrequency() { ++frequency.iterator(). } }. } public int hashCode() { return word.equals(((WordCounter)o). if(currentWC.import java.word.word). import net.word = word. private int frequency.word). WordCounter wc) { while(it. } public boolean equals(Object o) { return o instanceof WordCounter && word.equals(wc)) currentWC. "\\W+")) { WordCounter wc = new WordCounter(word).next(). WordCounter o2) { return o1. if(stat.mindview. } } public class E22_WordsInfo2 { static void updateStat(Iterator<WordCounter> it.add(wc). else stat.hashCode(). } List<WordCounter> l = new ArrayList<WordCounter>(stat). private final String word. } String getWord() { return word.util.java".incFrequency(). WordCounter(String word) { this. class WordCounter { public static final Comparator<WordCounter> CASE_INSENSITIVE_ORDER = new Comparator<WordCounter>() { public int compare(WordCounter o1. java.sort( ) method in the last exercise. It uses equals( ) and hashCode( ) methods. Exercise 23 //: holding/E23_MoreProbable. Without a HashMap. create a * program that runs the test repeatedly and * looks to see if any one number tends to appear 206 Thinking in Java. util => 2 void => 3 W => 1 wc => 9 while => 1 word => 13 WordCounter => 19 words => 1 *///:~ The WordCounter class contains a String and a frequency field to store each different word. Here it acts like String. 4th Edition Annotated Solution Guide . which shows how useful it is to keep your code base as small as possible (and boost productivity) by learning the classes offered by the JDK.out.CASE_INSENSITIVE_ORDER). and compare what (if any) improvement this makes to the program.CASE_INSENSITIVE_ORDER. respectively. Additionally. the situation gets more complicated. WordCounter.sort( l.getWord() + " => " + wc.Collections. try a TreeSet instead of a bare HashSet to maintain the list of words. } } /* Output: (Sample) 0 => 1 1 => 1 22 => 1 a => 4 add => 1 and => 2 args => 1 .println(wc..java /****************** Exercise 23 ***************** * Starting with Statistics. for(WordCounter wc : l) System. We created a custom Comparator to use the Collections.getFrequency()). to store class instances inside a HashSet.. Integer val) { this. Counter>> it = m. Integer val.put(r.Entry. Counter>(). } } class HistoUnit implements Comparable<HistoUnit> { Counter counter. for(int i = 0. public static void main(String[] args) { Map<Integer.util.entrySet().toString(i). return (lv < rv ? -1 : (lv == rv ? 0 : 1)). this. new Counter()). else m. public String toString() { return Integer.i. import java.nextInt(100).counter.* more than the others in the results. class Counter { int i = 1. Occurrences = " + counter.iterator(). Iterator<Entry<Integer. while(it.counter = counter.*.val = val.containsKey(r)) m. Counter> m = new HashMap<Integer. i++) { // Produce a number between 0 and 100: int r = rand. } // Make a histogram: List<HistoUnit> lst = new ArrayList<HistoUnit>().i++. if(m. int rv = counter.get(r). } } public class E23_MoreProbable { private static Random rand = new Random(47). import java. ***********************************************/ package holding. public HistoUnit(Counter counter.i.Map.hasNext()) { Holding Your Objects 207 .util. i < 10000000. } public int compareTo(HistoUnit o) { int lv = o. } public String toString() { return "Value = " + val + ".i + "\n". Occurrences = 99095 ] *///:~ To optimize built-in functionality.add(new HistoUnit( entry.. 4th Edition Annotated Solution Guide . lst. Value = 16.println("lst = " + lst).. Occurrences = 100539 . Value = 6. There is no preferred value. * Extract the pairs.*.toArray(new String[0]).sort(lst). you could calculate the distribution and standard deviation.java /******************* Exercise 24 ************************** * Fill a LinkedHashMap with String keys and objects. Value = 55. we make a list of objects called HistoUnit and sort based on the value of Counter with Collections.getValue(). Exercise 24 //: holding/E24_MapOrder2.out. Occurrences = 100503 .capitals(25)). Printing the list shows the results in order of occurrence. } } /* Output: (Sample) lst = [Value = 34. Value = 81. To determine how well the randomization function works. Occurrences = 99310 . 208 Thinking in Java. import java.out.mindview.println(m1). and * re-insert them into the Map.next(). *********************************************************/ package holding.getKey())). System.keySet().Entry<Integer. System.String>( Countries.String> m1 = new LinkedHashMap<String. import net. String[] keys = m1. Counter> entry = it. entry. Occurrences = 100660 . Value = 53.util.*.util. Occurrences = 100764 . Occurrences = 99427 .sort( ). public class E24_MapOrder2 { public static void main(String[] args) { Map<String. sort them based on the keys. } Collections. Value = 92. Occurrences = 99466 . Value = 86. . CAMEROON=Yaounde. BURUNDI=Bujumbura. CONGO=Brazzaville. ERITREA=Asmara. COMOROS=Moroni.*. EGYPT=Cairo. m1. CHAD=N'djamena. GUINEA=Conakry. ANGOLA=Luanda.Arrays. KENYA=Nairobi.mindview. System. import java. BURKINA FASO=Ouagadougou.get(key)).sort(keys). DJIBOUTI=Dijibouti. CHAD=N'djamena. EQUATORIAL GUINEA=Malabo. BURKINA FASO=Ouagadougou.String> m2 = new LinkedHashMap<String. } } /* Output: {ALGERIA=Algiers.util.java /****************** Exercise 25 ***************** * Create a Map<String. * in effect. EQUATORIAL GUINEA=Malabo.this is. DJIBOUTI=Dijibouti. Map<String. BENIN=Porto-Novo. COMOROS=Moroni. CONGO=Brazzaville. GHANA=Accra. COTE D'IVOIR (IVORY COAST)=Yamoussoukro. ***********************************************/ package holding.util.out. the location in the file where that * word was found.TextFile to open a text file and * read it in a word at a time (use "\\W+" as the * second argument to the TextFile constructor). * Count the words as you read them in. BOTSWANA=Gaberone. and for each * word in the file. for(String key : keys) m2. BOTSWANA=Gaberone. GHANA=Accra. ETHIOPIA=Addis Ababa.ArrayList<Integer>>. EGYPT=Cairo. Holding Your Objects 209 . GABON=Libreville.println(m2). ETHIOPIA=Addis Ababa. ANGOLA=Luanda.*. CENTRAL AFRICAN REPUBLIC=Bangui. BENIN=Porto-Novo. CENTRAL AFRICAN REPUBLIC=Bangui. BISSAU=Bissau. THE GAMBIA=Banjul} *///:~ Exercise 25 //: holding/E25_WordsInfo3.mindview. ERITREA=Asmara. KENYA=Nairobi. CAMEROON=Yaounde. record in the ArrayList<Integer> * the word count associated with that word . CAPE VERDE=Praia. CAPE VERDE=Praia. BURUNDI=Bujumbura. Use * net. GUINEA=Conakry. THE GAMBIA=Banjul. LESOTHO=Maseru} {ALGERIA=Algiers.put(key. LESOTHO=Maseru. COTE D'IVOIR (IVORY COAST)=Yamoussoukro.String>(). GABON=Libreville. import net. BISSAU=Bissau. stat=[101. 28]. null=[126]. 119. 127. 53. argument=[35]. 36]. 118. public=[88. 131. main=[94]. 37. file=[20. time=[29]. int wordCount = 0. put=[132]. add=[136].get(word). word=[26. 25=[5]. Use=[12]. 48]. } System. and=[21. 121. at=[27]. with=[64]. 105. open=[17]. 113. effect=[70]. count=[62]} *///:~ Exercise 26 //: holding/E26_WordsInfo4. 71. 95. 25. 117]. 51. net=[13. 106. static=[92]. was=[79]. 56. stat. 66. 84]. them=[46]. ArrayList<Integer>> stat = new HashMap<String. } loc. } } /* Output: (Sample) {holding=[1]. println=[140]. void=[93]. 18. 137]. 86]. 57. java=[3. 123.java /****************** Exercise 26 ***************** * Take the resulting Map from the previous * exercise and recreate the order of the words as 210 Thinking in Java.add(++wordCount). 73]. Count=[40]. 38. 43]. 129]. the=[33. mindview=[14. 115]. 59. 114]. for=[49. 4th Edition Annotated Solution Guide . 91]. args=[96]. 99. Create=[6]. you=[44]. 90.println(stat). associated=[63]. int=[107]. 69. ArrayList=[10. 116]. this=[67]. text=[19].java". constructor=[39]. Exercise=[4]. import=[81. 100. E25_WordsInfo3=[2. 104. for(String word : new TextFile("E25_WordsInfo3. System=[138]. in=[24. loc). 130]. 87]. is=[68]. Map=[8. 128]. 110]. 77]. out=[139]. 74]. loc=[120. 45]. HashMap=[103]. found=[80]. it=[23]. ArrayList<Integer>>(). second=[34]. 54. 41. 75]. 125. 58. 82. a=[7. 52. TextFile=[15. record=[55]. get=[122]. 0=[109]. 61. to=[16. Integer=[11. location=[72]. 133]. where=[76]. 97]. words=[42]. new=[102. 112. 85]. 98. 47. as=[32. "\\W+")) { ArrayList<Integer> loc = stat. if=[124]. 60.out. 141]. 111]. each=[50]. if(loc == null) { loc = new ArrayList<Integer>(). 134. class=[89].public class E25_WordsInfo3 { public static void main(String[] args) { Map<String. that=[65. 78. util=[83. read=[22. wordCount=[108. use=[30].put(word. 135]. W=[31. String=[9. stat. loc). original. for(Map. class. loc.getValue()) words.println(origWords). get.ArrayList<Integer>>(). net. } loc.put(word. String. word. int. Map. mindview. recreate.entrySet()) for(Integer pos : entry. Integer. of. from. 0. origWords. word. Integer. Exercise. List<String> origWords = new TextFile("E26_WordsInfo4.String> words = new TreeMap<Integer. entry.util.String>(). new. they. the. java. util. } } /* Output: (Sample) [holding. wordCount.* they appeared in the original file. void. order. in. for. appeared. String. ***********************************************/ package holding. // Test the correctness. System. Also. if. import. E26_WordsInfo4. TreeMap<Integer. java. Integer. words. public. where the key // is the position of the word in the file. public. and.println(words. Take. public class E26_WordsInfo4 { public static void main(String[] args) { Map<String.ArrayList<Integer>> entry : stat.mindview. null. System.out. String.put(pos. the. new. loc. new. as. the.util. exercise. for(String word : origWords) { ArrayList<Integer> loc = stat. resulting. word. } // Now recreate the original order of the words. ArrayList. String. the. W. loc. E26_WordsInfo4.Entry<String. Holding Your Objects 211 . HashMap.add(++wordCount). util. List. stat. java. the.ArrayList<Integer>> stat = new HashMap<String. // we will sort words based on their positions. previous. stat. put.*. // We will use an inverted structure. loc. E26_WordsInfo4. TextFile. main.values()). args.get(word). if(loc == null) { loc = new ArrayList<Integer>(). ArrayList. ArrayList. import.*. int wordCount = 0.getKey()). file. "\\W+").out. import java. import net. static. Integer. String. 26. loc. origWords. ArrayList. Map. stat.java". previous. Take. TextFile. words. E26_WordsInfo4. on. Entry. Also. TreeMap. Pass the filled Queue to a method * in a third class that consumes the objects in the 212 Thinking in Java. origWords. will. Integer. String. Integer. args. java. Integer. as. Test. E26_WordsInfo4. stat. We. origWords. new. String. public. the. util. the. entry. the. origWords. their. where. will. entrySet. Map. based. where. we. wordCount. out. entry. You can retrieve all entries of a map by calling the map’s entrySet( ) method. null. an. Integer. Write a second class with * a method that fills a Queue with Command objects * and returns it. an. recreate. System. Integer. original. new. getKey. entry. String. the. import. getValue. static. ArrayList. words. word. their. of. put. for. get. from. Exercise 27 //: holding/E27_CommandQueue. String. String. the. in. file. the. int. use. recreate. entry. in. Integer. System. resulting. is. Now. pos. key. loc. the. the. positions. new. stat. entrySet. will. the. entry. new. java. Integer. println. words. out. words. java. public. Map. Map. structure. words. in. ArrayList. the. values] *///:~ Map. words. ArrayList. String. stat. order. the. word. word. entry. 26. stat. of. loc. structure. wordCount. loc. exercise. on. println. order. of. correctness. the. we. file. file. the. Entry. put. Integer. Integer.java /****************** Exercise 27 ***************** * Write a class called Command that contains a * String and has a method operation() that * displays the String. Map. 4th Edition Annotated Solution Guide . use. of. the. getKey. System. List. word. order. words. Now. getValue. position. correctness. will. String. recreate. TreeMap. W. Integer.add. Integer. new. System. out. wordCount. net. mindview. for. Also. the. for. We. ArrayList. out. sort. import. positions. void. values] [holding. sort. String. class. words. loc. words. pos. TreeMap. inverted. position. String. inverted. word. the. Exercise. ArrayList. based. for. main. the. if. TreeMap. Test.Entry holds one key-value pair of a map. 0. of. original. pos. String. and. is. ArrayList. put. String. words. appeared. loc. println. E26_WordsInfo4. they. for. the. words. pos. Integer. origWords. stat. key. util. the. original. println. add. HashMap. *. import java.consume(cmds). Producer. Holding Your Objects 213 . with the main( ) method acting as a controller. Consumer.offer(new Command("delete")). q.* Queue and calls their operation() methods.produce(cmds). Command(String cmd) { this.println(cmd).offer(new Command("exit")). } } public class E27_CommandQueue { public static void main(String[] args) { Queue<Command> cmds = new LinkedList<Command>(). } } class Producer { public static void produce(Queue<Command> q) { q.cmd = cmd.remove().out.peek() != null) q. class Command { private final String cmd. ***********************************************/ package holding. } public void operation() { System.offer(new Command("save")). } } class Consumer { public static void consume(Queue<Command> q) { while(q.offer(new Command("load")). q.util. } } /* Output: load delete save exit *///:~ This classic producer-consumer scenario uses the queue to transfer objects from one area of a program (producer) to another (consumer).operation(). q. ***********************************************/ 214 Thinking in Java.5166020801268457 0.out.2613610344283964 0. public class E28_PriorityQueue { static Random rand = new Random(47).java // {ThrowsException} /****************** Exercise 29 ***************** * Create a simple class that inherits from Object * and contains no members.Exercise 28 //: holding/E28_PriorityQueue.poll()) System.println().print(data + " "). for(int i = 0.poll(). data != null.out. printQ(priorityQueue). i < 10.16020656493302599 0.5309454508634242 0.java /****************** Exercise 28 ***************** * Fill a PriorityQueue (using offer()) with * Double values created using java.nextDouble()). } } /* Output: 0. public static void printQ(Queue<?> queue) { for(Object data = queue. System. } public static void main(String[] args) { PriorityQueue<Double> priorityQueue = new PriorityQueue<Double>().18847866977771732 0.2678662084200585 0.Random. * then remove the elements using poll() and * display them.8037155449603999 *///:~ Exercise 29 //: holding/E29_PriorityQueueSubtlety. 4th Edition Annotated Solution Guide .*.7271157860730044 0. and show that you cannot * successfully add multiple elements of that class * to a PriorityQueue.util.0508673570556899 0.offer(rand.7620665811558285 0. data = queue. import java. ***********************************************/ package holding. i++) priorityQueue. This issue will be fully * explained in the Containers in Depth chapter.util. out.println(). System. System.offer(new Dummy()). import typeinfo.package holding. import java.println("Adding 2nd instance.util.java so that it does * not inherit from AbstractCollection. ***********************************************/ package holding.out. } Holding Your Objects 215 .").hasNext()) { Pet p = it. priorityQueue.. System. Exercise 30 //: holding/E30_CollectionSequence2.println().print(p.out.offer(new Dummy()).*..util. class Dummy {} public class E29_PriorityQueueSubtlety { public static void main(String[] args) { PriorityQueue<Dummy> priorityQueue = new PriorityQueue<Dummy>().out.next().println("Adding 1st instance."). System.*. import java.id() + ":" + p + " "). class CollectionSequence2 extends PetSequence implements Collection<Pet> { static void display(Iterator<Pet> it) { while(it.id() + ":" + p + " ")... priorityQueue.java /****************** Exercise 30 ***************** * Modify CollectionSequence.out.print(p. } System. but instead * implements Collection.” The second insertion will trigger an exception.out. } } ///:~ The JDK documentation states: “A priority queue relying on natural ordering also does not permit insertion of non-comparable objects (doing so may result in ClassCastException).pets.*. } static void display(Collection<Pet> pets) { for(Pet p : pets) System. } public boolean isEmpty() { return pets.length]. } public boolean addAll(Collection<? extends Pet> c) { throw new UnsupportedOperationException(). } public boolean containsAll(Collection<?> c) { Iterator<?> it = c.iterator().length. } // Other methods. } }. 216 Thinking in Java. } public void remove() { // Not implemented throw new UnsupportedOperationException().hasNext()) if(!contains(it.public int size() { return pets. for(int i = 0. which are also need to be provided.length. public boolean hasNext() { return index < pets. } public boolean retainAll(Collection<?> c) { throw new UnsupportedOperationException(). return false. 4th Edition Annotated Solution Guide . } public Object[] toArray() { Object[] result = new Object[pets.equals(pets[i])) return true. while (it. } public Pet next() { return pets[index++]. } public boolean remove(Object o) { throw new UnsupportedOperationException().length == 0. public boolean add(Pet o) { throw new UnsupportedOperationException(). i < pets.next())) return false. return true. } public Iterator<Pet> iterator() { return new Iterator<Pet>() { private int index. } public void clear() { throw new UnsupportedOperationException(). i++) if(o.length. } public boolean removeAll(Collection<?> c) { throw new UnsupportedOperationException(). } public boolean contains(Object o) { if(o == null) return false. You’ll need to add a * constructor that takes the number of elements Holding Your Objects 217 .getComponentType(). Pug. pets. } } /* Output: 0:Rat 1:Manx 2:Cymric 3:Mutt 4:Pug 5:Cymric 6:Pug 7:Manx 0:Rat 1:Manx 2:Cymric 3:Mutt 4:Pug 5:Cymric 6:Pug 7:Manx [Rat. nor is equals( ). optional operations (as marked in the interface description of Collection) are not supported. Pug. 0. Mutt.length < pets. 0. Manx] *///:~ In the minimalist approach used here.length] = null.newInstance( a.lang. Cymric.length).System.println( Arrays. Exercise 31 //: holding/E31_IterableRandomShapeGenerator.reflect. We also override hashCode( ) and toString( ). the code base is much bigger than that of AbstractCollection in the original version.arraycopy(pets. 0.display(c).arraycopy(pets. System.java * to make it Iterable. pets. return result.arraycopy( ) to copy the arrays quickly inside toArray( ).length). 0.display(c. result.toArray(new Pet[0]))).length > pets. CollectionSequence2.Array. if (result. System. CollectionSequence2. Cymric. } } public class E30_CollectionSequence2 { public static void main(String[] args) { CollectionSequence2 c = new CollectionSequence2().length) a = (T[])java. result.java /****************** Exercise 31 ***************** * Modify polymorphism/shape/RandomShapeGenerator.out. T[] result = a.length) result[pets.length). Manx.getClass(). Note the use of System.iterator()).toString(c. } @SuppressWarnings("unchecked") public <T> T[] toArray(T[] a) { if (a. pets. Even with these restrictions. return result. import java. } } /* Output: Triangle Triangle 218 Thinking in Java. } } } public class E31_IterableRandomShapeGenerator { public static void main(String[] args) { RandomShapeGenerator rsg = new RandomShapeGenerator(10). import polymorphism. 4th Edition Annotated Solution Guide .*. } }.util. } private Shape nextShape() { switch(rand. Verify that it works. case 2: return new Triangle().quantity = quantity. return nextShape().out. ***********************************************/ package holding. } public Iterator<Shape> iterator() { return new Iterator<Shape>() { private int count. } public void remove() { // Not implemented throw new UnsupportedOperationException().getClass().println(shape. } public Shape next() { ++count.nextInt(3)) { default: case 0: return new Circle(). private final int quantity. for(Shape shape : rsg) System.* that you want the iterator to produce before * stopping.shape.getSimpleName()). class RandomShapeGenerator implements Iterable<Shape> { private Random rand = new Random(47). public boolean hasNext() { return count < quantity. RandomShapeGenerator(int quantity) { this.*. case 1: return new Square(). } public Pet next() { return pets[current--]. class PetSequence { protected Pet[] pets = Pets. } }.pets.java.Square Triangle Square Triangle Square Triangle Circle Square *///:~ Exercise 32 //: holding/E32_MultiIterableNonCollectionSeq.1. } public Iterable<Pet> randomized() { Holding Your Objects 219 .*. ***********************************************/ package holding. public boolean hasNext() { return current > -1. * add reversed() and randomized() methods to * NonCollectionSequence. } }. import typeinfo. import java. as well as making * NonCollectionSequence implement Iterable.*. } class NonCollectionSequence extends PetSequence implements Iterable<Pet> { public Iterable<Pet> reversed() { return new Iterable<Pet>() { public Iterator<Pet> iterator() { return new Iterator<Pet>() { int current = pets.length .util.java /****************** Exercise 32 ***************** * Following the example of MultiIterableClass. } public void remove() { // Not implemented throw new UnsupportedOperationException(). and * show that all the approaches work in foreach * statements.createArray(8). } }. for(Pet pet : nc.println(). return shuffled.length. } public Iterator<Pet> iterator() { return new Iterator<Pet>() { private int index = 0. } } /* Output: Manx Pug Cymric Pug Mutt Cymric Manx Rat Pug Mutt Pug Rat Manx Manx Cymric Cymric Rat Manx Cymric Mutt Pug Cymric Pug Manx *///:~ 220 Thinking in Java.out.shuffle(shuffled. } public void remove() { // Not implemented throw new UnsupportedOperationException().print(pet + " ").print(pet + " ").println(). } public Pet next() { return pets[index++]. System. public boolean hasNext() { return index < pets.reversed()) System. Collections.iterator(). for(Pet pet : nc) System.return new Iterable<Pet>() { public Iterator<Pet> iterator() { List<Pet> shuffled = new ArrayList<Pet>(Arrays. new Random(47)).out.randomized()) System.out. } }.print(pet + " ").asList(pets)). 4th Edition Annotated Solution Guide .out. } } public class E32_MultiIterableNonCollectionSeq { public static void main(String[] args) { NonCollectionSequence nc = new NonCollectionSequence (). System.out. for(Pet pet : nc. public class E01_SimpleException { public static void main(String args[]) { try { throw new Exception("An exception in main").println("In finally clause"). } } } /* Output: e. ***********************************************/ package exceptions. } finally { System. Add a * finally clause and print a message to prove * you were there.Error Handling with Exceptions Exercise 1 //: exceptions/E01_SimpleException.out.java /****************** Exercise 1 ****************** * Create a class with a main() that throws an * object of class Exception inside a try block. Try to call a method through this 221 .getMessage()). } catch(Exception e) { System.getMessage() = An exception in main In finally clause *///:~ Exercise 2 //: exceptions/E02_NullReference.java /****************** Exercise 2 ****************** * Define an object reference and initialize it * to null. * Give the constructor for Exception a String * argument.out.getMessage() = " + e. Catch the exception inside a catch * clause and print the String argument.println( "e. try { array[10] = 'x'.* reference. public class E02_NullReference { public static void main(String args[]) { String s = null.toString().lang. } catch(ArrayIndexOutOfBoundsException e) { System. try { s.println("e = " + e). // Causes a NullPointerException: //! s.NullPointerException *///:~ When you catch the exception.println("Caught exception " + e). ***********************************************/ package exceptions. ***********************************************/ package exceptions. 4th Edition Annotated Solution Guide .out.toString(). } } } /* Output: e = java.lang. public class E03_ArrayIndexBounds { public static void main(String args[]) { char[] array = new char[10].ArrayIndexOutOfBoundsException: 10 *///:~ 222 Thinking in Java.out. } catch(Exception e) { System. } } } /* Output: Caught exception java. Now wrap the code in a try-catch * clause to catch the exception.java /****************** Exercise 3 ****************** * Write code to generate and catch an * ArrayIndexOutOfBoundsException. the program runs to completion. Exercise 3 //: exceptions/E03_ArrayIndexBounds. } catch(MyException e) { e.println("msg = " + msg). public MyException(String msg) { this.java /****************** Exercise 4 ****************** * Create your own exception class using the * extends keyword. } public void printMsg() { System.getMessage() = " + e. } } // Or take a more clever approach. } } public class E04_ExceptionClass { public static void main(String args[]) { try { throw new MyException("MyException message"). } catch(MyException2 e) { System. Create a try-catch clause to exercise * your new exception.Exercise 4 //: exceptions/E04_ExceptionClass.println( "e. * Write a method that prints out the stored * String. // noting that string storage and printing are // built into Exception: class MyException2 extends Exception { public MyException2(String s) { super(s).getMessage()).out. } try { throw new MyException2("MyException2 message"). ***********************************************/ package exceptions.msg = msg. Write a constructor for this * class that takes a String argument and stores * it inside the object with a String reference. // Following the instructions to the letter: class MyException extends Exception { String msg. } } Error Handling with Exceptions 223 .out.printMsg(). } System.out.} /* Output: msg = MyException message e.. Successful execution *///:~ 224 Thinking in Java. 4th Edition Annotated Solution Guide . class ResumerException extends Exception {} class Resumer { static int count = 3. continue.println("Got through. } System. break.ResumerException Got through..out. } } /* Output: Caught exceptions.println("Successful execution")..ResumerException Caught exceptions. } catch(ResumerException e) { System.println("Caught " + e). } } public class E05_Resumption { public static void main(String args[]) { while(true) { try { Resumer.out.java /****************** Exercise 5 ****************** * Create your own resumption-like behavior using * a while loop that repeats until an exception * is no longer thrown.f(). ***********************************************/ package exceptions. static void f() throws ResumerException { if(--count > 0) throw new ResumerException().getMessage() = MyException2 message *///:~ Exercise 5 //: exceptions/E05_Resumption.."). util. ***********************************************/ package exceptions.logging.err.getLogger("LoggingException2").*.err. } } } /* Output: (45% match) Error Handling with Exceptions 225 . import java.java /****************** Exercise 6 ****************** * Create two exception classes. } } public class E06_LoggingExceptions { public static void main(String[] args) { try { throw new LoggingException1(). printStackTrace(new PrintWriter(trace)). } try { throw new LoggingException2(). public LoggingException2() { StringWriter trace = new StringWriter().toString()). * Demonstrate that these work.io. } } class LoggingException2 extends Exception { private static Logger logger = Logger. import java. public LoggingException1() { StringWriter trace = new StringWriter(). class LoggingException1 extends Exception { private static Logger logger = Logger.println("Caught " + e). printStackTrace(new PrintWriter(trace)).getLogger("LoggingException1").severe(trace.Exercise 6 //: exceptions/E06_LoggingExceptions.*. each of which * performs its own logging automatically.toString()). logger. } catch(LoggingException2 e) { System.println("Caught " + e). } catch(LoggingException1 e) { System. logger.severe(trace. try { array[10] = 'x'.*. } public static void main(String args[]) { char[] array = new char[10]. java:39) Caught exceptions. 2007 3:10:36 PM exceptions.LoggingException1 at exceptions.LoggingException1 Sep 12.printStackTrace(new PrintWriter(trace)).Sep 12. } } 226 Thinking in Java.main(E06_LoggingExceptions. 2007 3:10:36 PM exceptions.LoggingException2 <init> SEVERE: exceptions.main(E06_LoggingExceptions.*.severe(trace.io. e. static void logException(Exception e) { StringWriter trace = new StringWriter().E06_LoggingExceptions.E06_LoggingExceptions. logger.LoggingException2 *///:~ Each exception employs its own logger instance.LoggingException1 <init> SEVERE: exceptions.logging.util. java:34) Caught exceptions. respectively. Exercise 7 //: exceptions/E07_LoggedArrayIndexBounds.getLogger("E07_LoggedArrayIndexBounds"). } catch(ArrayIndexOutOfBoundsException e) { logException(e).LoggingException2 at exceptions. 4th Edition Annotated Solution Guide .toString()). import java. import java. public class E07_LoggedArrayIndexBounds { private static Logger logger = Logger.java /****************** Exercise 7 ****************** * Modify Exercise 3 so that the catch clause logs * the results. ***********************************************/ package exceptions. LoggingException1 and LoggingException2. } public void g() throws MyException { throw new MyException("Inside g()"). ***********************************************/ package exceptions. try { t.printMsg(). 10:51:22 E07_LoggedArrayIndexBounds logException SEVERE: java. } } } /* Output: msg = Inside g() *///:~ Error Handling with Exceptions 227 .} /* Output: (80% match) 2005.g(). * Try compiling it without an exception * specification to see what the compiler says. } catch(MyException e) { e.16.09.java /****************** Exercise 8 ****************** * Write a class with a method that throws an * exception of the type created in Exercise 4. must be caught // or declared to be thrown" //! throw new MyException("Inside f()"). } } public class E08_ExceptionSpecification { public static void main(String args[]) { Thrower t = new Thrower().main(E07_LoggedArrayIndexBounds. * Add the appropriate exception specification. class Thrower { public void f() { // Compiler gives an error: "unreported // exception MyException.ArrayIndexOutOfBoundsException: 10 at E07_LoggedArrayIndexBounds.lang. * Try out your class and its exception inside a * try-catch clause.j ava:20) *///:~ Exercise 8 //: exceptions/E08_ExceptionSpecification. Exercise 9 //: exceptions/E09_CatchAll. you can just catch Exception. } } public class E09_CatchAll { public static void main(String args[]) { Thrower2 t = new Thrower2().java /****************** Exercise 9 ****************** * Create three new types of exceptions. } } } /* Output: caught exceptions. class class class class ExBase extends Exception {} Ex1 extends ExBase {} Ex2 extends ExBase {} Ex3 extends ExBase {} class Thrower2 { void f() throws Ex1. 228 Thinking in Java. ***********************************************/ package exceptions. 4th Edition Annotated Solution Guide . Ex3 { throw new Ex1(). Write a * class with a method that throws all three. } catch(Exception e) { System. try { t. then catch the common base exception.f().Ex1 *///:~ We create a common base class for all three exceptions. // You aren't forced to throw all the // exceptions in the specification. call the method but only use a single * catch clause that will catch all three types * of exceptions. from which all exceptions inherit.println("caught " + e). } catch(ExBase e) { System. Ex2. In * main(). Alternatively.println("caught " + e).out.out. try { ce.f().java /****************** Exercise 10 ****************** * Create a class with two methods.java Error Handling with Exceptions 229 . Test your code in main().Exercise 10 //: exceptions/E10_ChangeException. } catch(AnotherException e) { System. } } public static void main(String args[]) { E10_ChangeException ce = new E10_ChangeException(). } catch(AnException e) { throw new AnotherException(). } public void f() throws AnotherException { try { g(). including throw another exception. in the catch clause.out. Exercise 11 //: exceptions/E11_ChangeToRuntimeException. catch its * exception and. it’s handled. throw an exception of a new type that * you define. In f().println("Caught " + e). ***********************************************/ package exceptions. } } } /* Output: Caught exceptions. throw a * different exception (of a second type that you * define). call g(). class AnException extends Exception {} class AnotherException extends Exception {} public class E10_ChangeException { public void g() throws AnException { throw new AnException(). * In g(). f() and g(). and you can do anything with it.AnotherException *///:~ Once the catch clause grabs the exception. wrap g()'s exception in a * RuntimeException. but inside the * catch clause. } } public static void main(String args[]) { E11_ChangeToRuntimeException ce = new E11_ChangeToRuntimeException(). } } ///:~ RuntimeException now envelopes Exception. Exercise 12 //: exceptions/E12_SequenceExceptions. } public void f() { try { g(). class AnException2 extends Exception {} public class E11_ChangeToRuntimeException { public void g() throws AnException2 { throw new AnException2(). 4th Edition Annotated Solution Guide . class SequenceFullException extends RuntimeException {} class Sequence2 { private Object[] objects. ***********************************************/ package exceptions.java so that it throws an * appropriate exception if you try to put in too many * elements. *********************************************************/ package exceptions.java // {ThrowsException} /************************ Exercise 12 ******************** * Modify innerclasses/Sequence. so we do not need a try block. 230 Thinking in Java.// {ThrowsException} /****************** Exercise 11 ****************** * Repeat the previous exercise.f(). } catch(AnException2 e) { throw new RuntimeException(e). private int next. ce. else throw new SequenceFullException(). public boolean end() { return i == objects. } public void next() { if(i < objects. i++) sequence. } } ///:~ SequenceFullException is more appropriate than RuntimeException because it indicates a programmer error. public class E13_Finally { public static void throwNull() { throw new NullPointerException(). } public Object current() { return objects[i]. Exercise 13 //: exceptions/E13_Finally. i < 11. ***********************************************/ package exceptions.toString(i)). } public void add(Object x) { if(next < objects. } } public class E12_SequenceExceptions { public static void main(String[] args) { Sequence2 sequence = new Sequence2(10). } public static void main(String args[]) { Thrower2 t = new Thrower2(). for(int i = 0. even * if a NullPointerException is thrown. } private class SequenceSelector implements Selector { private int i. * Verify that your finally clause is executed.length.length) objects[next++] = x. Error Handling with Exceptions 231 . } } public Selector selector() { return new SequenceSelector().length) i++.add(Integer.java // {ThrowsException} /****************** Exercise 13 ***************** * Modify Exercise 9 by adding a finally clause.public Sequence2(int size) { objects = new Object[size]. println("caught " + } finally { System. t. it still executes the finally clause.f().java:10) at E13_Finally.err.println("caught " + } finally { System.err.NullPointerException at E13_Finally. public class E14_OnOffSwitch { static Switch sw = new Switch(). } catch(ExBase e) { System.throwNull(E13_Finally. } catch(ExBase e) { System. e).java:22) Although the code doesn’t catch the NullPointerException.println("In finally } try { throwNull().java can fail by * throwing a RuntimeException inside the try * block.f().out. 4th Edition Annotated Solution Guide . clause A").java /****************** Exercise 14 ***************** * Show that OnOffSwitch. OnOffException2 { throw new RuntimeException("Inside try"). } public static void main(String[] args) { 232 Thinking in Java.main(E13_Finally.try { t. The output is: caught Ex1 In finally clause A In finally clause B Exception in thread "main" java.println("In finally } } } ///:~ e). ***********************************************/ package exceptions. static void f() throws OnOffException1.lang. clause B"). Exercise 14 //: exceptions/E14_OnOffSwitch.out. out.out.out. f(). } catch(OnOffException2 e) { System.lang. static void f() throws OnOffException1. } } catch(RuntimeException e) { System. System.out. } } } /* Output: on on Oops! the exception 'java.off().. } catch(OnOffException1 e) { System. public class E15_WithFinally { static Switch sw = new Switch().println(sw).java /****************** Exercise 15 ***************** * Show that WithFinally.println("OnOffException2")..java doesn't fail by * throwing a RuntimeException inside the try * block.off().println("OnOffException1"). sw.try { try { sw.off(). sw. ***********************************************/ package exceptions.on(). sw.println("Oops! the exception '" + e + "' slipped through without " + "turning the switch off!"). } public static void main(String[] args) { try { try { sw. // Code that can throw exceptions.on(). Error Handling with Exceptions 233 . OnOffException2 { throw new RuntimeException("Inside try").RuntimeException: Inside try' slipped through without turning the switch off! *///:~ Exercise 15 //: exceptions/E15_WithFinally. java /****************** Exercise 16 ***************** * Modify reusing/CADSystem.println("Exception '" + e + "'.println("OnOffException1").println("OnOffException2").CADSystem x = new reusing.lang.. ***********************************************/ package exceptions.CADSystem(47).dispose(). f().out. try { return.out. Exercise 16 //: exceptions/E16_CADSystem.// Code that can throw exceptions.RuntimeException: Inside try'.. } } } /* Output: on off Exception 'java.off().out.println(sw). } catch(OnOffException1 e) { System. } finally { x. Did the switch get turned off?"). 4th Edition Annotated Solution Guide .out. } catch(OnOffException2 e) { System. even with an unexpected exception. public class E16_CADSystem { public static void main(String[] args) { reusing. System. } } } /* Output: 234 Thinking in Java. } } catch(RuntimeException e) { System.java to demonstrate * that returning from the middle of a try-finally * will still perform proper cleanup. Did the switch get turned off? off *///:~ The finally clause guarantees the necessary cleanup. } finally { sw. } } public class E17_Frog { public static void main(String[] args) { Frog2 frog = new Frog2().java so that it uses * try-finally to guarantee proper cleanup. ***********************************************/ package exceptions. 1 Shape dispose Erasing Line: 0. 1 Shape constructor Drawing Line: 2. and * show that this works even if you return from the * middle of the try-finally.dispose() Erasing Triangle Shape dispose Erasing Circle Shape dispose Erasing Line: 2. 0 Shape constructor Drawing Line: 1. 0 Shape dispose Shape dispose *///:~ Exercise 17 //: exceptions/E17_Frog.Shape constructor Shape constructor Drawing Line: 0. // Frog. cannot be called directly class Frog2 extends polymorphism.dispose().dispose() is protected.Frog { protected void dispose() { super. try { Error Handling with Exceptions 235 . 4 Shape dispose Erasing Line: 1.java /****************** Exercise 17 ***************** * Modify polymorphism/Frog. 4 Shape constructor Drawing Circle Shape constructor Drawing Triangle Combined constructor CADSystem. // No return in the middle. } finally { frog. try { // With return in the middle.dispose(). } frog = new Frog2(). return.. } finally { frog... } } } /* Output: Creating Characteristic is alive Creating Description Basic Living Creature LivingCreature() Creating Characteristic has heart Creating Description Animal not Vegetable Animal() Creating Characteristic can live in water Creating Description Both water and land Amphibian() Creating Characteristic Croaks Creating Description Eats Bugs Frog() Frog dispose disposing Description Eats Bugs disposing Characteristic Croaks Amphibian dispose disposing Description Both water and land disposing Characteristic can live in water Animal dispose disposing Description Animal not Vegetable disposing Characteristic has heart LivingCreature dispose disposing Description Basic Living Creature disposing Characteristic is alive Creating Characteristic is alive Creating Description Basic Living Creature LivingCreature() Creating Characteristic has heart Creating Description Animal not Vegetable Animal() Creating Characteristic can live in water Creating Description Both water and land Amphibian() Creating Characteristic Croaks 236 Thinking in Java..dispose(). 4th Edition Annotated Solution Guide . java /****************** Exercise 18 ***************** * Add a second level of exception loss to * LostMessage.Creating Description Eats Bugs Frog() Frog dispose disposing Description Eats Bugs disposing Characteristic Croaks Amphibian dispose disposing Description Both water and land disposing Characteristic can live in water Animal dispose disposing Description Animal not Vegetable disposing Characteristic has heart LivingCreature dispose disposing Description Basic Living Creature disposing Characteristic is alive *///:~ As you see. } } class LostMessage2 { void f() throws VeryImportantException { throw new VeryImportantException(). both cases elicit the proper cleanup. class YetAnotherException extends Exception { public String toString() { return "Yet another exception". Exercise 18 //: exceptions/E18_LostMessage. ***********************************************/ package exceptions.java so that the HoHumException is * itself replaced by a third exception. } } Error Handling with Exceptions 237 . } void cleanup() throws YetAnotherException { throw new YetAnotherException(). } void dispose() throws HoHumException { throw new HoHumException(). java /****************** Exercise 19 ***************** * Repair the problem in LostMessage.f(). public class E19_GuardedLostMessage { void f() throws VeryImportantException { throw new VeryImportantException(). } void dispose() throws HoHumException { throw new HoHumException(). ***********************************************/ package exceptions. try { try { lm.println(e). Note. 4th Edition Annotated Solution Guide .java by * guarding the call in the finally clause. } } catch(Exception e) { System. } public static void main(String[] args) { try { LostMessage lm = new LostMessage().public class E18_LostMessage { public static void main(String[] args) { try { LostMessage2 lm = new LostMessage2(). } finally { lm. try { lm.cleanup(). the outer try block enables the program to run to completion. not VeryImportantException or HoHumException.dispose(). however.f(). that YetAnotherException appears in the output. } } finally { lm. } } } /* Output: Yet another exception *///:~ Again.out. 238 Thinking in Java. Exercise 19 //: exceptions/E19_GuardedLostMessage. java by adding an * UmpireArgument exception type and methods * that throw this exception. Foul. the outer try block properly reports VeryImportantException. UmpireArgument. ***********************************************/ package exceptions. } } } catch(Exception e) { System.out. Exercise 20 //: exceptions/E20_UmpireArgument.dispose(). Test the modified * hierarchy. class class class class BaseballException extends Exception {} Foul extends BaseballException {} Strike extends BaseballException {} UmpireArgument extends BaseballException {} abstract class Inning { Inning() throws BaseballException {} void event () throws BaseballException {} abstract void atBat() throws Strike.} finally { try { lm.println(e). } catch(HoHumException e) { System.println(e).out.java /****************** Exercise 20 ***************** * Modify StormyInning. abstract void decision() throws UmpireArgument. } } } /* Output: A trivial exception A very important exception! *///:~ This time. void walk() {} // Throws nothing } class StormException extends Exception {} class RainedOut extends StormException {} class PopFoul extends Foul {} Error Handling with Exceptions 239 . println("Pop foul"). } catch(PopFoul e) { System. public void rainHard() throws RainedOut.println("Foul").interface Storm { public void event() throws RainedOut. still catches // the new exception: try { StormyInning si = new StormyInning(). } // Strike not thrown in derived version.println("Strike"). BaseballException {} StormyInning(String s) throws Foul.println("Rained out"). BaseballException {} public void rainHard() throws RainedOut {} public void event() {} void atBat() throws PopFoul. } catch(BaseballException e) { System.atBat().out. } catch(Foul e) { System.atBat(). } class StormyInning extends Inning implements Storm { StormyInning() throws RainedOut. 4th Edition Annotated Solution Guide .out.println("Rained out"). } } public class E20_UmpireArgument { public static void main(String[] args) { // Same code as before. si.out.out. } void decision() throws UmpireArgument { throw new UmpireArgument(). UmpireArgument { throw new UmpireArgument(). } catch(RainedOut e) { System. } catch(BaseballException e) { 240 Thinking in Java. } catch(RainedOut e) { System.println("Generic error"). } catch(Strike e) { System.out. i. try { Inning i = new StormyInning().out. Exercise 21 //: exceptions/E21_ConstructorExceptions.out. } catch(UmpireArgument e) { System.out.println("Rained out"). si. } } class BaseWithException { public BaseWithException() throws Except1 { throw new Except1("thrown by BaseWithException"). } Error Handling with Exceptions 241 . ***********************************************/ package exceptions. si.System. class Except1 extends Exception { public Except1(String s) { super(s).println( "Argument with the umpire").java /****************** Exercise 21 ***************** * Demonstrate that a derived-class constructor * cannot catch exceptions thrown by its * base-class constructor. } // Or you can add code to catch the // specific type of exception: try { StormyInning si = new StormyInning().out. } catch(BaseballException e) { System. } } } /* Output: Generic error Generic baseball exception Argument with the umpire *///:~ The exception hierarchy in the first two lines allows us to add new exceptions without forcing changes to existing code.decision().out. } catch(PopFoul e) { System.out.println("Pop foul"). } catch(RainedOut e) { System.atBat().println("Generic baseball exception").println("Generic error"). ***********************************************/ package exceptions.Except1: thrown by BaseWithException *///:~ Remember. write code that properly guards against * this failure. Exercise 22 //: exceptions/E22_FailingConstructor. 4th Edition Annotated Solution Guide . } catch(Except1 ex1) { System. In * main().} class DerivedWE extends BaseWithException { // Produces compile-time error: // unreported exception Except1 // ! public DerivedWE() {} // Gives compile error: call to super must be // first statement in constructor: //! public DerivedWE() { //! try { //! super().out. This is why a derived-class constructor cannot catch a base-class constructor exception: it can’t “recover” from the exception failure.java /****************** Exercise 22 ***************** * Create a class called FailingConstructor with a * constructor that might fail partway through the * construction process and throw an exception. when you throw an exception. } } } /* Output: Caught exceptions.println("Caught " + ex1). 242 Thinking in Java. since there’s no baseclass sub-object. //! } catch(Except1 ex1) { //! } //! } public DerivedWE() throws Except1 {} } public class E21_ConstructorExceptions { public static void main(String args[]) { try { new DerivedWE(). the compiler creates no object. .").class ConstructionException extends Exception {} class FailingConstructor { FailingConstructor(boolean fail) throws ConstructionException { if(fail) throw new ConstructionException()..println("Processing... Construction has failed: exceptions.ConstructionException *///:~ This program shows both the success and failure of the construction process. Processing.out. Error Handling with Exceptions 243 . ... Cleanup. Constructing. } } catch(ConstructionException e) { System.out. break. b = !b) try { System..out.println("Construction has failed: " + e)... } } public class E22_FailingConstructor { public static void main(String args[]) { for(boolean b = false.out. } } } /* Output: Constructing...println("Constructing. The code follows the idiom presented in TIJ4.java /****************** Exercise 23 ***************** * Add a class with a dispose() method to the * previous exercise. Exercise 23 //: exceptions/E23_FailingConstructor2. Modify FailingConstructor so * that the constructor creates one of these * disposable objects as a member object.. after which * the constructor might throw an exception.").."). try { System.. after * which it creates a second disposable member object. FailingConstructor fc = new FailingConstructor(b).println("Cleanup. } finally { System. } } public class E23_FailingConstructor2 { public static void main(String args[]) { for(boolean b = false. WithDispose(int id) { this."). throw e.println("WithDispose. } } catch(ConstructionException e) { System. 4th Edition Annotated Solution Guide ..println("Constructing. b = !b) try { System. } finally { // We do not have a decent method to call for // cleanup! System.out.out.id = id. break.out.."). try { System. } } class FailingConstructor2 { private final WithDispose wd1.println("Construction has failed: " + e). wd2. . class WithDispose { private final int id.out. } 244 Thinking in Java.")..* Write code to properly guard against failure. try { // "Simulate" other code that might throw exceptions if(fail) throw new ConstructionException()..dispose(): " + id).dispose(). FailingConstructor2 fc = new FailingConstructor2(b). and * in main() verify that all possible failure * situations are covered. } void dispose() { System. } catch(ConstructionException e) { wd1. FailingConstructor2(boolean fail) throws ConstructionException { wd1 = new WithDispose(1). } wd2 = new WithDispose(2).println("Cleanup.out.println("Processing.. ***********************************************/ package exceptions.. b = !b) try { System. Processing.dispose(): 1 Construction has failed: exceptions..dispose(). } } public class E24_FailingConstructor3 { public static void main(String args[]) { for(boolean b = false... try { Error Handling with Exceptions 245 . Constructing...} } /* Output: Constructing. wd1.ConstructionException *///:~ Exercise 24 //: exceptions/E24_FailingConstructor3.dispose(). wd2. WithDispose. class FailingConstructor3 { private final WithDispose wd1. FailingConstructor3 fc = new FailingConstructor3(b).java /****************** Exercise 24 ***************** * Add a dispose() method to the FailingConstructor * class and write code to properly use this class.. ***********************************************/ package exceptions.out. . throw e. FailingConstructor3(boolean fail) throws ConstructionException { wd1 = new WithDispose(1).. } void dispose() { wd2.")... } catch(ConstructionException e) { wd1. Cleanup. try { // "Simulate" other code that might throw exceptions if(fail) throw new ConstructionException().println("Constructing. } wd2 = new WithDispose(2).dispose().. WithDispose. Cleanup.").println("Cleanup. fc. break.ConstructionException *///:~ Exercise 25 //: exceptions/E25_ThreeLevelExceptions.... } } catch(ConstructionException e) { System.dispose(): 1 Construction has failed: exceptions.System. 4th Edition Annotated Solution Guide .dispose(): 2 WithDispose. In main()...dispose(): 1 Constructing. Inherit B from A and override the * method so it throws an exception at level two * of your hierarchy. * Now create a base class A with a method that * throws an exception at the base of your * hierarchy. ***********************************************/ package exceptions. Repeat by inheriting class * C from B.java /****************** Exercise 25 ****************** * Create a three-level hierarchy of exceptions.. create a C and upcast it * to A. Processing.out. } } } /* Output: Constructing.println("Processing. } finally { System. } } 246 Thinking in Java.out.. then call the method.println("Construction has failed: " + e).. class Level1Exception extends Exception {} class Level2Exception extends Level1Exception {} class Level3Exception extends Level2Exception {} class A { public void f() throws Level1Exception { throw new Level1Exception()...out..dispose().").. WithDispose. public class E26_MainException { // Pass all exceptions to the console: public static void main(String[] args) throws Exception { // Open the file: FileInputStream file = new FileInputStream("DoesnotExist.f( ) throws. } } } /* Output: Caught exceptions.java // {ThrowsException} /****************** Exercise 26 ****************** * Change the file name string in MainException.Level3Exception *///:~ The compiler forces you to catch a Level1Exception because that’s what A.println("Caught " + e). } } public class E25_ThreeLevelExceptions { public static void main(String args[]) { A a = new C().java to * name a file that doesn't exist. } catch(Level1Exception e) { System.f().file"). Run the program and * note the result ************************************************/ package exceptions. import java. try { a.*.out.class B extends A { public void f() throws Level2Exception { throw new Level2Exception(). Exercise 26 //: exceptions/E26_MainException. } } class C extends B { public void f() throws Level3Exception { throw new Level3Exception(). Error Handling with Exceptions 247 .io. java // {ThrowsException} /****************** Exercise 28 ****************** * Modify Exercise 4 so that the custom exception * class inherits from RuntimeException. // Close the file: file.<init>(Unknown Source) at E27_MainException.io. ***********************************************/ package exceptions.io.main(E27_MainException. try { array[10] = 'x'.FileInputStream.java // {ThrowsException} /****************** Exercise 27 ****************** * Modify Exercise 3 to convert the exception to a * RuntimeException.io. and show 248 Thinking in Java.java:14) Exercise 27 //: exceptions/E27_RuntimeArrayIndexBounds.// Use the file ..FileInputStream.. public class E27_RuntimeArrayIndexBounds { public static void main(String args[]) { char[] array = new char[10]. } } ///:~ The output is: Exception in thread "main" java.FileInputStream. } } } ///:~ Exercise 28 //: exceptions/E28_RuntimeExceptionClass.close().open(Native Method) at java. 4th Edition Annotated Solution Guide .<init>(Unknown Source) at java.FileNotFoundException: DoesnotExist. } catch(ArrayIndexOutOfBoundsException e) { throw new RuntimeException(e).file (The system cannot find the file specified) at java.io. java // {ThrowsException} /****************** Exercise 29 ****************** * Modify all the exception types in StormyInning. Remove the ‘//!’ comments and show how * the methods can be compiled without specifications. class BaseballException2 extends RuntimeException {} class Foul2 extends BaseballException2 {} class Strike2 extends BaseballException2 {} abstract class Inning2 { Inning2() {} public void event() {} abstract void atBat(). class MyRuntimeException extends RuntimeException { public MyRuntimeException(String s) { super(s). } } ///:~ MyRuntimeException is now inherited from RuntimeException. and show * that no exception specifications or try blocks are * necessary. so we do not need a try block.* that the compiler allows you to leave out the * try block. ***********************************************/ package exceptions.java * so that they extend RuntimeException. ***********************************************/ package exceptions. Exercise 29 //: exceptions/E29_StormyInning2. } } public class E28_RuntimeExceptionClass { public static void main(String args[]) { throw new MyRuntimeException("MyRuntimeException msg"). void walk() {} } class StormException2 extends RuntimeException {} class RainedOut2 extends StormException2 {} class PopFoul2 extends Foul2 {} Error Handling with Exceptions 249 . case 1: throw new Sneeze().java so that the * technique in TurnOffChecking. // Or let catch exceptions: for(int i = 0. } } ///:~ Exercise 30 //: exceptions/E30_Human. si. } } catch(Exception e) { // Adapt to unchecked: throw new RuntimeException(e). void rainHard(). ***********************************************/ package exceptions. } } public class E29_StormyInning2 { public static void main(String[] args) { StormyInning2 si = new StormyInning2(). } class StormyInning2 extends Inning2 implements Storm2 { StormyInning2() {} StormyInning2(String s) {} public void rainHard() {} void atBat() { throw new PopFoul2(). } } public static void main(String[] args) { // Let RuntimeExceptions go out of the method: throwRuntimeException(2).java /****************** Exercise 30 ****************** * Modify main() in Human.atBat(). 4th Edition Annotated Solution Guide . default: return.interface Storm2 { void event().java is used to * handle the different types of exceptions. public class E30_Human { static void throwRuntimeException(int type) { try { switch(type) { case 0: throw new Annoyance(). i < 2. i++) 250 Thinking in Java. getCause().println("Caught Sneeze").println("Caught Annoyance").try { throwRuntimeException(i). } catch(RuntimeException re) { try { throw re. } } } } /* Output: Caught Annoyance Caught Sneeze *///:~ Error Handling with Exceptions 251 .println(t). } catch(Throwable t) { System.out. } catch(Annoyance e) { System.out. } catch(Sneeze e) { System.out. . invokevirtual ldc #11. //StringBuilder. #8.append:() #14.append:() 253 .append:() //StringBuilder. //StringBuilder. #8. #9.append:() #16. invokevirtual ldc #18."<init>":() //StringBuilder.Strings Exercise 1 Invoke the javap tool (with javap –c SprinklerSystem) to generate this simplified output for the toString( ) method of the SprinklerSystem class: 0: 3: 4: 7: 9: 12: 13: 16: 19: 21: 24: 26: 29: 30: 33: 36: 38: 41: 43: 46: 47: 50: 53: 55: 58: 60: 63: 64: 67: 70: 72: 75: 77: 80: 81: new dup invokespecial ldc #7. invokevirtual aload_0 getfield invokevirtual ldc #17. #19. //StringBuilder.append:() //StringBuilder. invokevirtual aload_0 getfield invokevirtual ldc #10. invokevirtual aload_0 getfield invokevirtual ldc #10. #8. invokevirtual aload_0 getfield #5.append:() //StringBuilder.append:() #8.append:() //StringBuilder. #8. #8. #8. #8. #8.append:() #12. invokevirtual ldc #13.append:() #8. //StringBuilder.append:() //StringBuilder. #8. invokevirtual ldc #15.append:() #8. //StringBuilder. #8. //class StringBuilder //StringBuilder.append:() //StringBuilder. invokevirtual aload_0 getfield invokevirtual ldc #10. //StringBuilder. #6. invokevirtual aload_0 getfield invokevirtual ldc #10. #25.append:() #4. invokevirtual ldc #24.append:() #8. //StringBuilder.toString() + "\n".append:() #8. ************************************************/ package strings. Exercise 2 //: strings/E02_RepairInfinite. no looping is involved). so the compiler generates reasonably optimized code.java. } 254 Thinking in Java. invokevirtual aload_0 getfield invokevirtual invokevirtual areturn #20. public class E02_RepairInfinite { public String toString() { return " E02_RepairInfinite address: " + super. 4th Edition Annotated Solution Guide .append( ). i < 10.84: 87: 89: 92: 94: 97: 98: 101: 104: 106: 109: 111: 114: 115: 118: 121: 124: invokevirtual ldc #10.append:() //StringBuilder. #8. //StringBuilder. //StringBuilder. Note that you create only one instance of StringBuilder.util. each overloaded version has a unique index. System.append:() #26.toString:() The operations are simple (e. for(int i = 0. #23.append:() //StringBuilder.out.println(v). //StringBuilder. import java.add(new E02_RepairInfinite()). i++) v. } public static void main(String[] args) { List<E02_RepairInfinite> v = new ArrayList<E02_RepairInfinite>(). Though different indexes refer to StringBuilder. //StringBuilder. invokevirtual ldc #21. #8.append:() #22.java /****************** Exercise 2 ****************** * Repair InfiniteRecursion..g.*. //StringBuilder. private final Formatter f.move(3.name = name.move(2. name. this. Strings 255 . terry.*. tommy.java /****************** Exercise 3 ****************** * Modify Turtle. y). ***********************************************/ package strings. E02_RepairInfinite address: E02_RepairInfinite@addbf1 . tommy. import java.java so that it sends all output * to System. tommy.err).0). E02_RepairInfinite address: E02_RepairInfinite@9304b1 . Turtle tommy = new Turtle("Tommy". class Turtle { private final String name.move(0. } } public class E03_TurtleRedirected { public static void main(String[] args) { Formatter f = new Formatter(System.} /* Output: (70% match) [ E02_RepairInfinite address: E02_RepairInfinite@3e25a5 .3). } public void move(int x.f).f = f. E02_RepairInfinite address: E02_RepairInfinite@19821f . E02_RepairInfinite address: E02_RepairInfinite@a90653 .err.format("%s The Turtle is at (%d. terry = new Turtle("Terry". E02_RepairInfinite address: E02_RepairInfinite@1fb8ee3 ] *///:~ Exercise 3 //: strings/E03_TurtleRedirected. E02_RepairInfinite address: E02_RepairInfinite@de6ced .util.move(3. terry. E02_RepairInfinite address: E02_RepairInfinite@42e816 .move(4. Formatter f) { this. E02_RepairInfinite address: E02_RepairInfinite@190d11 .5).4).8).f).%d)\n". E02_RepairInfinite address: E02_RepairInfinite@c17164 . x. public Turtle(String name. int y) { f. Locale. import java.format(TITLE_FRMT. private static final String ITEM_FRMT = "%-" + ITEM_WIDTH + ". "----".move(3. class Receipt { public static final int ITEM_WIDTH = 15.4) (2.format(TITLE_FRMT. name. f. private double total = 0. public void printTitle() { f.2f\n". public static final int QTY_WIDTH = 5. double price) { f. price). * The goal is to allow you to easily change a * width by changing a single value in one place. ************************************************/ package strings. "Item"." + ITEM_WIDTH + "s %" + QTY_WIDTH + "d %" + PRICE_WIDTH + ". private static final String TITLE_FRMT = "%-" + ITEM_WIDTH + "s %" + QTY_WIDTH + "s %" + PRICE_WIDTH + "s\n".3) Exercise 4 //: strings/E04_CustomizableReceipt.5) (3. Formatter f = new Formatter(System.2f\n". "-----").util.out.US). "Qty". "Price").java so that the widths are all * controlled by a single set of constant values.java /****************** Exercise 4 ****************** * Modify Receipt.format(ITEM_FRMT. 4th Edition Annotated Solution Guide .terry.0) (4.8) (3. } public void print(String name. total += price. } } /* Output: Tommy The Turtle is at Terry The Turtle is at Tommy The Turtle is at Terry The Turtle is at Tommy The Turtle is at Terry The Turtle is at *///:~ (0.3) (3.3). private static final String TOTAL_FRMT = "%-" + ITEM_WIDTH + "s %" + QTY_WIDTH + "s %" + PRICE_WIDTH + ". int qty. "---". } 256 Thinking in Java. qty. public static final int PRICE_WIDTH = 10.*. 06). public class E05_ComplexConversion { public static void main(String[] args) { Formatter f = new Formatter(System. use all the * possible format specifiers available for that * conversion type.mindview.java /****************** Exercise 5 ****************** * For each of the basic conversion types in the * above table. Strings 257 . 4. import java. 3.math.format(TOTAL_FRMT. "".printTotal().29). 1. write the most complex formatting * expression possible.format(TITLE_FRMT.*.US).print("Princess Peas". "-----").29 Tax 1.out.*. } } /* Output: Item Qty Price ---------Jack's Magic Be 4 4. "Total".print("Three Bears Porridge". total * 1.*.25). "". 14.util. "Tax".format(TOTAL_FRMT.util. Locale.1). receipt. receipt. ************************************************/ package strings. receipt. That is.printTitle(). } } public class E04_CustomizableReceipt { public static void main(String[] args) { Receipt receipt = new Receipt(). import static net.06).Print. total*0. receipt. "".06 *///:~ Exercise 5 //: strings/E05_ComplexConversion.25 Princess Peas 3 5.public void printTotal() { f. f. "".print("Jack's Magic Beans".10 Three Bears Por 1 14. receipt. import java.42 ----Total 25. f. 4. 5. f. int v = 1000.format("c: %1$-10c\n".10s\n". f.format("h: %1$-10. f.format("b: %1$-10.format("d 1: %1$(.10s\n".format("e 2: %1$#(. v).2f\n".2f\n". x = -x: %1$#(. x). w). 258 Thinking in Java. x). y).format("b: %1$-10. f. z).format("f 3. x).10b\n".10b\n". -x).0+10d\n".10b\n". f.10s\n". v). x).format("s: %1$#-10. f.format("e 3. f. f. 10d\n".format("d 3. w = -w: %1$-( 10x\n".format("x 2: %1$-( 10x\n". f.format("s: %1$#-10. w). f. 10d\n".format("c. v).10b\n".10h\n". f. w). u). f.format("f 1: %1$#(.format("h: %1$-10.2e\n". f. print("z = false").format("x: %1$-#10x\n".10h\n". w. w).543.543"). v).2f\n".10h\n". print("x = 179. f.format("b: %1$-10.2e\n".format("h: %1$-10. f.char u = 'a'.10b\n". 4th Edition Annotated Solution Guide .format("d 1: %1$(. 10d\n". -v).10b\n".0+10.format("x 3. print("w = new BigInteger(\"50000000000000\")"). print("u = 'a'"). f. v).format("h: %1$-10.format("x 1: %1$(0+10x\n".2e\n". f.format("s: %1$-#10s\n".0+10d\n". f.format("d 3.10h\n".format("d 2: %1$-(. -x). f. v). w = -w: %1$-(. f.format("s: %1$#-10. f.format("b: %1$-10. boolean z = false. double x = 179. Object y = new Object().format("f 2: %1$#(. f. f. f. v = -v: %1$-(. f.format("s: %1$#-10.format("e 1: %1$#(0+10. y). u). x). u). y).. f.format("b: %1$-10.negate()). u). f. 121).10. f. f. x). x).negate()). f.10s\n". f. w. w).10. print("y = new Object()"). x = -x: %1$#(0+10. BigInteger w = new BigInteger("50000000000000"). f. f. z). v = 121: %1$-10c\n".0+10. f.10h\n". w).format("d 2: %1$-(. print("v = 1000").10s\n". f. 10d\n". w).format("b: %1$-10.format("h: %1$-10.format("s: %1$#-10. } } /* Output: (95% match) u = 'a' s: a c: a b: true h: 61 v = 1000 d 1: +00001.000.000.000.000.format("%%: %-10%").000.000 d 2: 50. w = -w: (2d79883d2000) h: 8842a1a7 x = 179. x = -x: (1. w = -w: (50. z). v = -v: (1. // A special no argument conversion type f.000.54 f 2: 179.format("h: %1$-10. v = 121: y b: true s: 1000 x: 0x3e8 h: 3e8 w = new BigInteger("50000000000000") d 1: +50. x = -x: (00179. h: 3e25a5 z = false b: false s: false h: 4d5 Strings 259 .10h\n".000 d 3.000.000 d 3.543 b: true s: 179.54) e 1: +01.000.80e+02 e 2: 1.000.f.000 d 2: 1.543 f 1: +000179.000) c.lang.80e+02 e 3.000) b: true s: 5000000000 x 1: +2d79883d2000 x 2: 2d79883d2000 x 3.80e+02) h: 1ef462c y = new Object() b: true s: java.54 f 3. long. result. public String toString() { StringBuilder result = new StringBuilder("DataHolder: \n"). doubleField)).’ flag for the ‘e’ because it’s inapplicable as a conversion type (though the J2SE5 API documentation mistakenly says otherwise).String.0. ************************************************/ package strings. Create a toString() method * for this class that uses String. and * demonstrate that your class works correctly.lang. as well.append( format(Locale. For example.*. We omitted incompatible cases where a conversion type is not appropriate for a particular variable type.append( format(Locale. Note that not all combinations of format specifiers are possible. long longField = 2L. result. intField)). float * and double fields. result. float floatField = 3. } } 260 Thinking in Java. double doubleField = 4.util. import java. Exercise 6 //: strings/E06_ClassDump. import static java.*. floatField)).US. "floatField: %f\n". 4th Edition Annotated Solution Guide . We omitted the ‘. result.US. return result.US.%: % *///:~ The above program includes format specifiers not mentioned in TIJ4 (the JDK documentation elaborates on all of them).format(). "intField: %d\n". class DataHolder { int intField = 1.java /****************** Exercise 6 ****************** * Create a class that contains int. giving both the ‘-’ and ‘0’ flags throws an IllegalFormatFlagsException. The format specifiers applicable to a particular conversion type depends on the variable type. longField)).append( format(Locale. "longField: %d\n".US.0f. "doubleField: %e\n".toString().append( format(Locale. In the rest of the text.out. English.")). System.S. This exercise highlights the difficulty of writing multilingual applications. } } /* Output: DataHolder: intField: 1 longField: 2 floatField: 3.out. System.java /****************** Exercise 7 ****************** * Using the documentation for java.000000 doubleField: 4.out. public class E07_SentenceChecker { public static boolean matches(String text) { return text.out. Strings 261 ..regex. System.")). write and test a regular expression * that checks a sentence to see that it begins with a * capital letter and ends with a period."). we use only U.public class E06_ClassDump { public static void main(String[] args) { System.println(matches("bad sentence 1.*\\. } public static void main(String[] args) { System. } } /* Output: true false false true *///:~ It’s easy to test for a capital letter in a regular expression.. but not in all languages.util.out.Pattern * as a resource.000000e+00 *///:~ Exercise 7 //: strings/E07_SentenceChecker.println(matches("This is correct.println(new DataHolder()).")).println(matches("This is also correct.println(matches("Bad sentence 2")).matches("\\p{javaUpperCase}. ************************************************/ package strings. public class E09_Replacing2 { public static void main(String[] args) { System. with. 4th Edition Annotated Solution Guide .out.java /****************** Exercise 9 ****************** * Using the documentation for java. public class E08_Splitting2 { public static void split(String regex) { System. } } /* Output: Th_n. _ h_rr_ng! *///:~ 262 Thinking in Java.split(regex))).util. have found .knights.. shrubbery."_")).regex. replace all the vowels in * Splitting.. a herring!] *///:~ Exercise 9 //: strings/E09_Replacing2. } public static void main(String[] args) { split("the|you")..Exercise 8 //: strings/E08_Splitting2.*.. must cut down .util. w_th. when . wh_n y__ h_v_ f__nd th_ shr_bb_ry.knights. mightiest tree in .knights on the words * "the" or "you.knights with underscores.replaceAll( "(?i)[aeiou]".Pattern * as a resource. forest.java /****************** Exercise 8 ****************** * Split the string Splitting...out.. . ************************************************/ package strings.println( Arrays." ************************************************/ package strings. import java.toString(Splitting. y__ m_st c_t d_wn th_ m_ght__st tr__ _n th_ f_r_st.. } } /* Output: [Then.println(Splitting. out. public class E10_CheckForMatch { public static void main(String[] args) { String source = "Java now has regular expressions".matcher(source). * s{0.*. System. Matcher m = p.". "s{4}". Exercise 10 //: strings/E10_CheckForMatch.println("Match \"" + m. Pattern p = Pattern.end() .3}"}.group() + "\" at positions " + m.* * n.out.java /********************** Exercise 10 ********************** * For the phrase "Java now has regular expressions" * evaluate whether the following expressions will find a * match: * * ^Java * \Breg.println( "Regular expression: \"" + pattern + "\"").*".out. "\\Breg. } } } } /* Output: Strings 263 .1)). "s*".regex. "s{1}. (though none occur above). while(m. "s?". String[] regEx = {"^Java". "s{0.3} ********************************************************/ package strings. import java. The regular expression demonstrates how to handle mixed uppercase/lowercase vowels. for(String pattern : regEx) { System. "s+".The embedded flag expression (?i) enables case-insensitive matching.compile(pattern).w\\s+h(a|i)s".find()) { System.w\s+h(a|i)s * s? * s* * s+ * s{4} * s{1}.println("Source string: " + source).start() + "-" + (m.util. "n. Source string: Java now has regular expressions Regular expression: "^Java" Match "Java" at positions 0-3 Regular expression: "\Breg. 4th Edition Annotated Solution Guide .w\s+h(a|i)s" Match "now has" at positions 5-11 Regular expression: "s?" Match "" at positions 0--1 Match "" at positions 1-0 Match "" at positions 2-1 Match "" at positions 3-2 Match "" at positions 4-3 Match "" at positions 5-4 Match "" at positions 6-5 Match "" at positions 7-6 Match "" at positions 8-7 Match "" at positions 9-8 Match "" at positions 10-9 Match "s" at positions 11-11 Match "" at positions 12-11 Match "" at positions 13-12 Match "" at positions 14-13 Match "" at positions 15-14 Match "" at positions 16-15 Match "" at positions 17-16 Match "" at positions 18-17 Match "" at positions 19-18 Match "" at positions 20-19 Match "" at positions 21-20 Match "" at positions 22-21 Match "" at positions 23-22 Match "" at positions 24-23 Match "" at positions 25-24 Match "s" at positions 26-26 Match "s" at positions 27-27 Match "" at positions 28-27 Match "" at positions 29-28 Match "" at positions 30-29 Match "s" at positions 31-31 Match "" at positions 32-31 Regular expression: "s*" Match "" at positions 0--1 Match "" at positions 1-0 Match "" at positions 2-1 Match "" at positions 3-2 Match "" at positions 4-3 Match "" at positions 5-4 264 Thinking in Java.*" Regular expression: "n. 3}" Match "" at positions 0--1 Match "" at positions 1-0 Match "" at positions 2-1 Match "" at positions 3-2 Match "" at positions 4-3 Match "" at positions 5-4 Match "" at positions 6-5 Match "" at positions 7-6 Match "" at positions 8-7 Match "" at positions 9-8 Match "" at positions 10-9 Match "s" at positions 11-11 Strings 265 ." Match "s " at positions 11-12 Match "ss" at positions 26-27 Regular expression: "s{0.Match "" at positions 6-5 Match "" at positions 7-6 Match "" at positions 8-7 Match "" at positions 9-8 Match "" at positions 10-9 Match "s" at positions 11-11 Match "" at positions 12-11 Match "" at positions 13-12 Match "" at positions 14-13 Match "" at positions 15-14 Match "" at positions 16-15 Match "" at positions 17-16 Match "" at positions 18-17 Match "" at positions 19-18 Match "" at positions 20-19 Match "" at positions 21-20 Match "" at positions 22-21 Match "" at positions 23-22 Match "" at positions 24-23 Match "" at positions 25-24 Match "ss" at positions 26-27 Match "" at positions 28-27 Match "" at positions 29-28 Match "" at positions 30-29 Match "s" at positions 31-31 Match "" at positions 32-31 Regular expression: "s+" Match "s" at positions 11-11 Match "ss" at positions 26-27 Match "s" at positions 31-31 Regular expression: "s{4}" Regular expression: "s{1}. Match "" at positions 12-11 Match "" at positions 13-12 Match "" at positions 14-13 Match "" at positions 15-14 Match "" at positions 16-15 Match "" at positions 17-16 Match "" at positions 18-17 Match "" at positions 19-18 Match "" at positions 20-19 Match "" at positions 21-20 Match "" at positions 22-21 Match "" at positions 23-22 Match "" at positions 24-23 Match "" at positions 25-24 Match "ss" at positions 26-27 Match "" at positions 28-27 Match "" at positions 29-28 Match "" at positions 30-29 Match "s" at positions 31-31 Match "" at positions 32-31 *///:~ Exercise 11 //: strings/E11_CheckForMatch2.matcher("Arline ate eight apples and " + "one orange while Anita hadn't any").java /************************** Exercise 11 ****************** *Apply the regular expression * (?i)((^[aeiou])|(\s+[aeiou]))\w+?[aeiou]\b * to * "Arline ate eight apples and one orange while Anita * hadn't any" *********************************************************/ package strings.group() + "\" at positions " + m. public class E11_CheckForMatch2 { public static void main(String[] args) { Pattern p = Pattern.compile( "(?i)((^[aeiou])|(\\s+[aeiou]))\\w+?[aeiou]\\b"). } 266 Thinking in Java.1)).start() + "-" + (m. import java.end() .regex. while(m. Matcher m = p. 4th Edition Annotated Solution Guide .util.*.find()) { System.out.println("Match \"" + m. brillig.*.java /************************** Exercise 12 ****************** * Modify Groups.java to count all unique words * with no initial capital letter. print(words. gyre. shun.add(m. bite. toves] *///:~ The regular expression represented by \b\w+\b allows you to perform a “whole words” search (in Java it becomes \\b\\w+\\b). were.mindview. import java. borogoves.compile("\\b((?![A-Z])\\w+)\\b") . *********************************************************/ package strings. import static net. that. the. As an additional exercise. outgrabe. Strings 267 . in. jaws.group(1)). mome. } } /* Output: Number of unique words = 25 [catch. and. gimble. wabe. bird. The program tests for initial capitals with a negative lookahead. print("Number of unique words = " + words.POEM). slithy. raths.*. son.util.util. frumious.regex.toString()).find()) words.} } /* Output: Match "Arline" at positions 0-5 Match " ate" at positions 6-9 Match " one" at positions 27-30 Match " orange" at positions 31-37 Match " Anita" at positions 44-49 *///:~ Exercise 12 //: strings/E12_Groups2.util. public class E12_Groups2 { public static void main(String[] args) { Set<String> words = new HashSet<String>(). claws.matcher(Groups. Matcher m = Pattern. mimsy. while(m.size()).Print. import java.*. rewrite this expression with no lookaround. A lookaround (the common name for lookahead and lookbehind) is sometimes indispensable. my. and often makes the whole regular expression seem more intuitive. *.matcher(s).start() + " end = " + m. * lookingAt() and matches().lookingAt()) // No reset() necessary d.end()). } print(message). } 268 Thinking in Java. import static net.display("find() '" + m.Exercise 13 //: strings/E13_StartEnd2.java /************************** Exercise 13 ****************** * Modify StartEnd.POEM as * input. 4th Edition Annotated Solution Guide . "T. *********************************************************/ package strings.mindview. Display(String regex) { this.start() + " end = " + m.end()). Pattern p = Pattern. private String regex. if(m.Print.find()) d.start() + " end = " + m.compile(regex).util.split("\n")) { print("input : " + in). "\\w*at".display("lookingAt() start = " + m. } } static void examine(String s.end()). import java.util. if(m. public class E13_StartEnd2 { private static class Display { private boolean regexPrinted = false. regex).java so that it uses Groups.POEM.matches()) // No reset() necessary d.*?.*.regex. but still produces positive outputs for find(). for(String regex : new String[] {"\\w*ere\\w*". String regex) { Display d = new Display(regex). regexPrinted = true.group() + "' start = "+ m. "t\\w+"."}) examine(in.display("matches() start = " + m. while(m. Matcher m = p. } public static void main(String[] args) { for(String in : Groups.regex = regex. } void display(String message) { if(!regexPrinted) { print(regex). t\w+ find() 'the' start = 23 end = 26 input : All mimsy were the borogoves. and the slithy toves t\w+ find() 'the' start = 18 end = 21 find() 'thy' start = 25 end = 28 find() 'toves' start = 29 end = 34 T. t\w+ find() 'the' start = 7 end = 10 input : The jaws that bite. \w*ere\w* find() 'were' start = 10 end = 14 t\w+ find() 'the' start = 15 end = 18 input : And the mome raths outgrabe. find() 'Tw' start = 0 end = 2 lookingAt() start = 0 end = 2 matches() start = 0 end = 34 input : Did gyre and gimble in the wabe. the claws that catch. \w*at find() 'rat' start = 13 end = 16 t\w+ find() 'the' start = 4 end = 7 find() 'ths' start = 15 end = 18 find() 'tgrabe' start = 21 end = 27 input : input : Beware the Jabberwock.} } /* Output: input : Twas brillig. \w*at find() 'that' start = 9 end = 13 find() 'that' start = 30 end = 34 find() 'cat' start = 35 end = 38 t\w+ find() 'that' start = 9 end = 13 find() 'te' start = 16 end = 18 find() 'the' start = 20 end = 23 find() 'that' start = 30 end = 34 find() 'tch' start = 37 end = 40 T. my son.*?.*?. find() 'Th' start = 0 end = 2 lookingAt() start = 0 end = 2 matches() start = 0 end = 41 input : Beware the Jubjub bird. and shun t\w+ Strings 269 . unusual use..*?. 4th Edition Annotated Solution Guide . of exclamation!!points] *///:~ Exercise 15 //: strings/E15_JGrep2.toString(input. of exclamation.g.util. \w*at find() 'Bandersnat' start = 13 end = 23 t\w+ find() 'tch' start = 22 end = 25 T. import java.java /************************** Exercise 14 ****************** * Rewrite SplitDemo using String. public class E14_SplitDemo2 { public static void main(String[] args) { String input = "This!!unusual use!!of exclamation!!points". print(Arrays.*.MULTILINE).regex. import java.split().java "\b[Ssct]\w+" CASE_INSENSITIVE} /*********************** Exercise 15 ********************* * Modify JGrep.split("!!". * Pattern. 3))).java // {Args: E15_JGrep2. *********************************************************/ package strings.util. unusual use.find() 'the' start = 7 end = 10 input : The frumious Bandersnatch.java to accept flags as arguments (e. Pattern. import static net.util.mindview. 270 Thinking in Java. } } /* Output: [This.toString(input.split("!!"))). points] [This.CASE_INSENSITIVE.*.Print. find() 'Th' start = 0 end = 2 lookingAt() start = 0 end = 2 matches() start = 0 end = 26 *///:~ Exercise 14 //: strings/E14_SplitDemo2. *********************************************************/ package strings.*. // Only do the first three: print(Arrays. } } } /* Output: (Sample) 0: strings: 4 1: Ssct: 30 2: CASE_INSENSITIVE: 40 3: to: 21 4: CASE_INSENSITIVE: 11 5: static: 7 Strings 271 .find()) print(index++ + ": " + m.equalsIgnoreCase("MULTILINE")) { flag = Pattern. print("CANON_EQ.equalsIgnoreCase("UNICODE_CASE")) { flag = Pattern. for(String line : new TextFile(args[0])) { m.equalsIgnoreCase("CANON_EQ")) { flag = Pattern.exit(0). } else if(args[2]. print("pattern can take one of the following values"). CASE_INSENSITIVE. " + "DOTALL. } else if(args[2]. } else if(args[2].equalsIgnoreCase("CASE_INSENSITIVE")) { flag = Pattern. flag). // Iterate through the lines of the input file: int index = 0.*. } else if(args[2]. } int flag = 0. if(args[2].compile(args[1].COMMENTS. } else if(args[2].UNIX_LINES. COMMENTS.Print.DOTALL. } else if(args[2].mindview.equalsIgnoreCase("COMMENTS")) { flag = Pattern. while(m.mindview.equalsIgnoreCase("DOTALL")) { flag = Pattern.util. UNICODE_CASE.util. public class E15_JGrep2 { public static void main(String[] args) throws Exception { if(args. } Pattern p = Pattern.start()).MULTILINE. MULTILINE.reset(line).matcher("").CASE_INSENSITIVE.CANON_EQ.*.group() + ": " + m.UNICODE_CASE. import static net. UNIX_LINES").equalsIgnoreCase("UNIX_LINES")) { flag = Pattern.length < 3) { print("Usage: java E15_JGrep2 file regex pattern"). System.import net. Matcher m = p. ********************************************************/ package strings.io.*. * Hint: you can generate a list of filenames with * File[] files = new File(".java java} /********************** Exercise 16 ********************* * Modify JGrep.java // {Args: E16_JGrep3.listFiles().println( 272 Thinking in Java. search * should include all files in the directory). import net.regex.6: class: 7 7: static: 9 8: String: 26 9: throws: 41 10: can: 21 11: take: 25 12: the: 37 13: CANON_EQ: 13 14: CASE_INSENSITIVE: 23 15: COMMENTS: 41 16: System: 6 17: CANON_EQ: 33 18: CANON_EQ: 21 19: CASE_INSENSITIVE: 35 20: CASE_INSENSITIVE: 21 21: COMMENTS: 40 22: COMMENTS: 21 23: compile: 24 24: through: 15 25: the: 23 26: the: 36 27: String: 8 28: TextFile: 26 29: start: 12 *///:~ Exercise 16 //: strings/E16_JGrep3.length < 2) { System.*.out. import java.").util. import java. 4th Edition Annotated Solution Guide .java to accept a directory name or a file * name as argument (if a directory is provided. public class E16_JGrep3 { public static void main(String[] args) throws Exception { if(args.util.mindview.*. // Iterate through the lines of the input file: int index = 0. Pattern p = Pattern.reset(line). File[] files = null.matcher("").g. for(String line : new TextFile(f. you just follow the program template. However.println("-. to display comments).exit(0). Matcher m = p.println(index++ + ": " + m. as most of the work is already done."Usage: java E16_JGrep3 file regex"). "// this is part of a string and not a comment"). } } } } /* Output: (Sample) -.out.getAbsolutePath())) { m. for(File f : files) { System.java-0: java: 23 1: java: 21 2: java: 26 3: java: 16 4: java: 7 5: java: 7 6: java: 16 *///:~ Exercise 17 This exercise looks like the previous ones.start()). if(file.compile(args[1]). We can either write a parser or enhance an existing general parser (e. } File file = new File(args[0]).group() + ": " + m. while(m.getName() + "--"). else files = new File[]{new File(args[0])}.File:E16_JGrep3. System.. We can reuse a stable base code to great advantage.File:" + f. it’s not that simple. Strings 273 .out. We need state information to distinguish between a real comment and text that looks like a comment (for example.isDirectory()) files = file. The program needs more than a search facility based in regular expression to locate itself inside the source code: it needs a miniature Java source code parser.find()) System. applying the right regular expression to extract comments from a Java source file.listFiles(). java // {Args: E17_JCommentExtractor. } } /* Output: (Sample) : strings/E17_JCommentExtractor.util. public static void main(String[] args) throws Exception { if(args.) Just provide the same input for both and check the differences in the outputs. while(m. import java.*?)$ # OR Match C++ style comments\n".regex. Multiline & Dotall: ON\n" + "/\\* # Match START OF THE COMMENT\n" + "(.out. 274 Thinking in Java.read(args[0]).length < 1) { System. public class E17_JCommentExtractor { static final String CMNT_EXT_REGEX = "(?x)(?m)(?s) # Comments.*?)$ # OR Match C++ style comments\n".java} /********************** Exercise 17 ********************* * Write a program that reads a Java source-code file (you * provide the file name on the command line) and displays * all the comments.Compare the alternative methods in the next three solutions.group(1) : m.println( "Usage: java E17_JCommentExtractor file").*.*?) # Match all chars\n" + "\\*/ # Match END OF THE COMMENT\n" + "| //(. import net.find()) System.mindview.matcher(src).java} ********************* Exercise 17 ********************* * Write a program that reads a Java source-code file (you * provide the file name on the command line) and displays * all the comments.compile(CMNT_EXT_REGEX). } String src = TextFile. (The second one amounts to a miniature tutorial for JavaCC. System.*.out. ********************************************************/ package strings.util.group(2)).exit(0).println(m. 4th Edition Annotated Solution Guide . Pattern p = Pattern.group(1) != null ? m.java {Args: E17_JCommentExtractor. ******************************************************* (. Alternative A //: strings/E17_JCommentExtractor. Matcher m = p. Assume JavaCC is installed and that we can build the example in the examples\JavaGrammars\1. accepts two command line arguments. 2. 4. An invalid invocation causes usage instructions to appear onscreen.jj folder (this may not be necessary for later releases of JavaCC).dev.java.*?)$ # OR Match C++ style comments\n".5. and embedded comments starting with # are ignored until the end of a line. A parser generator converts a grammar specification to a Java program that recognizes the grammar.io. Java1. Open CodeSniffer. 3. so we look at only those changes we must make to an existing J2SE5 grammar file (Java1. At this level the parser signals only whether the input is a legal Java program.jj.jj grammar file and the Token.5. The program. We can cover only part of the topic of parser generators here.jj contains everything to produce a fully functional J2SE5 parser. The (?x) flag enables the so-called COMMENTS mode. it erroneously reports as a comment part of the regular expression pattern clearly situated inside a Java string (like (.java into the CodeSniffer. Alternative B JavaCC is a parser generator for Java applications (free at https://javacc. Insert the following code fragment right after the line import java. Copy Token.*: Strings 275 . we present the solution for all three at once. the program is flawed. Follow these steps to create CodeSniffer. and “N” for Display Class Names).5 directory (see its README file for instructions). Java Code Sniffer.jj in a text editor. white space is ignored. the input file and the option to indicate a processing choice (“C” for Display Comments.).jj: 1./:~ *///:~ We must remove the comment markers to compile the new source code (created by the automated test system). However.net/). Copy Java1.jj).java file (from the examples\JavaGrammars\1. but as that solves 99% of exercises 17. 18 and 19.5. “S” for Display String Literals.jj into a local folder and rename it CodeSniffer.5 directory). We build the program with the CodeSniffer. equalsIgnoreCase("c")) { mode = OpMode. print("\nJava Code Sniffer Version 1.\n"). } else if (args[1].. } else if (args[1]. return. } if (args[1].. .\n"). public static void main(String args[]) { JavaParser parser."). } try { parser.0: Encountered " + 276 Thinking in Java.io. .equalsIgnoreCase("n")) { mode = OpMode.equalsIgnoreCase("s")) { mode = OpMode. 4th Edition Annotated Solution Guide .util..getMessage()). try { parser = new JavaParser( new java.*. DisplayClassNames } 5. } catch (java.").. print("DISPLAYING CLASS NAMES.0: Reading from file " + args[0] + " .0: File " + args[0] + " not found.FileNotFoundException e) { print("Java Code Sniffer Version 1.DisplayComments.FileInputStream(args[0])). } } else { usage(). print("DISPLAYING COMMENTS. Replace the main( ) method with the following code: static OpMode mode. return.import static net.mindview. } catch (ParseException e) { print(e. } else { usage()."). if (args. DisplayStrings. enum OpMode { DisplayComments. print("\nJava Code Sniffer Version 1..Print.length == 2) { print( "Java Code Sniffer Version 1. print("DISPLAYING STRING LITERALS. return.\n").DisplayClassNames.0: Java program" + " parsed successfully.io..DisplayStrings.CompilationUnit(). "). print(" N .mode == OpMode. you must still know regular expressions in order to understand the grammar.Display Class Names")."errors during parse. } Save your changes and everything should compile. print(" java JavaParser file <option>").jj javac *.java java JavaParser JavaParser.DisplayComments) printnb(image. (Assume the grammar file is in the current working directory and that CLASSPATH is properly set): • • • javacc CodeSniffer. print(" C .mode == OpMode. The parser now recognizes command line arguments and prints usage information. } The modified grammar should look like this: <IN_SINGLE_LINE_COMMENT> SPECIAL_TOKEN : { <SINGLE_LINE_COMMENT: "\n" | "\r" | "\r\n" > { if (JavaParser. and it tends to be less of a struggle than finding the right regular expression to extract comments from the source code (However. To solve the exercise: 1.0: Usage is:"). It’s surprisingly easy to build a parser with JavaCC because you’re reusing code. } : DEFAULT } Strings 277 . Insert the following code fragment immediately after <SINGLE_LINE_COMMENT: "\n" | "\r" | "\r\n" >: { if (JavaParser.toString()).java C We add only three lines of code to display comments.toString()).Display Comments").Display String Literals"). Now build the parser using the steps below. print(" S . } } private static void usage() { print("Java Code Sniffer Version 1.DisplayComments) printnb(image. print("where option is one of:"). Boilerplate code implements the corresponding actions. because the parser uses them to process input text). out.2. Exercise 18 Alternative A //: strings/E18_JStringExtractor. Insert the next code fragment right after both the <FORMAL_COMMENT: "*/" > and <MULTI_LINE_COMMENT: "*/" > constructions: { if (JavaParser.exit(0).java // {Args: E18_JStringExtractor.println( "Usage: java E18_JStringExtractor file"). } String src = TextFile.*.toString()).toString( ) to a custom routine allows us to pre-process extracted comments before sending them to standard output.util. substring(1.length() .mindview.length < 1) { System.mode == OpMode.java} /********************** Exercise 18 ********************* * Write a program that reads a Java source-code file * and displays all the string literals in the code * (provide the file name on the command line).matcher(src). import java. } Passing image. Try removing the comment markers as an additional exercise. public class E18_JStringExtractor { static final String STR_EXT_REGEX = "\"(?:[^\"\\\\\\n\\r]|(?:\\\\(?:[untbrf\\\\'\"]" + "|[0-9A-Fa-f]{4}|[0-7]{1.out.DisplayComments) print(image. Pattern p = Pattern.find()) System.read(args[0]). while(m.compile(STR_EXT_REGEX).println(m. 4th Edition Annotated Solution Guide . import net.regex. ********************************************************/ package strings.util.group().1)).group(). // "This is NOT a string but a comment!" String dummy = "\u003F\u003f\n\060\607". System.*. m. public static void main(String[] args) throws Exception { if(args.2}|[0-3][0-7]{2})))*\"". 278 Thinking in Java. Matcher m = p. "\\"."r". Display string literals by inserting the following code fragment: { if (JavaParser. Passing image.2}|[0-3][0-7]{2})))*\" Usage: java E18_JStringExtractor file This is NOT a string but a comment! \u003F\u003f\n\060\607 *///:~ The program incorrectly treats “string literals” inside comments as real strings. so you see processed characters rather than raw \uXXXX forms in the output."t".DisplayStrings) print(image."b"."\r"]) | ("\\" ( ["n". Try commenting the regular expression as we did in the previous solution."f". Alternative B This solution uses JavaCC following Exercise 17. Alternative B. Strings 279 ."\\".} } /* Output: \"(?:[^\"\\\\\\n\\r]|(?:\\\\(?:[untbrf\\\\'\"] |[0-9A-Fa-f]{4}|[0-7]{1. We pruned away string literal markers to avoid confusing TIJ4’s automated test system."\n"."\""] | ["0"-"7"] ( ["0"-"7"] )? | ["0"-"3"] ["0"-"7"] ["0"-"7"] ) ) )* "\"" > JavaCC runs java-like Unicode escape processing automatically.toString( ) to a custom routine allows us to further process extracted string literals before they go to standard output."'". Try to remove the string literal markers as an additional exercise.toString()). } immediately after the construction: < STRING_LITERAL: "\"" ( (~["\"".mode == OpMode. .util.Exercise 19 This exercise extends Exercise 18. static final String CU_REP_REGEX = "class\\s+" + Identifier + "|extends\\s+" + ClassOrInterfaceType + "|new\\s+" + ClassOrInterfaceType.*. Alternative A //: strings/E19_JClassUsageReporter. public static void main(String[] args) throws Exception { if(args. } String src = TextFile.util. private static final String ClassOrInterfaceType = Identifier + "(?:\\. 4th Edition Annotated Solution Guide . // Prune away comments .regex.exit(0).out.java} /********************** Exercise 19 ********************* * Build on Exercises 17 and 18 to write a program * that examines Java source code and produces all * class names used in a particular program.java // {Args: E19_JClassUsageReporter. import java.length < 1) { System.. 280 Thinking in Java. import net." + Identifier + ")*".read(args[0]).println( "Usage: java E19_JClassUsageReporter file").mindview. public class E19_JClassUsageReporter { private static final String Identifier = "[$A-Za-z_][$A-Za-z_0-9]*". System. we must unambiguously define “class names used in a particular program. ********************************************************/ package strings. First.” The program will dump type names that occur in the following contexts: • • Class declaration (including class names on the extends list) Object creation The program won’t report used class names because we can’t tell whether a particular reference type is an interface or a class without further examination (probably involving other external files).*. "").out. // Prune away string literals . ""). Matcher m = p.find()) System. src = src. Locate the method definition for ClassOrInterfaceDeclaration(int modifiers) then insert the following variable definition immediately after boolean isInterface = false: Token t. Replace the method ClassOrInterfaceType( ) with the following: void ClassOrInterfaceType(Boolean..toString())..DisplayClassNames) print("class: " + t. while(m.src = src.mode == OpMode.CMNT_EXT_REGEX.DisplayClassNames) print("extends:")." ClassOrInterfaceType(!isInterface) 4.replaceAll( E18_JStringExtractor.STR_EXT_REGEX.replaceAll( E17_JCommentExtractor.} ClassOrInterfaceType(!isInterface) (".} 3. args): { Token t.compile(CU_REP_REGEX).matcher(src). Strings 281 . Locate the method definition ExtendsList(boolean isInterface) and replace the piece of code that starts with “extends” and ends before {: "extends" {if (JavaParser.image. Pattern p = Pattern.group()). Replace the <IDENTIFIER> statement (inside the same method) with this code fragment: t = <IDENTIFIER> {if (!isInterface && JavaParser.println(m... Alternative B Incorporate the desired functionality using these steps: 1.mode == OpMode. 2. } } /* Output: class E19_JClassUsageReporter *///:~ Here we make a complex regular expression more comprehensible by splitting it into constituent parts and assembling the pieces. " t = <IDENTIFIER> {if (shouldPrint) printnb(".DisplayClassNames.image.boolean shouldPrint = args." + t.} [ LOOKAHEAD(2) TypeArguments() ] ( LOOKAHEAD(2) ".toString()). As an exercise. or arrange the output in some other way. private double d. ********************************************************/ package strings. } { t = <IDENTIFIER> {if (shouldPrint) printnb(t.booleanValue() && JavaParser. and scans that * string into the various fields.util. both programs will display it multiple times. float and double * and String fields.toString()).DisplayClassNames) printnb("new: ").*. private float f. Add a toString() method * and demonstrate that your class works correctly. Exercise 20 //: strings/E20_Scanner. and will list even those interface names used to create anonymous inner classes.mode == OpMode.java /********************** Exercise 20 ********************* * Create a class that contains int. Locate the method AllocationExpression( ) and replace the ClassOrInterfaceType( ) statement with this code fragment: {if (JavaParser.mode == OpMode. private String s.length == 1 && args[0]. 4th Edition Annotated Solution Guide .image. private long l. import java. class DataHolder2 { private int i.} } 5.} [ LOOKAHEAD(2) TypeArguments() ] )* {if (shouldPrint) print(). Create a constructor for this class * that takes a single String argument. write a custom component that filters out duplicate class names.} ClassOrInterfaceType(true) If you instantiate a class name from more than one place. 282 Thinking in Java. long. stdin.1 1.DataHolder2(String data) { Scanner stdin = new Scanner(data).out.println(dh. } public String toString() { return i + " " + l + " " + f + " " + d + " " + s.useLocale(Locale. l = stdin.nextInt().US). s = stdin. } } public class E20_Scanner { public static void main(String[] args) { DataHolder2 dh = new DataHolder2("1 10000000000000 1.toString()).next("\\w+"). } } /* Output: 1 10000000000000 1.nextFloat(). d = stdin. System.1 1e55 Howdy Hi").nextLong(). i = stdin. f = stdin.0E55 Howdy *///:~ Strings 283 .nextDouble(). . Type Information Exercise 1 //: typeinfo/E01_ToyTest. } public static void main(String[] args) { Class<?> c = null. // Copy-pasted because these interfaces are not public interface HasBatteries {} interface Waterproof {} interface Shoots {} class Toy { //Toy() {} Toy(int i) {} } class FancyToy extends Toy implements HasBatteries.*.getSimpleName()).java /********************** Exercise 1 ********************* * In ToyTest. import static net.mindview. } catch(ClassNotFoundException e) { print("Can't find FancyToy").isInterface() + "]").java.util. *******************************************************/ package typeinfo.forName("typeinfo.Print.getName() + " is interface? [" + cc. Shoots { FancyToy() { super(1).getCanonicalName()). comment out Toy's default constructor * and explain what happens. } 285 . Waterproof. try { c = Class.FancyToy"). } } public class E01_ToyTest { static void printInfo(Class<?> cc) { print("Class name: " + cc. print("Simple name: " + cc. return. print("Canonical name : " + cc. try { // Requires default constructor: obj = up.Waterproof is interface? [true] Simple name: Waterproof Canonical name : typeinfo. } printInfo(obj. the compiler can’t create it because a non-default constructor already exists. return.Shoots is interface? [true] Simple name: Shoots Canonical name : typeinfo.FancyToy is interface? [false] Simple name: FancyToy Canonical name : typeinfo.java /********************** Exercise 2 ********************* * Incorporate a new kind of interface into ToyTest.Print. return.java * and verify that it is detected and displayed properly.Waterproof Class name: typeinfo.mindview. } catch(InstantiationException e) { print("Cannot instantiate").newInstance().printInfo(c).getInterfaces()) printInfo(face).*. Exercise 2 //: typeinfo/E02_ToyTest2.FancyToy Class name: typeinfo.Shoots Cannot instantiate *///:~ You must define the required default constructor for up.newInstance( ). for(Class<?> face : c. } } /* Output: Class name: typeinfo.getClass()). 4th Edition Annotated Solution Guide . import static net. Object obj = null.HasBatteries Class name: typeinfo.HasBatteries is interface? [true] Simple name: HasBatteries Canonical name : typeinfo.getSuperclass(). 286 Thinking in Java.util. ********************************************************/ package typeinfo. } catch(IllegalAccessException e) { print("Cannot access"). Class<?> up = c. getCanonicalName()).isInterface() + "]").getSuperclass().getInterfaces()) printInfo(face). } catch(ClassNotFoundException e) { print("Can't find FancierToy"). print("Canonical name : " + cc. print("Simple name: " + cc. for(Class<?> face : c. Object obj = null. } catch(IllegalAccessException e) { print("Cannot access").FancyToy *///:~ Type Information 287 .forName("typeinfo. } catch(InstantiationException e) { print("Cannot instantiate").getName() + " is interface? [" + cc. } } /* Output: Class name: typeinfo. try { obj = up.interface HasCPU {} class FancierToy extends FancyToy implements HasCPU {} public class E02_ToyTest2 { static void printInfo(Class<?> cc) { print("Class name: " + cc.FancierToy Class name: typeinfo. try { c = Class.FancierToy is interface? [false] Simple name: FancierToy Canonical name : typeinfo. System. } printInfo(c).FancierToy").HasCPU Class name: typeinfo.exit(1). } printInfo(obj.getClass()).HasCPU is interface? [true] Simple name: HasCPU Canonical name : typeinfo. Class<?> up = c. System.getSimpleName()). } public static void main(String[] args) { Class<?> c = null. System.newInstance().exit(1).FancyToy is interface? [false] Simple name: FancyToy Canonical name : typeinfo.exit(1). Create a * Rhomboid. ***********************************************/ package typeinfo. import java. // but fails at runtime with a ClassCastException: //! Circle c = (Circle)shape. for(Shape shape : shapes) shape.*.java /****************** Exercise 4 ****************** * Modify the previous exercise so that it uses * instanceof to check the type before performing 288 Thinking in Java. then downcast * it back to a Rhomboid.Exercise 3 //: typeinfo/E03_Rhomboid. Succeeds at compile time.java /****************** Exercise 3 ****************** * Add Rhomboid to Shapes. upcast it to a Shape.draw() Rhomboid.asList( new Circle(). } } public class E03_Rhomboid { public static void main(String[] args) { List<Shape> shapes = Arrays. Try downcasting to a * Circle and see what happens.draw() Triangle. new Rhomboid() ).util. new Triangle().java.draw() Square. // Upcast to shape: Shape shape = new Rhomboid(). new Square(). 4th Edition Annotated Solution Guide . } } /* Output: Circle.draw(). class Rhomboid extends Shape { public String toString() { return "Rhomboid". // Downcast to Circle. // Downcast to Rhomboid: Rhomboid r = (Rhomboid)shape.draw() *///:~ Exercise 4 //: typeinfo/E04_Instanceof. println("Rotating " + this + " by " + degrees + " degrees"). } Type Information 289 .println("shape not a Circle").draw()"). // Test before Downcasting: Circle c = null.*. abstract class RShape { void draw() { System. // Downcast to Rhomboid: Rhomboid r = (Rhomboid)shape. void rotate(int degrees) { System.* the downcast.util.out.java.println(this + ". ***********************************************/ package typeinfo. doesn’t perform the * operation).java /****************** Exercise 5 ****************** * Implement a rotate(Shape) method in Shapes. public class E04_Instanceof { public static void main(String[] args) { // Upcast to shape: Shape shape = new Rhomboid(). } abstract public String toString(). ***********************************************/ package typeinfo.out. * such that it checks to see if it is rotating a * Circle (and.out. else System. if(shape instanceof Circle) c = (Circle)shape. if so. } void rotate(int degrees) { throw new UnsupportedOperationException(). import java. } } /* Output: shape not a Circle *///:~ Exercise 5 //: typeinfo/E05_RotateShapes. } } class RCircle extends RShape { public String toString() { return "Circle". class HShape { 290 Thinking in Java. } } class RTriangle extends RShape { public String toString() { return "Triangle".asList( new RCircle(). import java. 4th Edition Annotated Solution Guide . rotateAll(shapes). new RTriangle() ).util." ***********************************************/ package typeinfo. } } /* Output: Rotating Square by 45 degrees Rotating Triangle by 45 degrees *///:~ This program performs the required check using rotateAll( ) instead of rotate(Shape).rotate(45).} class RSquare extends RShape { public String toString() { return "Square". } public static void main(String[] args) { List<RShape> shapes = Arrays.*. } } public class E05_RotateShapes { static void rotateAll(List<RShape> shapes) { for(RShape shape : shapes) if(!(shape instanceof RCircle)) shape.java /****************** Exercise 6 ****************** * Modify Shapes. new RSquare().reflect.lang. import java. The toString() method for each derived * Shape should indicate whether that Shape is * "highlighted. Exercise 6 //: typeinfo/E06_Highlight.*.java so that it can "highlight" * (set a flag) in all shapes of a particular * type. isInstance(shape)) shape.highlight(). (Object[])null). "clearHighlight"). (Class<?>[])null).add(this).invoke(shape. String method) { try { Method m = HShape. } // Create an applicator and reuse it.isInstance(shape)) shape. } } class HCircle extends HShape {} class HSquare extends HShape {} class HTriangle extends HShape {} Type Information 291 . "highlight"). and are thus // RuntimeExceptions: static void forEach(Class<?> type.println(this + " draw()"). } void draw() { System. } } static void highlight2(Class<?> type) { forEach(type. } static List<HShape> shapes = new ArrayList<HShape>(). } static void clearHighlight1(Class<?> type) { for(HShape shape : shapes) if(type.getMethod(method.isInstance(shape)) m.getName() + (highlighted ? " highlighted" : " normal"). } // Basic approach (code duplication) static void highlight1(Class<?> type) { for(HShape shape : shapes) if(type. for(HShape shape : shapes) if(type. } public void clearHighlight() { highlighted = false. } catch(Exception e) { throw new RuntimeException(e). public void highlight() { highlighted = true. } static void clearHighlight2(Class<?> type) { forEach(type. } public String toString() { return getClass(). All exceptions // indicate programmer error. HShape() { shapes.boolean highlighted = false.class.out.clearHighlight(). for(HShape shape : shapes) shape. for(HShape shape : shapes) shape. for(HShape shape : shapes) shape.HSquare normal draw() typeinfo.HSquare highlighted draw() typeinfo.highlight2(HShape. new HSquare()).HCircle highlighted draw() typeinfo.HTriangle normal draw() typeinfo.class).HCircle highlighted draw() typeinfo.asList( new HCircle().draw().HCircle highlighted draw() typeinfo.HSquare highlighted draw() typeinfo.HCircle highlighted draw() typeinfo.class). HShape. // Not in the hierarchy: HShape.clearHighlight1(ArrayList.HSquare normal draw() ******* typeinfo. System.clearHighlight2(ArrayList.highlight2(HCircle.HTriangle highlighted draw() typeinfo.HSquare normal draw() typeinfo.HSquare highlighted draw() ******* typeinfo.HSquare highlighted draw() typeinfo.HCircle highlighted draw() typeinfo. new HTriangle().HCircle highlighted draw() typeinfo. new HCircle().class). // Highlight them all: HShape.HCircle highlighted draw() typeinfo. HShape.HSquare highlighted draw() 292 Thinking in Java.HTriangle normal draw() typeinfo.draw().out.highlight1(HCircle.public class E06_Highlight { public static void main(String[] args) { List<HShape> shapes = Arrays. System.HTriangle highlighted draw() typeinfo. } } /* Output: typeinfo. new HSquare().println("*******"). HShape.highlight1(HShape. 4th Edition Annotated Solution Guide .class). HShape. new HSquare(). new HTriangle().class).HTriangle highlighted draw() typeinfo.class).out.draw(). new HCircle().println("*******"). We keep track of shape objects with the static ArrayList shapes. You must keep track of all objects in a class to highlight or clear all objects of a particular type. pass the name of the specific HShape you want to highlight.HCircle highlighted draw() typeinfo. we could use reflection in forEach( ) to combine redundant code that appears in highlight1( ) and clearHighlight1( ) during iteration through the list.class as the argument. it matches and highlights every HShape in the list. using methods in HShape.Cookie} /****************** Exercise 7 ****************** * Modify SweetShop.typeinfo. That is. If you pass a class that isn’t in the HShape hierarchy. testing for a match with Class. the HShape default constructor adds the current object to shapes. To call HShape.highlight( ). Alternatively. If you pass HShape.java // {Args: typeinfo. clearHighlight1( ) works the same way.HSquare highlighted draw() *///:~ We eliminate duplicate code by moving all the methods (starting with toString) into the base class.HTriangle highlighted draw() typeinfo. ***********************************************/ Type Information 293 . Notice how * you can control which Class objects are loaded * via the command-line argument." then * only the Candy object is created.Gum typeinfo. It iterates through shapes and calls highlight( ) for each matched object. We set or clear a boolean flag. if your * command line is "java SweetShop Candy. to change highlighting on a per-object basis. The static method highlight1( ) takes an argument of type Class. there’s never a match so the highlight and clearHighlight methods do nothing. and use RTTI to determine the name of the class.class appended to produce the Class reference. which is the type to highlight. Use clearHighlight( ) the same way to clear all highlighting. Highlight2( ) and clearHighlight2( ) each call forEach( ).java so that each type of * object creation is controlled by a * command-line argument. Exercise 7 //: typeinfo/E07_CommandLoad.HCircle highlighted draw() typeinfo. toString( ) also prints whether the object is highlighted.isInstance( ). with . class Candy { static { print("Loading Candy"). } } class Gum { static { print("Loading Gum").util. ***********************************************/ package typeinfo. import static net.*.mindview.Print.util.mindview. print(c.getName()).forName(arg).getInterfaces()) { print("Interface: " + k.FancyToy} /****************** Exercise 8 ****************** * Write a method that takes an object and * recursively prints all the classes in that * object's hierarchy.package typeinfo.Circle typeinfo. 4th Edition Annotated Solution Guide .*. // Produces the interfaces that this class // implements: for(Class<?> k : c.Print. } } public class E07_CommandLoad { public static void main(String[] args) throws Exception { for(String arg : args) Class. } } /* Output: Loading Gum Loading Cookie *///:~ Exercise 8 //: typeinfo/E08_RecursiveClassPrint.getName()). } } class Cookie { static { print("Loading Cookie"). import static net. 294 Thinking in Java.java // {Args: typeinfo. public class E08_RecursiveClassPrint { static void printClasses(Class<?> c) { // getSuperclass() returns null on Object: if(c == null) return. lang.HasBatteries Interface: typeinfo.Waterproof Interface: typeinfo.Circle typeinfo. if(i < args.Print.Circle typeinfo. import static net.lang.Object ================== Displaying typeinfo.forName(args[i])). } printClasses(c.*. i++) { print("Displaying " + args[i]).getSuperclass()).mindview.Object *///:~ We enhance the solution a bit.java // {Args: typeinfo.lang.*.FancyToy typeinfo.Toy java.getSuperclass()). ***********************************************/ package typeinfo. interface Iface { int i = 47.util.getDeclaredFields() to also display * information about the fields in a class. printClasses(Class. Type Information 295 .1) System.*. Exercise 9 //: typeinfo/E09_GetDeclaredFields. import java.Shape java. i < args.out.printClasses(k. import java.util.length .length.FancyToy Interface: typeinfo. } } } /* Output: Displaying typeinfo.println("==================").Derived} /****************** Exercise 9 ****************** * Modify the previous exercise so that it uses * Class.reflect.Shoots typeinfo. running it on FancyToy (from Exercise 1) to print all the interfaces. } public static void main(String args[]) throws Exception { for(int i = 0. getSuperclass()). printClasses(k. alreadyDisplayed.add(k). } for(Field fld : fields) { Class<?> k = fld. if(fields.getName()).getDeclaredFields(). print(c. double d. } public static void main(String args[]) throws Exception { 296 Thinking in Java.contains(k)) { printClasses(k). 4th Edition Annotated Solution Guide .void f(). } class Derived extends Base { Composed c.getSuperclass()). String s. } class Base implements Iface { String s.println("Base.f").length != 0) print("Fields:").getType(). } } class Composed { Base b.out. Field[] fields = c. } public class E09_GetDeclaredFields { static Set<Class<?>> alreadyDisplayed = new HashSet<Class<?>>(). if(!alreadyDisplayed. } } // Produces the interfaces that this class // implements: for(Class<?> k : c.getName()). static void printClasses(Class<?> c) { // getSuperclass() returns null on Object: if(c == null) return. } printClasses(c.getInterfaces()) { print("Interface: " + k. public void f() { System. for(Field fld : fields) { print(" " + fld). lang.Serializable Interface: java.hash private static final long java.length.Base. } } } /* Output: Displaying typeinfo.length . Interface: java.serialVersionUID private static final java.lang.String.forName(args[i])). i < args.Object Type Information 297 .d java.ObjectStreamField[] java.lang.lang.lang.lang.util.Derived.Base Fields: java.CharSequence java.Cloneable Interface: java.b typeinfo.String typeinfo.Derived typeinfo.String. i++) { print("Displaying " + args[i]).Object java.io.lang.Base typeinfo.String.String.io.lang.Comparator java.lang.String.CASE_INSENSITIVE_ORDER [C Interface: java.Composed typeinfo.lang.io.s typeinfo.lang.lang.ObjectStreamField.serialPersistentFields public static final java.Composed Fields: typeinfo.s double typeinfo.out.offset private final int java.io.Composed. if(i < args.String typeinfo.count private int java.Comparable Interface: java.Derived.Comparator Interface: java.String Fields: private final char[] java.1) System.Derived Fields: typeinfo.String.println("==================").Cloneable Interface: java.String.c java.io. printClasses(Class.util.lang.lang.Object int long [Ljava.lang.Serializable java.Base.value private final int java.lang.Serializable java.for(int i = 0.lang. getClass() = " + da.String typeinfo. print("la.s double typeinfo. char c = 'c'. import static net.getClass()). // Can't do it.lang.Print. String[] sa = new String[3].toCharArray().getClass() = " + sa.getClass(). a HashSet keeps the output tidy.getClass(). print("ac.getClass(). public class E10_PrimitiveOrTrue { public static void main(String args[]) { char[] ac = "Hello. E10_PrimitiveOrTrue[] pot = 298 Thinking in Java.lang.getClass() = " + ac.Iface java. //! c. displaying fields only once.getClass()).util. double[] da = new double[3].getClass()).d Interface: typeinfo.lang.getClass() = " + la.double Interface: typeinfo.getClass()).java /****************** Exercise 10 ***************** * Write a program to determine whether an array * of char is a primitive type or a true object. ***********************************************/ package typeinfo.Object typeinfo.Object *///:~ The program uses an interesting class hierarchy of interfaces and composition of complex types.getSuperclass() = " + ac. Exercise 10 //: typeinfo/E10_PrimitiveOrTrue.Iface java.lang.getSuperclass()).*. print("sa. print("ia. Here. print("ac.Base Fields: java.getClass() = " + ia.mindview. long[] la = new long[3]. Exercise 10 may help you understand the output.Object java. primitives // are not true objects. int[] ia = new int[3]. 4th Edition Annotated Solution Guide .Base. print("da. World!".Base.getClass()). getClass()).getClass() = class [C ac.lang. Other variants needed only the proper import directive to work with the newly-added Gerbil type. ***********************************************/ Type Information 299 . the compiler complains. // Multi-dimensional arrays: int[][][] threed = new int[3][][]. Not all elements of Java are objects (remember this example when you hear that Java is a “pure” OO language).getClass() = class [Ljava.getClass(). pot.String.getClass() = class [[[I *///:~ We create an array of char by defining a string and calling toCharArray( ). and an L followed by the type of element contained in the array for arrays of objects. threed. } } /* Output: ac.getSuperclass() = class java. Multi-dimensional arrays add a ‘[‘ for each dimension. We continue looking at other arrays to examine the outputs when you print their class names.Object ia.getClass() = " + pot.getClass() = class [I la.getClass() = class [Ltypeinfo. 2nd Edition.pets library and a new version of PetCount. Note that you can call getClass( ) and getSuperclass( ) for ac because it is an object of a true class. print("pot. Learn more about such details in Inside the Java Virtual Machine.java /****************** Exercise 11 ***************** * Add Gerbil to the typeinfo. but if you try to call getClass( ) for a primitive like char c. //: typeinfo/pets1/Gerbil. Exercise 11 Here we show only changes to the existing typeinfo.getClass() = class [J da. All array class names begin with a ‘[‘ followed by a one-character code for primitive types.java. print("threed. by Bill Venners (McGraw-Hill 2000).getClass() = " + threed.getClass()).new E10_PrimitiveOrTrue[3].getClass() = class [D sa.lang.pets library and * modify all the examples in this chapter to adapt * to this new class.E10_PrimitiveOrTrue. Mutt". @SuppressWarnings("unchecked") 300 Thinking in Java. package typeinfo. } } public List<Class<? extends Pet>> types() {return types. import typeinfo. "typeinfo.pets1. import java. 4th Edition Annotated Solution Guide .pets.java // Using class literals. } catch(ClassNotFoundException e) { throw new RuntimeException(e).Manx".util.pets. // Types that you want to be randomly created: private static String[] typeNames = { "typeinfo.EgyptianMau". "typeinfo.util.*.Gerbil" }. "typeinfo.pets.pets. "typeinfo. "typeinfo.pets1.Hamster". "typeinfo.pets.pets.*. static { try { for(String name : typeNames) types.Pug".Mouse". import java.add( (Class<? extends Pet>)Class. "typeinfo.pets1.Rodent { public Gerbil(String name) { super(name).Rat".pets.forName(name)).pets.package typeinfo.pets.java package typeinfo. import typeinfo. } public Gerbil() {} } ///:~ //: typeinfo/pets1/ForNameCreator. @SuppressWarnings("unchecked") public class ForNameCreator extends PetCreator { private static List<Class<? extends Pet>> types = new ArrayList<Class<? extends Pet>>(). "typeinfo.pets.} } ///:~ //: typeinfo/pets1/LiteralPetCreator1.pets1.*. public class Gerbil extends typeinfo.pets.*.Cymric". Gerbil] *///:~ //: typeinfo/PetCount.unmodifiableList(Arrays.pets1. Mouse. allTypes. class typeinfo.*.util.class). import static net. else put(type.pets.pets.*. class typeinfo.mindview.java // Using instanceof. public List<Class<? extends Pet>> types() { return types.out.Pug.class.pets1.pets. public static final List<Class<? extends Pet>> allTypes = Collections.Mutt.class. } } /* Output: [class typeinfo.Hamster. class typeinfo. class typeinfo.class. import java.class.pets. Dog.ForNameCreator. Cymric.Cymric.util.pets.subList(allTypes.Mouse.class. class typeinfo.class.pets.EgyptianMau.Print.public class LiteralPetCreator1 extends PetCreator { // No try block needed.pets. quantity + 1). Cat.pets. } public static void main(String[] args) { System.indexOf(Mutt. import typeinfo.class.class. 1).Manx.class. // Types for random creation: private static final List<Class<? extends Pet>> types = allTypes.class.class)).Hamster. import typeinfo.pets1.asList( Pet. EgyptianMau. if(quantity == null) put(type.size()). Rodent.pets. public class PetCount { static class PetCounter extends HashMap<String.*. package typeinfo. Pug.Rat. Rat. Integer>{ public void count(String type) { Integer quantity = get(type).Gerbil. class typeinfo. class typeinfo. class typeinfo.class. Mutt.println(types). import typeinfo. Gerbil. Manx.class. } } public static void countPets(PetCreator creator) { Type Information 301 . count("Dog"). Mutt=1} *///:~ 302 Thinking in Java. if(pet instanceof Rodent) counter. if(pet instanceof Hamster) counter.count("Pet"). Gerbil=2.getClass(). Manx=8. if(pet instanceof Manx) counter. if(pet instanceof Pet) counter. EgyptianMau=8.createArray(20)) { // List each individual pet: printnb(pet. 4th Edition Annotated Solution Guide . if(pet instanceof Mouse) counter. if(pet instanceof Dog) counter.getSimpleName() + " ").count("Cymric").count("Mouse").count("Gerbil"). Rodent=4. if(pet instanceof Rat) counter. if(pet instanceof Gerbil) counter. } public static void main(String[] args) { countPets(new ForNameCreator()).count("EgyptianMau"). if(pet instanceof Cat) counter.count("Rat"). Hamster=1. if(pet instanceof Pug) counter. Pet=20. Cat=12.count("Manx"). if(pet instanceof Mutt) counter.count("Hamster"). } } /* Output: EgyptianMau Gerbil Cymric EgyptianMau Cymric EgyptianMau Pug Rat Mutt Cymric Manx Manx Manx Cymric EgyptianMau Pug Hamster Cymric Gerbil Pug {Rat=1. if(pet instanceof Manx) counter. Dog=4. if(pet instanceof Manx) counter. } // Show the counts: print(). Pug=3. Cymric=8.count("Pug").count("Mutt").count("Cat").PetCounter counter= new PetCounter(). print(counter). for(Pet pet : creator.count("Rodent"). *.iterator(). for(Iterator<Coffee> it = new CoffeeGenerator(20). it.See the Registered Factories section of TIJ4 for a discussion of the problems with this method of generating objects of the Pets hierarchy. import net. Coffee=20. Breve=3. ***********************************************/ package typeinfo.count(coffee).util. import static net. } print().mindview. ***********************************************/ package typeinfo.java /****************** Exercise 13 ***************** * Use TypeCounter with the RegisteredFactories.class).getSimpleName() + " ").util.) { Coffee coffee = it.coffee.java * example in this chapter. import generics.java /****************** Exercise 12 ***************** * Use TypeCounter with the CoffeeGenerator. Type Information 303 . Cappuccino=2.getClass(). import java.*.Print. public class E12_CoffeeCount { public static void main(String[] args) { TypeCounter counter = new TypeCounter(Coffee.next().*.java * class in the Generics chapter. print(counter). Americano=8. Exercise 12 //: typeinfo/E12_CoffeeCount.*. Mocha=4} *///:~ Exercise 13 //: typeinfo/E13_PartCount.util.mindview.hasNext(). printnb(coffee. counter. } } /* Output: Americano Latte Americano Mocha Mocha Breve Americano Latte Cappuccino Cappuccino Americano Americano Mocha Breve Breve Americano Americano Mocha Latte Americano {Latte=3. util.util. for(int i = 0.*. printnb(part. Exercise 14 //: typeinfo/E14_RegisteredFactories2. Part part. public class E13_PartCount { public static void main(String[] args) { TypeCounter counter = new TypeCounter(Part. and newInstance() is used to create * each object.Print. Belt=8.count(part).mindview.*. import java.createRandom().import net. we see the versatility of the TypeCounter class in different scenarios.java so that instead of using * an explicit factory. @SuppressWarnings("unchecked") class Part2 { public String toString() { 304 Thinking in Java. 4th Edition Annotated Solution Guide .getClass(). counter. CabinAirFilter=4. AirFilter=3} *///:~ These two exercises show that writing reusable code is extremely useful.util. import static net. Modify * RegisteredFactories.*.mindview. } } /* Output: GeneratorBelt CabinAirFilter GeneratorBelt AirFilter PowerSteeringBelt CabinAirFilter FuelFilter PowerSteeringBelt PowerSteeringBelt FuelFilter CabinAirFilter PowerSteeringBelt FanBelt AirFilter OilFilter OilFilter AirFilter PowerSteeringBelt FuelFilter CabinAirFilter {OilFilter=2.java /****************** Exercise 14 ***************** * A constructor is a kind of factory method. PowerSteeringBelt=5. } print(). FuelFilter=3. FanBelt=1. GeneratorBelt=2. ***********************************************/ package typeinfo. Part=20. i++) { part = Part. print(counter). Filter=12. i < 20. the class object is stored * in the List.getSimpleName() + " ").class). try { return partClasses. OilFilter2.out. } } /* Output: GeneratorBelt2 CabinAirFilter2 GeneratorBelt2 Type Information 305 . PowerSteeringBelt2.return getClass().class.class. public static Part2 createRandom() { int n = rand.asList(FuelFilter2.nextInt(partClasses.class.println(Part2. CabinAirFilter2.get(n). FanBelt2.size()). } catch(InstantiationException e) { throw new RuntimeException(e). } } } class Filter2 extends Part2 {} class FuelFilter2 extends Filter2 {} class AirFilter2 extends Filter2 {} class CabinAirFilter2 extends Filter2 {} class OilFilter2 extends Filter2 {} class Belt2 extends Part2 {} class FanBelt2 extends Belt2 {} class GeneratorBelt2 extends Belt2 {} class PowerSteeringBelt2 extends Belt2 {} public class E14_RegisteredFactories2 { public static void main(String[] args) { for(int i = 0. private static Random rand = new Random(47).newInstance(). } static List<Class<? extends Part2>> partClasses = Arrays.class.getSimpleName(). i++) System. } catch(IllegalAccessException e) { throw new RuntimeException(e).createRandom()).class). GeneratorBelt2. AirFilter2.class. i < 10.class. java package typeinfo. we’ll use the Façade itself as a central place to incorporate the registry. //: typeinfo/pets2/Cymric. and modify the Pets Façade so that * it uses this one instead of the other two.AirFilter2 PowerSteeringBelt2 CabinAirFilter2 FuelFilter2 PowerSteeringBelt2 PowerSteeringBelt2 FuelFilter2 *///:~ This solution closely resembles the Prototype design pattern. In this solution. Clients who access the library exclusively through a Façade will transparently switch to the new PetCreator (PetCount4.pets2.Cat. dependable framework or library spares clients the annoyance of internal changes.java /****************** Exercise 15 ***************** * Implement a new PetCreator using Registered * Factories. } } } ///:~ //: typeinfo/pets2/EgyptianMau.factory. public class Cymric extends Manx { public static class Factory implements typeinfo. import typeinfo.java still work correctly.java in our case). Another candidate is the base class of the hierarchy of interest. 306 Thinking in Java. a properly designed.pets2. * Ensure that the rest of the examples that use * Pets. Exercise 15 We show only the noteworthy changes to typeinfo. This shows again that systems built around mature patterns are more easily understood and maintained than ad hoc solutions. 4th Edition Annotated Solution Guide . The new library is typeinfo.pets2. since it represents an entry point for the library. ***********************************************/ package typeinfo.pets.pets. Moreover.Factory<Cymric> { public Cymric create() { return new Cymric(). which we used in TIJ4. Rodent.pets.Factory<Hamster> { public Hamster create() { return new Hamster().pets.factory. } } } ///:~ //: typeinfo/pets2/Hamster. } } } ///:~ //: typeinfo/pets2/Manx.Cat.java package typeinfo. public class Mutt extends Dog { Type Information 307 .factory.pets.Factory<EgyptianMau> { public EgyptianMau create() { return new EgyptianMau().pets. } } } ///:~ //: typeinfo/pets2/Mutt.factory. import typeinfo.pets2.pets2.factory. public class Manx extends Cat { public static class Factory implements typeinfo.pets2.Factory<Manx> { public Manx create() { return new Manx(). import typeinfo.Rodent.pets2. import typeinfo.java package typeinfo. public class Hamster extends Rodent { public static class Factory implements typeinfo.Factory<Mouse> { public Mouse create() { return new Mouse(). public class Mouse extends Rodent { public static class Factory implements typeinfo.Dog.java package typeinfo.public class EgyptianMau extends Cat { public static class Factory implements typeinfo. import typeinfo.java package typeinfo. } } } ///:~ //: typeinfo/pets2/Mouse. import typeinfo. this method is not used! } @Override public Pet randomPet() { // Make 1 random Pet 308 Thinking in Java.pets. } } } ///:~ //: typeinfo/pets2/Pug.Factory().Factory()). new Hamster.Factory().Factory. public class Pug extends Dog { public static class Factory implements typeinfo.Factory().java package typeinfo.factory.factory.Dog.java package typeinfo.pets2. new EgyptianMau. new Rat. package typeinfo. // Dummy value.Factory().*.pets.util. new Mouse. new Cymric. @Override public List<Class<? extends Pet>> types() { return null. import typeinfo. 4th Edition Annotated Solution Guide .Factory().pets2.Rodent.pets2.factory. } } } ///:~ //: typeinfo/pets2/Pets. new Manx.public static class Factory implements typeinfo.Factory<Mutt> { public Mutt create() { return new Mutt(). import typeinfo.Factory<Pug> { public Pug create() { return new Pug().pets.Factory(). import typeinfo.Factory().pets. new Pug.Factory<Rat> { public Rat create() { return new Rat(). import java.PetCreator. public class Rat extends Rodent { public static class Factory implements typeinfo.asList(new Mutt.factory. } } } ///:~ //: typeinfo/pets2/Rat. @SuppressWarnings("unchecked") public class Pets { private static class RFPetCreator extends PetCreator { static List<Factory<? extends Pet>> petFactories = Arrays. import typeinfo.java // Facade to produce a default PetCreator.Pet. pets. public E16_CoffeeGenerator(int sz) { size = sz.java /****************** Exercise 16 ***************** * Modify the Coffee hierarchy in the Generics * chapter to use Registered Factories. } public Coffee next() { return Coffee. Iterable<Coffee> { public E16_CoffeeGenerator() {} private int size = 0. import java. return E16_CoffeeGenerator.randomPet(). } public static Pet[] createArray(int size) { return creator.arrayList(size).util.*. import typeinfo. public class E16_CoffeeGenerator implements Generator<Coffee>.util. } } private static Random rand = new Random(47). } public static ArrayList<Pet> arrayList(int size) { return creator. ***********************************************/ package typeinfo.createRandom().create().*. return petFactories.size()).get(n). } public Coffee next() { count--. } class CoffeeIterator implements Iterator<Coffee> { int count = size. public boolean hasNext() { return count > 0. public static final PetCreator creator = new RFPetCreator(). import net. } Type Information 309 .this.next(). the referred package is typeinfo.int n = rand.*.mindview.coffee2. public static Pet randomPet() { return creator.pets2 instead of typeinfo.createArray(size). } } ///:~ In the rest of the examples. Exercise 16 //: typeinfo/E16_CoffeeGenerator.nextInt(petFactories. we change only the references to the library. public void remove() {} // Not implemented }; public Iterator<Coffee> iterator() { return new CoffeeIterator(); } public static void main(String[] args) { for(Coffee c : new E16_CoffeeGenerator(10)) System.out.println(c); } } /* Output: Mocha 0 Americano 1 Mocha 2 Breve 3 Breve 4 Cappucino 5 Mocha 6 Americano 7 Latte 8 Latte 9 *///:~ //: typeinfo/coffee2/Coffee.java package typeinfo.coffee2; import java.util.*; import typeinfo.factory.Factory; @SuppressWarnings("unchecked") public class Coffee { private static int counter = 0; private int id = counter++; private static List<Factory<? extends Coffee>> coffeeFactories = Arrays.asList(new Americano.Factory(), new Breve.Factory(), new Latte.Factory(), new Mocha.Factory(), new Cappucino.Factory()); private static Random rand = new Random(47); public static Coffee createRandom() { int n = rand.nextInt(coffeeFactories.size()); return coffeeFactories.get(n).create(); } public String toString() { return getClass().getSimpleName() + " " + id; } } ///:~ //: typeinfo/coffee2/Americano.java 310 Thinking in Java, 4th Edition Annotated Solution Guide package typeinfo.coffee2; public class Americano extends Coffee { public static class Factory implements typeinfo.factory.Factory<Americano> { public Americano create() { return new Americano(); } } } ///:~ //: typeinfo/coffee2/Breve.java package typeinfo.coffee2; public class Breve extends Coffee { public static class Factory implements typeinfo.factory.Factory<Breve> { public Breve create() { return new Breve(); } } } ///:~ //: typeinfo/coffee2/Cappucino.java package typeinfo.coffee2; public class Cappucino extends Coffee { public static class Factory implements typeinfo.factory.Factory<Cappucino> { public Cappucino create() { return new Cappucino(); } } } ///:~ //: typeinfo/coffee2/Latte.java package typeinfo.coffee2; public class Latte extends Coffee { public static class Factory implements typeinfo.factory.Factory<Latte> { public Latte create() { return new Latte(); } } } ///:~ //: typeinfo/coffee2/Mocha.java package typeinfo.coffee2; public class Mocha extends Coffee { public static class Factory implements typeinfo.factory.Factory<Mocha> { public Mocha create() { return new Mocha(); } } } ///:~ Type Information 311 Exercise 17 //: typeinfo/E17_ShowMethods2.java // {Args: typeinfo.E17_ShowMethods2} /********************** Exercise 17 ********************** * Modify the regular expression in ShowMethods.java to * also strip off the keywords native and final. * (Hint: Use the OR operator '|'.) *********************************************************/ package typeinfo; import java.lang.reflect.*; import java.util.regex.*; import static net.mindview.util.Print.*; public class E17_ShowMethods2 { private static String usage = "usage:\n" + "E17_ShowMethods2 qualified.class.name\n" + "To show all methods in class or:\n" + "E17_ShowMethods2 qualified.class.name word\n" + "To search for methods involving 'word'"; private static Pattern p = Pattern.compile("\\w+\\.|native\\s|final\\s"); public static void main(String[] args) { if(args.length < 1) { print(usage); System.exit(0); } int lines = 0; try { Class<?> c = Class.forName(args[0]); Method[] methods = c.getMethods(); Constructor<?>[] ctors = c.getConstructors(); if(args.length == 1) { for(Method method : methods) print( p.matcher(method.toString()).replaceAll("")); for(Constructor<?> ctor : ctors) print(p.matcher(ctor.toString()).replaceAll("")); lines = methods.length + ctors.length; } else { for(Method method : methods) if(method.toString().indexOf(args[1]) != -1) { print( p.matcher(method.toString()).replaceAll("")); lines++; 312 Thinking in Java, 4th Edition Annotated Solution Guide } for(Constructor<?> ctor : ctors) if(ctor.toString().indexOf(args[1]) != -1) { print(p.matcher( ctor.toString()).replaceAll("")); lines++; } } } catch(ClassNotFoundException e) { print("No such class: " + e); } } } /* Output: public static void main(String[]) public int hashCode() public Class getClass() public void wait(long,int) throws InterruptedException public void wait() throws InterruptedException public void wait(long) throws InterruptedException public boolean equals(Object) public String toString() public void notify() public void notifyAll() public E17_ShowMethods2() *///:~ Exercise 18 //: typeinfo/E18_ShowMethods3.java // {Args: typeinfo.E18_ShowMethods3} /********************** Exercise 18 ********************** * Make ShowMethods a non-public class and verify that * the synthesized default constructor no longer appears in * the output. *********************************************************/ package typeinfo; import java.lang.reflect.*; import java.util.regex.*; import static net.mindview.util.Print.*; class E18_ShowMethods3 { private static String usage = "usage:\n" + "E18_ShowMethods3 qualified.class.name\n" + "To show all methods in class or:\n" + "E18_ShowMethods3 qualified.class.name word\n" + Type Information 313 "To search for methods involving 'word'"; private static Pattern p = Pattern.compile("\\w+\\."); public static void main(String[] args) { if(args.length < 1) { print(usage); System.exit(0); } int lines = 0; try { Class<?> c = Class.forName(args[0]); Method[] methods = c.getMethods(); Constructor<?>[] ctors = c.getConstructors(); if(args.length == 1) { for(Method method : methods) print( p.matcher(method.toString()).replaceAll("")); for(Constructor<?> ctor : ctors) print(p.matcher(ctor.toString()).replaceAll("")); lines = methods.length + ctors.length; } else { for(Method method : methods) if(method.toString().indexOf(args[1]) != -1) { print( p.matcher(method.toString()).replaceAll("")); lines++; } for(Constructor<?> ctor : ctors) if(ctor.toString().indexOf(args[1]) != -1) { print(p.matcher( ctor.toString()).replaceAll("")); lines++; } } } catch(ClassNotFoundException e) { print("No such class: " + e); } } } /* Output: public static void main(String[]) public native int hashCode() public final native Class getClass() public final void wait(long,int) throws InterruptedException public final void wait() throws InterruptedException public final native void wait(long) throws InterruptedException public boolean equals(Object) public String toString() 314 Thinking in Java, 4th Edition Annotated Solution Guide public final native void notify() public final native void notifyAll() *///:~ Exercise 19 //: typeinfo/E19_ReflectionToyCreation.java /****************** Exercise 19 ***************** * In ToyTest.java, use reflection to create a * Toy object using the nondefault constructor. ***********************************************/ package typeinfo; import java.lang.reflect.*; class SuperToy extends FancierToy { int IQ; public SuperToy(int intelligence) { IQ = intelligence; } public String toString() { return "I'm a SuperToy. I'm smarter than you."; } } public class E19_ReflectionToyCreation { public static Toy makeToy(String toyName, int IQ) { try { Class<?> tClass = Class.forName(toyName); for(Constructor<?> ctor : tClass.getConstructors()) { // Look for a constructor with a single parameter: Class<?>[] params = ctor.getParameterTypes(); if(params.length == 1) if(params[0] == int.class) return (Toy)ctor.newInstance( new Object[]{ Integer.valueOf(IQ) } ); } } catch(Exception e) { throw new RuntimeException(e); } return null; } public static void main(String args[]) { System.out.println(makeToy("typeinfo.SuperToy", 150)); } } /* Output: I'm a SuperToy. I'm smarter than you. *///:~ Type Information 315 This approach is rather limited. If you want to get fancier, ask the user (via the console) what values to use once you get the argument types for the constructor. Exercise 20 //: typeinfo/E20_ClassDump.java // {Args: java.lang.String typeinfo.SuperToy} /****************** Exercise 20 ***************** * Look up the interface for java.lang.Class in * the JDK documentation from java.sun.com. * Write a program that takes the name of a class * as a command-line argument, then uses the * Class methods to dump all the information * available for that class. Test your program * with a standard library class and a class you * create. ***********************************************/ package typeinfo; // The solution is a much-modified version of // Showmethods.java. import static net.mindview.util.Print.*; public class E20_ClassDump { public static void display(String msg, Object[] vals) { print(msg); for(Object val : vals) print(" " + val); } public static void classInfo(Class<?> c) throws Exception { print("c.getName(): " + c.getName()); print("c.getPackage(): " + c.getPackage()); print("c.getSuperclass(): " + c.getSuperclass()); // This produces all the classes declared as members: display("c.getDeclaredClasses()", c.getDeclaredClasses()); display("c.getClasses()", c.getClasses()); display("c.getInterfaces()", c.getInterfaces()); // The "Declared" version includes all // versions, not just public: display("c.getDeclaredMethods()", c.getDeclaredMethods()); display("c.getMethods()", c.getMethods()); display("c.getDeclaredConstructors()", c.getDeclaredConstructors()); 316 Thinking in Java, 4th Edition Annotated Solution Guide display("c.getConstructors()", c.getConstructors()); display("c.getDeclaredFields()", c.getDeclaredFields()); display("c.getFields()", c.getFields()); if(c.getSuperclass() != null) { print("Base class: --------"); classInfo(c.getSuperclass()); } print("End of " + c.getName()); } public static void main(String[] args) throws Exception { for(String arg : args) classInfo(Class.forName(arg)); } } /* (Execute to see output) *///:~ This is a good sample of the methods in Class that give reflection information. Also, it recursively goes up the inheritance hierarchy. Exercise 21 //: typeinfo/E21_SimpleProxyDemo.java /****************** Exercise 21 ***************** * Modify SimpleProxyDemo.java so it measures * method-call times. ***********************************************/ package typeinfo; import static net.mindview.util.Print.*; interface Interface { void doSomething(); void somethingElse(String arg); } class RealObject implements Interface { public void doSomething() { print("doSomething"); } public void somethingElse(String arg) { print("somethingElse " + arg); } } class SimpleProxy implements Interface { private Interface proxied; public SimpleProxy(Interface proxied) { this.proxied = proxied; } Type Information 317 public void doSomething() { print("SimpleProxy doSomething"); long start = System.nanoTime(); proxied.doSomething(); long duration = System.nanoTime() - start; print("METHOD-CALL TIME: " + duration); } public void somethingElse(String arg){ print("SimpleProxy somethingElse " + arg); long start = System.nanoTime(); proxied.somethingElse(arg); long duration = System.nanoTime() - start; print("METHOD-CALL TIME: " + duration); } } public class E21_SimpleProxyDemo { public static void consumer(Interface iface) { iface.doSomething(); iface.somethingElse("bonobo"); } public static void main(String[] args) { consumer(new RealObject()); consumer(new SimpleProxy(new RealObject())); } } /* Output: (90% match) doSomething somethingElse bonobo SimpleProxy doSomething doSomething METHOD-CALL TIME: 52800 SimpleProxy somethingElse bonobo somethingElse bonobo METHOD-CALL TIME: 56711 *///:~ As stated in the JDK documentation, System.nanoTime( ) “provides nanosecond precision, but not necessarily nanosecond accuracy” (results vary on different computers). This is the main reason the output cannot be matched with greater than 90% certainty. Exercise 22 //: typeinfo/E22_SimpleDynamicProxyDemo.java /****************** Exercise 22 ***************** * Modify SimpleDynamicProxy.java so that it 318 Thinking in Java, 4th Edition Annotated Solution Guide * measures method-call times. ***********************************************/ package typeinfo; import java.lang.reflect.*; class DynamicProxyHandler implements InvocationHandler { private Object proxied; public DynamicProxyHandler(Object proxied) { this.proxied = proxied; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("**** proxy: " + proxy.getClass() + ", method: " + method + ", args: " + args); if(args != null) for(Object arg : args) System.out.println(" " + arg); long start = System.nanoTime(); Object ret = method.invoke(proxied, args); long duration = System.nanoTime() - start; System.out.println("METHOD-CALL TIME: " + duration); return ret; } } class E22_SimpleDynamicProxyDemo { public static void consumer(Interface iface) { iface.doSomething(); iface.somethingElse("bonobo"); } public static void main(String[] args) { RealObject real = new RealObject(); consumer(real); Interface proxy = (Interface)Proxy.newProxyInstance( Interface.class.getClassLoader(), new Class<?>[]{ Interface.class }, new DynamicProxyHandler(real)); consumer(proxy); } } /* Output: (85% match) doSomething somethingElse bonobo **** proxy: class $Proxy0, method: public abstract void Interface.doSomething(), args: null doSomething METHOD-CALL TIME: 301155 Type Information 319 **** proxy: class $Proxy0, method: public abstract void Interface.somethingElse(java.lang.String), args: [Ljava.lang.Object;@42e816 bonobo somethingElse bonobo METHOD-CALL TIME: 84648 *///:~ Here, we centralize the time measurement of method calls, previously dispersed among all methods of a proxy. Exercise 23 //: typeinfo/E23_SimpleDynamicProxyDemo2.java // {ThrowsException} /****************** Exercise 23 ***************** * Inside invoke() in SimpleDynamicProxy.java, * try to print the proxy argument and explain * what happens. ************************************************/ package typeinfo; import java.lang.reflect.*; class DynamicProxyHandler2 implements InvocationHandler { private Object proxied; public DynamicProxyHandler2(Object proxied) { this.proxied = proxied; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("proxy: " + proxy); return method.invoke(proxied, args); } } class E23_SimpleDynamicProxyDemo2 { public static void consumer(Interface iface) { iface.doSomething(); iface.somethingElse("bonobo"); } public static void main(String[] args) { RealObject real = new RealObject(); consumer(real); Interface proxy = (Interface)Proxy.newProxyInstance( Interface.class.getClassLoader(), 320 Thinking in Java, 4th Edition Annotated Solution Guide new Class<?>[]{ Interface.class }, new DynamicProxyHandler2(real)); consumer(proxy); } } ///:~ This program enters an infinite loop inside invoke( ) and eventually crashes (with a java.lang.StackOverflowError). We call the toString( ) method to print the proxy object; however, we redirect calls through the interface through the proxy, as well as calls of the methods inherited from Object. Exercise 24 //: typeinfo/E24_RegisteredFactories3.java /****************** Exercise 24 ***************** * Add Null Objects to RegisteredFactories.java. ************************************************/ package typeinfo; import java.lang.reflect.*; import java.util.*; import net.mindview.util.Null; import typeinfo.factory.*; class NullPartProxyHandler implements InvocationHandler { private String nullName; private IPart proxied = new NPart(); NullPartProxyHandler(Class<? extends IPart> type) { nullName = type.getSimpleName() + ": [Null Part]"; } private class NPart implements Null, IPart { public String toString() { return nullName; } } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { return method.invoke(proxied, args); } } interface IPart {} class Part3 implements IPart { public String toString() { return getClass().getSimpleName(); } public static IPart Type Information 321 newNull(Class<? extends IPart> type) { return (IPart)Proxy.newProxyInstance( IPart.class.getClassLoader(), new Class<?>[]{ Null.class, IPart.class }, new NullPartProxyHandler(type)); } static List<Factory<IPart>> partFactories = new ArrayList<Factory<IPart>>(); static { partFactories.add(new FuelFilter3.Factory()); partFactories.add(new AirFilter3.Factory()); partFactories.add(new CabinAirFilter3.Factory()); partFactories.add(new OilFilter3.Factory()); partFactories.add(new FanBelt3.Factory()); partFactories.add(new PowerSteeringBelt3.Factory()); partFactories.add(new GeneratorBelt3.Factory()); } private static Random rand = new Random(47); public static IPart createRandom() { int n = rand.nextInt(partFactories.size()); return partFactories.get(n).create(); } } class Filter3 extends Part3 {} class FuelFilter3 extends Filter3 { public static class Factory implements typeinfo.factory.Factory<IPart> { public IPart create() { return new FuelFilter3(); } } } class AirFilter3 extends Filter3 { public static class Factory implements typeinfo.factory.Factory<IPart> { public IPart create() { return new AirFilter3(); } } } class CabinAirFilter3 extends Filter3 { public static class Factory implements typeinfo.factory.Factory<IPart> { public IPart create() { return new CabinAirFilter3(); } } 322 Thinking in Java, 4th Edition Annotated Solution Guide } class OilFilter3 extends Filter3 { public static class Factory implements typeinfo.factory.Factory<IPart> { public IPart create() { return new OilFilter3(); } } } class Belt3 extends Part3 {} class FanBelt3 extends Belt3 { public static class Factory implements typeinfo.factory.Factory<IPart> { public IPart create() { return new FanBelt3(); } } } class GeneratorBelt3 extends Belt3 { public static class Factory implements typeinfo.factory.Factory<IPart> { public IPart create() { return new GeneratorBelt3(); } } } class PowerSteeringBelt3 extends Belt3 { public static class Factory implements typeinfo.factory.Factory<IPart> { public IPart create() { return new PowerSteeringBelt3(); } } } public class E24_RegisteredFactories3 { public static void main(String[] args) { for(int i = 0; i < 10; i++) { IPart part = Part3.createRandom(); // Real object System.out.println(part); // Null companion System.out.println(Part3.newNull(part.getClass())); } } } /* Output: Type Information 323 GeneratorBelt3 GeneratorBelt3: [Null Part] CabinAirFilter3 CabinAirFilter3: [Null Part] GeneratorBelt3 GeneratorBelt3: [Null Part] AirFilter3 AirFilter3: [Null Part] PowerSteeringBelt3 PowerSteeringBelt3: [Null Part] CabinAirFilter3 CabinAirFilter3: [Null Part] FuelFilter3 FuelFilter3: [Null Part] PowerSteeringBelt3 PowerSteeringBelt3: [Null Part] PowerSteeringBelt3 PowerSteeringBelt3: [Null Part] FuelFilter3 FuelFilter3: [Null Part] *///:~ Following TIJ4’s technique, we use a Dynamic Proxy to create Null Objects for different parts. The IPart interface is the most important innovation to RegisteredFactories.java. Notice that the rest of the class hierarchy is unchanged. Exercise 25 //: typeinfo/E25_HiddenMethodCalls.java /****************** Exercise 25 ***************** * Create a class containing private, protected and * package access methods. Write code to access these * methods from outside of the class’s package. ************************************************/ package typeinfo; import java.lang.reflect.*; import typeinfo.classa.*; public class E25_HiddenMethodCalls { static void callHiddenMethod(Object a, String methodName) throws Exception { Method g = a.getClass().getDeclaredMethod(methodName); g.setAccessible(true); g.invoke(a); } 324 Thinking in Java, 4th Edition Annotated Solution Guide c() A. Compile time error callHiddenMethod(objA.println("A.c()").println("A. "a"). but this is confusing because it implies that Percussion. "c").public static void main(String[] args) throws Exception { class B extends A { protected void b() { super. public class A { private void a() { System.b() A. } } ///:~ Exercise 26 //: typeinfo/E26_ClearSpitValve.out.out. // The summary reads: /* One option is to put a clearSpitValve() method in the base class Instrument. RTTI provides a much more reasonable solution because you can place the method in the specific class where it’s Type Information 325 .a() A.b(). } } /* Output: A.java package typeinfo.println("A. callHiddenMethod(objA. Compile time error //! objA.b(). B objB = new B().a().classa. } void c() { System. callHiddenMethod(objA.c().b()").a()").b() *///:~ //: typeinfo/classa/A.out. } } A objA = new A(). Compile time error //! objA. "b"). Stringed and Electronic instruments also have spit valves.b(). } protected void b() { System. //! objA. objB. ************************************************/ package typeinfo.java /****************** Exercise 26 ***************** * Implement clearSpitValve() as described in the * summary. } } 326 Thinking in Java.appropriate (Wind. However. interface Instrument { void play().play()"). } public void prepareInstrument() { clearSpitValve(). At the same time. } public void adjust() {} public void prepareInstrument() { print("Stringed. void prepareInstrument(). } public String what() { return "Percussion".clearSpitValve").prepareInstrument").play()"). you may discover that there’s a more sensible solution—here. a prepareInstrument() method in the base class. String what().*. */ // We'll use the last-defined version of the // instrument hierarchy: import static net.util. } public void adjust() {} public void prepareInstrument() { print("Percussion. void adjust(). 4th Edition Annotated Solution Guide . } public void adjust() {} public void clearSpitValve() { print("Wind. } class Wind implements Instrument { public void play() { print("Wind. } } class Percussion implements Instrument { public void play() { print("Percussion.prepareInstrument"). } public String what() { return "Wind". in this case). } public String what() { return "Stringed".mindview.Print. } } class Stringed implements Instrument { public void play() { print("Stringed. you might not see such a solution when you’re first solving the problem and could mistakenly assume that you must use RTTI.play()"). play().clearSpitValve Percussion. Music5. i. new Percussion().clearSpitValve Type Information 327 . new Brass(). } public void adjust() { print("Brass. } } /* Output: Wind.prepareAll(orchestra).play()"). } public String what() { return "Woodwind".class Brass extends Wind { public void play() { print("Brass.. new Woodwind().prepareInstrument().adjust()"). new Stringed().prepareInstrument Brass. } public void clearSpitValve() { print("Woodwind.prepareInstrument Stringed. }.clearSpitValve"). } } class Woodwind extends Wind { public void play() { print("Woodwind.clearSpitValve").tuneAll(orchestra). } } class Music5 { static void tune(Instrument i) { // . } static void tuneAll(Instrument[] e) { for(Instrument instrument : e) tune(instrument).. } } public class E26_ClearSpitValve { public static void main(String[] args) { Instrument[] orchestra = { new Wind().play()"). } public void clearSpitValve() { print("Brass. } static void prepareAll(Instrument[] e) { for(Instrument instrument : e) instrument. Music5. play() Brass.play() *///:~ 328 Thinking in Java.play() Woodwind.Woodwind.clearSpitValve Wind.play() Percussion. 4th Edition Annotated Solution Guide .play() Stringed. b. T b.a = a. Exercise 2 //: generics/E02_Holder4.pets library to * show that a Holder3 that is specified to hold * a base type can also hold a derived type.a = a. System. so Holder3<Pet> can hold an instance of Mouse. this.println(h3. } } /* Output: Mouse Mickey *///:~ Mouse is a kind of Pet. T c) { this.out. import typeinfo. ************************************************/ package generics.Generics Exercise 1 //: generics/E01_PetsHolder. public class E01_PetsHolder { public static void main(String[] args) { Holder3<Pet> h3 = new Holder3<Pet>(new Mouse("Mickey")). public Holder4(T a.java /****************** Exercise 1 ***************** * Use Holder3 with the typeinfo.get()).java /****************** Exercise 2 ***************** * Create a holder class that holds three objects * of the same type. c. this.c = c.b = b. } 329 . } public void setA(T a) { this. class Holder4<T> { private T a. ************************************************/ package generics.pets. along with the methods to * store and fetch those objects and a constructor * to initialize all three.*. " + fifth + ". } } 330 Thinking in Java. " + sixth + ")".D.E> { public final F sixth.B. } public class E02_Holder4 { public static void main(String[] args) { Holder4<String> h4 = new Holder4<String>("A".b = b. "C").println(h4. B b. h4.out. System. class SixTuple<A.println(h4.getC()). } } /* Output: A B C D *///:~ Exercise 3 //: generics/E03_SixTuple. "B". d. " + fourth + ".C. System.F> extends FiveTuple<A. ************************************************/ package generics.getB()). b. F f) { super(a. e). } void setC(T c) { this.E. C c. import net. c. } public String toString() { return "(" + first + ". public SixTuple(A a. E e.println(h4.out. System.getC()).c = c.public public public public public } void setB(T b) { this. } T getA() { return a. " + third + ".java /****************** Exercise 3 ***************** * Create and test a SixTuple generic.C. System.*. sixth = f.println(h4.getA()). } T getB() { return b. " + second + ".setC("D").B. D d.mindview. } T getC() { return c.out. 4th Edition Annotated Solution Guide .D.out.util. Amphibian@c17164. 4. interface Selector<T> { boolean end(). } } /* Output: (75% match) (generics.Double. } public static void main(String[] args) { System. ************************************************/ package generics. 1.public class E03_SixTuple { static SixTuple<Vehicle.out.Vehicle@de6ced. new Amphibian().java.println(a()).String.1.Double. 1) *///:~ Exercise 4 //: generics/E04_GenericSequence.Byte> a() { return new SixTuple<Vehicle. } private class SequenceSelector implements Selector<T> { private int i.length) items[next++] = x. Generics 331 .7. "hi".length) i++. (byte)1).Byte>( new Vehicle(). T current(). generics. } } public Selector<T> selector() { return new SequenceSelector().java /****************** Exercise 4 ***************** * "Generify" innerclasses/Sequence. 1.length.String. hi.7. private int next.1. } class Sequence<T> { private Object[] items. void next(). public boolean end() { return i == items. } public void next() { if(i < items. public Sequence(int size) { items = new Object[size].Float.Amphibian. (float)4. } public void add(T x) { if(next < items.Amphibian.Float. } @SuppressWarnings("unchecked") public T current() { return (T)items[i]. } } private Node top = new Node().current() + " "). top). 332 Thinking in Java. i++) sequence.next(). Node next) { this. i < 10. selector. this.item = item.selector().item. } boolean end() { return item == null && next == null. class LinkedStack<T> { private class Node { T item.end()) top = top.} } public class E04_GenericSequence { public static void main(String[] args) { Sequence<String> sequence = new Sequence<String>(10). } Node(T item. Selector<String> selector = sequence.add(Integer. if(!top. for(int i = 0. Node next.end()) { System. // End sentinel public void push(T item) { top = new Node(item. } public T pop() { T result = top.print(selector.java /****************** Exercise 5 ***************** * Remove the type parameter on the Node class and * modify the rest of the code in LinkedStack. Node() { item = null. return result.toString(i)).java * to show that an inner class has access to the * generic type parameters of its outer class. while(!selector. 4th Edition Annotated Solution Guide .next.out.next = next. ************************************************/ package generics. } } } /* Output: 0 1 2 3 4 5 6 7 8 9 *///:~ Exercise 5 //: generics/E05_LinkedStack2. next = null. System.push(s).next()). ************************************************/ package generics. import net.out. public class E06_RandomListTest { private static void dump(RandomList<?> rl) { for(int i = 0. i++) System.add(gi. i < 11. for(String s: ("The quick brown fox jumped over " + "the lazy brown dog").*.split(" ")) rs. } } /* Output: stun! on Phasers *///:~ Exercise 6 //: generics/E06_RandomListTest. i < 11.out.} } public class E05_LinkedStack2 { public static void main(String[] args) { LinkedStack<String> lss = new LinkedStack<String>(). String s. for(String s : "Phasers on stun!".println(s).util. } public static void main(String[] args) { RandomList<String> rs = new RandomList<String>().select() + " "). Generics 333 .mindview.pop()) != null) System.add(s). Generator<Integer> gi = new CountingGenerator.out.print(rl. for(int i = 0. RandomList<Integer> ri = new RandomList<Integer>().println(). dump(rs).Integer().java /****************** Exercise 6 ***************** * Use RandomList with two more types in addition * to the one shown in main().split(" ")) lss. while((s = lss. dump(ri). i ++) ri. java /****************** Exercise 7 ***************** * Use composition instead of inheritance to adapt * Fibonacci to make it Iterable. } public void remove() { // Not implemented throw new UnsupportedOperationException(). Generator<Character> gc = new CountingGenerator.util.RandomList<Character> rc = new RandomList<Character>(). i ++) rc.next()). } public Iterator<Integer> iterator() { return new Iterator<Integer>() { public boolean hasNext() { return n > 0.*. Notice the parameter type of the dump( ) method: RandomList<?> accepts any RandomList parameterized with an unknown type. i < 11. ************************************************/ package generics. for(int i = 0. } } 334 Thinking in Java. 4th Edition Annotated Solution Guide .next(). return fib. } public Integer next() { --n. } } /* Output: brown over fox quick quick dog brown The brown lazy brown 8 1 9 10 0 0 1 4 5 2 9 i b j k a a b e f c j *///:~ We use the Generator interface and the CountingGenerator class to produce sequences of integers and characters.Character(). private int n.add(gc. dump(rc). Exercise 7 //: generics/E07_IterableFibonacci2. You’ll learn more about these later in TIJ4. public IterableFibonacci(int count) { n = count. } }. respectively. import java. class IterableFibonacci implements Iterable<Integer> { private Fibonacci fib = new Fibonacci(). getSimpleName() + " " + id.java /****************** Exercise 8 ***************** * Following the form of the Coffee example. create * a hierarchy of StoryCharacters from your favorite * movie. } } class BadGuy extends StoryCharacter { public String toString() { return super. } } /* Output: 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 *///:~ Exercise 8 //: generics/E08_CharacterGenerator. * Create a generator for StoryCharacters.print(i + " "). import java. following * the form of CoffeeGenerator.util.*.toString() + " is a good guy". public String toString() { return getClass().out.public class E07_IterableFibonacci2 { public static void main(String[] args) { for(int i : new IterableFibonacci(18)) System. } } class Morton extends BadGuy {} Generics 335 . ************************************************/ package generics. } } class GoodGuy extends StoryCharacter { public String toString() { return super. import net.*.util.toString() + " is a bad guy". dividing them into GoodGuys and BadGuys. private final long id = counter++. class StoryCharacter { private static long counter.mindview. class. return CharacterGenerator. } } public class E08_CharacterGenerator { public static void main(String[] args) { CharacterGenerator gen = new CharacterGenerator().class.out.nextInt(types. Cheyenne. } public StoryCharacter next() { try { return (StoryCharacter) types[rand. i++) System. 4th Edition Annotated Solution Guide . public CharacterGenerator() {} private int size = 0. } } class CharacterIterator implements Iterator<StoryCharacter> { int count = size.class }. Harmonica.class Frank extends BadGuy {} class Harmonica extends GoodGuy {} class Cheyenne extends GoodGuy {} class CharacterGenerator implements Generator<StoryCharacter>.next()). Iterable<StoryCharacter> { private Class<?>[] types = { Morton. private static Random rand = new Random(47). } public StoryCharacter next() { count--. } public void remove() { // Not implemented throw new UnsupportedOperationException().next(). for(StoryCharacter p : new CharacterGenerator(7)) 336 Thinking in Java.this.newInstance(). Frank. for(int i = 0. i < 7. public boolean hasNext() { return count > 0. public Iterator<StoryCharacter> iterator() { return new CharacterIterator().length)].class. public CharacterGenerator(int sz) { size = sz. } }. } catch(Exception e) { throw new RuntimeException(e).println(gen. 0).out.getClass().getClass().Integer java.f("".f(1.java so that f() accepts * three arguments.lang. C c) { System.Double java. 1. 'c'.String java.lang. all of which are of a different * parameterized type.Float java.System.lang. System.C> void f(A a.println(a.println(c.E09_GenericMethods2 Generics 337 .println(b. } } /* Output: java. public class E09_GenericMethods2 { public <A.out.0F.println(p).java /****************** Exercise 9 ***************** * Modify GenericMethods. } } /* Output: Harmonica 0 is a good guy Frank 1 is a bad guy Harmonica 2 is a good guy Morton 3 is a bad guy Morton 4 is a bad guy Harmonica 5 is a good guy Morton 6 is a bad guy Frank 7 is a bad guy Harmonica 8 is a good guy Harmonica 9 is a good guy Frank 10 is a bad guy Cheyenne 11 is a good guy Frank 12 is a bad guy Morton 13 is a bad guy *///:~ Exercise 9 //: generics/E09_GenericMethods2. B b.lang.getName()).getClass(). ************************************************/ package generics. gm. gm). gm. System. } public static void main(String[] args) { E09_GenericMethods2 gm = new E09_GenericMethods2().getName()). 1.out.B.getName()).lang.out.Character generics. 1.Integer java.Boolean java.getName()). import net. System.out. gm. } } /* Output: java. System. ************************************************/ package generics.out.java /****************** Exercise 11 ***************** * Test New.getClass(). 1.lang. B b.mindview.lang.Character generics.Boolean java.*.Boolean *///:~ Exercise 11 //: generics/E11_NewTest.java /****************** Exercise 10 ***************** * Modify the previous exercise so that one of * f()'s arguments is non-parameterized.Double java. public class E10_GenericMethods3 { public <A. true).println(c.0. true).Float java.out. 4th Edition Annotated Solution Guide . import java.f(1.util. ************************************************/ package generics. gm. gm.String java.lang.0F.f("". } public static void main(String[] args) { E10_GenericMethods3 gm = new E10_GenericMethods3().getClass().util.B> void f(A a.lang.println(b.getClass().*.E10_GenericMethods3 java.getName()).println(a. false).*///:~ Exercise 10 //: generics/E10_GenericMethods3.lang.lang.getName()).java by creating your own classes and * ensuring that New will work properly with them. 338 Thinking in Java. Boolean c) { System. gm.lang.f('c'.lang. println(set).util.out. Generics 339 .println(list).*. (short)1. public class E12_NewTest2 { static void f(List< SixTuple<Byte.0F. 1)). 1. Exercise 12 //: generics/E12_NewTest2.String.mindview.Double. list.Short. System.Float.*.println(l).println(s).0.Integer>( (byte)1. System.Integer>> list()).0F. 1)).Float.Integer>> list = New. Set<Sequence<String>> set = New.java /****************** Exercise 12 ***************** * Repeat the previous exercise using explicit type * specification.Short. import java.Float.Float. (short)1. 1)] [Sequence@3e25a5] *///:~ Here we used the SixTuple and Sequence classes introduced earlier in this chapter.Short.String.set().add(new Sequence<String>(5)).String.add( new SixTuple<Byte. A.out. 1. ************************************************/ package generics.add( new SixTuple<Byte. } static void g(Set<Sequence<String>> s) { s.0.Integer>( (byte)1.Double. System. System.util.0.list().String.<SixTuple<Byte.out. 1.Double. 1.Double. "A". set. } public static void main(String[] args) { f(New. } } /* Output: (Sample) [(1.Float. "A".Double.Integer>> l) { l.Short. import net.public class E11_NewTest { public static void main(String[] args) { List<SixTuple<Byte.Short.0.String.out.add(new Sequence<String>(5)). 1. 1. 1. add(gen. i++) set. 1)] [Sequence@3e25a5] *///:~ Exercise 13 //: generics/E13_OverloadedFill. A. Generator<T> gen. you * don't lose the type of container.*. Queue and Set. return queue. public class E13_OverloadedFill { public static <T> List<T> fill(List<T> list. Generator<T> gen.<Sequence<String>>set()).mindview.*.next()).util. 4th Edition Annotated Solution Guide . import java. 1.g(New.next()). Generator<T> gen. i++) llist.coffee. 1.next()). Generator<T> gen. int n) { for(int i = 0.*. } } /* Output: (78% match) [(1. i++) queue. i < n.util. return llist.next()). return list. int n) { for(int i = 0. import generics. import net.add(gen.java /****************** Exercise 13 ***************** * Overload the fill() method so the arguments * and return types are the specific subtypes of * Collection: List. 1.0. return set. i < n. This way. int n) { for(int i = 0. i < n. 340 Thinking in Java. i++) list. int n) { for(int i = 0.add(gen.0. } public static <T> LinkedList<T> fill(LinkedList<T> llist. i < n. Can you overload * to distinguish between List and LinkedList? ************************************************/ package generics. } public static <T> Queue<T> fill(Queue<T> queue.add(gen. } public static <T> Set<T> fill(Set<T> set. new Fibonacci(). for(int i : iQueue) System. Set<Boolean> bSet = fill( new HashSet<Boolean>(). You can even overload to distinguish between List and LinkedList.out.out. Exercise 14 //: generics/E14_BasicGeneratorDemo2. } } /* Output: Americano 0 Latte 1 Americano 2 Mocha 3 1 1 2 3 5 8 13 21 34 55 89 144 abcdefghijkl false true *///:~ You need explicit casting from LinkedList to Queue because LinkedList implements both List and Queue interfaces. new CountingGenerator.println().print(ch). 4).println().println(b). the compiler will complain of an ambiguous method call.Boolean(). System.println(c). for(Boolean b : bSet) System. without an explicit cast. 12).Character().java /****************** Exercise 14 ***************** * Modify BasicGeneratorDemo. new CoffeeGenerator(). LinkedList<Character> cLList = fill( new LinkedList<Character>().java to use the * explicit form of creation for the Generator Generics 341 .out.print(i + " "). 12). System.out.out. new CountingGenerator.} public static void main(String[] args) { List<Coffee> coffeeList = fill( new ArrayList<Coffee>(). 10). for(Coffee c : coffeeList) System.out. for(char ch : cLList) System. Queue<Integer> iQueue = fill( (Queue<Integer>)new LinkedList<Integer>(). public class E15_LeftToReader { public static void main(String args[]) { System.java /****************** Exercise 15 ***************** * "Notice that f() returns a parameterized * TwoTuple object.out. i++) System. the compiler would issue a * warning.util. ************************************************/ package generics. However. public class E14_BasicGeneratorDemo2 { public static void main(String[] args) { Generator<CountedObject> gen = new BasicGenerator<CountedObject>( CountedObject. it is being “upcast” to an * unparameterized TwoTuple. import net. } } ///:~ 342 Thinking in Java. ***********************************************/ package generics.class).println(gen.mindview. The compiler * doesn’t warn about f2() in this case because the * return value is not being used in a parameterized * fashion.next()). 4th Edition Annotated Solution Guide . } } /* Output: CountedObject 0 CountedObject 1 CountedObject 2 CountedObject 3 CountedObject 4 *///:~ Exercise 15 //: generics/E15_LeftToReader.out. for(int i = 0.*.println("Exercise left to reader"). while f2() returns an * unparameterized TwoTuple object. i < 5. in a sense." * * Verify the previous statement. use the explicit constructor instead * of the generic create() method). if you were to * try to capture the result of f2() into a * parameterized TwoTuple.* (that is. out.String.java /****************** Exercise 16 ***************** * Add a SixTuple to Tuple. } static ThreeTuple<Amphibian.E. new Amphibian(). "hi".java.Tuple. C c.D.C. 47). } public static void main(String[] args) { System.Integer> g() { return tuple(new Amphibian(). 47). } } public class E16_TupleTest3 { static TwoTuple<String. } static FourTuple<Vehicle. 47.util.out. System.E. E e. B b. "hi". f).tuple(new Vehicle().out.Float> l() { return Tuple2. System.C. } static SixTuple<Vehicle. Generics 343 . System.D.String.0F).F>(a.java.B. "hi".Double> k() { return tuple(new Vehicle(). new Amphibian().1). class Tuple2 extends Tuple { public static <A. e. import net.String.String. b. F f) { return new SixTuple<A. 47).B. 47.util. "hi".*.mindview. new Amphibian(). c. D d.println(g()).*.C.println(k()).F> tuple(A a.Double. 1. d.Amphibian.1.E.Integer. import static net.mindview.println(f()).Integer.Amphibian. ************************************************/ package generics.B.Exercise 16 //: generics/E16_TupleTest3.Integer> h() { return tuple(new Vehicle(). 11.out.F> SixTuple<A. 11.Integer> f() { return tuple("hi". } static FiveTuple<Vehicle. and test it in * TupleTest2.println(h()).Amphibian.D. result. 47) (generics. hi.*. and the special case * of an EnumSet. import java.addAll(b).Watercolors.println(l()).Vehicle@61de33. 47. hi.watercolors.java /****************** Exercise 17 ***************** * Study the JDK documentation for EnumSet. Set<T> b) { 344 Thinking in Java. 47) (generics. return result.java to handle both the general case * of a Set interface as shown.util. You'll * see there's a clone() method. generics.*. 11.Amphibian@1b67f74. Set<T> b) { Set<T> result = copy(a). * However. 1. generics.*
[email protected]) (generics. import generics.util. generics. } public static <T> Set<T> intersection(Set<T> a. import static net. class Sets2 { @SuppressWarnings("unchecked") protected static <T> Set<T> copy(Set<T> s) { if(s instanceof EnumSet) return ((EnumSet)s).Amphibian@1fb8ee3. you cannot clone() from the reference * to the Set interface passed in Sets.mindview. 4th Edition Annotated Solution Guide . 11. using clone() instead of creating * a new HashSet? ************************************************/ package generics.*.watercolors.Print. } } /* Output: (65% match) (hi. Can you * modify Sets.Vehicle@1a758cb. return new HashSet<T>(s).0) *///:~ Exercise 17 //: generics/E17_Sets2.1.System.Amphibian@10b30a7. } public static <T> Set<T> union(Set<T> a. hi.java. 47) (generics.out. hi. 47. import static
[email protected](). getClass(). } } public class E17_Sets2 { public static void main(String[] args) { Set<Watercolors> set1 = EnumSet. print("set4: " + set4).union(set3. intersection(a. print("union(set3. } public static <T> Set<T> difference(Set<T> superset. return result. YELLOW_OCHRE. } } /* Output: set1: [BRILLIANT_RED. ROSE_MADDER. set4)). BURNT_UMBER). SAP_GREEN. COBALT_BLUE_HUE. b). VIRIDIAN_HUE] set2: [CERULEAN_BLUE_HUE. VIOLET. PERMANENT_GREEN. print("set1: " + set1). set2): " + Sets2. COBALT_BLUE_HUE. RAW_UMBER. ULTRAMARINE. ULTRAMARINE. CRIMSON. PHTHALO_BLUE. set4): " + Sets2.getSimpleName()). set3. CRIMSON.getClass(). PERMANENT_GREEN. ULTRAMARINE. set4. Set<Integer> set4 = new HashSet<Integer>(). result. set2) type: " + Sets2. VIOLET. VIRIDIAN_HUE). } public static <T> Set<T> complement(Set<T> a. COBALT_BLUE_HUE. CERULEAN_BLUE_HUE.add(1). PERMANENT_GREEN. MAGENTA. PHTHALO_BLUE. CERULEAN_BLUE_HUE.removeAll(subset). print("union(set1. VIRIDIAN_HUE. result. PHTHALO_BLUE. Set<Integer> set3 = new HashSet<Integer>(). print("union(set3.union(set3. BURNT_SIENNA. set2).Set<T> result = copy(a).union(set1.retainAll(b). Generics 345 .range(CERULEAN_BLUE_HUE. b)).add(2). MAGENTA. ROSE_MADDER. set2)).union(set1. set4). Set<T> b) { return difference(union(a. print("set2: " + set2). BURNT_UMBER] union(set1. print("set3: " + set3).range(BRILLIANT_RED. Set<Watercolors> set2 = EnumSet. return result. VIRIDIAN_HUE. set2): [BRILLIANT_RED.getSimpleName()). set4) type: " + Sets2. print("union(set1. Set<T> subset) { Set<T> result = copy(superset). java /****************** Exercise 18 ***************** * Following the form of BankTeller. private BigFish() {} public String toString() { return "BigFish " + id.util.util. YELLOW_OCHRE. import java. 4th Edition Annotated Solution Guide . ************************************************/ package generics.SAP_GREEN. create an * example where BigFish eat LittleFish in the Ocean. } 346 Thinking in Java. set4): [1. private LittleFish() {} public String toString() { return "LittleFish " + id. 2] union(set3. } } class BigFish { private static long counter = 1.*. class LittleFish { private static long counter = 1. private final long id = counter++. } }. private final long id = counter++. } public static Generator<LittleFish> generator() { return new Generator<LittleFish>() { public LittleFish next() { return new LittleFish(). set2) type: RegularEnumSet set3: [1] set4: [2] union(set3.mindview.*. } public static Generator<BigFish> generator = new Generator<BigFish>() { public BigFish next() { return new BigFish(). Exercise 18 //: generics/E18_OceanLife.java. BURNT_SIENNA. } }. set4) type: HashSet *///:~ This straightforward copy( ) method is based on the Factory Method design pattern. import net. BURNT_UMBER] union(set1. RAW_UMBER. for(LittleFish lf : littleF) eat(bigF. class Container extends ArrayList<Product> { public Container(int nProducts) { Generators.size())).generator.util.out. Product.*. lf).fill(bigF. } } /* Output: BigFish 3 eat LittleFish 1 BigFish 2 eat LittleFish 2 BigFish 3 eat LittleFish 3 BigFish 1 eat LittleFish 4 BigFish 1 eat LittleFish 5 BigFish 3 eat LittleFish 6 BigFish 1 eat LittleFish 7 BigFish 2 eat LittleFish 8 BigFish 3 eat LittleFish 9 BigFish 3 eat LittleFish 10 BigFish 2 eat LittleFish 11 BigFish 4 eat LittleFish 12 BigFish 2 eat LittleFish 13 BigFish 1 eat LittleFish 14 BigFish 1 eat LittleFish 15 *///:~ Exercise 19 //: generics/E19_CargoShip. List<LittleFish> littleF = new LinkedList<LittleFish>(). ************************************************/ package generics. List<BigFish> bigF = new ArrayList<BigFish>(). LittleFish.generator(). build a model * of a containerized cargo ship.java /****************** Exercise 19 ***************** * Following the form of Store.println(bf + " eat " + lf).fill(littleF. 15).java. BigFish. import java. Generators. } } Generics 347 . 4). } public static void main(String[] args) { Random rand = new Random(47).get(rand.generator.fill(this. LittleFish lf) { System.public class E18_OceanLife { public static void eat(BigFish bf. nProducts).nextInt(bigF. Generators. price: $268. } } class Crane {} class CommandSection {} class CargoShip extends ArrayList<CargoHold> { private ArrayList<Crane> cranes = new ArrayList<Crane>(). int nProducts) { for(int i = 0. 4th Edition Annotated Solution Guide .99 520: Test. i < nCargoHolds. private CommandSection cmdSection = new CommandSection(). i++) add(new Container(nProducts)). int nContainers.99 551: Test. price: $530.99 348 Thinking in Java. } } public class E19_CargoShip { public static void main(String[] args) { System.99 704: Test. } return result.println(new CargoShip(14. price: $554. price: $804.append("\n"). for(CargoHold ch : this) for(Container c : ch) for(Product p : c) { result. public CargoShip(int nCargoHolds.99 868: Test. } public String toString() { StringBuilder result = new StringBuilder(). price: $160.99 674: Test.99 278: Test. result.99 207: Test. price: $400. i++) add(new CargoHold(nContainers. price: $417. price: $24. int nProducts) { for(int i = 0. price: $440.append(p). 10)). } } /* Output: (Sample) 258: Test.class CargoHold extends ArrayList<Container> { public CargoHold(int nContainers.99 575: Test. i < nContainers.99 861: Test. 5. nProducts)). price: $114.99 140: Test.toString().out. price: $250. println("CombinedImpl::c()"). } } /* Output: Top::a() Top::b() *///:~ Generics 349 . *///:~ Exercise 20 //: generics/E20_Bounds.java /****************** Exercise 20 ***************** * Create an interface with two methods.out. and show that the methods in the * interface are callable inside this generic method.println("Top::b()").a(). create a generic method * with an argument type that is bounded by the * interface. // c() is not part of an interface // obj. } public static void main(String[] args) { f(new CombinedImpl()). price: $484. } class CombinedImpl implements Top { public void a() { System.99 .. } } public class E20_Bounds { static <T extends Top> void f(T obj) { obj. In another class. pass an instance of the implementing * class to the generic method.b().c().out. void b(). ************************************************/ package generics.println("Top::a()").out. interface Top { void a()..826: Test. obj. } public void c() { System. and a class * that implements that interface and adds another * method. * In main(). } public void b() { System. 350 Thinking in Java. 4th Edition Annotated Solution Guide . } return null.mindview. print(ctt.addType("Building".getClass()).java /****************** Exercise 21 ***************** * Modify ClassTypeCapture.class). Class<?> kind).put(typename. class ClassTypeCapture2 { Map<String.newInstance(). ctt.java by adding a * Map<String. ************************************************/ package generics. Building. ctt.addType("Product".Print. } catch(Exception e) { print(e.createNew() * will either produce a new instance of the class * associated with its argument string.util. ctt. print(ctt.Class<?>>. kind).createNew("Building"). } public void addType(String typename. import java.createNew("Product").Exercise 21 //: generics/E21_ClassTypeCapture2. a method * addType(String typename. import static net.*.addType("House". public Object createNew(String typename) { Class<?> cl = types.getClass()).get(typename). ctt. Product.*. House.Class<?>> types = new HashMap<String. } catch(NullPointerException e) { print("Not a registered typename: " + typename).createNew("House"). and a * method createNew(String typename). try { return cl. ctt.class).toString()). or produce * an error message.Class<?>>().createNew("Car").util.class). Class<?> kind) { types. } } public class E21_ClassTypeCapture2 { public static void main(String[] args) { ClassTypeCapture2 ctt = new ClassTypeCapture2(). public ClassAsFactory(Class<T> kind) { this. } return null.newInstance(arg)).getParameterTypes().lang.getConstructors()) { // Look for a constructor with a single parameter: Class<?>[] params = ctor. class ClassAsFactory<T> { Class<T> kind.getConstructor(int. } } Generics 351 .Building class generics.class) return kind.*.newInstance(arg). import java. } public T create(int arg) { try { // Technique 1 (verbose) for(Constructor<?> ctor : kind.*.Print.class).Product Not a registered typename: Car *///:~ The InstantiationException is thrown because Product does not have a noarg constructor.lang. // return ct. Exercise 22 //: generics/E22_InstantiateGenericType2.House java. if(params. import static net.java /****************** Exercise 22 ***************** * Use a type tag along with reflection to create * a method that uses the argument version of * newInstance() to create an object of a class * with a constructor that has arguments.mindview.cast(ctor. ************************************************/ package generics. } catch(Exception e) { throw new RuntimeException(e).reflect. } // Technique 2 (direct) // Constructor<T> ct = kind.kind = kind.length == 1) if(params[0] == int.util. ClassTypeCapture2 allows you to register any kind of class.InstantiationException: generics.} } /* Output: class generics. class). if(i == null) print("Integer cannot be instantiated!"). 4th Edition Annotated Solution Guide . whereas technique 2 does. } class IntegerFactory implements FactoryI<Integer> { 352 Thinking in Java. } // . ************************************************/ package generics. Employee emp = fe. Exercise 23 //: generics/E23_FactoryConstraint2.java so that create() * takes an argument. public Foo(FactoryI<T> factory) { x = factory. ClassAsFactory<Integer> fi = new ClassAsFactory<Integer>(Integer. Integer i = fi.. interface FactoryI<T> { T create(int arg). if(emp == null) print("Employee cannot be instantiated!"). } } /* Output: Employee cannot be instantiated! *///:~ The main difference here is that technique 1 throws no exception if it can’t find a suitable constructor.create(1).java /****************** Exercise 23 ***************** * Modify FactoryConstraint. } class Foo<T> { private T x.public class E22_InstantiateGenericType2 { public static void main(String[] args) { ClassAsFactory<Employee> fe = new ClassAsFactory<Employee>(Employee. Also note that we use the cast( ) method to perform a dynamic type-cast.create(0).class). If you want to get fancier. ask the user (via the console) what values to use.create(1).. class FactoryCapture { Map<String. int arg) { FactoryI<?> f = factories. } public static class Factory implements FactoryI<Widget> { public Widget create(int arg) { return new Widget(arg). } catch(NullPointerException e) { print("Not a registered factoryname: " + factoryname). try { return f. Generics 353 . Widget(int ident) { id = ident. Exercise 24 //: generics/E24_FactoryCapture.create(arg). } } class Widget { private final int id.get(factoryname). public Object createNew(String factoryname.*.public Integer create(int arg) { return new Integer(arg). and the whole solution leaner.Print. the factory and constrained approach for producing objects with arguments is much easier.util. import java. new Foo<Widget>(new Widget. } } ///:~ Obviously. } public String toString() { return "Widget " + id. ************************************************/ package generics.java /****************** Exercise 23 ***************** * Modify Exercise 21 so that factory objects are * held in the Map instead of Class<?>.FactoryI<?>>().FactoryI<?>> factories = new HashMap<String.Factory()).mindview.*.util. } } } public class E23_FactoryConstraint2 { public static void main(String[] args) { new Foo<Integer>(new IntegerFactory()). import static net. addFactory("Integer". interface Low { void c(). factory). 1)). one whose argument * parameter is bounded by the first interface and * one whose argument parameter is bounded by the * second interface. new IntegerFactory()). } } public void addFactory(String factoryname. print(fc. } } public class E24_FactoryCapture { public static void main(String[] args) { FactoryCapture fc = new FactoryCapture().Factory()).createNew("Widget". 2)).return null.createNew("Integer". Exercise 25 //: generics/E25_Bounds. Low { 354 Thinking in Java. ************************************************/ package generics. print(fc. new Widget.java /****************** Exercise 25 ***************** * Create two interfaces and a class that implements * both. fc. void d().createNew("Product".addFactory("Widget". 3). FactoryI<?> factory) { factories. fc. } } /* Output: 1 Widget 2 Not a registered factoryname: Product *///:~ Factories eliminate the problems of not finding a suitable constructor. and show that * it can be used with both generic methods. 4th Edition Annotated Solution Guide . Create two generic methods. fc.put(factoryname. } class TopLowImpl implements Top. Create an instance of the class * that implements both interfaces. println("Top::b()"). System. // OK // Runtime type is Integer[]. System.out.println(e). } } } } public class E25_Bounds { static <T extends Top> void top(T obj) { obj. not Float[] or Byte[]: try { // Compiler allows you to add Float: nums[1] = new Float(1. top(tli).d(). } } /* Output: Top::a() Top::b() Low::c() Low::d() *///:~ Exercise 26 //: generics/E26_CovariantArrays2.a(). obj.valueOf(1).out. } try { Generics 355 . obj. nums[0] = Integer.b().c().println("Low::d()"). } public static void main(String[] args) { TopLowImpl tli = new TopLowImpl(). System. public class E26_CovariantArrays2 { public static void main(String[] args) { Number[] nums = new Integer[10].out.out. } static <T extends Low> void low(T obj) { obj.println("Top::a()").out.public public public public } void void void void a() b() c() d() { { { { System. low(tli).println("Low::c()").java /****************** Exercise 26 ***************** * Demonstrate array covariance using Numbers and * Integers. // ArrayStoreException } catch(Exception e) { System.0). ************************************************/ package generics. public class E27_GenericsAndCovariance2 { public static void main(String[] args) { // Compile Error: incompatible types: // List<Number> nlist = new ArrayList<Integer>().java /****************** Exercise 27 ***************** * Show that covariance doesn’t work with Lists. // nlist.lang. // Compile Error: can't add any type of object: // nlist. // Wildcards allow covariance: List<? extends Number> nlist = new ArrayList<Integer>(). // Legal but uninteresting // We know that it returns at least Number: Number n = nlist. Write 356 Thinking in Java. // Above line produces an ArrayStoreException } catch(Exception e) { System. } } } /* Output: java. // nlist.Byte *///:~ Exercise 27 //: generics/E27_GenericsAndCovariance2.util.get(0).Float java.out.add(new Integer(1)).println(e). * using Numbers and Integers.*.add(null). import java. 4th Edition Annotated Solution Guide . Create * a second generic class Generic2<T> with a single * method that returns an argument of type T. nlist.java /****************** Exercise 28 ***************** * Create a generic class Generic1<T> with a single * method that takes an argument of type T. ************************************************/ package generics.ArrayStoreException: java.// Compiler allows you to add Byte: nums[2] = Byte.add(new Float(1.valueOf((byte)1).ArrayStoreException: java.lang. } } ///:~ Exercise 28 //: generics/E28_GenericReadAndWrite. then introduce * wildcards.lang.0)).lang.add(new Object()). T item) { obj. Generic2<Pet> g2 = new Generic2<Pet>(). } public static void main(String[] args) { Generic1<Rodent> g1 = new Generic1<Rodent>(). // OK // Compile error: Cat is not a Rodent // f1(g1. Test using the typeinfo.get(). // OK Generic2<Cat> g3 = new Generic2<Cat>(). ************************************************/ package generics.pets library. new Mouse()). * Write a second generic method with a covariant * argument of the second generic class that calls * its method.pets. } static <T> T f2(Generic2<? extends T> obj) { return obj.java /****************** Exercise 29 ***************** * Create a generic method that takes as an * argument a Holder<List<?>>. f1(g1. } } public class E28_GenericReadAndWrite { static <T> void f1(Generic1<? super T> obj. // OK } } ///:~ Exercise 29 //: generics/E29_WildcardTest. class Generic1<T> { public void set(T arg) {} } class Generic2<T> { public T get() { return null. Pet pet = f2(g2). Determine what * methods you can and can’t call for the Holder * and for the List.set(item). Cat cat = f2(g3). Generics 357 .*.* a generic method with a contravariant argument of * the first generic class that calls its method. new Cat()). import typeinfo. // OK pet = f2(g3). Repeat for an argument of * List<Holder<?>>. holder. // list.add(new Holder<Float>(1. // Compile error: // holder. // Compile errors: // list. print(list.contains(i)). list1.0F)). list.set(new Integer(2)).add(new Holder<Integer>(1)). f1(new Holder<List<?>>(list1)). 4th Edition Annotated Solution Guide . } } /* Output: true 1 true true true 1 1.equals(list)).*.get(). print(list.remove(0).add(1).valueOf(1))). f2(list2).util.mindview.size()).util. list. print(list.Print.get()).add(1). Integer i = (Integer)list.0 1 *///:~ 358 Thinking in Java.set(new ArrayList<Float>()).remove(i)).get()). } public static void main(String[] args) { List<Integer> list1 = new ArrayList<Integer>(). print(list.equals(Integer. print(i).************************************************/ package generics. } static void f2(List<Holder<?>> list) { Holder<?> holder = (Holder<?>)list. print(holder.get(0). print(holder. list2. import java.get(0).*. import static net.get(1). List<Holder<?>> list2 = new ArrayList<Holder<?>>(). public class E29_WildcardTest { static void f1(Holder<List<?>> holder) { List<?> list = holder. print(holder.add(new Object()). get(). hl.0F).set((short)1).set(1.set(1).mindview. but permits all other operations.get(). char c = hc. ************************************************/ package generics. Holder<Double> hd = new Holder<Double>(). long l = hl. int i = hi. hd. Exercise 30 //: generics/E30_AutoboxingUnboxingTest. Holder<Float> hf = new Holder<Float>(). and show that autoboxing and * autounboxing works for the set() and get() * methods of each instance. short s = hs. import static net. print(d == 1. float f = hf.0).0). print(l == 1).set(1L). print(c == 'A'). byte b = hb.get(). print(s == 1). Holder<Character> hc = new Holder<Character>(). print(i == 1). Generics 359 . double d = hd.Print.get().set('A'). hc. print(f == 1.set(1. hf.The compiler does not allow you to “write” into a generic type through an unbounded reference.get().util.set((byte)1).0F). hb.get(). hi. public class E30_AutoboxingUnboxingTest { public static void main(String[] args) { Holder<Integer> hi = new Holder<Integer>(). Holder<Long> hl = new Holder<Long>(). Holder<Short> hs = new Holder<Short>(). Holder<Byte> hb = new Holder<Byte>().java /****************** Exercise 30 ***************** * Create a Holder for each of the primitive * wrapper types.*. hs.get(). print(b == 1). print(bool). Does this mean that bounds-checking 360 Thinking in Java. boolean bool = hbool.java /****************** Exercise 32 ***************** * Verify that FixedSizeStack in GenericCast. 4th Edition Annotated Solution Guide .java and modify the * code so that the example compiles.set(true). new Hourly(). ************************************************/ package generics. hbool.Holder<Boolean> hbool = new Holder<Boolean>().java * generates exceptions if you try to go out of * its bounds. } } /* Output: true true true true true true true true *///:~ Exercise 31 //: generics/E31_MultipleInterfaceVariants2. } } ///:~ Exercise 32 //: generics/E32_FixedSizeStackTest.java /****************** Exercise 31 ***************** * Remove all the generics from * MultipleInterfaceVariants.get(). interface Payable {} class Employee implements Payable {} class Hourly extends Employee implements Payable {} public class E31_MultipleInterfaceVariants2 { public static void main(String[] args) { new Employee(). } catch(ArrayIndexOutOfBoundsException e) { System. e.out.java /****************** Exercise 33 ***************** * Repair GenericCast.lang.toString()).lang.println(stack.push(1). ************************************************/ Generics 361 .out. the FixedSizeStack does not check whether the index went out of bounds before accessing the internal array. Bounds checking both conceals the implementation details of a class and throws exceptions meaningful to the client. instead of a custom exception (like FullStackException).g.push(2).println(e.* code is not required? ************************************************/ package generics. public class E32_FixedSizeStackTest { public static void main(String[] args) { FixedSizeStack<Integer> stack = new FixedSizeStack<Integer>(1).java using an ArrayList.. stack. try { stack. try { stack. This triggers an inappropriate exception. System.ArrayIndexOutOfBoundsException: -1 java.out. } } } /* Output: 1 java. } catch(ArrayIndexOutOfBoundsException e) { System.EmptyStackException.ArrayIndexOutOfBoundsException: 1 *///:~ Obviously.pop()).util. Exercise 33 //: generics/E33_GenericCast2. the program signals ArrayIndexOutOfBoundsException instead of java.pop().println(e. which may be confusing. stack. in case of underflow.push(2).toString()). The program also signals ArrayIndexOutOfBoundsException in case of overflow. } stack = new FixedSizeStack<Integer>(1). util.add(item).pop(). throw new EmptyStackException(). for(String s : "A B C D E F G H I J". this. } public void push(T item) { if(index < size) { index++.package generics.size = size.get(--index). public static void main(String[] args) { FixedSizeStack2<String> strings = new FixedSizeStack2<String>(SIZE). 362 Thinking in Java.Print. i++) { String s = strings. printnb(s + " "). } else throw new FullStackException().*. import static net. strings. import java. private final int size. } catch(EmptyStackException e) { print("Stack is empty").*. } public T pop() { if(index > 0) return storage.mindview.split(" ")) strings.push(s). private final List<T> storage. 4th Edition Annotated Solution Guide . class FullStackException extends RuntimeException {} class FixedSizeStack2<T> { private int index. } print(). for(int i = 0. public FixedSizeStack2(int size) { storage = new ArrayList<T>(size). try { strings.push("A").pop(). i < SIZE.util. storage. } strings = new FixedSizeStack2<String>(1). } } public class E33_GenericCast2 { public static final int SIZE = 10. } } } /* Output: J I H G F E D C B A Stack is empty Stack is full *///:~ Exercise 34 //: generics/E34_SelfBounded.test()). System. } } class ConcreteProcessor extends GenericProcessor<ConcreteProcessor> { ConcreteProcessor process(ConcreteProcessor arg) { if(arg == null) return this. } Generics 363 . return arg.try { strings. ************************************************/ package generics.push("B"). In a non-abstract * method of the class. } catch(FullStackException e) { print("Stack is full"). T test() { return process(null). call the abstract method and * return its result.out.java /****************** Exercise 34 ***************** * Create a self-bounded generic type that contains * an abstract method that takes an argument of the * generic type parameter and produces a return value * of the generic type parameter. abstract class GenericProcessor<T extends GenericProcessor<T>> { abstract T process(T arg). } } public class E34_SelfBounded { public static void main(String[] args) { ConcreteProcessor cp = new ConcreteProcessor().println(cp == cp. Inherit from the self-bounded * type and test the resulting class. util.*. public class E35_CheckedList2 { @SuppressWarnings("unchecked") static void oldStyleMethod(List probablyAmericanos) { probablyAmericanos.java so that it uses the Coffee * classes defined in this chapter.*. 4th Edition Annotated Solution Guide . } public static void main(String[] args) { List<Americano> am1 = new ArrayList<Americano>().checkedList( new ArrayList<Coffee>(). } } /* Output: java. import java. } // Derived types work fine: List<Coffee> coffees = Collections.out.coffee.add(new Americano()). coffees. Americano.add(new Breve()).class).println(e).} /* Output: true *///:~ A self-bounded generic type enables covariant return and argument types.checkedList( new ArrayList<Americano>().add(new Breve()).lang.java /****************** Exercise 35 ***************** * Modify CheckedList.Americano *///:~ 364 Thinking in Java. // Quietly accepts a Breve List<Americano> am2 = Collections. try { oldStyleMethod(am2).class). Exercise 35 //: generics/E35_CheckedList2. Coffee. ************************************************/ package generics.ClassCastException: Attempt to insert class generics. import generics.Breve element into collection with element type class generics.coffee. coffees. // Throws an exception } catch(Exception e) { System. oldStyleMethod(am1).coffee. util.add("Hep!").> 1) resultCollector.E extends Exception. } class ProcessRunner<T.nextBoolean()) throw new Failure1_1().java /****************** Exercise 36 ***************** * Add a second parameterized exception to the * Processor class and demonstrate that the exceptions * can vary independently. F.*.F>> { List<T> processAll() throws E. interface Processor<T.E. F { List<T> resultCollector = new ArrayList<T>(). return resultCollector.Failure1_1. public void process(List<String> resultCollector) throws Failure1_1.F extends Exception> { void process(List<T> resultCollector) throws E.F> processor : this) processor. else resultCollector.F extends Exception> extends ArrayList<Processor<T. } } Generics 365 . ************************************************/ package generics. } } class Failure1_1 extends Exception {} class Failure1_2 extends Exception {} class Processor1 implements Processor<String. import java.E extends Exception. static int count = 3.Failure1_2> { static Random rnd = new Random(47). throw new Failure1_2(). for(Processor<T.Exercise 36 //: generics/E36_GenericExceptions. Failure1_2 { if(count-.process(resultCollector).add("Ho!"). if(count < 0) if(rnd.E. Failure1_2> runner = new ProcessRunner<String. } catch(Failure1_1 e) { System.println(runner2.== 0) resultCollector.Failure1_1.add(47).Failure2_2> runner2 = new ProcessRunner<Integer.processAll()). i < 3.println(e).out. } 366 Thinking in Java.Failure1_2>(). try { System. } catch(Failure2_2 e) { System.out. i < 3.Failure2_1. Failure2_2 { if(count-. else { resultCollector.out.add(new Processor1()).Failure2_2> { static Random rnd = new Random(47).println(runner. } catch(Failure1_2 e) { System.Failure2_2>().add(11). 4th Edition Annotated Solution Guide . i++) runner.println(e). i++) runner2. public void process(List<Integer> resultCollector) throws Failure2_1.processAll()).println(e). for(int i = 0.nextBoolean()) throw new Failure2_1().Failure2_1. for(int i = 0. try { System.class Failure2_1 extends Exception {} class Failure2_2 extends Exception {} class Processor2 implements Processor<Integer.out. } ProcessRunner<Integer. } } public class E36_GenericExceptions { public static void main(String[] args) { ProcessRunner<String.out. } if(count < 0) if(rnd.add(new Processor2()). } catch(Failure2_1 e) { System.Failure1_1.out. static int count = 2.println(e).Failure2_1. throw new Failure2_2(). getStamp() + " " + mixin1. public Color getColor() { return colored. interface Colored { Color getColor(). * mix it in to Mixin. // 2^24 public Color getColor() { return clr.java.java /****************** Exercise 37 ***************** * Add a new mixin class Colored to Mixins. } } public class E37_Mixins2 { public static void main(String[] args) { Mixin2 mixin1 = new Mixin2().*. mixin2 = new Mixin2().set("test string 1").Color[r=186.} } /* Output: generics.set("test string 2"). import java. } class ColoredImp implements Colored { private static Random rnd = new Random(47). } } class Mixin2 extends Mixin implements Colored { private Colored colored = new ColoredImp().awt. System.Failure1_2 generics. and show that it works.println(mixin1.getStamp() + " " + mixin2.util.getSerialNumber() + " " + mixin2.getColor()).out. mixin1.b=66] Generics 367 .get() + " " + mixin2.awt.g=36.getSerialNumber() + " " + mixin1.getColor(). ************************************************/ package generics.getColor()). mixin2.out. System. } } /* Output: (78% match) test string 1 1135953794432 1 java. private final Color clr = new Color(rnd. import java.nextInt(16777216)).Color.get() + " " + mixin1.println(mixin2.Failure2_2 *///:~ Exercise 37 //: generics/E37_Mixins2. Color[r=102. setType(getType() + " & steamed milk").b=5] *///:~ Exercise 38 //: generics/E38_DecoratorSystem.test string 2 1135953794479 2 java.java /****************** Exercise 38 ***************** * Create a simple Decorator system by starting * with basic coffee. } public void setType(String type) { this.awt. caramel * and whipped cream. setType(getType() + " & foam"). 4th Edition Annotated Solution Guide . } } 368 Thinking in Java.Color.g=91. public CoffeeDecorator(BasicCoffee basic) { this. class BasicCoffee { private String type. public BasicCoffee() {} public BasicCoffee(String type) { setType(type). import java. ************************************************/ package generics.awt. } public void setType(String type) { basic. chocolate. foam. } } class SteamedMilk extends CoffeeDecorator { public SteamedMilk(BasicCoffee basic) { super(basic).setType(type). } public String getType() { return basic.type = type. } public String getType() { return type. then providing decorators * of steamed milk.basic = basic. } } class Foam extends CoffeeDecorator { public Foam(BasicCoffee basic) { super(basic). } } class CoffeeDecorator extends BasicCoffee { protected BasicCoffee basic.getType(). b=255]] & whipped cream *///:~ Here. Generics 369 . setType(getType() + " & whipped cream"). } } /* Output: Capuccino is: espresso & steamed milk & foam White Chocolate Coffee is: hot coffee & chocolate[color = java. } } class WhippedCream extends CoffeeDecorator { public WhippedCream(BasicCoffee basic) { super(basic).g=255.println("White Chocolate Coffee is: " + whiteChocolateCoffee. Color color) { super(basic).getType()). CoffeeDecorator whiteChocolateCoffee = new WhippedCream( new Chocolate( new BasicCoffee("hot coffee"). } } class Caramel extends CoffeeDecorator { public Caramel(BasicCoffee basic) { super(basic). Notice that only Chocolate adds a new method to the CoffeeDecorator.Color[r=255.println( "Capuccino is: " + cappuccino.out. } } public class E38_DecoratorSystem { public static void main(String[] args) { CoffeeDecorator cappuccino = new Foam( new SteamedMilk(new BasicCoffee("espresso"))).WHITE)).out. System.getType()). Make some more coffees as an extra exercise. we “prepared” a cappuccino and a white-chocolate coffee. Color. System.color = color. setType(getType() + " & caramel"). } public Color getColor() { return color. public Chocolate(BasicCoffee basic.awt. this. setType(getType() + " & chocolate[color = " + getColor() + "]").class Chocolate extends CoffeeDecorator { private final Color color. Basic.java /****************** Exercise 39 ***************** * Add a new mixin class Colored to * DynamicProxyMixin. Basic b = (Basic)mixin.println(c. System.println(b.java to call the * speak() method for a heterogeneous collection * of Pet. tuple(new TimeStampedImp(). } } /* Output: (75% match) Hello 1135959445620 1 java. Colored.getStamp()).*. b.g=36. and * show that it works.getSerialNumber()).out.SerialNumbered. mix it in to mixin. import static net.class).out. System.Tuple. SerialNumbered s = (SerialNumbered)mixin. tuple(new ColoredImp(). System.set("Hello"). ************************************************/ package generics.awt.class). ************************************************/ package generics.pets.newInstance( tuple(new BasicImp().Exercise 39 //: generics/E39_DynamicProxyMixin2. TimeStamped t = (TimeStamped)mixin.util. tuple(new SerialNumberedImp().out.out.class)). 370 Thinking in Java.java.Color[r=186.mindview.get()).class). public class E39_DynamicProxyMixin2 { public static void main(String[] args) { Object mixin = MixinProxy. 4th Edition Annotated Solution Guide .println(t. System.b=66] *///:~ Exercise 40 //: generics/E40_SpeakingPets. Colored c = (Colored)mixin. TimeStamped.getColor()). Modify Apply.println(s.java /****************** Exercise 40 ***************** * Add a speak() method to all the pets in * typeinfo. out. } public SHamster() { super(). } } class SRat extends SRodent { public SRat(String name) { super(name).*. } public SRat() { super(). class SPet extends typeinfo. } public SPet() { super(). } public void speak() { System.println(this + " speak"). } } class SHamster extends SRodent { public SHamster(String name) { super(name). } public SPug() { super(). } } class SMutt extends SDog { public SMutt(String name) { super(name). } } class SEgyptianMau extends SCat { public SEgyptianMau(String name) { super(name). } public SRodent() { super(). } public SMutt() { super().util.Individual { public SPet(String name) { super(name). } public SManx() { super(). } } class SMouse extends SRodent { public SMouse(String name) { super(name).pets. } } class SRodent extends SPet { public SRodent(String name) { super(name). } } class SPug extends SDog { public SPug(String name) { super(name). } public SMouse() { super(). } Generics 371 . } } class SManx extends SCat { public SManx(String name) { super(name).import java. } public SCymric() { super(). ************************************************/ package generics.*. new SPug(). new SCymric()). } } class SCat extends SPet { public SCat(String name) { super(name).public SEgyptianMau() { super(). } } class SCymric extends SManx { public SCymric(String name) { super(name).java to use the classes in * typeinfo.class. Apply. import static net.util. } } public class E40_SpeakingPets { public static void main(String[] args) throws Exception { List<SPet> pets = Arrays. import java. } public SDog() { super(). new SEgyptianMau(). new SHamster().Print.mindview. new SMutt(). new SMouse(). new SManx(). } } /* Output: SRat speak SPug speak SMutt speak SMouse speak SManx speak SHamster speak SEgyptianMau speak SCymric speak *///:~ Exercise 41 //: generics/E41_Fill3. } public SCat() { super(). SPet.getMethod("speak")). 372 Thinking in Java.asList(new SRat().util.pets instead of the Coffee classes.*. 4th Edition Annotated Solution Guide .java /****************** Exercise 41 ***************** * Modify Fill2.apply(pets. } } class SDog extends SPet { public SDog(String name) { super(name). class. Cymric. Fill2.fill(Adapter. Each class should hold a value. Modify * Functional.pets. with nothing in * common. 3). print("----------------------").collectionAdapter(carrier).class. 2).*.java so that it performs functional Generics 373 . } } /* Output: Pet Pet Pet Mouse Mouse ---------------------Mutt Mutt Mutt Mutt Cymric *///:~ Exercise 42 //: generics/E42_Functional2. Mutt. Fill2. 4).fill(petQueue. for(Pet p: carrier) print(p). 1). // Helper method captures the type: Fill2. Fill2. Mouse.import typeinfo. Pet. // Use an adapted class: AddableSimpleQueue<Pet> petQueue = new AddableSimpleQueue<Pet>(). and at * least have methods that produce that value and * perform a modification upon that value. public class E41_Fill3 { public static void main(String[] args) throws Exception { // Adapt a Collection: List<Pet> carrier = new ArrayList<Pet>(). for(Pet p: petQueue) print(p).fill(petQueue.fill( new AddableCollectionAdapter<Pet>(carrier).java /****************** Exercise 42 ***************** * Create two separate classes.class.class. } } static class Concatenator implements Combiner<DataManipulator2> { 374 Thinking in Java. } public int getValue() { return value.*.value).getValue(). 4th Edition Annotated Solution Guide .increment(). public DataManipulator1(int value) { this.toString(value). } } final class DataManipulator2 { private String value.value = value. import static net.* operations on collections of your classes (these * operations do not have to be arithmetic as they * are in Functional. } public String getValue() { return value. } public String toString() { return Integer. import java. } public int compareTo(DataManipulator1 other) { return Integer. } } static class UpperCaseConverter implements UnaryFunction<String. ************************************************/ package generics.value = value.util.util.Print. return x.compareTo(other.DataManipulator1> { public DataManipulator1 function(DataManipulator1 x) { x. we need to create // function objects to adapt to our particular needs: static class Incrementor implements UnaryFunction<DataManipulator1.*.java). final class DataManipulator1 implements Comparable<DataManipulator1> { private int value.mindview.toUpperCase().DataManipulator2> { public String function(DataManipulator2 x) { return x. } public void increment() { ++value. } public void setValue(String value) { this. public DataManipulator2(String value) { setValue(value).valueOf(value). } } public class E42_Functional2 { // To use the above generic methods. } } /* Output: [11.new DataManipulator1(7)). new Incrementor())).GreaterThan<DataManipulator1>(rf)).getValue()).asList( new DataManipulator2("a"). new UpperCaseConverter())). new DataManipulator1(1). print( Functional. new DataManipulator2("B").getValue() + y. } } public static void main(String[] args) { DataManipulator1 rf = new DataManipulator1(4). print(Functional. B. List<DataManipulator2> ldm2 = Arrays. new DataManipulator2("c"). new Concatenator()). List<DataManipulator1> ldm1 = Arrays.reduce(ldm2.transform(ldm2. 8] [A.public DataManipulator2 combine(DataManipulator2 x.setValue(x. new DataManipulator1(10).DataManipulator2 y) { x. C.filter(ldm1. return x. print( Functional.new DataManipulator2("d")).transform( Functional. D] aBcd *///:~ Generics 375 .getValue()). new Functional.asList( new DataManipulator1(3). . public class E01_ArrayInitialization { static void hide(BerylliumSphere[] s) { System. * creating the argument dynamically. new BerylliumSphere(). new BerylliumSphere() }).length + " sphere(s)"). and where dynamic aggregate * initialization is redundant. Call the method. //! hide({ new BerylliumSphere() }). Discover the only * situations where ordinary aggregate array * initialization works.Arrays Exercise 1 //: arrays/E01_ArrayInitialization. ************************************************/ package arrays. } public static void main(String[] args) { // Dynamic aggregate initialization: hide(new BerylliumSphere[]{ new BerylliumSphere(). // The following line produces a compilation error. // Dynamic aggregate initialization is redundant // in the next case: BerylliumSphere[] a = new BerylliumSphere[]{ new BerylliumSphere().println("Hiding " + s.java /****************** Exercise 1 ****************** * Create a method that takes an array of * BerylliumSphere as an argument. hide(d). new BerylliumSphere() }.out. } } /* Output: Hiding 2 sphere(s) Hiding 3 sphere(s) Hiding 2 sphere(s) *///:~ 377 . hide(a). new BerylliumSphere() }. Demonstrate * that ordinary aggregate array initialization * doesn’t work in this case. // Aggregate initialization: BerylliumSphere[] d = { new BerylliumSphere(). public class E02_ReturningArray { static BerylliumSphere[] createArray(int size) { BerylliumSphere[] a = new BerylliumSphere[size]. ************************************************/ package arrays.java /****************** Exercise 2 ****************** * Write a method that takes an int argument and * returns an array of that size. Sphere 2. Sphere 7. } public static void main(String[] args) { System. Sphere 1. Sphere 6. * Create a second method that will print the * array generated by the first method. Sphere 8.Arrays. filled with * BerylliumSphere objects.println(Arrays. } } /* Output: [Sphere 0. 4th Edition Annotated Solution Guide . import java. return a. i++) a[i] = new BerylliumSphere(). and the initialization values are * a range determined by beginning and ending * values that are also arguments of the method. The size of * the array is determined by the arguments of * the method. Exercise 2 //: arrays/E02_ReturningArray.toString(createArray(10))). 378 Thinking in Java. Sphere 9] *///:~ Exercise 3 //: arrays/E03_TwoDDoubleArray. Sphere 3. Sphere 5.out.You must use aggregate initialization at the point of an array variable’s definition. In main() * test the methods by creating and printing * several different sizes of arrays. Sphere 4. for(int i = 0. i < size.util.java /****************** Exercise 3 ***************** * Write a method that creates and initializes a * two-dimensional array of double. With dynamic initialization you can create and initialize an array object anywhere. 00 88. double[][] twoD3 = twoDDoubleArray(9. for(int i = 0.00 62. 47.02 63.33 53. printArray(twoD2).17 51. print().00 ********************** 47.33 92.50 55.24 57.0).Locale. i < array. val += increment. double increment = (valEnd .length.util.62 52.length.47 51. double valStart. 99.00 75.67 57.56 59.67 96.33 79. j++) System.00 73.length.out.78 53.83 73.33 66.17 90. double valEnd) { double[][] array = new double[xLen][yLen].09 56. } } /* Output: 47. 47.17 64.83 60.2f ".printf(Locale.0). 5.0.67 83. } } public static void main(String args[]) { double[][] twoD = twoDDoubleArray(4.0). "%.40 58.87 62. print("**********************").*.mindview.31 50.18 Arrays 379 . printArray(twoD). printArray(twoD3). double[][] twoD2 = twoDDoubleArray(2.00 48.83 ********************** 47. 99.71 60.67 70.50 81.93 55. } public static void printArray(double[][] array) { for(int i = 0.00 60. i < array. i++) for(int j = 0. import java.util.50 94. j < array[i].length.00 86. import static net.US.16 49. j++) { array[i][j] = val. array[i][j]). public class E03_TwoDDoubleArray { public static double[][] twoDDoubleArray( int xLen.00 49.0. i++) { for(int j = 0. double val = valStart.0. j < array[i].50 68. 47.17 77. print("**********************"). } return array.Print.83 86. int yLen. 99. 2.***********************************************/ package arrays.valStart)/(xLen * yLen). 6. length. j++) for(int k = 0. } return array. since Arrays.64 72.valStart)/(xLen * yLen * zLen).67 82.04 81.java /****************** Exercise 4 ***************** * Repeat the previous exercise for a * three-dimensional array.US.51 86.64. val += increment.73 80. for(int i = 0.84 Calculate the step value for the initialization range by dividing the range by the number of elements in the array. j < array[i]. j < array[i].mindview.44 88.11 71.Locale.length. ***********************************************/ package arrays. int zLen.91 96.out. k < array[i][j].length.07 97.36 85.53 67.60 93. double val = valStart. k++) System. public class E04_ThreeDDoubleArray { public static double[][][] threeDDoubleArray( int xLen.49 70.58 79.2f ".29 92.13 90.69 68. "%.22 94.33 65.length. double valEnd) { double[][][] array = new double[xLen][yLen][zLen].length. 380 Thinking in Java. i++) { for(int j = 0. } public static void printArray(double[][][] array) { for(int i = 0.82 87.length. import java. double increment = (valEnd .42 78. i < array.96 74. k++) { array[i][j][k] = val. i < array.76 95.98 89.27 75. int yLen. import static net.Print.util. i++) for(int j = 0.printf( Locale. 4th Edition Annotated Solution Guide . array[i][j][k]).20 83.util.38 *///:~ 66. k < array[i][j].89 77.deepToString( ) produces output of low perspicuity (double values are printed with varying numbers of decimals).80 73. j++) { for(int k = 0. Exercise 4 //: arrays/E04_ThreeDDoubleArray. double valStart. You need a separate print routine.*. 0.33 92.92 87.25 91.92 Arrays 381 .50 68. print("**********************").42 80.print().33 66.0).00 62.75 84.67 96.75 71.0.17 50.00 88.50 54. print("**********************").92 74. 47. } } public static void main(String args[]) { double[][][] threeD = threeDDoubleArray(4.50 94. 99.67 56.83 86.17 77.58 82.42 67. 5.08 76.83 73.0). double[][][] threeD2 = threeDDoubleArray(2.08 63.50 81.58 95. 5.25 51.08 49. 47.67 83.0). printArray(threeD3).75 57.17 64.08 89. 2.92 60.33 52.75 97.67 70.25 78.58 55. 7.00 48. printArray(threeD). 99. printArray(threeD2). } } /* Output: 47. } print().25 65. double[][][] threeD3 = threeDDoubleArray(9.17 90.42 93.00 75. 99.42 53. 2. 6.58 69.83 61.0. 47.83 58.33 79. 56 59.20 93.55 77.69 62.22 77.57 75.83 65.40 86.57 57.21 78.37 78.65 66.26 73.79 69.99 49.47 84.27 54.80 57.00 47.05 77.48 49.60 65.08 74.55 60.58 56.42 56.20 54.81 83.62 51.89 60. 4th Edition Annotated Solution Guide .36 62.34 47.67 81.19 63.40 73.22 60.14 51.76 55.53 79.64 67.49 82.13 52.86 63.11 54.00 88.70 79.46 69.44 71.80 50.66 48.24 75.78 53.43 55.63 50.30 69.61 53.73 75.42 73.33 65.86 80.28 71.65 83.69 80.84 64.67 64.80 70.56 76.80 84.58 74.20 62.98 67.02 81.51 64.48 67.06 76.74 57.01 47.46 52.38 77.79 51.29 70.35 81.27 72.25 74.26 55.13 69.83 48.70 61.97 69.87 62.98 84.24 57.60 72.06 59.07 58.11 71.17 47.00 62.32 48.17 47.97 382 Thinking in Java.82 66.15 50.91 75.23 76.98 50.84 53.54 61.50 81.97 51.94 54.43 72.70 78.62 70.12 70.09 73.77 54.64 84.95 71.52 63.00 49.18 64.10 55.80 83.45 53.48 83.80 68.78 70.08 57.23 59.37 61.59 73.89 77.10 72.99 66.75 73.92 74.00 75.60 52.17 65.71 60.15 83.54 78.96 51.49 66.90 76.41 74.88 78.65 49.83 82.47 68.97 68.60 54.15 67.38 60.82 49.31 84.94 72.66 65.76 72.68 53.09 56.57 58.34 64.03 79.44 54.63 68.31 49.92 56.52 80.53 62.87 79.30 51.20 79.90 59.35 63.59 55.51 52.81 67.00 65.74 74.93 55.88 61.82 82.77 71.14 68.14 84.18 81.99 83.45 70.28 53.********************** 47.32 66.31 67.05 60.90 58.33 47.50 48.41 57.47 50.02 63.40 58.04 61.61 71.85 64.01 64.39 76.16 48.96 70.00 82.12 52.60 91.60 78.29 52.04 78.50 65.63 69.68 63.49 48.91 57.20 80.81 50.16 66.95 52.33 82.19 80.73 58.66 82.80 96.36 80.39 59.03 80.25 56.40 75.30 68.93 73.71 77.03 62.75 56.72 76.20 67.07 75.72 59.40 ********************** 47.21 61.32 83.64 49.16 82.40 60.85 81.37 79.23 58. 72 94.83 93.42 91.30 85. [[null.73 93.24 92.84 98.50 88.43 89.06 94.40 93. null]. ***********************************************/ package arrays.53 95. null.51 98.70 96.23 87.29 86.62 86.out.57 92.27 89.05 95.77 89.43 90. null].07 87.89 94.86 97.76 90.28 87.17 87.55 94.58 91.61 88.92 91.69 96.54 95. null. and you get built-in array bounds checking.44 88.85.01 *///:~ As complex as this seems.67 88.85 97.Arrays.10 90. [null.38 94.26 90. null.77 88. null.96 86.60 89. null]]. [null.78 86.56 93. null]. import java. null]. it’s still simpler than doing it in C or C++.java /****************** Exercise 5 ***************** * Demonstrate that multidimensional arrays of * non-primitive types are automatically initialized * to null.63 85.08 92.03 97.45 86.36 97.util. null. null]].04 96.79 85.21 95.09 91.75 91. null.91 92.13 85. null]]] *///:~ Arrays 383 .71 95. [null.41 92.88 95.34 88.18 98.12 86.68 98.52 97.22 93.25 91. Exercise 5 //: arrays/E05_NonPrimitiveMultiDArray. [null. null]. if you run off the end of a multidimensional array. public class E05_NonPrimitiveMultiDArray { public static void main(String[] args) { System.deepToString(new Object[3][3][3])).95 87.19 97. } } /* Output: [[[null.87 96.59 90.90 93.37 96.20 96. In C/C++.39 94.35 98.46 85.94 89.11 87. null. [null.10 89. [[null. null. you probably won’t catch the error.02 98.74 92.93 90. [null. null]. null.println( Arrays. util.Arrays. k < zLen. int zLen) { BerylliumSphere[][][] a = new BerylliumSphere[xLen][yLen][zLen]. Sphere 2]. ***********************************************/ package arrays.Exercise 6 //: arrays/E06_Filling2DArray. Sphere 5]. * indicating the two sizes of a 2-D array. public class E07_Filling3DArray { static BerylliumSphere[][][] fill(int xLen. int yLen) { BerylliumSphere[][] a = new BerylliumSphere[xLen][yLen].out. for(int i = 0. 4th Edition Annotated Solution Guide . j < yLen. k++) 384 Thinking in Java.deepToString(fill(3.println(Arrays. [Sphere 3. i++) for(int j = 0.Arrays. j < yLen. The * method should create and fill a 2-D array of * BerylliumSphere according to the size arguments. for(int i = 0. Sphere 7. j++) a[i][j] = new BerylliumSphere(). 3))). j++) for(int k = 0. int yLen. import java. i < xLen.java /****************** Exercise 7 ***************** * Repeat the previous exercise for a 3-D array. } public static void main(String[] args) { System. } } /* Output: [[Sphere 0. [Sphere 6. i < xLen. i++) for(int j = 0. Sphere 1. Sphere 8]] *///:~ Exercise 7 //: arrays/E07_Filling3DArray.util. Sphere 4. return a. public class E06_Filling2DArray { static BerylliumSphere[][] fill(int xLen. ***********************************************/ package arrays.java /****************** Exercise 6 ***************** * Write a method that takes two int arguments. import java. Sphere 17]]. [Sphere 15. However. * I can put anything except primitive types in that * array. Sphere 23]. Sphere 2]. [Sphere 24. public class E08_LeftToReader { public static void main(String args[]) { System. Sphere 1. } public static void main(String[] args) { System." * * Demonstrate the assertions in the previous * paragraph. Sphere 14]. [Sphere 12.java /****************** Exercise 8 ***************** * "Erasure gets in the way again—this example * attempts to create arrays of types that have been * erased.deepToString(fill(3. Sphere 11].out.println(Arrays. Sphere 26]]] *///:~ Exercise 8 //: arrays/E08_LeftToReader. [Sphere 3.out. Java will enforce at both compile time * and run time that I can only place String objects * in that array. ***********************************************/ package arrays. [[Sphere 18. Sphere 22. Sphere 16. } } /* Output: [[[Sphere 0. Sphere 13. and cast it. return a. * but you get an “unchecked” warning at compile * time because the array doesn’t really hold or * dynamically check for type T. [Sphere 21. and are thus unknown types. Sphere 25. Notice that * you can create an array of Object. [[Sphere 9. if I create an Object[]. Sphere 19. Sphere 20]. } } ///:~ Arrays 385 . Sphere 8]]. Sphere 7. 3. [Sphere 6.println("Exercise left to reader"). That is. Sphere 4. 3))).a[i][j][k] = new BerylliumSphere(). if I create * a String[]. Sphere 5]. Sphere 10. ArrayList<Peel<Banana>> a = new ArrayList<Peel<Banana>>(). } } class Peel<T> { T fruit.out. class Banana { private final int id. } void peel() { System.add(new Peel<Banana>(new Banana(i))).peel(). for(Peel<Banana> p : a) p. ***********************************************/ package arrays. i < 10. } public String toString() { return getClass().println("Peeling "+ fruit). Banana(int id) { this.Exercise 9 //: arrays/E09_PeelBanana. Fix the problem using an ArrayList.id = id. } } /* Output: Peeling Banana 0 Peeling Banana 1 Peeling Banana 2 Peeling Banana 3 Peeling Banana 4 Peeling Banana 5 Peeling Banana 6 Peeling Banana 7 Peeling Banana 8 386 Thinking in Java.getSimpleName() + " " + id.util. import java. 4th Edition Annotated Solution Guide . } } public class E09_PeelBanana { public static void main(String[] args) { // Compile error: //! Peel<Banana>[] a = new Peel<Banana>[10].*. for(int i = 0.fruit = fruit.java /****************** Exercise 9 ***************** * Create the classes necessary for the Peel<Banana> * example and show that the compiler doesn’t accept * it. Peel(T fruit) { this. i++) a. public class E10_ArrayOfGenerics2 { public static void main(String[] args) { ArrayList<List<String>> ls = new ArrayList<List<String>>().add(new ArrayList<Integer>()).Peeling Banana 9 *///:~ Exercise 10 //: arrays/E10_ArrayOfGenerics2.java /****************** Exercise 10 ***************** * Modify ArrayOfGenerics. } } /* Output: [[Array of Generics]] *///:~ Exercise 11 //: arrays/E11_AutoboxingWithArrays. 2. 4.get(0). ls.println(ls. int[] pb = wb. public class E11_AutoboxingWithArrays { public static void main(String[] args) { int[] pa = { 1.java to use containers * instead of arrays. // Compile-time checking produces an error: //! ls. 3. import java.util.*. System.out.add("Array of Generics").toString()).add(new ArrayList<String>()). Show that you can eliminate * the compile-time warnings. } } ///:~ Arrays 387 . 4. ************************************************/ package arrays. ************************************************/ package arrays. Integer[] wa = pa. 3.java // {CompileTimeError} /****************** Exercise 11 ***************** * Show that autoboxing doesn’t work with arrays. Integer[] wb = { 1. 5 }. 2. ls. 5 }. Character.class. new CountingGenerator.0.0. ************************************************/ package arrays. 6.out. 388 Thinking in Java. Print the results. 4.println(s).Exercise 12 //: arrays/E12_GeneratedDArray.*.out.0. 8. import java.mindview. 9. 12.java /****************** Exercise 12 ***************** * Create an initialized array of double using * CountingGenerator.0.0. } } /* Output: [0. 1.0.0.Arrays.*.toString(d)).0.Character.String automatically uses CountingGenerator.println(Arrays.primitive(Generated. 13.util.0. 5. import net.0] *///:~ Exercise 13 //: arrays/E13_StringFill. 7. public class E12_GeneratedDArray { public static void main(String[] args) { double[] d = ConvertTo.Double().0.0. public class E13_StringFill { public static void main(String[] args) { String s = new CountingGenerator.java /****************** Exercise 13 ***************** * Fill a String using CountingGenerator.util. 4th Edition Annotated Solution Guide .array( Double. System. 2. } } /* Output: abcdefghijklmno *///:~ Since CountingGenerator.String(15).0.0. 15)).next(). we just use the former class’s next( ) method to perform the task.util. 14. 3. ************************************************/ package arrays.mindview. 11. System. import net.0. 10. reflect. (Long)gen.TYPE) Array.next()).TYPE) Array. (Integer)gen.TYPE) Array. i.util.*. class Fill { public static void primitive(Object array. else if (type == Short. else if (type == Float. i++) if(type == Boolean. i < size. Generator<?> gen) { int size = Array. (Boolean)gen.TYPE) Array. (Byte)gen.setBoolean(array. for(int i = 0.Arrays. i. } } public class E14_PrimitiveArraysFill { public static void main(String[] args) { int size = 6. import static net. i.TYPE) Array.getClass().setDouble(array. else if (type == Integer. (Short)gen. else if (type == Double.TYPE) Array. i.next()).mindview. Class<?> type = array.setInt(array.lang.mindview. import java.next()).getLength(array). then * fill each array by using CountingGenerator. import net.setChar(array. char[] a3 = new char[size]. import java.next()).getComponentType().java /****************** Exercise 14 ***************** * Create an array of each primitive type. else if (type == Byte.Print.next()). i. ************************************************/ package arrays.*.Exercise 14 //: arrays/E14_PrimitiveArraysFill.util. i.util. // First create all primitive arrays boolean[] a1 = new boolean[size]. byte[] a2 = new byte[size]. * Print each array. i.Array.setLong(array.setByte(array. i.setFloat(array.next()). (Double)gen.TYPE) Array.TYPE) Array. else if (type == Character. else if (type == Long.next()).setShort(array. Arrays 389 . (Character)gen.next()). (Float)gen. Fill. 3.short[] a4 = new short[size]. f] a4 = [0. Fill.0.toString(a3)).primitive(a1.Short()).primitive(a2.primitive( ) to fill a primitive array with data from the appropriate Generator.toString(a2)). new CountingGenerator.0. but this one highlights Java’s dynamicity.java 390 Thinking in Java.primitive(a4.Double()). int[] a5 = new int[size]. print("a6 = " + Arrays. 1. 3.0.primitive(a5. print("a5 = " + Arrays. Note that you cannot invoke the overloaded Generated. new CountingGenerator.toString(a7)). 5] a6 = [0.0] *///:~ We use Fill.toString(a8)). new CountingGenerator. 3. // Now fill them using a matching generator Fill. true.toString(a4)). 1. } } /* Output: a1 = [true.primitive(a6. c. new CountingGenerator.Integer()). float[] a7 = new float[size]. 5] a3 = [a.0. It also illustrates how powerful Java can be if you leverage its generics and reflection features. 5] a7 = [0.Long()). 2. 2.array( ) method directly.toString(a1)). long[] a6 = new long[size]. 2.Float()). 1.Character()). d. 3. Fill. double[] a8 = new double[size]. print("a8 = " + Arrays. false.Byte()). 5. new CountingGenerator.toString(a5)).primitive(a7. 1. There are other ways to solve the exercise. b.0. print("a7 = " + Arrays. 4. 5] a5 = [0. 4th Edition Annotated Solution Guide . Fill. false] a2 = [0. 4.0. 2. 1. 4.primitive(a8. print("a2 = " + Arrays. 5.0. Fill. because it does not support primitive types and autoboxing does not work with arrays. print("a1 = " + Arrays.0.primitive(a3.0. new CountingGenerator. print("a4 = " + Arrays. 2.0] a8 = [0. 4. 1. true. new CountingGenerator.toString(a6)). 3. Fill. new CountingGenerator.Boolean()). 2. 4.0. 4. e. Exercise 15 //: arrays/E15_BSContainerComparison. Fill. 3. print("a3 = " + Arrays. false. Sphere 8. 3. class BSGenerator implements Generator<BerylliumSphere> { public BerylliumSphere next() { return new BerylliumSphere().java by creating a * Generator for BerylliumSphere.array(BerylliumSphere. import java.asList(0. } } public class E15_BSContainerComparison { public static void main(String[] args) { BSGenerator gen = new BSGenerator(). 3. List<BerylliumSphere> sphereList = Arrays. Sphere 7. print(integers[4]). 5)). 5 }.Print. 2. import static net. int[] integers = { 0. ************************************************/ package arrays.mindview. Sphere 4] Sphere 4 [Sphere 5.*. Sphere 6. Sphere 3.toString(spheres)). 97] Arrays 391 .add(97). 1. print(sphereList). and change main() * to use that Generator with Generated. import net. 4.util. 5] 4 [0. Sphere 9] Sphere 9 [0.class.array( BerylliumSphere. print(intList). 1. 4. print(sphereList.toString(integers)). 4. 1. print(intList.*. 5. Sphere 2. gen. intList. List<Integer> intList = new ArrayList<Integer>( Arrays. print(spheres[4]).array(). 5)). 3. 2.util.*. gen. print(Arrays.get(4)).asList( Generated. 4. 2. 2.class.mindview./****************** Exercise 15 ***************** * Modify ContainerComparison. 1. } } /* Output: [Sphere 0. 5). BerylliumSphere[] spheres = Generated.get(4)). 3.util. print(Arrays. Sphere 1. toCharArray(). 4th Edition Annotated Solution Guide .Boolean next() { value = step ? !value : value. } } static char[] chars = ("abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ"). return oldValue.Byte next() { byte oldValue = value.java to show that your * new class works correctly.util. } } public static class Byte implements Generator<java.*. private byte step. create a * SkipGenerator class that produces new values by * incrementing according to a constructor argument. value += step. * Modify TestArrayGeneration.lang.mindview.step = step. private boolean step. } public java.*.Character> { int index. 392 Thinking in Java. public static class Character implements Generator<java.step = step.Byte> { private byte value.java. private int step.*.mindview. import java.lang.lang. public Boolean(boolean step) { this. import net.java /****************** Exercise 16 ***************** * Starting with CountingGenerator.lang. return value. import static net.Boolean> { private boolean value.lang. ************************************************/ package arrays. } public java. class SkipGenerator { public static class Boolean implements Generator<java.util. public Byte(byte step) { this.util.4 *///:~ Exercise 16 //: arrays/E16_SkipGenerator.Print. public Short(short step) { this.String> { private int length. } public java. return oldValue.Character> cg.lang.Integer next() { int oldValue = value. private int step.String(buf).lang. int length) { this. return oldValue. } } public static class Short implements Generator<java.step = step.step = step. 7).lang. return oldValue. return new java.lang. Generator<java.Character next() { char oldValue = chars[index]. public String(int step) { this(step.Long> { Arrays 393 . for(int i = 0.Integer> { private int value.lang.lang. } public java. i < length.String next() { char[] buf = new char[length].public Character(int step) { this. } public String(int step. } } public static class Long implements Generator<java.lang. value += step. index = (index + step) % chars.lang. i++) buf[i] = cg.lang. } } public static class Integer implements Generator<java. value += step.length.step = step. } public java. private short step.lang. } public java. } } public static class String implements Generator<java. cg = new Character(step).Short next() { short oldValue = value.next(). public Integer(int step) { this.length = length.Short> { private short value. public Float(float step) { this.toString(a3)). short[] a4 = ConvertTo.Float> { private float value. } public java. char[] a3 = ConvertTo.lang.class.step = step. value += step.Long next() { long oldValue = value. } } } public class E16_SkipGenerator { public static void main(String[] args) { boolean[] a1 = ConvertTo.class. } } public static class Float implements Generator<java. return oldValue. } public java.primitive(Generated.array( Byte. 6)). new SkipGenerator. return oldValue. 6)). new SkipGenerator. 394 Thinking in Java.class.toString(a2)). 6)).Double next() { double oldValue = value.lang. public Double(double step) { this. public Long(long step) { this. byte[] a2 = ConvertTo.array( Boolean. value += step. private long step. private double step. new SkipGenerator.step = step.lang.array( Character. print("a3 = " + Arrays.Byte((byte)1).class.Boolean(true). 6)). value += step.Character(2).lang.Short((short)3).toString(a1)). return oldValue.primitive(Generated. print("a1 = " + Arrays.primitive(Generated.lang. } public java. private float step.Double> { private double value. print("a2 = " + Arrays.array( Short. new SkipGenerator. } } public static class Double implements Generator<java.primitive(Generated. 4th Edition Annotated Solution Guide .private long value.Float next() { float oldValue = value.step = step. print("a7 = " + Arrays. float[] a7 = ConvertTo.toString(a5)). new SkipGenerator.class.array( Long. new SkipGenerator. i. private BigDecimal step.step = step.print("a4 = " + Arrays.0. 16. import java.toString(a4)). class BigDecimalGenerator implements Generator<BigDecimal> { private BigDecimal value = new BigDecimal(0). 5] a3 = [a.0.class.class. 3.0.0).array( Double. 3. c.primitive(Generated.0. 10.5.util. 6)).0.5. 12. 7. 15] a5 = [0. 6. 1. 3.primitive(Generated. print("a5 = " + Arrays. import java. 9. 15. 8.array( Integer. 10. true. 8.toString(a8)).Double(2. 12. false. 20. ************************************************/ package arrays.math. 4. print("a8 = " + Arrays. k] a4 = [0.Integer(4).toString(a7)).mindview. new SkipGenerator. 4.primitive(Generated.5] a8 = [0. double[] a8 = ConvertTo. 6.java /****************** Exercise 17 ***************** * Create and test a Generator for BigDecimal.array( Float.0. } public BigDecimal next() { BigDecimal oldValue = value. return oldValue.util.0] *///:~ Exercise 17 //: arrays/E17_BigDecimalGenerator. 6)). 25] a7 = [0. 4.*. value = value.toString(a6)). e. 6)).0. 5. false. 6)). 6. g.add(step).class. BigDecimalGenerator(BigDecimal step) { this. 1. print("a6 = " + Arrays. true. new SkipGenerator. } } /* Output: a1 = [true. import net. 2.primitive(Generated. int[] a5 = ConvertTo.*.0. 4.Long(5l).Float(1. and * ensure that it works with the Generated methods. 2.5f). false] a2 = [0. } Arrays 395 .*. long[] a6 = ConvertTo. 20] a6 = [0. a = Generated. value).4.8. 1.*. 0. 1. long value) { try { Field fid = BerylliumSphere.java /****************** Exercise 18 ***************** * Create and fill an array of BerylliumSphere.array(BigDecimal.out. new BigDecimal(7).*.getDeclaredField("id").array(a.Print. System.8] *///:~ Exercise 18 //: arrays/E18_ArrayCopy. 1.} public class E17_BigDecimalGenerator { public static void main(String[] args) { BigDecimal[] a = { new BigDecimal(9).toString(b)). 0. 2.setAccessible(true). import java.8. 0. 0.6. 2. 15).println(Arrays.setLong(bs.6.1.2. public class E18_ArrayCopy { // BerylliumSphere.id is private. } catch(Exception e) { e. fid. 0.println(Arrays. 1.util. fid. new BigDecimal(8). 2. } 396 Thinking in Java.0. ************************************************/ package arrays. 2.lang.toString(a)).println(Arrays. 7.reflect.0.4.class.2")). 6] [0.class.2.6. System. * Copy this array to a new array and show that it’s * a shallow copy. 8.3] [0.mindview. new BigDecimalGenerator( new BigDecimal("0. import static net.util. static void setID(BerylliumSphere bs. new BigDecimal(6) }.printStackTrace(). 0.2. 4th Edition Annotated Solution Guide .1"))). 2. BigDecimal[] b = Generated.toString(a)). new BigDecimalGenerator(new BigDecimal("0.*. System.out. 1. so we need to use // reflection to alter its value.4. import java.2.out. } } /* Output: [9. 0. java /****************** Exercise 19 ***************** * Create a class with an int field that’s initialized * from a constructor argument. Create two arrays * of these objects. ************************************************/ package arrays. Arrays. Sphere 1] b = [Sphere 0.util. setID(a[0]. // Changing a reference in 'a' will not impact 'b'.*. Sphere 0. Sphere -1. DataHolder(int data) { this. import java. Sphere 0. Sphere 0] b = [null.toString(b)). a[1] = a[3] = new BerylliumSphere(). class DataHolder { protected int data.toString(b)). Sphere -1. b. } } /* Output: a = [Sphere 0.toString(b)). Sphere -1] *///:~ Exercise 19 //: arrays/E19_ArrayEquals.fill(a. new BerylliumSphere()). Add an equals() method * to your class to fix the problem. print("a = " + Arrays. Sphere 0. Sphere -1. and show that Arrays. Sphere 0] a = [Sphere 0. null] b = [Sphere 0.toString(a)).} public static void main(String[] args) { BerylliumSphere[] a = new BerylliumSphere[4].arraycopy(a.data = data. Sphere 0. print("a = " + Arrays. print("a = " + Arrays.toString(a)). } } Arrays 397 . -1L). Sphere 0. 0.toString(a)). as well. System. print("b = " + Arrays.toString(b)). null. print("b = " + Arrays. 0. Sphere 0. BerylliumSphere[] b = new BerylliumSphere[4]. // Changing an object's state will impact 'b'. Sphere 1. Sphere 0] a = [Sphere -1. b. print("b = " + Arrays. print("b = " + Arrays. using identical initialization * values for each array.equals() * says that they are unequal. Sphere 1] b = [Sphere -1.length). null. Sphere 0. Sphere 1. } public boolean equals(Object o) { return o instanceof DataHolderWithEquals && data == ((DataHolder)o). System.*. Arrays. a2)). 3}. Integer[][] table3 = 398 Thinking in Java.equals(a1. {4. } } public class E19_ArrayEquals { public static void main(String[] args) { DataHolder[] a1 = new DataHolder[5]. public class E20_ArrayDeepEquals { public static void main(String[] args) { int[][] table1 = {{1. {7. new DataHolder(1)). 2.fill(a2.println(Arrays. 2. ************************************************/ package arrays. 9. 4th Edition Annotated Solution Guide . 9. Arrays.out.println(Arrays. 10}}.java /****************** Exercise 20 ***************** * Demonstrate deepEquals() for multidimensional * arrays.equals(a1. {7.fill(a1. 5}. System. int[][] table2 = {{1.out.fill(a1. Arrays.data. 5}. {4. new DataHolder(1)). import java.util. 8. 3}. Arrays. 8.fill(a2. new DataHolderWithEquals(1)). 10}}. } } /* Output: false true *///:~ Exercise 20 //: arrays/E20_ArrayDeepEquals. a2)). DataHolder[] a2 = new DataHolder[5]. new DataHolderWithEquals(1)).class DataHolderWithEquals extends DataHolder { DataHolderWithEquals(int data) { super(data). deepEquals(table1. 10}}. } } /* Output: true false false true *///:~ Arrays.deepEquals(table1.out. Arrays 399 . Implement Comparable to fix the problem. 9. // Let us check manually for equality between table1 and // table3 boolean res = true. 3}.length. int[][] table4 = {{1. 5.” transparent autoboxing produces the desired outcome. 8.println(Arrays. System. table3)). exit_loop: for(int i = 0. table2)). table4)). i < table1. {7.println(Arrays. 5}.deepEquals(table1.println(Arrays.out.println(res). System. System. 4}. import java. {4. {7. Now * create a Comparator to sort the objects into reverse * order. though they are semantically the same. because the compiler sees them as different. break exit_loop.length. } System.*.util. i++) for(int j = 0.out.deepEquals( ) does not report table1 and table3 as equal. {6.out.{{1.java /****************** Exercise 21 ***************** * Try to sort an array of the objects in Exercise * 18. Exercise 21 //: arrays/E21_ArraySort. 2. when we compare the two arrays “manually. ************************************************/ package arrays. j < table1[i]. j++) if(table1[i][j] != table3[i][j]) { res = false. 8}}. 3}. 2. However. getLong(bs). 4th Edition Annotated Solution Guide .id is private. sort 3: a = " + Arrays.printStackTrace(). print("Before sort 2: a = " + Arrays. try { Arrays.asList(a). return 0L.import java.toString(a)).class.shuffle(Arrays. static long getID(BerylliumSphere bs) { try { Field fid = BerylliumSphere.mindview. r). fid.*. long rvid = getID(rv). Collections. print("After sort 1: a = " + Arrays. import net. } for(int i = 0.*. 400 Thinking in Java. i < a. print("After sort 2: a = " + Arrays.toString(a)).shuffle(Arrays.asList(a). Arrays.toString(a)). } catch(ClassCastException e) { System.toString(a)). } catch(Exception e) { e.util.util. // Bogus value } } public int compareTo(BerylliumSphere rv) { long id = getID(this). i++) a[i] = new ComparableBerylliumSphere(). Collections.shuffle(Arrays. so we need to use // reflection to get its value. class ComparableBerylliumSphere extends BerylliumSphere implements Comparable<BerylliumSphere> { // BerylliumSphere.out.setAccessible(true). } } public class E21_ArraySort { public static void main(String[] args) { Random r = new Random(47). new BSGenerator().lang. r).*.Print. Collections.mindview. r).toString(a)).getDeclaredField("id"). 5). return fid. print("Before sort 1: a = " + Arrays.asList(a). import static net. return (id < rvid ? -1 : (id == rvid ? 0 : 1)).length.sort(a).reflect.class. BerylliumSphere[] a = Generated.sort(a).println("Array cannot be sorted!").array( BerylliumSphere. print("Before rev. Sphere 5. if(location >= 0) print(". int[] a = ConvertTo. else print().mindview. int location = Arrays. Sphere 7. print("Unsorted array: " + Arrays. Arrays 401 .util. Collections. sort 3: a = [Sphere 8. Sphere 5. Sphere 7. ************************************************/ package arrays. public class E22_ArrayBinarySearch { public static void main(String[] args) { Generator<Integer> gen = new RandomGenerator. Sphere 6.toString(a)). a[" + location + "] = " + a[location]). printnb("Location of " + a[10] + " is " + location).*. gen)). Sphere 6.*. import static net. Sphere 4.toString(a)).util. } } /* Output: Before sort 1: a = [Sphere 2. sort 3: a = [Sphere 9. sort 3: a = " + Arrays. Sphere 6. Sphere 9] Before rev. Sphere 8. Sphere 0. Sphere 7.Arrays.primitive( Generated.mindview.sort(a.java /****************** Exercise 22 ***************** * Show that the results of performing a * binarySearch() on an unsorted array are * unpredictable. Sphere 9.reverseOrder()). Sphere 5] *///:~ Sorting an array of objects without the Comparable interface triggers ClassCastException (as the output shows). Sphere 3] Array cannot be sorted! Before sort 2: a = [Sphere 8. Sphere 7] After rev.*. a[10]).util. print("After rev.binarySearch(a.Print.Integer(1000). Sphere 1. Exercise 22 //: arrays/E22_ArrayBinarySearch. import java. Sphere 9. Sphere 6] After sort 2: a = [Sphere 5. import net.array(new Integer[25]. Sphere 8. if(location >= 0) print(". 207. 861. 278. 704] Location of 288 is -2 Location of 429 is 5. 200. 868. 429. 704] Sorted array: [998. 258. 258. 200.util. import java.*. a[" + location + "] = " + a[location]).array(new Integer[25]. a[5] = 429 *///:~ Clearly. print("Unsorted array: " + Arrays. 322. Arrays. ************************************************/ package arrays. 861. 961. 809. 589. 511. 520. 693. public class E23_ArraySort2 { public static void main(String[] args) { Generator<Integer> gen = new RandomGenerator. 258. 589. 278. 809. fill it with random * int values (using autoboxing). you cannot trust a binary search of an unsorted array. 809. 288.util.*. 511. 916.mindview. 200. 4th Edition Annotated Solution Guide . 961. 589. 322. Integer[] a = Generated.util.*.mindview.location = Arrays. 288. 861. Collections. 861. 522. 322. 861. 961. 522. 868. gen). 551. 522. a[5]). and sort it into * reverse order using a Comparator. 511. 140. 128. 258.java /****************** Exercise 23 ***************** * Create an array of Integer. 140. 140.sort(a.reverseOrder()). 693. 128. 429. 278. } } /* Output: Unsorted array: [258. 128] *///:~ 402 Thinking in Java. 207. printnb("Location of " + a[5] + " is " + location). 693. 551. import static net.toString(a)). Our search finds the sixth element but not the eleventh. 288. } } /* Output: Unsorted array: [258. print("Sorted array: " + Arrays.toString(a)). import net. 520. 998. 868. Exercise 23 //: arrays/E23_ArraySort2. 916.Integer(1000). 704. 998. 429. 861.Print. 916.binarySearch(a. 555. 551. 207. 555. 555. 520. util. Exercise 24 //: arrays/E24_ArraySearch. comp). public class E24_ArraySearch { public static void main(String[] args) { Comparator<DataHolder> comp = new Comparator<DataHolder>() { public int compare(DataHolder o1. printnb("Location of " + a[7] + " is " + location). location = Arrays. a[7] = arrays. i < a.binarySearch(a. else print(). int location = Arrays.data ? 0 : 1)).DataHolderWithEquals@a90653 is 7. import static net. a[" + location + "] = " + a[location]).length.java /****************** Exercise 24 ***************** * Show that the class from Exercise 19 can be * searched. DataHolder[] a = new DataHolderWithEquals[10]. printnb("Location of " + a[5] + " is " + location). a[" + location + "] = " + a[location]). if(location >= 0) print(".data == o2. Arrays.data < o2.binarySearch(a. Note that autoboxing from int to Integer happens behind the scenes in the RandomGenerator.mindview. but arrays filled with wrapper objects do.Print.*. comp).util. for(int i = 0. } }.*.DataHolderWithEquals@a90653 Location of arrays. } } /* Output: (83% match) Location of arrays. a[5]. a[7].DataHolderWithEquals@de6ced *///:~ Arrays 403 .Primitive arrays do not allow sorting with a Comparator.sort(a.DataHolderWithEquals@de6ced is 5. if(location >= 0) print(". ************************************************/ package arrays. i++) a[i] = new DataHolderWithEquals(i). DataHolder o2) { return (o1.Integer class. comp).data ? -1 : (o1. a[5] = arrays. import java. though it isn’t very efficient.py in Java.mindview. at a minimum. It iterates over the elements in the collection.previous()). print(aList).asList(7. 4.subList(2.*.util. List<Integer> aSlice = aList.java /****************** Exercise 25 ***************** * Rewrite PythonLists. } List<T> getReversed() { List<T> reversed = new MyArrayList<T>(size()). import static net. 4). ListIterator<T> it = listIterator(size()).get(4)).add(6). print(aList. aList. public class E25_PythonLists { public static void main(String[] args) { List<Integer> aList = new ArrayList<Integer>(Arrays. print(aSlice).util.*. ************************************************/ package arrays.You must supply the proper Comparator to perform the search in Arrays. Exercise 25 //: arrays/E25_PythonLists. } MyArrayList(int initialCapacity) { super(initialCapacity). checking each element in turn for equality with the specified element—a cumbersome method for large collections.getSimpleName()).binarySearch( ).addAll(Arrays. 4th Edition Annotated Solution Guide . print(aList). 5)). 3.sort( ) and Arrays.asList(1.getClass(). 8)). To search for a specific instance of that class you can use indexOf( ) method (part of the List interface). use the equals( ) method to make a class searchable. import java. print(aList.hasPrevious()) reversed. 2.add(it. while(it. } } MyArrayList<Integer> list2 = 404 Thinking in Java. return reversed.Print. aList. class MyArrayList<T> extends ArrayList<T> { MyArrayList(Collection<? extends T> c) { super(c). You must. new MyArrayList<Integer>(aList). 7. 2. Python has a more concise way of expressing this. 4. 2. 2. 4. 6. print(list2. 6. print(list2.getClass(). 3. 1] *///:~ The sophisticated collection classes in java. 8] [3.getSimpleName()). you don’t manually code the different constructors. 7. Arrays 405 . } } /* Output: ArrayList [1. 4.util make the conversion trivial. 3.getReversed()). for example. 5] 5 [1. 5. 3. but rather inherit them automatically (unlike in Java). 5. 4] MyArrayList [8. . print("sorted: " + l). import java. BURKINA FASO. BENIN. BURUNDI. BURUNDI. BURKINA FASO] *///:~ 407 . CAPE VERDE. BOTSWANA. ANGOLA. BENIN] shuffled (4): [BENIN. CAMEROON.*.util. CAMEROON. CAPE VERDE. ANGOLA. } } } /* Output: sorted: [ALGERIA.util.shuffle(l. BURUNDI. BOTSWANA.util. CAMEROON. CAPE VERDE] shuffled (1): [BURKINA FASO. BOTSWANA. ALGERIA] shuffled (3): [ALGERIA. public static void main(String[] args) { List<String> l = new ArrayList<String>(names(8)).sort(l).Countries.Print. BURKINA FASO. BURUNDI] shuffled (2): [BENIN. i < 5. BURUNDI. Collections. ALGERIA. print("shuffled (" + i + "): " + l). then apply Collections. rnd). ANGOLA. BURKINA FASO. ANGOLA. public class E01_CountryList { private static Random rnd = new Random(47).*. import static net. CAPE VERDE. printing it each time so you * can see how the shuffle() method randomizes the * list differently each time.shuffle() to the * list repeatedly. CAPE VERDE.mindview.*.java /****************** Exercise 1 ****************** * Create a List (try both ArrayList and LinkedList) * and fill it using Countries. ANGOLA. BOTSWANA. Sort the list and * print it.Containers in Depth Exercise 1 //: containers/E01_CountryList. CAMEROON. CAMEROON. for(int i = 1. BENIN. i++) { Collections. import static net. BOTSWANA. ************************************************/ package containers.mindview. ALGERIA. The methods headMap( ) and headSet( ) 408 Thinking in Java. AUSTRIA=Vienna. ANTIGUA AND BARBUDA.out. String b = null.startsWith("B")) { b = s. ARGENTINA.String> map = new TreeMap<String. ALBANIA=Tirana. filling both with the country information. ANDORRA. ANGOLA.mindview. AZERBAIJAN=Baku} aSet = [AFGHANISTAN. } Map<String. break. ALBANIA.util. AUSTRALIA.println("aSet = " + aSet). ARMENIA=Yerevan. for(String s : map. ANGOLA=Luanda.*. AUSTRALIA=Canberra. ANDORRA=Andorra la Vella. and you can change the solution in only one place to solve it using LinkedList. Exercise 2 //: containers/E02_ACountries.headMap(b). import static net. Then it finds the first string that doesn’t begin with ‘A.Countries.out. ALGERIA.String> aMap = map. Set<String> aSet = set. AZERBAIJAN] *///:~ You solve the problem easily by recognizing that the compiler sorts Map and Set trees automatically.println("aMap = " + aMap).headSet(b). ARGENTINA=Buenos Aires. public class E02_ACountries { public static void main(String[] args) { TreeMap<String. ARMENIA.We solve the problem for ArrayList. 4th Edition Annotated Solution Guide .java /****************** Exercise 2 ****************** * Produce a Map and a Set containing all the * countries that begin with 'A'. } } /* Output: aMap = {AFGHANISTAN=Kabul.util. ANTIGUA AND BARBUDA=Saint John's. import java.keySet()) if(s. ALGERIA=Algiers. TreeSet<String> set = new TreeSet<String>(names()).*. ************************************************/ package containers. System.’ using an iterator from either container (since both sort themselves). AUSTRIA.String>(capitals()). System. BENIN. LinkedHashSet. CAMEROON. import java. BOTSWANA. which adds the contents of the List to itself. We solve for LinkedHashSet. import static net. Try this with * HashSet.java program from the Holding Your Objects chapter). and TreeSet.mindview. } } /* Output: [ALGERIA. ************************************************/ package containers. BURKINA FASO. Exercise 4 TIJ4 already provides a solution for this exercise (see holding/UniqueWords.*. i++) set. fill a Set with the same data * multiple times.util. CHAD] *///:~ Note that the output lists each country only once. for(int i = 0.*. then verify that the Set ends up * with only one of each instance. Exercise 3 //: containers/E03_VerifySet. but just one change in the code solves for HashSet and TreeSet. Exercise 5 //: containers/E05_CountingMapData2.Countries. BURUNDI. CAPE VERDE. ANGOLA.util. You can find descriptions of both in the JDK HTML documentation. the compiler hands TextFile’s data to the TreeSet constructor. public class E03_VerifySet { public static void main(String[] args) { Set<String> set = new LinkedHashSet<String>().addAll(names(10)).out.println(set). There.(belonging to TreeMap and TreeSet) create subsets using this pattern.java /****************** Exercise 3 ****************** * Using Countries. CENTRAL AFRICAN REPUBLIC.java /****************** Exercise 5 ****************** Containers in Depth 409 . i < 5. System. The output differs because each Set orders items differently. } private class Entry implements Map. ************************************************/ package containers.split(" ").Entry<Integer.index.length] + Integer. Entry(int index) { this.index++. public boolean hasNext() { return entry.java to fully implement * the flyweight by adding a custom EntrySet class * like the one in Countries.Entry<Integer. public CountingMapData(int size) { if(size < 0) this. import java. } public String setValue(String value) { throw new UnsupportedOperationException(). } public Map.size = 0.index = index. } public int hashCode() { return Integer.String>> { public int size() { return size.String> { int index. 410 Thinking in Java.index < size .length). } public String getValue() { return chars[index % chars. 4th Edition Annotated Solution Guide . } } class EntrySet extends AbstractSet<Map.String>> { private Entry entry = new Entry(-1). } public boolean equals(Object o) { return o instanceof Entry && index == ((Entry)o).Entry<Integer.util. } private class Iter implements Iterator<Map.valueOf(index).Entry<Integer.hashCode().1.size = size.toString(index / chars.*. class CountingMapData extends AbstractMap<Integer. this. private static String[] chars = "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" .* Modify CountingMapData. } public Integer getKey() { return index.String> { private int size.java.String> next() { entry. 53=B2. 47=V1. 41=P1. } } public class E05_CountingMapData2 { public static void main(String[] args) { System. 27=B1. 7=H0.String>> iterator() { return new Iter(). 42=Q1. Exercise 6 //: containers/E06_UnsupportedList. 17=R0. 13=N0. 40=O1. 10=K0. 32=G1. so you don’t need a constructor to specify the size of the returned chunk of data. 26=A1. 19=T0. * Write a version of Unsupported. Entry and EntrySet are now inner classes and not nested ones (like in Countries. 16=Q0. } } private Set<Map. 33=H1.java). 25=Z0.Entry<Integer. 23=X0.Entry<Integer.out. 24=Y0. 55=D2. 49=X1. public Set<Map.Entry<Integer. EntrySet refers to the size instance field defined in its parent class. 34=I1. 36=K1. 29=D1. 59=H2} *///:~ 8=I0. 4=E0. 46=U1. 48=W1. 15=P0. 22=W0. 28=C1. } } public Iterator<Map. 11=L0. 51=Z1. 37=L1. 2=C0. 35=J1. 31=F1.String>> entries = new EntrySet().return entry. 6=G0. Containers in Depth 411 . 56=E2. 21=V0. 57=F2. 44=S1. 58=G2. 14=O0. 1=B0. 9=J0.println(new CountingMapData(60)).java that tests * these additional optional operations. } public void remove() { throw new UnsupportedOperationException(). 3=D0. 39=N1. 30=E1. 12=M0. 54=C2. 18=S0. 50=Y1. 20=U0. 52=A2.java /****************** Exercise 6 ****************** * Note that List has additional "optional" * operations that are not included in Collection. 5=F0.String>> entrySet() { return entries. } } /* Output: {0=A0. 43=R1. 45=T1. 38=M1. util.UnsupportedOperationException addAll(index. list).out.unmodifiableList( new ArrayList<String>(list))).subList(1. c): java.println("--.out. public class E06_UnsupportedList { static void test(String msg. new ArrayList<String>(list)).addAll(0. try { l. List<String> l) { System. element): java. 4th Edition Annotated Solution Guide .UnsupportedOperationException *///:~ Since we already presented set( ) in Unsupported.asList("A B C D E F G H I J K L".UnsupportedOperationException addAll(index.add(0." + msg + " ---").UnsupportedOperationException remove(index): java.split(" ")). test("unmodifiableList()". this program just demonstrates those overloaded methods that cannot be found in the Collections interface.println("addAll(index. } try { l. element): java.8)).out. subList). test("Arrays. } } public static void main(String[] args) { List<String> list = Arrays. List<String> subList = new ArrayList<String>(l.lang.Modifiable Copy ----.unmodifiableList() --add(index. } catch(Exception e) { System.UnsupportedOperationException --.remove(0). import java. } catch(Exception e) { System.lang.java. test("Modifiable Copy".lang.*. 412 Thinking in Java. Collections. "Test"). c): " + e).asList() --add(index.println("remove(index): " + e). } catch(Exception e) { System.lang.Arrays.asList()". element): " + e).************************************************/ package containers. c): java.println("add(index. } } /* Output: --.lang.UnsupportedOperationException remove(index): java.lang. } try { l.out. for(Iterator<String> it = al. lit2. alindex = 0. * Now perform the insertion starting at the end of * the first list and moving backward.Print. 20)).names() generator. lit2.listIterator(ll.next()).hasNext().names(10)).listIterator(). print("********").next()). import java. } Containers in Depth 413 .java /****************** Exercise 7 ***************** * Create both an ArrayList and a LinkedList.*. ***********************************************/ package containers. it. LinkedList<String> ll = new LinkedList<String>( Countries.util.util.) print(it.subList(10. alindex += 2.names(20).previous()). print("********").Exercise 7 //: containers/E07_CrossInsertion.) { al.add(alindex. // Start at the end: for(ListIterator<String> lit2 = ll.*. * Print each list using an ordinary Iterator. then * insert one list into the other by using a * ListIterator. import static net. lit2.iterator().*. print("********").add(alindex.mindview. public class E07_CrossInsertion { public static void main(String args[]) { ArrayList<String> al = new ArrayList<String>(Countries. it.iterator().util.mindview. int alindex = 0.hasNext(). and * fill each using the Countries.next()).hasPrevious().hasNext(). import net.) { al. } print("al = " + al).size()). inserting at every other location. for(ListIterator<String> lit2 = ll. lit2.) print(it. for(Iterator<String> it = ll. alindex += 2. DJIBOUTI. BURKINA FASO. EQUATORIAL GUINEA. ANGOLA. BOTSWANA. BENIN. GABON. THE GAMBIA. ETHIOPIA. CONGO.print("al = " + al). DJIBOUTI. THE GAMBIA. BURKINA FASO. CONGO. BURUNDI. EGYPT. ALGERIA. EGYPT. CAMEROON. 4th Edition Annotated Solution Guide . CENTRAL AFRICAN REPUBLIC. BENIN. EQUATORIAL GUINEA. BOTSWANA. THE GAMBIA. ERITREA. but incorrect sizes cause an exception. ANGOLA. } } /* Output: ALGERIA ANGOLA BENIN BOTSWANA BURKINA FASO BURUNDI CAMEROON CAPE VERDE CENTRAL AFRICAN REPUBLIC CHAD ******** COMOROS CONGO DJIBOUTI EGYPT EQUATORIAL GUINEA ERITREA ETHIOPIA GABON THE GAMBIA GHANA ******** al = [COMOROS. singly-linked list class 414 Thinking in Java. CAPE VERDE.java /****************** Exercise 8 ***************** * Create a generic. ERITREA. ETHIOPIA. Exercise 8 //: containers/E08_SList. CHAD] ******** al = [GHANA. GABON. CAPE VERDE. ETHIOPIA. GABON. GHANA. COMOROS. CENTRAL AFRICAN REPUBLIC. CONGO. CHAD] *///:~ Writing this solution for two lists of the same size is a little risky. ERITREA. COMOROS. EQUATORIAL GUINEA. BURUNDI. ALGERIA. DJIBOUTI. CAMEROON. GHANA. EGYPT. *.) { T element = it.util. return buf.valueOf(element)). interface SListIterator<T> { boolean hasNext(). which means it maintains links in both * directions).mindview. which. if(it. for(SListIterator<T> it = iterator(). Create your own SListIterator which. is a doubly-linked * list. "). * again for simplicity.next(). * Write code to demonstrate SList.util. ***********************************************/ package containers.Print.*. The only way to insert and remove * elements from an SList is through SListIterator. buf. * The only method in SList other than toString() * should be iterator(). which produces an * SListIterator. T next().next = header. void remove().* called SList.append("[").append(". SList() { header.hasNext()) buf. } buf.hasNext(). but not the previous * one (LinkedList. Each Link * object in the list should contain a reference to * the next element in the list. it.append("]"). import java. } public SListIterator<T> iterator() { return new SListIteratorImpl(). import static net. } class SList<T> { // Utilization of the 'Null Object' pattern private final Link<T> header = new Link<T>(null. in contrast. * does not implement the List interface. Containers in Depth 415 . } public String toString() { StringBuilder buf = new StringBuilder(). to keep things simple. does not implement ListIterator.append(element == this ? "(this SList)" : String. null). } private static class Link<T> { T element.toString(). buf. void add(T element). next = next. 4th Edition Annotated Solution Guide . this.next.element = element.next) if(curr.element. . } public T next() { if(next == header) throw new NoSuchElementException(). if(header. break. } } } } } 416 Thinking in Java.next == lastReturned) { curr. private Link<T> next.next. // Find an element before the last returned one for(Link<T> curr = header. } public void add(T element) { lastReturned = header. break. curr = curr.Link<T> next. } public void remove() { if(lastReturned == header) throw new IllegalStateException(). . else { // Find an element before the one pointed by 'next' for(Link<T> curr = header.next) if(curr.next == header) // Empty list header. return lastReturned. } } private class SListIteratorImpl implements SListIterator<T> { private Link<T> lastReturned = header. } lastReturned = header. Link(T element.next = lastReturned.next = newLink.next = newLink. SListIteratorImpl() { next = header. next = next. Link<T> next) { this. } public boolean hasNext() { return next != header.next. lastReturned = next. Link<T> newLink = new Link<T>(element.next == next) { curr. curr = curr. next). print(lit. slit.iterator(). lit. lit. lit = l. show some use cases for SListIterator print("Demonstrating SListIterator.next()). too print("\nDemonstrating ListIterator.listIterator()."). show the same use cases for ListIterator.next()). print(l). print(slit.iterator(). SListIterator<String> slit = sl.add("Four"). print(slit.hasNext()). for(. lit.add("One").next()). lit = l.hasNext(). lit. lit. print(sl).add("Three"). slit.. slit.listIterator(). slit. slit. [] false Containers in Depth 417 . slit = sl.public class E08_SList { public static void main(String[] args) { // First.next()). slit.add("Two"). print(lit. List<String> l = new ArrayList<String>().listIterator().) print(slit.add("Two"). lit. print(sl). print(slit.. // Now. } } /* Output: Demonstrating SListIterator. SList<String> sl = new SList<String>().hasNext().hasNext()). print(sl). print(sl). print(l).add("Four"). ListIterator<String> lit = l.iterator(). for(..remove(). print(lit.) print(lit..add("One").next()). slit = sl..add("Three"). print(l).").next()).. print(l).remove(). util. Three] Four One [One. The behavior of each operation in SListIterator partially meets the specifications in the JDK (which see). Two.. Two.. import java.[One. Two.java /****************** Exercise 9 ***************** * Use RandomGenerator. is sufficient for this demonstration. main( ) contains a test program to show you the similarity of these two interfaces. * but use alphabetic ordering. insertions and removals are especially timeconsuming. ***********************************************/ package containers. though much smaller than ListIterator’s. Two. Three] Four One [One. Three] One Two Three [Four. import net. Print the TreeSet * to verify the sort order. One. try to improve the speed. Three] Demonstrating ListIterator.*. [] false [One. public class E09_RandTreeSet { public static void main(String args[]) { 418 Thinking in Java. Three] One Two Three [Four. Three] *///:~ SList Iterator’s set of operations. 4th Edition Annotated Solution Guide .mindview.*. Also.util. One. Note that the code is not optimized. As an additional exercise. Two. Two.String to fill a TreeSet.) Exercise 9 //: containers/E09_RandTreeSet. (Hint: get rid of search loops. import static java. oEsuEcU. The set will not tolerate null elements.CASE_INSENSITIVE_ORDER). ***********************************************/ package containers. //: containers/E10_CustomSortedSet. class CustomSortedSet<T> implements SortedSet<T> { private final List<T> list.list = list.binarySearch. naMesbt. qUCBbkI. Assume the set is going to hold only objects of a class whose natural ordering is consistent with equals. YNzbrny] *///:~ Exercise 10 Before we delve into our solution.TreeSet<String> ts = new TreeSet<String>(String.out.Collections.java /****************** Exercise 10 ***************** * Using a LinkedList as your underlying * implementation.mindview. TcQrGse. import static net. We won’t optimize the code for speed (for which purpose LinkedList is unsuited anyway).addAll(CollectionData. System. } } /* Output: ts = [ahKcxrE. } // If this sorted set is backed by an another one private CustomSortedSet(List<T> list) { this. OneOEdL.Print.util. 10)).println("ts = " + ts).toString(). } /*** Methods defined in the Collection interface ***/ • • • Containers in Depth 419 .list( new RandomGenerator. import java.util. ts. smwHLGE.util. define your own SortedSet. GcFOWZn.*. public CustomSortedSet() { list = new LinkedList<T>().String().*. } public String toString() { return list. GZMmJMR. Our sorted set will have only one public no-arg constructor. so our SortedSet will use the elements’ natural ordering. we simplify the use of SortedSet with the following stipulations: • There is no need to support an additional Comparator. containsAll(c). } public boolean addAll(Collection<? extends T> c) { checkForNull(c). return list. return list.add(o). o). if(ip < 0) { ip = -(ip + 1). int ip = binarySearch((List<Comparable<T>>)list.size().iterator(). } public boolean removeAll(Collection<?> c) { checkForNull(c).toArray(a). } public Iterator<T> iterator() { return list. } public boolean remove(Object o) { checkForNull(o). } public Object[] toArray() { return list.public int size() { return list. if(ip == list. return list. return binarySearch((List<Comparable<T>>)list. } public boolean retainAll(Collection<?> c) { checkForNull(c). checkForNullElements(c). } @SuppressWarnings("unchecked") public boolean add(T o) { checkForNull(o). 4th Edition Annotated Solution Guide . return true. } return false. return res.add(ip. for(T item : c) res |= add(item).remove(o). o). } public boolean containsAll(Collection<?> c) { checkForNull(c). boolean res = false.removeAll(c). } public boolean isEmpty() { return list. (T)o) >= 0.toArray(). 420 Thinking in Java. } public <T> T[] toArray(T[] a) { return list.size()) list.isEmpty(). } @SuppressWarnings("unchecked") public boolean contains(Object o) { checkForNull(o). else list. indexOf(fromElement).return list.subList(0. toIndex)). } catch(IndexOutOfBoundsException e) { throw new IllegalArgumentException(e). } public SortedSet<T> subSet(T fromElement. try { return new CustomSortedSet<T>( list. } catch(IndexOutOfBoundsException e) { throw new IllegalArgumentException(e). toIndex)). } public void clear() { list. } public int hashCode() { return list. int fromIndex = list. } } public SortedSet<T> headSet(T toElement) { checkForNull(toElement). try { return new CustomSortedSet<T>( list. checkForValidIndex(toIndex). T toElement) { checkForNull(fromElement).indexOf(toElement). list.retainAll(c). checkForNull(toElement). } public boolean equals(Object o) { return o instanceof CustomSortedSet && list.subList(fromIndex.size())). } } public T first() { Containers in Depth 421 . int toIndex = list.subList(fromIndex. checkForValidIndex(toIndex). } } public SortedSet<T> tailSet(T fromElement) { checkForNull(fromElement). checkForValidIndex(fromIndex).indexOf(toElement). checkForValidIndex(fromIndex).indexOf(fromElement). try { return new CustomSortedSet<T>( list.equals(((CustomSortedSet<?>)o). int fromIndex = list.hashCode().clear(). } catch(IndexOutOfBoundsException e) { throw new IllegalArgumentException(e). int toIndex = list. } /*** Methods defined in the SortedSet interface ***/ public Comparator<? super T> comparator() { return null.list). iterator(). print(sortedSet).next(). } } public class E10_CustomSortedSet { // The whole main() method is basically copy-pasted from // containers/SortedSetDemo. print(low).next() == null) throw new NullPointerException().java! public static void main(String[] args) { SortedSet<String> sortedSet = new CustomSortedSet<String>().next(). } } /*** Utility methods ***/ private void checkForNullElements(Collection<?> c) { for(Iterator<?> it = c. if(i == 6) high = it. i++) { if(i == 3) low = it. for(int i = 0. } private void checkForValidIndex(int idx) { if(idx == -1) throw new IllegalArgumentException(). 422 Thinking in Java. 4th Edition Annotated Solution Guide . it.iterator().split(" ")). Collections.) if(it.addAll(sortedSet.1).get(list.get(0). } private void checkForNull(Object o) { if(o == null) throw new NullPointerException(). i <= 6. String high = sortedSet. } catch(IndexOutOfBoundsException e) { throw new NoSuchElementException(). Iterator<String> it = sortedSet.first(). String low = sortedSet. print(high). } catch(IndexOutOfBoundsException e) { throw new NoSuchElementException(). "one two three four five six seven eight" .hasNext().size() .try { return list. } } public T last() { try { return list.last(). two] true false true [eight. print(sortedSet). four.asList("zero". three] [one. six. print(sortedSet.tailSet(low)). } } /* Output: [eight. four. three] Null elements not supported! [eleven.next(). three] *///:~ Most of the methods just forward the request to the underlying LinkedList. one. print(sortedSet). "eleven"))). print(sortedSet. "eleven"))). two] true [eleven. null)).asList( "three".println("Null elements not supported!"). seven. seven. // Demonstrate data integrity try { sortedSet. which the add( ) method guarantees. print(sortedSet. five. Containers in Depth 423 . six.retainAll(Arrays. print(high).subSet(low.out. } print(low).asList( "three". print(sortedSet. six. The program lacks efficiency but highlights the basics. } catch(NullPointerException e) { System. four. five. seven. one. seven. high)). three. Both use the fact that the list is already sorted. six. six. two] eight two one two [one.headSet(high)).addAll(Arrays.else it. print(sortedSet.contains("three")). Only the contains( ) and add( ) methods are particularly important.contains("eleven")). eleven. three. print(sortedSet. print(sortedSet. three] [eight.addAll(Arrays. one. five. three. } // The set will not contain "zero"! print(sortedSet). seven. private Integer priority = rnd.nextInt(101). while((item = queue.util.if the specified collection contains one or more null elements and this set does not support null elements. or if the specified collection is null. 4th Edition Annotated Solution Guide . Item item.poll()) != null) 424 Thinking in Java. class Item implements Comparable<Item> { private static final Random rnd = new Random(47). Fill a PriorityQueue with objects of * your class. The JDK says this about the addAll( ) method: “Throws NullPointerException . import java. we choose to roll back all changes in case of an exception.*. ***********************************************/ package containers. This also happens for a ClassCastException when an element of the specified collection cannot be added to the set due to its class. } public String toString() { return Integer. for(int i = 0.Notice that the exception handling satisfies the JDK documentation requirements.” Given this vague description. Exercise 11 //: containers/E11_PriorityQueue. public int compareTo(Item arg) { return priority < arg.priority ? 0 : 1. Implement Comparable using this * Integer field.java /****************** Exercise 11 ***************** * Create a class that contains an Integer that is * initialized to a value between 0 and 100 using * java.toString(priority).Random.util. If you comment out the checkForNullElements(c) call you get a second variation. and extract the values using poll() to * show that it produces the expected order. i < 10. i++) queue.priority ? -1 : priority == arg. } } public class E11_PriorityQueue { public static void main(String[] args) { PriorityQueue<Item> queue = new PriorityQueue<Item>().add(new Item()). Also notice that we assure data integrity. String>()).put("earth".put("sky".put("sun". map. } public static void main(String[] args) { test(new HashMap<String. "object"). and a LinkedHashMap * in AssociativeArray. "brown"). ***********************************************/ package containers. "warm"). "blue").*.put("tree". public class E12_MapsDemo { private static void test(Map<String. import java.Print.println(item).mindview. test(new TreeMap<String.put("extra". try { map.put("grass".java /****************** Exercise 12 ***************** * Substitute a HashMap. a TreeMap. Containers in Depth 425 . "tall"). String> map) { map. } catch(ArrayIndexOutOfBoundsException e) { // Never happen! print("Too many objects!").util. import static net. map. map. } } /* Output: 15 17 18 20 22 62 65 67 95 100 *///:~ Exercise 12 //: containers/E12_MapsDemo.put("ocean".*.util. print(map. map.out. "green").java's main(). map. String>()).get("ocean")). "dancing").System. } print(map). *.util. extra=object. mapping String to Integer. tree=tall. and count the occurrence of the * words in that file.mindview.V> { private Object[][] pairs. ocean=dancing. value }.*. } public void put(K key.length) 426 Thinking in Java.util. tree=tall. 4th Edition Annotated Solution Guide . sky=blue. ***********************************************/ package containers. grass=green.test(new LinkedHashMap<String. extra=object} dancing *///:~ Notice that different Map implementations hold and present the pairs in different orders. return. V value) { for(int i = 0. open a text file and break up the * words in that file using whitespace and * punctuation. extra=object.equals(pairs[i][0])) { pairs[i] = new Object[]{ key. earth=brown. sun=warm} dancing {earth=brown. public AssociativeArray(int length) { pairs = new Object[length][2]. import java. import net.java /****************** Exercise 13 ***************** * Use AssociativeArray. i < index.util. ocean=dancing.mindview. i++) if(key.java to create a * word-occurrence counter.TextFile utility in * this book. String>()). Exercise 13 //: containers/E13_WordCounter. } if(index >= pairs. earth=brown. ocean=dancing. grass=green. private int index. sun=warm. * Using the net. class AssociativeArray<K. } } /* Output: {sky=blue. tree=tall} dancing {sky=blue. sun=warm. grass=green. get(word). Integer> map = new AssociativeArray<String.append("\n").equals(pairs[i][0])) return (V)pairs[i][1].toString()). i++) if(key. result.append(" : ").append(pairs[i][0]. i < index. pairs[index++] = new Object[]{ key.toString()). i++) { result. result. } } public class E13_WordCounter { public static void main(String[] args) { List<String> words = new TextFile("E12_MapsDemo. } return result. i < index.java". AssociativeArray<String. } System.out.put(word. for(String word : words) { Integer freq = map. } } /* Output: (Sample) containers : 1 E12_MapsDemo : 2 java : 3 Exercise : 1 12 : 1 Substitute : 1 a : 3 HashMap : 2 TreeMap : 2 . freq == null ? 1 : freq + 1). map. } @SuppressWarnings("unchecked") public V get(K key) { for(int i = 0. value }.append(pairs[i][1].toString().. // Did not find key } public String toString() { StringBuilder result = new StringBuilder(). "\\W+").throw new ArrayIndexOutOfBoundsException(). return null.1) result.size()).println(map).. Integer>(words. if(i < index . for(int i = 0. objects : 1 Containers in Depth 427 . *. ***********************************************/ package containers. print(map.mindview. Object key = map. print("First key in map: " + key). since all attributes are private and there are no corresponding getXXX( ) methods. public class E14_PropertiesDemo { static void printKeys(Map<Object.containsKey(11): " + map.keySet(). 4th Edition Annotated Solution Guide .values()).size() + ". To remedy this. map.containsValue(\"F0\"): " + map.get(11)).putAll(new CountingMapData(25)). print("map.getSimpleName()).util. we alter the method put( ).get : 1 args : 1 new : 3 Output : 1 *///:~ The original AssociativeArray is inadequate here: if the array previously held a mapping for some key.Properties works in the above * program (containers/Maps.util. print(map). Object> map) { print(map. map. print(map.*. import java. print("map. Exercise 14 //: containers/E14_PropertiesDemo.remove(key). 428 Thinking in Java.keySet()). "). print("map.java /****************** Exercise 14 ***************** * Show that java.putAll(new CountingMapData(25)). // Produce a Set of the keys } static void test(Map<Object.util.get(11): " + map. Object> map) { printnb("Size = " + map. the new value would not replace the old one.next().java from the book).Print.containsValue("F0")). printnb("Keys: "). // Map has 'Set' behavior for keys: map. import static net. // Producing a Collection of the values: printnb("Values: ").containsKey(11)). printKeys(map).iterator(). Notice that we cannot simply create a new class by inheriting it from the original AssociativeArray.getClass(). 19=T0. R0.java /****************** Exercise 15 ***************** * Repeat Exercise 13 using a SlowMap. 0=A0} map. 13.Properties extends Hashtable<Object. K0. 1. 14=O0. 15.isEmpty(): " + map. 21=V0. P0. F0. 4. D0.containsValue("F0"): true First key in map: 24 Size = 24.*. 12. map.clear(). 22. 8. 5. Q0. 21. N0. 10.*. 13=N0. 19. U0. map. V0.util. 14. 3=D0. 7. G0. 2. H0.containsKey(11): true map. 23. 16. 4=E0. M0. 2=C0. 22=W0. 11. 23=X0. 14.mindview. X0. O0.isEmpty(): true map. 19. B0. 10=K0. A0] {24=Y0. Keys: [24. // Operations on the Set change the Map: map. print("map. J0.isEmpty(): true *///:~ java. 9. 21. 12. 17=R0. 5=F0. Keys: [23. public class E15_WordCounter2 { public static void main(String[] args) { List<String> words = Containers in Depth 429 . 17. 15. L0.util.keySet(). 10. 11=L0. 8=I0. print("map.printKeys(map). 6=G0. 18. 20. E0. ***********************************************/ package containers. 7=H0. 12=M0. and requires only a few changes to integrate it into the original code. 2.keySet()). 9. } public static void main(String[] args) { test(new Properties()). Exercise 15 //: containers/E15_WordCounter2.get(11): L0 map.isEmpty(): " + map.util. 18=S0. 8.isEmpty()). import java. 4. 6. 1=B0. import net. 18.removeAll(map. 11. 17. 3.putAll(new CountingMapData(25)). 22. 15=P0. 16. 5. I0. 20=U0. 0] map. 6.Object>. W0.isEmpty()). 7. 3. C0. } } /* Output: Properties Size = 25. 1. 16=Q0. 0] Values: [Y0. 20. T0. S0. 13. 9=J0. util. objects=1. e=1.mindview. private=1. } System. a=3.contains(key)) { keys. } } /* Output: {AssociativeArray=1. "\\W+"). map=10. @Override public V put(K key.V> { private List<K> keys = new ArrayList<K>().java". void=2.java /****************** Exercise 16 ***************** * Apply the tests in Maps. sky=4.get(word).util. put=7. V oldValue = get(key). import java.add(key). net=1. args=1. new=3. sun=4. 12=1. catch=1. many=1. map. values. // Does not support null value as key! class SlowMap2<K. String=9. grass=4. Output=1. ArrayIndexOutOfBoundsException=1} *///:~ Exercise 16 //: containers/E16_SlowMapTest.Integer>().add(value).V> extends AbstractMap<K. tall=4. ocean=5. get=1. package=1.*. and=1. extra=4. s=1. earth=4.Integer> map = new SlowMap<String.put(word. Map=1. class=1. warm=4.new TextFile("E12_MapsDemo. try=1.out. print=3. Never=1. 430 Thinking in Java. main=2. Too=1. freq == null ? 1 : freq + 1). test=4. java=3. // The old value or null if(!keys. green=4.java to SlowMap to * verify that it works. blue=4. E12_MapsDemo=2. import static net.Print. Substitute=1. for(String word : words) { Integer freq = map. mindview=1. object=4. tree=4. HashMap=2. static=3. TreeMap=2. containers=2. brown=4.println(map). Exercise=1. dancing=7. happen=1. in=1. Print=1. Fix anything in SlowMap * that doesn't work correctly. 4th Edition Annotated Solution Guide . SlowMap<String. public=2. util=2. import=2. private List<V> values = new ArrayList<V>().*. LinkedHashMap=2. V value) { if(key == null) throw new NullPointerException(). ***********************************************/ package containers. V> next() { canRemove = true. boolean canRemove.V>> { @Override public Iterator<Map. if(keys.set(keys.remove(index--).Entry<K.get(index).1.V>>() { private int index = -1. } private EntrySet entrySet = new EntrySet().remove(index).V>> iterator() { return new Iterator<Map. values.Entry<K. } // Uses the 'Flyweight' pattern private class EntrySet extends AbstractSet<Map. public boolean hasNext() { return index < keys.indexOf(key). canRemove = false.get(keys. value).Entry<K. } @Override public V get(Object key) { if(key == null) throw new NullPointerException(). ++index. return new MapEntry<K.equals( Containers in Depth 431 .Entry<K.Entry<K.getKey(). } @SuppressWarnings("unchecked") @Override public boolean contains(Object o) { if(o instanceof MapEntry) { MapEntry<K.size() .contains(key)) return e.indexOf(key)).V>)o. K key = e. } }. } public Map.} else values. return values. return oldValue. values.V> e = (MapEntry<K. @Override public Set<Map. } public void remove() { if(!canRemove) throw new IllegalStateException().V>( keys.contains(key)) return null.V>> entrySet() { return entrySet. keys.get(index)). if(!keys. containsValue(\"F0\"): " + map.String> map) { printnb("Size = " + map. map.containsKey(11)).get(11): " + map.remove(key).get(11)). } return false. } } } public class E16_SlowMapTest { public static void printKeys(Map<Integer.remove(key).next(). printnb("Keys: "). } @SuppressWarnings("unchecked") @Override public boolean remove(Object o) { if(contains(o)) { MapEntry<K. map. print("First key in map: " + key).V>(key. K key = e.getSimpleName()).putAll(new CountingMapData(25)). print(map). } return false. values. printKeys(map).iterator(). V val = e. get(key))). Integer key = map. print("map.keySet().size().getKey(). } @Override public int size() { return keys.values()). // Producing a Collection of the values: printnb("Values: "). 4th Edition Annotated Solution Guide .remove(val). print(map.getValue(). keys.clear().getClass().containsValue("F0")).size() + ". "). print(map. print("map. // Map has 'Set' behavior for keys: map. } @Override public void clear() { keys. print("map. return true.clear(). values. // Produce a Set of the keys } public static void test(Map<Integer.V> e = (MapEntry<K.putAll(new CountingMapData(25)).new MapEntry<K.containsKey(11): " + map.keySet()).V>)o. 432 Thinking in Java.String> map) { print(map. B0. D0. 2=C0. 15. 13. 16. 1. C0. 24. H0. 23=X0. 7=H0. print("map. Keys: [14. T0. 10. 22. 20. 3=D0. 18=S0. X0.keySet()). U0.printKeys(map). 15=P0. F0. J0. J0. R0. 6. N0. 0. 14=O0. G0. S0. 17.isEmpty(): " + map. 15=P0. E0. 6. 21=V0. 17=R0. 8. 4. R0.isEmpty(): false Testing SlowMap2 SlowMap2 Size = 25. 12. 16. 2. 23. 4. 4=E0. F0. 10. 17=R0. I0. 19. 3. 1=B0. 4. 7. 2. print("map. 15. C0. 17. 21. 7. 23. 0. 21. T0] {14=O0. E0. 1. 16=Q0.isEmpty()). 4. 5. N0.out. 23=X0.containsKey(11): true map. 22. B0. 10=K0. 12=M0. 15. 5.isEmpty(): " + map.isEmpty(): false map. 24=Y0. 11=L0. 9. } } /* Output: Testing SlowMap SlowMap Size = 25.keySet(). 6. 14. 8. 11. 20=U0. 24] Containers in Depth 433 . 12.get(11): L0 map. 12. 5. 23. 21. W0. 24=Y0} map. P0. 9. Q0. 6.out. 5=F0. test(new SlowMap<Integer. V0. 10. M0. D0. 11. 12.get(11): L0 map. G0. 13=N0. 21=V0. 16=Q0. 8.isEmpty()). 13. 8=I0. test(new SlowMap2<Integer. 15. V0.String>()). 22=W0. 24] Values: [A0. 6=G0. 18=S0. 11. S0. map. 18. 20. 17. 2. 21. 19=T0. 19] map. 22. 10.containsKey(11): true map. 18. 18. 19] Values: [O0. P0. 2. Y0. Keys: [0.removeAll(map. 20=U0. 6=G0. H0. 5=F0.putAll(new CountingMapData(25)). 7. map. 0=A0. 4=E0. 20. Keys: [14. 16. 16. X0. Keys: [1. I0. 19. L0. L0. 13=N0.println("Testing SlowMap2"). 23. 13. 9. 7. 8. 14. 22=W0. A0. 22. System. 12=M0. 8=I0.println("Testing SlowMap"). M0. 3. 19=T0} map. U0. 9. 1. } public static void main(String[] args) { System. 13.containsValue("F0"): true First key in map: 0 Size = 24. 3. 20. 2=C0. 10=K0. 3. 11. K0. 24. 9=J0. 1=B0. 3=D0. 18. 17.String>()). 5.clear(). W0. 11=L0. O0. // Operations on the Set change the Map: map. 7=H0.containsValue("F0"): true First key in map: 14 Size = 25. 9=J0. Y0] {0=A0. K0. Q0. import java.e.util. see the difference in the output. so integrating it into Maps. overriding them in SlowMap2 gains nothing.. Remember. Their default use requires linear time in the size of the map though.isEmpty(): true *///:~ As stated in the JDK. SlowMap returns a fresh copy of the keys instead. public boolean add(K key) { if(!contains(key)) { keys.java is troublesome. The two most frequently overridden methods of AbstractMap (besides those already overridden in SlowMap2) are containsKey( ) and remove( ). The returned entry set doesn’t properly remove elements.*. class SlowSet<K> extends AbstractSet<K> { private List<K> keys = new ArrayList<K>().java /****************** Exercise 18 ***************** * Using SlowMap. Exercise 18 //: containers/E18_SlowSet. However. which is awkward. operations on the set do not change the map. import net.add(key). return true. ***********************************************/ package containers.map. The SlowMap2 class fixes this. You really can’t speed up the code. 4th Edition Annotated Solution Guide . sometimes the abstract base version of the container you are creating already uses the methods you need (not that you create your own containers all that often). so extra development is unnecessary. entrySet( ) should return a set view of the mappings contained in a map.java for inspiration.*. create a * SlowSet.util. i.mindview. Exercise 17 The SlowMap2 class already uses all of Map (which you can test by invoking each method listed in Map on SlowMap2). } return false.isEmpty(): true map. } 434 Thinking in Java. ***********************************************/ package containers.out. } } /* Output: [ALGERIA.Integer> map = new SimpleHashMap<String.get(word). slowSet. Map=1. ANGOLA. package=1. BOTSWANA.util.public Iterator<K> iterator() { return keys. get=1. import=2.addAll(Countries. util=2. for(String word : words) { Integer freq = map.*. import java. Too=1. Output=1.addAll(Countries. } public int size() { return keys. map. test=4.Integer>().names(10)). CHAD] *///:~ We test by repeatedly adding the same elements to the set. BURKINA FASO.addAll(Countries. } } public class E18_SlowSet { public static void main(String[] args) { SlowSet<String> slowSet = new SlowSet<String>(). public class E19_WordCounter3 { public static void main(String[] args) { List<String> words = new TextFile("E12_MapsDemo.mindview. CAMEROON.java".println(slowSet). BURUNDI. SimpleHashMap<String. class=1. BENIN. import net.util.java /****************** Exercise 19 ***************** * Repeat Exercise 13 using a SimpleHashMap.put(word. sky=4. CENTRAL AFRICAN REPUBLIC. } } /* Output: {AssociativeArray=1. many=1.names(10)). objects=1. System. happen=1. "\\W+"). String=9. Substitute=1. Print=1.out. slowSet. tree=4. grass=4. Containers in Depth 435 . static=3.size().names(10)). } System. try=1. freq == null ? 1 : freq + 1). extra=4. CAPE VERDE.iterator(). slowSet.*. and=1. args=1. Exercise 19 //: containers/E19_WordCounter3.println(map). put=7. void=2. Iterator<MapEntry<K. net=1.out. MapEntry<K.abs(key. class SimpleHashMap2<K. ListIterator<MapEntry<K. warm=4.java /****************** Exercise 20 ***************** * Modify SimpleHashMap so it reports * collisions. 12=1. containers=2. map=10.getKey(). } // End of lines added LinkedList<MapEntry<K. V value) { V oldValue = null. import net. tall=4. brown=4. main=2.V> pair = new MapEntry<K. a=3.println( "Collision while adding\n" + pair + "\nBucket already contains:"). 4th Edition Annotated Solution Guide .V> iPair = it.V>(key. new=3. sun=4. ArrayIndexOutOfBoundsException=1. ***********************************************/ package containers. 436 Thinking in Java. earth=4} *///:~ Exercise 20 //: containers/E20_SimpleHashMapCollisons. public=2. if(buckets[index] == null) buckets[index] = new LinkedList<MapEntry<K.*.out. while(it.mindview. dancing=7.V>> it = bucket. e=1.equals(key)) { oldValue = iPair. TreeMap=2. E12_MapsDemo=2.hasNext()) { MapEntry<K. LinkedHashMap=2.V>> bucket = buckets[index].println(it. blue=4. while(it.iterator(). HashMap=2.V> extends SimpleHashMap<K. object=4.listIterator().hasNext()) System. // Lines added here: else { System. print=3.V> { @Override public V put(K key. catch=1. Never=1.*.util. value).util. Exercise=1. import java. s=1.V>>().hashCode()) % SIZE. boolean found = false. ocean=5. and test this by adding the same * data set twice so you see collisions.next()). int index = Math. private=1. green=4.next().V>> it = buckets[index]. java=3. in=1.getValue(). if(iPair. mindview=1. println(m).it. } } public class E20_SimpleHashMapCollisons { public static void main(String[] args) { SimpleHashMap2<String. } } if(!found) buckets[index].add(pair). m. System. return oldValue.capitals(25)). break.capitals(25)). // Replace old with new found = true.putAll(Countries.set(pair). } } /* Output: Collision while adding ALGERIA=Algiers Bucket already contains: ALGERIA=Algiers Collision while adding ANGOLA=Luanda Bucket already contains: ANGOLA=Luanda Collision while adding BENIN=Porto-Novo Bucket already contains: BENIN=Porto-Novo Collision while adding BOTSWANA=Gaberone Bucket already contains: BOTSWANA=Gaberone Collision while adding BURKINA FASO=Ouagadougou Bucket already contains: BURKINA FASO=Ouagadougou Collision while adding BURUNDI=Bujumbura Bucket already contains: BURUNDI=Bujumbura Collision while adding CAMEROON=Yaounde Bucket already contains: Containers in Depth 437 .String> m = new SimpleHashMap2<String.String>().putAll(Countries.out. m. CAMEROON=Yaounde Collision while adding CAPE VERDE=Praia Bucket already contains: CAPE VERDE=Praia Collision while adding CENTRAL AFRICAN REPUBLIC=Bangui Bucket already contains: CENTRAL AFRICAN REPUBLIC=Bangui Collision while adding CHAD=N'djamena Bucket already contains: CHAD=N'djamena Collision while adding COMOROS=Moroni Bucket already contains: COMOROS=Moroni Collision while adding CONGO=Brazzaville Bucket already contains: CONGO=Brazzaville Collision while adding DJIBOUTI=Dijibouti Bucket already contains: DJIBOUTI=Dijibouti Collision while adding EGYPT=Cairo Bucket already contains: EGYPT=Cairo Collision while adding EQUATORIAL GUINEA=Malabo Bucket already contains: EQUATORIAL GUINEA=Malabo Collision while adding ERITREA=Asmara Bucket already contains: ERITREA=Asmara Collision while adding ETHIOPIA=Addis Ababa Bucket already contains: ETHIOPIA=Addis Ababa Collision while adding GABON=Libreville Bucket already contains: GABON=Libreville Collision while adding THE GAMBIA=Banjul 438 Thinking in Java. 4th Edition Annotated Solution Guide . THE GAMBIA=Banjul. KENYA=Nairobi. COTE D'IVOIR (IVORY COAST)=Yamoussoukro. EQUATORIAL GUINEA=Malabo. ERITREA=Asmara. GUINEA=Conakry. BOTSWANA=Gaberone. DJIBOUTI=Dijibouti. ANGOLA=Luanda. LESOTHO=Maseru. GHANA=Accra. BURUNDI=Bujumbura. CAMEROON=Yaounde} *///:~ We added the lines surrounded by comments in the put( ) method.java /****************** Exercise 21 ***************** * Modify SimpleHashMap so that it reports the Containers in Depth 439 . BURKINA FASO=Ouagadougou. CAPE VERDE=Praia. GABON=Libreville. CENTRAL AFRICAN REPUBLIC=Bangui. ETHIOPIA=Addis Ababa. BISSAU=Bissau. and moved the creation of the pair object so it prints when the collision occurs. ALGERIA=Algiers. Everything else is the same.Bucket already contains: THE GAMBIA=Banjul Collision while adding GHANA=Accra Bucket already contains: GHANA=Accra Collision while adding GUINEA=Conakry Bucket already contains: GUINEA=Conakry Collision while adding BISSAU=Bissau Bucket already contains: BISSAU=Bissau Collision while adding COTE D'IVOIR (IVORY COAST)=Yamoussoukro Bucket already contains: COTE D'IVOIR (IVORY COAST)=Yamoussoukro Collision while adding KENYA=Nairobi Bucket already contains: KENYA=Nairobi Collision while adding LESOTHO=Maseru Bucket already contains: LESOTHO=Maseru {CHAD=N'djamena. EGYPT=Cairo. COMOROS=Moroni. Exercise 21 //: containers/E21_LeftToReader. BENIN=PortoNovo. CONGO=Brazzaville. Exercise 22 //: containers/E22_SimpleHashMapClearRemove.V> iPair = it.println("Exercise left to reader"). import java.getKey().abs(key. the Map. That is. if(buckets[index] == null) return null. // Removes the last fetched value: 440 Thinking in Java. if(iPair. how many calls to next() must * be made on the Iterators that walk the * LinkedLists looking for matches? ***********************************************/ package containers.out.util.V>> it = buckets[index].*. 4th Edition Annotated Solution Guide . class SimpleHashMap3<K.* number of "probes" necessary when collisions * occur. } } ///:~ Hint: Modify the get( ) method the same way as put( ) in Exercise 20. while(it. public class E21_LeftToReader { public static void main(String args[]) { System. import net.getValue().hasNext()) { MapEntry<K. } public V remove(Object key) { // Code is copied from get(). except that // before returning the value.Entry is // removed from the list: int index = Math.java /****************** Exercise 22 ***************** * Implement the clear() and remove() methods for * SimpleHashMap. ***********************************************/ package containers.util.equals(key)) { // Changes are here: V value = iPair.mindview. Iterator<MapEntry<K.hashCode()) % SIZE.iterator().V> { @SuppressWarnings("unchecked") public void clear() { // Effectively erase everything by allocating // a new empty array of buckets: buckets = new LinkedList[SIZE].*.next().V> extends SimpleHashMap<K. util. BENIN=Porto-Novo. } } /* Output: {ANGOLA=Luanda. ***********************************************/ package containers.get("BURUNDI")). import java. System. m.get("BURUNDI")).println("After clearing: " + m).util. import net.println( "After removal.capitals(10)).remove(). import static net.*.Print.get("BURUNDI") = Bujumbura After removal.util. BURUNDI=Bujumbura.println(m). CAMEROON=Yaounde} m.putAll(Countries.java /****************** Exercise 23 ***************** * Implement the rest of the Map interface for * SimpleHashMap.*.mindview.get(\"BURUNDI\") = " + m. m.clear(). BURKINA FASO=Ouagadougou.get("BURUNDI") = null After clearing: {} *///:~ Exercise 23 //: containers/E23_FullSimpleHashMap. CAPE VERDE=Praia. ALGERIA=Algiers.String>(). return value. CHAD=N'djamena.get(\"BURUNDI\") = " + m. } } public class E22_SimpleHashMapClearRemove { public static void main(String[] args) { SimpleHashMap3<String. m.out.remove("BURUNDI").String> m = new SimpleHashMap3<String.out. m.println("m. } } return null.mindview.V> { Containers in Depth 441 .*. m. class SimpleHashMap4<K. System.V> extends SimpleHashMap3<K. System.it. CENTRAL AFRICAN REPUBLIC=Bangui.out.out. System. BOTSWANA=Gaberone. print("m.getKey().containsKey(\"BENIN\") = " + m. return sz.V>> bucket : buckets) if(bucket != null) return false. 4th Edition Annotated Solution Guide . } } public class E23_FullSimpleHashMap { public static void main(String args[]) { SimpleHashMap4<String.V>> bucket : buckets) if(bucket != null) sz += bucket.containsKey(\"MARS\") = " + m.size().putAll(Countries.containsKey("MARS")). but this is faster: int sz = 0.V> mPair : bucket) if(mPair.capitals(10)). } return false. m2.public int size() { // Could rely on the inherited implementation.equals(m2) = " + m. for(LinkedList<MapEntry<K.equals(m2)). print("m. m2 = new SimpleHashMap4<String. which // returns entrySet(). print("m.size()). } public boolean isEmpty() { // Could just say return size() == 0.values() = " + m. } } /* Output: 442 Thinking in Java.String>().String> m = new SimpleHashMap4<String.keySet() = " + m. print("m.isEmpty()).equals(key)) return true.V>> bucket : buckets) { if(bucket == null) continue. } public boolean containsKey(Object key) { // A slight modification of the previous method: for(LinkedList<MapEntry<K. print("m.keySet()).size().capitals(10)). print("m. for(MapEntry<K. m.putAll(Countries.String>().isEmpty() = " + m.containsKey("BENIN")).size() = " + m. // but this is faster: for(LinkedList<MapEntry<K.values()). return true. print("m. if(buckets[index] == null) return false. class SimpleHashSet<K> extends AbstractSet<K> { private final static int SIZE = 997. Porto-Novo. @SuppressWarnings("unchecked") private LinkedList<K>[] buckets = new LinkedList[SIZE].m.values() = [Luanda. Ouagadougou. Bangui.java /****************** Exercise 24 ***************** * Following the example in SimpleHashMap.hashCode()) % SIZE. BURUNDI.java.abs(key. // Sets do not permit duplicates and one // was already in the set. BURKINA FASO. import java. N'djamena. it. Gaberone.hasNext()) if(it.equals(m2) = true m.size() = 10 m. * create and test a SimpleHashSet.containsKey("BENIN") = true m.hashCode()) % SIZE. return true.util. Algiers. import net. ***********************************************/ package containers. Praia. ListIterator<K> it = bucket.isEmpty() = false m. } public boolean contains(Object key) { int index = Math.equals(key)) return false. CAMEROON] m.containsKey("MARS") = false m.listIterator(). Yaounde] *///:~ Exercise 24 //: containers/E24_SimpleHashSet.next(). CAPE VERDE.*.iterator().abs(key. BOTSWANA. CENTRAL AFRICAN REPUBLIC. Iterator<K> it = buckets[index].util. LinkedList<K> bucket = buckets[index]. public boolean add(K key) { int index = Math.mindview. if(buckets[index] == null) buckets[index] = new LinkedList<K>().*. Bujumbura. BENIN. while(it. CHAD. ALGERIA.add(key).keySet() = [ANGOLA. Containers in Depth 443 . ) { // Position of the next non-empty bucket while(buckets[index1] == null) ++index1.equals(key)) return true. if(buckets[index1].isEmpty()) // Housekeeping. index2 = 0.remove(--index2).next(). ++count. } else 444 Thinking in Java. for(. --count.. // Position of the next item to return try { return buckets[index1]. } public void remove() { if(canRemove) { canRemove = false. index2.. private boolean canRemove. } } } throw new NoSuchElementException(). buckets[index1++] = null. } catch(IndexOutOfBoundsException e) { // Continue search from the next bucket ++index1. private int index1. for(LinkedList<K> bucket : buckets) { if(bucket != null) sz += bucket.size(). } public Iterator<K> iterator() { return new Iterator<K>() { private int count. 4th Edition Annotated Solution Guide .get(index2++). buckets[index1]. } public int size() { int sz = 0.while(it. public boolean hasNext() { return count < size().hasNext()) if(it.. return false. } return sz. } public K next() { if(hasNext()) { canRemove = true. System.addAll(Countries. ALGERIA.next()= "+ it.size() = " + m.next()).out.out. CHAD.println("m.throw new IllegalStateException(). m. ANGOLA.next()= BOTSWANA it.println("m = " + m).next()= BENIN m = [BENIN. Exercise 25 //: containers/E25_FullSimpleHashMap2.out. we declare different methods in Set than in AbstractSet.addAll(Countries. m. CAMEROON. ANGOLA. BURKINA FASO. CAPE VERDE. BURKINA FASO.remove("ALGERIA"). m.iterator().java /****************** Exercise 25 ***************** * Instead of using a ListIterator for each bucket. Iterator<String> it = m. BURUNDI] *///:~ This contains keys but not values. CENTRAL AFRICAN REPUBLIC.java so Containers in Depth 445 .out. CHAD. System. System. BURKINA FASO. } } /* Output: m = [BOTSWANA. CAMEROON. System.println("m = " + m). CAPE VERDE. Pay special attention to the iterator( ) method.size()).remove(). ANGOLA. CENTRAL AFRICAN REPUBLIC. BURUNDI] m = [BENIN. BENIN. BURUNDI] m. * modify MapEntry so it is a self-contained * singly-linked list (each MapEntry should have a * forward link to the next MapEntry). Modify the * rest of the code in SimpleHashMap.out. Also.println("it.println("it. } }. CAMEROON.next()= "+ it. System. it. System.println("m = " + m). CHAD.out. ALGERIA.size() = 10 it. the above methods are the only ones necessary to implement the SimpleHashSet.names(10)). } } public class E24_SimpleHashSet { public static void main(String[] args) { SimpleHashSet<String> m = new SimpleHashSet<String>().names(10)). CENTRAL AFRICAN REPUBLIC. CAPE VERDE.next()). V> e = (Entry<K.hashCode()). V value) { this.* this new approach works correctly.util. value = v.util.Entry<K.mindview. public Entry(K key. Entry<K.*.Print.V> { static class Entry<K. import static net.value = value.mindview. ***********************************************/ package containers. private V value.V>[] buckets = new Entry[SIZE].V> { private K key.*. 4th Edition Annotated Solution Guide .key = key.V>)o.V> extends AbstractMap<K.*. this. return val1 == null ? val2 == null : val1. if(key1. Object key2 = e. } public V getValue() { return value.V> implements Map.V> next. } public int hashCode() { return key.util. 446 Thinking in Java. } } return false. } public String toString() { return key + "=" + value. import net.getValue(). class SimpleHashMap5<K. import java. } public K getKey() { return key.equals(val2).getKey(). return result. @SuppressWarnings("unchecked") Entry<K.equals(key2)) { Object val1 = getValue(). } public V setValue(V v) { V result = value. } public boolean equals(Object o) { if(o instanceof Entry) { @SuppressWarnings("unchecked") Entry<K.hashCode() ^ (value == null ? 0 : value. } } static final int SIZE = 997. Object val2 = e. Object key1 = getKey(). equals(key)) return pair.V>> entrySet() { Set<Map. for(Entry<K. return null.getValue(). pair != null. V value) { V oldValue = null. } public Set<Map. newPair.next = pair.next) set.V> prevPair = null.V>>().V> bucket : buckets) { for(Entry<K. for(Entry<K.getKey(). for(Entry<K.add(pair). // Replace old with new if(prevPair != null) prevPair.getKey().abs(key.getValue(). } @SuppressWarnings("unchecked") public void clear() { // Effectively erase everything by allocating Containers in Depth 447 . pair = pair. pair = pair.next) if(pair.V>(key. int index = Math. pair = pair. Entry<K.equals(key)) { oldValue = pair.V> pair = buckets[index] . } public V get(Object key) { int index = Math. Entry<K. else buckets[index] = newPair.abs(key.Entry<K. found = true.next = newPair.next.V>> set = new HashSet<Map. pair != null. } prevPair = pair. pair != null. } return set. value).hashCode()) % SIZE.next = newPair.Entry<K. if(buckets[index] == null) buckets[index] = newPair.next) { if(pair. break. return oldValue.V> pair = buckets[index].Entry<K. // Previous element boolean found = false.V> newPair = new Entry<K.V> pair = bucket.public V put(K key.hashCode()) % SIZE. } if(!found) prevPair. } public boolean containsKey(Object key) { // A slight modification of the previous method: for(Entry<K. // but this is faster: for(Entry<K.getKey().equals(key)) return true. if(prevPair != null) prevPair.next) sz++. } public int size() { // Could rely on the inherited implementation.abs(key.size(). for(Entry<K. } } return null.V> pair = bucket.V> prevPair = null.V> bucket : buckets) for(Entry<K. 448 Thinking in Java. pair != null. 4th Edition Annotated Solution Guide . pair != null. pair = pair. but this is faster: int sz = 0.V> pair = buckets[index] .next = pair.Entry is // removed from the "list": int index = Math. // Previous element for(Entry<K. pair = pair. else buckets[index] = null.equals(key)) { V value = pair.next) if(pair. the Map. return true. } public V remove(Object key) { // Code is copied from get(). } public boolean isEmpty() { // Could just say return size() == 0.// a new empty array of buckets: buckets = new Entry[SIZE]. return sz. return value. pair = pair.hashCode()) % SIZE.V> bucket : buckets) if(bucket != null) return false.getKey().V> pair = bucket. except that // before returning the value.V> bucket : buckets) { for(Entry<K.getValue(). which // returns entrySet(). Entry<K.next.next) { if(pair. pair != null. print("m. CAMEROON=Yaounde} *///:~ Exercise 26 //: containers/E26_CountedString2. BOTSWANA=Gaberone.values() = [Luanda. CENTRAL AFRICAN REPUBLIC=Bangui.containsKey("MARS")).isEmpty() = " + m. BOTSWANA. BENIN=Porto-Novo. print("m. m2 = new SimpleHashMap5<String.containsKey(\"BENIN\") = " + m. BURUNDI. CHAD. } } /* Output: m.keySet() = " + m.size() = 10 m.putAll(Countries. m.equals(m2)).equals(m2) = true m.containsKey("BENIN") = true m. Bangui. Gaberone.size() = " + m. print("m.} return false.String> m = new SimpleHashMap5<String. print("m. m.String>(). Algiers. Porto-Novo. BURKINA FASO.keySet() = [ANGOLA. Ouagadougou.values() = " + m.containsKey("BENIN")).size()).String>(). BURKINA FASO=Ouagadougou. print("m. m2. BURUNDI=Bujumbura. N'djamena.capitals(10)). print("m = " + m).remove("ALGERIA"). Yaounde] m = {ANGOLA=Luanda.equals(m2) = " + m.containsKey("MARS") = false m. BENIN.keySet()).java /****************** Exercise 26 ***************** Containers in Depth 449 . print("m. CENTRAL AFRICAN REPUBLIC. CAMEROON] m. CAPE VERDE=Praia.containsKey(\"MARS\") = " + m. Praia. CAPE VERDE. } } public class E25_FullSimpleHashMap2 { public static void main(String args[]) { SimpleHashMap5<String.putAll(Countries. print("m. ALGERIA.values()).capitals(10)).isEmpty() = false m. Bujumbura.isEmpty()). CHAD=N'djamena. ***********************************************/ package containers. private int id. return result.id && c == ((CountedString2)o). created.*. class CountedString2 { private static List<String> created = new ArrayList<String>(). // id is the total number of instances // of this string in use by CountedString2: for(String s2 : created) if(s2. public CountedString2(String str. result = 37 * result + c.c. } public boolean equals(Object o) { return o instanceof CountedString2 && s. private String s. import static net. import java. // Using Joshua Bloch's recipe: int result = 17. result = 37 * result + s. 4th Edition Annotated Solution Guide .* Add a char field to CountedString that is also * initialized in the constructor.*.add(s).util. and modify the * hashCode() and equals() methods to include * the value of this char.hashCode() * id.equals(s)) id++. result = 37 * result + id. c = ci. private char c. } public String toString() { return "String: " + s + " id: " + id + " char: " + c + " hashCode(): " + hashCode().mindview.s) && id == ((CountedString2)o).util.equals(((CountedString2)o). } } 450 Thinking in Java. } public int hashCode() { // The very simple approach: // return s.Print. char ci) { s = str.hashCode(). java /****************** Exercise 27 ***************** * Modify the hashCode() in CountedString. i < cs. and demonstrate * that CountedString still works as a key. for(CountedString2 cstring : cs) { print("Looking up " + cstring). import java. CountedString2[] cs = new CountedString2[5]. } } } /* Output: {String: hi id: 2 char: c hashCode(): 5418675=1.length.Integer> map = new HashMap<CountedString2.put(cs[i].public class E26_CountedString2 { public static void main(String[] args) { Map<CountedString2. String: hi id: 5 char: c hashCode(): 5418786=4.Integer>(). String: hi id: 1 char: c hashCode(): 5418638=0. for(int i = 0. print(map. i).util. 'c'). // Autobox int -> Integer } print(map).java by * removing the combination with id. i++) { cs[i] = new CountedString2("hi".*. What is * the problem with this approach? ***********************************************/ package containers. hashCode( ) and equals( ). Exercise 27 //: containers/E27_CountedString3. map. String: hi id: 4 char: c hashCode(): 5418749=3.get(cstring)). String: hi id: 3 char: c hashCode(): 5418712=2} Looking up String: hi id: 1 char: c hashCode(): 5418638 0 Looking up String: hi id: 2 char: c hashCode(): 5418675 1 Looking up String: hi id: 3 char: c hashCode(): 5418712 2 Looking up String: hi id: 4 char: c hashCode(): 5418749 3 Looking up String: hi id: 5 char: c hashCode(): 5418786 4 *///:~ Notice the addition of char c in toString( ). Containers in Depth 451 . } public int hashCode() { // The very simple approach: // return s.id. i++) { cs[i] = new CountedString3("hi"). return result.Integer> map = new HashMap<CountedString3. i < cs. class CountedString3 { private static List<String> created = new ArrayList<String>(). map.Integer>().equals(((CountedString3)o).equals(s)) id++. 4th Edition Annotated Solution Guide .*.hashCode(). } public String toString() { return "String: " + s + " id: " + id + " hashCode(): " + hashCode(). } public boolean equals(Object o) { return o instanceof CountedString3 && s. 452 Thinking in Java. result = 37 * result + s.util.import static net.add(s).hashCode(). created.put(cs[i]. CountedString3[] cs = new CountedString3[5]. public CountedString3(String str) { s = str.s) && id == ((CountedString3)o). // Using Joshua Bloch's recipe: int result = 17. // Autobox int -> Integer } for(CountedString3 cstring : cs) { print("Looking up " + cstring). // id is the total number of instances // of this string in use by CountedString3: for(String s2 : created) if(s2. i). private int id = 0.mindview. private String s. for(int i = 0.length. // result = 37 * result + id. } } public class E27_CountedString3 { public static void main(String[] args) { Map<CountedString3.Print. print(map.get(cstring)); } } } /* Output: Looking up String: 0 Looking up String: 1 Looking up String: 2 Looking up String: 3 Looking up String: 4 *///:~ hi id: 1 hashCode(): 3958 hi id: 2 hashCode(): 3958 hi id: 3 hashCode(): 3958 hi id: 4 hashCode(): 3958 hi id: 5 hashCode(): 3958 The only change to the original program is the line: // result = 37 * result + id; Prior to this line, each object has a unique hash code. After commenting this line, each object produces the same hash code. The lookup still produces the correct values, but each object hashes to the same one. We lose the performance advantage of hashing when we must use equals( ) to compare values until we find the right object. Exercise 28 //: containers/E28_Tuple.java /****************** Exercise 28 ***************** * Modify net/mindview/util/Tuple.java to make it * a general-purpose class by adding hashCode(), * equals(), and implementing Comparable for each * type of Tuple. ***********************************************/ package containers; import static net.mindview.util.Print.*; class Tuple { public static class T2<A,B> implements Comparable<T2<A,B>> { private final A first; private final B second; public T2(A a, B b) { first = a; second = b; Containers in Depth 453 } public String toString() { return "(" + first + ", " + second + ")"; } public int hashCode() { int result = 17; if(first != null) result = result * 37 + first.hashCode(); if(second != null) result = result * 37 + second.hashCode(); return result; } public boolean equals(Object obj) { if(obj instanceof T2) { @SuppressWarnings("unchecked") T2<A,B> o = (T2<A,B>)obj; return (first == null ? o.first == null : first.equals(o.first)) && (second == null ? o.second == null : second.equals(o.second)); } return false; } @SuppressWarnings("unchecked") public int compareTo(T2<A,B> o) { int res = ((Comparable<A>)first).compareTo(o.first); if(res != 0) return res; return ((Comparable<B>)second).compareTo(o.second); } public A getFirst() { return first; } public B getSecond() { return second; } } public static class T3<A,B,C> implements Comparable<T3<A,B,C>> { private final T2<A,B> tuple; private final C third; public T3(A a, B b, C c) { tuple = new T2<A,B>(a, b); third = c; } public String toString() { return "(" + tuple.getFirst() + ", " + tuple.getSecond() + ", " + third +")"; } public int hashCode() { int result = tuple.hashCode(); if(third != null) 454 Thinking in Java, 4th Edition Annotated Solution Guide result = result * 37 + third.hashCode(); return result; } public boolean equals(Object obj) { if(obj instanceof T3) { @SuppressWarnings("unchecked") T3<A,B,C> o = (T3<A,B,C>)obj; return (third == null ? o.third == null : third.equals(o.third)) && tuple.equals(o.tuple); } return false; } @SuppressWarnings("unchecked") public int compareTo(T3<A,B,C> o) { int res = tuple.compareTo(o.tuple); if(res != 0) return res; return ((Comparable<C>)third).compareTo(o.third); } public A getFirst() { return tuple.getFirst(); } public B getSecond() { return tuple.getSecond(); } public C getThird() { return third; } } public static class T4<A,B,C,D> implements Comparable<T4<A,B,C,D>> { private final T3<A,B,C> tuple; private final D fourth; public T4(A a, B b, C c, D d) { tuple = new T3<A,B,C>(a, b, c); fourth = d; } public String toString() { return "(" + tuple.getFirst() + ", " + tuple.getSecond() + ", " + tuple.getThird() + ", " + fourth + ")"; } public int hashCode() { int result = tuple.hashCode(); if(fourth != null) result = result * 37 + fourth.hashCode(); return result; } public boolean equals(Object obj) { if(obj instanceof T4) { @SuppressWarnings("unchecked") T4<A,B,C,D> o = (T4<A,B,C,D>)obj; return (fourth == null ? Containers in Depth 455 o.fourth == null : fourth.equals(o.fourth)) && tuple.equals(o.tuple); } return false; } @SuppressWarnings("unchecked") public int compareTo(T4<A,B,C,D> o) { int res = tuple.compareTo(o.tuple); if(res != 0) return res; return ((Comparable<D>)fourth).compareTo(o.fourth); } public A getFirst() { return tuple.getFirst(); } public B getSecond() { return tuple.getSecond(); } public C getThird() { return tuple.getThird(); } public D getFourth() { return fourth; } } public static class T5<A,B,C,D,E> implements Comparable<T5<A,B,C,D,E>> { private final T4<A,B,C,D> tuple; private final E fifth; public T5(A a, B b, C c, D d, E e) { tuple = new T4<A,B,C,D>(a, b, c, d); fifth = e; } public String toString() { return "(" + tuple.getFirst() + ", " + tuple.getSecond() + ", " + tuple.getThird() + ", " + tuple.getFourth() + ", " + fifth + ")"; } public int hashCode() { int result = tuple.hashCode(); if(fifth != null) result = result * 37 + fifth.hashCode(); return result; } public boolean equals(Object obj) { if(obj instanceof T5) { @SuppressWarnings("unchecked") T5<A,B,C,D,E> o = (T5<A,B,C,D,E>)obj; return (fifth == null ? o.fifth == null : fifth.equals(o.fifth)) && tuple.equals(o.tuple); } return false; } @SuppressWarnings("unchecked") public int compareTo(T5<A,B,C,D,E> o) { 456 Thinking in Java, 4th Edition Annotated Solution Guide int res = tuple.compareTo(o.tuple); if(res != 0) return res; return ((Comparable<E>)fifth).compareTo(o.fifth); } public public public public public A B C D E getFirst() { return tuple.getFirst(); } getSecond() { return tuple.getSecond(); } getThird() { return tuple.getThird(); } getFourth() { return tuple.getFourth(); } getFifth() { return fifth; } } public static <A,B> T2<A,B> tuple(A a, B b) { return new T2<A,B>(a, b); } public static <A,B,C> T3<A,B,C> tuple(A a, B b, C c) { return new T3<A,B,C>(a, b, c); } public static <A,B,C,D> T4<A,B,C,D> tuple(A a, B b, C c, D d) { return new T4<A,B,C,D>(a, b, c, d); } public static <A,B,C,D,E> T5<A,B,C,D,E> tuple(A a, B b, C c, D d, E e) { return new T5<A,B,C,D,E>(a, b, c, d, e); } } public class E28_Tuple { public static void main(String[] args) { Tuple.T5<String,Integer,Boolean,Short,Long> t5_1 = Tuple.tuple("a", 1, false, (short)2, 3L), t5_2 = Tuple.tuple("b", 2, true, (short)3, 4L); print("t5_1 = " + t5_1); print("t5_2 = " + t5_2); print("t5_1.equals(t5_1) = " + t5_1.equals(t5_1)); print("t5_1.equals(t5_2) = " + t5_1.equals(t5_2)); print("t5_1.compareTo(t5_1) = " + t5_1.compareTo(t5_1)); print("t5_1.compareTo(t5_2) = " + t5_1.compareTo(t5_2)); } } /* Output: t5_1 = (a, 1, false, 2, 3) t5_2 = (b, 2, true, 3, 4) t5_1.equals(t5_1) = true t5_1.equals(t5_2) = false t5_1.compareTo(t5_1) = 0 t5_1.compareTo(t5_2) = -1 *///:~ Containers in Depth 457 We refactor the code in net/mindview/util/Tuple.java to incorporate the Comparable for each type of Tuple. When a class extends a concrete Comparable class (as T3<A,B,C> extends T2<A,B> in the original program), and adds a significant field, there is no way to simultaneously implement compareTo( ) correctly. Therefore we use composition instead of inheritance. The same holds true for equals( ). For more information see Effective Java by Joshua Bloch (Addison-Wesley, 2001). Our solution follows Bloch’s recommendations. Exercise 29 //: containers/E29_ListPerformance2.java // {Args: 100 500} Small to keep build testing short /****************** Exercise 29 ***************** * Modify ListPerformance.java so that the Lists hold * String objects instead of Integers. Use a Generator * from the Arrays chapter to create test values. ***********************************************/ package containers; import java.util.*; import net.mindview.util.*; public class E29_ListPerformance2 { static Generator<String> gen = new CountingGenerator.String(); static Random rand = new Random(); static int reps = 1000; static List<Test<List<String>>> tests = new ArrayList<Test<List<String>>>(); static List<Test<LinkedList<String>>> qTests = new ArrayList<Test<LinkedList<String>>>(); static { tests.add(new Test<List<String>>("add") { int test(List<String> list, TestParam tp) { int loops = tp.loops; int listSize = tp.size; for(int i = 0; i < loops; i++) { list.clear(); for(int j = 0; j < listSize; j++) list.add(gen.next()); } return loops * listSize; } }); tests.add(new Test<List<String>>("get") { 458 Thinking in Java, 4th Edition Annotated Solution Guide int test(List<String> list, TestParam tp) { int loops = tp.loops * reps; int listSize = list.size(); for(int i = 0; i < loops; i++) list.get(rand.nextInt(listSize)); return loops; } }); tests.add(new Test<List<String>>("set") { int test(List<String> list, TestParam tp) { int loops = tp.loops * reps; int listSize = list.size(); for(int i = 0; i < loops; i++) list.set(rand.nextInt(listSize), "Hello"); return loops; } }); tests.add(new Test<List<String>>("iteradd") { int test(List<String> list, TestParam tp) { final int LOOPS = 1000000; int half = list.size() / 2; ListIterator<String> it = list.listIterator(half); for(int i = 0; i < LOOPS; i++) it.add("Hello"); return LOOPS; } }); tests.add(new Test<List<String>>("insert") { int test(List<String> list, TestParam tp) { int loops = tp.loops; for(int i = 0; i < loops; i++) // Minimize random-access cost list.add(5, "Hello"); return loops; } }); tests.add(new Test<List<String>>("remove") { int test(List<String> list, TestParam tp) { int loops = tp.loops; int size = tp.size; for(int i = 0; i < loops; i++) { list.clear(); list.addAll(CollectionData.list( new CountingGenerator.String(), size)); while(list.size() > 5) list.remove(5); // Minimize random-access cost } Containers in Depth 459 return loops * size; } }); // Tests for queue behavior: qTests.add(new Test<LinkedList<String>>("addFirst") { int test(LinkedList<String> list, TestParam tp) { int loops = tp.loops; int size = tp.size; for(int i = 0; i < loops; i++) { list.clear(); for(int j = 0; j < size; j++) list.addFirst("Hello"); } return loops * size; } }); qTests.add(new Test<LinkedList<String>>("addLast") { int test(LinkedList<String> list, TestParam tp) { int loops = tp.loops; int size = tp.size; for(int i = 0; i < loops; i++) { list.clear(); for(int j = 0; j < size; j++) list.addLast("Hello"); } return loops * size; } }); qTests.add( new Test<LinkedList<String>>("rmFirst") { int test(LinkedList<String> list, TestParam tp) { int loops = tp.loops; int size = tp.size; for(int i = 0; i < loops; i++) { list.clear(); list.addAll(CollectionData.list( new CountingGenerator.String(), size)); while(list.size() > 0) list.removeFirst(); } return loops * size; } }); qTests.add(new Test<LinkedList<String>>("rmLast") { int test(LinkedList<String> list, TestParam tp) { int loops = tp.loops; int size = tp.size; 460 Thinking in Java, 4th Edition Annotated Solution Guide for(int i = 0; i < loops; i++) { list.clear(); list.addAll(CollectionData.list( new CountingGenerator.String(), size)); while(list.size() > 0) list.removeLast(); } return loops * size; } }); } static class ListTester extends Tester<List<String>> { public ListTester(List<String> container, List<Test<List<String>>> tests) { super(container, tests); } // Fill to the appropriate size before each test: @Override protected List<String> initialize(int size){ container.clear(); container.addAll(CollectionData.list( new CountingGenerator.String(), size)); return container; } // Convenience method: public static void run(List<String> list, List<Test<List<String>>> tests) { new ListTester(list, tests).timedTest(); } } public static void main(String[] args) { if(args.length > 0) Tester.defaultParams = TestParam.array(args); // Can only do these two tests on an array: Tester<List<String>> arrayTest = new Tester<List<String>>(null, tests.subList(1, 3)){ // This will be called before each test. It // produces a non-resizeable array-backed list: @Override protected List<String> initialize(int size) { String[] sa = Generated.array(String.class, new CountingGenerator.String(), size); return Arrays.asList(sa); } }; arrayTest.setHeadline("Array as List"); arrayTest.timedTest(); Tester.defaultParams = TestParam.array( Containers in Depth 461 10, 5000, 100, 5000, 1000, 1000, 10000, 200); if(args.length > 0) Tester.defaultParams = TestParam.array(args); ListTester.run(new ArrayList<String>(), tests); ListTester.run(new LinkedList<String>(), tests); ListTester.run(new Vector<String>(), tests); Tester.fieldWidth = 12; Tester<LinkedList<String>> qTest = new Tester<LinkedList<String>>( new LinkedList<String>(), qTests); qTest.setHeadline("Queue tests"); qTest.timedTest(); } } /* Output: (Sample) --- Array as List --size get set 100 106 104 --------------------- ArrayList --------------------size add get set iteradd insert remove 100 541 97 99 322 791 748 --------------------- LinkedList --------------------size add get set iteradd insert remove 100 471 175 159 447 305 546 ----------------------- Vector ----------------------size add get set iteradd insert remove 100 485 126 191 310 905 881 -------------------- Queue tests -------------------size addFirst addLast rmFirst rmLast 100 69 78 516 504 *///:~ Exercise 30 //: containers/E30_SortPerformance.java // {Args: 1000 500} /****************** Exercise 30 ***************** * Compare the performance of Collections.sort() * between an ArrayList and a LinkedList. ***********************************************/ package containers; import java.util.*; import net.mindview.util.*; public class E30_SortPerformance { static List<Test<List<Integer>>> tests = new ArrayList<Test<List<Integer>>>(); 462 Thinking in Java, 4th Edition Annotated Solution Guide static { tests.add(new Test<List<Integer>>("sort") { int test(List<Integer> list, TestParam tp) { for(int i = 0; i < tp.loops; i++) { list.clear(); list.addAll(CollectionData.list( new RandomGenerator.Integer(), tp.size)); Collections.sort(list); } return tp.loops; } }); } static class ListTester extends Tester<List<Integer>> { public ListTester(List<Integer> container, List<Test<List<Integer>>> tests) { super(container, tests); } // Fill to the appropriate size before each test: @Override protected List<Integer> initialize(int size){ container.clear(); container.addAll(CollectionData.list( new RandomGenerator.Integer(), size)); return container; } // Convenience method: public static void run(List<Integer> list, List<Test<List<Integer>>> tests) { new ListTester(list, tests).timedTest(); } } public static void main(String[] args) { if(args.length > 0) Tester.defaultParams = TestParam.array(args); ListTester.run(new ArrayList<Integer>(), tests); ListTester.run(new LinkedList<Integer>(), tests); } } /* Output: (Sample) - ArrayList size sort 1000 529815 - LinkedList size sort 1000 527436 *///:~ Containers in Depth 463 Both maps perform similarly in Collections.sort( ), which makes sense given the way we implement it (for more details consult the JDK). Don’t be fooled by benchmarking results. TIJ4’s Microbenchmarking dangers section (see the index) explains some common traps to avoid. Exercise 31 //: containers/E31_StringContainer.java /****************** Exercise 31 ****************** * Create a container that encapsulates an array of * String, and that only allows adding Strings and * getting Strings, so that there are no casting * issues during use. If the internal array isn’t big * enough for the next add, your container should * automatically resize it. In main(), compare the * performance of your container with an * ArrayList<String>. ***********************************************/ package containers; import java.util.*; class StringContainer { private String[] array; private int index; private static final int INCR = 255; public StringContainer() { array = new String[10]; } public StringContainer(int sz) { array = new String[sz]; } public void add(String s) { if(index >= array.length) { String[] tmp = new String[array.length + INCR]; for(int i = 0; i < array.length; i++) tmp[i] = array[i]; index = array.length; array = tmp; } array[index++] = s; } public String get(int i) { return array[i]; } public int size() { return index; } } 464 Thinking in Java, 4th Edition Annotated Solution Guide public class E31_StringContainer { static final int LOOPS = 10000; static List<Test<List<String>>> alTests = new ArrayList<Test<List<String>>>(); static List<Test<StringContainer>> scTests = new ArrayList<Test<StringContainer>>(); static { alTests.add(new Test<List<String>>("addget") { int test(List<String> list, TestParam tp) { for(int i = 0; i < LOOPS; i++) { list.add(Integer.toString(i)); list.get(i); } return LOOPS; } }); scTests.add(new Test<StringContainer>("addget") { int test(StringContainer sc, TestParam tp) { for(int i = 0; i < LOOPS; i++) { sc.add(Integer.toString(i)); sc.get(i); } return LOOPS; } }); } public static void main(String[] args) { // Parameters are also hard-coded inside tests. Tester.defaultParams = TestParam.array(LOOPS, 1); Tester.run(new ArrayList<String>(LOOPS), alTests); Tester.run(new StringContainer(LOOPS), scTests); } } /* Output: (Sample) - ArrayList size addget 10000 1266 StringContainer size addget 10000 574 *///:~ This simple though somewhat inaccurate test is from TIJ4. Note that the ArrayList performance is close to what you might hand-code; this emphasizes that you shouldn’t waste time writing containers yourself when you can use the standard library. Wait to optimize code until you find the bottleneck by thoroughly profiling your application. Containers in Depth 465 Exercise 32 //: containers/E32_IntContainer.java /****************** Exercise 32 ****************** * Repeat the previous exercise for a container of * int, and compare the performance to an * ArrayList<Integer>. In your performance comparison, * include the process of incrementing each object * in the container. ***********************************************/ package containers; import java.util.*; class IntContainer { private int[] array; private int index; private static final int INCR = 255; public IntContainer() { array = new int[10]; } public IntContainer(int sz) { array = new int[sz]; } public void add(int i) { if(index >= array.length) { int[] tmp = new int[array.length + INCR]; for(int j = 0; j < array.length; j++) tmp[j] = array[j]; index = array.length; array = tmp; } array[index++] = i; } public int get(int i) { return array[i]; } public void set(int i, int val) { array[i] = val; } public int size() { return index; } } public class E32_IntContainer { static final int LOOPS = 10000; static List<Test<List<Integer>>> alTests = new ArrayList<Test<List<Integer>>>(); static List<Test<IntContainer>> icTests = new ArrayList<Test<IntContainer>>(); static { alTests.add(new Test<List<Integer>>("addget") { 466 Thinking in Java, 4th Edition Annotated Solution Guide int test(List<Integer> list, TestParam tp) { for(int i = 0; i < LOOPS; i++) { list.add(i); list.set(i, list.get(i) + 1); } return LOOPS; } }); icTests.add(new Test<IntContainer>("addget") { int test(IntContainer ic, TestParam tp) { for(int i = 0; i < LOOPS; i++) { ic.add(i); ic.set(i, ic.get(i) + 1); } return LOOPS; } }); } public static void main(String[] args) { // Parameters are also hard-coded inside tests. Tester.defaultParams = TestParam.array(LOOPS, 1); Tester.run(new ArrayList<Integer>(LOOPS), alTests); Tester.run(new IntContainer(LOOPS), icTests); } } /* Output: (Sample) - ArrayList size addget 10000 8975 IntContainer size addget 10000 491 *///:~ IntContainer is significantly faster than ArrayList. Exercise 33 //: containers/E33_ListPerformance3.java // {RunByHand} (Takes too long during the build process) /******************** Exercise 33 ************************ * Create a FastTraversalLinkedList that internally uses a * LinkedList for rapid insertions and removals, and an * ArrayList for rapid traversals and get() operations. * Test it by modifying ListPerformance.java. *********************************************************/ package containers; Containers in Depth 467 import java.util.*; import net.mindview.util.*; class FastTraversalLinkedList<T> extends AbstractList<T> { private class FlaggedArrayList { private FlaggedLinkedList companion; boolean changed = false; private ArrayList<T> list = new ArrayList<T>(); public void addCompanion(FlaggedLinkedList other) { companion = other; } private void synchronize() { if(companion.changed) { list = new ArrayList<T>(companion.list); companion.changed = false; } } public T get(int index) { synchronize(); return list.get(index); } public int size() { synchronize(); return list.size(); } public T remove(int index) { synchronize(); changed = true; return list.remove(index); } public boolean remove(Object item) { synchronize(); changed = true; return list.remove(item); } // Always broadcasted to the companion container, too. public void clear() { list.clear(); changed = false; } } private class FlaggedLinkedList { private FlaggedArrayList companion; boolean changed = false; LinkedList<T> list = new LinkedList<T>(); public void addCompanion(FlaggedArrayList other) { companion = other; 468 Thinking in Java, 4th Edition Annotated Solution Guide } private void synchronize() { if(companion.changed) { list = new LinkedList<T>(companion.list); companion.changed = false; } } public void add(int index, T item) { synchronize(); changed = true; list.add(index, item); } public boolean add(T item) { synchronize(); changed = true; return list.add(item); } public Iterator<T> iterator() { synchronize(); return list.iterator(); } // Always broadcasted to the companion container, too. public void clear() { list.clear(); changed = false; } } private FlaggedArrayList aList = new FlaggedArrayList(); private FlaggedLinkedList lList = new FlaggedLinkedList(); // Connect the two so they can synchronize: { aList.addCompanion(lList); lList.addCompanion(aList); } public int size() { return aList.size(); } public T get(int arg) { return aList.get(arg); } public void add(int index, T item) { lList.add(index, item); } public boolean add(T item) { return lList.add(item); } // Through testing, we discovered that the ArrayList is // actually much faster for removals than the LinkedList: public T remove(int index) { Containers in Depth 469 return aList.remove(index); } public boolean remove(Object item) { return aList.remove(item); } public Iterator<T> iterator() { return lList.iterator(); } public void clear() { aList.clear(); lList.clear(); } } public class E33_ListPerformance3 { static Random rand = new Random(); static int reps = 1000; static List<Test<List<Integer>>> tests = new ArrayList<Test<List<Integer>>>(); static { tests.add(new Test<List<Integer>>("iter") { int test(List<Integer> list, TestParam tp) { for(int i = 0; i < tp.loops; i++) { Iterator<Integer> it = list.iterator(); while(it.hasNext()) it.next(); // Produces value } return tp.loops; } }); tests.add(new Test<List<Integer>>("get") { int test(List<Integer> list, TestParam tp) { int loops = tp.loops * reps; int listSize = list.size(); for(int i = 0; i < loops; i++) list.get(rand.nextInt(listSize)); return loops; } }); tests.add(new Test<List<Integer>>("insert") { int test(List<Integer> list, TestParam tp) { int loops = tp.loops; for(int i = 0; i < loops; i++) list.add(5, 47); // Minimize random-access cost return loops; } }); 470 Thinking in Java, 4th Edition Annotated Solution Guide tests.add(new Test<List<Integer>>("remove_I ") { int test(List<Integer> list, TestParam tp) { int count = 0; for(int i = list.size() / 2 ; i < list.size(); i++) { ++count; list.remove(i); } return count; } }); tests.add(new Test<List<Integer>>("remove_O") { int test(List<Integer> list, TestParam tp) { int count = 0; for(int i = list.size() / 2 ; i < list.size(); i++) { ++count; list.remove(list.get(i)); } return count; } }); } static class ListTester extends Tester<List<Integer>> { public ListTester(List<Integer> container, List<Test<List<Integer>>> tests) { super(container, tests); } // Fill to the appropriate size before each test: @Override protected List<Integer> initialize(int size){ container.clear(); container.addAll(new CountingIntegerList(size)); return container; } // Convenience method: public static void run(List<Integer> list, List<Test<List<Integer>>> tests) { new ListTester(list, tests).timedTest(); } } public static void main(String[] args) { ListTester.run(new ArrayList<Integer>(), tests); ListTester.run(new LinkedList<Integer>(), tests); ListTester.run( new FastTraversalLinkedList<Integer>(), tests); } } ///:~ Containers in Depth 471 When you perform an operation on either FlaggedArrayList or FlaggedLinkedList, it checks for changes in the other and self-updates, setting or clearing both lists’ changed flags. You can see the two lists connected in the instance initialization clause. We implement only the methods used in the tests in FlaggedArrayList and FlaggedLinkedList. The FastTraversalLinkedList forwards method calls to the most efficient list. The insertion and removal tests don’t use the iterator (which would confound the results). Also, we distinguish “remove at location” as ‘remove_I’ and “remove by object” as ‘remove_O’. The results of one run are as follows: ----------------- ArrayList ----------------size iter get insertremove_I remove_O 10 1302 106 4231 12329 16513 100 4104 96 3701 1005 47717 1000 33924 95 4846 1108 9171 10000 339132 92 14295 4124 85110 ----------------- LinkedList ----------------size iter get insertremove_I remove_O 10 1104 125 676 118204 120866 100 2489 165 68 783 34911 1000 24384 769 69 1071 10800 10000 245605 13097 87 14333 98771 ---------- FastTraversalLinkedList ---------size iter get insertremove_I remove_O 10 512 108 235 26553 20298 100 2491 108 195 1553 2008 1000 24158 107 80 1292 12888 10000 243113 103 97 4062 94922 Surprisingly, removing an element in the middle of an ArrayList turns out to be far less damaging than removing one from the middle of a LinkedList. Iteration is somewhat better in LinkedList, which is noticeably faster only with insertions. FastTraversalLinkedList’s repeated insertion and removal entails list copying which can negatively affect overhead. Exercise 34 //: containers/E34_StringSetPerformance.java // {Args: 1000 500} Medium to keep build testing reasonable /******************** Exercise 34 ************************ * Modify SetPerformance.java so that the Sets hold String 472 Thinking in Java, 4th Edition Annotated Solution Guide * objects instead of Integers. Use a Generator from the * Arrays chapter to create test values. *********************************************************/ package containers; import java.util.*; import net.mindview.util.*; public class E34_StringSetPerformance { static List<Test<Set<String>>> tests = new ArrayList<Test<Set<String>>>(); static { tests.add(new Test<Set<String>>("add") { int test(Set<String> set, TestParam tp) { Generator<String> gen; int loops = tp.loops; int size = tp.size; for(int i = 0; i < loops; i++) { set.clear(); // Always starts the sequence from the beginning. gen = new CountingGenerator.String(); for(int j = 0; j < size; j++) set.add(gen.next()); } return loops * size; } }); tests.add(new Test<Set<String>>("contains") { int test(Set<String> set, TestParam tp) { Generator<String> gen = new CountingGenerator.String(5); int loops = tp.loops; int span = tp.size * 2; for(int i = 0; i < loops; i++) for(int j = 0; j < span; j++) set.contains(gen.next()); return loops * span; } }); tests.add(new Test<Set<String>>("iterate") { int test(Set<String> set, TestParam tp) { int loops = tp.loops * 10; for(int i = 0; i < loops; i++) { Iterator<String> it = set.iterator(); while(it.hasNext()) it.next(); } return loops * set.size(); Containers in Depth 473 length > 0) Tester. tests).Integer> map.Integer>>>(). static { tests. 4th Edition Annotated Solution Guide . int size = tp. } public static void main(String[] args) { if(args. j++) map. tests). } 474 Thinking in Java.LinkedHashSet ---------size add contains iterate 1000 528 338 110 *///:~ Exercise 35 //: containers/E35_MapPerformance2.Integer>>> tests = new ArrayList<Test<Map<Integer.java // {Args: 100 5000} Small to keep build testing short /******************** Exercise 35 ************************ * Modify MapPerformance.util.} }). Tester. for(int i = 0.run(new HashSet<String>(). tests). j). i++) { map. i < loops. Tester.java to include tests of SlowMap.defaultParams = TestParam. public class E35_MapPerformance2 { static List<Test<Map<Integer.run(new LinkedHashSet<String>(). *********************************************************/ package containers.fieldWidth = 10.array(args). j < size.TreeSet ------------size add contains iterate 1000 687 497 58 ------------.Integer>>("put") { int test(Map<Integer.HashSet ------------size add contains iterate 1000 517 360 66 ---------.size.add(new Test<Map<Integer. TestParam tp) { int loops = tp. } } /* Output: (Sample) ------------. import java.loops. Tester.run(new TreeSet<String>(). for(int j = 0.clear().*. Tester.put(j. j < span.run(new SlowMap2<Integer. tests). } return loops * map.add(new Test<Map<Integer.run(new WeakHashMap<Integer. return loops * span. Tester. Tester.get(j). tests).Integer>>("get") { int test(Map<Integer. tests. tests).run(new HashMap<Integer. i < loops.size * 2. Tester. Tester. for(int i = 0. Tester.iterator(). } } /* Output: (Sample) ---------.Integer>().TreeMap ---------size put get iterate 100 529 177 34 ---------.Integer>().Integer>().run( new IdentityHashMap<Integer.Integer>(). tests).Integer>(). i++) for(int j = 0.run(new Hashtable<Integer.array(args). i < loops.Entry<Integer.return loops * size.run(new TreeMap<Integer. while(it. tests.add(new Test<Map<Integer. Tester.loops.Integer> map. } }).Integer>().hasNext()) it.tests). } }).Integer>().next().HashMap ---------size put get iterate 100 178 43 64 ------. } }).Integer>>("iterate") { int test(Map<Integer.loops * 10. i ++) { Iterator<Map.length > 0) Tester.defaultParams = TestParam.entrySet(). for(int i = 0.size(). Integer>> it = map.LinkedHashMap ------size put get iterate Containers in Depth 475 . j++) map.run(new LinkedHashMap<Integer. Tester. TestParam tp) { int loops = tp. tests). } public static void main(String[] args) { if(args.Integer> map. tests). TestParam tp) { int loops = tp. int span = tp. though. and * modify get() to use Collections.IdentityHashMap -----size put get iterate 100 270 222 47 -------. protected MapEntry<K.V>> entries = new ArrayList<MapEntry<K.util. Its performance degrades sharply as a function of size.java.” SlowMap2 clearly lives up to its name.100 221 55 28 -----.V> { protected List<MapEntry<K. Exercise 36 //: containers/E36_MapEntrySlowMap. * Using MapPerformance. 4th Edition Annotated Solution Guide . Try running the test with “size 1000.*.V> entry : entries) if(entry.WeakHashMap -------size put get iterate 100 288 174 83 --------.SlowMap2 ---------size put get iterate 100 2078 1396 79 *///:~ Here. @SuppressWarnings("unchecked") class MapEntrySlowMap1<K. test the speed of your * new Map. ***********************************************/ package containers.V> extends AbstractMap<K.V>>().Hashtable --------size put get iterate 100 163 102 49 ---------. * Verify that the modified version works correctly. // Does not support 'null' keys. // Returns the entry matching the specified key. we use the SlowMap2 class. the more solid Map implementation. * it holds a single ArrayList of MapEntry objects.equals(key)) 476 Thinking in Java.binarySearch() to * look up the key. Now change the put() method so that it * performs a sort() after each pair is entered.java // {Args: 100 5000} Small to keep build testing short /****************** Exercise 36 ***************** * Modify SlowMap so that instead of two ArrayLists.getKey(). Compare the performance of the new * version with the old ones.V> getEntry(Object key) { for(MapEntry<K. import java. or null. V> entry = getEntry(key).V>>() { private int index = -1.Entry<K. } return oldValue.setValue(value).V> oldEntry = getEntry(key). } private EntrySet entrySet = new EntrySet().getValue(). Containers in Depth 477 .V>(key.1. MapEntry<K.size() . return entry == null ? null : entry.V>> { @Override public Iterator<Map. public boolean hasNext() { return index < entries.Entry<K. V value) { if(key == null) throw new NullPointerException(). } public void remove() { if(!canRemove) throw new IllegalStateException(). canRemove = false. value)). return null. if(oldEntry == null) entries.remove(index--). } @Override public V put(K key.getValue(). @Override public Set<Map.Entry<K.add(new MapEntry<K.return entry. } @Override public V get(Object key) { if(key == null) throw new NullPointerException(). entries. return entries. boolean canRemove. oldEntry. MapEntry<K.V> next() { canRemove = true. } public Map. V oldValue = null.Entry<K.get(index). else { oldValue = oldEntry.V>> entrySet() { return entrySet. } // Uses the 'Flyweight' pattern private class EntrySet extends AbstractSet<Map. ++index.V>> iterator() { return new Iterator<Map.Entry<K. } @Override public boolean remove(Object o) { if(contains(o)) { entries.equals(getEntry(e. } return false.clear().binarySearch(entries.V> { final MapEntryComp<K.size().compareTo(o2.V> o1. V value) { if(key == null) throw new NullPointerException().V>(). return null.V>)o).} }. null). return true. } @Override public int size() { return entries.V> o2) { Comparable<K> c1 = (Comparable<K>)o1.V> comp = new MapEntryComp<K.remove((MapEntry<K. } @Override public void clear() { entries. 4th Edition Annotated Solution Guide .getKey())). // Returns the entry matching the specified key. } } @SuppressWarnings("unchecked") class MapEntrySlowMap2<K. searchEntry.getKey().V> extends MapEntrySlowMap1<K. } @Override public boolean contains(Object o) { if(o instanceof MapEntry) { MapEntry<K. return e.V> e = (MapEntry<K.V>)o. MapEntry<K.V>((K)key.V> getEntry(Object key) { MapEntry<K. } @Override public V put(K key. 478 Thinking in Java.V> implements Comparator<MapEntry<K. if(index >= 0) return entries.V>> { public int compare(MapEntry<K.get(index). return c1. or null. } } } @SuppressWarnings("unchecked") class MapEntryComp<K.V> searchEntry = new MapEntry<K.getKey()). comp). int index = Collections. @Override protected MapEntry<K. } return false. System.Integer>(). if(args.defaultParams = TestParam. } else { oldValue = oldEntry. System..run( new IdentityHashMap<Integer. E35_MapPerformance2. E16_SlowMapTest. Tester. V oldValue = null.length > 0) Tester.tests).run(new WeakHashMap<Integer. E35_MapPerformance2.out.test( new MapEntrySlowMap1<Integer. E35_MapPerformance2. E35_MapPerformance2.run(new MapEntrySlowMap1<Integer. E16_SlowMapTest.. E35_MapPerformance2.array(args).Integer>().Integer>(). E35_MapPerformance2. if(oldEntry == null) { entries.Integer>().setValue(value). comp).run(new LinkedHashMap<Integer. Tester.. oldEntry.sort(entries.Integer>(). value)). Tester.MapEntry<K.run(new SlowMap2<Integer.println("Testing MapEntrySlowMap2").Integer>().println("Testing MapEntrySlowMap1").Integer>().String>()). E35_MapPerformance2.String>()).run(new MapEntrySlowMap2<Integer. Tester.run(new HashMap<Integer. } } /* Output: (Sample) Testing MapEntrySlowMap1 Containers in Depth 479 . Tester.tests).V> oldEntry = getEntry(key).run(new TreeMap<Integer.tests).Integer>(). E35_MapPerformance2. Tester. Tester. } } public class E36_MapEntrySlowMap { public static void main(String[] args) { // Testing correctness.out.tests)..tests).add(new MapEntry<K.test( new MapEntrySlowMap2<Integer. E35_MapPerformance2. // Measuring performance. Tester.getValue(). } return oldValue.Integer>().run(new Hashtable<Integer.tests). Tester.tests). Collections.tests).V>(key.tests). 8=I0. 2. 23=X0.get(11): L0 map. 3. 18=S0. L0. Y0] {0=A0. 12=M0. 11=L0. O0. 20. Keys: [1. 18. 7=H0.WeakHashMap -------size put get iterate 480 Thinking in Java. 4. O0. G0. 4. Y0] {0=A0. 15=P0. S0.HashMap ---------size put get iterate 100 499 213 168 ------. U0. 21. 24] map. 24=Y0} map. 3=D0. 11=L0. Q0. 12=M0. K0. 6.MapEntrySlowMap1 Size = 25. 6. 10. 16. 9=J0. T0. 13. 4. R0. 15=P0. 5. Keys: [1. 17=R0. 3. 14=O0. 13.get(11): L0 map. R0. 19. 8. K0. 17=R0. 24] Values: [A0. W0. X0. 4=E0. 9. 9.containsValue("F0"): true First key in map: 0 Size = 24. J0. 19. 14=O0. 6=G0. 2=C0. 8. P0. 16. 21. 21=V0. 7. 5. N0. 5. 12. F0. 7. 22. 14. Keys: [0. 23. 9=J0. 21. 10. 17. H0. 10. 16.containsValue("F0"): true First key in map: 0 Size = 24. 2. 18. J0. 22. L0. 15. 15. D0. Q0. 19.isEmpty(): true ---------. 13. 6. 19=T0. 11. P0. E0. 17. 1. 23.isEmpty(): true map. 1=B0. 3. 12. 19. 13. G0. 20=U0. M0. 21. 24] map. 21=V0.isEmpty(): true Testing MapEntrySlowMap2 MapEntrySlowMap2 Size = 25. 10. S0. 18. 7=H0. 22. 19=T0. 12. 2=C0. H0. 24] Values: [A0. 2. 4. I0. T0. 22. 16=Q0. 18. 24=Y0} map. 14. 10=K0. 3.containsKey(11): true map. 7. 11. 18=S0. 8=I0. V0. 22=W0. 5.LinkedHashMap ------size put get iterate 100 704 293 115 -----. 20. 2. 4th Edition Annotated Solution Guide . 11. 16. 13=N0. Keys: [0. N0. 20=U0. 15. 14. 9. F0. 4=E0. 23. 8. 15. 16=Q0. M0. 14. 22=W0. 1=B0. 5=F0. 10=K0. 9. B0. 20. 1. B0.IdentityHashMap -----size put get iterate 100 598 580 181 -------. 23.TreeMap ---------size put get iterate 100 1564 842 148 ---------. W0.containsKey(11): true map. C0. 3=D0. 23=X0. C0. 6=G0. 17. 11. 17. I0.isEmpty(): true map. V0. 6. D0. E0. X0. 7. 20. 8. 5=F0. 12. 13=N0. U0. Containers in Depth 481 . Exercise 37 //: containers/E37_SimpleHashMapArrays. ArrayList<MapEntry<K. public V put(K key.V> pair = new MapEntry<K. Modify MapPerformance.V>(key.V> extends AbstractMap<K. ***********************************************/ package containers.hashCode()) % SIZE. but put( ) is disastrous. Notice that the @SuppressWarnings("unchecked") annotations appear immediately before class definitions. always confine these annotations by placing them near the problem.java to * compare the performance of the two * implementations. @SuppressWarnings("unchecked") ArrayList<MapEntry<K. MapEntry<K.V> { static final int SIZE = 997. if(buckets[index] == null) buckets[index] = new ArrayList<MapEntry<K.abs(key.java // {Args: 100 5000} Small to keep build testing short /****************** Exercise 37 ***************** * Modify SimpleHashMap to use ArrayLists instead * of LinkedLists.MapEntrySlowMap1 -----size put get iterate 100 10334 15029 195 -----.Hashtable --------size put get iterate 100 460 265 219 ---------.V>>().*.100 687 317 220 --------. value).V>>[] buckets = new ArrayList[SIZE]. import java.util.MapEntrySlowMap2 -----size put get iterate 100 28463 1484 196 *///:~ The MapEntrySlowMap2’s get( ) method performs well.V>> bucket = buckets[index]. boolean found = false. class SimpleHashMap6<K.SlowMap2 ---------size put get iterate 100 6238 6228 363 -----. V value) { V oldValue = null. int index = Math. V>> it = bucket.V>>(). 4th Edition Annotated Solution Guide . return oldValue. } public Set<Map.Entry is // removed from the list: int index = Math.abs(key.hasNext()) { MapEntry<K.iterator().V> mpair : bucket) set.next().getKey().getKey().getKey(). return null.equals(key)) return iPair. if(buckets[index] == null) return null. Iterator<MapEntry<K. if(iPair. the Map. it. if(buckets[index] == null) return null.hashCode()) % SIZE.equals(key)) { 482 Thinking in Java. break. // Replace old with new found = true.V> iPair = it.V> iPair = it.Entry<K.V>> set= new HashSet<Map.hasNext()) { MapEntry<K. } public V get(Object key) { int index = Math.hashCode()) % SIZE.V>> bucket : buckets) { if(bucket == null) continue. while(it.equals(key)) { oldValue = iPair. } public V remove(Object key) { // Code is copied from get().V>> it = buckets[index].next(). for(ArrayList<MapEntry<K.abs(key. } return set.getValue(). for(MapEntry<K.V>> entrySet() { Set<Map. } } if(!found) buckets[index]. if(iPair.add(pair).set(pair). while(it.getValue(). } @SuppressWarnings("unchecked") public void clear() { // Effectively erase everything by allocating // a new empty array of buckets: buckets = new ArrayList[SIZE].ListIterator<MapEntry<K.V> iPair : buckets[index]) if(iPair.add(mpair). for(MapEntry<K.listIterator(). except that // before returning the value.Entry<K.Entry<K. V>> bucket : buckets) if(bucket != null) return false.remove(). E35_MapPerformance2. } public boolean isEmpty() { // Could just say return size() == 0. Tester. for(MapEntry<K. } } public class E37_SimpleHashMapArrays { public static void main(String[] args) { if(args.Integer>().equals(key)) return true.run(new SimpleHashMap6<Integer. for(ArrayList<MapEntry<K. return value. } public boolean containsKey(Object key) { // A slight modification of the previous method: for(ArrayList<MapEntry<K.V>> bucket : buckets) if(bucket != null) sz += bucket.getValue().tests). return sz.V>> bucket : buckets) { if(bucket == null) continue.run(new SimpleHashMap4<Integer. Tester.array(args).length > 0) Tester.V> mPair : bucket) if(mPair. } } return null. but this is faster: int sz = 0.Integer>().// Changes are here: V value = iPair.tests). } public int size() { // Could rely on the inherited implementation. Tester.defaultParams = TestParam. Containers in Depth 483 . which // returns entrySet(). // but this is faster: for(ArrayList<MapEntry<K.getKey().size(). return true. E35_MapPerformance2.run(new SimpleHashMap5<Integer. // Removes the last fetched value: it.size(). } return false.Integer>(). import net.*. SimpleHashMap5 uses its own singly-linked list. 484 Thinking in Java.util. and * determine the load factor. * then run your lookup speed test again on the new map. then attempt to increase the speed * by making a new HashMap with a larger initial * capacity and copying the old map into the new one.tests). tc < 1000000. like HashMap from the JDK.mindview.SimpleHashMap5 ------size put get iterate 100 118 54 1389 ------. 4th Edition Annotated Solution Guide .util. The iteration in SimpleHashMap5 is clearly not optimized. Sometimes your own data structure is more efficient than general purpose List objects. tc++) for(int i = 0.SimpleHashMap6 ------size put get iterate 100 278 92 407 *///:~ Earlier exercises explain the SimpleHashMap4 and SimpleHashMap5 classes. import java.E35_MapPerformance2. Check the source code for HashMap in the JDK to see how chaining works.length.DATA[i][0].*.util. fill it with elements.String> filledMap.*. Exercise 38 //: containers/E38_HashMapLoadFactor. import static net.java // {RunByHand} /****************** Exercise 38 ***************** * Look up the HashMap class in the JDK documentation. } } /* Output: (Sample) ------. i < Countries. ***********************************************/ package containers. Test the lookup speed * with this map. i++) { String key = Countries. * Create a HashMap.mindview.Print.SimpleHashMap4 ------size put get iterate 100 293 92 419 ------. int n) { for(int tc = 0. public class E38_HashMapLoadFactor { public static void testGet(Map<String.DATA. t1)).capitals(11)). A load factor of 0 is an empty table.34.. etc.75 * 16) = 12.75 The source code also has the threshold — the value at which rehash( ) is called to reorganize the table. print("map1 : " + (t2 . testGet(map2.filledMap.String>(32). print("map2 : " + (t2 . The default constructor has initial values of: initialCapacity = 16 loadFactor = .String>(). which you must calculate using data from the map.get(key).5 is a halffull table.String> map2 = new HashMap<String.String> map1 = new HashMap<String. 11). map2.currentTimeMillis(). t2 = System. // Initial capacity 32: HashMap<String.t1)). Containers in Depth 485 . So when we add a twelfth element the table rehashes. 0. // Fill to less than threshold: map1.putAll(Countries.currentTimeMillis(). map1’s load factor before executing the test is 11/16 = 0. 11).putAll(map1). as we do here. The table size increases when the number of elements is greater than or equal to (int)(0.69. and map2’s is 11/32 = 0. From the JDK: threshold = (int)(initialCapacity * loadFactor).currentTimeMillis(). } } public static void main(String args[]) { // Initial capacity 16: HashMap<String. testGet(map1. t1 = System. Either call the appropriate HashMap constructor or look up the default source code in the JDK. long t1 = System. long t2 = System. } } ///:~ From TIJ4: Load factor: size/capacity.currentTimeMillis(). There are no public methods to access capacity. // Number of elements private static final double loadFactor = 0.util. // Use a prime initial capacity. Now we have a HashMap with double the default initial capacity.mindview.V> { private int count. ***********************************************/ package containers.String> map = new HashMap<String. the JDK uses a number. determine * the new number of buckets by finding the first prime * number greater than twice the original number of buckets. LinkedList<MapEntry<K. // which is a power of 2: private final static int initialCapacity = 11. private int capacity = initialCapacity. import net.V> extends SimpleHashMap<K. Exercise 39 //: containers/E39_SimpleHashMapRehash.V>>().abs(key.*. if(buckets[index] == null) buckets[index] = new LinkedList<MapEntry<K. int index = Math. to make a HashMap with a larger initial capacity: HashMap<String. boolean found = false. private int threshold = (int)(capacity * loadFactor).java /****************** Exercise 39 ***************** * Invoke a private rehash() method in SimpleHashMap when * the load factor exceeds 0. During rehash.75. } @Override public V put(K key. @SuppressWarnings("unchecked") class SimpleHashMap7<K. import java. 486 Thinking in Java.util.V>> bucket = buckets[index].V> pair = new MapEntry<K. 4th Edition Annotated Solution Guide .Use HashMap(int initialCapacity).String>(32). a one-argument constructor. value).hashCode()) % capacity. MapEntry<K.*.75.75 * 32) = 24.V>(key. V value) { V oldValue = null. and a threshold of (int)(0. The output from one run is: map1 : 8469 map2 : 7671 Lookups make the map with a higher load factor less efficient but higher initial capacity minimizes (or even eliminates) rehash operations. { buckets = new LinkedList[capacity]. break. count = 0. if(iPair. me.next(). // Replace old with new found = true. new capacity = " + capacity).V>>().getKey(). } private boolean isPrime(int candidate) { for(int j = 2.V> me = it.ListIterator<MapEntry<K.getValue()).listIterator(). put(me.V>> it = bucket.getKey(). but it works): while(it. while(it.iterator(). } return oldValue. return true. so it // won't be bothered by what we're about to do: Iterator<Map. ++count. buckets = new LinkedList[capacity]. j++) if(candidate % j == 0) return false.add(pair).Entry<K. buckets[index].getValue(). } } Containers in Depth 487 . // Fill new buckets (crude. } private int nextPrime(int candidate) { while(!isPrime(candidate)) candidate++.println( "Rehashing.V>> it = entrySet(). } private void rehash() { // Points to a new Set of the entries.hasNext()) { Map.equals(key)) { oldValue = iPair. if(buckets[index] == null) buckets[index] = new LinkedList<MapEntry<K. j < candidate. } } if(!found) { if(count >= threshold) rehash(). // Get next prime capacity: capacity = nextPrime(capacity * 2). threshold = (int)(capacity * loadFactor). return candidate.next().hasNext()) { MapEntry<K.Entry<K.V> iPair = it. System. it.set(pair).out. Note the new fields at the top of the class: count keeps track of the number of elements. ALGERIA=Algiers. MOROCCO=Rabat.println(m). and rehash( ) itself. new capacity = 47 Rehashing. SEYCHELLES=Victoria. GABON=Libreville. SENEGAL=Dakar. THE GAMBIA=Banjul. COMOROS=Moroni. We also add code in put( ) to call rehash( ). BOTSWANA=Gaberone.HashMap in the JDK. EGYPT=Cairo. NIGERIA=Abuja. and threshold determines when to call rehash( ). TUNISIA=Tunis} *///:~ The solution is more simple than efficient. new capacity = 23 Rehashing. COTE D'IVOIR (IVORY COAST)=Yamoussoukro.String> m = new SimpleHashMap7<String. ERITREA=Asmara. GUINEA=Conakry. LIBYA=Tripoli. System.out. RWANDA=Kigali. loadFactor is fixed at the default for HashMap. At the bottom of the class there are three new methods: isPrime( ) and nextPrime( ) generate prime numbers. ANGOLA=Luanda. SIERRA LEONE=Freetown. capacity is the current number of buckets (called SIZE in SimpleHashMap. SOMALIA=Mogadishu. NAMIBIA=Windhoek. CAPE VERDE=Praia. SOUTH AFRICA=Pretoria/Cape Town. Some information comes from the source code for java. CENTRAL AFRICAN REPUBLIC=Bangui. UGANDA=Kampala.putAll(Countries. CONGO=Brazzaville. GHANA=Accra. LESOTHO=Maseru. 0.75. MOZAMBIQUE=Maputo. MADAGASCAR=Antananarivo. CAMEROON=Yaounde. TOGO=Lome. SWAZILAND=Mbabane. BURUNDI=Bujumbura. MAURITANIA=Nouakchott. SUDAN=Khartoum.String>(). BENIN=Porto-Novo. KENYA=Nairobi. SAO TOME E PRINCIPE=Sao Tome. 4th Edition Annotated Solution Guide . ETHIOPIA=Addis Ababa. NIGER=Niamey. new capacity = 97 {CHAD=N'djamena. MALAWI=Lilongwe. TANZANIA=Dodoma. BISSAU=Bissau. LIBERIA=Monrovia.java in TIJ4). MAURITIUS=Port Louis. 488 Thinking in Java. EQUATORIAL GUINEA=Malabo. DJIBOUTI=Dijibouti. MALI=Bamako. BURKINA FASO=Ouagadougou. } } /* Output: Rehashing.capitals(50)).util. m.} public class E39_SimpleHashMapRehash { public static void main(String[] args) { SimpleHashMap7<String. while the other methods do not change. initialCapacity is 11. mindview. s2 = s2i. Fill an array * and an ArrayList with objects of your class.) Exercise 40 //: containers/E40_ComparableClass. First we increase the buckets to a new prime number that is more than double the current number. public TwoString(String s1i. then gives it to nextPrime( ).rehash( ) begins by getting an Iterator to the entrySet( ) (a set of all keys and values) for this map.util. which calls isPrime( ) to see if the number is prime.util. (See the java. import java. * using the RandomGenerator generator. Also perform a binary * search using your Comparator.*. For further exercise. Demonstrate that * sorting works properly. class TwoString implements Comparable<TwoString> { String s1. import net. import static net. s2 = " + s2 + "]". This doubles the capacity. and demonstrate * that sorting works properly. the process is not very efficient.util.HashMap source code for a better approach. so the objects are not garbage-collected when we change the bucket container. } public String toString() { return "[s1 = " + s1 + ". The code for entrySet( ) makes a new collection to hold the objects.util. create a lookup table for prime numbers to use in the map. and the entrySet( ) iterator and put( ) reload the elements into the hash table—again. String s2i) { s1 = s1i. the new threshold is calculated. and if not. s2.*. Now make a Comparator that * only cares about the second String.mindview. increments the candidate and repeats its test. effective but inefficient. ***********************************************/ package containers.Print. } Containers in Depth 489 . New capacity creates a new bucket array.java /****************** Exercise 40 ***************** * Create a class containing two String objects * and make it Comparable so that the comparison * only cares about the first String.*. sort(list). Generated. list = " + list). } } public class E40_ComparableClass { public static void main(String[] args) { TwoString[] array = new TwoString[10]. array = " + Arrays. list = " + list). Arrays. print("\nafter sorting.sort(array. comp).generator()).s2. public static Generator<TwoString> generator() { return new Generator<TwoString>() { public TwoString next() { return new TwoString(gen. TwoString. new CompareSecond()). print( "\nafter sorting with CompareSecond.public int compareTo(TwoString rv) { String rvi = rv. Collections.compareTo(rvi).generator(). } private static RandomGenerator.s1. 10)).binarySearch(list. Collections. int index = Collections.asList(array)).array(array. 490 Thinking in Java.String(). Comparator<TwoString> comp = new CompareSecond().list(TwoString. print("\nbefore sorting.asList(array)).compareTo(sc2. } }. TwoString zeroth = list. print("before sorting.sort(list. ArrayList<TwoString> list = new ArrayList<TwoString>( CollectionData. Arrays. print("\nafter sorting.next().String gen = new RandomGenerator.get(0).s2).asList(array)). array = " + Arrays. } } class CompareSecond implements Comparator<TwoString> { public int compare(TwoString sc1. gen. array = " + Arrays. 4th Edition Annotated Solution Guide . comp).sort(array).next()). TwoString sc2) { return sc1. zeroth. print( "\nafter sorting with CompareSecond. list = " + list). return s1. [s1 = qUocIBR. s2 s2 s2 s2 s2 = = = = = cXZJoog]. s2 = IVPJWjR]. s2 = FJQAHxx]. [s1 [s1 [s1 [s1 [s1 = = = = = after sorting with CompareSecond. s2 s2 s2 s2 s2 Containers in Depth 491 . TcQrGse. [s1 = [s1 = PzDyCyR. s2 = GcFOWZn]. s2 = kpTkWng]. array = TcQrGse. s2 = GZMmJMR]. s2 = kZPgwsq]. s2 = euTpnXs]. [s1 = SOSdgHz. [s1 = PzDyCyR. [s1 zqvPZdC. [s1 = after sorting. gLhnfDa]. qUCBbkI. s2 = gLhnfDa]. rcVwhKO]. [s1 oYWMNvq. [s1 = JjhcYVz. [s1 = [s1 = qUCBbkI. s2 = euTpnXs]. [s1 SOSdgHz. s2 = kpTkWng]. [s1 tviIOeV. [s1 = slJrLvp. s2 = BnFGofp]. = kZPgwsq]. s2 = = = = = fFvHVEE]. s2 = ohnYVma]. YNzbrny. s2 = BnFGofp]. tviIOeV. [s1 [s1 [s1 [s1 [s1 = = = = = after sorting. SOSdgHz. s2 = ahKcxrE]] = = = = = HvHqXum. GcFOWZn]. [s1 = opwBTwV. s2 = gLhnfDa]. [s1 = tviIOeV. s2 [s1 = HvHqXum. HvHqXum. s2 = PCQQBnK]. [s1 = TQzGSrL. s2 = GcFOWZn]. JjhcYVz. s2 [s1 = WHkjUrU. s2 = fFvHVEE]. [s1 = zqvPZdC. [s1 = qjncLdZ. naMesbt]. s2 = gTALcXW]] = = = = = slJrLvp. gqiaxxE. [s1 = [s1 = oEsuEcU. } } /* Output: before sorting. s2 = gTALcXW]. s2 = rcVwhKO]] = = = = = qUocIBR. GZMmJMR]. s2 = FJQAHxx]. = cXZJoog]. [s1 = qUCBbkI. s2 = PCQQBnK]. OneOEdL]. BqtXbJV. WHkjUrU. list = [[s1 = BqtXbJV. s2 = IVPJWjR]. [s1 = YNzbrny. smwHLGE. s2 = kZPgwsq]. [s1 = IVPJWjR]. zqvPZdC. = GZMmJMR]. list = [[s1 qjncLdZ. [s1 = fFvHVEE]. [s1 = JjhcYVz. [s1 = ohnYVma]. [s1 = [s1 = oYWMNvq. s2 = euTpnXs]. s2 = rcVwhKO]. s2 = kpTkWng]. [s1 gqiaxxE. s2 = AJJmzMs]] [[s1 = YNzbrny. s2 = AJJmzMs]. list = [[s1 = BnFGofp]. s2 = IMLPoVg]. PCQQBnK]. s2 = ohnYVma]] after sorting with CompareSecond. array = = AJJmzMs]. [s1 WHkjUrU. s2 = ahKcxrE]. [s1 = BqtXbJV.print("\nFormer zeroth element " + zeroth + " now located at " + index). TQzGSrL. = OneOEdL]. [s1 qUocIBR. array = [[s1 PzDyCyR. [s1 = oYWMNvq. slJrLvp. s2 s2 s2 s2 s2 [[s1 = gqiaxxE. [s1 = opwBTwV. s2 = ahKcxrE]. s2 = FJQAHxx]. [s1 smwHLGE. opwBTwV. s2 = naMesbt]. s2 = naMesbt]] before sorting. [s1 = smwHLGE. [s1 = TQzGSrL. [s1 = gTALcXW]. s2 = IMLPoVg]. IMLPoVg]. s2 = OneOEdL]. s2 [s1 = oEsuEcU. s2 [s1 = TcQrGse. s2 = cXZJoog]. [s1 = qjncLdZ. oEsuEcU. import java.hashCode().String gen = new RandomGenerator. s2 = s2i. } public boolean equals(Object obj) { return obj instanceof TwoString2 && ((TwoString2)obj). s2 = fFvHVEE] now located at 4 *///:~ Exercise 41 //: containers/E41_HashedComparable.s1.s1. import net.util. // use s1's hashCode: return s1. s2. String s2i) { s1 = s1i.util.*.mindview. 492 Thinking in Java. class TwoString2 implements Comparable<TwoString2> { String s1. } public int hashCode() { // Since the comparisons are based on s1. } public int compareTo(TwoString2 rv) { String rvi = rv.equals(s1). public static Generator<TwoString2> generator() { return new Generator<TwoString2>() { public TwoString2 next() { return new TwoString2(gen.*. } private static RandomGenerator. } }.compareTo(rvi). } public String toString() { return "[s1 = " + s1 + ". 4th Edition Annotated Solution Guide .String(). ***********************************************/ package containers.Former zeroth element [s1 = slJrLvp.java /****************** Exercise 41 ***************** * Modify the class in Exercise 40 so * that it works with HashSets and as a key in * HashMaps.next(). gen. return s1. s2 = " + s2 + "]". public TwoString2(String s1i.next()). put(gen. [s1 = eRzDqLz. [s1 = TcQrGse. [s1 = smwHLGE. s2 = IMLPoVg]. [s1 = SOSdgHz. i). s2 = euTpnXs]. [s1 = fHggOPz. s2 = ikOSUvL]=9. [s1 = gqiaxxE. HashMap<TwoString2. s2 = lEcBfEA]=16. [s1 = olwrSJY. [s1 = DPbmXRQ. [s1 = oEsuEcU. [s1 = TQzGSrL. s2 = gepwlXE]=2. [s1 = GpArCjV. [s1 = CJIkjyU. [s1 = WVdlNmu. [s1 = YOzPgpj. [s1 = qYNYZGi. s2 = FSRWuZJ]=4} *///:~ You must add hashCode( ) and equals( ) to use a class as a key in a hashed container. s2 = MJOTlae]=3. [s1 = qjncLdZ. s2 = ieETLdl]=12. [s1 = JjhcYVz. System. [s1 = kkXmZpE. s2 = lUnpFsT]=0. s2 = OneOEdL]. s2 = cXZJoog]. [s1 = PzDyCyR. for(int i = 0. s2 = BnFGofp]. s2 = TmKrsgn]=14. [s1 = hfFkVrA.addAll(CollectionData. i < 20.out. [s1 = HvHqXum.next(). s2 = GZMmJMR]. [s1 = oYWMNvq. [s1 = slJrLvp.} } public class E41_HashedComparable { public static void main(String[] args) { HashSet<TwoString2> hs = new HashSet<TwoString2>(). s2 = pNVvUuk]=15. [s1 = vWlnklx. s2 = lXXnWSa]=11. s2 = LXdaCcH]=13. System. s2 = RRDDPuI]=18. s2 = SpNQlmO]=8. [s1 = BcatxLS. i++) hm. [s1 = xcwSwRL. s2 = yIPMqvD]=7. [s1 = WHkjUrU. [s1 = hLIpVVl. 20)).list(gen. [s1 = qUocIBR. s2 = ZPzWRSy]=10. s2 = PCQQBnK]. s2 = rcVwhKO]. s2 = ahKcxrE]] hm = {[s1 = JYmXfyL. s2 = fFvHVEE].Integer>(). s2 = AJJmzMs].out. s2 = HZAeudW]=19. } } /* Output: hs = [[s1 = tviIOeV. [s1 = zqvPZdC. s2 = ohnYVma].Integer> hm = new HashMap<TwoString2. [s1 = haoyWJS. [s1 = PKNjWUl. Generator<TwoString2> gen = TwoString2. Containers in Depth 493 . s2 = IVPJWjR]. s2 = GcFOWZn]. s2 = TJqCrcf]=6. s2 = yXPQvPa]=17. [s1 = CmEtqPh. [s1 = BqtXbJV. [s1 = vAusWxO. s2 = naMesbt]. s2 = gLhnfDa].generator(). hs. [s1 = opwBTwV. [s1 = YNzbrny. [s1 = qUCBbkI. s2 = FJQAHxx]. s2 = kZPgwsq].println("hs = " + hs).println("hm = " + hm). s2 = kihQiHq]=5. s2 = gTALcXW]. s2 = kpTkWng]. s2 = IhHLLNA]=1. s2 = s2i.toLowerCase().String gen = new RandomGenerator. String s2i) { s1 = s1i.toLowerCase()). s2 = " + s2 + "]".mindview.util. } }. 4th Edition Annotated Solution Guide . } private static RandomGenerator. import net.*.String().mindview. } public int compareTo(TwoStringAlphabetic rv) { String rvi = rv.toLowerCase().s1. ***********************************************/ package containers. import java.compareTo(rvi.toLowerCase()). gen.util. } } 494 Thinking in Java.next().util.*. TwoStringAlphabetic sc2) { return sc1.Exercise 42 //: containers/E42_AlphaComparable. import static net. } public String toString() { return "[s1 = " + s1 + ". public static Generator<TwoStringAlphabetic> generator() { return new Generator<TwoStringAlphabetic>() { public TwoStringAlphabetic next() { return new TwoStringAlphabetic( gen. } } class CompareSecondAlphabetic implements Comparator<TwoStringAlphabetic> { public int compare(TwoStringAlphabetic sc1. return s1.next()). s2.s1. class TwoStringAlphabetic implements Comparable<TwoStringAlphabetic> { String s1. public TwoStringAlphabetic(String s1i.compareTo( sc2.Print.*.s1.java /****************** Exercise 42 ***************** * Modify Exercise 40 so that an alphabetic sort * is used. [s1 = gqiaxxE. smwHLGE.sort(array. 10)). int index = Collections. [s1 = oEsuEcU. [s1 oYWMNvq. Arrays.array(array. s2 = cXZJoog]. Comparator<TwoStringAlphabetic> comp = new CompareSecondAlphabetic(). s2 = GcFOWZn]. Arrays. print("\nafter sorting. s2 = kZPgwsq].generator(). [s1 = HvHqXum. Collections. [s1 = smwHLGE.sort(list).asList(array)).generator()).public class E42_AlphaComparable { public static void main(String[] args) { TwoStringAlphabetic [] array = new TwoStringAlphabetic [10]. print("before sorting.binarySearch(list. comp). [s1 = PzDyCyR. array = [[s1 HvHqXum. s2 = euTpnXs]. PzDyCyR." + " array = " + Arrays. s2 = naMesbt].asList(array)). s2 = OneOEdL]. new CompareSecondAlphabetic()). comp). list = " + list). print("\nFormer zeroth element " + zeroth + " now located at " + index). zeroth. oEsuEcU. [s1 = oYWMNvq. [s1 = WHkjUrU. array = " + Arrays. print("\nbefore sorting. TwoStringAlphabetic. [s1 = qUCBbkI.sort(list. } } /* Output: before sorting. print("\nafter sorting. FJQAHxx].list( TwoStringAlphabetic. [s1 = = = = gqiaxxE. [s1 qUCBbkI." + " list = " + list). list = " + list).asList(array)). ahKcxrE]. s2 = euTpnXs].sort(array). Generated. s2 = naMesbt]. array = " + Arrays. OneOEdL]. s2 = cXZJoog]. s2 = ahKcxrE]. ArrayList<TwoStringAlphabetic> list = new ArrayList<TwoStringAlphabetic>( CollectionData. s2 = GZMmJMR]. print("\nafter sorting with CompareSecondAlphabetic. s2 s2 s2 s2 = = = = AJJmzMs]. [s1 [s1 [s1 [s1 = = = = Containers in Depth 495 . TwoStringAlphabetic zeroth = list. [s1 = TcQrGse. Collections. array = [[s1 = YNzbrny.get(0). s2 = FJQAHxx]. print("\nafter sorting with CompareSecondAlphabetic. s2 = AJJmzMs]] after sorting. JjhcYVz. s2 = GZMmJMR]. s2 = PCQQBnK]. [s1 = SOSdgHz. s2 = tviIOeV. s2 = IVPJWjR]. [s1 = slJrLvp. [s1 = zqvPZdC. [s1 = YNzbrny. s2 = kpTkWng]. s2 = GcFOWZn]] after sorting gqiaxxE. opwBTwV. AJJmzMs]. s2 s2 s2 s2 s2 = = = = = array = [[s1 = cXZJoog]. s2 = gLhnfDa]. s2 = qUocIBR. gLhnfDa]. [s1 [s1 [s1 [s1 [s1 = = = = = before sorting. [s1 qUocIBR. s2 = ahKcxrE]. IMLPoVg]. s2 = ohnYVma]. [s1 = GcFOWZn]] fFvHVEE]. s2 = gTALcXW]. [s1 = oYWMNvq. [s1 = TcQrGse. [s1 = TQzGSrL. s2 = gTALcXW]] after sorting. [s1 = opwBTwV. [s1 = qjncLdZ. [s1 = JjhcYVz. s2 = IMLPoVg]. s2 = PzDyCyR. [s1 = TQzGSrL. [s1 = qjncLdZ. s2 = opwBTwV. [s1 = zqvPZdC. [s1 zqvPZdC. s2 = fFvHVEE] now located at 5 *///:~ 496 Thinking in Java. s2 = = = = = = slJrLvp. s2 = ohnYVma]] after sorting BqtXbJV. [s1 fFvHVEE]. [s1 = tviIOeV. [s1 SOSdgHz. [s1 tviIOeV. s2 = gLhnfDa]. s2 = OneOEdL]. s2 = with CompareSecondAlphabetic. s2 = kpTkWng]. s2 = SOSdgHz. s2 = fFvHVEE]. s2 = BnFGofp]. s2 = FJQAHxx]. TQzGSrL. IMLPoVg]. s2 = WHkjUrU. [s1 = YNzbrny. list = [[s1 qjncLdZ. s2 = oEsuEcU. [s1 = JjhcYVz. [s1 = qUCBbkI. [s1 = naMesbt]. s2 = BnFGofp]. s2 = list = [[s1 = PCQQBnK].TcQrGse. [s1 = euTpnXs]. [s1 = HvHqXum. [s1 rcVwhKO]. [s1 = GZMmJMR]. list = [[s1 = BqtXbJV. [s1 kpTkWng]. s2 = IVPJWjR]. BqtXbJV. s2 = smwHLGE. 4th Edition Annotated Solution Guide . s2 = rcVwhKO]. s2 = kZPgwsq]. [s1 = slJrLvp. rcVwhKO]. [s1 ohnYVma]] = = = = Former zeroth element [s1 = slJrLvp. s2 = with CompareSecondAlphabetic. s2 = IVPJWjR]. s2 = kZPgwsq]. [s1 = qUocIBR. PCQQBnK]. s2 = gTALcXW]. [s1 = WHkjUrU. s2 = BnFGofp]. toLowerCase().TextFile. import net.mindview.java (or one of its variants) so * that the FilenameFilter opens and reads each file * (using the net.toLowerCase().list(new FilenameFilter() { private String ext = args[0].").length == 1) return true. import java.util. "\\W+")).length == 0) list = path. for(int i = 1. String name) { // Only analyze source files with the specified // extension (given as the first command line // argument) if(name.endsWith(ext)) { // Only filter upon file extension? if(args.*. name).I/O Exercise 1 //: io/E01_SearchWords.io. public class E01_SearchWords { public static void main(final String[] args) { File path = new File(". else list = path. String[] list. i < args. if(args. import java.getAbsolutePath().list().TextFile utility) and * accepts the file based on whether any of the * trailing arguments on the command line exist in * that file. 497 .length.util.*.mindview.util. i++) if(words. public boolean accept(File dir. Set<String> words = new HashSet<String>( new TextFile(new File( dir.contains(args[i])) return true.java // {Args: java E01_SearchWords} /****************** Exercise 1 ***************** * Modify DirList. ***********************************************/ package io. regex. import java. } public SortedDirList(File path) { this. import java.io. import static net.println(dirItem). } public String[] list() { 498 Thinking in Java.java /****************** Exercise 2 ****************** * Create a class called SortedDirList with a * constructor that takes a File object and builds * a sorted directory list from the files at that * File.TextFile utility constructor expects a pathname string as the first argument.CASE_INSENSITIVE_ORDER). } }).path = path.*. 4th Edition Annotated Solution Guide . class SortedDirList { private File path.*.mindview.*. } } /* Output: E01_SearchWords.sort(list. for(String dirItem : list) System. Arrays. The program uses the getAbsolutePath( ) method of the File class to produce one (see the JDK for more information).java E02_SortedDirList.java E03_DirSize. The net. and the rest are the words to search for. import java.out. String.*. If you do not provide any information on the command line.java *///:~ The first command line argument is the file extension. and * the second produces the subset of the list that * matches its argument (which is a regular * expression).mindview.util.util. Exercise 2 //: io/E02_SortedDirList.} return false. public SortedDirList() { path = new File(". ***********************************************/ package io.").Print.util. Add to this class two overloaded list() * methods: the first produces the whole list.util. the program will list the contents of the current folder. *\\.regex.CASE_INSENSITIVE_ORDER).compile(fn_regex).list("E0[12]_. import java. import static net.list().io. String. public class E03_DirSize { public static void main(final String[] args) { I/O 499 .util. } } /* Output: [E01_SearchWords.java (or one of its variants) so * that it sums up the file sizes of the selected * files. E02_SortedDirList. } }).*.java"} /****************** Exercise 3 ***************** * Modify DirList. ***********************************************/ package io.CASE_INSENSITIVE_ORDER). import java. return list.java // {Args: "E0[12]_. Arrays. import java.java] *///:~ Much of this is a rewrite of DirList. String. String name) { return pattern.*\.list(new FilenameFilter() { private Pattern pattern = Pattern. Exercise 3 //: io/E03_DirSize.String[] list = path. print(Arrays.*. public boolean accept(File dir.util.java"))).asList(dir.java.matcher(name).sort(list.*.*.mindview.sort(list. Arrays.java into a reusable class. } } public class E02_SortedDirList { public static void main(String args[]) { // Default constructor == current directory SortedDirList dir = new SortedDirList(). return list.matches().util. } public String[] list(final String fn_regex) { String[] list = path.Print. " + total + " bytes"). String name) { return pattern.*\. Exercise 4 //: io/E04_DirSize2. " + fs + " byte(s)").java // {Args: ".length(). public class E04_DirSize2 { public static void main(String[] args) { 500 Thinking in Java. for(String dirItem : list) { fs = new File(path.list().*.length( ) method reads the number of bytes in the file."). import net.compile(args[0]). import java.java"} All java files /****************** Exercise 4 ***************** * Use Directory. if(args. total += fs.sort(list. long fs. String[] list. else list = path.length + " file(s). } } /* Output: E01_SearchWords. 4th Edition Annotated Solution Guide . print(dirItem + ". ***********************************************/ package io. dirItem). print(list.java. } print("=======================").matches().matcher(name). public boolean accept(File dir.io.*.mindview. } }).length == 0) list = path. 3337 bytes *///:~ The File. String.java. 1606 byte(s) ======================= 2 file(s). 1731 byte(s) E02_SortedDirList. long total = 0.CASE_INSENSITIVE_ORDER).list(new FilenameFilter() { private Pattern pattern = Pattern.File path = new File(".util. Arrays.walk() to sum the sizes of all * files in a directory tree whose names match a * particular regular expression. isDirectory()) I/O 501 . } public void start(String[] args) { try { if(args. } } /* Output: (Sample) 16 file(s). else for(String arg : args) { File fileArg = new File(arg).walk(".walk(".. this. else ti = Directory. 11647 bytes *///:~ The program sums the sizes of all Java files representing solutions of exercises from the Everything is an Object chapter of TIJ4. ***********************************************/ package io.util. private String regex.files. for(File file : ti) total += file.*. Exercise 5 //: io/E05_ProcessFiles2. long total = 0. if(fileArg.Directory.out. import net. String regex) { this./object").java so that it matches a * regular expression rather than a fixed * extension..length(). class ProcessFiles2 { private ProcessFiles. import java.length == 0) processDirectoryTree(new File(". " + total + " bytes"). if(args.mindview.length == 0) ti = Directory.*.Strategy strategy.regex = regex.Strategy strategy.TreeInfo ti.println( ti.strategy = strategy.")). System.size() + " file(s). args[0]).java /****************** Exercise 5 ***************** * Modify ProcessFiles. public ProcessFiles2(ProcessFiles./object".io. mindview.*.*. else if(arg. import java.process(file.getCanonicalFile()).process(fileArg.*.processDirectoryTree(fileArg). Exercise 6 //: io/E06_ProcessFiles3. } }.start(args). import java.getCanonicalFile()). 1/1/06} /****************** Exercise 6 ***************** * Use ProcessFiles to find all the Java * source-code files in a particular directory * subtree that have been modified after a * particular date.walk( root.*\\.text.java"). 502 Thinking in Java. import net. } } catch(IOException e) { throw new RuntimeException(e). ".io. ***********************************************/ package io. } } public class E05_ProcessFiles2 { // Demonstration of how to use it: public static void main(String[] args) { new ProcessFiles2(new ProcessFiles. } } public void processDirectoryTree(File root) throws IOException { for(File file : Directory.java // {Args: .out.*.println(file). regex)) strategy.util. import java. } } /* (Execute to see output) *///:~ The program lists all the Java source-code files in given directories.matches(regex)) strategy.util.Strategy() { public void process(File file) { System.getAbsolutePath(). 4th Edition Annotated Solution Guide . java /****************** Exercise 7 ***************** * Open a text file so that you can read the file * one line at a time. "java"). * Print all of the lines in the LinkedList in reverse * order.getDateInstance( ) for the date formatter with the right style (short for “M/d/yy”) for the US locale.length != 2) { System. tmp = df. ***********************************************/ I/O 503 . new ProcessFiles(new ProcessFiles. try { df. } } /* (Execute to see output) *///:~ The program calls DateFormat. if(args. 1970. For strict adherence to the specified format.err.start(new String[] {args[0]}). The formatter then parses input.setLenient(false).US). converting a string into a Date object.println(file). return.lastModified()) System. } final long modTime = tmp.getDateInstance( DateFormat. } catch(ParseException pe) { pe. Finally.printStackTrace(). } long tmp = 0. Locale. 00:00:00 GMT represented by the Date object. } }.parse(args[1]). Read each line as a String * and place that String object into a LinkedList.out.SHORT. return.Strategy() { public void process(File file) { if(modTime < file.getTime().println( "Usage: java E06_ProcessFiles3 path date"). you must call setLenient(false) before invoking parse( ).public class E06_ProcessFiles3 { public static void main(String[] args) { DateFormat df = DateFormat. The program throws a ParseException for invalid input. getTime( ) finds the number of milliseconds since January 1. Exercise 7 //: io/E07_FileIntoList. Exercise 8 //: io/E08_CommandLine. 4th Edition Annotated Solution Guide .previous()). it. String s. for(ListIterator<String> it = list.err. in.util.close(). import java.java // {Args: E08_CommandLine.) System.io.IOException { if(args. ***********************************************/ package io. import java. public class E07_FileIntoList { // Throw exceptions to console: public static List<String> read(String filename) throws IOException { // Reading input by lines: BufferedReader in = new BufferedReader( new FileReader(filename)).size()).add(s).out. 504 Thinking in Java.java").println( "Usage: java E08_CommandLine file").java} /****************** Exercise 8 ***************** * Modify Exercise 7 so that the name of the file * you read is provided as a command-line argument.readLine())!= null) list. List<String> list = new LinkedList<String>().length != 1) { System. while((s = in.package io.hasPrevious().util.*. return list.*.io.println(it. } } /* (Execute to see output) *///:~ The output is the above file in reverse order. } public static void main(String[] args) throws IOException { List<String> list = read("E07_FileIntoList. import java.listIterator(list. public class E08_CommandLine { public static void main(String[] args) throws java.*. return; } List<String> list = E07_FileIntoList.read(args[0]); for(ListIterator<String> it = list.listIterator(list.size()); it.hasPrevious();) System.out.println(it.previous()); } } /* (Execute to see output) *///:~ Exercise 9 //: io/E09_UpperCase.java // {Args: E09_UpperCase.java} /****************** Exercise 9 ***************** * Modify Exercise 8 to force all the lines in * the LinkedList to uppercase and send the results * to System.out. ***********************************************/ package io; import java.util.*; public class E09_UpperCase { public static void main(String[] args) throws java.io.IOException { if(args.length != 1) { System.err.println( "Usage: java E09_UpperCase file"); return; } List<String> list = E07_FileIntoList.read(args[0]); for(ListIterator<String> it = list.listIterator(list.size()); it.hasPrevious();) System.out.println(it.previous().toUpperCase()); } } /* (Execute to see output) *///:~ Exercise 10 //: io/E10_FindWords.java // {Args: E10_FindWords.java import public} /****************** Exercise 10 ***************** * Modify Exercise 8 to take additional command-line * arguments of words to find in the file. Print * all lines in which any of the words match. ***********************************************/ I/O 505 package io; import java.util.*; public class E10_FindWords { public static void main(String[] args) throws java.io.IOException { if(args.length < 2) { System.err.println( "Usage: java E10_FindWords file words"); return; } Set<String> words = new HashSet<String>(); for(int i = 1; i < args.length; i++) words.add(args[i]); List<String> list = E07_FileIntoList.read(args[0]); for(ListIterator<String> it = list.listIterator(list.size()); it.hasPrevious();) { String candidate = it.previous(); for(String word : words) if(candidate.indexOf(word) != -1) { System.out.println(candidate); break; } } } } /* (Execute to see output) *///:~ Set automatically eliminates duplicate candidate words from the command line. The break statement prevents the line from being printed more than once, if more than one candidate word is present in the line. Exercise 11 //: io/E11_GreenhouseControls2.java // {Args: 5000000} /****************** Exercise 11 *************************** * (Intermediate) In the * innerclasses/GreenhouseController.java example, * GreenhouseController contains a hard-coded set of events. * Change the program so that it reads the events and their * relative times from a text file. (Challenging: Use a * Factory Method design pattern to build the events—see * Thinking in Patterns (with Java) at www.MindView.net.) *********************************************************/ package io; import java.util.*; 506 Thinking in Java, 4th Edition Annotated Solution Guide import java.io.*; import java.lang.reflect.*; import innerclasses.controller.*; class GreenhouseControls2 extends GreenhouseControls { class Restart extends Event { private Event[] eventList; public Restart(long delayTime) { super(delayTime); } public void action() { for(Event e : eventList) { e.start(); // Rerun each event addEvent(e); } start(); addEvent(this); // Rerun this Event } public String toString() { return "Restarting system"; } public void setEventList(Event[] eventList) { this.eventList = eventList; } } class GHEventFactory { LinkedList<EventCreator> events = new LinkedList<EventCreator>(); class EventCreator { Constructor<Event> ctor; long offset; public EventCreator(Constructor<Event> ctor, long offset) { this.ctor = ctor; this.offset = offset; } } @SuppressWarnings("unchecked") public GHEventFactory(String eventFile) { try { BufferedReader in = new BufferedReader( new FileReader(eventFile)); String s; while((s = in.readLine())!= null) { int colon = s.indexOf(':'); // Must use '$' instead of '.' to // describe inner classes: String className = s.substring(0, colon).trim(); I/O 507 Class<?> outer = className.equals("Restart") ? GreenhouseControls2.class : GreenhouseControls.class; String type = outer.getSimpleName() + "$" + className; long offset = Long.parseLong( s.substring(colon + 1).trim()); // Use Reflection to find and call // the right constructor: Class<Event> eventClass = (Class<Event>)Class.forName(type); // Inner class constructors implicitly // take the outer-class object as a // first argument: Constructor<Event> ctor = eventClass.getConstructor( new Class<?>[] { outer, long.class }); events.add(new EventCreator(ctor, offset)); } } catch(Exception e) { throw new RuntimeException(e); } } Iterator<Event> iterator() { return new Iterator<Event>() { Iterator<EventCreator> it = events.iterator(); public boolean hasNext() { return it.hasNext(); } public Event next() { EventCreator ec = it.next(); Event returnVal = null; try { returnVal = ec.ctor.newInstance( new Object[] { GreenhouseControls2.this, ec.offset }); } catch(Exception e) { throw new RuntimeException(e); } return returnVal; } public void remove() { throw new UnsupportedOperationException(); } }; } 508 Thinking in Java, 4th Edition Annotated Solution Guide } GHEventFactory gheFactory; public GreenhouseControls2(String initFile) { gheFactory = new GHEventFactory(initFile); // Now we need some logic to setup the system. // The restart event requires a special attention. LinkedList<Event> restartableEvents = new LinkedList<Event>(); Iterator<Event> it = gheFactory.iterator(); while(it.hasNext()) { Event e = it.next(); if(e instanceof Bell || e instanceof Restart) continue; restartableEvents.add(e); } it = gheFactory.iterator(); while(it.hasNext()) { Event e = it.next(); addEvent(e); if(e instanceof Restart) ((Restart)e).setEventList( restartableEvents.toArray(new Event[0])); } } } public class E11_GreenhouseControls2 { public static void main(String[] args) { GreenhouseControls2 gc = new GreenhouseControls2("GreenhouseConfig.dat"); try { if(args.length == 1) gc.addEvent(new GreenhouseControls.Terminate( Long.parseLong(args[0]))); } catch(NumberFormatException e) { System.err.println("Terminate event is not added!"); e.printStackTrace(); } gc.run(); } } /* Output: (Sample) Thermostat on night setting Light is on Light is off Greenhouse water is on Greenhouse water is off Bing! I/O 509 Thermostat on day setting Restarting system Bing! Thermostat on night setting Light is on Light is off Greenhouse water is on Greenhouse water is off Bing! Thermostat on day setting Restarting system Bing! Thermostat on night setting Light is on Light is off Greenhouse water is on Greenhouse water is off Terminating *///:~ The configuration file consists of the class name, a colon, and the time offset. The ‘//:!’ on the first line causes the extraction program to remove the first and last lines, so they do not appear in the file included in the code distribution for TIJ4: //:! io/GreenhouseConfig.dat ThermostatNight: 100000 LightOn: 200000 LightOff: 400000 WaterOn: 600000 WaterOff: 800000 Bell: 1000000 ThermostatDay: 1400000 Restart: 2000000 ///:~ The GHEventFactory class contains a LinkedList to hold an EventCreator object for each line in the configuration file. Each EventCreator holds a Constructor and the offset value for each event. The Constructor is a java.lang.reflection object that represents a constructor for a class, which you can use to dynamically create new objects. The trick is in producing the Constructor object, which we see in the GHEventFactory constructor. The GHEventFactory constructor takes a text configuration file argument. It opens that file, finds the colon in each line, and divides the line on the colon into the class name and offset value. Since these are inner classes, you must prepend GreenhouseControls to qualify the name, but never separate it with a ‘.’ 510 Thinking in Java, 4th Edition Annotated Solution Guide because Class.forName( ) actually looks for the filename to load, so you must separate the names with the ‘$’ (which separates class names in the compilergenerated internal name for an inner class). Class.forName( ) uses the string we provide to produce a reference to the Class object. The method getConstructor( ) produces the Constructor object, but needs an argument list to match with the appropriate constructor. Give it an array of Class objects matching your argument list; then you can add the EventCreator. The program could throw several exceptions here, which we convert to RuntimeExceptions. If anything fails, it reports everything to the console. We use an iterator to move through the EventCreator objects and produce new Event objects, which we define as anonymous inner classes built on top of the events LinkedList. Every time you call next( ), the iterator fetches the next EventCreator in the list and uses its Constructor and offset to build a new Event object. The newInstance method is called on the Constructor object; it requires the correct number and type of arguments passed to it as a dynamically created array of Objects. The offset is added to the current time each time you call next( ). We initialize the Restart class with a new setEventList( ) method. During startup it hands eligible events to the matching Restart object (e.g., the Bell restarts itself automatically). Exercise 12 //: io/E12_LineNumber.java // {Args: E12_LineNumber.java E12_LineNumber.out} /****************** Exercise 12 ***************** * Modify Exercise 8 to also open a text file so * you can write text into it. Write the lines in the * LinkedList, along with line numbers (do not * attempt to use the “LineNumber” classes), out * to the file. ***********************************************/ package io; import java.io.*; import java.util.*; public class E12_LineNumber { public static void main(String[] args) throws java.io.IOException { if(args.length != 2) { I/O 511 System.err.println( "Usage: java E12_LineNumber infile outfile"); return; } List<String> list = E07_FileIntoList.read(args[0]); PrintWriter out = new PrintWriter( new BufferedWriter( new FileWriter(args[1]))); // We need to count backwards... int line = list.size(); for(ListIterator<String> it = list.listIterator(list.size()); it.hasPrevious();) out.println(line-- + ": " + it.previous()); out.close(); } } ///:~ Exercise 13 //: io/E13_CountLines.java /****************** Exercise 13 ***************** * Modify BasicFileOutput.java so that it uses * LineNumberReader to keep track of the line * count. Note that it’s much easier to just keep * track programmatically. ***********************************************/ package io; import java.io.*; public class E13_CountLines { static String file = "E13_CountLines.out"; public static void main(String[] args) throws IOException { // LineNumberReader is inherited from // BufferedReader so we don't need to // explicitly buffer it: LineNumberReader in = new LineNumberReader( new FileReader("E13_CountLines.java")); PrintWriter out = new PrintWriter( new BufferedWriter(new FileWriter(file))); String s; while((s = in.readLine()) != null ) out.println(in.getLineNumber() + ": " + s); out.close(); 512 Thinking in Java, 4th Edition Annotated Solution Guide // Show the stored file: System.out.println(E07_FileIntoList.read(file)); } } /* (Execute to see output) *///:~ LineNumberReader counts from one, while most counting begins at zero. Exercise 14 //: io/E14_BufferPerformance.java /****************** Exercise 14 ***************** * Starting with BasicFileOutput.java, write a * program that compares the performance of writing * to a file when using buffered and unbuffered I/O. ***********************************************/ package io; import java.io.*; import java.util.*; public class E14_BufferPerformance { static String file = "E14_BufferPerformance.out"; public static void main(String[] args) throws IOException { List<String> list = E07_FileIntoList.read( "E14_BufferPerformance.java"); PrintWriter out = new PrintWriter( new BufferedWriter(new FileWriter(file))); int lineCount = 1; long t1 = System.currentTimeMillis(); for(String s : list) { for(int i = 0; i < 10000; i++) out.println(lineCount + ": " + s); lineCount++; } long t2 = System.currentTimeMillis(); System.out.println("buffered: " + (t2 - t1)); out.close(); out = new PrintWriter(new FileWriter(file)); lineCount = 1; t1 = System.currentTimeMillis(); for(String s : list) { for(int i = 0; i < 10000; i++) out.println(lineCount + ": " + s); lineCount++; } t2 = System.currentTimeMillis(); I/O 513 System.out.println("unbuffered: " + (t2 - t1)); out.close(); } } /* Output: (Sample) buffered: 3385 unbuffered: 4196 *///:~ Exercise 15 //: io/E15_StoringAndRecoveringAllData.java /****************** Exercise 15 ***************** * Look up DataOutputStream and DataInputStream in * the JDK documentation. Starting with * StoringAndRecoveringData.java, create a program * that stores and then retrieves all the different * possible types provided by the DataOutputStream * and DataInputStream classes. Verify that the * values are stored and retrieved accurately. ***********************************************/ package io; import java.io.*; import static net.mindview.util.Print.*; public class E15_StoringAndRecoveringAllData { public static void main(String[] args) throws IOException { DataOutputStream out = new DataOutputStream( new BufferedOutputStream( new FileOutputStream("Data.txt"))); out.writeBoolean(true); out.writeByte(100); out.writeByte(255); out.writeChar('A'); out.writeFloat(1.41413f); out.writeLong(1000000000L); out.writeInt(100000); out.writeShort(30000); out.writeShort(65535); out.writeDouble(3.14159); out.writeUTF("That was pi"); out.close(); DataInputStream in = new DataInputStream( new BufferedInputStream( new FileInputStream("Data.txt"))); print(in.readBoolean()); 514 Thinking in Java, 4th Edition Annotated Solution Guide print(in.readByte()); print(in.readUnsignedByte()); print(in.readChar()); print(in.readFloat()); print(in.readLong()); print(in.readInt()); print(in.readShort()); print(in.readUnsignedShort()); print(in.readDouble()); // Only readUTF() will recover the // Java-UTF String properly: print(in.readUTF()); } } /* Output: true 100 255 A 1.41413 1000000000 100000 30000 65535 3.14159 That was pi *///:~ This program demonstrates the appropriate methods for writing/reading the basic data types in Java. Observe, however, that there are no equivalent methods for readUnsignedByte( ) and readUnsignedShort( ) in DataOutputStream, another reason to be careful when using this technique for storing and retrieving complex data structures. Sometimes you need to know both the type and the range of stored data, though we don’t advise that you hard code all this inside a program. Exercise 16 //: io/E16_UsingAllRandomAccessFile.java /****************** Exercise 16 ***************** * Look up RandomAccessFile in the JDK * documentation. Starting with * UsingRandomAccessFile.java, create a program * that stores and then retrieves all the different * possible types provided by the RandomAccessFile * class. Verify that the values are stored and I/O 515 * retrieved accurately. ***********************************************/ package io; import java.io.*; import static net.mindview.util.Print.*; public class E16_UsingAllRandomAccessFile { static String file = "rtest.dat"; static void display() throws IOException { RandomAccessFile rf = new RandomAccessFile(file, "r"); print(rf.readBoolean()); print(rf.readByte()); print(rf.readUnsignedByte()); print(rf.readChar()); print(rf.readFloat()); print(rf.readLong()); print(rf.readInt()); print(rf.readShort()); print(rf.readUnsignedShort()); print(rf.readDouble()); print(rf.readUTF()); rf.close(); } public static void main(String[] args) throws IOException { RandomAccessFile rf = new RandomAccessFile(file, "rw"); rf.writeBoolean(true); rf.writeByte(100); rf.writeByte(255); rf.writeChar('A'); rf.writeFloat(1.41413f); rf.writeLong(1000000000L); rf.writeInt(100000); rf.writeShort(30000); rf.writeShort(65535); rf.writeDouble(3.14159); rf.writeUTF("The end of the file"); rf.close(); display(); rf = new RandomAccessFile(file, "rw"); rf.seek(3); // 1 boolean + 2 bytes rf.writeChar('B'); rf.close(); display(); } } /* Output: true 516 Thinking in Java, 4th Edition Annotated Solution Guide 100 255 A 1.41413 1000000000 100000 30000 65535 3.14159 The end of the file true 100 255 B 1.41413 1000000000 100000 30000 65535 3.14159 The end of the file *///:~ Exercise 17 //: io/E17_CharactersInfo.java /****************** Exercise 17 ***************** * Using TextFile and a Map<Character,Integer>, * create a program that counts the occurrence of * all the different characters in a file. (So if * there are 12 occurrences of the letter 'a' in * the file, the Integer associated with the Character * containing 'a' in the Map contains '12'). ***********************************************/ package io; import java.util.*; import net.mindview.util.*; public class E17_CharactersInfo { public static void main(String[] args) { Map<Character,Integer> charsStat = new HashMap<Character,Integer>(); for(String word : new TextFile("E17_CharactersInfo.java", "\\W+")) for(int i = 0; i < word.length(); i++) { Character ch = word.charAt(i); I/O 517 Integer freq = charsStat.get(ch); charsStat.put(ch, freq == null ? 1 : freq + 1); } List<Character> keys = Arrays.asList( charsStat.keySet().toArray(new Character[0])); Collections.sort(keys); for(Character key : keys) System.out.println(key + " => " + charsStat.get(key)); } } /* Output: (Sample) 0 => 2 1 => 8 2 => 2 7 => 4 A => 3 C => 12 E => 4 ... u => 10 v => 5 w => 8 x => 3 y => 10 *///:~ Exercise 18 //: io/E18_TextFile2.java /****************** Exercise 18 ***************** * Modify TextFile.java so that it passes * IOExceptions out to the caller. ***********************************************/ package io; import java.io.*; import java.util.*; class TextFile2 extends ArrayList<String> { // Read a file as a single string: public static String read(String fileName) throws IOException { StringBuilder sb = new StringBuilder(); BufferedReader in= new BufferedReader(new FileReader( new File(fileName).getAbsoluteFile())); try { String s; while((s = in.readLine()) != null) { 518 Thinking in Java, 4th Edition Annotated Solution Guide sb.append(s); sb.append("\n"); } } finally { in.close(); } return sb.toString(); } // Write a single file in one method call: public static void write(String fileName, String text) throws IOException { PrintWriter out = new PrintWriter( new BufferedWriter(new FileWriter( new File(fileName).getAbsoluteFile()))); try { out.print(text); } finally { out.close(); } } // Read a file, split by any regular expression: public TextFile2(String fileName, String splitter) throws IOException { super(Arrays.asList(read(fileName).split(splitter))); // Regular expression split() often leaves an empty // String at the first position: if(get(0).equals("")) remove(0); } // Normally read by lines: public TextFile2(String fileName) throws IOException { this(fileName, "\n"); } public void write(String fileName) throws IOException { PrintWriter out = new PrintWriter( new BufferedWriter(new FileWriter( new File(fileName).getAbsoluteFile()))); try { for(String item : this) out.println(item); } finally { out.close(); } } } public class E18_TextFile2 { public static void main(String[] args) I/O 519 StringBuilder.println(words. BufferedWriter. System. // Display the capitalized words: System. Read.txt").get(bt). } } /* Output: (95% match) [0. E18_TextFile2.io.Integer>. String.Integer>(). FileReader.write("test. text. "\\W+")). W. ArrayList.util.java"). ***********************************************/ package io.util. File.Integer> bytesStat = new HashMap<Byte.sort(keys). Arrays.*. freq == null ? 1 : freq + 1).mindview.read("E18_TextFile2.txt". for(Byte bt : BinaryFile. bytesStat.java /****************** Exercise 19 ***************** * Using BinaryFile and a Map<Byte.write("test2. Normally. Exercise. file). IOExceptions.*. import java.put(bt.out.keySet()). Collections. 4th Edition Annotated Solution Guide . Modify. Write] *///:~ Exercise 19 //: io/E19_BytesInfo. // Break into unique sorted list of words: TreeSet<String> words = new TreeSet<String>( new TextFile2("E18_TextFile2. TextFile2. } List<Byte> keys = new ArrayList<Byte>(bytesStat. TextFile. Regular.*. IOException.headSet("a")).class")) { Integer freq = bytesStat. 18. create * a program that counts the occurrence of all the * different bytes in a file. for(Byte key : keys) 520 Thinking in Java.read("E19_BytesInfo. TextFile2 text = new TextFile2("test. TreeSet.throws IOException { String file = TextFile2. public class E19_BytesInfo { public static void main(String[] args) throws IOException { Map<Byte. import net.txt"). Display. PrintWriter.java". import java. FileWriter. BufferedReader. Break. TextFile2. read(file).class")) { byte[] bt = BinaryFile. ". ***********************************************/ package io.*.length. for(int i = 0.util.mindview. } } /* Output: (Sample) -124 => 2 -103 => 1 -94 => 1 -89 => 3 -84 => 1 . (byte)186.. public class E20_ClassSignatureChecker { final static byte[] signature = {(byte)202. verify * that all . } I/O 521 .walk() and BinaryFile.err. import net. the output contains negative values. i< signature. break.io. (byte)254. i++) if(bt[i] != signature[i]) { System. (byte)190}. public static void main(String[] args) throws IOException { String dir = ". 116 => 73 117 => 22 118 => 42 119 => 2 120 => 5 121 => 10 *///:~ Since there is no unsigned byte in Java.get(key)).out.length == 1) dir = args[0].walk(dir. for(File file : Directory. Exercise 20 //: io/E20_ClassSignatureChecker. if(args.class files in a directory tree begin * with the hex characters ‘CAFEBABE’.*\\.println(file + " is corrupt!").System.java /****************** Exercise 20 ***************** * Using Directory. import java.println(key + " => " + bytesStat.*..". *. public class E21_UpperCaseEcho { public static void main(String[] args) throws IOException { BufferedReader stdin = new BufferedReader( new InputStreamReader(System.*.length() != 0) System. it returns * the results of executing the program as a List * of Strings.readLine()) != null && s. then puts the results * on standard output.io. Redirect the contents of a * file into this program (the process of * redirection will vary depending on your operating * system). ***********************************************/ package io. String s. instead of * printing the standard output stream. 4th Edition Annotated Solution Guide . import java.*.util. while((s = stdin.*. // An empty line or Ctrl-Z terminates the program } } ///:~ Exercise 22 //: io/E22_OSExecuteDemo.util.io.println(s. import net.mindview.in)). 522 Thinking in Java. ***********************************************/ package io.toUpperCase()).} } } ///:~ Exercise 21 //: io/E21_UpperCaseEcho. import java.java // {RunByHand} /****************** Exercise 21 ***************** * Write a program that takes standard input and * capitalizes all characters. Demonstrate the use of this new * version of the utility.java /****************** Exercise 22 ***************** * Modify OSExecute.out. import java.java so that. add(s).Object{ public io.split(" ")).E22_OSExecuteDemo().getErrorStream())).lang.command("javap E22_OSExecuteDemo"). return output.println(s).out. } } /* Output: Compiled from "E22_OSExecuteDemo. public static void main(java. throw new RuntimeException(e).readLine()) != null) { System.startsWith("CMD /C")) return command("CMD /C " + command). } } public class E22_OSExecuteDemo { public static void main(String[] args) { List<String> result = OSExecute2.E22_OSExecuteDemo extends java.lang.println(s).java" public class io.readLine()) != null) output. try { Process process = new ProcessBuilder(command.err. BufferedReader errors = new BufferedReader( new InputStreamReader(process.String[]).getInputStream())).start(). } if(err) throw new OSExecuteException("Errors executing " + command). } catch(Exception e) { throw new RuntimeException(e). } } catch(IOException e) { if(!command. // Report errors and return nonzero value // to calling process if there are problems: while((s = errors. I/O 523 . BufferedReader results = new BufferedReader( new InputStreamReader(process. while((s = results. err = true.class OSExecute2 { public static List<String> command(String command) { boolean err = false. String s. List<String> output = new LinkedList<String>(). for(String s : result) System. public class E23_PrintCharBuffer { static BitSet isPrintable = new BitSet(127). static String encoding = System.rewind(). cb.out. static { // Assume an encoding that obeys ASCII eg. while(isPrintable.rewind(). System. CharBuffer buffer = ByteBuffer. // Print printable } } ///:~ 524 Thinking in Java.get(cb. // Print everything setPrintableLimit(buffer). import java.set(i). } public static void main(String[] args) { System.ISO-8859-1. cb.position() . buffer. *********************************************************/ package io.asCharBuffer().getProperty("file. System.limit(cb. i++) isPrintable.out. i <= 127.println("Default Encoding is: " + encoding). for(int i = 32.java // {RunByHand} /********************* Exercise 23 *********************** * Create and test a utility method to print the contents * of a CharBuffer up to the point where the characters * are no longer printable.allocate(16). // Characters 32 to 127 represent printable characters.nio.rewind(). } // Set the position to the last printable character public static void setPrintableLimit(CharBuffer cb) { cb. 4th Edition Annotated Solution Guide .out.put("ABCDE" + (char) 0x01 + "FG").println(buffer).*.*. import java.println(buffer).encoding"). buffer.util.1).} *///:~ Exercise 23 //: io/E23_PrintCharBuffer.get())). db.println(db.asDoubleBuffer(). 1.java // {RunByHand} /******************** Exercise 25 ************************* * Experiment with changing the ByteBuffer.3.0.println(d).hasRemaining()) { double d = db.1 1.3). DoubleBuffer db = bb.0 1.put(3.4. *********************************************************/ package io. 1. 1.5.allocate() * statements in the examples in this chapter to * ByteBuffer.out.put( new double[]{ 1.flip().Exercise 24 //: io/E24_DoubleBufferDemo.3 1. while(db.5 1.*.allocateDirect(). Demonstrate performance I/O 525 . System.2 0. import java. db. 1.6 *///:~ Exercise 25 //: io/E25_AllocateDirect.4 1. } } } /* Output: 1.java /********************* Exercise 24 *********************** * Modify IntBufferDemo.out.get(). public static void main(String[] args) { ByteBuffer bb = ByteBuffer.java to use doubles. 1. // Absolute location read and write: System.3 1.1.6 }). public class E24_DoubleBufferDemo { private static final int BSIZE = 1024.nio. // Store an array of double: db.2. 0.get(3)). 1.allocate(BSIZE). nio.nio.println("Program Name: <" + name + ">").charset.*.io. private int size. indirectAllocate(). System.* differences.*. abstract class CompareAllocations { private String name. startTime = System. 4th Edition Annotated Solution Guide .startTime) + ">"). System.out. } catch (IOException e) { throw new RuntimeException(e). public CompareAllocations(String name. startTime = System.println( "Indirect Allocation Cost for buffer of size: <" + size + "> is <" + (endTime .nanoTime().startTime) + ">"). directAllocate().nanoTime(). but also notice whether the startup time * of the programs noticeably changes. endTime = System. protected ByteBuffer buffer. System.*.out. this.channels.println( "Direct Allocation Cost for buffer of size: <" + size + "> is <" + (endTime .nanoTime(). System. import java.nio.startTime) + ">").nanoTime().out.nanoTime(). } public void runComparison() { System. endTime = System.nanoTime().out.*. *********************************************************/ package io. import java. try { long startTime = System. startTime = System.nanoTime().name = name. } 526 Thinking in Java. long endTime = System.startTime) + ">"). import java. import java.println( "Execution cost using indirect buffer: <" + (endTime .println( "Execution cost using direct buffer: <" + (endTime . execute(). endTime = System.out.size = size. int size) { this. execute().nanoTime(). } public abstract void execute() throws IOException.txt") . fc. fc. } }.getBytes())).getChannel().get().flip().txt") .getChannel(). // Prepare for reading } } }.getChannel(). 8192) { public void execute() throws IOException { FileChannel fc = new FileOutputStream("data2. public void indirectAllocate() { buffer = ByteBuffer.write(ByteBuffer. 16384) { public void execute() throws IOException { FileChannel in = new FileInputStream("E25_AllocateDirect.} public void directAllocate() { buffer = ByteBuffer.allocateDirect(size).write(buffer).getChannel(). while(buffer.close().read(buffer).read(buffer) != -1) { buffer. I/O 527 .getChannel(). buffer. out = new FileOutputStream("temp. } } public class E25_AllocateDirect { public static void main(String[] args) { CompareAllocations[] comparisons = { new CompareAllocations("GetChannel".wrap("Some text".hasRemaining()) buffer.java") . fc. new CompareAllocations("BufferToText". fc = new FileInputStream("data2. new CompareAllocations("ChannelCopy".txt") .clear(). while(in. buffer. 8192) { public void execute() throws IOException { FileChannel fc = new FileInputStream("E25_AllocateDirect. // Prepare for writing out.flip().java") .allocate(size). asCharBuffer().flip().asIntBuffer().close(). // Store and read a short: buffer. 1024) { public void execute() throws IOException { // Store and read a char array: buffer. fc.clear().asCharBuffer().getShort().getBytes("UTF-16BE"))). buffer.txt") .asCharBuffer(). new CompareAllocations("GetData".put(99471142).toString(). fc. buffer. fc.read(buffer).wrap( "Some text". buffer.txt") . // Decode using this system's default Charset: buffer. // Store and read an int: buffer.rewind().getProperty("file. buffer.clear().fc. // Use a CharBuffer to write through: fc = new FileOutputStream("data2. Charset.txt") .getChannel(). buffer.getChannel().put((short)471142). buffer. fc. buffer. fc.txt") .getChannel(). } }. 4th Edition Annotated Solution Guide .clear(). // Now try reading again: fc = new FileInputStream("data2. 528 Thinking in Java.close(). buffer. // Read and display: fc = new FileInputStream("data2. buffer. buffer. buffer. fc.flip(). buffer.toString().encoding")) .getChannel().read(buffer).write(buffer).decode(buffer).put("Some text").getInt().write(ByteBuffer.flip(). fc = new FileOutputStream("data2.asCharBuffer().read(buffer).toString().asCharBuffer().put("Howdy!").forName( System.rewind().asShortBuffer(). buffer. put(3.put( new int[] { 11. 811.asIntBuffer(). ib. buffer. 143. symmetricScramble(cb).rewind().put(99471142).put(99471142). 1024) { public void execute() throws IOException { IntBuffer ib = buffer. 32) { public void execute() throws IOException { char[] data = "UsingBuffers". buffer. 42. cb.flip().hasRemaining()) { buffer.asLongBuffer().rewind(). // Absolute location read and write: ib.toCharArray(). char c2 = buffer.buffer.rewind(). // Store and read a float: buffer.asCharBuffer().getDouble(). } }.get(). cb. I/O 529 . symmetricScramble(cb).mark().rewind(). CharBuffer cb = buffer.reset().getLong(). // Store and read a long: buffer.get().asFloatBuffer(). // Store and read a double: buffer. 47.put(99471142). buffer. ib. buffer. new CompareAllocations("UsingBuffers". } private void symmetricScramble(CharBuffer buffer) { while(buffer.asDoubleBuffer(). // Store an array of int: ib. 99. buffer. cb.rewind().put(data). 1811).rewind().get(3). buffer. char c1 = buffer.rewind(). buffer. new CompareAllocations("IntBufferDemo".hasRemaining()) { int i = ib. while(ib. cb.get(). 1016 }).getFloat(). } } }. 4th Edition Annotated Solution Guide .decode(buffer). but typically have somewhat higher allocation and deallocation costs than non-direct buffers.nio. } Pattern p = Pattern.java to use Java nio memory-mapped * files.*.map(FileChannel. for(int i = 0. ********************************************************/ package io.put(c1). import java. Matcher m = p. String[] fileAsArray = cb. fc.util. } } ///:~ Direct buffers increase program performance in bulk operations.compile(args[1]). int index = 0.mindview.runComparison().*. import java.*. ByteBuffer buffer = fc.*.put(c2).split("\n").nio.channels.io.*.READ_ONLY.charset.matcher(""). public class E26_JGrepMM { public static void main(String[] args) throws Exception { if(args. Exercise 26 //: io/E26_JGrepMM. import java.getChannel().Print. 0.length.nio.toString().size()).java // {Args: E26_JGrepMM.getProperty("file. import java.length < 2) { print("Usage: java E26_JGrepMM file regex").forName( System. i++) comparisons[i]. } } } }. import java. FileChannel fc = new FileInputStream(args[0]).regex. i < comparisons. System.encoding")).MapMode. for(String line : fileAsArray) { 530 Thinking in Java.buffer.*.exit(0). CharBuffer cb = Charset. import static net.java \b[Ssct]\w+} /********************** Exercise 26 ********************* * Modify strings/JGrep.util. reset(line). import static net. Create an instance * of your class. while(m.*. Exercise 27 //: io/E27_ObjectSerialization.start()).java /********************** Exercise 27 ********************* * Create a Serializable class containing a reference to an * object of a second Serializable class.close(). } fc. ********************************************************/ package io. } } /* Output: (Sample) 0: Ssct: 30 1: strings: 10 2: to: 29 3: channels: 16 4: charset: 16 5: static: 7 6: class: 7 7: static: 9 8: String: 26 9: throws: 41 10: System: 6 11: compile: 24 12: size: 50 13: cb: 15 14: System: 6 15: String: 4 16: cb: 27 17: toString: 30 18: split: 41 19: String: 8 20: start: 19 21: close: 7 *///:~ This program demonstrates how to set up a Java nio memory-mapped file.m. serialize it to disk.Print.util. then restore it and * verify that the process worked correctly.*.mindview.group() + ": " + m.io.find()) print(index++ + ": " + m. import java. I/O 531 . } } class Thing2 implements Serializable { private int id.close().out")). Thing1 t3 = (Thing1)in2.id = id. ByteArrayOutputStream bout = new ByteArrayOutputStream(). return result.append("))"). // Also flushes output ObjectInputStream in = new ObjectInputStream( new FileInputStream("thing1. } } public class E27_ObjectSerialization { public static void main(String[] args) throws ClassNotFoundException. s = (String)in2. public Thing1(int id) { next = new Thing2(id). } } /* Output: 532 Thinking in Java.writeObject(t1). out.writeObject("Thing1 storage\n"). print(s + "t3 = " + t3). out2.toString().class Thing1 implements Serializable { private Thing2 next. String s = (String)in. public Thing2(int id) { this. ObjectOutputStream out2 = new ObjectOutputStream(bout).readObject(). out2. result. out.toByteArray())).append(next). result. } public String toString() { return Integer.writeObject("Thing1 storage\n").readObject().readObject().writeObject(t1). ObjectInputStream in2 = new ObjectInputStream( new ByteArrayInputStream(bout. } public String toString() { StringBuilder result = new StringBuilder("Thing1(Thing2("). ObjectOutputStream out = new ObjectOutputStream( new FileOutputStream("thing1.readObject().toString(id). print("t1 = " + t1). IOException { Thing1 t1 = new Thing1(1). out. 4th Edition Annotated Solution Guide . Thing1 t2 = (Thing1)in.flush(). print(s + "t2 = " + t2). out2.out")). mindview. ***********************************************/ package io. } public void writeExternal(ObjectOutput out) throws IOException { print("Blip1.io.*.readExternal"). I/O 533 . import java. } } public class E28_BlipCheck implements Externalizable { // E28_BlipCheck() { // print("BlipCheck Constructor"). Next.java // {RunByHand} /****************** Exercise 28 ***************** * In Blips.java. Run it and explain * why it works.java and rename the class Blip2 to * BlipCheck (making it public and removing the * public scope from the class Blips in the * process).Print. Note that after compiling. } public void readExternal(ObjectInput in) throws IOException.writeExternal"). class Blip1 implements Externalizable { public Blip1() { print("Blip1 Constructor").*.util. ClassNotFoundException { print("Blip1. copy the file and rename it to * BlipCheck. Remove the //! marks in the file and * execute the program including the offending * lines. you * must execute the program with "java Blips" * because the main() method is still in class * Blips. comment out the default * constructor for BlipCheck.t1 = Thing1(Thing2(1)) Thing1 storage t2 = Thing1(Thing2(1)) Thing1 storage t3 = Thing1(Thing2(1)) *///:~ Exercise 28 //: io/E28_BlipCheck. import static net. 4th Edition Annotated Solution Guide . } } class Blips { // Throw exceptions to console: public static void main(String[] args) throws IOException. } public void readExternal(ObjectInput in) throws IOException. // Now get them back: ObjectInputStream in = new ObjectInputStream( new FileInputStream("Blips. print("Recovering b1:"). // OOPS! Throws an exception: print("Recovering b2:").readObject(). Blip1 b1 = new Blip1(). ClassNotFoundException { print("BlipCheck.writeObject(b2).readObject().out")). } } ///:~ When we remove the //!’s.close().writeObject(b1).out")). } public static void main(String[] args) throws Exception { // To make it run with Ant. o.readExternal"). b2 = (E28_BlipCheck)in.// } public void writeExternal(ObjectOutput out) throws IOException { print("BlipCheck.main(args). Blips. ClassNotFoundException { print("Constructing objects:"). o. ObjectOutputStream o = new ObjectOutputStream( new FileOutputStream("Blips. the output is: Constructing objects: Blip1 Constructor BlipCheck Constructor Saving objects: 534 Thinking in Java.writeExternal"). E28_BlipCheck b2 = new E28_BlipCheck(). b1 = (Blip1)in. print("Saving objects:"). o. ObjectStreamClass. When the compiler synthesizes the default constructor for a public class.io.java:64) at E28_BlipCheck.<init>(Unknown Source) at java. ***********************************************/ package io.java /****************** Exercise 29 ***************** * In Blip3.readExternal Recovering b2: Exception in thread "main" java.Blip1.ObjectOutputStream.InvalidClassException: E28_BlipCheck.main(E28_BlipCheck.ObjectStreamClass.io.writeExternal Recovering b1: Blip1 Constructor Blip1.*.readExternal Recovering b2: BlipCheck. which removes the exception.writeObject0(Unknown Source) at java.io. comment out the two lines after * the phrases "You must do this:" and run the * program.java:48) When we comment out the constructor (as above) the output is: Constructing objects: Blip1 Constructor Saving objects: Blip1.readExternal Eliminating the explicit default constructor allows the compiler to generate the default constructor. import java.java. it makes that constructor public. no valid constructor at java. so it works as an Externalizable object.io.io.io.writeExternal BlipCheck.lookup(Unknown Source) at java.ObjectOutputStream.writeExternal Recovering b1: Blip1 Constructor Blip1.writeExternal BlipCheck.writeObject(Unknown Source) at Blips. Explain the result and why it differs * from when the two lines are in the program. Exercise 29 //: io/E29_Blip3Test. I/O 535 .main(E28_BlipCheck. Blip3B b3 = new Blip3B("A String ". // i = in.util.writeInt(i). int a) { super(x.import static net.Print. // Now get it back: ObjectInputStream in = new ObjectInputStream( new FileInputStream("Blip3B. print(b3). print("Recovering b3:").*.readObject().writeExternal").writeExternal Recovering b3: Blip3 Constructor 536 Thinking in Java. int a) A String 47 Saving object: Blip3B. b3 = (Blip3B)in.readExternal"). ClassNotFoundException { print("Constructing objects:"). print("Saving object:"). // You must do this: // out.mindview.close(). } } /* Output: Constructing objects: Blip3(String x.writeObject(b3).readInt(). 4th Edition Annotated Solution Guide .out")). ClassNotFoundException { print("Blip3B.writeObject(s). // You must do this: // s = (String)in. // out. print(b3). o. 47). } } public class E29_Blip3Test { public static void main(String[] args) throws IOException.readObject(). a).out")). } @Override public void writeExternal(ObjectOutput out) throws IOException { print("Blip3B. o. class Blip3B extends Blip3 { public Blip3B() {} public Blip3B(String x. } @Override public void readExternal(ObjectInput in) throws IOException. ObjectOutputStream o = new ObjectOutputStream( new FileOutputStream("Blip3B. null and 0.readExternal null0 *///:~ writeObject( ) calls the writeExternal( ) method . dimension.nextInt(100).java as described in * the text. import java.Blip3B. switch(counter++ % 3) { default: case 0: return new Circle(xVal. } public static Shape randomFactory() { int xVal = rand. int dim = rand. yVal. private static Random rand = new Random(47). BLUE = 2. yPos = yVal. case 1: return new Square(xVal. so the object gets only the default values for its fields. case 2: return new Line(xVal.java /****************** Exercise 30 ***************** * Repair the program CADState. private static int counter = 0. public Shape(int xVal. dim). I/O 537 . ***********************************************/ package io. dimension = dim.io. Exercise 30 //: io/E30_RepairCADState. If these do not explicitly write and read the relevant parts of the object. yPos. dim). they aren’t stored or retrieved. import java.nextInt(100). private int xPos. int yVal.util.nextInt(100). GREEN = 3.*. yVal. and readObject( ) calls readExternal( ).*. public abstract int getColor(). int dim) { xPos = xVal. int yVal = rand. yVal. dim). abstract class Shape implements Serializable { public static final int RED = 1. } public String toString() { return getClass() + "color[" + getColor() + "] xPos[" + xPos + "] yPos[" + yPos + "] dim[" + dimension + "]\n". public abstract void setColor(int newColor). } public int getColor() { return color. } public static void deserializeStaticState(ObjectInputStream os) throws IOException { color = os. public static void serializeStaticState(ObjectOutputStream os) throws IOException { os. } public void setColor(int newColor) { color = newColor. public static void serializeStaticState(ObjectOutputStream os) throws IOException { os.readInt(). } public void setColor(int newColor) { color = newColor. 4th Edition Annotated Solution Guide . } public static void deserializeStaticState(ObjectInputStream os) throws IOException { color = os. int yVal. int dim) { super(xVal. } public Square(int xVal. } public Line(int xVal. int yVal. } } class Line extends Shape { private static int color. } public Circle(int xVal. } public void setColor(int newColor) { color = newColor.writeInt(color). } public int getColor() { return color. dim).writeInt(color). dim).readInt(). yVal. yVal.writeInt(color).} } } class Circle extends Shape { private static int color. int dim) { super(xVal. } public static void deserializeStaticState(ObjectInputStream os) throws IOException { color = os. } } class Square extends Shape { private static int color. yVal. int dim) { super(xVal.readInt(). public static void serializeStaticState(ObjectOutputStream os) throws IOException { os. } 538 Thinking in Java. } public int getColor() { return color. int yVal. dim). Circlecolor[3] xPos[7] yPos[88] dim[28] I/O 539 .deserializeStaticState(in). Line.Circlecolor[3] xPos[20] yPos[58] dim[16] .out")). // Read in the same order they were written: Circle.Linecolor[3] xPos[68] yPos[0] dim[22] .out.writeObject(shapes).get(i)).Squarecolor[3] xPos[61] yPos[61] dim[29] . i < 10. Circle. class io. out.GREEN).serializeStaticState(out).Linecolor[3] xPos[4] yPos[83] dim[6] .Linecolor[3] xPos[78] yPos[98] dim[61] .randomFactory()).println(shapes).Circlecolor[3] xPos[58] yPos[55] dim[93] .Circlecolor[3] xPos[7] yPos[88] dim[28] .serializeStaticState(out). // Save the state vector: ObjectOutputStream out = new ObjectOutputStream( new FileOutputStream("CADState.setColor(Shape.Linecolor[3] xPos[68] yPos[0] dim[22] .Circlecolor[3] xPos[75] yPos[10] dim[42] ] [class io.serializeStaticState(out).deserializeStaticState(in). // Display the shapes: System. } } /* Output: [class io.add(Shape. class io. // Make some shapes: for(int i = 0.Squarecolor[3] xPos[51] yPos[89] dim[9] . class io. // Set all the static colors to GREEN: for(int i = 0. class io.deserializeStaticState(in). Square. // Now read the file back in: ObjectInputStream in = new ObjectInputStream( new FileInputStream("CADState. System. shapes = (List<Shape>)in. class io. i < 10. Square.readObject(). i++) ((Shape)shapes. class io.Squarecolor[3] xPos[40] yPos[11] dim[22] . Line. class io.} public class E30_RepairCADState { @SuppressWarnings("unchecked") public static void main(String[] args) throws Exception { List<Shape> shapes = new ArrayList<Shape>().Circlecolor[3] xPos[58] yPos[55] dim[93] . class io. class io.Squarecolor[3] xPos[61] yPos[61] dim[29] . i++) shapes. class io.out")).out. class io.println(shapes). class io. private int zipCode. class Person { private String first.xom. } // Produce an XML Element from this Person object: public Element getXML() { Element person = new Element("person"). Element addr = new Element("address").appendChild(address).state = state. import java. this. import nu.appendChild(last). firstName. this. address. Element lastName = new Element("last"). String city. Exercise 31 //: io/E31_PeopleWithAddresses.nu } /****************** Exercise 31 ***************** * Add appropriate address information to Person. state. this. this.Circlecolor[3] xPos[20] yPos[58] dim[16] io.java.io. ***********************************************/ package io. rather than the default values.city = city. public Person(String first.Squarecolor[3] xPos[51] yPos[89] dim[9] io.xom. class .Node. class . this.first = first.Squarecolor[3] xPos[40] yPos[11] dim[22] io. String address. last. class . 540 Thinking in Java.Linecolor[3] xPos[4] yPos[83] dim[6] io. lastName. 4th Edition Annotated Solution Guide .*. city.Circlecolor[3] xPos[75] yPos[10] dim[42] We retrieve the stored color for each type.last = last.address = address. addr.. import java.xom. You must install // the XOM library from http://www. String state.zipCode = zipCode. class .java * and People. int zipCode) { this.java // {Requires: nu.*.util.*. class ] *///:~ io.appendChild(first). String last.Linecolor[3] xPos[78] yPos[98] dim[61] io. class . Element firstName = new Element("first"). setIndent(4). person. } // Make it human-readable: public static void format(OutputStream os."ISO-8859-1").appendChild(addr). } } class People extends ArrayList<Person> { public People(String fileName) throws Exception Document doc = new Builder().appendChild(lastName).appendChild(Integer. serializer.getFirstChildElement("first").getFirstChildElement("address"). for(int i = 0.appendChild(city).appendChild(firstName).appendChild(state).getRootElement(). serializer.getValue(). serializer. } public String toString() { return first + " " + last + " " + address + " " + city + " " + state + " " + zipCode. i < elements.flush().appendChild(cty). i++) add(new Person(elements. cty. Element st = new Element("state").size().getValue().build(fileName).getFirstChildElement("zipCode").setMaxLength(60). st. } // Constructor to restore a Person from an XML Element: public Person(Element person) { first= person.getFirstChildElement("state"). person. Element zc = new Element("zipCode"). person. zc. person.getValue()). person.appendChild(zc).valueOf( person.appendChild(st). Elements elements = doc. person.getFirstChildElement("city"). address = person.getValue().get(i))).write(doc).getValue(). Document doc) throws Exception { Serializer serializer= new Serializer(os. zipCode = Integer. city = person.Element cty = new Element("city"). serializer. last = person.getChildElements(). state = person.getValue(). { I/O 541 . return person.getFirstChildElement("last").toString(zipCode)). Person.out. "Street 1". new Person("Gonzo". "New York".} } public class E31_PeopleWithAddresses { public static void main(String[] args) throws Exception { List<Person> people = Arrays. Bunsen Honeydew Street 1 New York NY 10001. Fry Street 3 New York NY 30003] <?xml version="1. "Street 2".format(new BufferedOutputStream( new FileOutputStream("People.println(p). Document doc = new Document(root). "New York".xml")). "Fry".getXML()). System.out.asList( new Person("Dr. 10001).format(System. for(Person p : people) root. People p = new People("People.</first> 542 Thinking in Java. 4th Edition Annotated Solution Guide . System.appendChild(p. doc). "NY".out. new Person("Phillip J. Phillip J. Gonzo The Great Street 2 New York NY 20002. } } /* Output: [Dr. "Honeydew". "NY". "The Great". "NY".xml"). "Street 3". 20002). Person. Element root = new Element("people"). Bunsen". "New York".0" encoding="ISO-8859-1"?> <people> <person> <first>Dr. 30003)).println(people). doc).". Bunsen</first> <last>Honeydew</last> <address>Street 1</address> <city>New York</city> <state>NY</state> <zipCode>10001</zipCode> </person> <person> <first>Gonzo</first> <last>The Great</last> <address>Street 2</address> <city>New York</city> <state>NY</state> <zipCode>20002</zipCode> </person> <person> <first>Phillip J. *.util. write a * program that counts the occurrence of words in * a file (use "\\W+" as the second argument to the * TextFile constructor).nu } // {RunByHand} /****************** Exercise 32 ***************** * Using a Map<String.util.getValue(). record.mindview.Entry<String. return record. freq.mindview.toString()).Integer> wordsStat = I/O 543 .TextFile utility.getKey()).appendChild(me. Gonzo The Great Street 2 New York NY 20002. Bunsen Honeydew Street 1 New York NY 10001. You must install // the XOM library from http://www. word.Entry object: static Element getXML(Map.appendChild(freq).Node.Integer> me) { Element record = new Element("record"). ***********************************************/ package io.java // {Requires: nu.*. Element freq = new Element("frequency"). import net. } public static void main(String[] args) throws Exception { Map<String. import java. Fry Street 3 New York NY 30003] *///:~ Exercise 32 //: io/E32_WordsInfoXML. Store the results as an * XML file. record. Phillip J.util.<last>Fry</last> <address>Street 3</address> <city>New York</city> <state>NY</state> <zipCode>30003</zipCode> </person> </people> [Dr.xom. import java. public class E32_WordsInfoXML { // Produce an XML Element from this Map.io.appendChild(word). Element word = new Element("word").appendChild(me.*.xom.Integer> and the * net.*.xom. import nu. put(word.util.put("base directory".Print. "Not defined").mindview. import static net.Entry<String. Scanner sc = new Scanner(System. Person.out. doc).prefs.new HashMap<String.java // {RunByHand} /****************** Exercise 33 ***************** * Write a program that displays the current value * of a directory called "base directory" and * prompts you for a new value. directory = sc. ***********************************************/ package io.xml")).format(System.Integer>().util. for(String word : new TextFile("E32_WordsInfoXML.get(word).*. } Element root = new Element("words"). } } ///:~ Exercise 33 //: io/E33_PreferencesDemo. doc). directory).nextLine(). Person.util.class). printnb("Enter a new directory: ").*. String directory = prefs. "\\W+")) { Integer freq = wordsStat. freq == null ? 1 : freq + 1). 4th Edition Annotated Solution Guide . import java.entrySet()) root.*.Integer> me : wordsStat.appendChild(getXML(me)). wordsStat. Use the Preferences * API to store the value.format( new BufferedOutputStream(new FileOutputStream( "WordsInfo.java". for(Map. prefs.get("base directory". public class E33_PreferencesDemo { public static void main(String[] args) throws Exception { Preferences prefs = Preferences . Document doc = new Document(root).userNodeForPackage(E33_PreferencesDemo. print("\ndirectory: " + directory). import java.in). print("directory: " + directory). } } ///:~ 544 Thinking in Java. I/O 545 . . public void change() { switch(color) { case RED: color = GREEN. } } } /* Output: The traffic light is RED The traffic light is GREEN The traffic light is YELLOW The traffic light is RED The traffic light is GREEN The traffic light is YELLOW The traffic light is RED 547 .java * so you don’t have to qualify the enum instances. import static net. case YELLOW: color = RED. break. i < 7. } } public String toString() { return "The traffic light is " + color. for(int i = 0.util. ***********************************************/ package enumerated. } public static void main(String[] args) { E01_TrafficLight2 t = new E01_TrafficLight2().Signal.change().Enumerated Types Exercise 1 //: enumerated/E01_TrafficLight2. case GREEN: color = YELLOW.Print. public class E01_TrafficLight2 { Signal color = RED. import static enumerated.java /****************** Exercise 1 ***************** * Use a static import to modify TrafficLight.*.mindview.*. t. i++) { print(t). break. NUTTY.*. public static CartoonCharacter next() { return values()[rand.java package enumerated. RED. SILLY. } public static void main(String[] args) { for(int i = 0. PUNCHY. *///:~ The benefit of making the next( ) method static is that you do not need an instance of the enum to call a method. PUNCHY. What are the benefits * and drawbacks of this approach? ***********************************************/ package enumerated. BOB. 548 Thinking in Java. private static Random rand = new Random(47). The downside is that methods that take a Generator cannot accept CartoonCharacter.java /****************** Exercise 2 ***************** * Instead of implementing an interface. YELLOW. NUTTY.print(CartoonCharacter. i++) printNext(). PUNCHY. Exercise 2 //: enumerated/E02_EnumStaticImplementation. "). NUTTY. SPANKY. i < 10.out. } } /* Output: BOB. SLAPPY.length)]. BOB. 4th Edition Annotated Solution Guide .*///:~ //: enumerated/Signal.next() + ". import java. BOUNCY. } ///:~ This technique is not possible if the enum is defined in the same file or the default package.util. public enum Signal { GREEN. make * next() a static method. } } public class E02_EnumStaticImplementation { public static void printNext() { System. SLAPPY. NUTTY. enum CartoonCharacter { SLAPPY. SPANKY.nextInt(values(). } } Enumerated Types 549 . private Food[] values.Coffee.mindview. import net. DESSERT(Food.class). HUMMOUS. WATER. BEVERAGE(Food. MAINCOURSE(Food. DECAF_COFFEE. } enum MainCourse implements Food { LASAGNE.*. LENTILS. private Course(Class<? extends Food> kind) { values = kind.class).Dessert. ESPRESSO.class). SOUP. CREME_CARAMEL. COFFEE(Food. CAPPUCCINO. } enum Dessert implements Food { TIRAMISU.Beverage.java and demonstrate * that it works in Meal. interface Food { enum Appetizer implements Food { SALAD. } enum Beverage implements Food { BEER. GELATO. COLA.Exercise 3 //: enumerated/E03_Meal.MainCourse.Appetizer.java /****************** Exercise 3 ***************** * Add a new Course to Course. FRUIT.java.random(values). HERB_TEA. } public Food randomSelection() { return Enums. VINE.getEnumConstants(). PAD_THAI.util. BLACK_FOREST_CAKE. LATTE. } enum Coffee implements Food { BLACK_COFFEE.class). BURRITO. JUICE. } } enum Course { APPETIZER(Food. ***********************************************/ package enumerated. TEA. VINDALOO.class). SPRING_ROLLS. System.println("---"). i < 5. } System. 4th Edition Annotated Solution Guide .out.out.java 550 Thinking in Java. i++) { for(Course course : Course. } } } /* Output: SPRING_ROLLS VINDALOO COLA GELATO TEA --SPRING_ROLLS HUMMOUS BEER BLACK_FOREST_CAKE BLACK_COFFEE --SALAD LASAGNE VINE CREME_CARAMEL LATTE --SOUP HUMMOUS VINE TIRAMISU ESPRESSO --SOUP LASAGNE VINE BLACK_FOREST_CAKE BLACK_COFFEE --*///:~ Exercise 4 //: enumerated/E04_Meal2.public class E03_Meal { public static void main(String[] args) { for(int i = 0.values()) { Food food = course.randomSelection().println(food). BURRITO. GELATO. LENTILS. } enum Dessert implements Food { TIRAMISU.mindview. CAPPUCCINO. VINE. CREME_CARAMEL.Dessert.getEnumConstants().randomSelection(). LATTE. DECAF_COFFEE.Appetizer. WATER. } } public Food randomSelection() { return Enums. i++) { for(Meal2 meal : Meal2. DESSERT(Food.Coffee.class).class). MAINCOURSE(Food.java.random(values). } enum Coffee implements Food { BLACK_COFFEE.values()) { Meal2. import net. } enum Beverage implements Food { BEER. FRUIT.class).Beverage. TEA. Enumerated Types 551 . } enum MainCourse implements Food { LASAGNE. COLA./****************** Exercise 4 ***************** * Repeat the above exercise for Meal2. BLACK_FOREST_CAKE. VINDALOO. private Food[] values. BEVERAGE(Food. HERB_TEA. SOUP. System. HUMMOUS.class).MainCourse. COFFEE(Food. JUICE.util.println(food).class).*. enum Meal2 { APPETIZER(Food. } } public class E04_Meal2 { public static void main(String[] args) { for(int i = 0. PAD_THAI. i < 5. } public interface Food { enum Appetizer implements Food { SALAD. ***********************************************/ package enumerated. SPRING_ROLLS.Food food = meal.out. private Meal2(Class<? extends Food> kind) { values = kind. ESPRESSO. 4th Edition Annotated Solution Guide . * and CONSONANT.println("---"). Hint: Use varargs. } } } /* Output: SPRING_ROLLS VINDALOO COLA GELATO TEA --SPRING_ROLLS HUMMOUS BEER BLACK_FOREST_CAKE BLACK_COFFEE --SALAD LASAGNE VINE CREME_CARAMEL LATTE --SOUP HUMMOUS VINE TIRAMISU ESPRESSO --SOUP LASAGNE VINE BLACK_FOREST_CAKE BLACK_COFFEE --*///:~ Exercise 5 //: enumerated/E05_VowelsAndConsonants2.} System. SOMETIMES_A_VOWEL.out.java /****************** Exercise 5 ***************** * Modify control/VowelsAndConsonants.java so that * it uses three enum types: VOWEL. and remember that 552 Thinking in Java. The enum constructor should take * the various letters that describe that particular * category. } } public class E05_VowelsAndConsonants2 { public static void main(String[] args) { Random rand = new Random(47). for(int i = 0.*. print( CharacterCategory.addAll(Arrays. import java. i++) { int c = rand. 'o'. 'e'.toString()). 'w') { public String toString() { return "sometimes a vowel". } }.chars.*. chars) { if(chars != null) this.asList(chars)). CONSONANT { public String toString() { return "consonant".chars. 110: consonant z. } }. } } } /* Output: (Sample) y.getCategory((char)c). i < 100.Print. } public static CharacterCategory getCategory(Character c) { if(VOWEL.contains(c)) return VOWEL.. 121: sometimes a vowel n. SOMETIMES_A_VOWEL('y'.* varargs automatically creates an array for you.nextInt(26) + 'a'. 'u') { public String toString() { return "vowel". import static net. if(SOMETIMES_A_VOWEL. return CONSONANT.util.mindview.. 122: consonant Enumerated Types 553 . private HashSet<Character> chars = new HashSet<Character>(). ***********************************************/ package enumerated. private CharacterCategory(Character.chars. } }.contains(c)) return SOMETIMES_A_VOWEL. enum CharacterCategory { VOWEL('a'. 'i'. " + c + ": ").util. printnb((char)c + ". Each enum has a custom toString( ) method to return the appropriate text for that enum. SOMETIMES_A_VOWEL. and RegularEnumSet for types with up to 64 elements.) Exercise 6 We nest Appetizer. 554 Thinking in Java.Appetizer in the code. this produces a clearer structure. especially during maintenance. 98: consonant r. Nesting has another benefit when you use reflection to build up the grouping enum (like Course from TIJ4). Dessert.lang..html.Enum class. h.riehle. Pay special attention to overcoming the JDK’s constraints on the java.org/computer-science/research/1995/plop-1995trading. 104: consonant v. 4th Edition Annotated Solution Guide . whereby JumboEnumSet uses an array of longs. The main factory method (entry point) of the EnumSet class is noneOf( ). 120: consonant h. while RegularEnumSet uses a single long. when you reduce software maintenance you increase profit. Remember. 118: consonant *///:~ The getCategory( ) factory method returns the appropriate enum constant (based on the character provided): VOWEL. Try this as an additional exercise with TIJ4’s enumerated/menu/Meal. 104: consonant x. (See http://www. which the other static methods also call. 120: consonant x.) TIJ4 describes EnumSet’s very efficient bit vector representation.java program.. you know the terms are related. The design comes from Patterns for Encapsulating Class Trees. or CONSONANT. The clarity and comprehensibility of code are crucial.b. (More can be found in the Constant-specific methods section in TIJ4. When you see Food. MainCourse. and Coffee inside Food rather than making them independent enums that coincidentally use Food. 114: consonant . Exercise 7 EnumSet is an abstract class with two private implementation classes: JumboEnumSet for types with more than 64 elements. import static net. import java.OK3.YES2. m.YES2.OK1.NO3.YES4} enum Readability {ILLEGIBLE.class).NO2.Print.OK2. static long counter.NO1.*.java /****************** Exercise 8 ***************** * Modify PostOffice.address = Enums. Address Address: " + address + ".YES4} enum Address {INCORRECT. class FMail { // The NO's lower the probability of random selection: enum GeneralDelivery {YES.YES1.generalDelivery= Enums. import net.returnAddress = Enums.OK5.readability = Enums.class). m. Address Scanability: " + scannability + ".util.OK4. Address Readability: " + readability + ". Enumerated Types 555 . General Delivery: " + generalDelivery + ". Return address: " + returnAddress + ". Scannability scannability.OK6} enum ReturnAddress {MISSING. ***********************************************/ package enumerated. Forward address: " + forwardAddress.OK5} GeneralDelivery generalDelivery.random(Readability.OK3.scannability = Enums.util.YES3. public String toString() { return "Mail " + id.*.NO4. m. m.OK3. ReturnAddress returnAddress.OK2. Readability readability.random(ReturnAddress. Address address. ForwardAddress forwardAddress.java so it has the ability to * forward mail.mindview.NO5} enum Scannability {UNSCANNABLE.OK2.random(GeneralDelivery.random(Address. } // Generate test FMail: public static FMail randomMail() { FMail m = new FMail().YES1. m.random(Scannability. } public String details() { return toString() + ".mindview.util.OK5} enum ForwardAddress {MISSING.OK4.OK1.class).OK1.class).Exercise 8 //: enumerated/E08_MailForwarding.OK4.class).YES3.*. long id = counter++. } } } }.address) { case INCORRECT: return false. public Iterator<FMail> iterator() { return new Iterator<FMail>() { public boolean hasNext() { return n-. } }. default: print("Delivering "+ m + " automatically").random(ForwardAddress. 4th Edition Annotated Solution Guide . default: return false.generalDelivery) { case YES: print("Using general delivery for " + m). } public void remove() { // Not implemented throw new UnsupportedOperationException().scannability) { case UNSCANNABLE: return false. return true. return true.m. return m. } } public class E08_MailForwarding { enum MailHandler { GENERAL_DELIVERY { boolean handle(FMail m) { switch(m.forwardAddress = Enums.> 0. } public FMail next() { return randomMail(). } }. VISUAL_INSPECTION { boolean handle(FMail m) { 556 Thinking in Java. } } }. } public static Iterable<FMail> generator(final int count) { return new Iterable<FMail>() { int n = count.class). default: switch(m. MACHINE_SCAN { boolean handle(FMail m) { switch(m. print(m + " is a dead letter"). } } Enumerated Types 557 .values()) if(handler. handle(mail).returnAddress) { case MISSING: return false. default: print("Forwarding " + m). } public static void main(String[] args) { for(FMail mail : FMail. return true. default: print("Delivering " + m + " normally"). } static void handle(FMail m) { for(MailHandler handler : MailHandler. default: print("Returning " + m + " to sender").readability) { case ILLEGIBLE: return false. abstract boolean handle(FMail m). } } }. } } }.address) { case INCORRECT: return false. RETURN_TO_SENDER { boolean handle(FMail m) { switch(m.generator(10)) { print(mail. return true.forwardAddress) { case MISSING: return false. } } } }. default: switch(m. FORWARD_MAIL { boolean handle(FMail m) { switch(m.switch(m.handle(m)) return. print("*****"). return true.details()). Forward address: MISSING Mail 1 is a dead letter ***** Mail 2. Return address: OK1. Address Address: OK4. Return address: OK1. Forward address: MISSING Using general delivery for Mail 5 ***** Mail 6. Address Scanability: YES1.} /* Output: Mail 0. Return address: OK4. Address Readability: YES1. Forward address: OK2 Delivering Mail 4 automatically ***** Mail 5. Address Readability: YES3. General Delivery: NO1. Address Readability: YES1. Return address: OK4. Address Scanability: YES4. Return address: OK1. Address Address: OK4. Return address: MISSING. Forward address: MISSING Delivering Mail 3 automatically ***** Mail 4. Address Scanability: YES4. Forward address: OK5 Delivering Mail 6 automatically ***** Mail 7. General Delivery: NO3. Address Scanability: UNSCANNABLE. Address Address: OK6. Address Scanability: UNSCANNABLE. Address Scanability: YES3. General Delivery: NO2. General Delivery: YES. Return address: OK4. General Delivery: NO4. Return address: OK4. Address Scanability: YES2. General Delivery: NO4. Address Address: OK3. Return address: OK3. General Delivery: YES. Address Address: INCORRECT. Forward address: OK1 Delivering Mail 2 automatically ***** Mail 3. Address Address: INCORRECT. Forward address: OK5 Delivering Mail 0 normally ***** Mail 1. Address Readability: ILLEGIBLE. Address Address: OK1. Address Address: OK5. Address Readability: YES1. 4th Edition Annotated Solution Guide . Address Scanability: YES4. Forward address: OK5 Forwarding Mail 8 ***** 558 Thinking in Java. Address Readability: YES2. General Delivery: NO2. Address Scanability: YES3. Address Readability: YES4. Address Readability: YES4. Address Readability: YES2. Address Address: INCORRECT. General Delivery: NO2. Forward address: OK2 Using general delivery for Mail 7 ***** Mail 8. generalDelivery) { case YES: print("Using general delivery for " + m).scannability) { case UNSCANNABLE: return false.put(MailHandler. return true.MACHINE_SCAN.Command> em = new EnumMap<MailHandler. static { em.Command>(MailHandler. Return address: MISSING. General Delivery: NO1.Print. default: return false. new Command() { public boolean handle(Mail m) { switch(m.address) { case INCORRECT: return false. em. Forward address: OK1 Delivering Mail 9 automatically ***** *///:~ Exercise 9 //: enumerated/E09_PostOffice2. default: switch(m.util. default: print("Delivering "+ m + " automatically"). } } }).GENERAL_DELIVERY.*. interface Command { boolean handle(Mail m). import java. ***********************************************/ package enumerated. import static net. return true.java /****************** Exercise 9 ***************** * Modify class PostOffice so that it uses an * EnumMap.put(MailHandler. Address Scanability: YES4.Mail 9. Address Readability: YES1.*. new Command() { public boolean handle(Mail m) { switch(m.util. } public class E09_PostOffice2 { static EnumMap<MailHandler. } } Enumerated Types 559 .class).mindview. Address Address: OK5. } } }). new Command() { public boolean handle(Mail m) { switch(m.address) { case INCORRECT: return false. } } } /* Output: 560 Thinking in Java.handle(m)) return. return true. print("*****").generator(10)) { print(mail. 4th Edition Annotated Solution Guide . } } } }).RETURN_TO_SENDER. new Command() { public boolean handle(Mail m) { switch(m.values()) if(cmd. } static void handle(Mail m) { for(Command cmd : em. default: print("Returning " + m + " to sender"). } enum MailHandler { GENERAL_DELIVERY. handle(mail). default: switch(m. print(m + " is a dead letter").put(MailHandler.} }). } public static void main(String[] args) { for(Mail mail : Mail. default: print("Delivering " + m + " normally"). RETURN_TO_SENDER.VISUAL_INSPECTION.put(MailHandler. return true. MACHINE_SCAN. VISUAL_INSPECTION.returnAddress) { case MISSING: return false. em.readability) { case ILLEGIBLE: return false.details()). em. General Delivery: YES. Address Readability: ILLEGIBLE. General Delivery: NO5. Address Address: INCORRECT. Address Scanability: UNSCANNABLE. Address Scanability: YES3. General Delivery: YES. Address Scanability: YES1. General Delivery: NO4. Address Address: OK5. General Delivery: NO3. General Delivery: NO4. Return address: MISSING Mail 8 is a dead letter ***** Enumerated Types 561 . Address Readability: ILLEGIBLE. Address Scanability: YES1. Address Scanability: YES3. Address Address: OK2. Address Scanability: YES3. Return address: OK4 Returning Mail 3 to sender ***** Mail 4. Address Readability: YES3. Return address: OK2 Delivering Mail 5 automatically ***** Mail 6. Return address: OK5 Using general delivery for Mail 2 ***** Mail 3. Address Scanability: YES4. Return address: OK1 Delivering Mail 1 automatically ***** Mail 2. Address Address: OK1. Address Address: OK4. Return address: MISSING Using general delivery for Mail 7 ***** Mail 8. Address Readability: YES1. Return address: OK2 Returning Mail 4 to sender ***** Mail 5. Address Readability: YES4. Return address: OK1 Delivering Mail 0 normally ***** Mail 1. Address Readability: YES3. Return address: OK4 Using general delivery for Mail 6 ***** Mail 7.Mail 0. Address Address: INCORRECT. General Delivery: YES. General Delivery: NO3. Address Scanability: UNSCANNABLE. Address Address: INCORRECT. Address Readability: ILLEGIBLE. Address Scanability: YES3. Address Address: OK4. General Delivery: NO2. Address Address: OK1. Address Readability: YES1. Address Readability: YES1. RESTING.Print. Address Readability: YES2. // ***************************** // *** NEWLY ADDED CODE: END *** // ***************************** private static State state.*. private Input selection. import java.concurrent.java // {Args: VendingMachineInput.*. class VendingMachine2 { // ******************************* // *** NEWLY ADDED CODE: BEGIN *** // ******************************* private static class Context { private State state = State.util. 4th Edition Annotated Solution Guide . private int amount.*. private static int amount. } enum Machine { M1.class)).util. Return address: OK4 Delivering Mail 9 normally ***** *///:~ Exercise 10 //: enumerated/E10_VendingMachine2.txt} /****************** Exercise 10 ***************** * Modify class VendingMachine (only) using EnumMap * so that one program can have multiple instances * of VendingMachine.mindview.values()) em. import static net. 562 Thinking in Java.*. Address Scanability: UNSCANNABLE. ***********************************************/ package enumerated.Mail 9.Context> em = Collections.put(m. import java.mindview. static { for(Machine m : Machine.util. } private static Map<Machine. M3 } private static final ReentrantLock lock = new ReentrantLock(). Address Address: OK1. import net. new Context()). General Delivery: NO1.Context>(Machine.util.synchronizedMap( new EnumMap<Machine. M2.locks. private static Input selection. break.amount(). GIVING_CHANGE(StateDuration.amount(). else state = DISPENSING. case ITEM_SELECTION: selection = input.amount()) print("Insufficient money for " + selection). break. break. default: } } }. case SHUT_DOWN: state = TERMINAL. break. DISPENSING(StateDuration.amount(). case SHUT_DOWN: state = TERMINAL.categorize(input)) { case MONEY: amount += input.TRANSIENT) { void next() { print("here is your " + selection). state = ADDING_MONEY. } }. if(amount < selection. default: } } }.TRANSIENT) { void next() { if(amount > 0) { Enumerated Types 563 .categorize(input)) { case MONEY: amount += input. state = GIVING_CHANGE. case QUIT_TRANSACTION: state = GIVING_CHANGE. enum StateDuration { TRANSIENT } // Tagging enum enum State { RESTING { void next(Input input) { switch(Category. ADDING_MONEY { void next(Input input) { switch(Category. amount -= selection. } }.put(m.amount. try { state. } void next(Input input) { throw new RuntimeException("Only call " + "next(Input input) for non-transient states"). while(ctx. selection = ctx.next()).amount = amount.next(). TERMINAL { void output() { print("Halted"). } } // This method is now taking an extra parameter denoting // the ID of the VendingMachine "instance". ctx. static void run(Generator<Input> gen. ctx. } state = RESTING. ctx. ctx).unlock(). } void output() { print(amount).selection.lock(). 4th Edition Annotated Solution Guide .TRANSIENT states"). amount = 0.selection = selection. Machine m) { Context ctx = em. } finally { lock. } void next() { throw new RuntimeException("Only call next() for " + "StateDuration. state = ctx.TERMINAL) { lock.isTransient) state.state != State. } }. } Thread.state = state. state.print("Your change: " + amount).output(). em.state. } } } public class E10_VendingMachine2 { 564 Thinking in Java. while(state. amount = ctx.yield().get(m).next(gen. State() {} State(StateDuration trans) { isTransient = true. private boolean isTransient = false. m). final Generator<Input> g = gen.Machine.Machine m : VendingMachine2.length == 1) gen = new FileInputGenerator(args[0]). if(args. } }).run(g.start(). } } } /* Output: (Sample) 25 25 25 50 50 50 75 75 75 here is your CHIPS 0 here is your CHIPS 0 here is your CHIPS 0 100 100 100 200 200 200 here is your TOOTHPASTE 0 here is your TOOTHPASTE 0 here is your TOOTHPASTE 0 25 25 25 35 35 35 Your change: 35 Enumerated Types 565 . new Thread(new Runnable() { public void run() { VendingMachine2.public static void main(String[] args) { for(final VendingMachine2.values()) { Generator<Input> gen = new RandomInputGenerator(). all grouped as the Context class.0 Your change: 0 Your change: 0 25 25 25 35 35 35 Insufficient 35 Insufficient 35 Insufficient 35 60 60 60 70 70 70 75 75 75 Insufficient 75 Insufficient 75 Insufficient 75 Your change: 0 Your change: 0 Your change: 0 Halted Halted Halted *///:~ 35 35 money for SODA money for SODA money for SODA money for SODA money for SODA money for SODA 75 75 75 Each instance of VendingMachine2 has a different set of values for the variables that define its state. 4th Edition Annotated Solution Guide . We associate each VendingMachine2 instance with its context information using 566 Thinking in Java. *.*.mindview.util.util. so the * limits imposed by an enum on Input are impractical * (remember that enums are for a restricted set of * types).amount = amount.add(item). Modify VendingMachine. and initialize an * ArrayList of these objects from a text file (using * net. import java.Print.TextFile). We alter the run( ) method to incorporate the context switching mechanism. Exercise 11 //: enumerated/E11_VendingMachine3.parseInt(s[1])). VendedItem(String name. return new VendedItem(s[0]. Threading is only used as a demonstration in this solution. import net. public static void addItem(VendedItem item) { items. We run each machine in a different thread. } private static List<VendedItem> items = new ArrayList<VendedItem>(). Machine enum enumerates each different instance.EnumMap. Enumerated Types 567 . ***********************************************/ package enumerated. import static net. as shown in the main( ) method.mindview. } // The data is expected to be in a format: <name> <amount> public static VendedItem parse(String data) { String[] s = data. int amount) { this.txt} /****************** Exercise 11 ***************** * In a real vending machine you will want to easily * add and change the type of vended items.split(" "). you don’t need to know the details yet. String name.util.*. which we fill with data during class initialization.java so that the * vended items are represented by a class instead * of being part of Input.util. // A simple data holder class class VendedItem { int amount.name = name.java // {Args: VendedItems.mindview. this.txt VendingMachineInput. Integer. amount()").} // A very slow lookup procedure public static VendedItem lookup(String name) { for(VendedItem item : items) if(item. VENDED_ITEM.size())). } } enum Input2 { NICKEL(5). } } // A class representing an input to a state machine class ExtInput { Input2 input. ABORT_TRANSACTION { public int amount() { // Disallow throw new RuntimeException("ABORT. public int amount() { // Disallow throw new RuntimeException("SHUT_DOWN.nextInt(items. } }. } public int amount() { return item != null ? item. } Input2() {} int amount() { return value. }.value = value.equals(name)) return item. DOLLAR(100).toString(). this. VendedItem item. // In cents 568 Thinking in Java. 4th Edition Annotated Solution Guide . DIME(10).amount : input. // In cents Input2(int value) { this. } }.item = item. VendedItem item) { this. return null. } public String toString() { return item != null ? item. QUARTER(25). int value.input = input.amount().name : input.name.get(rand. ExtInput(Input2 input. STOP { // This must be the last instance. public static VendedItem randomSelection() { return items. } private static Random rand = new Random(47).amount()"). NICKEL.class. private static int amount = 0.class). ITEM_SELECTION(Input2. types) { values = types.VENDED_ITEM). case SHUT_DOWN: state = TERMINAL.length .put(type..input)) { case MONEY: amount += input.nextInt(values().amount().getEnumConstants()) for(Input2 type : c. Input2. c).RESTING.Category2> categories = new EnumMap<Input2.ABORT_TRANSACTION).categorize(input. static { for(Category2 c : Category2.get(input). SHUT_DOWN(Input2.values) categories. } public static Category2 categorize(Input2 input) { return categories. Input2. Category2(Input2.. QUIT_TRANSACTION(Input2. } private static EnumMap<Input2. state = ADDING_MONEY. public static Input2 randomSelection() { // Don't include STOP: return values()[rand. ADDING_MONEY { Enumerated Types 569 .DOLLAR). enum StateDuration { TRANSIENT } // Tagging enum enum State { RESTING { void next(ExtInput input) { switch(Category2. private Input2[] values. default: } } }. } } enum Category2 { MONEY(Input2.Category2>(Input2. break.DIME. Input2.static Random rand = new Random(47).1)]. } } public class E11_VendingMachine3 { private static State state = State.QUARTER.STOP). private static ExtInput selection = null. amount -= selection. case SHUT_DOWN: state = TERMINAL. } state = RESTING. DISPENSING(StateDuration.categorize(input.input)) { case MONEY: amount += input. } }. } }.TRANSIENT) { void next() { print("here is your " + selection). break. state = GIVING_CHANGE. case ITEM_SELECTION: selection = input. case QUIT_TRANSACTION: state = GIVING_CHANGE. if(amount < selection. } }.void next(ExtInput input) { switch(Category2. 4th Edition Annotated Solution Guide .amount()) print("Insufficient money for " + selection). TERMINAL { void output() { print("Halted"). amount = 0. break. GIVING_CHANGE(StateDuration.TRANSIENT) { void next() { if(amount > 0) { print("Your change: " + amount). 570 Thinking in Java. else state = DISPENSING. break. } void next(ExtInput input) { throw new RuntimeException("Only call " + "next(ExtInput input) for non-transient states"). } void next() { throw new RuntimeException("Only call next() for " + "StateDuration. State() {} State(StateDuration trans) { isTransient = true.amount(). default: } } }.TRANSIENT states").amount(). private boolean isTransient = false. Enumerated Types 571 . } Generator<ExtInput> gen.TERMINAL) { state.next()). ".")) VendedItem.hasNext()) return null.trim())). if(args. state.length < 1) { System.println( "The vended items data file is not given!").").parse(data. VendedItem. } } static void run(Generator<ExtInput> gen) { while(state != State.err. else gen = new RandomExtInputGenerator(). run(gen). } public ExtInput next() { if(!input. ".randomSelection(). // Parse the vended items data file for(String data : new TextFile(args[0].next(). while(state. } } // Create Inputs from a file of '. public FileExtInputGenerator(String fileName) { input = new TextFile(fileName.randomSelection()). } } // For a basic sanity check: class RandomExtInputGenerator implements Generator<ExtInput> { public ExtInput next() { return new ExtInput(Input2. } } public static void main(String[] args) { if(args.output().iterator(). return.isTransient) state.'-separated strings: class FileExtInputGenerator implements Generator<ExtInput> { private Iterator<String> input.next(gen.} void output() { print(amount).length == 2) gen = new FileExtInputGenerator(args[1]).addItem(VendedItem. // Rethrow the catched exception } } } /* Output: 25 50 75 here is your 0 100 200 here is your 0 25 35 Your change: 0 25 35 Insufficient 35 60 70 75 Insufficient 75 Your change: 0 Halted *///:~ CHIPS TOOTHPASTE 35 money for SODA money for SODA 75 This program uses a text file containing the merchandise list and merchandise values. if(item != null) return new ExtInput(Input2. Here’s the text file we use to produce the output above (VendingMachineInput.class. throw e. } catch(IllegalArgumentException e) { // B plan: probably a vended item. null).valueOf(Input2. item).txt’s content remains the same): //:! enumerated/VendedItems. try { return new ExtInput(Enum.next()..CHIPS 75.txt TOOTHPASTE 200.trim(). VendedItem item = VendedItem..VENDED_ITEM.String s = input.SOAP 50 ///:~ 572 Thinking in Java.SODA 100.s).lookup(s). 4th Edition Annotated Solution Guide . with a static ArrayList holding all items read from VendingMachine.java.java: VendedItem is a data holder with some utility methods.We add two classes to VendingMachine. and ExtInput represents an input to a state machine. Enumerated Types 573 . like parse( ). State enum does not require substantial change. . *.Member} /****************** Exercise 01 ***************** * Implement more SQL types in the database example. } 575 .*.database.java // {Args: annotations. @Target(ElementType. ***********************************************/ package annotations. @SQLCharacter(value = 15.RUNTIME) @interface SQLBoolean { String name() default "". Constraints constraints() default @Constraints. constraints = @Constraints(primaryKey = true)) String handle. } @Target(ElementType.Annotations Exercise 1 //: annotations/E01_TableCreator.annotation. String name() default "". @SQLBoolean Boolean isVIP.lang. import java. public String getHandle() { return handle. } @DBTable(name = "MEMBER") class Member { @SQLString(30) String firstName. import java.*. import java.FIELD) @Retention(RetentionPolicy.FIELD) @Retention(RetentionPolicy. import annotations.RUNTIME) @interface SQLCharacter { int value() default 0. @SQLString(50) String lastName. Constraints constraints() default @Constraints. } public String getFirstName() { return firstName.reflect.*.util. static int memberCount. @SQLInteger Integer age.lang. getAnnotation(DBTable.add(columnName + " VARCHAR(" + 576 Thinking in Java. for(Field field : cl. else columnName = sInt. } String tableName = dbTable.out.public public public public } String getLastName() { return lastName. columnDefs.out.toUpperCase().name().class). DBTable dbTable = cl. } public class E01_TableCreator { public static void main(String[] args) throws Exception { if(args.getName().println("arguments: annotated classes"). // Use field name if name not specified if(sInt.constraints())).name().name(). use the Class name: if(tableName.toUpperCase(). } Integer getAge() { return age. 4th Edition Annotated Solution Guide . continue.add(columnName + " INT" + getConstraints(sInt.println( "No DBTable annotations in class " + className). if(dbTable == null) { System. columnDefs. // Not a db table column if(anns[0] instanceof SQLInteger) { SQLInteger sInt = (SQLInteger) anns[0]. // If the name is empty. Annotation[] anns = field. System.length() < 1) columnName = field.name().getDeclaredAnnotations().name(). } else if(anns[0] instanceof SQLString) { SQLString sString = (SQLString) anns[0].getName(). // Use field name if name not specified. else columnName = sString.length() < 1) tableName = cl.toUpperCase(). } for(String className : args) { Class<?> cl = Class. if(sString.forName(className). if(anns.length < 1) continue.exit(0).length() < 1) columnName = field.getDeclaredFields()) { String columnName = null.length < 1) { System. } String toString() { return handle. } Boolean isVIP() { return isVIP. List<String> columnDefs = new ArrayList<String>().getName(). primaryKey()) constraints += " PRIMARY KEY". return constraints.Member is : CREATE TABLE MEMBER( Annotations 577 .name().getName().unique()) constraints += " UNIQUE". System.println("Table Creation SQL for " + className + " is :\n" + tableCreate).toUpperCase(). } } /* Output: Table Creation SQL for annotations.").substring( 0. } else if(anns[0] instanceof SQLBoolean) { SQLBoolean sBol = (SQLBoolean) anns[0]. else columnName = sChar.length() .append("\n " + columnDef + ".add(columnName + " CHARACTER(" + sChar.length() < 1) columnName = field. // Use field name if name not specified if(sBol. // Remove trailing comma String tableCreate = createCommand. for(String columnDef : columnDefs) createCommand.getName(). createCommand. // Use field name if name not specified. if(sChar.name().sString. columnDefs. columnDefs. if(con.name(). } } } private static String getConstraints(Constraints con) { String constraints = "".add(columnName + " BOOLEAN" + getConstraints(sBol.constraints())).value() + ")" + getConstraints(sString.value() + ")" + getConstraints(sChar. } else if(anns[0] instanceof SQLCharacter) { SQLCharacter sChar = (SQLCharacter) anns[0].toUpperCase().name().constraints())).1) + ").".constraints())). else columnName = sBol.allowNull()) constraints += " NOT NULL".length() < 1) columnName = field. } StringBuilder createCommand = new StringBuilder( "CREATE TABLE " + tableName + "(").out. if(con. if(!con. java // APT-based annotation processing. ISVIP BOOLEAN). public class E02_InterfaceExtractorProcessorFactory implements AnnotationProcessorFactory { public AnnotationProcessor getProcessorFor( Set<AnnotationTypeDeclaration> atds. /****************** Exercise 02 ***************** * Add support for division to the interface * extractor. LASTNAME VARCHAR(50)). Table Creation SQL for annotations. the latter represents a fixed-size string.*. *///:~ is : is : is : is : We add two new SQL types: SQLBoolean and SQLCharacter. AnnotationProcessorEnvironment env) { 578 Thinking in Java. HANDLE CHARACTER(15) PRIMARY KEY).FIRSTNAME VARCHAR(30)).Member CREATE TABLE MEMBER( FIRSTNAME VARCHAR(30). HANDLE CHARACTER(15) PRIMARY KEY.mirror. AGE INT.Member CREATE TABLE MEMBER( FIRSTNAME VARCHAR(30). LASTNAME VARCHAR(50).mirror.apt. LASTNAME VARCHAR(50). Table Creation SQL for annotations. import com. ***********************************************/ package annotations. LASTNAME VARCHAR(50).declaration. 4th Edition Annotated Solution Guide . AGE INT).Member CREATE TABLE MEMBER( FIRSTNAME VARCHAR(30). Table Creation SQL for annotations. Table Creation SQL for annotations.sun.*.*. import com.util.Member CREATE TABLE MEMBER( FIRSTNAME VARCHAR(30). Exercise 2 //: annotations/E02_InterfaceExtractorProcessorFactory. import java. AGE INT.sun. import com. public InterfaceExtractorProcessor( AnnotationProcessorEnvironment env) { this. 134)).util.divide(2678.out. import com.env = env.*.y.return new InterfaceExtractorProcessor(env).sun. } public Collection<String> supportedAnnotationTypes() { return Collections.emptySet().mirror.declaration. } public Collection<String> supportedOptions() { return Collections.sun.java package annotations. } private int sub(int x.*. public class InterfaceExtractorProcessor implements AnnotationProcessor { private final AnnotationProcessorEnvironment env. } } /* Output: 2678/134 = 19 *///:~ //: annotations/InterfaceExtractorProcessor. import java. while(x >= y) { x = sub(x.mirror. y).println( "2678/134 = " + new Divisor(). } Annotations 579 .*.java package annotations.singleton("annotations.ExtractInterface"). import java. int y) { int total = 0.*.io. private ArrayList<MethodDeclaration> interfaceMethods = new ArrayList<MethodDeclaration>(). } public static void main(String[] args) { System. } return total. } } ///:~ //: annotations/Divisor. total++. @ExtractInterface("IDivisor") class Divisor { public int divide(int x. int y) { return x .apt. getSimpleName() + " (").print(m.getType() + " " + parm.getParameters()) { writer.getFiler(). like Multiplier.getQualifiedName() +". int i = 0.class). if(interfaceMethods. With one minor modification to the InterfaceExtractorProcessor.println("public interface " + annot. writer. } writer. writer.print(parm.getMethods()) if(m. for(MethodDeclaration m : interfaceMethods) { writer. writer.getAnnotation(ExtractInterface.getModifiers(). } writer.size() > 0) { try { PrintWriter writer = env.contains(Modifier. ExtractInterface annot = typeDecl.size()) writer.STATIC))) interfaceMethods.close().PUBLIC) && !(m.println("package " + typeDecl.getSimpleName()).println(").add(m).clear(). if(++i < m.getReturnType() + " ").print(m.").println("}").print(".value() + " {"). for(ParameterDeclaration parm : m.contains(Modifier.public void process() { for(TypeDeclaration typeDecl : env."). works only with positive integers.print(" public "). } } } } } ///:~ Divisor.getSpecifiedTypeDeclarations()) { interfaceMethods.getModifiers().getPackage(). interfaceMethods now 580 Thinking in Java. for(MethodDeclaration m : typeDecl. writer. if(annot == null) break. 4th Edition Annotated Solution Guide . writer. ").value()).createSourceFile(annot.getParameters(). } catch(IOException ioe) { throw new RuntimeException(ioe). "annotations.*. import com.database.util.java.Constraints".mirror. "annotations.*.*.database.sun.util.mirror.util.java /****************** Exercise 03 ***************** * Add support for more SQL types to * TableCreationProcessorFactory. } Annotations 581 . private String sql = "".) Exercise 3 //: annotations/E03_TableCreationProcessorFactory.sun. "annotations.asList( "annotations.sun. "annotations.DeclarationVisitors.SQLInteger".database. public TableCreationProcessor( AnnotationProcessorEnvironment env) { this.xml.declaration. ***********************************************/ package annotations. AnnotationProcessorEnvironment env) { return new TableCreationProcessor(env). see build. } public Collection<String> supportedOptions() { return Collections.mirror.DBTable".*.env = env. import java.SQLString".clears at each iteration (and thus avoids accumulating methods from other classes).apt.SQLCharacter"). import com. } private static class TableCreationProcessor implements AnnotationProcessor { private final AnnotationProcessorEnvironment env.emptySet().SQLBoolean".mirror.database.sun. public class E03_TableCreationProcessorFactory implements AnnotationProcessorFactory { public AnnotationProcessor getProcessorFor( Set<AnnotationTypeDeclaration> atds. "annotations.database. import static com. import annotations.*. } public Collection<String> supportedAnnotationTypes() { return Arrays. import com. (To properly invoke apt.*. NO_OP)).length() < 1) columnName = d.public void process() { for(TypeDeclaration typeDecl : env. if(sString. if(dbTable != null) { sql += "CREATE TABLE ". else columnName = sString.name().length() < 1) columnName = d. sql = "". else columnName = sInt.length() .toUpperCase() : dbTable.getSpecifiedTypeDeclarations()) { typeDecl. if(d.name().toUpperCase(). sql.getSimpleName(). sql += "\n " + columnName + " VARCHAR(" + sString.constraints()) + ".getAnnotation( SQLInteger. } } private class TableCreationVisitor extends SimpleDeclarationVisitor { public void visitClassDeclaration( ClassDeclaration d) { DBTable dbTable = d.accept(getDeclarationScanner( new TableCreationVisitor().class).out. } } public void visitFieldDeclaration( FieldDeclaration d) { String columnName = "". sql += "\n " + columnName + " INT" + getConstraints(sInt.class) != null) { SQLString sString = d.value() + ")" + 582 Thinking in Java.name().class).getSimpleName(). sql = sql. // Use field name if name not specified. // Use field name if name not specified if(sInt.name().class) != null) { SQLInteger sInt = d.name(). sql += " (".getAnnotation(DBTable.". System.length() < 1) ? d.name().getAnnotation(SQLInteger.getAnnotation( SQLString.class).".println("creation SQL is :\n" + sql). 4th Edition Annotated Solution Guide .toUpperCase(). } if(d.1) + ").getAnnotation(SQLString. sql += (dbTable.substring(0.getSimpleName(). name(). else columnName = sChar.length() < 1) columnName = d. if(!con.name().value() + ")" + getConstraints(sChar. if(con.getAnnotation( SQLBoolean. if(sChar.toUpperCase().".class). } } } } ///:~ We reuse the new SQL types introduced in Exercise 1.) The –XclassesAsDecls non-standard apt option treats both class and source files as declarations to process. If you preserve annotations in class definitions. // Use field name if name not specified if(sBol.getAnnotation(SQLBoolean.length() < 1) columnName = d. else columnName = sBol. } if(d.allowNull()) constraints += " NOT NULL". return constraints.name(). // Use field name if name not specified.primaryKey()) constraints += " PRIMARY KEY".xml file shows how to properly invoke apt.toUpperCase().name().".constraints()) + ". } if(d.constraints()) + ". you can list compiled classes as declarations to process instead of source files.".getAnnotation(SQLCharacter.class) != null) { SQLCharacter sChar = d. The output is: Annotations 583 .getAnnotation( SQLCharacter. if(con.class) != null) { SQLBoolean sBol = d.unique()) constraints += " UNIQUE".getConstraints(sString. sql += "\n " + columnName + " CHARACTER(" + sChar. } } private String getConstraints(Constraints con) { String constraints = "".getSimpleName(). sql += "\n " + columnName + " BOOLEAN" + getConstraints(sBol.constraints()) + ". (The build.class).getSimpleName(). and accelerate the whole cycle. *.add("one"). 584 Thinking in Java. import java.add("one"). HANDLE CHARACTER(15) PRIMARY KEY. AGE INT. import net.util. } @Test void _t2() { assert testObject. } } /* Output: annotations.util. ISVIP BOOLEAN). } public static void main(String[] args) throws Exception { OSExecute.isEmpty().E04_TestSetupFixture .atunit.mindview.atunit.mindview. 4th Edition Annotated Solution Guide . _t1 . ***********************************************/ package annotations. testObject.*. LASTNAME VARCHAR(50). @Test void _t1() { assert testObject.command("java " + "net. testObject. Exercise 4 //: annotations/E04_TestSetupFixture. _t2 OK (2 tests) *///:~ We create a new testObject before each test.java /****************** Exercise 04 ***************** * Verify that a new testObject is created before * each test. public class E04_TestSetupFixture { HashSet<String> testObject = new HashSet<String>().creation SQL is : CREATE TABLE MEMBER ( FIRSTNAME VARCHAR(30). import net.isEmpty().*.mindview.AtUnit E04_TestSetupFixture"). otherwise either _t1 or _t2 would fail (depending on the order of execution). public class E06_LinkedListTest { Annotations 585 . import net.*.Exercise 5 //: annotations/E05_TestSetupFixture2.mindview.util.atunit. ***********************************************/ package annotations. add("one").mindview.util. } @Test void _t2() { assert isEmpty().*.E05_TestSetupFixture2 .java /****************** Exercise 06 ***************** * Test LinkedList using the approach shown in * HashSetTest. import net. add("one"). _t2 OK (2 tests) *///:~ Exercise 6 //: annotations/E06_LinkedListTest. import java.java.command("java " + "net.*.mindview. ***********************************************/ package annotations.*. _t1 .mindview. import java. } } /* Output: annotations.atunit.*.atunit. public class E05_TestSetupFixture2 extends HashSet<String> { @Test void _t1() { assert isEmpty().*.mindview. import net.util. } public static void main(String[] args) throws Exception { OSExecute.util.java /****************** Exercise 05 ***************** * Modify the above example to use the inheritance * approach. import net.AtUnit E05_TestSetupFixture2"). @Test void initialization() { assert testObject.LinkedList<String> testObject = new LinkedList<String>(). } @Test void _contains() { testObject.java /****************** Exercise 07 ***************** * Use the inheritance approach to modify Exercise 6. 4th Edition Annotated Solution Guide . } public static void main(String[] args) throws Exception { OSExecute.add("one").isEmpty(). } @Test void _remove() { testObject. assert testObject.util. } } /* Output: annotations.command("java " + " net.*. public class E07_LinkedListTest2 extends LinkedList<String> { @Test void initialization() { assert isEmpty().mindview.AtUnit E06_LinkedListTest").add("one"). } @Test void _remove() { 586 Thinking in Java.mindview.E06_LinkedListTest . assert testObject.util.atunit. testObject.mindview. import net.isEmpty(). initialization .*. _remove . _contains OK (3 tests) *///:~ Exercise 7 //: annotations/E07_LinkedListTest2.*.atunit. ***********************************************/ package annotations. import java.remove("one"). } @Test void _contains() { add("one"). assert contains("one").contains("one"). import net. } } /* Output: annotations. _hidden OK (1 tests) *///:~ Annotations 587 .mindview.atunit. } } /* Output: annotations. } public static void main(String[] args) throws Exception { OSExecute. } } public class E08_TestPrivateMethod extends PrivateMethod { @Test void _hidden() { assert visible(). } @TestProperty boolean visible() { return hidden(). _remove .command("java " + " net.*.AtUnit E07_LinkedListTest2").E07_LinkedListTest2 . remove("one").E08_TestPrivateMethod . _contains OK (3 tests) *///:~ Exercise 8 //: annotations/E08_TestPrivateMethod.mindview. class PrivateMethod { private boolean hidden() { return true. import net.mindview. assert isEmpty().mindview. import net.atunit.java /****************** Exercise 08 ***************** * Add a non-private @TestProperty method (described * above) to create a class with a private method. initialization .command("java " + " net.add("one").util. * Call this method in your test code.AtUnit E08_TestPrivateMethod"). ***********************************************/ package annotations. } public static void main(String[] args) { OSExecute.atunit.*. assert testObject.put("one".isEmpty().*.remove("one"). testObject. assert !testObject. 1). ***********************************************/ package annotations. 1). testObject. @Test void initialization() { assert testObject. } @Test void _size() { testObject.isEmpty(). public class E09_HashMapTest { HashMap<String.util. assert testObject.*.containsKey("one"). 1). import net.isEmpty().Integer> testObject = new HashMap<String. } @Test void _get() { testObject. 4th Edition Annotated Solution Guide . } @Test void _clear() { testObject.Integer>(). 2).containsValue(1).isEmpty().java /****************** Exercise 09 ***************** * Write basic @Unit tests for HashMap.put("one".get("one") == 1.util. assert testObject.put("one". } @Test void _containsValue() { testObject.*. } @Test void _remove() { testObject.mindview.Exercise 9 //: annotations/E09_HashMapTest. assert testObject. 1). 1).put("two". assert testObject.put("one". testObject.size() == 2. import net. import java. } @Test void _containsKey() { testObject. } public static void main(String[] args) throws Exception { 588 Thinking in Java. 1).put("one".atunit.clear().put("one". assert testObject.mindview. import net.YELLOW.util. case GREEN: color = Signal. case YELLOW: color = Signal. _containsValue . } } /* Output: annotations. _containsKey . } class TrafficLight { Signal color = Signal.RED // in the case statement: case RED: color = Signal. break. _clear . break. _get OK (7 tests) *///:~ Exercise 10 //: annotations/E10_TrafficLightTest.RED.mindview.RED.E09_HashMapTest . import net. break. enum Signal { GREEN.atunit.mindview.GREEN. ***********************************************/ package annotations. } } public String toString() { return "The traffic light is " + color. RED.*. } } Annotations 589 .AtUnit E09_HashMapTest").mindview.atunit.java /****************** Exercise 10 ***************** * Select an example from elsewhere in the book * and add @Unit tests.OSExecute.command("java " + " net. _remove . public void change() { switch(color) { // Note that you don't have to say Signal. initialization . _size . YELLOW.*. reflect.*.command("java " + " net. ***********************************************/ package annotations.*. 4th Edition Annotated Solution Guide .mindview.util. assert testObject.equals(str(Signal. import net. testObject.equals(str(Signal. } } /* Output: annotations.io. assert testObject.mindview.equals(str(Signal.AtUnit E10_TrafficLightTest"). @Test void initialization() { assert testObject. } @TestProperty private static String str(Signal s) { return "The traffic light is " + s.Print.E10_TrafficLightTest .lang. assert testObject.toString(). initialization . import java. testObject.mindview.util. so that the * accompanying note is simply displayed during testing.util.atunit. } public static void main(String[] args) throws Exception { OSExecute. import java.change().*.change(). 590 Thinking in Java. Exercise 11 //: annotations/E11_AtUnit2.*.YELLOW)).GREEN)).*.RED)).java program (from the Enumerated Types chapter) as an example. } @Test void _change() { testObject. import static net. _change OK (2 tests) *///:~ We use the enumerated/TrafficLight.*.atunit. import java.toString().equals(str(Signal.mindview.toString().public class E10_TrafficLightTest { TrafficLight testObject = new TrafficLight().change().RED)).java // {RunByHand} /****************** Exercise 11 ***************** * Add an @TestNote annotation to @Unit.toString(). import net. Annotations 591 . "class").read(cFile)). for(String failed : failedTests) print(" " + failed). if((testNote = m. // Check to see whether @TestNote is present. } TestMethods testMethods = new TestMethods().getAnnotation(Test. and used // correctly. static long testsRun = 0.put(m. if(failures == 0) print("OK (" + testsRun + " tests)"). testNote.forName(cName).String> testNotes = new HashMap<Method. } catch(Exception e) { throw new RuntimeException(e). print("\n>>> " + failures + " FAILURE" + (failures > 1 ? "S" : "") + " <<<"). static List<String> failedTests= new ArrayList<String>(). testNotes.thisClass( BinaryFile.getAnnotation(TestNote. // Ignore unpackaged classes testClass = Class.value()).Strategy { static Class<?> testClass.setDefaultAssertionStatus(true).addIfTestMethod(m).start(args). } } public void process(File cFile) { try { String cName = ClassNameFinder. HashMap<Method.getDeclaredMethods()) { testMethods. if(!cName.")) return.class)) != null) { if(m. // Enable asserts new ProcessFiles( new E11_AtUnit2(). TestNote testNote.getSystemClassLoader() .String>(). Method creator = null. else { print("(" + testsRun + " tests)"). Method cleanup = null. static long failures = 0. public static void main(String[] args) throws Exception { ClassLoader. for(Method m : testClass. too").class) == null) throw new RuntimeException("@TestNote method" + " must be a @Test method.public class E11_AtUnit2 implements ProcessFiles.contains(". equals(boolean.invoke(testObject. testsRun++. try { Object testObject = createTestObject(creator). } if(testMethods.getName()). if(testNotes.exit(1). } } catch(NoSuchMethodException e) { // Synthesized default constructor. } for(Method m : testMethods) { printnb(" . 592 Thinking in Java.getReturnType().invoke(testObject).getName()). if(cleanup == null) cleanup = checkForCleanupMethod(m).getName() + " "). boolean success = false.class)) success = (Boolean)m. OK } print(testClass. failedTests.} if(creator == null) creator = checkForCreatorMethod(m).getName() + ": " + m.get(m) + " "). 4th Edition Annotated Solution Guide . if(!success) { failures++. " + m.isPublic(testClass . } print(success ? "" : "(failed)").add(testClass. } if(cleanup != null) cleanup.containsKey(m)) printnb(" : " + testNotes. try { if(m.getDeclaredConstructor(). success = true.size() > 0) { if(creator == null) try { if(!Modifier. System. testObject). else { m.getModifiers())) { print("Error: " + testClass + " default constructor must be public").getCause()). // If no assert fails } } catch(InvocationTargetException e) { // Actual exception is inside e: print(e.invoke(testObject). getReturnType().setAccessible(true). m.equals(void. if(!m.getAnnotation(Test.").setAccessible(true)."). } } } static class TestMethods extends ArrayList<Method> { void addIfTestMethod(Method m) { if(m.getModifiers() & java.lang.lang.Modifier.").getReturnType().getReturnType(). return m.getModifiers() & java.class) == null) return.class))) throw new RuntimeException("@Test method" + " must return boolean or void").class) || m. } } private static Method checkForCreatorMethod(Method m) { if(m.reflect.STATIC) < 1) throw new RuntimeException("@TestObjectCleanup " + "must be static. if(m.getParameterTypes()[0] != testClass) throw new RuntimeException("@TestObjectCleanup " + "must take an argument of the tested type.STATIC) < 1) throw new RuntimeException("@TestObjectCreate " + "must be static.} catch(Exception e) { throw new RuntimeException(e).length == 0 || m.setAccessible(true).equals(testClass)) throw new RuntimeException("@TestObjectCreate " + "must return instance of Class to be tested").reflect. if((m. etc. m. m. return m. if((m.class) == null) return null. if(!(m.equals(boolean.Modifier. } private static Method checkForCleanupMethod(Method m) { if(m.getAnnotation(TestObjectCreate. add(m). // In case it's private.class) == null) return null.equals(void.class)) throw new RuntimeException("@TestObjectCleanup " + "must return void").getAnnotation(TestObjectCleanup. } Annotations 593 . if(!m.getParameterTypes().getReturnType(). } catch(Exception e) { throw new RuntimeException("Couldn't create a " + "test object.java // The @Unit @TestNote tag. @Target(ElementType.").lang.isEmpty(). } } // Use the default constructor: try { return testClass. 594 Thinking in Java. @TestNote("Tests if the new HashSet is empty ") @Test void initialization() { assert testObject. } @TestNote("Tests the HashSet.atunit. import net. } ///:~ Always pair the @TestNote annotation (if one is specified) with its corresponding @Test.RUNTIME) public @interface TestNote { String value() default "".util.mindview.").*. public class HashSetCommentedTest { HashSet<String> testObject = new HashSet<String>(). import java. } } } ///:~ //: annotations/TestNote. Here’s a demonstration: //: annotations/HashSetCommentedTest.contains(Object o) method") @Test void _contains() { testObject. } catch(Exception e) { throw new RuntimeException("Couldn't run " + "@TestObject (creator) method. package annotations.annotation.util.add("one").private static Object createTestObject(Method creator) { if(creator != null) { try { return creator. 4th Edition Annotated Solution Guide .*.invoke(testClass).java package annotations.*.METHOD) @Retention(RetentionPolicy.newInstance(). Try using a @TestObject method. import net.mindview.*. import java. initialization : Tests if the new HashSet is empty .HashSetCommentedTest . _contains : Tests the HashSet.contains(Object o) method OK (3 tests) *///:~ Annotations 595 . } @TestNote("Tests the HashSet. _remove : Tests the HashSet. } } /* Output: annotations.isEmpty(). } public static void main(String[] args) throws Exception { OSExecute.add("one").assert testObject.remove(Object o) method .E11_AtUnit2 HashSetCommentedTest").remove(Object o) method") @Test void _remove() { testObject. testObject.command("java " + " annotations.remove("one"). assert testObject.contains("one"). . print a * message.println("Printer ended. System. } public void run() { System.. Inside run(). and then return from run(). class Printer implements Runnable { private static int taskCount. ID = 2 597 .println("Stage 2.out. ID = 0 Printer started. Create * a number of these tasks and drive them using * threads.println("Printer started. Printer() { System.out.out.println("Stage 3. ***********************************************/ package concurrency. ID = " + id).out. ID = " + id).yield().. ID = " + id). } } public class E01_Runnable { public static void main(String[] args) { for(int i = 0. Repeat this * three times..start(). Thread.. i++) new Thread(new Printer()).. and then call yield().out. System.yield(). System.. Thread.. Thread. i < 5. } } /* Output: (Sample) Printer started...println("Stage 1.java /****************** Exercise 1 ***************** * Implement a Runnable. ID = 1 Printer started. Put * a startup message in the constructor and a * shutdown message when the task terminates. ID = " + id).Concurrency Exercise 1 //: concurrency/E01_Runnable.yield(). private final int id = taskCount++. ID = " + id). ID = 2 Stage 2.. public Fibonacci(int n) { this... Runnable { private int count.. * create a task that produces a sequence of n * Fibonacci numbers. ID = Printer ended. ***********************************************/ package concurrency. ID = *///:~ = 3 = 4 0 1 2 3 4 Exercise 2 //: concurrency/E02_Fibonacci.... ID = 0 Stage 3.. ID = 0 Stage 1. ID = Printer ended.. import java.mindview.... ID = 4 Stage 3. ID = 3 Stage 3. return fib(n-2) + fib(n-1). ID = Printer ended. ID = 1 Stage 2. ID = 4 Printer ended.... 598 Thinking in Java.. class Fibonacci implements Generator<Integer>. private final int n. ID = 0 Stage 2.. ID = 2 Stage 1.. ID = 4 Stage 2.. } public Integer next() { return fib(count++). ID = 3 Stage 1..... ID Stage 1.. 4th Edition Annotated Solution Guide ..Printer started... ID = 1 Stage 1...util.util.*. Create a number of these * tasks and drive them using threads. ID Printer started... } private int fib(int n) { if(n < 2) return 1..java.. ID = 3 Stage 2. ID = 2 Stage 3. import net.java /****************** Exercise 2 ***************** * Following the form of generics/Fibonacci..*...n = n...... ID = 1 Stage 3. where n is provided to the * constructor of the task..... ID = Printer ended. } public void run() { Integer[] sequence = new Integer[n]. exec.java /****************** Exercise 3 ***************** * Repeat Exercise 1 using the different types of * executors shown in this section. exec = Executors. for(int i = 0.newSingleThreadExecutor(). 2] Seq.shutdown(). i++) exec. of 1: [1] *///:~ Exercise 3 //: concurrency/E03_Runnable2.*. Thread.println( "Seq. of 3: [1. 2. i++) sequence[i] = next(). i < 5. i <= 5. public class E03_Runnable2 { public static void main(String[] args) { ExecutorService exec = Executors. i < 5. exec. 1.shutdown(). 2.execute(new Printer()). ***********************************************/ package concurrency.out.util. of 2: [1. exec = Executors. 3. of 5: [1. Thread. of " + n + ": " + Arrays. } } public class E02_Fibonacci { public static void main(String[] args) { for(int i = 1. 1. 5] Seq.yield(). for(int i = 0. of 4: [1. 1.start(). System.yield().newFixedThreadPool(5).newCachedThreadPool(). Concurrency 599 .toString(sequence)). } } /* Output: (Sample) Seq. i++) new Thread(new Fibonacci(i)). 1] Seq. i < n. 3] Seq. for(int i = 0. i++) exec.concurrent.execute(new Printer()). import java. Stage 3..... ID = 11 Stage 1. ID = 0 Stage 1..for(int i = 0... ID = 1 Stage 1. ID = 12 Stage 3. ID = 14 Stage 2.. ID = 4 . ID = 14 *///:~ The call to shutdown( ) is an asynchronous method call to initiate an orderly shutdown procedure. ID = 14 Stage 3. the executor will eventually terminate... ID = 12 Stage 1.” Exercise 4 //: concurrency/E04_Fibonacci2.. ID = 4 Stage 1. ID = 2 Printer started..yield()... i++) exec. no tasks are awaiting execution........ at which point no tasks are actively executing.execute(new Printer())... ID = 1 Printer started. } } /* Output: (Sample) Printer started. and no new tasks can be submitted.... i < 5.. 4th Edition Annotated Solution Guide ... ID = 2 Stage 1.. ID = 13 Printer ended. ***********************************************/ package concurrency. ID = 13 Stage 3... 600 Thinking in Java.. ID = 3 Stage 1... ID = 12 Printer ended..java /****************** Exercise 4 ***************** * Repeat Exercise 2 using the different types of * executors shown in this section. The JDK states: “After being shut down.. Thread.. ID = 3 Printer started. ID = 13 Stage 1.. ID = 12 Stage 2.. ID = 14 Printer ended.. exec.. ID = 13 Stage 2.... ID = 11 Printer ended.. ID = 0 Printer started.shutdown().. 5] *///:~ Exercise 5 //: concurrency/E05_FibonacciSum. Thread. for(int i = 1. exec = Executors. 3. 2] Seq.shutdown(). i++) exec. i++) exec. of 4: [1. 1] Seq. i <= 5.yield(). 2. exec. of 3: [1. of 3: [1. 2. 1. 2] Seq. 1. of 5: [1. * Create several tasks and display the results.execute(new Fibonacci(i)). 3. of 1: [1] Seq. ***********************************************/ Concurrency 601 . of 1: [1] Seq. 1.execute(new Fibonacci(i)). i++) exec. 2.concurrent. 2. 3] Seq. i <= 5. 1.shutdown().newSingleThreadExecutor(). of 2: [1. 2] Seq. 1. 2. of 4: [1. } } /* Output: (Sample) Seq. i <= 5. 3] Seq. 1. 1. exec. of 4: [1. 2. of 2: [1. of 5: [1. Thread.util.newFixedThreadPool(5). exec.java /****************** Exercise 5 ***************** * Modify Exercise 2 so that the task is a Callable * that sums the values of all the Fibonacci numbers.*. public class E04_Fibonacci2 { public static void main(String[] args) { ExecutorService exec = Executors. for(int i = 1.execute(new Fibonacci(i)).shutdown(). of 1: [1] Seq. 5] Seq. 1] Seq. 3.newCachedThreadPool(). 1. Thread. of 2: [1. of 5: [1.yield().yield(). 1. 5] Seq. 1] Seq. of 3: [1.import java. exec = Executors. for(int i = 1. 3] Seq. yield(). i++) results.*. import java. for(Future<Integer> fi : results) try { System.mindview. class FibonacciSum implements Generator<Integer>. } private int fib(int n) { if(n < 2) return 1. public FibonacciSum(int n) { this. i < n.add(exec. ArrayList<Future<Integer>> results = new ArrayList<Future<Integer>>().shutdown().util. Callable<Integer> { private int count.printStackTrace().*. } catch(Exception e) { e.newCachedThreadPool(). for(int i = 0. import java.n = n.util. i++) sum += next(). } } public class E05_FibonacciSum { public static void main(String[] args) { ExecutorService exec = Executors.util. Thread. import net.concurrent. i <= 5. return sum.submit(new FibonacciSum(i))). for(int i = 1.*.get()).out. exec. 4th Edition Annotated Solution Guide . } public Integer next() { return fib(count++).package concurrency. private final int n. } public Integer call() { int sum = 0. } } } /* Output: 1 2 4 7 12 *///:~ 602 Thinking in Java. return fib(n-2) + fib(n-1).println(fi. Create and run a quantity * (given on the command line) of these tasks.SECONDS.out. exec. private final int sleep_time = rnd. import java.*.execute(new SleepingTask2()).shutdown().err. Thread.nextInt(10) + 1. } catch(InterruptedException e) { System. public void run() { try { TimeUnit.err.sleep(sleep_time).java // {Args: 5} /****************** Exercise 6 ***************** * Create a task that sleeps for a random amount * of time between 1 and 10 seconds. return. i < Integer. } System. import java.util.println(sleep_time).yield(). } } /* Output: (Sample) 1 1 2 6 10 Concurrency 603 . ***********************************************/ package concurrency.length != 1) { System.concurrent.*. class SleepingTask2 implements Runnable { private static Random rnd = new Random(). then displays * its sleep time and exits.Exercise 6 //: concurrency/E06_SleepingTask2.newCachedThreadPool().parseInt(args[0]).util. if(args. } for(int i = 0.println("Interrupted: " + e). } } public class E06_SleepingTask2 { public static void main(String[] args) { ExecutorService exec = Executors. i++) exec.println( "Provide the quantity of tasks to run"). printnb("d.length != 1) { System.start().sleep(100).length. i < t.Print. ").isDaemon() + ". } try { // To better see the effect of altering main // application thread's sleep time.util. while(true) Thread.*. } catch(InterruptedException e) { /* Ignore */ } for(int i = 0.concurrent. Thread d = new Thread(new Daemon2()).java /****************** Exercise 7 ***************** * Experiment with different sleep times in * Daemons. TimeUnit. d. import static net.*. i++) { t[i] = new Thread(new DaemonSpawn()).isDaemon() = " + t[i].parseInt(args[0]).length.setDaemon(true).yield().java to see what happens. i < t. return.mindview. 4th Edition Annotated Solution Guide . ***********************************************/ package concurrency.println( "Sleep time needs to be provided in msecs"). "). d. } int sleep_time = Integer.err. i++) printnb("t[" + i + "].util.start().MILLISECONDS. public void run() { for(int i = 0.isDaemon() + ". import java. ").*///:~ Exercise 7 //: concurrency/E07_Daemons2.isDaemon() = " + d. 604 Thinking in Java. t[i]. class Daemon2 implements Runnable { private Thread[] t = new Thread[10]. } } public class E07_Daemons2 { public static void main(String[] args) throws Exception { if(args. printnb("DaemonSpawn " + i + " started. import java. t.TimeUnit. public class E08_MoreBasicDaemonThreads { public static void main(String[] args) { for(int i = 0.*. By providing different values (usually much less than 1 second).java so that a custom * ThreadFactory sets the priorities of the threads. } } /* Output: (Sample) Waiting for LiftOff *///:~ Run the program and you see that the daemon threads are unable to complete their countdowns before the program terminates.setDaemon(true).sleep(sleep_time). } System. Exercise 9 //: concurrency/E09_SimplePrioritiesTF. and verify that the * program ends as soon as main() is able to exit. ***********************************************/ package concurrency. Exercise 8 //: concurrency/E08_MoreBasicDaemonThreads.util. ***********************************************/ package concurrency.concurrent.println("Waiting for LiftOff").java /****************** Exercise 9 ***************** * Modify SimplePriorities.MILLISECONDS.java so that all the * threads are daemon threads. i < 5.java /****************** Exercise 8 ***************** * Modify MoreBasicThreads.out. you see that the chief daemon thread (running the Daemon2 task) abruptly terminates before even reaching the endless loop.start(). i++) { Thread t = new Thread(new LiftOff()). Concurrency 605 . } } /* (Execute to see output) *///:~ Our program accepts the sleep time (in milliseconds) as a command line argument. t. } public Thread newThread(Runnable r) { Thread t = new Thread(r). exec.yield(). PriorityThreadFactory(int priority) { this. private volatile double d. exec.) { // An expensive. } } } class PriorityThreadFactory implements ThreadFactory { private final int priority. i < 5. if(--countDown == 0) return. i < 100000.E) / (double)i. return t.shutdown(). exec = Executors. t.println(this).yield(). Thread.currentThread() + ": " + countDown. } public void run() { for(.priority = priority. } System. if(i % 1000 == 0) Thread. // No optimization public String toString() { return Thread. i++) exec.setPriority(priority).class SimplePriorities2 implements Runnable { private int countDown = 5.newCachedThreadPool( new PriorityThreadFactory(Thread. i++) { d += (Math.execute(new SimplePriorities2()). exec. Thread. } } /* Output: (Sample) 606 Thinking in Java.execute(new SimplePriorities2()). } } public class E09_SimplePrioritiesTF { public static void main(String[] args) { ExecutorService exec = Executors.newCachedThreadPool( new PriorityThreadFactory(Thread.shutdown(). 4th Edition Annotated Solution Guide .MIN_PRIORITY)).. interruptable operation: for(int i = 1.MAX_PRIORITY)). for(int i = 0.yield().PI + Math.out. 1.10.main]: 2 Thread[Thread-4.main]: 1 Thread[Thread-2.main]: 3 Thread[Thread-5. import java.*.10.1.1.1.main]: 3 Thread[Thread-4.main]: 5 Thread[Thread-3.1.main]: 1 Thread[Thread-3.1.main]: 3 Thread[Thread-1.1.1.main]: 3 Thread[Thread-0.main]: 1 *///:~ Exercise 10 //: concurrency/E10_FibonacciSum2.main]: 2 Thread[Thread-3.main]: 4 Thread[Thread-5.1.main]: 2 Thread[Thread-5.main]: 2 Thread[Thread-2.1.Thread[Thread-0.main]: 4 Thread[Thread-4.1.main]: 4 Thread[Thread-1.util.10.main]: 5 Thread[Thread-2.main]: 3 Thread[Thread-2. Concurrency 607 .main]: 4 Thread[Thread-3.*.1. ***********************************************/ package concurrency.main]: 5 Thread[Thread-5.1.util.main]: 3 Thread[Thread-3.1.1.main]: 2 Thread[Thread-1.main]: 5 Thread[Thread-1.main]: 2 Thread[Thread-0.main]: 1 Thread[Thread-0. so that runTask() takes an * argument of the number of Fibonacci numbers to sum.10.main]: 4 Thread[Thread-0.10.main]: 4 Thread[Thread-2.main]: 5 Thread[Thread-5.1.concurrent. import java.1.1.1.1.java /****************** Exercise 10 ***************** * Modify Exercise 5 following the example of the * ThreadMethod class.main]: 5 Thread[Thread-4.1.1.1.main]: 1 Thread[Thread-1. * and each time you call runTask() it returns * the Future produced by the call to submit().1.main]: 1 Thread[Thread-4.1. yield(). return fib(n-2) + fib(n-1). FibonacciSum2.runTask(i)).class FibonacciSum2 { private static ExecutorService exec. FibonacciSum2. for(int i = 1.add(FibonacciSum2.out.get()). } } } /* Output: 1 608 Thinking in Java.printStackTrace().submit(new Callable<Integer>() { public Integer call() { int sum = 0. } } public class E10_FibonacciSum2 { public static void main(String[] args) { ArrayList<Future<Integer>> results = new ArrayList<Future<Integer>>(). return exec. for(Future<Integer> fi : results) try { System.newCachedThreadPool(). } public static synchronized Future<Integer> runTask(final int n) { assert exec != null.shutdown(). } catch(Exception e) { e.shutdown(). 4th Edition Annotated Solution Guide . Thread. } }). } public static synchronized void init() { if(exec == null) exec = Executors. return sum. private static int fib(int n) { if(n < 2) return 1. i < n. } public static synchronized void shutdown() { if(exec != null) exec. for(int i = 0. i++) sum += fib(i).init(). i <= 5.println(fi. exec = null. i++) results. *.util. } public void fill() { state = State. which always must hold are the following: // 1. import java. // The conditions. * those fields are in an "improper state" (according to * some definition that you establish). ***********************************************/ package concurrency. If the tank is LOADED then the current_load >= 0 class Tank { enum State { EMPTY.util. during the execution of that method. If the tank is EMPTY then the current_load == 0 // 2.shutdown( ) static method at the end." Fix the problem using the * synchronized keyword. Notice the use of the synchronized keyword.java // {RunByHand} /****************** Exercise 11 ***************** * Create a class containing two data fields. public void validate() { if((state == State.init( ) at the beginning of each session. // Cause failure faster current_load = 10. LOADED }. and create multiple threads to call * the various methods and show that the data is visible * in its "improper state. import java. Exercise 11 //: concurrency/E11_RaceCondition.EMPTY && current_load != 0) || (state == State.EMPTY. private State state = State. // Arbitrary value Concurrency 609 . and the FibonacciSum2.yield(). Thread.concurrent. private int current_load = 0. Add methods to * read the fields.2 4 7 12 *///:~ Call FibonacciSum2. and a * method that manipulates those fields in a multistep * process so that.LOADED && current_load == 0)) throw new IllegalStateException().*.LOADED. after a dedicated thread finishes with FibonacciSum2. *. i < 10. 4th Edition Annotated Solution Guide . private Tank tank. exec. System.tank = tank.java // {RunByHand} package concurrency. } } class ConsistencyChecker implements Runnable { private static Random rnd = new Random(). ConsistencyChecker(Tank tank) { this.” We use a thread-safe variant of Tank.EMPTY. Tank tank = new Tank(). } } } public class E11_RaceCondition { public static void main(String[] args) { Thread. //: concurrency/E11_RaceConditionB.shutdown().newCachedThreadPool(). else tank.setDefaultUncaughtExceptionHandler( new MyUncaughtExceptionHandler()).yield(). Thread. press Control-C to exit the program.println("Press Control-C to exit").out.drain().) { // Decide whether to fill or drain the tank if(rnd. i++) exec. import java.util. ExecutorService exec = Executors. for(int i = 0.yield().validate(). } public void run() { for(. current_load = 0.nextBoolean()) tank.} public void drain() { state = State. tank.. 610 Thinking in Java.fill(). } } ///:~ This program throws an IllegalStateException when it reaches an “improper state.execute(new ConsistencyChecker(tank)).concurrent. Thread. out. i < 10.println("Press Control-C to exit"). } } i++. class AtomicityTest2 implements Runnable { private int i. } public class E12_AtomicityTest2 { public static void main(String[] args) { System.shutdown(). } } ///:~ Exercise 12 //: concurrency/E12_AtomicityTest2.fill().java using the synchronized * keyword.util. ExecutorService exec = Executors. public void run() { while(true) evenIncrement(). Thread. } } public class E11_RaceConditionB { public static void main(String[] args) { Thread.drain().execute(new ConsistencyChecker(tank)). for(int i = 0.validate(). } public synchronized void fill() { super.newCachedThreadPool(). System.println("Press Control-C to exit"). SafeTank tank = new SafeTank(). } private synchronized void evenIncrement() { i++.java // {RunByHand} /****************** Exercise 12 ***************** * Repair AtomicityTest.class SafeTank extends Tank { public synchronized void validate() { super.newCachedThreadPool().setDefaultUncaughtExceptionHandler( new MyUncaughtExceptionHandler()). i++) exec.*. ExecutorService exec = Executors. public synchronized int getValue() { return i.concurrent. } public synchronized void drain() { super. exec.out. Concurrency 611 . Can you demonstrate that it is now * correct? ***********************************************/ package concurrency. import java.yield(). java using the * synchronized keyword.execute(at). Accesses to the shared resource are fully serialized. However. } } public class E13_SerialNumberChecker2 { private static final int SIZE = 10. class SerialNumberGenerator2 { private static int serialNumber. System.getValue(). 4th Edition Annotated Solution Guide . using some semi-formal verification. the other waits. Formal verification is necessary for concurrent programming.AtomicityTest2 at = new AtomicityTest2().*. private static CircularSet serials = new CircularSet(1000).java // {Args: 4} /****************** Exercise 13 ***************** * Repair SerialNumberChecker.exit(0). and no race condition can occur. public synchronized static int nextSerialNumber() { return serialNumber++.util. we can reasonably examine the program. Writes by one thread are always visible to the other. But when you assess your program based on testing alone.println(val). Can you demonstrate that * it is now correct? ***********************************************/ package concurrency. Both methods are synchronized. with no visibility problems. exec. while(true) { int val = at. which might mean that it’s working correctly.out.concurrent. Exercise 13 //: concurrency/E13_SerialNumberChecker2. 612 Thinking in Java. } } } } ///:~ The program now runs indefinitely. you risk shipping incorrect code to the customer. import java. While one thread changes the state of an object. if(val % 2 != 0) { System. System.out.nextSerialNumber().exit(1).contains(serial)) { System. } } } /* Output: No duplicates detected *///:~ Again.newCachedThreadPool(). static class SerialChecker implements Runnable { public void run() { while(true) { int serial = SerialNumberGenerator2. so if we guard a field (in our case serialNumber) with synchronized methods or blocks.err.println("Provide a sleep time in sec.SECONDS. Exercise 14 //: concurrency/E14_ManyTimers.java // {Args: 100} /****************** Exercise 14 ************************ * Demonstrate that java.exit(0). } else { System. i++) exec. System.util.Timer scales to large numbers * by creating a program that generates many Timer objects * that perform some simple task when the timeout completes. i < SIZE. System. Synchronization flushes main memory.sleep(new Integer(args[0])). Concurrency 613 .length > 0) { TimeUnit. notice that you can safely remove the volatile keyword from the code.private static ExecutorService exec = Executors. if(args. if(serials. ******************************************************/ package concurrency.add(serial).println("Duplicate: " + serial). System. } serials.execute(new SerialChecker()). } } } public static void main(String[] args) throws Exception { for(int i = 0. it does not need to be volatile.").exit(0).println("No duplicates detected").out. 4th Edition Annotated Solution Guide .out.println(System.err. } int numOfTimers = Integer.MILLISECONDS.sleep(2 * numOfTimers). Now modify the methods so * that each one synchronizes on a different object and 614 Thinking in Java. public class E14_ManyTimers { public static void main(String[] args) throws Exception { if(args.exit(0).currentTimeMillis()).i). Create * multiple tasks to demonstrate that only one of these * methods can run at a time. System. import java.util.java /****************** Exercise 15 ************************ * Create a class with three methods containing critical * sections that all synchronize on the same object. i++) { new Timer().parseInt(args[0]). numOfTimers .*. i < numOfTimers.util.concurrent. } // Wait for timers to expire TimeUnit.length < 1) { System.import java.schedule(new TimerTask() { public void run() { System. for(int i = 0...*.println( "Usage: java E14_ManyTimers <num of timers>"). } } /* Output: (Sample) 1133967112886 1133967112886 1133967112886 1133967112886 1133967112886 1133967112896 1133967112896 1133967112896 . } }. 1133967112966 1133967112966 1133967112966 1133967112966 *///:~ Exercise 15 //: concurrency/E15_SyncObject. } } public synchronized void g() for(int i = 0. Thread. i++) { print("f()").yield(). i++) print("f()"). Concurrency 615 .yield().mindview.Print. i++) print("h()"). } } } { { { { { { class TripleSynch { private Object syncObjectG = new Object(). } } public void g() { synchronized(syncObjectG) { for(int i = 0.yield(). } } public synchronized void h() for(int i = 0. private Object syncObjectH = new Object().* show that all three methods can be running at once. public synchronized void f() { for(int i = 0. i < 5. i < 5. import static net.util. ******************************************************/ package concurrency. } } } public void h() { synchronized(syncObjectH) { for(int i = 0. Thread. Thread. i++) { print("h()"). i < 5. Thread. i < 5. i++) { print("g()").yield(). i < 5. i < 5. i++) print("g()"). Thread.yield(). class SingleSynch { public synchronized void f() for(int i = 0.*. start(). } } } } public class E15_SyncObject { public static void main(String[] args) throws Exception { final SingleSynch singleSync = new SingleSynch(). // Wait for t2 to finish its work print("Test TripleSynch.h(). tripleSync. t2. h() h() h() h() h() f() 616 Thinking in Java.. } } /* Output: (Sample) Test SingleSynch. new Thread() { public void run() { tripleSync. } }. t1."). print("Test SingleSynch.start()..Thread. Thread t1 = new Thread() { public void run() { singleSync.join(). 4th Edition Annotated Solution Guide . Thread t2 = new Thread() { public void run() { singleSync.f(). // Wait for t1 to finish its work t2.h(). final TripleSynch tripleSync = new TripleSynch().g(). t1..start().start(). } }.f(). } }. } }. singleSync.yield()."). new Thread() { public void run() { tripleSync...g().join().. lock().f() f() f() f() g() g() g() g() g() Test TripleSynch.*. try { Concurrency 617 .util. ******************************************************/ package concurrency... public void f() { lock.util. h() f() g() h() f() g() h() f() g() h() f() g() h() f() g() *///:~ The output shows that all methods of TripleSynch are running at the same time.Print. import static net. none are blocked by the synchronization of another.locks. Exercise 16 //: concurrency/E16_ExplicitSyncObject.*. This is clearly not the case with SingleSynch.mindview. import java.java /****************** Exercise 16 ************************ * Modify Exercise 15 to use explicit Lock objects.concurrent. class ExplicitSingleSynch { private Lock lock = new ReentrantLock(). } } } class ExplicitTripleSynch { private Lock lockF = new ReentrantLock(). Thread. } } finally { lock.lock(). } } public void h() { lock. public void f() { lockF.yield().lock().for(int i = 0. } } 618 Thinking in Java. try { for(int i = 0.unlock(). Thread.yield().unlock(). private Lock lockH = new ReentrantLock().lock().yield(). 4th Edition Annotated Solution Guide . } } public void g() { lock. i < 5. Thread. i < 5. i < 5. i++) { print("g()").unlock(). private Lock lockG = new ReentrantLock(). Thread. } } finally { lock. } } finally { lockF. i++) { print("f()").yield().unlock(). i++) { print("f()"). try { for(int i = 0. } } finally { lock. i < 5. try { for(int i = 0. i++) { print("h()"). join(). } } finally { lockG. t1.yield().lock(). Thread t2 = new Thread() { public void run() { singleSync.unlock().yield(). i < 5. singleSync. t2. try { for(int i = 0. } }. Thread. } }. Thread t1 = new Thread() { public void run() { singleSync. Thread.h().. } } finally { lockH. i++) { print("g()"). t1. i++) { print("h()").join()...lock()."). } } } public class E16_ExplicitSyncObject { public static void main(String[] args) throws Exception { final ExplicitSingleSynch singleSync = new ExplicitSingleSynch(). // Wait for t1 to finish its work t2. } } public void h() { lockH. Concurrency 619 .."). i < 5.start().start(). final ExplicitTripleSynch tripleSync = new ExplicitTripleSynch(). try { for(int i = 0.public void g() { lockG. print("Test ExplicitSingleSynch. // Wait for t2 to finish its work print("Test ExplicitTripleSynch.f().g().unlock(). new Thread() { public void run() { tripleSync..start()..g(). } } /* Output: (Sample) Test ExplicitSingleSynch. h() f() g() h() f() g() h() f() g() h() f() g() h() f() g() *///:~ 620 Thinking in Java. h() h() h() h() h() f() f() f() f() f() g() g() g() g() g() Test ExplicitTripleSynch.h().f(). new Thread() { public void run() { tripleSync. } }.start(). } }.. 4th Edition Annotated Solution Guide .. tripleSync. public static void cancel() { canceled = true. import java. ******************************************************/ package concurrency. private static List<Sensor> sensors = new ArrayList<Sensor>(). if(rand. } public Sensor(int id) { this. private static Count count = new Count(). private final int id. import static net.*.Print.util.add(this).mindview.*.util.*. private static volatile boolean canceled = false. private Random rand = new Random(47). private int number.util. return (count = ++temp). // Remove the synchronized keyword to see counting fail: public synchronized int increment() { int temp = count. import java. } public void run() { while(!canceled) { // Simulate a random occurence of an ionizing event if(rand. } public synchronized int value() { return count. } try { Concurrency 621 .id = id.yield(). } count.nextInt(5) == 0) { synchronized(this) { ++number.increment(). class Count { private int count = 0.Exercise 17 //: concurrency/E17_RadiationCounter.concurrent. sensors.nextBoolean()) // Yield half the time Thread.java /****************** Exercise 17 ************************ * Create a radiation counter that can have any number of * remote sensors. } } class Sensor implements Runnable { private static Random rand = new Random(47). exec.sumSensors()). print("Total: " + Sensor. return sum. } catch(InterruptedException e) { print("sleep interrupted"). Sensor. } public String toString() { return "Sensor " + id + ": " + getValue(). TimeUnit.MILLISECONDS)) print("Some tasks were not terminated!"). } public static int sumSensors() { int sum = 0.value().getValue(). print("Sum of Sensors: " + Sensor.TimeUnit. 622 Thinking in Java.newCachedThreadPool().getTotalCount()). for(Sensor sensor : sensors) sum += sensor.shutdown(). } public static int getTotalCount() { return count.SECONDS. } } /* Output: (Sample) Total: 25 Sum of Sensors: 25 *///:~ We follow the logic of the concurrency/OrnamentalGarden.sleep(100).awaitTermination(250. } } } public synchronized int getValue() { return number.sleep(3).cancel(). TimeUnit. The radiation counter processes and counts the electrical pulse caused by each ionizing event.MILLISECONDS. i++) exec. and Sensor a radiation detector. } } public class E17_RadiationCounter { public static void main(String[] args) throws Exception { ExecutorService exec = Executors. The output measures the radiation over time (hard-coded to 3 seconds). i < 5.java program from TIJ4. 4th Edition Annotated Solution Guide . Count acts as an event processing unit. for(int i = 0.execute(new Sensor(i)). if(!exec. *.java /****************** Exercise 18 ************************ * Create a non-task class with a method that calls * sleep() for a long interval.interrupt().util.SECONDS. } } /* Output: java. then call interrupt() to terminate it.sleep(1).SECONDS.longMethod().java Concurrency 623 . } catch (InterruptedException ie) { System. // Waits one minute } } class Task implements Runnable { public void run() { try { NonTask.InterruptedException: sleep interrupted *///:~ Exercise 19 //: concurrency/E19_OrnamentalGarden2. In main()...start(). Make sure * that the task shuts down safely. start the * task. } finally { // Any cleanup code here.lang. t.toString()). import java.Exercise 18 //: concurrency/E18_Interruption.out. } } } public class E18_Interruption { public static void main(String[] args) throws Exception { Thread t = new Thread(new Task()).println(ie. TimeUnit. t.sleep(60). Create a task that calls * the method in the non-task class.concurrent. ******************************************************/ package concurrency. class NonTask { static void longMethod() throws InterruptedException { TimeUnit. ******************************************************/ package concurrency. entrances.) { synchronized(this) { ++number. class Entrance2 implements Runnable { private static Count count = new Count(). } print(this + " Total: " + count. 4th Edition Annotated Solution Guide .MILLISECONDS./****************** Exercise 19 ************************ * Modify OrnamentalGarden. } public String toString() { return "Entrance " + id + ": " + getValue(). private static List<Entrance2> entrances = new ArrayList<Entrance2>().id = id.mindview. } public static int sumEntrances() { int sum = 0.increment()). public Entrance2(int id) { this.. import static net. private final int id. return. private int number.util. } catch(InterruptedException e) { print("Stopping " + this).sleep(100).value(). try { TimeUnit.getValue(). import java. for(Entrance2 entrance : entrances) sum += entrance. } } } public synchronized int getValue() { return number.util.*. import java. } } public class E19_OrnamentalGarden2 { 624 Thinking in Java.*.Print.util. } public void run() { for(.java so that it uses * interrupt(). } public static int getTotalCount() { return count.*.concurrent.add(this). return sum. Exercise 20 //: concurrency/E20_InterruptCachedThreadPool. exec. print("Sum of Entrances: " + Entrance2. Entrance 2: 31 Total: 152 Entrance 3: 31 Total: 153 Entrance 4: 31 Total: 154 Entrance 0: 31 Total: 155 Terminating Entrance 0: 31 Terminating Entrance 4: 31 Terminating Entrance 3: 31 Terminating Entrance 2: 31 Terminating Entrance 1: 31 Total: 155 Sum of Entrances: 155 *///:~ Notice how much neater this program is with the built-in thread interruption facility. private static int taskCount. i++) exec. if(!exec.shutdownNow(). i < 5.newCachedThreadPool(). class LiftOff2 implements Runnable { protected int countDown = 5000.public static void main(String[] args) throws Exception { ExecutorService exec = Executors. } } /* Output: (Sample) Entrance 1: 1 Total: 2 Entrance 2: 1 Total: 3 Entrance 3: 1 Total: 4 Entrance 4: 1 Total: 5 .concurrent.awaitTermination(250. TimeUnit.*. ******************************************************/ package concurrency.getTotalCount()).SECONDS.MILLISECONDS)) print("Some tasks were not terminated!").sleep(3). TimeUnit.util. import java. Concurrency 625 .. print("Total: " + Entrance2.java /****************** Exercise 20 ************************ * Modify CachedThreadPool..sumEntrances()).java so that all tasks receive * an interrupt() before they are completed. for(int i = 0.execute(new Entrance2(i)). #1(4999).private final int id = taskCount++. Interrupted: #0 Interrupted: #1 Interrupted: #2 Interrupted: #3 Interrupted: #4 *///:~ The above solution is simple: all tasks receive an interrupt( ) before completing.isInterrupted()) { System. } } /* Output: (Sample) #0(4999).print(status()).out. i < 5. #4(4999). } public String status() { return "#" + id + "(" + (countDown > 0 ? countDown : "Liftoff!") + "). exec. } System. #3(4999).yield(). return.yield(). #2(4999).java /****************** Exercise 21 ****************** * Create two Runnables. ".currentThread(). one with a run() that 626 Thinking in Java.execute(new LiftOff2()).shutdownNow(). 4th Edition Annotated Solution Guide . } } } public class E20_InterruptCachedThreadPool { public static void main(String[] args) { ExecutorService exec = Executors. } public void run() { while(countDown-.countDown = countDown. Thread.newCachedThreadPool(). Thread. for(int i = 0. Exercise 21 //: concurrency/E21_ThreadCooperation.out.println("Interrupted: #" + id).> 0) { if(Thread. public LiftOff2() {} public LiftOff2(int countDown) { this. i++) exec. Concurrency 627 . Test * your classes using an Executor.mindview. try { TimeUnit.execute(coop1). ExecutorService exec = Executors. } catch(InterruptedException ignore) {} print("Coop2 calling notifyAll"). The second class should * capture the reference of the first Runnable object.sleep(5). synchronized(otherTask) { otherTask.newCachedThreadPool(). print("Constructed Coop2"). import java. } public void run() { print("Coop2 pausing 5 seconds"). } } class Coop2 implements Runnable { Runnable otherTask. coop2 = new Coop2(coop1). ***********************************************/ package concurrency.otherTask = otherTask. exec. } public void run() { print("Coop1 going into wait"). public Coop2(Runnable otherTask) { this.* starts and calls wait().notifyAll(). import static net. exec.*.*.execute(coop2). } } } public class E21_ThreadCooperation { public static void main(String args[]) throws Exception { Runnable coop1 = new Coop1(). class Coop1 implements Runnable { public Coop1() { print("Constructed Coop1"). * Its run() should call notifyAll() for the first * task after some number of seconds have passed so * that the first task can display a message.concurrent. synchronized(this) { try { wait().util.SECONDS.Print. } catch(InterruptedException ignore) {} } print("Coop1 exited wait").util. . public static void main(String[] args) throws Exception { Runnable r1 = new Runnable() { public void run() { for(. } } }. 4th Edition Annotated Solution Guide . You will explore more sophisticated solutions later in TIJ4.) { try { TimeUnit.*. One task sleeps for a * while and then sets a flag to true. Exercise 22 //: concurrency/E22_BusyWait. } catch (InterruptedException e) { return. Note how * much wasted time the program spends inside the busy wait.sleep(10). import java.util. private static int spins.MILLISECONDS. The second task * watches that flag inside a while loop (this is the busy * wait) and when the flag becomes true. sets it back to * false and reports the change to the console.Thread. ********************************************************/ package concurrency. You should not synchronize tasks based on different timings. } flag = true.java /****************** Exercise 22 *************************** * Create an example of a busy wait. exec. * and create a second version of the program that uses * wait() instead of the busy wait. public class E22_BusyWait { private static volatile boolean flag.shutdown(). although it runs correctly. 628 Thinking in Java. } } /* Output: (Sample) Constructed Coop1 Constructed Coop2 Coop1 going into wait Coop2 pausing 5 seconds Coop2 calling notifyAll Coop1 exited wait *///:~ This is not a properly written concurrent program.yield().concurrent. Normally.currentThread(). System.execute(r1). However.util.java // The second version using wait(). TimeUnit..Runnable r2 = new Runnable() { public void run() { for(. exec. import java..) { // The busy-wait loop while(!flag && !Thread. you might see this with a busy-wait: while(!flag) . spins = 0.concurrent..out.sleep(1). Run the program to see why it’s called “busy wait. exec. //: concurrency/E22_WaitNotify. } } /* Output: (Sample) Spun 5546 times Spun 6744 times Spun 20270 times .SECONDS.println("Spun " + spins + " times"). Spun 35944 times Spun 17733 times Spun 23670 times *///:~ flag facilitates communication between the two tasks.*. we are tracking the amount of activity in the busy-wait loop. exec. package concurrency.” wait( ) and notify( ) don’t raise a flag because the communication occurs via the threading mechanism.execute(r2).newCachedThreadPool().shutdownNow(). ExecutorService exec = Executors. Concurrency 629 .isInterrupted()) spins++.interrupted()) return. if(Thread. flag = false. } } }. *********************************************************/ package concurrency.SECONDS.execute(r1). if you comment out the corresponding line. exec. } } /* Output: (Sample) Cycled Cycled Cycled Cycled Cycled Cycled Cycled Cycled Cycled Cycled *///:~ The r2 task calls wait( ) on r1. synchronized(this) { notify(). 630 Thinking in Java.. TimeUnit.) { try { TimeUnit.public class E22_WaitNotify { public static void main(String[] args) throws Exception { final Runnable r1 = new Runnable() { public void run() { for(.print("Cycled "). 4th Edition Annotated Solution Guide .waxomatic. this program’s “correctness” depends upon r1’s sleeping time. and r1 calls notify( ) on itself. } } } }. } } catch(InterruptedException e) { return. ExecutorService exec = Executors. } } }. } } catch(InterruptedException e) { return..java /********************** Exercise 23 *********************** * Demonstrate that WaxOMatic. You’ll study advanced task control techniques later in TIJ4. } System.sleep(1).out.shutdownNow(). exec. Exercise 23 //: concurrency/waxomatic/E23_WaxOMatic2. Again.MILLISECONDS.java works when * you use notify() instead of notifyAll().execute(r2).) { try { synchronized(r1) { r1.wait().sleep(100). Runnable r2 = new Runnable() { public void run() { for(. exec.newCachedThreadPool(). you deadlock due to a missed signal. *.mindview.concurrent.util. } public synchronized void waitForBuffing() throws InterruptedException { while(waxOn == true) wait(). car. } public void run() { Concurrency 631 .sleep(200).interrupted()) { printnb("Wax On! ").waxed(). } } catch(InterruptedException e) { print("Exiting via interrupt"). } public synchronized void waitForWaxing() throws InterruptedException { while(waxOn == false) wait(). public synchronized void waxed() { waxOn = true.MILLISECONDS. } } class WaxOn implements Runnable { private Car car. TimeUnit.waitForBuffing(). } public void run() { try { while(!Thread. import static net. class Car { private boolean waxOn. public WaxOff(Car c) { car = c. // Ready to buff notify(). } public synchronized void buffed() { waxOn = false.util. } print("Ending Wax On task"). } } class WaxOff implements Runnable { private Car car. // Ready for another coat of wax notify().*. car.import java.Print. public WaxOn(Car c) { car = c. } } public class E23_WaxOMatic2 { public static void main(String[] args) throws Exception { Car car = new Car().. } } catch(InterruptedException e) { print("Exiting via interrupt"). exec.execute(new WaxOn(car)). // Run for a while. Exercise 24 //: concurrency/E24_ProducerConsumer. TimeUnit. so using notify( ) is fine.sleep(5).sleep(200).interrupted()) { car. ExecutorService exec = Executors. TimeUnit. exec. The producer must not overflow * the receiver’s buffer. // Interrupt all tasks } } /* Output: (Sample) Wax On! Wax Off! Wax On! Wax Off! Wax On! Wax Off! Wax On! Wax Off! Wax On! Wax Off! Wax On! Wax Off! Wax On! Wax Off! Wax On! Wax Off! Wax On! Wax Off! Wax On! Wax Off! Wax On! Wax Off! Wax On! Wax Off! Wax On! Wax Off! Exiting via interrupt Ending Wax Off task Exiting via interrupt Ending Wax On task *///:~ The output is equal to the previous program.newCachedThreadPool(). which uses notifyAll( ).java // {Args: 1 200} /********************** Exercise 24 *********************** * Solve a single-producer. } print("Ending Wax Off task"). printnb("Wax Off! ").buffed().SECONDS.shutdownNow(). notifyAll( ) section of TIJ4. which can happen if the producer 632 Thinking in Java. single-consumer problem using * wait() and notifyAll(). car.waitForWaxing().MILLISECONDS.execute(new WaxOff(car)). exec..try { while(!Thread. We meet the requirements of the notify( ) vs. 4th Edition Annotated Solution Guide . queue. import java.output = output. } public synchronized T get() throws InterruptedException { while(queue. maxSize++. If the consumer is faster * than the producer.concurrent. delay = sleepTime. } public void run() { for(. *********************************************************/ package concurrency. notifyAll().size() >= maxSize) wait().maxSize = maxSize.) { Concurrency 633 . } } // Produces Item objects class Producer implements Runnable { private int delay. private int id = counter++. class FlowQueue<T> { private Queue<T> queue = new LinkedList<T>().util. private FlowQueue<Item> output.*.poll(). } } class Item { private static int counter.*. maxSize--. public FlowQueue(int maxSize) { this.isEmpty()) wait()..offer(v). private int maxSize. T returnVal = queue. public String toString() { return "Item " + id. notifyAll(). import java. } public synchronized void put(T v) throws InterruptedException { while(queue.* is faster than the consumer. // A queue for solving flow-control problems. Do not assume anything about the relative * speeds of the producer or consumer.util. return returnVal. int sleepTime) { this. then it must not read the same data * more than once. public Producer(FlowQueue<Item> output. println("Usage java E24_ProducerConsumer" + " <producer sleep time> <consumer sleep time>"). ExecutorService exec = Executors.MILLISECONDS. TimeUnit. } public void run() { for(.execute(new Producer(fq. int sleepTime) { this.MILLISECONDS. private FlowQueue<?> input. consumerSleep)).parseInt(args[0]). } catch(InterruptedException e) { return. TimeUnit.println(input.sleep(delay).shutdownNow().err. exec.get()). int consumerSleep = Integer.out. producerSleep)).length < 2) { System.SECONDS. exec.execute(new Consumer(fq. } } } } // Consumes any object class Consumer implements Runnable { private int delay.parseInt(args[1]).exit(1).input = input. 4th Edition Annotated Solution Guide . public Consumer(FlowQueue<?> input. delay = sleepTime. } catch(InterruptedException e) { return.sleep(delay). } } /* Output: (Sample) Item 0 Item 1 Item 2 Item 3 634 Thinking in Java. exec. System.) { try { System. FlowQueue<Item> fq = new FlowQueue<Item>(100).put(new Item()).try { output.sleep(2). TimeUnit. } } } } public class E24_ProducerConsumer { public static void main(String[] args) throws Exception { if(args..newFixedThreadPool(2). } int producerSleep = Integer. import static net.orderNum = orderNum. } public String toString() { return "Meal " + orderNum.mindview. class Meal { private final int orderNum...util. This solution is elegant and reusable. public Meal(int orderNum) { this.java. // . for the chef to produce a meal Concurrency 635 . If the producer overfills the queue.*. For example: java E24_ProducerConsumer 200 1 Exercise 25 //: concurrency/E25_Restaurant.Print. } } class WaitPerson implements Runnable { private Restaurant restaurant. } public void run() { try { while(!Thread. *********************************************************/ package concurrency.java /********************** Exercise 25 *********************** * In the Chef class in Restaurant.interrupted()) { synchronized(this) { while(restaurant.Item 4 Item 5 Item 6 Item 7 Item 8 Item 9 Item 10 *///:~ The queue ensures that the consumer will not read a value more than once. import java.concurrent. Try the program with different production and consumption speeds.util. the call to put( ) suspends the producer’s thread until the number of elements drops below maxSize. public WaitPerson(Restaurant r) { restaurant = r.*. return from run() * after calling shutdownNow() and observe the difference * in behavior.meal == null) wait(). shutdownNow().notifyAll()..MILLISECONDS. WaitPerson waitPerson = new WaitPerson(this).newCachedThreadPool().meal = null.waitPerson. } printnb("Order up! ").chef) { restaurant. 4th Edition Annotated Solution Guide .waitPerson) { restaurant. // . synchronized(restaurant. restaurant.. } public void run() { try { while(!Thread.exec.sleep(100).interrupted()) { synchronized(this) { while(restaurant. closing"). 636 Thinking in Java.meal = new Meal(count). restaurant.meal). } } } class Restaurant { Meal meal. // Ready for another } } } catch(InterruptedException e) { print("WaitPerson interrupted").} print("Waitperson got " + restaurant. private int count = 0. Chef chef = new Chef(this). for the meal to be taken } if(++count == 10) { print("Out of food. public Chef(Restaurant r) { restaurant = r. } } } class Chef implements Runnable { private Restaurant restaurant. restaurant. } } catch(InterruptedException e) { print("Chef interrupted"). return. synchronized(restaurant.meal != null) wait().chef. } TimeUnit. ExecutorService exec = Executors.notifyAll(). import java. After the meal is * delivered. import static net.Print. } public static void main(String[] args) { new Restaurant(). exec. public WaitPerson2(Restaurant2 r) { restaurant = r. } } public class E25_Restaurant { public static void main(String[] args) { new Restaurant().util. Exercise 26 //: concurrency/E26_Restaurant2.execute(waitPerson). boolean notified. closing WaitPerson interrupted *///:~ The “Order up!” and “Chef interrupted” messages no longer appear.concurrent.execute(chef). } } /* Output: (Sample) Order up! Waitperson got Meal 1 Order up! Waitperson got Meal 2 Order up! Waitperson got Meal 3 Order up! Waitperson got Meal 4 Order up! Waitperson got Meal 5 Order up! Waitperson got Meal 6 Order up! Waitperson got Meal 7 Order up! Waitperson got Meal 8 Order up! Waitperson got Meal 9 Out of food.*.util. } Concurrency 637 .*.public Restaurant() { exec. the WaitPerson should notify the BusBoy to * clean up. since the Chef task exits immediately.java.mindview. *********************************************************/ package concurrency. class WaitPerson2 implements Runnable { private Restaurant2 restaurant.java /********************** Exercise 26 *********************** * Add a BusBoy class to Restaurant. // ..waitPerson) { restaurant. } } } catch(InterruptedException e) { print("WaitPerson interrupted"). boolean notified.notifyAll(). // Clean up } synchronized(restaurant.notifyAll().waitPerson.busBoy.chef) { restaurant. // . synchronized(restaurant.meal = null.busBoy) { restaurant.interrupted()) { synchronized(this) { while(restaurant. restaurant. for meal delivery notified = false. public BusBoy(Restaurant2 r) { restaurant = r.meal). synchronized(restaurant. restaurant.meal = restaurant.meal == null) wait(). restaurant. for the chef to produce a meal } print("Waitperson got " + restaurant.busBoy...busBoy. } public void run() { try { while(!Thread. // . // Ready for another } synchronized(this) { if(!notified) wait(). volatile Meal meal. } } } class BusBoy implements Runnable { private Restaurant2 restaurant.notified = true..meal.waitPerson..notifyAll(). for the bus boy to clean up notified = false. } print("Busboy cleaned up " + meal). restaurant.public void run() { try { while(!Thread.chef..notified = true.interrupted()) { synchronized(this) { if(!notified) wait(). } 638 Thinking in Java. 4th Edition Annotated Solution Guide . BusBoy busBoy = new BusBoy(this).waitPerson) { restaurant.MILLISECONDS.notifyAll().execute(busBoy). restaurant. ExecutorService exec = Executors. } } Concurrency 639 . } TimeUnit.sleep(100). } printnb("Order up! ").interrupted()) { synchronized(this) { while(restaurant.newCachedThreadPool(). } } catch(InterruptedException e) { print("Chef interrupted"). private int count = 0.execute(chef). } } } class Chef2 implements Runnable { private Restaurant2 restaurant. // . } } } class Restaurant2 { Meal meal.} } catch(InterruptedException e) { print("BusBoy interrupted"). public Chef2(Restaurant2 r) { restaurant = r. synchronized(restaurant.shutdownNow(). restaurant. closing"). Chef2 chef = new Chef2(this). for the meal to be taken } if(++count == 10) { print("Out of food. WaitPerson2 waitPerson = new WaitPerson2(this). } public void run() { try { while(!Thread.exec.execute(waitPerson)..meal != null) wait(). exec. public Restaurant2() { exec. exec.meal = new Meal(count)..waitPerson. *.util. It is vital that you understand how these entities communicate with each other. *********************************************************/ package concurrency. a missed signal may stall the program.public class E26_Restaurant2 { public static void main(String[] args) { new Restaurant2(). 4th Edition Annotated Solution Guide . without this flag. 640 Thinking in Java.concurrent.util. Exercise 27 //: concurrency/E27_Restaurant3. so meal preparation and clean up are simultaneous.concurrent.java to use explicit Lock and Condition * objects.*. import java.java /********************** Exercise 27 *********************** * Modify Restaurant. We use the notified field in the BusBoy and WaitPerson classes. and the various scenarios that you encounter in concurrent programs. import java.locks. closing WaitPerson interrupted BusBoy interrupted Order up! Chef interrupted *///:~ The waitperson notifies the busboy and the chef in parallel. } } /* Output: (Sample) Order up! Waitperson got Meal 1 Busboy cleaned up Meal 1 Order up! Waitperson got Meal 2 Busboy cleaned up Meal 2 Order up! Waitperson got Meal 3 Busboy cleaned up Meal 3 Order up! Waitperson got Meal 4 Busboy cleaned up Meal 4 Order up! Waitperson got Meal 5 Busboy cleaned up Meal 5 Order up! Waitperson got Meal 6 Busboy cleaned up Meal 6 Order up! Waitperson got Meal 7 Busboy cleaned up Meal 7 Order up! Waitperson got Meal 8 Busboy cleaned up Meal 8 Order up! Waitperson got Meal 9 Busboy cleaned up Meal 9 Out of food. await(). private int count. } public void run() { try { while(!Thread.mindview. try { while(restaurant.meal = null. try { restaurant.lock(). restaurant.unlock(). Lock lock = new ReentrantLock().interrupted()) { lock. } } } class Chef3 implements Runnable { private Restaurant3 restaurant.unlock(). } finally { lock.newCondition().*.unlock(). Condition condition = lock. try { while(restaurant. Concurrency 641 .newCondition(). public WaitPerson3(Restaurant3 r) { restaurant = r. restaurant.condition.signalAll().interrupted()) { lock.util.lock.lock(). } } } catch(InterruptedException e) { print("WaitPerson interrupted").Print.chef. Lock lock = new ReentrantLock().lock. } public void run() { try { while(!Thread. public Chef3(Restaurant3 r) { restaurant = r.chef.meal != null) condition. } print("Waitperson got " + restaurant.chef.lock().await().import static net. class WaitPerson3 implements Runnable { private Restaurant3 restaurant. } finally { lock. Condition condition = lock. } finally { restaurant.meal).meal == null) condition. exec.execute(chef). } } catch(InterruptedException e) { print("Chef interrupted").lock. WaitPerson3 waitPerson = new WaitPerson3(this).} if(++count == 10) { print("Out of food. } printnb("Order up! "). closing 642 Thinking in Java.lock.shutdownNow(). restaurant. ExecutorService exec = Executors. Chef3 chef = new Chef3(this).exec.execute(waitPerson).sleep(100).newCachedThreadPool().lock().MILLISECONDS. } } public class E27_Restaurant3 { public static void main(String[] args) { new Restaurant3(). 4th Edition Annotated Solution Guide .signalAll(). } } } class Restaurant3 { Meal meal. } finally { restaurant. try { restaurant.waitPerson.waitPerson. restaurant.unlock().condition. public Restaurant3() { exec. closing"). restaurant. } } /* Output: (Sample) Order up! Waitperson got Meal 1 Order up! Waitperson got Meal 2 Order up! Waitperson got Meal 3 Order up! Waitperson got Meal 4 Order up! Waitperson got Meal 5 Order up! Waitperson got Meal 6 Order up! Waitperson got Meal 7 Order up! Waitperson got Meal 8 Order up! Waitperson got Meal 9 Out of food. } TimeUnit.waitPerson.meal = new Meal(count). rocket.*.take(). } print("Exiting LiftOffRunner"). public LiftOffProducer(LiftOffRunner runner) { this. import static net. *********************************************************/ package concurrency.*. import java.util.WaitPerson interrupted Order up! Chef interrupted *///:~ Exercise 28 //: concurrency/E28_TestBlockingQueues2.java by adding a new task that * places LiftOff on the BlockingQueue.Print.*.mindview.concurrent. // Use this thread } } catch(InterruptedException e) { print("Waking from take()").io.util. } } class LiftOffProducer implements Runnable { private LiftOffRunner runner. class LiftOffRunner implements Runnable { private BlockingQueue<LiftOff> rockets. instead of doing it * in main().interrupted()) { LiftOff rocket = rockets. import java. } public void run() { try { Concurrency 643 . } public void run() { try { while(!Thread. } public void add(LiftOff lo) throws InterruptedException { rockets. public LiftOffRunner(BlockingQueue<LiftOff> queue) { rockets = queue.runner = runner.run().put(lo).java // {RunByHand} /********************** Exercise 28 *********************** * Modify TestBlockingQueues. i < 5. LiftOffRunner runner = new LiftOffRunner(queue). } } ///:~ 644 Thinking in Java. print("Finished " + msg + " test"). getkey("Press 'ENTER' (" + msg + ")"). test("ArrayBlockingQueue". } } private static void getkey(String message) { print(message).add(new LiftOff(5)). 4th Edition Annotated Solution Guide .newFixedThreadPool(2).IOException e) { throw new RuntimeException(e). print(msg).execute(runner). BlockingQueue<LiftOff> queue) { ExecutorService exec = Executors. exec. test("SynchronousQueue".io. exec.shutdownNow(). getkey(). LiftOffProducer producer = new LiftOffProducer(runner). } private static void test(String msg. } catch(InterruptedException e) { print("Waking from put()"). // Unlimited size new LinkedBlockingQueue<LiftOff>()).for(int i = 0. } public static void main(String[] args) { test("LinkedBlockingQueue".readLine(). } catch(java. exec.in)). } print("Exiting LiftOffProducer"). // Size of 1 new SynchronousQueue<LiftOff>()). } } public class E28_TestBlockingQueues2 { private static void getkey() { try { new BufferedReader( new InputStreamReader(System.execute(producer). i++) runner. // Fixed size new ArrayBlockingQueue<LiftOff>(3)). BUTTERED : Status. } public String toString() { return "Toast " + id + ": " + status. import java.*. BUTTERED. import java.DRY.READY.java to create peanut butter and jelly * on toast sandwiches using two separate assembly lines * (one for peanut butter. } public void jam() { status = (status == Status.DRY) ? Status. } public Status getStatus() { return status. import static net.*.Print.java /********************** Exercise 29 *********************** * Modify ToastOMatic.mindview. READY { public String toString() { return BUTTERED. private final int id. the second for jelly. then * merging the two lines). } } } private Status status = Status. } public int getId() { return id. } } Concurrency 645 .toString() + " & " + JAMMED.DRY) ? Status. } public void butter() { status = (status == Status.READY. class Toast { public enum Status { DRY.toString().*.concurrent.util.util.Exercise 29 //: concurrency/E29_ToastOMatic2. *********************************************************/ package concurrency. JAMMED. public Toast(int idn) { id = idn.util.JAMMED : Status. } public void run() { try { while(!Thread. } } catch(InterruptedException e) { print("Butterer interrupted"). ToastQueue buttered) { inQueue = in. } public void run() { try { while(!Thread. print(t). private int count. print(t).interrupted()) { TimeUnit. } } // Apply butter to toast: class Butterer implements Runnable { private ToastQueue inQueue. butteredQueue = buttered.nextInt(500)).take(). butteredQueue. } } catch(InterruptedException e) { print("Toaster interrupted").put(t). butteredQueue.put(t).MILLISECONDS. // Make toast Toast t = new Toast(count++). } print("Butterer off"). } print("Toaster off"). 4th Edition Annotated Solution Guide . // Insert into queue toastQueue. public Butterer(ToastQueue in.butter(). public Toaster(ToastQueue tq) { toastQueue = tq.class ToastQueue extends LinkedBlockingQueue<Toast> {} class Toaster implements Runnable { private ToastQueue toastQueue. } } 646 Thinking in Java. private Random rand = new Random(47).sleep( 100 + rand. t.interrupted()) { // Blocks until next piece of toast is available: Toast t = inQueue. } } catch(InterruptedException e) { print("Jammer interrupted"). ToastQueue jammed) { inQueue = in. print(t). } else print("Chomp! " + t). } print("Jammer off"). // Verify that all pieces are ready for consumption: if(t. System.Status. } } // Consume the toast: class Eater implements Runnable { private ToastQueue finishedQueue. } print("Eater off"). } public void run() { try { while(!Thread.READY) { print(">>>> Error: " + t). public Eater(ToastQueue finished) { finishedQueue = finished. jammedQueue.put(t).exit(1). public Jammer(ToastQueue in. } Concurrency 647 . jammedQueue = jammed.take().getStatus() != Toast.jam().// Apply jam to toast: class Jammer implements Runnable { private ToastQueue inQueue. jammedQueue.take().interrupted()) { // Blocks until next piece of toast is available: Toast t = inQueue. t. } public void run() { try { while(!Thread. } } catch(InterruptedException e) { print("Eater interrupted").interrupted()) { // Blocks until next piece of toast is available: Toast t = finishedQueue. put(t). // change state for next time } } catch(InterruptedException e) { print("Alternator interrupted").put(t). } public void run() { try { while(!Thread. finishedQueue = finished. ToastQueue out1. public Merger(ToastQueue in1. else out2Queue. } print("Alternator off"). out2Queue = out2. out1Queue. out2Queue. ToastQueue finished) { in1Queue = in1. in2Queue. finishedQueue. ToastQueue toBeJammed. outTo2 = !outTo2. } public void run() { try { while(!Thread. // control alternation public Alternator(ToastQueue in. out1Queue = out1. 4th Edition Annotated Solution Guide .interrupted()) { // Blocks until next piece of toast is available: Toast t = inQueue. in2Queue = in2. } } // Accepts toasts on either channel.} // Outputs alternate inputs on alternate channels: class Alternator implements Runnable { private ToastQueue inQueue. toBeButteredQueue. private boolean outTo2. ToastQueue out2) { inQueue = in. if(!outTo2) out1Queue. toBeButteredQueue = toBeButtered. ToastQueue toBeButtered. toBeJammedQueue = toBeJammed. toBeJammedQueue. ToastQueue in2. and relays them on to // a "single" successor class Merger implements Runnable { private ToastQueue in1Queue.take().interrupted()) { 648 Thinking in Java. execute(new Alternator(dryQueue. } } public class E29_ToastOMatic2 { public static void main(String[] args) throws Exception { ToastQueue dryQueue = new ToastQueue(). case JAMMED: toBeButteredQueue. exec. finishedQueue = new ToastQueue().// Blocks until next piece of toast is available: Toast t = null. finishedQueue)). toBeJammedQueue)). TimeUnit. } } } catch(InterruptedException e) { print("Merger interrupted"). butteredQueue = new ToastQueue(). default: finishedQueue.execute(new Toaster(dryQueue)). } print("Merger off"). toBeJammedQueue = new ToastQueue().put(t). break. toBeButteredQueue.MILLISECONDS). t = in2Queue. while(t == null) { t = in1Queue.execute( new Jammer(toBeJammedQueue.execute(new Merger(butteredQueue .put(t).put(t). exec. butteredQueue)). jammedQueue.MILLISECONDS). toBeButteredQueue.newCachedThreadPool(). jammedQueue = new ToastQueue(). Concurrency 649 . exec. break.poll(50.poll(50. toBeJammedQueue. if(t != null) break. exec.getStatus()) { case BUTTERED: toBeJammedQueue. exec. exec. TimeUnit.execute(new Eater(finishedQueue)). } // Relay toast onto the proper queue switch(t. jammedQueue)). ExecutorService exec = Executors. toBeButteredQueue = new ToastQueue().execute( new Butterer(toBeButteredQueue. SECONDS.sleep(rand. Jammer and Eater remain the same.html).Print. so blocking a specific one indefinitely doesn’t work.util. The timed poll( ) method from the JDK needs no busy-wait loop.util.java /********************** Exercise 30 *********************** * Modify PipedIO. public CharQueue getQueue() { return out.*.util.” We must poll each queue in the program with a wait time of 50 milliseconds (inside the Merger task). 4th Edition Annotated Solution Guide . c++) { out.put(c).oswego. *********************************************************/ package concurrency. } public void run() { try { while(true) for(char c = 'A'. TimeUnit. 650 Thinking in Java. } } /* (Execute to see output) *///:~ The finished sandwiches no longer come in predefined order because the two assembly lines work in parallel. Our solution enhances the original program from TIJ4 with an assembly line elaboration by Doug Lea (http://gee.cs. class CharQueue extends LinkedBlockingQueue<Character> {} class Sender implements Runnable { private Random rand = new Random(47). private CharQueue out = new CharQueue().MILLISECONDS. Toaster.concurrent. import java. import java.shutdownNow(). exec. Lea writes: “Polling intrinsically relies on busy-wait loops.edu/dl/cpj/assembly.java to use a BlockingQueue instead of * a pipe.*. Butterer.TimeUnit. or alternative actions to strike a balance between conserving CPU time and maintaining acceptable average response latencies. Coping with this requires empirically guided decisions about how to insert sleeps. Exercise 30 //: concurrency/E30_SendReceive. We add only Alternator and Merger. yields. import static net. c <= 'z'. which are intrinsically wasteful (but still sometimes less so than context-switching).*.nextInt(500)).sleep(5). because data could come from any queue.mindview. Receiver receiver = new Receiver(sender). ").getQueue(). ExecutorService exec = Executors. Read: N. } } /* Output: (Sample) Read: A. Read: F.shutdownNow(). Read: K.} } catch(InterruptedException e) { print(e + " Sender interrupted"). } } } public class E30_SendReceive { public static void main(String[] args) throws Exception { Sender sender = new Sender(). } } } class Receiver implements Runnable { private CharQueue in. and setting up the communication channel is very straightforward. } public void run() { try { while(true) { // Blocks until characters are there: printnb("Read: " + in. } } catch(InterruptedException e) { print(e + " Reader interrupted").execute(sender). Read: L. Read: M.InterruptedException Reader interrupted java. Read: G. you do not have to catch or handle IOExceptions.execute(receiver). public Receiver(Sender sender) { in = sender. Read: J. TimeUnit. Read: Q. exec.InterruptedException: sleep interrupted Sender interrupted *///:~ Note that the BlockingQueues are more robust and easier to use.lang. Read: C.take() + ". exec. Read: H. exec. java. Read: B.sleep(4).lang. Read: D. Read: I. Read: P.newCachedThreadPool(). Concurrency 651 . Read: O.SECONDS. Read: E. take().util.Exercise 31 //: concurrency/E31_DiningPhilosophers2. private boolean taken. notifyAll().java // {Args: 5 0 deadlock 5} /******************** Exercise 31 ************************ * Change DeadlockingDiningPhilosophers.java so that when a * philosopher is done with their chopsticks. taken = true. they take * the next two available chopsticks from the bin. When a philosopher wants to eat. public Chopstick(int ident) { id = ident.put(stick). import java. class Chopstick { private final int id. 4th Edition Annotated Solution Guide . 652 Thinking in Java. } public synchronized void take() throws InterruptedException { while(taken) wait().Print.util. } public void put(Chopstick stick) throws InterruptedException { bin. they drop them * into a bin. public Chopstick get() throws InterruptedException { return bin. Does this * eliminate the possibility of deadlock? Can you * reintroduce deadlock by simply reducing the number of * available chopsticks? *********************************************************/ package concurrency.*. } public synchronized void drop() { taken = false.*. import static net.mindview.*.concurrent. } public String toString() { return "Chopstick " + id. import java.util. } } class ChopstickQueue extends LinkedBlockingQueue<Chopstick>{} class ChopstickBin { private ChopstickQueue bin = new ChopstickQueue(). nextInt(ponderFactor * 250)). } } catch(InterruptedException e) { print(this + " " + "exiting via interrupt"). } public Philosopher(ChopstickBin bin. print(this + " has " + c2). } } public class E31_DiningPhilosophers2 { public static void main(String[] args) throws Exception { if(args. // Put the chopsticks back in bin.get(). private ChopstickBin bin. bin.sleep( rand. } } public String toString() { return "Philosopher " + id. // Get one chopstick from the bin Chopstick c1 = bin. pause(). // Get another chopstick from bin Chopstick c2 = bin.} } class Philosopher implements Runnable { private static Random rand = new Random(47). int ponder) { this. pause().bin = bin. id = ident. ponderFactor = ponder.put(c1).put(c2). int ident.interrupted()) { print(this + " " + "thinking"). bin.get().MILLISECONDS. private final int ponderFactor. print(this + " has " + c1 + " waiting for another one").length < 3) { Concurrency 653 . } public void run() { try { while(!Thread. TimeUnit. private final int id. print(this + " eating"). private void pause() throws InterruptedException { if(ponderFactor == 0) return. SECONDS.put(new Chopstick(i)).println("usage:\n" + "java E31_DiningPhilosophers2 " + "numberOfPhilosophers ponderFactor deadlock " + "timeout\n" + "A nonzero ponderFactor will " + "generate a random sleep time during think().in. i++) bin.sleep(Integer. i++) exec.newCachedThreadPool(). } } /* Output: (Sample) Philosopher 1 thinking Philosopher 1 has Chopstick Philosopher 1 has Chopstick Philosopher 1 eating Philosopher 1 thinking .. the program will not deadlock. i < size. } exec.\n" + "If deadlock is not the string " + "'deadlock'. System. i < size.exit(1).\n" + "A nonzero timeout will stop the program after " + "that number of seconds.read().parseInt(args[3])). i. } ChopstickBin bin = new ChopstickBin().shutdownNow().System.parseInt(args[1]).equals("deadlock")) bin. 4th Edition Annotated Solution Guide . ponder)).. int ponder = Integer. // One additional chopstick guarantees that at least // one philosopher can eat without blocking.").execute(new Philosopher(bin. if(!args[2].err. else { print("Press 'ENTER' to quit"). for(int i = 0. for(int i = 0. if(args.length == 4) TimeUnit. int size = Integer.parseInt(args[0]). ExecutorService exec = Executors. Philosopher 2 has Chopstick Philosopher 2 has Chopstick Philosopher 2 eating Philosopher 2 thinking Philosopher 2 has Chopstick Philosopher 2 has Chopstick Philosopher 2 eating Philosopher 2 thinking Philosopher 2 has Chopstick 0 waiting for another one 1 1 waiting for another one 2 1 waiting for another one 2 1 waiting for another one 654 Thinking in Java.put(new Chopstick(size)). System. import java.java /******************** Exercise 32 ************************ * Use a CountDownLatch to solve the problem of correlating * the results from the Entrances in OrnamentalGarden. import static net.mindview. private static List<Entrance3> entrances = new ArrayList<Entrance3>().java. private final int id.*. import java. but a single extra chopstick prevents this.util. Concurrency 655 . * Remove the unnecessary code from the new version of the * example. private int number.util. private static volatile boolean canceled.Print.*.concurrent. private static Count count = new Count(). Instead of picking up the left and right chopsticks.util.*. class Entrance3 implements Runnable { private final CountDownLatch latch. philosophers take them from a common bin. *********************************************************/ package concurrency.Philosopher Philosopher Philosopher Philosopher Philosopher Philosopher Philosopher Philosopher Philosopher Philosopher Philosopher Philosopher Philosopher Philosopher *///:~ 2 2 2 2 2 2 4 2 2 3 0 1 4 2 has Chopstick 2 eating thinking has Chopstick 1 waiting for another one has Chopstick 2 eating has Chopstick 1 waiting for another one thinking has Chopstick 2 waiting for another one exiting via interrupt exiting via interrupt exiting via interrupt exiting via interrupt exiting via interrupt We use ChopstickQueue to create the ChopstickBin. The results are the same. so it gains all of LinkedBlockingQueue’s control logic. an activity we suspend when too few chopsticks remain. Exercise 32 //: concurrency/E32_OrnamentalGarden3. with the flaw that an equal number of chopsticks and philosophers can quickly deadlock. print("Sum of Entrances: " + Entrance3. Entrance3. } } public class E32_OrnamentalGarden3 { public static void main(String[] args) throws Exception { // All must share a single CountDownLatch object: CountDownLatch latch = new CountDownLatch(5). print("Stopping " + this). try { TimeUnit.value(). i++) exec. entrances.sleep(3). this. i < 5. } print(this + " Total: " + count. // Wait for results print("Total: " + Entrance3.add(this).SECONDS.sleep(100).newCachedThreadPool(). } public String toString() { return "Entrance " + id + ": " + getValue(). } public static int sumEntrances() { int sum = 0. for(int i = 0.cancel(). } } latch.getValue(). latch.MILLISECONDS. i)). int id) { latch = ltc. for(Entrance3 entrance : entrances) sum += entrance. TimeUnit. } public static int getTotalCount() { return count.getTotalCount()). } catch(InterruptedException e) { print("sleep interrupted"). 4th Edition Annotated Solution Guide . } public void run() { while(!canceled) { synchronized(this) { ++number. } public synchronized int getValue() { return number.execute(new Entrance3(latch.public static void cancel() { canceled = true.sumEntrances()). ExecutorService exec = Executors.await(). 656 Thinking in Java.id = id. } public Entrance3(CountDownLatch ltc.countDown(). return sum.shutdown().increment()). exec. concurrent. Delayed { protected final long delayTime. import java. Terminating Entrance 2: Terminating Entrance 0: Terminating Entrance 3: Terminating Entrance 4: Terminating Entrance 1: Total: 150 Sum of Entrances: 150 *///:~ 30 30 30 30 30 CountDownLatch shortens and clarifies the code considerably.concurrent.util.util.java // {Args: 5000} /******************** Exercise 33 ************************ * Modify GreenhouseScheduler. Exercise 33 //: concurrency/E33_GreenhouseController. abstract class Event implements Runnable. *********************************************************/ package concurrency.} } /* Output: (Sample) Entrance 1: 1 Total: 2 Entrance 2: 1 Total: 3 Entrance 3: 1 Total: 4 Entrance 4: 1 Total: 5 Entrance 0: 1 Total: 1 Entrance 1: 2 Total: 6 Entrance 2: 2 Total: 7 Entrance 3: 2 Total: 8 Entrance 4: 2 Total: 9 Entrance 0: 2 Total: 10 Entrance 1: 3 Total: 11 Entrance 2: 3 Total: 12 . import static net.*.mindview.*. import static java.Print..util.delayTime = delayTime. private long trigger. } public long getDelay(TimeUnit unit) { Concurrency 657 .TimeUnit.java to use a * DelayQueue instead of a ScheduledExecutor.*. public Event(long delayTime) { this.. convert(delayTime. } public void run() { try { while(!Thread. event.System.nanoTime().convert( trigger . if(trigger < that. public class LightOn extends Event { public LightOn(long delayTime) { super(delayTime). } public void run() { 658 Thinking in Java.start().interrupted()) { Event event = q.trigger) return -1.trigger) return 1.run(). q. } } catch(InterruptedException e) { // Acceptable way to exit } print("Finished Controller"). 4th Edition Annotated Solution Guide . if(trigger > that. } class Controller implements Runnable { private DelayQueue<Event> q. } } class GreenhouseControls extends Controller { public GreenhouseControls(DelayQueue<Event> q) { super(q). MILLISECONDS). } public int compareTo(Delayed arg) { Event that = (Event)arg. return 0.nanoTime() + NANOSECONDS. public Controller(DelayQueue<Event> q) { this. } public void addEvent(Event c) { c. print(event).q = q. } private boolean light. NANOSECONDS). } public void start() { // Allows restarting trigger = System. } public abstract void run().put(c).return unit.take(). } public void run() { // Put hardware control code here. } public String toString() { return "Greenhouse water is on". public class ThermostatNight extends Event { public ThermostatNight(long delayTime) { super(delayTime). light = true.// Put hardware control code here to // physically turn on the light. } public String toString() { return "Light is on". } } public class WaterOff extends Event { public WaterOff(long delayTime) { super(delayTime). } public void run() { // Put hardware control code here to // physically turn off the light. } public String toString() { return "Light is off". light = false. } public void run() { // Put hardware control code here. } } private boolean water. water = false. Concurrency 659 . thermostat = "Night". } public void run() { // Put hardware control code here. public class WaterOn extends Event { public WaterOn(long delayTime) { super(delayTime). } public String toString() { return "Thermostat on night setting". } } private String thermostat = "Day". } } public class LightOff extends Event { public LightOff(long delayTime) { super(delayTime). } public String toString() { return "Greenhouse water is off". water = true. } public void run() { // Put hardware control code here. Event[] eventList) { super(delayTime). } public void run() { for(Event e : eventList) { addEvent(e). } } public static class Terminate extends Event { private ExecutorService exec. this. } addEvent(this). } 660 Thinking in Java. public Terminate(long delayTime. } public String toString() { return "Bing!". exec = e.} } public class ThermostatDay extends Event { public ThermostatDay(long delayTime) { super(delayTime). } public String toString() { return "Restarting system". ExecutorService e) { super(delayTime). thermostat = "Day". } public void run() { addEvent(new Bell(delayTime)). for(Event e : eventList) addEvent(e). public Restart(long delayTime. } public String toString() { return "Thermostat on day setting". } } public class Restart extends Event { private Event[] eventList.eventList = eventList. 4th Edition Annotated Solution Guide . } } // An example of an action() that inserts a // new one of itself into the event list: public class Bell extends Event { public Bell(long delayTime) { super(delayTime). addEvent(gc. Event[] eventList = { gc.length == 1) gc.addEvent( new GreenhouseControls. } } /* Output: Thermostat on night setting Light is on Light is off Greenhouse water is on Greenhouse water is off Bing! Thermostat on day setting Bing! Restarting system Thermostat on night setting Light is on Light is off Greenhouse water is on Bing! Greenhouse water is off Thermostat on day setting Bing! Restarting system Thermostat on night setting Light is on Concurrency 661 .new ThermostatNight(0).new ThermostatDay(1400) }. gc.new LightOff(400).addEvent(gc.new WaterOff(800). eventList)). gc. } public String toString() { return "Terminating". gc. gc.execute(gc).public void run() { exec. DelayQueue<Event> queue = new DelayQueue<Event>().newCachedThreadPool().Terminate( new Integer(args[0]).new Bell(900)). gc. GreenhouseControls gc = new GreenhouseControls(queue).new Restart(2000. exec)). gc. } } } public class E33_GreenhouseController { public static void main(String[] args) { ExecutorService exec = Executors.new LightOn(200). if(args. gc.shutdownNow().new WaterOn(600). exec. next()).interrupted()) { for(int i = 0.java from TIJ4.Light is off Bing! Greenhouse water is on Greenhouse water is off Terminating Finished Controller *///:~ This program combines concurrency/DelayQueueDemo.size. this.util.*. Using DelayQueue instead of an ordinary ArrayList cleans up the code a bit.java /******************** Exercise 34 ************************ * Modify ExchangerDemo. i++) holder.holder = holder.concurrent. import java. } } catch(InterruptedException e) { // OK to terminate this way. List<T> holder) { exchanger = exchg. Exercise 34 //: concurrency/E34_ExchangerDemo2. ExchangerProducer(Exchanger<List<T>> exchg. 4th Edition Annotated Solution Guide . import net.exchange(holder). *********************************************************/ package concurrency. i < E34_ExchangerDemo2. } } 662 Thinking in Java.util. // Exchange full for empty: holder = exchanger.java and innerclasses/GreenhouseController.add(generator.util. private Exchanger<List<T>> exchanger. class ExchangerProducer<T> implements Runnable { private Generator<T> generator. } public void run() { try { while(!Thread. private List<T> holder. Generator<T> gen.*.*. import java.java to use your own class instead * of Fat. generator = gen.mindview. private List<T> holder.sleep(delay).SECONDS. // OK for CopyOnWriteArrayList } } } catch(InterruptedException e) { // OK to terminate this way. consumerList)). Exchanger<List<Integer>> xc = new Exchanger<List<Integer>>(). exec.out. // Seconds public static void main(String[] args) throws Exception { if(args.println("Final value: " + value).shutdownNow(). } public void run() { try { while(!Thread. for(T x : holder) { value = x.holder = holder. exec.interrupted()) { holder = exchanger. } } public class E34_ExchangerDemo2 { static int size = 10.Integer(). producerList)). static int delay = 5. new CountingGenerator.newCachedThreadPool().exchange(holder). if(args. consumerList = new CopyOnWriteArrayList<Integer>(). // Fetch out value holder.length > 0) size = new Integer(args[0]). exec.execute( new ExchangerConsumer<Integer>(xc.} class ExchangerConsumer<T> implements Runnable { private Exchanger<List<T>> exchanger. List<T> holder){ exchanger = ex. private volatile T value.remove(x). } Concurrency 663 . ExchangerConsumer(Exchanger<List<T>> ex.execute(new ExchangerProducer<Integer>(xc. } System.length > 1) delay = new Integer(args[1]). this. TimeUnit. List<Integer> producerList = new CopyOnWriteArrayList<Integer>(). ExecutorService exec = Executors. toString(). public WebClient(int tm) { serviceTime = tm. for(WebClient client : this) result.append(client). } public int getServiceTime() { return serviceTime. and use CountingGenerator. Exercise 35 //: concurrency/E35_WebClientServerSimulation.*. StringBuilder result = new StringBuilder(). * The goal is to determine the load that the group of * servers can handle.concurrent.Integer to generate numbers.size() == 0) return "[Empty]". } } class WebClientLine extends ArrayBlockingQueue<WebClient> { public WebClientLine(int maxLineSize) { super(maxLineSize).util.java // {Args: 8} /******************** Exercise 35 ************************ * Modify BankTellerSimulation.*.util. *********************************************************/ package concurrency.java so that it represents * Web clients making requests of a fixed number of servers. import java. import java.mindview. } public String toString() { if(this. return result. 4th Edition Annotated Solution Guide . class WebClient { private final int serviceTime.util.Print.*. import static net. } public String toString() { return "[" + serviceTime + "]". } } class WebClientGenerator implements Runnable { 664 Thinking in Java.} /* Output: (Sample) Final value: 2903359 *///:~ We replace Fat with Integer. getServiceTime()).interrupted()) { WebClient client = clients.interrupted()) { clients. } } catch(InterruptedException e) { print("WebClientGenerator interrupted").private WebClientLine clients. } print(this + "terminating"). private int adjustmentPeriod. private WebClientLine clients. volatile int loadFactor = 1. public WebClientGenerator(WebClientLine cq) { clients = cq. TimeUnit.nextInt(1000))).put(new WebClient(rand. public Server(WebClientLine cq) { clients = cq. // Start with one client/sec private static Random rand = new Random(47).MILLISECONDS. Concurrency 665 . private Queue<Server> servers = new LinkedList<Server>(). } public String toString() { return "Server " + id + " ". } public void run() { try { while(!Thread. } public String shortString() { return "T" + id.sleep( client.sleep(1000 / loadFactor). } } class Server implements Runnable { private static int counter. private WebClientLine clients. } print("WebClientGenerator terminating").MILLISECONDS. TimeUnit. } } class SimulationManager implements Runnable { private ExecutorService exec. } } catch(InterruptedException e) { print(this + "interrupted").take(). private WebClientGenerator gen. private final int id = counter++. } public void run() { try { while(!Thread. shutdownNow(). WebClientGenerator gen. adjustLoadFactor().add(server). servers. } public void run() { try { while(!Thread.loadFactor). } } else { print("New load factor: " + ++gen.execute(server). } } public void adjustLoadFactor() { // This is actually a control system. 666 Thinking in Java.MILLISECONDS. // If line is stable. print("}"). exec. else if(!stable) { // Not stable for a second time print("Peak load factor: ~" + gen. WebClientLine clients.clients = clients.shortString() + " "). stable = true.print(clients + " { "). } } catch(InterruptedException e) { print(this + "interrupted"). int adjustmentPeriod. you can reveal stability issues in // the control mechanism. } prevSize = clients.// Indicates whether the queue is stable private boolean stable = true. System.interrupted()) { TimeUnit.size(). this.out. private int prevSize. exec. i < n. this. for(Server server : servers) printnb(server.adjustmentPeriod = adjustmentPeriod.size() > prevSize) { if(stable) // Was stable previous time stable = false. i++) { Server server = new Server(clients). public SimulationManager(ExecutorService e. By adjusting // the numbers.gen = gen. this. 4th Edition Annotated Solution Guide .sleep(adjustmentPeriod). // Start with 'n' servers: for(int i= 0. int n) { exec = e.loadFactor). increase the 'load factor': if(clients. in. System.shutdownNow(). else { System.} System. exec.execute(new SimulationManager( exec.sleep(new Integer(args[0])). if(args.println("Press 'ENTER' to quit"). exec.out. clients.println(this + "terminating"). ADJUSTMENT_PERIOD. g. static final int ADJUSTMENT_PERIOD = 1000.length > 0) // Optional argument TimeUnit. } } public class E35_WebClientServerSimulation { static final int MAX_LINE_SIZE = 50. WebClientLine clients = new WebClientLine(MAX_LINE_SIZE). } exec. } } /* Output: (Sample) [Empty] { T0 T1 T2 } New load factor: 2 [Empty] { T0 T1 T2 } New load factor: 3 [Empty] { T0 T1 T2 } New load factor: 4 [Empty] { T0 T1 T2 } New load factor: 5 [Empty] { T0 T1 T2 } New load factor: 6 [258] { T0 T1 T2 } [704][383] { T0 T1 T2 } Peak load factor: ~6 Server 2 interrupted Server 2 terminating SimulationManager terminating Server 1 interrupted Server 0 interrupted WebClientGenerator interrupted Server 1 terminating Concurrency 667 .newCachedThreadPool().read().out. static final int NUM_OF_SERVERS = 3.execute(g). NUM_OF_SERVERS)). public static void main(String[] args) throws Exception { ExecutorService exec = Executors. } public String toString() { return "SimulationManager ".SECONDS. WebClientGenerator g = new WebClientGenerator(clients). import java.Print. but instead of lacking a predefined number of tellers.util.*.util. Exercise 36 //: concurrency/restaurant2/E36_RestaurantWithQueues2. We set the load (clients per second) by increasing the loadFactor until the number of clients in the queue exceeds our chosen limit.Server 0 terminating WebClientGenerator terminating *///:~ This is like an inversion of BankTellerSimulation.*. However. private final int id = counter++. private final List<Order> orders = Collections. private final Table table.add(order). The inference engine is simple: if the client line is growing then the program has reached peak loadFactor. } public void placeOrder(Customer cust. with multiple * Customers per table.menu.restaurant2.setOrderTicket(this). 4th Edition Annotated Solution Guide .*.util. and there's one ticket // per table: class OrderTicket { private static int counter. the number of servers is fixed. import static net.*.mindview. public OrderTicket(Table table) { this. 668 Thinking in Java. Food food) { Order order = new Order(cust.java // {Args: 5} /******************** Exercise 36 ************************ * Modify RestaurantWithQueues. import enumerated. you must run the simulation for awhile to find this loadFactor. and add a Table class.concurrent. food).java.getWaitPerson(). // This is consisted of many orders. order.java so there’s one * OrderTicket object per table. } public WaitPerson getWaitPerson() { return table. Try changing parameters like serviceTime of clients to simulate different scenarios.table = table. import java. *********************************************************/ package concurrency. orders.synchronizedList(new LinkedList<Order>()). Change order to * orderTicket. i < nCustomers.add(customer = new Customer(this. exec. } public void run() { Customer customer. } }). } public void placeOrder(Customer cust. private final ExecutorService exec.toString()).toString(). this. } } class Table implements Runnable { private static int counter. private final CyclicBarrier barrier. food). new Runnable() { public void run() { print(orderTicket. private final OrderTicket orderTicket = new OrderTicket(this). customers = Collections. private final int id = counter++.length() . private final List<Customer> customers. ExecutorService e) { this.substring(0. sb. i++) { customers. synchronized(orders) { for(Order order : orders) sb. } // Prune away the last added 'new-line' character return sb.1). exec = e. Concurrency 669 . public Table(WaitPerson waitPerson. barrier)).append(order. for(int i = 0. int nCustomers. barrier = new CyclicBarrier(nCustomers + 1.execute(customer). } public WaitPerson getWaitPerson() { return waitPerson.} public List<Order> getOrders() { return orders.waitPerson = waitPerson.nCustomers = nCustomers.placeOrder(cust. private final int nCustomers.synchronizedList( new LinkedList<Customer>()).toString() + "\n"). private final WaitPerson waitPerson. } public String toString() { StringBuilder sb = new StringBuilder( "Order Ticket: " + id + " for: " + table + "\n"). Food food) { orderTicket. } try { barrier. Food f) { customer = cust.placeOrderTicket(orderTicket). synchronized(customers) { for(Customer customer : customers) sb.append(customer.toString(). } waitPerson. } public Customer getCustomer() { return customer. } } 670 Thinking in Java. private final Food food. } } void setOrderTicket(OrderTicket orderTicket) { this. } public String toString() { return "Order: " + id + " item: " + food + " for: " + customer. 4th Edition Annotated Solution Guide .await(). private volatile OrderTicket orderTicket.substring(0. food = f.1). public Order(Customer cust. private final int id. return. private final Customer customer. } catch(BrokenBarrierException e) { throw new RuntimeException(e). } } // This is part of an order ticket (given to the chef): class Order { private static int counter. } public String toString() { StringBuilder sb = new StringBuilder( "Table: " + id + " served by: " + waitPerson + "\n"). } catch(InterruptedException ie) { print(this + " interrupted"). sb.length() .class) { id = counter++.toString() + "\n"). synchronized(Order.orderTicket = orderTicket. } return sb. } public Food item() { return food. } public OrderTicket getOrderTicket() { return orderTicket. values()) { Food food = course.table = table.class) { id = counter++. this.toString().await(). synchronized(Customer. } catch(BrokenBarrierException e) { Concurrency 671 . } public Order getOrder() { return order. table. } try { barrier. } public Food getFood() { return food. // Number of plates ordered public Customer(Table table. food). public void deliver(Plate p) throws InterruptedException { // Only blocks if customer is still // eating the previous course: placeSetting.randomSelection(). Food f) { order = ord. ++nPlates. public Plate(Order ord. } } // Only one course at a time can be received: private final SynchronousQueue<Plate> placeSetting = new SynchronousQueue<Plate>(). return. private int nPlates.barrier = barrier.put(p). } public String toString() { return food. private final int id. private final Food food. CyclicBarrier barrier) { this.placeOrder(this. } } class Customer implements Runnable { private static int counter. private final Table table. food = f. private final CyclicBarrier barrier. } public void run() { // First place an order: for(Course course : Course. } catch(InterruptedException ie) { print(this + " interrupted while ordering meal").// This is what comes back from the chef: class Plate { private final Order order. getCustomer()). } } public void run() { try { while(!Thread. plate.interrupted()) { // Blocks until a course is ready Plate plate = filledOrders.getOrder(). i++) try { // Blocks until course has been delivered: print(this + "eating " + placeSetting.deliver(plate). private final Restaurant restaurant.take(). 4th Edition Annotated Solution Guide . } public void placeOrderTicket(OrderTicket orderTicket) { try { // Shouldn't actually block because this is // a LinkedBlockingQueue with no size limit: restaurant. } print(this + "finished meal. } public String toString() { return "Customer " + id + " ". return.orderTickets. } } catch(InterruptedException e) { print(this + " interrupted").getOrder().getCustomer(). private final int id = counter++. print(this + "received " + plate + " delivering to " + plate. public WaitPerson(Restaurant rest) { restaurant = rest. final BlockingQueue<Plate> filledOrders = new LinkedBlockingQueue<Plate>().throw new RuntimeException(e). i < nPlates. } catch(InterruptedException e) { print(this + "waiting for meal interrupted").put(orderTicket). } 672 Thinking in Java. leaving").take()). } } class WaitPerson implements Runnable { private static int counter. } catch(InterruptedException e) { print(this + " placeOrderTicket interrupted"). } // Now wait for each ordered plate: for(int i = 0. } } class Restaurant implements Runnable { private List<WaitPerson> waitPersons = new ArrayList<WaitPerson>().getOrderTicket(). private ExecutorService exec. filledOrders. } } class Chef implements Runnable { private static int counter.nextInt(500)). Plate plate = new Plate(order. List<Order> orders = orderTicket.put(plate). final BlockingQueue<OrderTicket> orderTickets = new LinkedBlockingQueue<OrderTicket>(). } public void run() { try { while(!Thread. private static Random rand = new Random(47). private List<Chef> chefs = new ArrayList<Chef>().item().interrupted()) { // Blocks until an order ticket appears: OrderTicket orderTicket = restaurant. private final Restaurant restaurant.getOrders().orderTickets. synchronized(orders) { for(Order order : orders) { Food requestedItem = order. Concurrency 673 .getWaitPerson().MILLISECONDS.take(). // Time to prepare order: TimeUnit. private final int id = counter++. } public String toString() { return "WaitPerson " + id + " ". public Chef(Restaurant rest) { restaurant = rest. } public String toString() { return "Chef " + id + " ".print(this + " off duty"). requestedItem).sleep(rand. order. } print(this + " off duty"). } } } } catch(InterruptedException e) { print(this + " interrupted"). private static Random rand = new Random(47). } exec.nextInt(waitPersons. nCustomers. exec. exec.length > 0) // Optional argument TimeUnit.execute(chef). } } catch(InterruptedException e) { print("Restaurant interrupted").add(waitPerson). chefs.interrupted()) { // A new group of customers arrive.get( rand. 5.shutdownNow(). int nCustomers = rand.nextInt(4) + 1.MILLISECONDS. Restaurant restaurant = new Restaurant(exec.size())). waitPersons.sleep(400 * nCustomers). 2).SECONDS.add(chef). } } public void run() { try { while(!Thread. 4th Edition Annotated Solution Guide .sleep(new Integer(args[0])). i < nWaitPersons. int nChefs) { exec = e. i++) { WaitPerson waitPerson = new WaitPerson(this).read(). i++) { Chef chef = new Chef(this). TimeUnit. System. assign a // WaitPerson: WaitPerson wp = waitPersons. } print("Restaurant closing"). } } public class E36_RestaurantWithQueues2 { public static void main(String[] args) throws Exception { ExecutorService exec = Executors. exec. for(int i = 0. exec.execute(t). int nWaitPersons. if(args. } } /* Output: (Sample) 674 Thinking in Java.in. i < nChefs.execute(restaurant). } for(int i = 0.execute(waitPerson). Table t = new Table(wp. else { print("Press 'ENTER' to quit"). exec).public Restaurant(ExecutorService e.newCachedThreadPool(). . Customer 12 waiting for meal interrupted WaitPerson 3 interrupted Customer 9 waiting for meal interrupted Customer 13 waiting for meal interrupted WaitPerson 3 off duty WaitPerson 1 interrupted Customer 8 waiting for meal interrupted WaitPerson 2 interrupted Customer 7 waiting for meal interrupted WaitPerson 0 interrupted Restaurant interrupted WaitPerson 1 off duty WaitPerson 2 off duty Chef 0 interrupted Customer 14 waiting for meal interrupted Customer 10 waiting for meal interrupted WaitPerson 0 off duty WaitPerson 4 interrupted Restaurant closing Chef 0 off duty WaitPerson 4 off duty Concurrency 675 ..Order Ticket: 0 for: Table: 0 served by: WaitPerson 3 Customer 0 Customer 1 Order: 0 item: SPRING_ROLLS for: Customer 1 Order: 1 item: SPRING_ROLLS for: Customer 0 Order: 2 item: VINDALOO for: Customer 0 Order: 3 item: BURRITO for: Customer 1 Order: 4 item: GELATO for: Customer 0 Order: 5 item: CREME_CARAMEL for: Customer 1 Order: 6 item: BLACK_COFFEE for: Customer 0 Order: 7 item: TEA for: Customer 1 WaitPerson 3 received SPRING_ROLLS delivering to Customer 1 Customer 1 eating SPRING_ROLLS WaitPerson 3 received SPRING_ROLLS delivering to Customer 0 Customer 0 eating SPRING_ROLLS WaitPerson 3 received VINDALOO delivering to Customer 0 Customer 0 eating VINDALOO Order Ticket: 1 for: Table: 1 served by: WaitPerson 3 Customer 2 Order: 8 item: SALAD for: Customer 2 Order: 9 item: BURRITO for: Customer 2 Order: 10 item: FRUIT for: Customer 2 Order: 11 item: TEA for: Customer 2 WaitPerson 3 received BURRITO delivering to Customer 1 Customer 1 eating BURRITO . } public Car2() { id = -1. private boolean engine = false. assume these * processes can be performed simultaneously by robots. driveTrain = false. exhaustSystem = false. * body. 676 Thinking in Java. import java.concurrent. wheels = false. class Car2 { private final int id. The Table class creates a group order. The OrderTicket consists of many orders. fender = false. which adds the exhaust system.java /******************** Exercise 37 ************************ * Modify CarBuilder. so customers come in groups and occupy a particular table. 4th Edition Annotated Solution Guide . } public synchronized int getId() { return id. and each chef handles one ticket.Print. allowing many approaches.mindview. Table passes the order ticket to a waitperson. *********************************************************/ package concurrency. perhaps grouping food types for delivery.java to add another stage to the * carbuilding process. import java.*. For example. A CyclicBarrier synchronizes the tasks. and when they are all done.util. Exercise 37 //: concurrency/E37_CarBuilder2.Chef 1 interrupted Customer 11 waiting for meal interrupted Chef 1 off duty *///:~ The exercise is vague. } public synchronized void addDriveTrain() { driveTrain = true. The rest of the program follows the original version. body = false. As with the second stage. to increase restaurant productivity. import static net. Restaurant produces Table tasks. but try experimenting with others. public Car2(int idn) { id = idn. Each customer at a table orders.*. and fenders. Our solution works well. } public synchronized void addEngine() { engine = true. eliminate the inefficiency of repeated roundtrips to deliver each plate as soon as possible.*.util.util. CarQueue fq. private Car2 car. } } class CarQueue extends LinkedBlockingQueue<Car2> {} class ChassisBuilder implements Runnable { private CarQueue carQueue.interrupted()) { TimeUnit. finishingQueue. } public void run() { try { while(!Thread. } public synchronized void addExhaustSystem() { exhaustSystem = true. } } catch(InterruptedException e) { print("Interrupted: ChassisBuilder"). } public synchronized void addFender() { fender = true. // Insert into queue carQueue. // Make chassis: Car2 c = new Car2(counter++). public Assembler(CarQueue cq. private CyclicBarrier barrier = new CyclicBarrier(4). } print("ChassisBuilder off"). private RobotPool robotPool. RobotPool rp){ chassisQueue = cq.put(c).sleep(500).} public synchronized void addWheels() { wheels = true. public ChassisBuilder(CarQueue cq) { carQueue = cq.MILLISECONDS. } public synchronized String toString() { return "Car " + id + " [" + " engine: " + engine + " driveTrain: " + driveTrain + " wheels: " + wheels + " exhaust system: " + exhaustSystem + " body: " + body + " fender: " + fender + "]". } public synchronized void addBody() { body = true. Concurrency 677 . print("ChassisBuilder created " + c). private int counter = 0. } } class Assembler implements Runnable { private CarQueue chassisQueue. interrupted()) { print(carQueue. // Until the robots finish // Hire robots to perform work (third stage): robotPool. public Reporter(CarQueue cq) { carQueue = cq. this). this).hire(ExhaustSystemRobot. } catch(BrokenBarrierException e) { // This one we want to know about throw new RuntimeException(e).finishingQueue = fq. } public void run() { try { while(!Thread. this).hire(FenderRobot. this). } } class Reporter implements Runnable { private CarQueue carQueue. } print("Reporter off").class. this). } public void run() { try { while(!Thread.class. 4th Edition Annotated Solution Guide . robotPool.take()).hire(WheelRobot. robotPool = rp.take(). } } 678 Thinking in Java.await(). } print("Assembler off").class.interrupted()) { // Blocks until chassis is available: car = chassisQueue.await(). // Put car into finishingQueue for further work finishingQueue. } } catch(InterruptedException e) { print("Exiting Assembler via interrupt").put(car). } public Car2 car() { return car. this).class.hire(DriveTrainRobot.class. } public CyclicBarrier barrier() { return barrier.hire(BodyRobot. robotPool.hire(EngineRobot. robotPool. barrier. barrier. // Hire robots to perform work (second stage): robotPool.class. robotPool. } } catch(InterruptedException e) { print("Exiting Reporter via interrupt"). return this. } public String toString() { return getClass(). public void run() { try { powerDown(). } // The part of run() that's different for each robot: abstract protected void performService().interrupted()) { performService(). } Concurrency 679 . } private synchronized void powerDown() throws InterruptedException { engage = false. } print(this + " off"). public Robot(RobotPool p) { pool = p. assembler.barrier(). while(engage == false) // Power down wait(). notifyAll(). } } catch(InterruptedException e) { print("Exiting " + this + " via interrupt").await(). } private boolean engage = false. } } class EngineRobot extends Robot { public EngineRobot(RobotPool pool) { super(pool). // Disconnect from the Assembler // Put ourselves back in the available pool: pool. } catch(BrokenBarrierException e) { // This one we want to know about throw new RuntimeException(e). public Robot assignAssembler(Assembler assembler) { this.release(this). } protected Assembler assembler. public synchronized void engage() { engage = true. // Synchronize // We're done with that job.. powerDown().abstract class Robot implements Runnable { private RobotPool pool. // Wait until needed while(!Thread.assembler = assembler..getName(). assembler = null. addWheels().addExhaustSystem(). } } class RobotPool { 680 Thinking in Java.addFender().car().car(). } } class FenderRobot extends Robot { public FenderRobot(RobotPool pool) { super(pool). } protected void performService() { print(this + " installing Wheels").addEngine(). 4th Edition Annotated Solution Guide .car(). } } class BodyRobot extends Robot { public BodyRobot(RobotPool pool) { super(pool). assembler. } protected void performService() { print(this + " installing exhausting system"). } protected void performService() { print(this + " installing fender"). } protected void performService() { print(this + " installing body").car().protected void performService() { print(this + " installing engine").car(). } } class DriveTrainRobot extends Robot { public DriveTrainRobot(RobotPool pool) { super(pool). assembler. } } class ExhaustSystemRobot extends Robot { public ExhaustSystemRobot(RobotPool pool) { super(pool). assembler.car(). assembler. } protected void performService() { print(this + " installing DriveTrain"). } } class WheelRobot extends Robot { public WheelRobot(RobotPool pool) { super(pool). assembler.addBody().addDriveTrain(). assembler. sleep(7). } } public class E37_CarBuilder2 { public static void main(String[] args) throws Exception { CarQueue chassisQueue = new CarQueue().execute(new ExhaustSystemRobot(robotPool)). } wait(). exec. ExecutorService exec = Executors.execute(new EngineRobot(robotPool)). // Start everything running by producing chassis: exec.SECONDS.// Quietly prevents identical entries: private Set<Robot> pool = new HashSet<Robot>().execute(new Assembler( chassisQueue. // Try again.execute(new WheelRobot(robotPool)).remove(r). r. exec.execute(new DriveTrainRobot(robotPool)). exec.execute(new BodyRobot(robotPool)). } public synchronized void hire(Class<? extends Robot> robotType.add(r). notifyAll().engage(). exec. public synchronized void add(Robot r) { pool.execute(new Reporter(finishingQueue)). } } /* (Execute to see output) *///:~ Concurrency 681 . r. exec. d). finishingQueue = new CarQueue(). exec. // Power it up to do the task return. exec. recursively } public synchronized void release(Robot r) { add(r). exec.newCachedThreadPool(). TimeUnit. exec.shutdownNow(). finishingQueue.execute(new FenderRobot(robotPool)).assignAssembler(d). robotPool)). // None available hire(robotType. Assembler d) throws InterruptedException { for(Robot r : pool) if(r.getClass().equals(robotType)) { pool.execute(new ChassisBuilder(chassisQueue)). RobotPool robotPool = new RobotPool(). model the * house-building story that was given in this chapter. *********************************************************/ package concurrency. } public synchronized int getId() { return id. class House { private final int id.*. concreteFoundation = false. public House(int idn) { id = idn. } public synchronized String toString() { return "House " + id + " [" + " steel: " + steel + " concreteForms: " + concreteForms + " concreteFoundation: " + concreteFoundation + " plumbing: " + plumbing + " concreteSlab: " + concreteSlab + " framing: " + framing + "]". private boolean steel = false. } public synchronized void buildConcreteForms() { concreteForms = true.util.mindview.Exercise 38 //: concurrency/E38_HouseBuilder.java. import static net. } public synchronized void pourConcreteSlab() { concreteSlab = true. concreteForms = false. } } 682 Thinking in Java.*.util. 4th Edition Annotated Solution Guide . } public synchronized void laySteel() { steel = true.util.concurrent. import java. framing = false.Print. } public synchronized void startFraming() { framing = true. } public synchronized void addPlumbing() { plumbing = true. import java.*. concreteSlab = false. } public House() { id = -1. plumbing = false.java /******************** Exercise 38 ************************ * Using the approach in CarBuilder. } public synchronized void pourConcreteFoundation() { concreteFoundation = true. finishingQueue. } public void run() { try { while(!Thread. private House house. public HouseBuilder(HouseQueue hq. teamPool.class. } public void run() { try { while(!Thread. teamPool. private CyclicBarrier barrier1 = new CyclicBarrier(3). TeamPool tp){ footingsQueue = hq. this). HouseQueue fq. private CyclicBarrier barrier2 = new CyclicBarrier(2).interrupted()) { TimeUnit. public FootingsDigger(HouseQueue cq) { houseQueue = cq. } public House house() { return house.interrupted()) { // Blocks until footings are dug: house = footingsQueue. private boolean secondStage = true. // Dig footings: House h = new House(counter++). } print("FootingsDigger off").class HouseQueue extends LinkedBlockingQueue<House> {} class FootingsDigger implements Runnable { private HouseQueue houseQueue. finishingQueue = fq.put(h).sleep(500). } } class HouseBuilder implements Runnable { private HouseQueue footingsQueue.take(). houseQueue.class. private TeamPool teamPool.hire(SteelTeam. private int counter = 0. print("FootingsDigger created " + h). Concurrency 683 . this). teamPool = tp.MILLISECONDS. } } catch(InterruptedException e) { print("Interrupted: FootingsDigger"). } public CyclicBarrier barrier() { return secondStage ? barrier1 : barrier2.hire(ConcreteFormsTeam. barrier2.class. secondStage = true. return this.hire(PlumbingTeam.await().await(). barrier2. public Team(TeamPool p) { pool = p. secondStage = false. barrier2. teamPool. public Team assignHouseBuilder(HouseBuilder hb) { this.hire(ConcreteFoundationTeam. } } catch(InterruptedException e) { print("Exiting Reporter via interrupt"). } public void run() { try { while(!Thread. teamPool. teamPool. } private boolean engage = false.hire(FramingTeam.await(). public Reporter2(HouseQueue hq) { houseQueue = hq. } } abstract class Team implements Runnable { private TeamPool pool. finishingQueue. public synchronized void engage() { 684 Thinking in Java.interrupted()) { print(houseQueue. } print("Reporter off").hire(ConcreteSlabTeam.await(). } } class Reporter2 implements Runnable { private HouseQueue houseQueue. barrier2.take()). } catch(BrokenBarrierException e) { throw new RuntimeException(e).put(house). this). 4th Edition Annotated Solution Guide .hb = hb. this).await().class. this).barrier1. } print("HouseBuilder off"). this).class.class. teamPool. } } catch(InterruptedException e) { print("Exiting HouseBuilder via interrupt"). } protected HouseBuilder hb. while(!Thread. } abstract protected void performService(). } } class SteelTeam extends Team { public SteelTeam(TeamPool pool) { super(pool).house(). } private synchronized void rest() throws InterruptedException { engage = false. // Put ourselves back in the available pool: pool.await(). } } Concurrency 685 . while(engage == false) wait(). hb. public void run() { try { rest(). } protected void performService() { print(this + " building concrete forms"). } public String toString() { return getClass().getName(). hb.buildConcreteForms(). } } class ConcreteFormsTeam extends Team { public ConcreteFormsTeam(TeamPool pool) { super(pool). } protected void performService() { print(this + " laying steel"). } print(this + " off").house(). hb = null.release(this).barrier().engage = true. hb. rest(). } } catch(InterruptedException e) { print("Exiting " + this + " via interrupt").interrupted()) { performService(). } catch(BrokenBarrierException e) { throw new RuntimeException(e). notifyAll().laySteel(). house().pourConcreteSlab().pourConcreteFoundation(). 686 Thinking in Java. } protected void performService() { print(this + " start framing"). 4th Edition Annotated Solution Guide . } } class FramingTeam extends Team { public FramingTeam(TeamPool pool) { super(pool).addPlumbing(). t. } protected void performService() { print(this + " pour concrete slab"). } protected void performService() { print(this + " pouring concrete foundation"). hb. } } class TeamPool { private Set<Team> pool = new HashSet<Team>(). hb. } public synchronized void hire(Class<? extends Team> teamType.house(). hb. hb.equals(teamType)) { pool. notifyAll().class ConcreteFoundationTeam extends Team { public ConcreteFoundationTeam(TeamPool pool) { super(pool). } } class PlumbingTeam extends Team { public PlumbingTeam(TeamPool pool) { super(pool).house(). HouseBuilder hb) throws InterruptedException { for(Team t : pool) if(t.add(t). public synchronized void add(Team t) { pool.remove(t).startFraming(). } } class ConcreteSlabTeam extends Team { public ConcreteSlabTeam(TeamPool pool) { super(pool).assignHouseBuilder(hb). } protected void performService() { print(this + " add plumbing").getClass().house(). execute(new ConcreteFormsTeam(teamPool)).java // {RunByHand} /******************** Exercise 39 ************************ * Does FastSimulation. teamPool)).execute(new FramingTeam(teamPool)).t. exec. exec.*.execute(new Reporter2(finishingQueue)). *********************************************************/ package concurrency. and RobotPool -> TeamPool. finishingQueue. exec. exec. return.execute(new ConcreteFoundationTeam(teamPool)).execute(new SteelTeam(teamPool)). exec.java make reasonable assumptions? * Change the array to ordinary ints instead of * AtomicInteger and use Lock mutexes. The program logic remains basically the same. } } /* (Execute to see output) *///:~ Only the names of the entities change: Car -> House. hb). } } public class E38_HouseBuilder { public static void main(String[] args) throws Exception { HouseQueue footingsQueue = new HouseQueue(). Robot -> Team. finishingQueue = new HouseQueue(). } public void release(Team t) { add(t). Compare * performance between the two versions of the program.concurrent. Exercise 39 //: concurrency/E39_FastSimulation2.engage(). TeamPool teamPool = new TeamPool().execute(new ConcreteSlabTeam(teamPool)). exec.shutdownNow().newCachedThreadPool().SECONDS.sleep(7). ExecutorService exec = Executors. exec. TimeUnit.execute(new FootingsDigger(footingsQueue)). } wait().execute(new PlumbingTeam(teamPool)). hire(teamType.execute(new HouseBuilder( footingsQueue.util. import java. exec. exec. Concurrency 687 . exec. Assembler -> HouseBuilder. util. static class Evolver1 implements Runnable { public void run() { while(!Thread.*. if(!GRID[element][i] . if(previous < 0) previous = N_ELEMENTS .incrementAndGet(). 4th Edition Annotated Solution Guide . // Variant 1 (optimistic locking): static final AtomicInteger[][] GRID = new AtomicInteger[N_ELEMENTS][N_GENES]. int oldvalue = GRID[element][i]. if(next >= N_ELEMENTS) next = 0. static final int N_GENES = 30.util.get() + GRID[next][i]. newvalue /= 3.util. static final int N_EVOLVERS = 50.*.concurrent.get(). static net. i < N_GENES. newvalue)) { // Some backup action.nextInt(N_ELEMENTS). static Random rand = new Random(47).util. java. // Counts the number of evolutions using 'variant 2': static final AtomicInteger counter2 = new AtomicInteger(). int next = element + 1.*.mindview.import import import import java. // Counts the number of evolutions using 'variant 1': static final AtomicInteger counter1 = new AtomicInteger().1. // Variant 2 (explicit locking): static final int[][] grid = new int[N_ELEMENTS][N_GENES]. } } counter1. int newvalue = oldvalue + GRID[previous][i]. } } } static class Evolver2 implements Runnable { public void run() { 688 Thinking in Java.concurrent..atomic.Print. static final ReentrantLock[] lock = new ReentrantLock[N_ELEMENTS].interrupted()) { // Randomly select an element to work on: int element = rand. i++) { int previous = element ..get().compareAndSet(oldvalue. for(int i = 0. java.1. public class E39_FastSimulation2 { static final int N_ELEMENTS = 10000.locks.*. print("Variant 2: " + counter2.get()).get()). i < N_ELEMENTS. i++) for(int j = 0. for(int i = 0. newvalue /= 3. i < N_EVOLVERS.nextInt(1000). i++) for(int j = 0. // Lock the whole row: lock[element]. i++) lock[i] = new ReentrantLock(). for(int i = 0. i < N_ELEMENTS. Here is our output: Variant 1: 3376049 Variant 2: 1362210 Concurrency 689 . j < N_GENES.execute(new Evolver2()). j++) grid[i][j] = rand. if(next >= N_ELEMENTS) next = 0. grid[element][i] = newvalue. int next = element + 1.1. j < N_GENES. } } } public static void main(String[] args) throws Exception { ExecutorService exec = Executors. } counter2.sleep(5). } } /* (Execute to see output) *///:~ The assumptions are reasonable.nextInt(1000)).newCachedThreadPool().unlock().1.SECONDS. try { for(int i = 0.interrupted()) { // Randomly select an element to work on: int element = rand. i < N_GENES. for(int i = 0.incrementAndGet(). exec. print("Variant 1: " + counter1.shutdownNow().execute(new Evolver1()).nextInt(N_ELEMENTS). exec. j++) GRID[i][j] = new AtomicInteger(rand. i++) { exec. i++) { int previous = element . if(previous < 0) previous = N_ELEMENTS . for(int i = 0.lock(). i < N_ELEMENTS. } } finally { lock[element]. } TimeUnit.while(!Thread. int newvalue = grid[element][i] + grid[previous][i] + grid[next][i]. map(genK.util. size)).*. public ReaderWriterMap(Generator<K> genK. rlock. Otherwise.java // {Args: 1 10 10} (Fast verification check during build) /******************** Exercise 40 ************************ * Following the example of ReaderWriterList.V> extends AbstractMap<K.java.util.java.lock(). import java.*. } public V put(K key. How does * it compare to a synchronized HashMap and a * ConcurrentHashMap? *********************************************************/ package concurrency. Risk optimistic locking only if the cost of a failure is low. class ReaderWriterMap<K. Investigate its * performance by modifying MapComparisons. try { return lockedMap. value). create * a ReaderWriterMap using a HashMap. import net. initialValue. int size. for example.put(key.*.V>( MapData. you lose the advantage gained by avoiding explicit locks in the first place.readLock().concurrent. V initialValue) { lockedMap = new HashMap<K. wlock.lock(). 4th Edition Annotated Solution Guide .V> lockedMap.mindview. lock something smaller than the whole row. } finally { wlock.util.locks. import java. Exercise 40 //: concurrency/E40_MapComparisons2. } } public V get(Object key) { Lock rlock = lock.writeLock().V> { private HashMap<K. V value) { Lock wlock = lock. try { // Comment out if you would like to trace how many // readers are acquiring the lock simultaneously: 690 Thinking in Java.Experiment with different locking schemes. private ReentrantReadWriteLock lock = new ReentrantReadWriteLock(true).unlock(). nWriters).Integer> containerInitializer() { return new ReaderWriterMap<Integer. containerSize.Integer(). 5). 0).// // if(lock. 1). 5).getReadLockCount()). 1). 1). new ReaderWriterMapTest(10.initMain(args). } } /* Output: (Sample) Type Read time Synched HashMap 10r 0w 4034312 Synched HashMap 9r 1w 9416558 readTime + writeTime = 10947478 Synched HashMap 5r 5w 119009 readTime + writeTime = 2488863 ConcurrentHashMap 10r 0w 2892266 ConcurrentHashMap 9r 1w 7266285 readTime + writeTime = 7955478 Write time 0 1530920 2369854 0 689193 Concurrency 691 .getReadLockCount() > 1) print(lock. } } public class E40_MapComparisons2 { public static void main(String[] args) { Tester.Entry<K. 0). new ConcurrentHashMapTest(9. Tester. new ConcurrentHashMapTest(10. } } class ReaderWriterMapTest extends MapTest { Map<Integer. } finally { rlock.V>> entrySet() { return null.Integer>( new CountingGenerator. } ReaderWriterMapTest(int nReaders.yield(). new ConcurrentHashMapTest(5. 0). new SynchronizedHashMapTest(10. return lockedMap. new ReaderWriterMapTest(9.unlock(). } } // Dummy implementation public Set<Map. new SynchronizedHashMapTest(5.exec. new ReaderWriterMapTest(5. new SynchronizedHashMapTest(9. Thread.get(key).shutdown(). nReaders. 5). int nWriters) { super("ReaderWriterMap". 1). *********************************************************/ package concurrency. return x + y.util. private Random rand = new Random(47).newSingleThreadExecutor(). 692 Thinking in Java. // Insert a random delay to produce the effect // of a calculation time: private void pause(int factor) { try { TimeUnit. import java. Exercise 41 //: concurrency/E41_ActiveObjectDemo2. } } public Future<Integer> calculateInt(final int x. import java.MILLISECONDS.ConcurrentHashMap 5r 5w readTime + writeTime = ReaderWriterMap 10r 0w ReaderWriterMap 9r 1w readTime + writeTime = ReaderWriterMap 5r 5w readTime + writeTime = *///:~ 132698 3938488 6972675 13310350 15706744 204774 10482337 3805790 0 2396394 10277563 ReaderWriterMap extends AbstractMap to make it amenable to the MapComparisons.java program.mindview.submit(new Callable<Integer>() { public Integer call() { print("starting " + x + " + " + y). You see from the output that it doesn’t perform well here.sleep( 100 + rand. import static net. pause(500).*.*. but sometimes it may be your best option.*. final int y) { return ex.util. 4th Edition Annotated Solution Guide .java /******************** Exercise 41 ************************ * Add a message handler to ActiveObjectDemo.util. public class E41_ActiveObjectDemo2 { private ExecutorService ex = Executors.java that * has no return value.concurrent. } catch(InterruptedException e) { print("sleep() interrupted").nextInt(factor)).Print. and call this within main(). d1. i)). f < 1.0f. } // Message handler without a return value: public void printDocument(final String s) { ex. } }).calculateFloat(f.isDone()) { try { print(f.shutdown(). i++) { results. } }).printDocument("DOC_" + i). } } Concurrency 693 . } catch(Exception e) { throw new RuntimeException(e).add(d1. // Prevents ConcurrentModificationException: List<Future<?>> results = new CopyOnWriteArrayList<Future<?>>(). for(int i = 0. } public static void main(String[] args) { E41_ActiveObjectDemo2 d1 = new E41_ActiveObjectDemo2(). } public void shutdown() { ex.calculateInt(i. i < 5.execute(new Runnable() { public void run() { print("printing document " + s). pause(2000).} }).2f) results.remove(f).0f.size() > 0) { for(Future<?> f : results) if(f. print("document " + s + " printed"). f += 0. for(float f = 0. } results. } public Future<Float> calculateFloat(final float x. pause(1000). } print("All asynch calls made"). final float y) { return ex. f)).submit(new Callable<Float>() { public Float call() { print("starting " + x + " + " + y).get()). while(results. return x + y.add(d1. import java.2 starting 0 + 0 1. 4th Edition Annotated Solution Guide .0 starting 0.java /******************** Exercise 42 ************************ * Modify WaxOMatic.*.2 + 0. } } /* Output: (Sample) All asynch calls made starting 0.8 starting 0.6 printing document DOC_0 0 document DOC_0 printed starting 1 + 1 printing document DOC_1 2 document DOC_1 printed starting 2 + 2 printing document DOC_2 4 document DOC_2 printed starting 3 + 3 printing document DOC_3 6 document DOC_3 printed starting 4 + 4 printing document DOC_4 8 document DOC_4 printed *///:~ Exercise 42 //: concurrency/E42_ActiveObjectWaxOMatic.java so that it implements active * objects.4 + 0.util.4 0. 694 Thinking in Java.0 + 0.concurrent.Print.8 1.6 0.2 starting 0.mindview.4 starting 0.d1.8 + 0.6 + 0. import static net.*.util.0 0. *********************************************************/ package concurrency.shutdown(). WAX) { printnb("Wax On! "). } catch(InterruptedException e) { print("sleep() interrupted"). private class BuffingTask implements Runnable { public void run() { if(lastAction != Action. } catch(RejectedExecutionException e) {} } public void shutdown() { ex. private boolean waxOn. } catch(RejectedExecutionException e) {} } public void buff() { try { ex.sleep(sleep_time).MILLISECONDS.BUFF. } } } private final BuffingTask buffingTask = new BuffingTask(). } } } private final WaxingTask waxingTask = new WaxingTask().shutdown(). pause(200). public void wax() { try { ex. pause(200).execute(buffingTask).class ActiveCar { private ExecutorService ex = Executors. } } private class WaxingTask implements Runnable { public void run() { if(lastAction != Action. BUFF } private Action lastAction = Action.BUFF) { printnb("Wax Off! ").newSingleThreadExecutor(). } private static void pause(int sleep_time) { try { TimeUnit. Concurrency 695 . waxOn = true. private enum Action { WAX. lastAction = Action. lastAction = Action.BUFF.WAX. waxOn = false.execute(waxingTask). newScheduledThreadPool(2). public BuffCar(ActiveCar c) { car = c.MILLISECONDS)..” ActiveCar still handles the previously posted messages.buff(). TimeUnit. } } public class E42_ActiveObjectWaxOMatic { public static void main(String[] args) throws Exception { ActiveCar car = new ActiveCar().MILLISECONDS).SECONDS. } public void run() { car. You must catch the RejectedExecutionException inside wax( ) and buff( ) (the methods accepting messages). // Interrupt all tasks car. the Active Object Car accepts two kinds of messages: wax and buff.shutdown().. 0. exec.wax().sleep(5). 0. so they need no locks. TimeUnit. 200. public WaxCar(ActiveCar c) { car = c. } public void run() { car. exec. The output shows that even after WaxCar and BuffCar “stop. exec. ScheduledExecutorService exec = Executors. } } /* Output: (Sample) Wax On! Wax Off! Wax On! Wax Off! Wax On! Wax Off! Wax On! Wax Off! Wax On! Wax Off! Wax On! Wax Off! Wax On! Wax Off! Wax On! Wax Off! Wax On! Wax Off! Wax On! Wax Off! Wax On! Wax Off! Wax On! Wax Off! Wax On! Wax Off! Wax On! Wax Off! *///:~ Here. 200. WaxCar and BuffCar serialize their message requests to run in parallel.scheduleAtFixedRate( new BuffCar(car). because the client programmer needs to interrogate the return value to know if the message has been accepted or rejected. // Run for a while. so we take some precautions to avoid running out of memory. New messages arrive faster than the active object can handle them. 4th Edition Annotated Solution Guide . } } class BuffCar implements Runnable { private final ActiveCar car.scheduleAtFixedRate( new WaxCar(car). TimeUnit.} class WaxCar implements Runnable { private final ActiveCar car. 696 Thinking in Java.shutdownNow(). *.setSize(300. import java. class DynamicHelloLabel extends JFrame { 697 . you will need to manually terminate the corresponding OS process. import java.java /******************** Exercise 01 ************************ * Modify HelloSwing. import javax.java to show that label addition is * dynamic.awt.concurrent.Graphical User Interfaces Exercise 1 //: gui/E01_HelloSwing2. } } ///:~ After closing the main application window. import javax.setVisible(true). *********************************************************/ package gui. import java.java to prove to yourself that the * application will not close without the call to * setDefaultCloseOperation(). frame.*.java /******************** Exercise 02 ************************ * Modify HelloLabel.*. *********************************************************/ package gui.swing. Exercise 2 //: gui/E02_DynamicHelloLabel.swing.util.util. public class E01_HelloSwing2 { public static void main(String[] args) { JFrame frame = new JFrame("Hello Swing").*. 100). frame.*. by adding a random number of labels. SECONDS. JLabel[] labels. i++) dhl.invokeLater(new Runnable() { public void run() { for(int i = 0.java /******************** Exercise 03 ************************ * Modify SubmitSwingProgram.SwingConsole. i < numOfLabels. setSize(300. 100).labels.labels[i]. 4th Edition Annotated Solution Guide .concurrent. import java.*. *********************************************************/ package gui. setDefaultCloseOperation(JFrame.*. import static net.swing.nextInt(10) + 1.EXIT_ON_CLOSE).sleep(2). import javax.mindview.private static Random rnd = new Random(47).length.setText("LABEL: " + i). for(int i = 0. TimeUnit. 698 Thinking in Java.invokeLater(new Runnable() { public void run() { dhl = new DynamicHelloLabel().util. SwingUtilities. labels = new JLabel[numOfLabels]. } }). } } public class E02_DynamicHelloLabel { static DynamicHelloLabel dhl.*. } }). DynamicHelloLabel() { super("Hello Label"). i< dhl. setVisible(true). public static void main(String[] args) throws Exception { SwingUtilities. i++) add(labels[i] = new JLabel("label: " + i)).util.java so that it uses * SwingConsole. Exercise 3 //: gui/E03_SubmitSwingProgram2. setLayout(new FlowLayout()). } } ///:~ See the Making a button section for an explanation of why we change the default layout manager. int numOfLabels = rnd. setText("Hey! This is Different!"). } } public class E03_SubmitSwingProgram2 { static SubmitSwingProgram2 ssp. add(b2).sleep(2). only one button will appear in the * resulting program. SwingUtilities.*. 300.class SubmitSwingProgram2 extends JFrame { JLabel label.SECONDS. } }).java /******************** Exercise 04 ************************ * Verify that without the setLayout() call in * Button1. public SubmitSwingProgram2() { label = new JLabel("A Label"). public class E04_Button1UsingDefaultLayout extends JFrame { private JButton b1 = new JButton("Button 1").label. *********************************************************/ package gui. 200. TimeUnit. } public static void main(String[] args) { run(new E04_Button1UsingDefaultLayout().util.swing. add(label). public static void main(String[] args) throws Exception { run(ssp = new SubmitSwingProgram2(). 100). 100).invokeLater(new Runnable() { public void run() { ssp. import javax. } } ///:~ Exercise 4 //: gui/E04_Button1UsingDefaultLayout.SwingConsole. public E04_Button1UsingDefaultLayout () { add(b1). } } ///:~ Graphical User Interfaces 699 . import static net. b2 = new JButton("Button 2").*.java.mindview. add(b3).swing. setLayout(new FlowLayout()).SwingConsole.addActionListener(bl). import javax.mindview.getSource()). private JTextField txt = new JTextField(10).util. b2 = new JButton("Button 2"). } public static void main(String[] args) { run(new E05_Button3().*. import java. When you press * each button.addActionListener(bl). make different text appear in the text * field. 700 Thinking in Java.getText(). txt. add(txt). public E05_Button3() { b1. add(b2). * Include one text field and three buttons. import java.*.awt. b3 = new JButton("Button 3"). private ActionListener bl = new ActionListener() { public void actionPerformed(ActionEvent e) { String name = ((JButton)e. public class E05_Button3 extends JFrame { private JButton b1 = new JButton("Button 1"). 150). 4th Edition Annotated Solution Guide .event. add(b1). } }.Exercise 5 //: gui/E05_Button3. b2.setText(name).awt.*. import static net. } } ///:~ The text that appears in the JTextField is created from the label on each button.java /******************** Exercise 05 ************************ * Create an application using the SwingConsole class. *********************************************************/ package gui. 200.*.addActionListener(bl). b3. java into an * interactive Swing program that allows you to put an * input string in one JTextArea and a regular expression in * a JTextField. import javax.add(new JScrollPane(output)).add(outputL).Exercise 6 //: gui/E06_TestRegularExpression2.*. The results should be displayed in a second * JTextArea. add(panel2). output = new JTextArea(3. public class E06_TestRegularExpression2 extends JFrame { private JTextArea input = new JTextArea(3.java /******************** Exercise 6 ************************ * Turn strings/TestRegularExpression.getText().mindview.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { String inputS = input. panel1.add(new JScrollPane(input)).1)). private JButton match = new JButton("Match"). panel2 = new JPanel(). panel3. panel2.event. panel1. 60).regex.add(expression).util. import static net. panel2. String regEx = expression. *********************************************************/ package gui.awt.compile(regEx). outputL = new JLabel("Output").add(expressionL). import java. import java. match. private JTextField expression = new JTextField(40).SwingConsole. public E06_TestRegularExpression2() { setLayout(new GridLayout(3. panel3. import java.add(inputL).*. Graphical User Interfaces 701 . private JPanel panel1 = new JPanel().awt.add(match). add(panel1). panel2.util.*. private JLabel inputL = new JLabel("Input"). Matcher m = p.matcher(inputS). 60). add(panel3).*.*. expressionL = new JLabel("Expression").swing. panel3 = new JPanel(). Pattern p = Pattern.getText(). import java. JMenu & // JRadioButtonMenuItem will not be shown separately: JMenuItem mi = new JMenuItem("Menu Item"). then apply a panel to represent each one. } }).awt. JButton b1 = new JButton("Button 1").*.com.awt.SwingConsole. while(m. "In". you can segregate it into different areas. 4th Edition Annotated Solution Guide .*. } public static void main(String[] args) { run(new E06_TestRegularExpression2().find()) { outputS += "Match \"" + m.) Capture their events and display an * appropriate message for each inside a text field.sun.*. import java.setText(outputS).swing.String outputS = "". JTextField txt = new JTextField(30). class AllAction extends JFrame { // The JMenuItem's derivatives JCheckBoxMenuItem. We use a relatively simple UI here to demonstrate this technique.1) + '\n'. } output.util. ***********************************************/ package gui. "To". import static net. "Combobox" 702 Thinking in Java. "Place".group() + "\" at positions " + m.java /****************** Exercise 7 ****************** * Create an application using SwingConsole. Exercise 7 //: gui/E07_AllAction. 400). (Look these up in * the JDK documentation from http://java. import javax. JComboBox jcb = new JComboBox(new String[]{ "Elements".start() + "-" + (m. and * add all the Swing components that have an * addActionListener() method.event.mindview. } } ///:~ Here we introduce a new layout manager called GridLayout. 700. * Hint: Search for addActionListener() using the * index.end() .*. When building a more complex UI. start(). } }).setText("JComboBox selected: " + jcb. } }).INFORMATION_MESSAGE). "information". public AllAction() { setLayout(new FlowLayout()). } }).addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) txt. add(jcb). add(txt).").addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) txt.setText("Button pressed"). JOptionPane. mi.showMessageDialog( null. } }). b1. add(mi). JFileChooser jfc = new JFileChooser(". add(jfc). } }).getSelectedItem()).}). { { { { { { Graphical User Interfaces 703 . jfc.setText("Timer Ticked " + i++).getSelectedFile()).setText("JMenuItem selected"). jcb. new ActionListener() { int i = 0.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) JOptionPane. new Timer(5000. } }). add(b1).addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) txt. txt. public void actionPerformed(ActionEvent e) txt.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) txt.setText( "FileChooser ActionListener fired: " + jfc. "JTexfField ActionListener fired". 550.SwingConsole.util. 400). * Look this up in the JDK documentation. class Cursors extends JFrame { JTextField txt = new JTextField(10).setCursor(hand).*. This example is a fun way to show what these components can do. import javax. and reminds us just how good Swing is. } } ///:~ You can fire the ActionListener for the JTextField only by pressing return (unless you can find an alternative in the documentation). which has a setCursor() method.getPredefinedCursor( Cursor. import java. Cursor hand = Cursor. JButton b1 = new JButton("Button 1").} } public class E07_AllAction { public static void main(String args[]) { run(new AllAction(). but the garbage collector never collects it.*. txt.java /****************** Exercise 8 ***************** * Almost every Swing component is derived from * Component. add(txt).swing. 4th Edition Annotated Solution Guide . Exercise 8 //: gui/E08_Cursors.Timer are very different. The constructor typically stores a reference to the object somewhere so that it won’t be garbagecollected. as it normally would any object whose reference is unused.Timer and java.mindview. We create the first without capturing the reference. The javax.swing. 704 Thinking in Java.HAND_CURSOR). ***********************************************/ package gui.awt. Create * an application and change the cursor to one of * the stock cursors in the Cursor class.util.*. import static net. public Cursors() { setLayout(new FlowLayout()). regex.util.java. } } ///:~ The constants in the Cursor class are ints.*. JScrollPane scrollPane = new JScrollPane(results).mindview.util. 100). JTextField name = new JTextField(25).*.event. on Windows anyway. setCursor(hand). Exercise 9 //: gui/E09_ShowMethods. class ShowMethodsClean extends JFrame { private static Pattern qualifier = Pattern.").add(b1).*. not Cursor objects.ShowMethods. JTextArea results = new JTextArea(40.awt. import java.*.SwingConsole.awt. can be used up easily—this may be the reason for the design). 200.util.java. 65). but cursors are system resources which. import java. } } public class E08_Cursors { public static void main(String args[]) { run(new Cursors(). import java.*. ***********************************************/ package gui. it goes back to the default cursor.compile("\\w+\\. so you must use either the Cursor constructor or getPredefinedCursor( ) method to generate a Cursor object from the int (this seems a bit strange. Graphical User Interfaces 705 .reflect. create a * program with the full functionality of * typeinfo. import java. Note that if you don’t also set the cursor in the JTextField.java /****************** Exercise 9 ***************** * Starting with ShowAddListeners. import javax. String[] n. import static net.swing.*. import java.lang.*. searchFor = new JTextField(25). class NameL implements ActionListener { public void actionPerformed(ActionEvent e) { String nm = name.length. while(it.append(s + "\n").matcher(s).setText(""). } } void reDisplay() { results. for(String s : n) { Iterator<String> it = lookFor.append( // qualifier. if(nm.util.length + ctor. i++) n[i + m. 4th Edition Annotated Solution Guide .trim().getText().toString().setText("No match"). for(int i = 0. boolean include = true. i++) n[i] = m[i].split("\\s+")).toString(). try { cl = Class.length() == 0) // Include everything: for(String s : n) results. 706 Thinking in Java.setText("No match"). } Class<?> cl. Constructor<?>[] ctor = cl. for(int i = 0. if(searchFor.next()) == -1) include = false. return. // OR: // results.getMethods().length() == 0) { results. i < m.append(s + "\n").asList( searchFor.hasNext()) if(s. // Convert to an array of cleaned Strings: n = new String[m.List<String> lookFor = Arrays.trim().indexOf(it. reDisplay().length.length]. } Method[] m = cl.iterator().getConstructors(). else { // Include only methods that have ALL the // words listed in searchFor: java.replaceAll("") + "\n").length] = ctor[i]. } catch(ClassNotFoundException ex) { results. return. i < ctor.forName(nm). if(include == true) results.getText().getText(). the program shows everything.add(name). the String from that field must be broken up into words using String. top2.name (press ENTER):")).// // // } OR: results. 400).add(new JLabel( "Qualified. top. } public ShowMethodsClean() { name. } }). top1. JPanel top1 = new JPanel(). } // Force the scrollpane back to the top: scrollPane. Then we copy the methods and constructors from the Class object into n (the Strings array of all possible names). JPanel top2 = new JPanel().addActionListener(new NameL()). top2. top. and call reDisplay( ) to choose methods to display the names. } } ///:~ We add a second JTextField called searchFor to hold a list of words to find.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { reDisplay().add(top2). add(scrollPane).1)).split( ).class.NORTH.matcher(s). We put each Graphical User Interfaces 707 .getViewport(). If there are no words in the searchFor field. top). 600. } } public class E09_ShowMethods { public static void main(String args[]) { run(new ShowMethodsClean().append( qualifier. 0)).setViewPosition( new Point(0. // There is no need to parse the class file again when // only the search conditions have changed: searchFor.replaceAll("") + "\n"). top1. add(BorderLayout.add(top1).add(searchFor). If there are words in the searchFor field.add(new JLabel( "Words to search for (optional):")). JPanel top = new JPanel(new GridLayout(2. addKeyListener(new KeyAdapter() { public void keyTyped(KeyEvent e) { txt. } }). import javax. characters typed into it will appear in the * JTextField.awt. then use the flag include to examine each method/constructor. b. so we move the scroll-pane back to the top whenever JTextArea data is modified.mindview.event. and place it into the JTextArea. You must modify the Viewport.java /****************** Exercise 10 ****************** * Create an application using SwingConsole. as you can see in the line of code at the end of reDisplay( ). you can remove the pertinent comments from the code to strip off the qualifiers. Optionally.SwingConsole.*. and resize the main application window if the UI displays awkwardly. add(txt). the search is case-sensitive.*. with a * JButton and a JTextField.setText(txt. The JTextArea can appear blank when elements in the output have just scrolled off the page. import java. import static net. public TypeableButton() { setLayout(new FlowLayout()). add(b).getKeyChar()). JButton b = new JButton("Button 1").word into java. ***********************************************/ package gui. import java.swing. Write and attach the * appropriate listener so that if the button has the * focus.util. and not the JScrollPane directly.*.awt.getText() + e. } } 708 Thinking in Java.List lookFor. Exercise 10 //: gui/E10_TypeableButton. Remember.*.util. 4th Edition Annotated Solution Guide . class TypeableButton extends JFrame { JTextField txt = new JTextField(10). making sure it contains all the words in lookFor. magenta. Color. setBackground(newColor()). 100).black. } } ///:~ Exercise 11 //: gui/E11_RandomColorButton. Each * time you press this button. import static net.gray.lightGray.red. Color.util. 200.darkGray. private static Random rnd = new Random(47). import java.public class E10_TypeableButton { public static void main(String args[]) { run(new TypeableButton().blue.java /****************** Exercise 11 ***************** * Inherit a new type of button from JButton.pink. Color. } }).event.SwingConsole. import java. private static final Color newColor() { return colors[rnd. Color.awt.orange. import java.mindview.*.*. it should change its * color to a randomly selected value. class RandomColorButton extends JButton { private static final Color[] colors = { Color.util. See * ColorBoxes. Color. Color.*.nextInt(colors. import javax.Random. Color.*. Color.yellow }. } public RandomColorButton(String text) { super(text).cyan.java (later in this chapter) for an * example of how to generate a random color value.swing. ***********************************************/ package gui. Color.length)].awt.white. Color. } } Graphical User Interfaces 709 . addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { setBackground(newColor()).green. Color. Color. 710 Thinking in Java. } public static void main(String args[]) { run(new E11_RandomColorButton(). "keyTyped".*. b2 = new MyButton(Color.*. "mouseDragged".awt. e.event.get(field).setText(msg).*. String msg) { h.JTextField>().RED.swing. import java. "keyPressed". "mouseClicked".*.*.java /****************** Exercise 12 ***************** * Monitor a new type of event in TrackEvent.paramString()). 75). import javax. class TrackEvent extends JFrame { private HashMap<String.BLUE.JTextField> h = new HashMap<String.public class E11_RandomColorButton extends JFrame { E11_RandomColorButton() { add(new RandomColorButton("Random Colors")).java * by adding the new event-handling code. import java. "mouseReleased". "focusGained". You'll * need to discover on your own the type of event * that you want to monitor. "mouseEntered". "mousePressed". "test1"). "focusLost".util. ***********************************************/ package gui. class MyButton extends JButton { void report(String field. private String[] event = { "actionPerformed". "test2"). "mouseMoved" }. import java.util. import static net. private MyButton b1 = new MyButton(Color. } ActionListener al = new ActionListener() { public void actionPerformed(ActionEvent e) { report("actionPerformed". } } ///:~ Exercise 12 //: gui/E12_NewEvent.awt. "stateChanged". 4th Edition Annotated Solution Guide . "keyReleased".event.*.swing.SwingConsole. 150. import javax. "mouseExited".mindview. } }. } public void mousePressed(MouseEvent e) { report("mousePressed".paramString()).} }. e. e. e. e. } public void mouseExited(MouseEvent e) { report("mouseExited". } public void mouseEntered(MouseEvent e) { report("mouseEntered". ChangeListener cl = new ChangeListener() { public void stateChanged(ChangeEvent e) { report("stateChanged".paramString()). MouseMotionListener mml = new MouseMotionListener() { public void mouseDragged(MouseEvent e) { report("mouseDragged".paramString()). e.paramString()).paramString()).paramString()). } public void keyReleased(KeyEvent e) { report("keyReleased". e.paramString()). } }. } public void focusLost(FocusEvent e) { report("focusLost". } public void keyTyped(KeyEvent e) { report("keyTyped".toString()). e. } }. } }. e. KeyListener kl = new KeyListener() { public void keyPressed(KeyEvent e) { report("keyPressed". MouseListener ml = new MouseListener() { public void mouseClicked(MouseEvent e) { report("mouseClicked".paramString()). e.paramString()).paramString()). } public void mouseReleased(MouseEvent e) { report("mouseReleased". e. FocusListener fl = new FocusListener() { public void focusGained(FocusEvent e) { report("focusGained". } Graphical User Interfaces 711 . e.paramString()). e. e. JLabel.put(evt. addFocusListener(fl). String label) { super(label).public void mouseMoved(MouseEvent e) { report("mouseMoved". addMouseMotionListener(mml). addChangeListener(cl).RIGHT)). addKeyListener(kl). 700. t. setBackground(color). public MyButton(Color color. addMouseListener(ml).paramString()).length + 1. h.java /****************** Exercise 13 ***************** * Modify TextFields. } } public class E12_NewEvent { public static void main(String[] args) { run(new TrackEvent(). 4th Edition Annotated Solution Guide .java so that the characters * in t2 retain the original case that they were * typed in. addActionListener(al). but we also added one for addChangeListener( ).setEditable(false). 500). } } ///:~ JButton has many “add listener” methods. 2)). Exercise 13 //: gui/E13_OriginalCase. add(t). add(b2). instead of automatically being forced * to uppercase. } add(b1). 712 Thinking in Java. add(new JLabel(evt. } } public TrackEvent() { setLayout(new GridLayout(event. t). } }. for(String evt : event) { JTextField t = new JTextField(). An obvious choice is addActionListener( ). b1.event. import java. b2 = new JButton("Set Text").swing.addActionListener(new B1()). setLayout(new FlowLayout()).*.awt.setText("Text: "+ t1. add(b1). private String s = "". } } class T1A implements ActionListener { private int count = 0. import javax.mindview.getText()). public void actionPerformed(ActionEvent e) { t3.setText(t1. t3. t2 = new JTextField(30).addActionListener(new T1A()). import java. add(t1).SwingConsole. class TextFields extends JFrame { private JButton b1 = new JButton("Get Text"). t3 = new JTextField(30).addActionListener(new B2()).setDocument(ucd).getText()). } class T1 implements DocumentListener { public void changedUpdate(DocumentEvent e) {} public void insertUpdate(DocumentEvent e) { t2. public TextFields() { t1. add(t2). private UpperCaseDocument ucd = new UpperCaseDocument().*. private JTextField t1 = new JTextField(30).awt.swing.event. } Graphical User Interfaces 713 . } public void removeUpdate(DocumentEvent e) { t2. add(t3).text. import static net. ucd. t1.setText("t1 Action Event " + count++). import javax.***********************************************/ package gui.addDocumentListener(new T1()).*.util.swing.setText(t1. add(b2). import javax.*. b2.getText()).*.*. t1.getSelectedText(). Exercise 14 //: gui/E14_UseTextArea. 4th Edition Annotated Solution Guide .setUpperCase(true). 375.setText("Inserted by Button 2: " + s).toUpperCase(). AttributeSet attSet) throws BadLocationException { if(upperCase) str = str.getText().setEditable(true).setUpperCase(false).setEditable(false). String str. } public void insertString(int offset. t1. } } ///:~ This exercise forces you to study the example thoroughly in order to find where to make the changes (the second and fourth lines of UpperCaseDocument).getSelectedText() == null) s = t1. ucd. public void setUpperCase(boolean flag) { // upperCase = flag.} class B1 implements ActionListener { public void actionPerformed(ActionEvent e) { if(t1. } } class B2 implements ActionListener { public void actionPerformed(ActionEvent e) { ucd.java /****************** Exercise 14 ***************** 714 Thinking in Java. else s = t1. } } public class E13_OriginalCase { public static void main(String[] args) { run(new TextFields().insertString(offset. super. t1. 200). attSet). str. } } } class UpperCaseDocument extends PlainDocument { private boolean upperCase /*= true*/. add(new JScrollPane(tp)). ***********************************************/ package gui. ***********************************************/ package gui. b). } }). import net.next() + "\n").*.java /****************** Exercise 15 ***************** * Add a check box to the application created in * Exercise 5. import java.*. class TextPane extends JFrame { private JButton b = new JButton("Add Text"). import java. add(BorderLayout.*.SwingConsole.*.swing.mindview.mindview.awt. and insert * different text into the text field.util.String(7).addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { for(int i = 1. } } ///:~ Copying from the example in TIJ4. private static Generator<String> sg = new RandomGenerator. we change only the line that defines the JTextPane.* Modify TextPane.SOUTH. // Only needed to change this one line: JTextArea tp = new JTextArea().setText(tp. 425). public TextPane() { b.getText() + sg.awt.util. i < 10. import static net. 475. capture the event.event.*. } } public class E14_UseTextArea { public static void main(String[] args) { run(new TextPane(). import javax.java to use a JTextArea instead * of a JTextPane. Exercise 15 //: gui/E15_CheckBoxApplication. Graphical User Interfaces 715 . i++) tp. } } ///:~ Exercise 16 //: gui/E16_SimplifyList. public class E15_CheckBoxApplication extends JFrame { private JButton b1 = new JButton("Button 1").mindview. } public static void main(String[] args) { run(new E15_CheckBoxApplication(). 4th Edition Annotated Solution Guide . add(b2).*.*.getSource()).setText("Checkbox unchecked").import import import import javax.setText("Checkbox checked"). private JTextField txt = new JTextField(12). else txt.SwingConsole. b2 = new JButton("Button 2"). add(b3).swing.*. check.setText(name). java. public E15_CheckBoxApplication() { b1.event.addActionListener(bl).isSelected()) txt. b3 = new JButton("Button 3"). setLayout(new FlowLayout()).addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { JCheckBox jcb = (JCheckBox)e.java 716 Thinking in Java.getText(). } }. add(check). 180).util.addActionListener(bl).awt.awt. JCheckBox check = new JCheckBox("CheckBox"). 200. txt.addActionListener(bl). add(txt). add(b1). b2.getSource(). java. } }). static net. b3. private ActionListener bl = new ActionListener() { public void actionPerformed(ActionEvent e) { String name = ((JButton)e. if(jcb.*. public class E16_SimplifyList extends JFrame { private String[] flavors = { "Chocolate". for(Object item : lst./****************** Exercise 16 ***************** * Simplify List. public E16_SimplifyList() { t.awt. "Praline Cream".event. "Mint Chip". t. add(lst).util. "Strawberry".mindview. import java. "Mud Pie" }.append(item + "\n"). "Mocha Almond Fudge". t.swing.BLACK).*. private ListSelectionListener ll = new ListSelectionListener() { public void valueChanged(ListSelectionEvent e) { if(e. add(t).*.setText(""). ***********************************************/ package gui.setBorder(brd). import javax. } } ///:~ Graphical User Interfaces 717 .createMatteBorder( 1. private JList lst = new JList(flavors).border.SwingConsole.java by passing the array to the * constructor and eliminating the dynamic addition * of elements to the list.addListSelectionListener(ll). import javax. } }.length. setLayout(new FlowLayout()).*. "Vanilla Fudge Swirl". 250.*. Border brd = BorderFactory. 1. import static net.getSelectedValues()) t. private JTextArea t = new JTextArea(flavors. Color. lst. 2.setBorder(brd). } public static void main(String[] args) { run(new E16_SimplifyList(). 2.*. lst.setEditable(false). "Rum Raisin".swing. import javax.getValueIsAdjusting()) return. 375).swing. 20). * If the user types in the correct password.INFORMATION_MESSAGE). ***********************************************/ package gui.getPassword()))) message = "Correct Password".getSource(). In the * JDK documentation from http://java.com.SwingConsole.mindview. use * JOptionPane to provide a success message to the * user. public Password() { setLayout(new FlowLayout()). add(new JLabel("Type in your password:")). produced by calling getModel( ).*.showMessageDialog( null. import static net. import java.util. JOptionPane.java /****************** Exercise 17 ****************** * Create an application using SwingConsole.event.awt. it also emphasizes that to access information about the elements in the list you must use the list model. } }). add(pwd). pwd. class Password extends JFrame { JPasswordField pwd = new JPasswordField(10). Exercise 17 //: gui/E17_Password.swing.While this example mainly involves eliminating extra code. "information". JPasswordField pass = (JPasswordField)e.*. find * the JPasswordField and add this to the program. message.sun. JOptionPane. import java.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { String message = "Incorrect Password". import javax. 4th Edition Annotated Solution Guide . if("Blarth".awt.*. } } public class E17_Password { 718 Thinking in Java.*.equals(new String(pass. ***********************************************/ package gui.awt. import javax.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { Object[] options = { "Red".*.java so that it has an * individual ActionListener for each button (instead * of matching the button text).java /****************** Exercise 18 ****************** * Modify MessageBoxes.mindview. import java. "There's a bug on you!". b5 = new JButton("3 Vals"). JOptionPane.YES_NO_OPTION). } }).*. b2 = new JButton("Yes/No"). } }). b3 = new JButton("Color"). b2. "or no". "Green" }.awt.swing. 100). b3. b4 = new JButton("Input").showMessageDialog(null. import static net. private JTextField txt = new JTextField(15). JOptionPane.public static void main(String args[]) { run(new Password().showConfirmDialog(null.*.event. 200. "choose yes". } } ///:~ Exercise 18 //: gui/E18_MessageAction.showOptionDialog( Graphical User Interfaces 719 . "Hey!".ERROR_MESSAGE). int sel = JOptionPane.*.SwingConsole. import java.util. class MessageAction extends JFrame { private JButton b1 = new JButton("Alert").addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { JOptionPane. public MessageAction() { b1.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { JOptionPane. "Choose a Color!".INFORMATION_MESSAGE. if(sel != JOptionPane. add(txt). } } ///:~ Exercise 19 //: gui/E19_RadioMenus. 200. "Input".null. selections. } }). add(b3).setText(val). txt. add(b1). JOptionPane.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { Object[] selections = {"First". JOptionPane. null. add(b4). b5.showInputDialog( "How many fingers do you see?"). options. setLayout(new FlowLayout()). "Second".addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { String val = JOptionPane.showInputDialog( null. } }).WARNING_MESSAGE.CLOSED_OPTION) txt. } }). JOptionPane. options[0]). 200).toString()). "Third"}.setText("Color Selected: " + options[sel]).java /****************** Exercise 19 ****************** 720 Thinking in Java. if(val != null) txt. add(b2).setText(val. add(b5). null. b4. } } public class E18_MessageAction { public static void main(String[] args) { run(new MessageAction(). "Choose one". selections[0]). "Warning". 4th Edition Annotated Solution Guide . Object val = JOptionPane.DEFAULT_OPTION. "Mud Pie" }. private JButton b = new JButton("Swap Menus").getSource(). import static net. private JMenuItem[] other = { new JMenuItem("Foo".util.*. private JMenuItem[] file = { new JMenuItem("Open") }. }. private JRadioButtonMenuItem[] safety = { new JRadioButtonMenuItem("Guard"). new JMenuItem("Baz").* Modify Menus. private JTextField t = new JTextField("No flavor". validate().*. new JRadioButtonMenuItem("Hide") }. "Mint Chip".*. Graphical User Interfaces 721 .mindview.awt. import java. s = new JMenu("Safety").*. // Refresh the frame } } class ML implements ActionListener { public void actionPerformed(ActionEvent e) { JMenuItem target = (JMenuItem)e.getActionCommand(). private JMenuBar mb1 = new JMenuBar().event. private JMenu f = new JMenu("File"). private JMenuBar mb2 = new JMenuBar(). "Vanilla Fudge Swirl".awt. new JMenuItem("Bar". 30). "Praline Cream". "Mocha Almond Fudge". private JMenu fooBar = new JMenu("fooBar"). ***********************************************/ package gui. m = new JMenu("Flavors").getText(). import javax. class BL implements ActionListener { public void actionPerformed(ActionEvent e) { JMenuBar m = getJMenuBar().swing. class RadioMenus extends JFrame { private String[] flavors = { "Chocolate".SwingConsole. import java.VK_A). KeyEvent.VK_F).equals("Open")) { String s = t. "Strawberry". KeyEvent. if(actionCommand. String actionCommand = target. "Rum Raisin". setJMenuBar(m == mb1 ? mb2 : mb1).java to use radio buttons instead * of check boxes on the menus. isSelected()). 722 Thinking in Java.setText("Baz selected"). } } class BarL implements ActionListener { public void actionPerformed(ActionEvent e) { t. } } class CMIL implements ItemListener { public void itemStateChanged(ItemEvent e) { JRadioButtonMenuItem target = (JRadioButtonMenuItem)e.setText("Guard the Ice Cream! " + "Guarding is " + target.equals("Hide")) t. Mmm.getText()).equals("Guard")) t.setText("Bar selected"). } } class BazL implements ActionListener { public void actionPerformed(ActionEvent e) { t.setText("Hide the Ice Cream! " + "Is it hidden? " + target. } } class FooL implements ActionListener { public void actionPerformed(ActionEvent e) { t.setText("Foo selected"). if(!chosen) t. mm!").setText(target. } } } class FL implements ActionListener { public void actionPerformed(ActionEvent e) { JMenuItem target = (JMenuItem)e. else if(actionCommand.getSource().equals(flavor)) chosen = true.boolean chosen = false. for(String flavor : flavors) if(s. else t.setText("Choose a flavor first!").getSource(). t.getActionCommand().isSelected()).setText("Opening " + s + ". } } public RadioMenus() { ML ml = new ML(). String actionCommand = target. 4th Edition Annotated Solution Guide . if(actionCommand. addActionListener(new FooL()).VK_F). fooBar.add(f). f.addSeparator().VK_B). add(t. 200). } for(JRadioButtonMenuItem sfty : safety) s. for(JMenuItem oth : other) fooBar. i++) { file[i].setMnemonic(KeyEvent. add(b.setMnemonic(KeyEvent.addActionListener(ml). other[0].setMnemonic(KeyEvent. b.setMnemonic(KeyEvent. mb2.add(sfty). for(int i = 0. int n = 0. safety[1]. setJMenuBar(mb1).addItemListener(cmil). i < file.addItemListener(cmil). safety[0].add(fooBar). other[1].VK_A). for(String flavor : flavors) { JMenuItem mi = new JMenuItem(flavor).setMnemonic(KeyEvent. mi. safety[1]. BorderLayout. if((n++ + 1) % 3 == 0) m. safety[0].VK_H). f. BorderLayout.add(s). 300.addActionListener(new BL()).addActionListener(new BazL()). f.add(file[i]).CMIL cmil = new CMIL(). safety[1]. mb1.setMnemonic(KeyEvent.VK_S). safety[0].add(m).addActionListener(fl).add(mi). b. } Graphical User Interfaces 723 .NORTH). other[2].VK_G). m. t.add(oth).CENTER). } mb1. FL fl = new FL(). s.setEditable(false). } } public class E19_RadioMenus { public static void main(String[] args) { run(new RadioMenus().setActionCommand("Guard").addActionListener(new BarL()).length.setActionCommand("Hide"). util. // Words starting with a lower case letter: tm2 = new JMenu("Words Starting With LCC"). setJMenuBar(mb).*. // All other words not belonging to the above groups: tm3 = new JMenu("Other Words"). class DynamicMenus extends JFrame { // You may want to change the code to use a command-line // parameter instead of a hard-coded value: private Set<String> words = new TreeSet<String>( new TextFile("E20_DynamicMenus.java". mb. DynamicMenus() { distributeWordsOnMenus(). import java.add(tm1). Distribute those words as labels on menus * and submenus.add(tm3). and all calls to getState( ) with calls to isSelected( ). mb. // The top level menu is fixed: private JMenu // Words starting with an upper case letter: tm1 = new JMenu("Words Starting With UCC"). mb.mindview.util. import javax. ***********************************************/ package gui. 4th Edition Annotated Solution Guide .java /****************** Exercise 20 ****************** * Create a program that breaks a text file into * words. lastGroup = (char)0. JMenu currentMenu.swing.add(tm2). JMenu currentSM = null.*.util. // The current sub-menu 724 Thinking in Java. import net. "\\W+")). } private void distributeWordsOnMenus() { boolean firstInGroup.*.SwingConsole. private JMenuBar mb = new JMenuBar().mindview. then replace all JCheckBoxMenuItems with JRadioButtonMenuItems. char currentGroup.*. Exercise 20 //: gui/E20_DynamicMenus. import static net.} ///:~ We copy the example. break. else if(Character.add(itemToAdd).hasNext(). callNext = true.toString(currentGroup)). // Decide which top level menu to extend: if(Character. if(currentGroup != nextWord. firstInGroup = currentGroup != lastGroup. } if(word.hasNext()) { currentMenu. } nextWord = it. lastGroup = currentGroup. else currentMenu = tm3.charAt(0)) currentMenu. nextWord = null.isUpperCase(currentGroup)) currentMenu = tm1. if(firstInGroup) { // Now we need to see whether we need a new // sub-menu or just a plain menu item: if(!it. boolean callNext = true. // Ignore plain numbers.matches("[0-9]+")) continue.add(itemToAdd).isLowerCase(currentGroup)) currentMenu = tm2. } } Graphical User Interfaces 725 .add(itemToAdd).add(currentSM). else { currentSM = new JMenu(Character. for(Iterator<String> it = words. currentMenu.add(itemToAdd). // Decide whether this item should be put as a menu // item beneath the top menu.iterator().) { if(callNext) word = it. currentSM.String word. it. or under the // corresponding sub-menu: JMenuItem itemToAdd = new JMenuItem(word). } } else currentSM. callNext = false.charAt(0). else { word = nextWord. currentGroup = word.next().next(). 1) * hstep). ***********************************************/ package gui.setColor(Color. class SineDrawBean extends JPanel { private static final int SCALEFACTOR = 200. int x2 = (int)(i * hstep).SwingConsole. 400. } } ///:~ Notice that we group single words as standalone menu items (categorized by their first character) under corresponding top-level menus.RED). i < points.mindview. Exercise 21 //: gui/E21_SineDrawBean.*.awt. public SineDrawBean() { setCycles(5).java program in TIJ4 shows another approach to the solution.util.java /****************** Exercise 21 ****************** * Modify SineWave. import static net. 4th Edition Annotated Solution Guide .*. for(int i = 1. pts = new int[points].*. g. The swt/Menus. double hstep = (double)maxWidth / (double)points. import javax. import java. private int[] pts.swing. int y1 = pts[i-1]. 726 Thinking in Java. for(int i = 0. int maxWidth = getWidth().paintComponent(g). import javax.95 + maxHeight/2). } public void paintComponent(Graphics g) { super.java to turn SineDraw into a * JavaBean by adding "getter" and "setter" methods. private double[] sines. private int cycles. i < points. int maxHeight = getHeight(). private int points.*. 400). i++) pts[i] = (int)(sines[i] * maxHeight/2 * .} public class E20_DynamicMenus { public static void main(String[] args) { run(new DynamicMenus().swing.event. i++) { int x1 = (int)((i . private JSlider adjustCycles = new JSlider(1. adjustCycles). 5).addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent e) { sines. Those dependent variables are calculated on other values within the object. so you can’t allow them to be set from outside of the bean. i < points. adjustCycles. Graphical User Interfaces 727 . add(BorderLayout.getSource()). } }). i++) { double radians = (Math. Be careful when designing beans so that you don’t mistake getters and setters for trivial access methods to variables inside an object. } } class SineWave extends JFrame { private SineDrawBean sines = new SineDrawBean(). y1. but no getters and setters for SCALEFACTOR.setCycles( ((JSlider)e. } repaint(). } public int getCycles() { return cycles. } } public class E21_SineDrawBean { public static void main(String[] args) { run(new SineWave(). for example. sines = new double[points]. 30.drawLine(x1.PI / SCALEFACTOR) * i. y2). } } public void setCycles(int newCycles) { cycles = newCycles. g. sines. 400). public SineWave() { add(sines). points = SCALEFACTOR * cycles * 2. or pts. x2. } } ///:~ There’s a trick to this exercise. sines[i] = Math. 700. points.int y2 = pts[i]. We added only the method getCycles( ).sin(radians). for(int i = 0.SOUTH.getValue()). does much more than modify cycles. setCycles( ). value. green = new ColorSlider("green").Exercise 22 //: gui/E22_ColorMixer. void reColor() { color.event.setBackground(new Color( red. 255.java /****************** Exercise 22 ***************** * Create an application using SwingConsole. import javax. } } ColorSlider red = new ColorSlider("red"). blue = new ColorSlider("blue"). class ColorMixer extends JFrame { class ColorSlider extends JPanel { JSlider slider. 0).getValue(). JPanel color = new JPanel(). import java. public ColorSlider(String title) { super(new FlowLayout()).swing. value = new JTextField(3).*.setText("" + slider. slider.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent e) { value.awt. This * should have three sliders. Also * include non-editable text fields that show the * current RGB values.getValue()).*.swing.*. * green. add(value).setText("" + slider.mindview. reColor().SwingConsole. slider = new JSlider(0.getValue()). add(new JLabel(title)).util.*. import static net. The rest * of the form should be a JPanel that displays the * color determined by the three sliders. and blue values in java. one each for the red.awt. add(slider). ***********************************************/ package gui.slider.Color. 4th Edition Annotated Solution Guide . } }). import javax. JTextField value. 728 Thinking in Java. import java.*. import javax. import javax.*.*.awt. 1)). import static net.getValue(). and a second slider should control the * size of the box. import java. jp.java /****************** Exercise 23 ***************** * Using SineWave.add(red). } } public class E22_ColorMixer { public static void main(String args[]) { run(new ColorMixer(). 300). add a check box that causes the sliders to jump to the next web-safe color when you release them.add(green).add(blue). As an additional exercise. Graphical User Interfaces 729 .slider.event. } } ///:~ The ColorSlider inner class really simplifies and organizes the code.*.SwingConsole. add(jp). Exercise 23 //: gui/E23_RotatingSquare.swing. 100f). 350.*.slider. reColor(). jp. JPanel jp = new JPanel(new FlowLayout()).Float(-50f. add(color). jp.swing. ***********************************************/ package gui. -50f.green.awt. create * a program that displays a rotating square on the * screen. blue. One slider should control the speed of * rotation. 100f.geom.getValue())).java as a starting point. } public ColorMixer() { setLayout(new GridLayout(2. class SquareRotate extends JPanel { private Rectangle2D square = new Rectangle2D.util.mindview. setPaint(Color.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent e) { sq. 4th Edition Annotated Solution Guide . = speed. g2.scale(boxSize / 10.this. public RotatingSquare() { add(sq). public SquareRotate() { setSpeed(5).0) as the center of this g2.0.. private volatile int speed.speed public void setBoxSize(int boxSize) { this.rotate(Math.transform(rot). 5).private AffineTransform rot = new AffineTransform(). boxSize / 10.sleep(1000 / speed). } public void paintComponent(Graphics g) { super. private JSlider adjustBoxSize = new JSlider(1. 10. g2.setSpeed(adjustSpeed. 10). getHeight() / g2. } }).getValue()). adjustSpeed.translate(getWidth() / 2. } class RotatingSquare extends JFrame { private SquareRotate sq = new SquareRotate(). scale = new AffineTransform().repaint().start(). 730 Thinking in Java. Graphics2D g2 = (Graphics2D)g. 20. private int boxSize. try { Thread. // Makes point (0. } } {} canvas 2).boxSize = boxSize.0). } public void setSpeed(int speed) { this. private JSlider adjustSpeed = new JSlider(1. setBoxSize(10). rot.toRadians(20)).blue).draw(square). new Thread(new Runnable() { public void run() { for(. } catch(InterruptedException ignore) } } }).) { SquareRotate.paintComponent(g). g2. add(adjustBoxSize). add(BorderLayout. As an additional exercise. 400. Consult the JDK for more information about the classes used here. Graphical User Interfaces 731 . 1)). try to solve this without the Java 2D API and compare the two versions. 400).swing. } } public class E23_RotatingSquare { public static void main(String args[]) { run(new RotatingSquare(). import javax.java /****************** Exercise 24 ***************** * Remember the "sketching box" toy with two knobs. import javax.event. } }).*. * use sliders. Add a button that will erase the entire * sketch. A dedicated thread controls the speed of rotation.setBoxSize(adjustBoxSize. ***********************************************/ package gui. You can safely call the repaint( ) method from any thread. sliders. sliders. Exercise 24 //: gui/E24_SketchBox. sliders. Notice how the AffineTransform class spares you from complex mathematics by handling all aspects of rotation and scaling.getValue()). * one that controls the vertical movement of the * drawing point.SOUTH.*.setLayout(new GridLayout(2. and one that controls the horizontal * movement? Create a variation of this toy.java to get you started.add(adjustSpeed). } } ///:~ This solution demonstrates the Java 2D API.swing.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent e) { sq. Instead of knobs. JPanel sliders = new JPanel(). using * SineWave. sliders).adjustBoxSize. util. previousPoint = p. class SketchArea extends JPanel { java.x. repaint(). int y) { points. 732 Thinking in Java.*.util. p. int y) { this.y). repaint().List<Point> points = new ArrayList<Point>().import import import import java. y. static net. 4th Edition Annotated Solution Guide .util. } public void paintComponent(Graphics g) { super. } public void clear() { points. previousPoint.*.event. g.paintComponent(g). public Point(int x.awt.SwingConsole. } } public void addPoint(int x.awt. return. for(Point p : points) drawPoint(g. rather // than drawing from 0.*.setColor(Color. } } class SketchBox extends JFrame { SketchArea sketch = new SketchArea(). } Point previousPoint.x.*. class Point { int x. } g. p).mindview. 0: if(previousPoint == null) { previousPoint = p. java. java.clear(). previousPoint = null.red). this. JSlider hAxis = new JSlider().y.drawLine(previousPoint.y)).add(new Point(x.y = y.x = x. void drawPoint(Graphics g. Point p) { // So that it starts from anywhere. p. getValue(). vAxis. hAxis. add(BorderLayout. hAxis).size()). } }).size() = " + sketch.setText( "[Erase] points.vAxis = new JSlider(JSlider. public SketchBox() { add(sketch).points.setValue(0). erase.size()). vAxis.getValue()).addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { sketch. erase. erase.getWidth()).SOUTH. } } public class E24_SketchBox { public static void main(String args[]) { run(new SketchBox(). } }). vAxis. hAxis.clear(). // So vertical axis synchronizes with line: vAxis.setText( "[Erase] points. // The width and height values are zero until // initial resizing (when the component is // first drawn). vAxis. } }. Also takes care of things // if it's resized: addComponentListener(new ComponentAdapter() { public void componentResized(ComponentEvent e) { super.getHeight()). 700.componentResized(e). hAxis.addPoint(hAxis.setMaximum(sketch.points.setMaximum(sketch. } Graphical User Interfaces 733 . erase). add(BorderLayout.addChangeListener(cl).NORTH. 400).setValue(0).VERTICAL).WEST.setInverted(true). add(BorderLayout. ChangeListener cl = new ChangeListener() { public void stateChanged(ChangeEvent e) { sketch. vAxis).size() = " + sketch. JButton erase = new JButton("Erase").addChangeListener(cl). 734 Thinking in Java. This does more than make the program resize properly.*.} ///:~ SketchArea contains a List of Point objects. We also use addComponentListener( ).*.java /****************** Exercise 25 ****************** * Starting with SineWave. using drawPoint( ) to draw a line from each coordinate to the next. We call paintComponent( ) to move through the list. so getWidth( ) and getHeight( ) always return zero inside the constructor. ***********************************************/ package gui. then call componentResized( ) to set the slider maximums.Timer. import static net. import javax. Exercise 25 //: gui/E25_AnimatedSine.TimerTask. The SketchBox has a horizontal JSlider (the default orientation) and a vertical one. import java.*.JSlider control. each of which has an x and y coordinate.swing.java.awt. In fact.util. private int cycles. 4th Edition Annotated Solution Guide .mindview. create a program * (an application using the SwingConsole class) * that draws an animated sine wave that appears to * scroll past the viewing window like an oscilloscope.SwingConsole.util. import java. private double[] sines. the application isn’t sized during construction. The ChangeListener for both JSliders adds a new Point at the current location and changes the text on the JButton to track the number of points in a drawing.swing. We determine the initial application size after object construction. private int points.util. import javax.event. private int[] pts. setInverted( ) on the vAxis draws the line in the direction of the slider.swing.*. The * speed of the animation should be controlled with * a javax. you can’t just call setMaximum( ) in the constructor. * driving the animation with a java. passing it a ComponentAdapter with an overridden componentResized( ). class ExtSineDraw extends JPanel { private static final int SCALEFACTOR = 200. // Incorporate a 1 sec. private double offset. } public synchronized void setCycles(int newCycles) { cycles = newCycles. } } repaint(). private class MyTimerTask extends TimerTask { public void run() { offset += 0.Timer timer = new java. 65). sines = new double[points].PI / SCALEFACTOR) * i.private java. pts = new int[points].25.paintComponent(g). ExtSineDraw() { super(new BorderLayout()). speed. setCycles(5).scheduleAtFixedRate( new MyTimerTask().SOUTH. timer = new java. i++) { double radians = (Math. speed. // So that left to right is slow to fast: speed. for(int i = 0. synchronized(ExtSineDraw. 1000. 115. initial delay to give time // for initialization: timer. } repaint(). } public void paintComponent(Graphics g) { super. i < points.Timer().cancel().util. i < points.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent e) { timer.PI/SCALEFACTOR) * i + offset.sin(radians). points = SCALEFACTOR * cycles * 2.util. add(BorderLayout. } }). 0.getValue()). sines[i] = Math.scheduleAtFixedRate(new MyTimerTask(). Graphical User Interfaces 735 . private JSlider speed = new JSlider(15.this) { for(int i = 0. 65). timer.sin(radians). } }. i++) { double radians = (Math. sines[i] = Math.setInverted(true).util.Timer(). speed). addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent e) { sines. 4th Edition Annotated Solution Guide . synchronized(this) { for(int i = 0.drawLine(x1. } g. 700. i < points. i++) // Some adjustments here to compensate for // the added JSlider in the panel: pts[i] = (int)(sines[i] * maxHeight/2 * . adjustCycles). adjustCycles.1) * hstep). which uses a java. private JSlider adjustCycles = new JSlider(1.89 + maxHeight/2 * .RED). 5). and add the JSlider speed (changing the speed “reprograms” the timer). g. x2.Timer with a run( ) method to drive the animation. 736 Thinking in Java. public ExtSineWave() { add(sines). } } } class ExtSineWave extends JFrame { private ExtSineDraw sines = new ExtSineDraw(). } }).91). 400). 30.setCycles(adjustCycles. i < points. for(int i = 1. y2). We use a BorderLayout in the constructor instead of the default FlowLayout. int x2 = (int)(i * hstep). int y2 = pts[i].int maxWidth = getWidth().setColor(Color. add(BorderLayout. int maxHeight = getHeight(). i++) { int x1 = (int)((i . y1.SOUTH. } } ///:~ Most of the changes are in SineDraw.getValue()). int y1 = pts[i-1]. double hstep = (double)maxWidth / (double)points. } } public class E25_AnimatedSine { public static void main(String[] args) { run(new ExtSineWave().util. add(sines[i]). import java. import static net.swing. 5).*. i++) sines[i].mindview.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent e) { for(int i = 0.*.util. Exercise 26 //: gui/E26_MultipleSine. We use the synchronized clause in run( ) and paintComponent( ) and synchronize setCycles( ) to prevent threading collisions when we change the array sizes. and also increments and adds a value offset each time (this value moves the sine wave forward). sines = new ExtSineDraw[panels]. i < sines.*. ***********************************************/ package gui. import javax.length.java /****************** Exercise 26 ***************** * Modify the previous exercise so that multiple * sine wave panels are created within the * application. JPanel jp = new JPanel(new GridLayout(side.SwingConsole.sqrt((double)panels)).swing. i++) { sines[i] = new ExtSineDraw().awt. public MultiSineWave(int panels) { int side = Math. side)). }}). class MultiSineWave extends JFrame { ExtSineDraw[] sines. } } Graphical User Interfaces 737 . jp. The number of sine wave panels should * be controlled by command-line parameters.event. cycles. import javax.length. for(int i = 0.SOUTH.round( (float)Math. cycles). } add(jp).*. i < sines. JSlider cycles = new JSlider(1.The run( ) method calculates the points as before.setCycles( ((JSlider)e.getSource()). add(BorderLayout. 30.getValue()). length != 0) panels = Integer.parseInt(args[0]). 5). private JSlider speed = new JSlider(0.awt.public class E26_MultipleSine { public static void main(String[] args) { int panels.util.util. class SineDraw_T extends JPanel { private static final int SCALEFACTOR = 200.*.Timer class * is used to drive the animation. import javax. private Timer timer = new Timer(500. if(args.SwingConsole. import java.event.swing. Note the difference * between this and java. 400). else panels = 4. *********************************************************/ package gui.mindview. When you change the number of cycles. run(new MultiSineWave(panels).*.swing.java /*********************** Exercise 27 ********************* * Modify Exercise 25 so that the javax.*.swing. private int cycles. } } ///:~ We use the ExtSineDraw class from the previous exercise. 700. private int points.*. 15. Exercise 27 //: gui/E27_TimerAnimation. import static net. private double[] sines. import javax.event. We get a reasonably square array by rounding up the square root of the desired number of ExtSineDraw objects for both dimensions of a GridLayout. 4th Edition Annotated Solution Guide .Timer. private double offset.awt.*. This solution is fairly rudimentary: you get the argument from the command line (or use a default value of 4) and lay out the panels. import java. new ActionListener() { 738 Thinking in Java. ChangeListener moves through an array of ExtSineDraw objects and calls setCycles( ) for each one. private int[] pts. sines[i] = Math. g.1) * hstep).public void actionPerformed(ActionEvent e) { offset += 0. i < points. } public void setCycles(int newCycles) { cycles = newCycles. speed. SineDraw_T() { super(new BorderLayout()).96). i++) // Some adjustments here to compensate for // the added JSlider in the panel: pts[i] = (int)(sines[i] * maxHeight/2 * .94 + maxHeight/2 * . i < points.setInverted(true). i++) { double radians = (Math.PI/SCALEFACTOR) * i. for(int i = 0. i++) { int x1 = (int)((i .getValue() * 100).paintComponent(g). } repaint().start(). i++) { double radians = (Math. int maxWidth = getWidth().sin(radians). i < points. } public void paintComponent(Graphics g) { super.SOUTH. sines = new double[points]. timer. timer. for(int i = 0. i < points.setDelay(speed. double hstep =(double)maxWidth/(double)points. } }). add(BorderLayout. for(int i = 1.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent e) { timer. for(int i = 0.25. } }). setCycles(5). Graphical User Interfaces 739 . int maxHeight = getHeight().sin(radians).setInitialDelay(1000). pts = new int[points].setColor(Color.PI/SCALEFACTOR) * i + offset.RED). speed). points = SCALEFACTOR * cycles * 2. sines[i] = Math. speed. } repaint(). int y2 = pts[i]. private JSlider cycles = new JSlider(1. x2. since the event-dispatching thread executes the callback. and show the * curve evolving dynamically as you throw more and more * times. int y1 = pts[i-1]. you should not overuse javax. cycles. } } ///:~ javax.Timer.getValue()). } }).Timer repeatedly calls an ActionListener.int x2 = (int)(i * hstep).. 5). Exercise 28 //: gui/E28_DiceTossing. 30. The code is simpler than in Exercise 25 (e. add(BorderLayout.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent e) { sines. public SineWave_T() { add(sines). y2).SOUTH. *********************************************************/ package gui.setCycles(cycles. y1. the handler must execute quickly to keep the GUI responsive.java /*********************** Exercise 28 ********************* * Create a dice class (just a class. Create * five dice and throw them repeatedly. no need for synchronization).*. } } public class E27_TimerAnimation { public static void main(String args[]) { run(new SineWave_T(). 400). g.swing. without a GUI). 740 Thinking in Java. } } } class SineWave_T extends JFrame { private SineDraw_T sines = new SineDraw_T().swing.swing. import javax. 4th Edition Annotated Solution Guide . However. Draw the curve * showing the sum of the dots from each throw. cycles).g.drawLine(x1. As a corollary. 700. Random.util. class Dice { static Random rnd = new Random().mindview. data.drawLine(x2.*. int maxWidth = getWidth(). private int[] data = new int[MAX_DATA_POINTS]. offset. i < offset. i++) pts[i] = (int)((data[i] . x2. // Temporary store for drawing: private int[] pts = new int[MAX_DATA_POINTS]. y1.event. x2. int x2 = (int)(i * hstep). public void addData(int sumOfDots) { // Check whether we need to throw away the first half // of old data: if(offset == MAX_DATA_POINTS) { offset = MAX_DATA_POINTS / 2.import import import import java. double hstep =(double)maxWidth/(double)MAX_DATA_POINTS. java. y1). Graphical User Interfaces 741 . int maxHeight = getHeight(). i < offset.util. int throwDice() { return rnd.setColor(Color. g. g. i++) { int x1 = (int)((i . } data[offset++] = sumOfDots. System. for(int i = 1.SwingConsole. 0.*.awt.GREEN). int y1 = pts[i-1]. int y2 = pts[i].paintComponent(g).drawLine(x1.1) * hstep). g.awt. static net. } public void paintComponent(Graphics g) { super.nextInt(6) + 1. for(int i = 0.arraycopy(data.*. y2). } } class Curve extends JPanel { private static final int MAX_DATA_POINTS = 100. offset). y1.6) * maxHeight/24). } } } class DiceTossing extends JFrame { private final Curve curve = new Curve(). repaint(). java. private int offset. ***********************************************/ package gui. 4th Edition Annotated Solution Guide .awt. } } public class E28_DiceTossing { public static void main(String args[]) { run(new DiceTossing(). new Dice().setInitialDelay(1000).private final Dice[] dices = {new Dice(). 742 Thinking in Java.awt.*.*. 300). public void actionPerformed(ActionEvent e) { sumOfDots = 0. Write a program * with a button that brings up the color * chooser as a dialog.throwDice().SwingConsole. import java. curve. Exercise 29 //: gui/E29_ColorChooser. } }). timer.swing. 500.*. for(Dice dice : dices) sumOfDots += dice.start(). import java.util. new Dice().*.event. new ActionListener() { int sumOfDots. public DiceTossing() { add(curve). the timer drives the dice tossing simulation. initial delay: timer. // A "standard" 1 sec.java program. } } ///:~ Here.swing. import javax. Everything basically follows the structure of the SineWave. new Dice()}. and calls addData( ) at each iteration.mindview.addData(sumOfDots). new Dice(). The Curve uses the generated data to draw the curve showing the sum of the dice from each throw. * look up the JColorChooser.java /****************** Exercise 29 ***************** * In the JDK documentation for javax. // Simulates dice tossing: private Timer timer = new Timer(100. import static net. * JMenuItem. class HTMLComponents extends JFrame { JMenu[] menus = { new JMenu("Text"). import static net.*.class ColorChooser extends JFrame { JButton b1 = new JButton("Color Chooser").showDialog( null.*. use the color display panel from Exercise22 to show the resulting color after the dialog is closed. } }). public ColorChooser() { setLayout(new FlowLayout()).awt. Color. and * JCheckBox. "E29_ColorChooser".mindview.SwingConsole. ***********************************************/ package gui. new JMenuItem("<html><b><font size=+1>HTML Item") }.swing.util. } } public class E29_ColorChooser { public static void main(String args[]) { run(new ColorChooser()." * Write a program that shows the use of HTML text * on all the items from the previous paragraph.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { JColorChooser.*. } } ///:~ As an additional exercise. new JMenu("<html><i>HTML") }. 150. import java. JMenuItem[] items = { new JMenuItem("Text Item"). import javax.cyan). JToolTip. 75). add(b1). JRadioButton. b1.java /****************** Exercise 30 ***************** * "You can also use HTML text for JTabbedPane. Exercise 30 //: gui/E30_HTMLComponents. public HTMLComponents() { Graphical User Interfaces 743 . util. import java. } } public class E30_HTMLComponents { public static void main(String args[]) { run(new HTMLComponents().*. mb.*.add(menus[0]). import static net. Add random erratic behavior so * it will periodically look like it's starting * to speed up.mindview. new JRadioButton("<html><i>Click me!")).awt. 4th Edition Annotated Solution Guide . mb. } } ///:~ Exercise 31 //: gui/E31_WindozeProgress. tabs.swing.add(menus[1]).event. menus[1]. 200). class WindozeProgress extends JFrame { JProgressBar pb = new JProgressBar(0.java /****************** Exercise 31 ***************** * Create an "asymptotic progress indicator" that * gets slower and slower as it approaches the * finish point. import java. 100). menus[0]. new ActionListener() { public void actionPerformed(ActionEvent e) { 744 Thinking in Java. JMenuBar mb = new JMenuBar(). JTabbedPane tabs = new JTabbedPane(). add(tabs).setLayout(new FlowLayout()).add(items[1]). setJMenuBar(mb).addTab("<html><font size=-1>Tab 2".*. import javax.setToolTipText( "<html><center>Dummy Item<br><i>No action"). tabs. new JCheckBox("<html><u>Check me!")).*. items[1].addTab("<html><font size=+1>Tab 1". 180. ***********************************************/ package gui. Timer tm = new Timer(50.awt.SwingConsole. JLabel jl = new JLabel("Makin' some progress now!").add(items[0]). awt. 300.25)).stop(). ***********************************************/ package gui. import javax.getDelay() * (1.if(pb.getValue() == pb.07))). } }).25) pb. was it?").*.random() < 0. tm. tm.java /****************** Exercise 32 ***************** * Modify Progress.0 + percent * 0. but instead uses a listener to connect * the slider and progress bar. add(jl).setDelay((int)(tm.getMaximum() * 0. } } ///:~ We control the progress bar with a Timer . the program chooses a random number between zero and one. jl.*. import java.setValue(pb. } } public class E31_WindozeProgress { public static void main(String args[]) { run(new WindozeProgress(). pb.getValue() + 1). then decrements the progress bar by 10 units if it’s less than 0. add(pb). } double percent = (double)pb.setValue(pb. public WindozeProgress() { setLayout(new FlowLayout()). if(percent > 0. Graphical User Interfaces 745 . Exercise 32 //: gui/E32_SharedListener. If you’re closer than 90%.java so that it does not share * models.getMaximum()) { tm.getValue() . with a delay adjusted according to your proximity to finishing.90) if(Math.start().setValue((int)(pb.getMaximum().swing.getValue() / (double)pb. pb. 100).25.10).setText("That wasn't so bad. import javax.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent e) { pb. Exercise 33 //: gui/E33_ParallelCallables.java /****************** Exercise 33 ***************** * Modify InterruptableLongRunningCallable.swing.getModel()). We also remove ProgressMonitor because it is irrelevant.import javax.getValue()).SwingConsole.setBorder(new TitledBorder("Slide Me")). public Progress() { setLayout(new GridLayout(2. import java. sb. 4th Edition Annotated Solution Guide . JSlider sb = new JSlider(JSlider.swing. add(pb).setPaintTicks(true). sb. 60). } } ///:~ We just comment out setModel( ) and add ChangeListener.HORIZONTAL. 200). } } public class E32_SharedListener { public static void main(String args[]) { run(new Progress().setValue(sb. sb.setValue(0). sb. import javax.setMajorTickSpacing(20).util. sb. 100. 0.border.awt. ***********************************************/ package gui.*. import static net. } }).java so * that it runs all the tasks in parallel rather * than sequentially.1)). class Progress extends JFrame { JProgressBar pb = new JProgressBar(). // Share model add(sb).setMinorTickSpacing(5).mindview.event. 746 Thinking in Java.*.*. 300.swing. // pb.*. sb.*.setModel(sb. while(items.submit(task).util.awt.C> item = items. java.next().util. List<R> results = new ArrayList<R>(). if(item.util. } catch(Exception e) { throw new RuntimeException(e). List<String> results = new ArrayList<String>().List. java.second).C>> items = iterator(). net. } items.isDone()) { try { results.first.import import import import import import import java. return "Return value of " + this.first. static net.first. class CallableTask extends Task implements Callable<String> { public String call() { run(). while(items.C>> { private ExecutorService exec = Executors. if(!item.hasNext()) { TwoTuple<Future<R>.util.cancel(true). item.C> item = items.hasNext()) { TwoTuple<Future<R>.C extends Callable<R>> extends ArrayList<TwoTuple<Future<R>.newCachedThreadPool(). public void add(C task) { add(new TwoTuple<Future<R>.task)).ArrayList.mindview.isDone()) { results. } public List<R> getResults() { Iterator<TwoTuple<Future<R>. } } class TaskManager<R. } public List<String> purge() { Iterator<TwoTuple<Future<R>.mindview.concurrent.get()).Iterator.event.next().*.remove(). // May interrupt Graphical User Interfaces 747 .util.add("Cancelling " + item.*.SwingConsole.add(item.first. java. java. } } return results.C>(exec.C>> items = iterator().TwoTuple.*.util. } } ///:~ 748 Thinking in Java.CallableTask> tt : manager) tt. b3.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { CallableTask task = new CallableTask().remove(). } }).addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { for(String result : manager. b2 = new JButton("End Long Running Task"). } } public class E33_ParallelCallables extends JFrame { private JButton b1 = new JButton("Start Long Running Task"). manager.println(result). private TaskManager<String. public E33_ParallelCallables() { b1.id().purge()) System.println(result). // No cast required for(String result : manager.CallableTask> manager = new TaskManager<String. } } return results.println(task + " added to the queue").getResults()) System. b3 = new JButton("Get results"). System. } }). add(b3).addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { for(TwoTuple<Future<String>. 200.out.add(task). } public static void main(String[] args) { run(new E33_ParallelCallables(). 4th Edition Annotated Solution Guide .out. setLayout(new FlowLayout()). 150). } }). add(b1). add(b2).second.out.CallableTask>(). b2.items. int pause) { this. x = new int[num_points * 2]. public JStar(int num_points. static Random rand = new Random(). private final double[] dx.num_points = num_points.java /****************** Exercise 34 ***************** * Modify ColorBoxes. import java.util.*. import javax.util.mindview. dy[i] = Math.*. int height = getHeight(). Exercise 34 //: gui/E34_ColorStars. class JStar extends JComponent implements Runnable { // The number of points on the star: private final int num_points.cos(delta * i). } } public void paintComponent(Graphics g) { int width = getWidth(). for(int i = 0.SwingConsole.swing. this.awt. import java. // Cache calculated data: double delta = Math.*. i < num_points * 2.sin(delta * i). private final int pause.*. private final double[] dy. dx = new double[num_points * 2].concurrent.util. Graphical User Interfaces 749 . * then randomly changes the colors of those "stars.java so that it begins by * sprinkling points ("stars") across the canvas.*.pause = pause. private final int[] y." ***********************************************/ package gui. private Color color = new Color(0). dy = new double[num_points * 2]. i++) { dx[i] = Math. y = new int[num_points * 2]. // Fields used by the drawing algorithm: private final int[] x. import static net.The crucial (and only) change is a different Executor in TaskManager. import java.PI / num_points. for(int i = 0. respectively: int outerRing.nextInt(12) + 3.y . // The distance from the centre to points of the star // and to the inner core of the star. 4th Edition Annotated Solution Guide .fillPolygon(x . private static ExecutorService exec = Executors. num_points * 2). // Asynchronously request a paint() TimeUnit. i++) { JStar js = new JStar(JStar.rand. y[i] = centreY + (int)(d * dy[i]).sleep(pause). 750 Thinking in Java.interrupted()) { color = new Color(rand. x[i] = centreX + (int)(d * dx[i]). innerRing. if(width < height) { innerRing = width / 8. outerRing = height / 2.// The coordinates of the centre of the star: int centreX = width / 2. } } catch(InterruptedException e) { // Acceptable way to exit } } } public class E34_ColorStars extends JFrame { private static int grid = 12. i++) { d = (i % 2 == 0) ? innerRing : outerRing. outerRing = width / 2. } g. public E34_ColorStars() { setLayout(new GridLayout(grid.newCachedThreadPool(). for(int i = 0. } else { innerRing = height / 8. add(js). grid)).setColor(color). g.execute(js). exec. pause). int centreY = height / 2. i < num_points * 2.nextInt(0xFFFFFF)). } int d. private static int pause = 50.MILLISECONDS. repaint(). i < grid * grid. } public void run() { try { while(!Thread. class to the manifest file in this * section and run jar to create a JAR file containing * both Frog and BangBean.length > 0) E34_ColorStars.length > 1) E34_ColorStars.java /****************** Exercise 35 ****************** * Locate and download one or more of the free * GUI builder development environments available * on the Internet. Now either download and * install the Bean Builder from Sun.grid = new Integer(args[0]). Discover what is necessary to add * BangBean to this environment and to use it. ***********************************************/ Graphical User Interfaces 751 . or use a commercial product if * you own one. ***********************************************/ package gui. 500. if(args.println("Left to the reader").pause = new Integer(args[1]). or use your * own Beans-enabled program builder tool and add * the JAR file to your environment so you can test * the two Beans. } } ///:~ Exercise 36 //: gui/E36_LeftToReader. 400). run(stars.fillPolygon( ) method to visualize them. } } ///:~ The paintComponent( ) uses an algorithm to draw the stars and the Graphics. E34_ColorStars stars = new E34_ColorStars().} } public static void main(String[] args) { if(args. public class E35_LeftToReader { public static void main(String args[]) { System.java /****************** Exercise 36 ****************** * Add Frog.out. Exercise 35 //: gui/E35_LeftToReader. 100). int level) { this. } public int getLevel() { return level.level = level.out.level = level. private int level. then load it into * the Bean Builder or into a Beans-enabled * program builder tool so that you can test it.println("v. run the following command // in directory gui: // java gui.valve. } public void setOn(boolean on) { this. * use jar to package your Bean. } public boolean isOn() { return on. } public void setLevel(int level) { this.jar E37_Valve. this.Serializable { private boolean on. public class E37_Valve implements java. ***********************************************/ // To test the program. System.getLevel()). } } ///:~ Exercise 37 //: gui/valve/E37_Valve.io.valve.on = on. public class E36_LeftToReader { public static void main(String args[]) { System.on = on. } public static void main(String args[]) { E37_Valve v = new E37_Valve(true.isOn() = " + v.println("v.println("Left to the reader"). } } ///:~ 752 Thinking in Java. public E37_Valve() {} public E37_Valve(boolean on. 4th Edition Annotated Solution Guide . System.package gui." Create a manifest file. run the following command // in the directory above valve: // jar cfm E37_Valve.E37_Valve // To make the jar file.mf valve package gui.isOn()).out.java /****************** Exercise 37 ***************** * Create your own JavaBean called Valve that * contains two properties: a boolean called "on" * and an int called "level.out.getLevel() = " + v. println("Left to the reader").out. Find * some MP3s and JPGs. Exercise 38 //: gui/E38_LeftToReader.java. public class E38_LeftToReader { public static void main(String args[]) { System.println("Left to the reader"). ***********************************************/ package gui.out.java.class Java-Bean: True ///:~ Run the jar command from the root of the code tree using the commands shown as comments in E37_Valve. Validate the resulting jar file by loading it into any Beans-enabled editor (we use Sun’s Bean Builder).mf Manifest-Version: 1.java to * include their file names.Note the organization of the file inside its package. public class E39_LeftToReader { public static void main(String args[]) { System. modify SongService. ***********************************************/ package gui.0 Name: valve/E37_Valve. Here is the manifest file: //:! gui/E37_Valve.java /****************** Exercise 39 ****************** * The code download for this book does not include * the MP3s or JPGs shown in SongService.java /****************** Exercise 38 ****************** * Build the "simple example of data binding syntax" * shown above (see the book). } } ///:~ Graphical User Interfaces 753 . download the Flex trial * and build the application. } } ///:~ Exercise 39 //: gui/E39_LeftToReader. eclipse.java // {Requires: org. You must // install the SWT library from http://www.io.setLayout(new FillLayout()). StringWriter props = new StringWriter().widgets. 4th Edition Annotated Solution Guide .util. import org. 400.layout.*.getProperties(). You must // install the SWT library from http://www.list(new PrintWriter(props)). import org.eclipse. } } ///:~ Exercise 41 //: gui/E41_DisplayEnvironment2.swt.Display.java so that it does * not use SWTConsole.layout. import org. ***********************************************/ package gui. import org.util.widgets.eclipse. SWT.*.*.Display.org } /****************** Exercise 41 ****************** * Modify DisplayEnvironment.eclipse. 300).run(new E40_DisplayProperties2().swt.*.swt.WRAP | SWT.swt.*. import swt.swt.Exercise 40 //: gui/E40_DisplayProperties2.swt.*. System.widgets. text. 754 Thinking in Java. Text text = new Text(parent.setText(props.*.V_SCROLL).eclipse.eclipse. import java.eclipse. import java. ***********************************************/ package gui.toString()).swt.*.widgets. import org.java so that it uses * SWTConsole.*.swt.org } /****************** Exercise 40 ****************** * Modify DisplayProperties.eclipse.java // {Requires: org. } public static void main(String [] args) { SWTConsole.eclipse. import org.eclipse. public class E40_DisplayProperties2 implements SWTApplication { public void createContents(Composite parent) { parent. util. import swt.mindview. for(Map.swt. shell.gc.swt.eclipse.java // {Requires: org.getValue() + "\n").*.Display.dispose().setBackground(color). shell.WRAP | SWT.swt.util.entrySet()) { text." ***********************************************/ package gui. class CStar extends Canvas implements Runnable { class CStarPaintListener implements PaintListener { public void paintControl(PaintEvent e) { Color color = new Color(e. SWT.isDisposed()) if(!display. import org.java so that it begins by * sprinkling points ("stars") across the canvas.graphics. import org.swt.events.display. import java. while(!shell.*.*. import org.org } /****************** Exercise 42 ****************** * Modify swt/ColorBoxes.setText("Display Environment").layout. You must // install the SWT library from http://www.append(entry.eclipse.swt. } } ///:~ Exercise 42 //: gui/E42_ColorStars2.widgets. display. import org.util.open(). e. import org.concurrent.Entry<String. import java. import net.eclipse.*. Text text = new Text(shell. cColor).public class E41_DisplayEnvironment2 { public static void main(String [] args) { Display display = new Display().eclipse. Graphical User Interfaces 755 .V_SCROLL).*. * then randomly changes the colors of those "stars. Shell shell = new Shell(display).eclipse.*.eclipse.sleep().*.eclipse. String> entry: System.*.getenv().getKey() + ": " + entry.*.swt.widgets.util. } shell.setLayout(new FillLayout()).readAndDispatch()) display. private final double[] dy. this. int pause) { super(parent. rand. private static RGB newColor() { return new RGB(rand.nextInt(255). respectively: int outerRing.gc. // The distance from the centre to points of the star // and to the inner core of the star.NONE).x. public CStar(Composite parent. innerRing. int centreY = height / 2. rand. private final int num_points. i < num_points * 2.fillPolygon(p). p[idx] = centreX + (int)(d * dx[i]). private final double[] dx.y.dispose(). idx = i * 2.nextInt(255)). } private final int pause. // The coordinates of the centre of the star: int centreX = width / 2. outerRing = width / 2. outerRing = height / 2. SWT.pause = pause. 756 Thinking in Java. for(int i = 0. p[idx + 1] = centreY + (int)(d * dy[i]). int width = size. this. private RGB cColor = newColor(). } else { innerRing = height / 8.Point size = getSize(). } e. int idx. // Fields used by the drawing algorithm: private final int[] p. p = new int[num_points * 4]. addPaintListener(new CStarPaintListener()). if(width < height) { innerRing = width / 8. 4th Edition Annotated Solution Guide . int num_points. i++) { d = (i % 2 == 0) ? innerRing : outerRing. } int d. int height = size. color. } } static Random rand = new Random().num_points = num_points.nextInt(255). rand. // Cache calculated data: double delta = Math. getDisplay(). cs.nextInt(12) + 3. ExecutorService exec = new DaemonThreadPoolExecutor().sleep(pause).setLayout(gridLayout).asyncExec(new Runnable() { public void run() { try { redraw(). dy[i] = Math. i < (grid * grid). dy = new double[num_points * 2].sin(delta * i). for(int i = 0.verticalSpacing = 0.horizontalSpacing = 0. } } catch(InterruptedException e) { // Acceptable way to exit } catch(SWTException e) { // Acceptable way to exit: our parent // was terminated from under us. } }). TimeUnit.interrupted()) { cColor = newColor(). gridLayout. private int pause = 50. i < num_points * 2. public void createContents(Composite parent) { GridLayout gridLayout = new GridLayout(grid.MILLISECONDS.execute(cs).setLayoutData(new GridData(GridData.cos(delta * i).FILL_BOTH)). for(int i = 0. pause). gridLayout. } } Graphical User Interfaces 757 . CStar. parent. i++) { final CStar cs = new CStar(parent.dx = new double[num_points * 2]. i++) { dx[i] = Math. } catch(SWTException e) {} // SWTException is OK when the parent // is terminated from under us. true). } } public void run() { try { while(!Thread.PI / num_points. exec. } } } public class E42_ColorStars2 implements SWTApplication { private int grid = 12. 400).pause = new Integer(args[1]).println("Left to the reader").length > 1) stars. and translate it to * SWT.java /****************** Exercise 43 ****************** * Choose any one of the Swing examples that wasn't * translated in this section. 4th Edition Annotated Solution Guide . In this respect.grid = new Integer(args[0]). if(args. ***********************************************/ package gui. public class E43_LeftToReader { public static void main(String args[]) { System. Exercise 43 //: gui/E43_LeftToReader.run(stars. if(args.public static void main(String[] args) { E42_ColorStars2 stars = new E42_ColorStars2().out. the Swing version seems better. } } ///:~ Notice how the performance degrades as you increase the grid. SWTConsole.length > 0) stars. 500. } } ///:~ 758 Thinking in Java.