Main Core Java Volume II - Advanced Features.

Core Java Volume II - Advanced Features.

Core Java has long been recognized as the leading, no-nonsense tutorial and reference for experienced programmers who want to write robust Java code for real-world applications. Now, Core Java, Volume II—Advanced Features, Eleventh Edition, has been updated for Java 11, with up-to-date coverage of advanced UI and enterprise programming, networking, security, and Java’s powerful new module system.



Cay S. Horstmann explores sophisticated new language and library features with the depth and completeness that readers expect from Core Java. He demonstrates how to use these features to build professional-quality applications, using thoroughly tested examples that reflect modern Java style and best practices, including modularization. Horstmann’s examples are carefully crafted for easy understanding and maximum practical value, so you can consistently use them to jump-start your own code.

Master advanced techniques, idioms, and best practices for writing superior Java code

Take full advantage of modern Java I/O APIs, object serialization, and regular expressions

Efficiently connect to network services, implement network clients and servers, and harvest web data

Query databases and manage database connections with the latest version of JDBC

Simplify all aspects of date and time programming with the Java Date and Time API

Write internationalized programs that localize dates, times, numbers, text, and GUIs

Process code in three powerful ways: the scripting API, compiler API, and annotation processing

Learn how to migrate legacy code to the Java Platform Module System

Leverage the modern Java security features most valuable to application programmers

Program advanced client-side user interfaces, and generate images on the server

Use JNI to interoperate with native C code

See Core Java, Volume I—Fundamentals, Eleventh Edition (ISBN-13: 978-0-13-516630-7), for expert coverage of fundamental Java and UI programming, including objects, generics, collections, lambda expressions, Swing design, concurrency, and functional programming.
Volume: II
Year: 2019
Edition: 11th - Vol II
Publisher: Prentice Hall
Language: english
ISBN: ISBN-13: 978-0135166314
Series: 11th Edition
File: EPUB, 88.77 MB
Download (epub, 88.77 MB)

You may be interested in

 

Most frequently terms

 
 
You can write a book review and share your experiences. Other readers will always be interested in your opinion of the books you've read. Whether you've loved the book or not, if you give your honest and detailed thoughts then people will find new books that are right for them.
Contents


Cover Page

Title Page

Copyright Page

Contents

Preface

Acknowledgments

Chapter 1: Streams 1.1 From Iterating to Stream Operations

1.2 Stream Creation

1.3 The filter, map, and flatMap Methods

1.4 Extracting Substreams and Combining Streams

1.5 Other Stream Transformations

1.6 Simple Reductions

1.7 The Optional Type 1.7.1 Getting an Optional Value

1.7.2 Consuming an Optional Value

1.7.3 Pipelining Optional Values

1.7.4 How Not to Work with Optional Values

1.7.5 Creating Optional Values

1.7.6 Composing Optional Value Functions with flatMap

1.7.7 Turning an Optional into a Stream





1.8 Collecting Results

1.9 Collecting into Maps

1.10 Grouping and Partitioning

1.11 Downstream Collectors

1.12 Reduction Operations

1.13 Primitive Type Streams

1.14 Parallel Streams





Chapter 2: Input and Output 2.1 Input/Output Streams 2.1.1 Reading and Writing Bytes

2.1.2 The Complete Stream Zoo

2.1.3 Combining Input/Output Stream Filters

2.1.4 Text Input and Output

2.1.5 How to Write Text Output

2.1.6 How to Read Text Input

2.1.7 Saving Objects in Text Format

2.1.8 Character Encodings





2.2 Reading and Writing Binary Data 2.2.1 The DataInput and DataOutput interfaces

2.2.2 Random-Access Files

2.2.3 ZIP Archives





2.3 Object Input/Output Streams and Serialization 2.3.1 Saving and Loading Serializable Objects

2.3.2 Understanding the Object Serialization File Format

2.3.3 Modifying the Default Serialization Mechanism

2.3.4 Serializing Singletons and Typesafe Enumerations

2.3.5 Versioning

2.3.6 Using Serialization for Cloning





2.4 Working with Files 2.4.1 Paths

2.4.2 Reading and Writing Files

2.4.3 Creating Files and Directories

2.4.4 Copying, Moving, and Deleting Files

2.4.5 Getting File Information

2.4.6 Visiting Directory Entries

2.4.7 Using Directory Streams

2.4.8 ZIP File Systems





2.5 Memory-Mapped Files 2.5.1 Memory-Mapped File Performance

2.5.2 The Buffer Data Structure





2.6 File Locking

2.7 Regular Expressions 2.7.1 The Regular Expression Syntax

2.7.2 Matching a String

2.7.3 Finding Multiple Matches

2.7.4 Splitting along Delimiters

2.7.5 Replacing Matches





Chapter 3: XML 3.1 Introducing XML

3.2 The Structure of an XML Document

3.3 Parsing an XML Document

3.4 Validating XML Documents 3.4.1 Document Type Definitions

3.4.2 XML Schema

3.4.3 A Practical Example





3.5 Locating Information with XPath

3.6 Using Namespaces

3.7 Streaming Parsers 3.7.1 Using the SAX Parser

3.7.2 Using the StAX Parser





3.8 Generating XML Documents 3.8.1 Documents without Namespaces

3.8.2 Documents with Namespaces

3.8.3 Writing Documents

3.8.4 Writing an XML Document with StAX

3.8.5 An Example: Generating an SVG File





3.9 XSL Transformations





Chapter 4: Networking 4.1 Connecting to a Server 4.1.1 Using Telnet

4.1.2 Connecting to a Server with Java

4.1.3 Socket Timeouts

4.1.4 Internet Addresses





4.2 Implementing Servers 4.2.1 Server Sockets

4.2.2 Serving Multiple Clients

4.2.3 Half-Close

4.2.4 Interruptible Sockets





4.3 Getting Web Data 4.3.1 URLs and URIs

4.3.2 Using a URLConnection to Retrieve Information

4.3.3 Posting Form Data





4.4 The HTTP Client

4.5 Sending E-Mail





Chapter 5: Database Programming 5.1 The Design of JDBC 5.1.1 JDBC Driver Types

5.1.2 Typical Uses of JDBC





5.2 The Structured Query Language

5.3 JDBC Configuration 5.3.1 Database URLs

5.3.2 Driver JAR Files

5.3.3 Starting the Database

5.3.4 Registering the Driver Class

5.3.5 Connecting to the Database





5.4 Working with JDBC Statements 5.4.1 Executing SQL Statements

5.4.2 Managing Connections, Statements, and Result Sets

5.4.3 Analyzing SQL Exceptions

5.4.4 Populating a Database





5.5 Query Execution 5.5.1 Prepared Statements

5.5.2 Reading and Writing LOBs

5.5.3 SQL Escapes

5.5.4 Multiple Results

5.5.5 Retrieving Autogenerated Keys





5.6 Scrollable and Updatable Result Sets 5.6.1 Scrollable Result Sets

5.6.2 Updatable Result Sets





5.7 Row Sets 5.7.1 Constructing Row Sets

5.7.2 Cached Row Sets





5.8 Metadata

5.9 Transactions 5.9.1 Programming Transactions with JDBC

5.9.2 Save Points

5.9.3 Batch Updates

5.9.4 Advanced SQL Types





5.10 Connection Management in Web and Enterprise Applications





Chapter 6: The Date and Time API 6.1 The Time Line

6.2 Local Dates

6.3 Date Adjusters

6.4 Local Time

6.5 Zoned Time

6.6 Formatting and Parsing

6.7 Interoperating with Legacy Code





Chapter 7: Internationalization 7.1 Locales 7.1.1 Why Locales?

7.1.2 Specifying Locales

7.1.3 The Default Locale

7.1.4 Display Names





7.2 Number Formats 7.2.1 Formatting Numeric Values

7.2.2 Currencies





7.3 Date and Time

7.4 Collation and Normalization

7.5 Message Formatting 7.5.1 Formatting Numbers and Dates

7.5.2 Choice Formats





7.6 Text Input and Output 7.6.1 Text Files

7.6.2 Line Endings

7.6.3 The Console

7.6.4 Log Files

7.6.5 The UTF-8 Byte Order Mark

7.6.6 Character Encoding of Source Files





7.7 Resource Bundles 7.7.1 Locating Resource Bundles

7.7.2 Property Files

7.7.3 Bundle Classes





7.8 A Complete Example





Chapter 8: Scripting, Compiling, and Annotation Processing 8.1 Scripting for the Java Platform 8.1.1 Getting a Scripting Engine

8.1.2 Script Evaluation and Bindings

8.1.3 Redirecting Input and Output

8.1.4 Calling Scripting Functions and Methods

8.1.5 Compiling a Script

8.1.6 An Example: Scripting GUI Events





8.2 The Compiler API 8.2.1 Invoking the Compiler

8.2.2 Launching a Compilation Task

8.2.3 Capturing Diagnostics

8.2.4 Reading Source Files from Memory

8.2.5 Writing Byte Codes to Memory

8.2.6 An Example: Dynamic Java Code Generation





8.3 Using Annotations 8.3.1 An Introduction into Annotations

8.3.2 An Example: Annotating Event Handlers





8.4 Annotation Syntax 8.4.1 Annotation Interfaces

8.4.2 Annotations

8.4.3 Annotating Declarations

8.4.4 Annotating Type Uses

8.4.5 Annotating this





8.5 Standard Annotations 8.5.1 Annotations for Compilation

8.5.2 Annotations for Managing Resources

8.5.3 Meta-Annotations





8.6 Source-Level Annotation Processing 8.6.1 Annotation Processors

8.6.2 The Language Model API

8.6.3 Using Annotations to Generate Source Code





8.7 Bytecode Engineering 8.7.1 Modifying Class Files

8.7.2 Modifying Bytecodes at Load Time





Chapter 9: The Java Platform Module System 9.1 The Module Concept

9.2 Naming Modules

9.3 The Modular “Hello, World!” Program

9.4 Requiring Modules

9.5 Exporting Packages

9.6 Modular JARs

9.7 Modules and Reflective Access

9.8 Automatic Modules

9.9 The Unnamed Module

9.10 Command-Line Flags for Migration

9.11 Transitive and Static Requirements

9.12 Qualified Exporting and Opening

9.13 Service Loading

9.14 Tools for Working with Modules





Chapter 10: Security 10.1 Class Loaders 10.1.1 The Class-Loading Process

10.1.2 The Class Loader Hierarchy

10.1.3 Using Class Loaders as Namespaces

10.1.4 Writing Your Own Class Loader

10.1.5 Bytecode Verification





10.2 Security Managers and Permissions 10.2.1 Permission Checking

10.2.2 Java Platform Security

10.2.3 Security Policy Files

10.2.4 Custom Permissions

10.2.5 Implementation of a Permission Class





10.3 User Authentication 10.3.1 The JAAS Framework

10.3.2 JAAS Login Modules





10.4 Digital Signatures 10.4.1 Message Digests

10.4.2 Message Signing

10.4.3 Verifying a Signature

10.4.4 The Authentication Problem

10.4.5 Certificate Signing

10.4.6 Certificate Requests

10.4.7 Code Signing





10.5 Encryption 10.5.1 Symmetric Ciphers

10.5.2 Key Generation

10.5.3 Cipher Streams

10.5.4 Public Key Ciphers





Chapter 11: Advanced Swing and Graphics 11.1 Tables 11.1.1 A Simple Table

11.1.2 Table Models

11.1.3 Working with Rows and Columns 11.1.3.1 Column Classes

11.1.3.2 Accessing Table Columns

11.1.3.3 Resizing Columns

11.1.3.4 Resizing Rows

11.1.3.5 Selecting Rows, Columns, and Cells

11.1.3.6 Sorting Rows

11.1.3.7 Filtering Rows

11.1.3.8 Hiding and Displaying Columns





11.1.4 Cell Rendering and Editing 11.1.4.1 Rendering Cells

11.1.4.2 Rendering the Header

11.1.4.3 Editing Cells

11.1.4.4 Custom Editors





11.2 Trees 11.2.1 Simple Trees 11.2.1.1 Editing Trees and Tree Paths





11.2.2 Node Enumeration

11.2.3 Rendering Nodes

11.2.4 Listening to Tree Events

11.2.5 Custom Tree Models





11.3 Advanced AWT 11.3.1 The Rendering Pipeline

11.3.2 Shapes 11.3.2.1 The Shape Class Hierarchy

11.3.2.2 Using the Shape Classes





11.3.3 Areas

11.3.4 Strokes

11.3.5 Paint

11.3.6 Coordinate Transformations

11.3.7 Clipping

11.3.8 Transparency and Composition





11.4 Raster Images 11.4.1 Readers and Writers for Images 11.4.1.1 Obtaining Readers and Writers for Image File Types

11.4.1.2 Reading and Writing Files with Multiple Images





11.4.2 Image Manipulation 11.4.2.1 Constructing Raster Images

11.4.2.2 Filtering Images





11.5 Printing 11.5.1 Graphics Printing

11.5.2 Multiple-Page Printing

11.5.3 Print Services

11.5.4 Stream Print Services

11.5.5 Printing Attributes





Chapter 12: Native Methods 12.1 Calling a C Function from a Java Program

12.2 Numeric Parameters and Return Values

12.3 String Parameters

12.4 Accessing Fields 12.4.1 Accessing Instance Fields

12.4.2 Accessing Static Fields





12.5 Encoding Signatures

12.6 Calling Java Methods 12.6.1 Instance Methods

12.6.2 Static Methods

12.6.3 Constructors

12.6.4 Alternative Method Invocations





12.7 Accessing Array Elements

12.8 Handling Errors

12.9 Using the Invocation API

12.10 A Complete Example: Accessing the Windows Registry 12.10.1 Overview of the Windows Registry

12.10.2 A Java Platform Interface for Accessing the Registry

12.10.3 Implementation of Registry Access Functions as Native Methods





Index

Credits

Code Snippets





Page List


i

ii

iii

iv

v

vi

vii

viii

ix

x

xi

xii

xiii

xiv

xv

xvi

xvii

xviii

xix

xx

xxi

xxii

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

259

260

261

262

263

264

265

266

267

268

269

270

271

272

273

274

275

276

277

278

279

280

281

282

283

284

285

286

287

288

289

290

291

292

293

294

295

296

297

298

299

300

301

302

303

304

305

306

307

308

309

310

311

312

313

314

315

316

317

318

319

320

321

322

323

324

325

326

327

328

329

330

331

332

333

334

335

336

337

338

339

340

341

342

343

344

345

346

347

348

349

350

351

352

353

354

355

356

357

358

359

360

361

362

363

364

365

366

367

368

369

370

371

372

373

374

375

376

377

378

379

380

381

382

383

384

385

386

387

388

389

390

391

392

393

394

395

396

397

398

399

400

401

402

403

404

405

406

407

408

409

410

411

412

413

414

415

416

417

418

419

420

421

422

423

424

425

426

427

428

429

430

431

432

433

434

435

436

437

438

439

440

441

442

443

444

445

446

447

448

449

450

451

452

453

454

455

456

457

458

459

460

461

462

463

464

465

466

467

468

469

470

471

472

473

474

475

476

477

478

479

480

481

482

483

484

485

486

487

488

489

490

491

492

493

494

495

496

497

498

499

500

501

502

503

504

505

506

507

508

509

510

511

512

513

514

515

516

517

518

519

520

521

522

523

524

525

526

527

528

529

530

531

532

533

534

535

536

537

538

539

540

541

542

543

544

545

546

547

548

549

550

551

552

553

554

555

556

557

558

559

560

561

562

563

564

565

566

567

568

569

570

571

572

573

574

575

576

577

578

579

580

581

582

583

584

585

586

587

588

589

590

591

592

593

594

595

596

597

598

599

600

601

602

603

604

605

606

607

608

609

610

611

612

613

614

615

616

617

618

619

620

621

622

623

624

625

626

627

628

629

630

631

632

633

634

635

636

637

638

639

640

641

642

643

644

645

646

647

648

649

650

651

652

653

654

655

656

657

658

659

660

661

662

663

664

665

666

667

668

669

670

671

672

673

674

675

676

677

678

679

680

681

682

683

684

685

686

687

688

689

690

691

692

693

694

695

696

697

698

699

700

701

702

703

704

705

706

707

708

709

710

711

712

713

714

715

716

717

718

719

720

721

722

723

724

725

726

727

728

729

730

731

732

733

734

735

736

737

738

739

740

741

742

743

744

745

746

747

748

749

750

751

752

753

754

755

756

757

758

759

760

761

762

763

764

765

766

767

768

769

770

771

772

773

774

775

776

777

778

779

780

781

782

783

784

785

786

787

788

789

790

791

792

793

794

795

796

797

798

799

800

801

802

803

804

805

806

807

808

809

810

811

812

813

814

815

816

817

818

819

820

821

822

823

824

825

826

827

828

829

830

831

832

833

834

835

836

837

838

839

840

841

842

843

844

845

846

847

848

849

850

851

852

853

854

855

856

857

858

859

860

861

862

863

864

865

866

867

868

869

870

871

872

873

874

875

876

877

878

879

880

881

882

883

884

885

886

887

888

889

890

891

892

893

894

895

896

897

898

899

900

901

902

903

904

905

906

907

908

909

910

911

912

913

914

915

916

917

918

919

920

921

922

923

924

925

926

927

928

929

930

931

932

933

934

935

936

937

938





About This E-Book


EPUB is an open, industry-standard format for e-books. However, support for EPUB and its many features varies across reading devices and applications. Use your device or app settings to customize the presentation to your liking. Settings that you can customize often include font, font size, single or double column, landscape or portrait mode, and figures that you can click or tap to enlarge. For additional information about the settings and features on your reading device or app, visit the device manufacturer’s Web site.

Many titles include programming code or configuration examples. To optimize the presentation of these elements, view the e-book in single-column, landscape mode and adjust the font size to the smallest setting. In addition to presenting code and configurations in the reflowable text format, we have included images of the code that mimic the presentation found in the print book; therefore, where the reflowable format may compromise the presentation of the code listing, you will see a “Click here to view code image” link. Click the link to view the print-fidelity code image. To return to the previous page viewed, click the Back button on your device or app.





Core Java


Volume II–Advanced Features

Eleventh Edition





Cay S. Horstmann





Boston • Columbus • New York • San Francisco • Amsterdam • Cape Town

Dubai • London • Madrid • Milan • Munich • Paris • Montreal • Toronto • Delhi • Mexico City

São Paulo • Sydney • Hong Kong • Seoul • Singapore • Taipei • Tokyo





The author and publisher have taken care in the preparation of this book, but make no expressed or implied warranty of any kind and assume no responsibility for errors or omissions. No liability is assumed for incidental or consequential damages in connection with or arising out of the use of the information or programs contained herein.

For information about buying this title in bulk quantities, or for special sales opportunities (which may include electronic versions; custom cover designs; and content particular to your business, training goals, marketing focus, or branding interests), please contact our corporate sales department at corpsales@pearsoned.com or (800) 382-3419.

For government sales inquiries, please contact governmentsales@pearsoned.com.

For questions about sales outside the United States, please contact intlcs@pearson.com.

Visit us on the Web: informit.com

Library of Congress Preassigned Control Number: 2018963595

Copyright © 2019 Pearson Education Inc.

Portions copyright © 1996-2013 Oracle and/or its affiliates. All Rights Reserved.

Oracle America Inc. does not make any representations or warranties as to the accuracy, adequacy or completeness of any information contained in this work, and is not responsible for any errors or omissions.

Microsoft and/or its respective suppliers make no representations about the suitability of the information contained in the documents and related graphics published as part of the services for any purpose. All such documents and related graphics are provided “as is” without warranty of any kind. Microsoft and/or its respective suppliers hereby disclaim all warranties and conditions with regard to this information, including all warranties and conditions of merchantability, whether express, implied or statutory, fitness for a particular purpose, title and non-infringement. In no event shall Microsoft and/or its respective suppliers be liable for any special, indirect or consequential damages or any damages whatsoever resulting from loss of use, data or profits, whether in an action of contract, negligence or other tortious action, arising out of or in connection with the use or performance of information available from the services. The documents and related graphics contained herein could include technical inaccuracies or typographical errors. Changes are periodically added to the information herein. Microsoft and/or its respective suppliers may make improvements and/or changes in the product(s) and/or the program(s) described herein at any time. Partial screen shots may be viewed in full within the software version specified.

Microsoft® Windows®, and Microsoft Office® are registered trademarks of the Microsoft Corporation in the U.S.A. and other countries. This book is not sponsored or endorsed by or affiliated with the Microsoft Corporation.

All rights reserved. This publication is protected by copyright, and permission must be obtained from the publisher prior to any prohibited reproduction, storage in a retrieval system, or transmission in any form or by any means, electronic, mechanical, photocopying, recording, or likewise. For information regarding permissions, request forms and the appropriate contacts within the Pearson Education Global Rights & Permissions Department, please visit www.pearsoned.com/permissions/.

ISBN-13: 978-0-13-516631-4

ISBN-10: 0-13-516631-4

1 19





Contents


Preface

Acknowledgments

Chapter 1: Streams

1.1 From Iterating to Stream Operations

1.2 Stream Creation

1.3 The filter, map, and flatMap Methods

1.4 Extracting Substreams and Combining Streams

1.5 Other Stream Transformations

1.6 Simple Reductions

1.7 The Optional Type

1.7.1 Getting an Optional Value

1.7.2 Consuming an Optional Value

1.7.3 Pipelining Optional Values

1.7.4 How Not to Work with Optional Values

1.7.5 Creating Optional Values

1.7.6 Composing Optional Value Functions with flatMap

1.7.7 Turning an Optional into a Stream

1.8 Collecting Results

1.9 Collecting into Maps

1.10 Grouping and Partitioning

1.11 Downstream Collectors

1.12 Reduction Operations

1.13 Primitive Type Streams

1.14 Parallel Streams

Chapter 2: Input and Output

2.1 Input/Output Streams

2.1.1 Reading and Writing Bytes

2.1.2 The Complete Stream Zoo

2.1.3 Combining Input/Output Stream Filters

2.1.4 Text Input and Output

2.1.5 How to Write Text Output

2.1.6 How to Read Text Input

2.1.7 Saving Objects in Text Format

2.1.8 Character Encodings

2.2 Reading and Writing Binary Data

2.2.1 The DataInput and DataOutput interfaces

2.2.2 Random-Access Files

2.2.3 ZIP Archives

2.3 Object Input/Output Streams and Serialization

2.3.1 Saving and Loading Serializable Objects

2.3.2 Understanding the Object Serialization File Format

2.3.3 Modifying the Default Serialization Mechanism

2.3.4 Serializing Singletons and Typesafe Enumerations

2.3.5 Versioning

2.3.6 Using Serialization for Cloning

2.4 Working with Files

2.4.1 Paths

2.4.2 Reading and Writing Files

2.4.3 Creating Files and Directories

2.4.4 Copying, Moving, and Deleting Files

2.4.5 Getting File Information

2.4.6 Visiting Directory Entries

2.4.7 Using Directory Streams

2.4.8 ZIP File Systems

2.5 Memory-Mapped Files

2.5.1 Memory-Mapped File Performance

2.5.2 The Buffer Data Structure

2.6 File Locking

2.7 Regular Expressions

2.7.1 The Regular Expression Syntax

2.7.2 Matching a String

2.7.3 Finding Multiple Matches

2.7.4 Splitting along Delimiters

2.7.5 Replacing Matches

Chapter 3: XML

3.1 Introducing XML

3.2 The Structure of an XML Document

3.3 Parsing an XML Document

3.4 Validating XML Documents

3.4.1 Document Type Definitions

3.4.2 XML Schema

3.4.3 A Practical Example

3.5 Locating Information with XPath

3.6 Using Namespaces

3.7 Streaming Parsers

3.7.1 Using the SAX Parser

3.7.2 Using the StAX Parser

3.8 Generating XML Documents

3.8.1 Documents without Namespaces

3.8.2 Documents with Namespaces

3.8.3 Writing Documents

3.8.4 Writing an XML Document with StAX

3.8.5 An Example: Generating an SVG File

3.9 XSL Transformations

Chapter 4: Networking

4.1 Connecting to a Server

4.1.1 Using Telnet

4.1.2 Connecting to a Server with Java

4.1.3 Socket Timeouts

4.1.4 Internet Addresses

4.2 Implementing Servers

4.2.1 Server Sockets

4.2.2 Serving Multiple Clients

4.2.3 Half-Close

4.2.4 Interruptible Sockets

4.3 Getting Web Data

4.3.1 URLs and URIs

4.3.2 Using a URLConnection to Retrieve Information

4.3.3 Posting Form Data

4.4 The HTTP Client

4.5 Sending E-Mail

Chapter 5: Database Programming

5.1 The Design of JDBC

5.1.1 JDBC Driver Types

5.1.2 Typical Uses of JDBC

5.2 The Structured Query Language

5.3 JDBC Configuration

5.3.1 Database URLs

5.3.2 Driver JAR Files

5.3.3 Starting the Database

5.3.4 Registering the Driver Class

5.3.5 Connecting to the Database

5.4 Working with JDBC Statements

5.4.1 Executing SQL Statements

5.4.2 Managing Connections, Statements, and Result Sets

5.4.3 Analyzing SQL Exceptions

5.4.4 Populating a Database

5.5 Query Execution

5.5.1 Prepared Statements

5.5.2 Reading and Writing LOBs

5.5.3 SQL Escapes

5.5.4 Multiple Results

5.5.5 Retrieving Autogenerated Keys

5.6 Scrollable and Updatable Result Sets

5.6.1 Scrollable Result Sets

5.6.2 Updatable Result Sets

5.7 Row Sets

5.7.1 Constructing Row Sets

5.7.2 Cached Row Sets

5.8 Metadata

5.9 Transactions

5.9.1 Programming Transactions with JDBC

5.9.2 Save Points

5.9.3 Batch Updates

5.9.4 Advanced SQL Types

5.10 Connection Management in Web and Enterprise Applications

Chapter 6: The Date and Time API

6.1 The Time Line

6.2 Local Dates

6.3 Date Adjusters

6.4 Local Time

6.5 Zoned Time

6.6 Formatting and Parsing

6.7 Interoperating with Legacy Code

Chapter 7: Internationalization

7.1 Locales

7.1.1 Why Locales?

7.1.2 Specifying Locales

7.1.3 The Default Locale

7.1.4 Display Names

7.2 Number Formats

7.2.1 Formatting Numeric Values

7.2.2 Currencies

7.3 Date and Time

7.4 Collation and Normalization

7.5 Message Formatting

7.5.1 Formatting Numbers and Dates

7.5.2 Choice Formats

7.6 Text Input and Output

7.6.1 Text Files

7.6.2 Line Endings

7.6.3 The Console

7.6.4 Log Files

7.6.5 The UTF-8 Byte Order Mark

7.6.6 Character Encoding of Source Files

7.7 Resource Bundles

7.7.1 Locating Resource Bundles

7.7.2 Property Files

7.7.3 Bundle Classes

7.8 A Complete Example

Chapter 8: Scripting, Compiling, and Annotation Processing

8.1 Scripting for the Java Platform

8.1.1 Getting a Scripting Engine

8.1.2 Script Evaluation and Bindings

8.1.3 Redirecting Input and Output

8.1.4 Calling Scripting Functions and Methods

8.1.5 Compiling a Script

8.1.6 An Example: Scripting GUI Events

8.2 The Compiler API

8.2.1 Invoking the Compiler

8.2.2 Launching a Compilation Task

8.2.3 Capturing Diagnostics

8.2.4 Reading Source Files from Memory

8.2.5 Writing Byte Codes to Memory

8.2.6 An Example: Dynamic Java Code Generation

8.3 Using Annotations

8.3.1 An Introduction into Annotations

8.3.2 An Example: Annotating Event Handlers

8.4 Annotation Syntax

8.4.1 Annotation Interfaces

8.4.2 Annotations

8.4.3 Annotating Declarations

8.4.4 Annotating Type Uses

8.4.5 Annotating this

8.5 Standard Annotations

8.5.1 Annotations for Compilation

8.5.2 Annotations for Managing Resources

8.5.3 Meta-Annotations

8.6 Source-Level Annotation Processing

8.6.1 Annotation Processors

8.6.2 The Language Model API

8.6.3 Using Annotations to Generate Source Code

8.7 Bytecode Engineering

8.7.1 Modifying Class Files

8.7.2 Modifying Bytecodes at Load Time

Chapter 9: The Java Platform Module System

9.1 The Module Concept

9.2 Naming Modules

9.3 The Modular “Hello, World!” Program

9.4 Requiring Modules

9.5 Exporting Packages

9.6 Modular JARs

9.7 Modules and Reflective Access

9.8 Automatic Modules

9.9 The Unnamed Module

9.10 Command-Line Flags for Migration

9.11 Transitive and Static Requirements

9.12 Qualified Exporting and Opening

9.13 Service Loading

9.14 Tools for Working with Modules

Chapter 10: Security

10.1 Class Loaders

10.1.1 The Class-Loading Process

10.1.2 The Class Loader Hierarchy

10.1.3 Using Class Loaders as Namespaces

10.1.4 Writing Your Own Class Loader

10.1.5 Bytecode Verification

10.2 Security Managers and Permissions

10.2.1 Permission Checking

10.2.2 Java Platform Security

10.2.3 Security Policy Files

10.2.4 Custom Permissions

10.2.5 Implementation of a Permission Class

10.3 User Authentication

10.3.1 The JAAS Framework

10.3.2 JAAS Login Modules

10.4 Digital Signatures

10.4.1 Message Digests

10.4.2 Message Signing

10.4.3 Verifying a Signature

10.4.4 The Authentication Problem

10.4.5 Certificate Signing

10.4.6 Certificate Requests

10.4.7 Code Signing

10.5 Encryption

10.5.1 Symmetric Ciphers

10.5.2 Key Generation

10.5.3 Cipher Streams

10.5.4 Public Key Ciphers

Chapter 11: Advanced Swing and Graphics

11.1 Tables

11.1.1 A Simple Table

11.1.2 Table Models

11.1.3 Working with Rows and Columns

11.1.3.1 Column Classes

11.1.3.2 Accessing Table Columns

11.1.3.3 Resizing Columns

11.1.3.4 Resizing Rows

11.1.3.5 Selecting Rows, Columns, and Cells

11.1.3.6 Sorting Rows

11.1.3.7 Filtering Rows

11.1.3.8 Hiding and Displaying Columns

11.1.4 Cell Rendering and Editing

11.1.4.1 Rendering Cells

11.1.4.2 Rendering the Header

11.1.4.3 Editing Cells

11.1.4.4 Custom Editors

11.2 Trees

11.2.1 Simple Trees

11.2.1.1 Editing Trees and Tree Paths

11.2.2 Node Enumeration

11.2.3 Rendering Nodes

11.2.4 Listening to Tree Events

11.2.5 Custom Tree Models

11.3 Advanced AWT

11.3.1 The Rendering Pipeline

11.3.2 Shapes

11.3.2.1 The Shape Class Hierarchy

11.3.2.2 Using the Shape Classes

11.3.3 Areas

11.3.4 Strokes

11.3.5 Paint

11.3.6 Coordinate Transformations

11.3.7 Clipping

11.3.8 Transparency and Composition

11.4 Raster Images

11.4.1 Readers and Writers for Images

11.4.1.1 Obtaining Readers and Writers for Image File Types

11.4.1.2 Reading and Writing Files with Multiple Images

11.4.2 Image Manipulation

11.4.2.1 Constructing Raster Images

11.4.2.2 Filtering Images

11.5 Printing

11.5.1 Graphics Printing

11.5.2 Multiple-Page Printing

11.5.3 Print Services

11.5.4 Stream Print Services

11.5.5 Printing Attributes

Chapter 12: Native Methods

12.1 Calling a C Function from a Java Program

12.2 Numeric Parameters and Return Values

12.3 String Parameters

12.4 Accessing Fields

12.4.1 Accessing Instance Fields

12.4.2 Accessing Static Fields

12.5 Encoding Signatures

12.6 Calling Java Methods

12.6.1 Instance Methods

12.6.2 Static Methods

12.6.3 Constructors

12.6.4 Alternative Method Invocations

12.7 Accessing Array Elements

12.8 Handling Errors

12.9 Using the Invocation API

12.10 A Complete Example: Accessing the Windows Registry

12.10.1 Overview of the Windows Registry

12.10.2 A Java Platform Interface for Accessing the Registry

12.10.3 Implementation of Registry Access Functions as Native Methods

Index





Preface


To the Reader


The book you have in your hands is the second volume of the eleventh edition of Core Java, fully updated for Java SE 11. The first volume covers the essential features of the language; this volume deals with the advanced topics that a programmer needs to know for professional software development. Thus, as with the first volume and the previous editions of this book, we are still targeting programmers who want to put Java technology to work in real projects.



As is the case with any book, errors and inaccuracies are inevitable. Should you find any in this book, we would very much like to hear about them. Of course, we would prefer to hear about them only once. For this reason, we have put up a web site at http://horstmann.com/corejava with a FAQ, bug fixes, and workarounds. Strategically placed at the end of the bug report web page (to encourage you to read the previous reports) is a form that you can use to report bugs or problems and to send suggestions for improvements for future editions.





About This Book


The chapters in this book are, for the most part, independent of each other. You should be able to delve into whatever topic interests you the most and read the chapters in any order.



In Chapter 1, you will learn all about the Java stream library that brings a modern flavor to processing data, by specifying what you want without describing in detail how the result should be obtained. This allows the stream library to focus on an optimal evaluation strategy, which is particularly advantageous for optimizing concurrent computations.

The topic of Chapter 2 is input and output handling (I/O). In Java, all input and output is handled through input/output streams. These streams (not to be confused with those in Chapter 1) let you deal, in a uniform manner, with communications among various sources of data, such as files, network connections, or memory blocks. We include detailed coverage of the reader and writer classes that make it easy to deal with Unicode. We show you what goes on under the hood when you use the object serialization mechanism, which makes saving and loading objects easy and convenient. We then move on to regular expressions and working with files and paths. Throughout this chapter, you will find welcome enhancements in recent Java versions.

Chapter 3 covers XML. We show you how to parse XML files, how to generate XML, and how to use XSL transformations. As a useful example, we show you how to specify the layout of a Swing form in XML. We also discuss the XPath API, which makes finding needles in XML haystacks much easier.

Chapter 4 covers the networking API. Java makes it phenomenally easy to do complex network programming. We show you how to make network connections to servers, how to implement your own servers, and how to make HTTP connections. This chapter includes coverage of the new HTTP client.

Chapter 5 covers database programming. The main focus is on JDBC, the Java database connectivity API that lets Java programs connect to relational databases. We show you how to write useful programs to handle realistic database chores, using a core subset of the JDBC API. (A complete treatment of the JDBC API would require a book almost as big as this one.) We finish the chapter with a brief introduction into hierarchical databases and discuss JNDI (the Java Naming and Directory Interface) and LDAP (the Lightweight Directory Access Protocol).

Java had two prior attempts at libraries for handling date and time. The third one was the charm in Java 8. In Chapter 6, you will learn how to deal with the complexities of calendars and time zones, using the new date and time library.

Chapter 7 discusses a feature that we believe can only grow in importance: internationalization. The Java programming language is one of the few languages designed from the start to handle Unicode, but the internationalization support on the Java platform goes much further. As a result, you can internationalize Java applications so that they cross not only platforms but country boundaries as well. For example, we show you how to write a retirement calculator that uses either English, German, or Chinese languages.

Chapter 8 discusses three techniques for processing code. The scripting and compiler APIs allow your program to call code in scripting languages such as JavaScript or Groovy, and to compile Java code. Annotations allow you to add arbitrary information (sometimes called metadata) to a Java program. We show you how annotation processors can harvest these annotations at the source or class file level, and how annotations can be used to influence the behavior of classes at runtime. Annotations are only useful with tools, and we hope that our discussion will help you select useful annotation processing tools for your needs.

In Chapter 9, you will learn about the Java Platform Module System that was introduced in Java 9 to facilitate an orderly evolution of the Java platform and core libraries. This module system provides encapsulation for packages and a mechanism for describing module requirements. You will learn the properties of modules so that you can decide whether to use them in your own applications. Even if you decide not to, you need to know the new rules so that you can interact with the Java platform and other modularized libraries.

Chapter 10 takes up the Java security model. The Java platform was designed from the ground up to be secure, and this chapter takes you under the hood to see how this design is implemented. We show you how to write your own class loaders and security managers for special-purpose applications. Then, we take up the security API that allows for such important features as message and code signing, authorization and authentication, and encryption. We conclude with examples that use the AES and RSA encryption algorithms.

Chapter 11 contains all the Swing material that didn’t make it into Volume I, especially the important but complex tree and table components. We also cover the Java 2D API, which you can use to create realistic drawings and special effects. Of course, not many programmers need to program Swing user interfaces these days, so we pay particular attention to features that are useful for images that can be generated on a server.

Chapter 12 takes up native methods, which let you call methods written for a specific machine such as the Microsoft Windows API. Obviously, this feature is controversial: Use native methods, and the cross-platform nature of Java vanishes. Nonetheless, every serious programmer writing Java applications for specific platforms needs to know these techniques. At times, you need to turn to the operating system’s API for your target platform when you interact with a device or service that is not supported by Java. We illustrate this by showing you how to access the registry API in Windows from a Java program.

As always, all chapters have been completely revised for the latest version of Java. Outdated material has been removed, and the new APIs of Java 9, 10, and 11 are covered in detail.





Conventions


As is common in many computer books, we use monospace type to represent computer code.




Note

Notes are tagged with “note” icons that look like this.




Tip

Tips are tagged with “tip” icons that look like this.




Caution

When there is danger ahead, we warn you with a “caution” icon.




C++ Note

There are a number of C++ notes that explain the difference between the Java programming language and C++. You can skip them if you aren’t interested in C++.



Java comes with a large programming library, or Application Programming Interface (API). When using an API call for the first time, we add a short summary description at the end of the section. These descriptions are a bit more informal but, we hope, also a little more informative than those in the official online API documentation. The names of interfaces are in italics, just like in the official documentation. The number after a class, interface, or method name is the JDK version in which the feature was introduced.


Application Programming Interface 1.2



Programs whose source code is included in the companion code for this book are listed as examples, for instance

Listing 1.1 ScriptTest.java



You can download the companion code from http://horstmann.com/corejava.


Register your copy of Core Java, Volume II—Advanced Features, Eleventh Edition, on the InformIT site for convenient access to updates and/or corrections as they become available. To start the registration process, go to informit.com/register and log in or create an account. Enter the product ISBN (9780135166314) and cl ick Submit. Look on the Registered Products tab for an Access Bonus Content link next to this product, and follow that link to access any available bonus materials. If you would like to be notified of excl usive offers on new editions and updates, please check the box to receive email from us.





Acknowledgments


Writing a book is always a monumental effort, and rewriting doesn’t seem to be much easier, especially with such a rapid rate of change in Java technology. Making a book a reality takes many dedicated people, and it is my great pleasure to acknowledge the contributions of the entire Core Java team.

A large number of individuals at Pearson provided valuable assistance, but they managed to stay behind the scenes. I’d like them all to know how much I appreciate their efforts. As always, my warm thanks go to my editor, Greg Doench, for steering the book through the writing and production process, and for allowing me to be blissfully unaware of the existence of all those folks behind the scenes. I am very grateful to Julie Nahil for production support, and to Dmitry Kirsanov and Alina Kirsanova for copyediting and typesetting the manuscript.

Thanks to the many readers of earlier editions who reported embarrassing errors and made lots of thoughtful suggestions for improvement. I am particularly grateful to the excellent reviewing team that went over the manuscript with an amazing eye for detail and saved me from many more embarrassing errors.

Reviewers of this and earlier editions include Chuck Allison (Contributing Editor, C/C++ Users Journal), Lance Anderson (Oracle), Alec Beaton (PointBase, Inc.), Cliff Berg (iSavvix Corporation), Joshua Bloch, David Brown, Corky Cartwright, Frank Cohen (PushToTest), Chris Crane (devXsolution), Dr. Nicholas J. De Lillo (Manhattan College), Rakesh Dhoopar (Oracle), Robert Evans (Senior Staff, The Johns Hopkins University Applied Physics Lab), David Geary (Sabreware), Jim Gish (Oracle), Brian Goetz (Oracle), Angela Gordon, Dan Gordon, Rob Gordon, John Gray (University of Hartford), Cameron Gregory (olabs.com), Steve Haines, Marty Hall (The Johns Hopkins University Applied Physics Lab), Vincent Hardy, Dan Harkey (San Jose State University), William Higgins (IBM), Vladimir Ivanovic (PointBase), Jerry Jackson (ChannelPoint Software), Tim Kimmet (Preview Systems), Chris Laffra, Charlie Lai, Angelika Langer, Doug Langston, Hang Lau (McGill University), Mark Lawrence, Doug Lea (SUNY Oswego), Gregory Longshore, Bob Lynch (Lynch Associates), Philip Milne (consultant), Mark Morrissey (The Oregon Graduate Institute), Mahesh Neelakanta (Florida Atlantic University), Hao Pham, Paul Philion, Blake Ragsdell, Ylber Ramadani (Ryerson University), Stuart Reges (University of Arizona), Simon Ritter, Rich Rosen (Interactive Data Corporation), Peter Sanders (ESSI University, Nice, France), Dr. Paul Sanghera (San Jose State University and Brooks College), Paul Sevinc (Teamup AG), Yoshiki Shabata, Devang Shah, Richard Slywczak (NASA/Glenn Research Center), Bradley A. Smith, Steven Stelting, Christopher Taylor, Luke Taylor (Valtech), George Thiruvathukal, Kim Topley (author of Core JFC, Second Edition), Janet Traub, Paul Tyma (consultant), Christian Ullenboom, Peter van der Linden, Burt Walsh, Joe Wang (Oracle), and Dan Xu (Oracle).


Cay Horstmann

San Francisco, California

December 2018





Chapter 1. Streams



In this chapter

1.1 From Iterating to Stream Operations



1.2 Stream Creation



1.3 The filter, map, and flatMap Methods



1.4 Extracting Substreams and Combining Streams



1.5 Other Stream Transformations



1.6 Simple Reductions



1.7 The Optional Type



1.8 Collecting Results



1.9 Collecting into Maps



1.10 Grouping and Partitioning



1.11 Downstream Collectors



1.12 Reduction Operations



1.13 Primitive Type Streams



1.14 Parallel Streams





Compared to collections, streams provide a view of data that lets you specify computations at a higher conceptual level. With a stream, you specify what you want to have done, not how to do it. You leave the scheduling of operations to the implementation. For example, suppose you want to compute the average of a certain property. You specify the source of data and the property, and the stream library can then optimize the computation, for example by using multiple threads for computing sums and counts and combining the results.

In this chapter, you will learn how to use the Java stream library, which was introduced in Java 8, to process collections in a “what, not how” style.





1.1 From Iterating to Stream Operations


When you process a collection, you usually iterate over its elements and do some work with each of them. For example, suppose we want to count all long words in a book. First, let’s put them into a list:



Click here to view code image

var contents = new String(Files.readAllBytes( Paths.get("alice.txt")), StandardCharsets.UTF_8); // Read file into string List<String> words = List.of(contents.split("\\PL+")); // Split into words; nonletters are delimiters

Now we are ready to iterate:

Click here to view code image

int count = 0; for (String w : words) { if (w.length() > 12) count++; }

With streams, the same operation looks like this:

Click here to view code image

long count = words.stream() .filter(w -> w.length() > 12) .count();

Now you don’t have to scan the loop for evidence of filtering and counting. The method names tell you right away what the code intends to do. Moreover, where the loop prescribes the order of operations in complete detail, a stream is able to schedule the operations any way it wants, as long as the result is correct.

Simply changing stream to parallelStream allows the stream library to do the filtering and counting in parallel.

Click here to view code image

long count = words.parallelStream() .filter(w -> w.length() > 12) .count();

Streams follow the “what, not how” principle. In our stream example, we describe what needs to be done: get the long words and count them. We don’t specify in which order, or in which thread, this should happen. In contrast, the loop at the beginning of this section specifies exactly how the computation should work, and thereby forgoes any chances of optimization.

A stream seems superficially similar to a collection, allowing you to transform and retrieve data. But there are significant differences:

A stream does not store its elements. They may be stored in an underlying collection or generated on demand.



Stream operations don’t mutate their source. For example, the filter method does not remove elements from a stream but yields a new stream in which they are not present.



Stream operations are lazy when possible. This means they are not executed until their result is needed. For example, if you only ask for the first five long words instead of all, the filter method will stop filtering after the fifth match. As a consequence, you can even have infinite streams!





Let us have another look at the example. The stream and parallelStream methods yield a stream for the words list. The filter method returns another stream that contains only the words of length greater than twelve. The count method reduces that stream to a result.

This workflow is typical when you work with streams. You set up a pipeline of operations in three stages:

Create a stream.



Specify intermediate operations for transforming the initial stream into others, possibly in multiple steps.



Apply a terminal operation to produce a result. This operation forces the execution of the lazy operations that precede it. Afterwards, the stream can no longer be used.





In the example in Listing 1.1, the stream is created with the stream or parallelStream method. The filter method transforms it, and count is the terminal operation.

In the next section, you will see how to create a stream. The subsequent three sections deal with stream transformations. They are followed by five sections on terminal operations.

Listing 1.1 streams/CountLongWords.java

Click here to view code image

1 package streams; 2 3 /** 4 * @version 1.01 2018-05-01 5 * @author Cay Horstmann 6 */ 7 8 import java.io.*; 9 import java.nio.charset.*; 10 import java.nio.file.*; 11 import java.util.*; 12 13 public class CountLongWords 14 { 15 public static void main(String[] args) throws IOException 16 { 17 var contents = new String(Files.readAllBytes( 18 Paths.get("../gutenberg/alice30.txt")), StandardCharsets.UTF_8); 19 List<String> words = List.of(contents.split("\\PL+")); 20 21 long count = 0; 22 for (String w : words) 23 { 24 if (w.length() > 12) count++; 25 } 26 System.out.println(count); 27 28 count = words.stream().filter(w -> w.length() > 12).count(); 29 System.out.println(count); 30 31 count = words.parallelStream().filter(w -> w.length() > 12).count(); 32 System.out.println(count); 33 } 34 }




java.util.stream.Stream<T> 8

Stream<T> filter(Predicate<? super T> p)

yields a stream containing all elements of this stream fulfilling p.



long count()

yields the number of elements of this stream. This is a terminal operation.





java.util.Collection<E> 1.2

default Stream<E> stream()



default Stream<E> parallelStream()

yields a sequential or parallel stream of the elements in this collection.





1.2 Stream Creation


You have already seen that you can turn any collection into a stream with the stream method of the Collection interface. If you have an array, use the static Stream.of method instead.



Click here to view code image

Stream<String> words = Stream.of(contents.split("\\PL+")); // split returns a String[] array

The of method has a varargs parameter, so you can construct a stream from any number of arguments:

Click here to view code image

Stream<String> song = Stream.of("gently", "down", "the", "stream");

Use Arrays.stream(array, from, to) to make a stream from a part of an array.

To make a stream with no elements, use the static Stream.empty method:

Click here to view code image

Stream<String> silence = Stream.empty(); // Generic type <String> is inferred; same as Stream.<String>empty()

The Stream interface has two static methods for making infinite streams. The generate method takes a function with no arguments (or, technically, an object of the Supplier<T> interface). Whenever a stream value is needed, that function is called to produce a value. You can get a stream of constant values as

Click here to view code image

Stream<String> echos = Stream.generate(() -> "Echo");

or a stream of random numbers as

Click here to view code image

Stream<Double> randoms = Stream.generate(Math::random);

To produce sequences such as 0 1 2 3 . . ., use the iterate method instead. It takes a “seed” value and a function (technically, a UnaryOperator<T>) and repeatedly applies the function to the previous result. For example,

Click here to view code image

Stream<BigInteger> integers = Stream.iterate(BigInteger.ZERO, n -> n.add(BigInteger.ONE));

The first element in the sequence is the seed BigInteger.ZERO. The second element is f(seed) which yields 1 (as a big integer). The next element is f(f(seed)) which yields 2, and so on.

To produce a finite stream instead, add a predicate that specifies when the iteration should finish:

Click here to view code image

var limit = new BigInteger("10000000"); Stream<BigInteger> integers = Stream.iterate(BigInteger.ZERO, n -> n.compareTo(limit) < 0, n -> n.add(BigInteger.ONE));

As soon as the predicate rejects an iteratively generated value, the stream ends.

Finally, the Stream.ofNullable method makes a really short stream from an object. The stream has length 0 if the object is null or length 1 otherwise, containing just the object. This is mostly useful in conjunction with flatMap—see Section 1.7.7, “Turning an Optional into a Stream,” on p. 22 for an example.


Note

A number of methods in the Java API yield streams. For example, the Pattern class has a method splitAsStream that splits a CharSequence by a regular expression. You can use the following statement to split a string into words:

Click here to view code image

Stream<String> words = Pattern.compile("\\PL+").splitAsStream(contents);

The Scanner.tokens method yields a stream of tokens of a scanner. Another way to get a stream of words from a string is

Click here to view code image

Stream<String> words = new Scanner(contents).tokens();

The static Files.lines method returns a Stream of all lines in a file:

Click here to view code image

try (Stream<String> lines = Files.lines(path)) { Process lines }




Note

If you have an Iterable that is not a collection, you can turn it into a stream by calling

Click here to view code image

StreamSupport.stream(iterable.spliterator(), false);

If you have an Iterator and want a stream of its results, use

Click here to view code image

StreamSupport.stream(Spliterators.spliteratorUnknownSize( iterator, Spliterator.ORDERED), false);




Caution

It is very important that you don’t modify the collection backing a stream while carrying out a stream operation. Remember that streams don’t collect their data—the data is always in a separate collection. If you modify that collection, the outcome of the stream operations becomes undefined. The JDK documentation refers to this requirement as noninterference.

To be exact, since intermediate stream operations are lazy, it is possible to mutate the collection up to the point where the terminal operation executes. For example, the following, while certainly not recommended, will work:

Click here to view code image

List<String> wordList = . . .; Stream<String> words = wordList.stream(); wordList.add("END"); long n = words.distinct().count();

But this code is wrong:

Click here to view code image

Stream<String> words = wordList.stream(); words.forEach(s -> if (s.length() < 12) wordList.remove(s)); // ERROR--interference



The example program in Listing 1.2 shows the various ways of creating a stream.

Listing 1.2 streams/CreatingStreams.java

Click here to view code image

1 package streams; 2 3 /** 4 * @version 1.01 2018-05-01 5 * @author Cay Horstmann 6 */ 7 8 import java.io.IOException; 9 import java.math.BigInteger; 10 import java.nio.charset.StandardCharsets; 11 import java.nio.file.*; 12 import java.util.*; 13 import java.util.regex.Pattern; 14 import java.util.stream.*; 15 16 public class CreatingStreams 17 { 18 public static <T> void show(String title, Stream<T> stream) 19 { 20 final int SIZE = 10; 21 List<T> firstElements = stream 22 .limit(SIZE + 1) 23 .collect(Collectors.toList()); 24 System.out.print(title + ": "); 25 for (int i = 0; i < firstElements.size(); i++) 26 { 27 if (i > 0) System.out.print(", "); 28 if (i < SIZE) System.out.print(firstElements.get(i)); 29 else System.out.print(". . ."); 30 } 31 System.out.println(); 32 } 33 34 public static void main(String[] args) throws IOException 35 { 36 Path path = Paths.get("../gutenberg/alice30.txt"); 37 var contents = new String(Files.readAllBytes(path), StandardCharsets.UTF_8); 38 39 Stream<String> words = Stream.of(contents.split("\\PL+")); 40 show("words", words); 41 Stream<String> song = Stream.of("gently", "down", "the", "stream"); 42 show("song", song); 43 Stream<String> silence = Stream.empty(); 44 show("silence", silence); 45 46 Stream<String> echos = Stream.generate(() -> "Echo"); 47 show("echos", echos); 48 49 Stream<Double> randoms = Stream.generate(Math::random); 50 show("randoms", randoms); 51 52 Stream<BigInteger> integers = Stream.iterate(BigInteger.ONE, 53 n -> n.add(BigInteger.ONE)); 54 show("integers", integers); 55 56 Stream<String> wordsAnotherWay = Pattern.compile("\\PL+").splitAsStream(contents); 57 show("wordsAnotherWay", wordsAnotherWay); 58 59 try (Stream<String> lines = Files.lines(path, StandardCharsets.UTF_8)) 60 { 61 show("lines", lines); 62 } 63 64 Iterable<Path> iterable = FileSystems.getDefault().getRootDirectories(); 65 Stream<Path> rootDirectories = StreamSupport.stream(iterable.spliterator(), false); 66 show("rootDirectories", rootDirectories); 67 68 Iterator<Path> iterator = Paths.get("/usr/share/dict/words").iterator(); 69 Stream<Path> pathComponents = StreamSupport.stream(Spliterators.spliteratorUnknownSize( 70 iterator, Spliterator.ORDERED), false); 71 show("pathComponents", pathComponents); 72 } 73 }




java.util.stream.Stream 8

static <T> Stream<T> of(T. . . values)

yields a stream whose elements are the given values.



static <T> Stream<T> empty()

yields a stream with no elements.



static <T> Stream<T> generate(Supplier<T> s)

yields an infinite stream whose elements are constructed by repeatedly invoking the function s.



static <T> Stream<T> iterate(T seed, UnaryOperator<T> f)



static <T> Stream<T> iterate(T seed, Predicate<? super T> hasNext, UnaryOperator<T> f)

yields a stream whose elements are seed, f invoked on seed, f invoked on the preceding element, and so on. The first method yields an infinite stream. The stream of the second method comes to an end before the first element that doesn’t fulfill the hasNext predicate.



static <T> Stream<T> ofNullable(T t) 9

returns an empty stream if t is null or a stream containing t otherwise.





java.util.Spliterators 8

static <T> Spliterator<T> spliteratorUnknownSize(Iterator<? extends T> iterator, int characteristics)

turns an iterator into a splittable iterator of unknown size with the given characteristics (a bit pattern containing constants such as Spliterator.ORDERED).





java.util.Arrays 1.2

static <T> Stream<T> stream(T[] array, int startInclusive, int endExclusive) 8

yields a stream whose elements are the specified range of the array.





java.util.regex.Pattern 1.4

Stream<String> splitAsStream(CharSequence input) 8

yields a stream whose elements are the parts of the input that are delimited by this pattern.





java.nio.file.Files 7

static Stream<String> lines(Path path) 8



static Stream<String> lines(Path path, Charset cs) 8

yields a stream whose elements are the lines of the specified file, with the UTF-8 charset or the given charset.





java.util.stream.StreamSupport 8

static <T> Stream<T>	stream(Spliterator<T> spliterator, boolean parallel)

yields a stream containing the values produced by the given splittable iterator.





java.lang.Iterable 5

Spliterator<T> spliterator() 8

yields a splittable iterator for this Iterable. The default implementation does not split and does not report a size.





java.util.Scanner 5

public Stream<String> tokens() 9

yields a stream of strings returned by calling the next method of this scanner.





java.util.function.Supplier<T> 8

T get()

supplies a value.





1.3 The filter, map, and flatMap Methods


A stream transformation produces a stream whose elements are derived from those of another stream. You have already seen the filter transformation that yields a new stream with those elements that match a certain condition. Here, we transform a stream of strings into another stream containing only long words:



Click here to view code image

List<String> words = . . .; Stream<String> longWords = words.stream().filter(w -> w.length() > 12);

The argument of filter is a Predicate<T>—that is, a function from T to boolean.

Often, you want to transform the values in a stream in some way. Use the map method and pass the function that carries out the transformation. For example, you can transform all words to lowercase like this:

Click here to view code image

Stream<String> lowercaseWords = words.stream().map(String::toLowerCase);

Here, we used map with a method reference. Often, you will use a lambda expression instead:

Click here to view code image

Stream<String> firstLetters = words.stream().map(s -> s.substring(0, 1));

The resulting stream contains the first letter of each word.

When you use map, a function is applied to each element, and the result is a new stream with the results. Now, suppose you have a function that returns not just one value but a stream of values. Here is an example—a method that turns a string into a stream of strings, namely the individual code points:

Click here to view code image

public static Stream<String> codePoints(String s) { var result = new ArrayList<String>(); int i = 0; while (i < s.length()) { int j = s.offsetByCodePoints(i, 1); result.add(s.substring(i, j)); i = j; } return result.stream(); }

This method correctly handles Unicode characters that require two char values because that’s the right thing to do. But you don’t have to dwell on that.

For example, codePoints("boat") is the stream ["b", "o", "a", "t"].

Now let’s map the codePoints method on a stream of strings:

Click here to view code image

Stream<Stream<String>> result = words.stream().map(w -> codePoints(w));

You will get a stream of streams, like [. . . ["y", "o", "u", "r"], ["b", "o", "a", "t"], . . .]. To flatten it out to a single stream [. . . "y", "o", "u", "r", "b", "o", "a", "t", . . .], use the flatMap method instead of map:

Click here to view code image

Stream<String> flatResult = words.stream().flatMap(w -> codePoints(w)); // Calls codePoints on each word and flattens the results


Note

You will find a flatMap method in classes other than streams. It is a general concept in computer science. Suppose you have a generic type G (such as Stream) and functions f from some type T to G<U> and g from U to G<V>. Then you can compose them—that is, first apply f and then g, by using flatMap. This is a key idea in the theory of monads. But don’t worry—you can use flatMap without knowing anything about monads.




java.util.stream.Stream 8

Stream<T> filter(Predicate<? super T> predicate)

yields a stream containing the elements of this stream that fulfill the predicate.



<R> Stream<R> map(Function<? super T,? extends R> mapper)

yields a stream containing the results of applying mapper to the elements of this stream.



<R> Stream<R> flatMap(Function<? super T,? extends Stream<? extends R>> mapper)

yields a stream obtained by concatenating the results of applying mapper to the elements of this stream. (Note that each result is a stream.)





1.4 Extracting Substreams and Combining Streams


The call stream.limit(n) returns a new stream that ends after n elements (or when the original stream ends if it is shorter). This method is particularly useful for cutting infinite streams down to size. For example,



Click here to view code image

Stream<Double> randoms = Stream.generate(Math::random).limit(100);

yields a stream with 100 random numbers.

The call stream.skip(n) does the exact opposite. It discards the first n elements. This is handy in our book reading example where, due to the way the split method works, the first element is an unwanted empty string. We can make it go away by calling skip:

Click here to view code image

Stream<String> words = Stream.of(contents.split("\\PL+")).skip(1);

The stream.takeWhile(predicate) call takes all elements from the stream while the predicate is true, and then stops.

For example, suppose we use the codePoints method of the preceding section to split a string into characters, and we want to collect all initial digits. The takeWhile method can do this:

Click here to view code image

Stream<String> initialDigits = codePoints(str).takeWhile( s -> "0123456789".contains(s));

The dropWhile method does the opposite, dropping elements while a condition is true and yielding a stream of all elements starting with the first one for which the condition was false. For example,

Click here to view code image

Stream<String> withoutInitialWhiteSpace = codePoints(str).dropWhile( s -> s.trim().length() == 0);

You can concatenate two streams with the static concat method of the Stream class:

Click here to view code image

Stream<String> combined = Stream.concat( codePoints("Hello"), codePoints("World")); // Yields the stream ["H", "e", "l", "l", "o", "W", "o", "r", "l", "d"]

Of course, the first stream should not be infinite—otherwise the second one wouldn’t ever get a chance.


java.util.stream.Stream 8

Stream<T> limit(long maxSize)

yields a stream with up to maxSize of the initial elements from this stream.



Stream<T> skip(long n)

yields a stream whose elements are all but the initial n elements of this stream.



Stream<T> takeWhile(Predicate<? super T> predicate) 9

yields a stream whose elements are the initial elements of this stream that fulfill the predicate.



Stream<T> dropWhile(Predicate<? super T> predicate) 9

yields a stream whose elements are the elements of this stream except for the initial ones that do not fulfill the predicate.



static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b)

yields a stream whose elements are the elements of a followed by the elements of b.





1.5 Other Stream Transformations


The distinct method returns a stream that yields elements from the original stream, in the same order, except that duplicates are suppressed. The duplicates need not be adjacent.



Click here to view code image

Stream<String> uniqueWords = Stream.of("merrily", "merrily", "merrily", "gently").distinct(); // Only one "merrily" is retained

For sorting a stream, there are several variations of the sorted method. One works for streams of Comparable elements, and another accepts a Comparator. Here, we sort strings so that the longest ones come first:

Click here to view code image

Stream<String> longestFirst = words.stream().sorted(Comparator.comparing(String::length).reversed());

As with all stream transformations, the sorted method yields a new stream whose elements are the elements of the original stream in sorted order.

Of course, you can sort a collection without using streams. The sorted method is useful when the sorting process is part of a stream pipeline.

Finally, the peek method yields another stream with the same elements as the original, but a function is invoked every time an element is retrieved. That is handy for debugging:

Click here to view code image

Object[] powers = Stream.iterate(1.0, p -> p * 2) .peek(e -> System.out.println("Fetching " + e)) .limit(20).toArray();

When an element is actually accessed, a message is printed. This way you can verify that the infinite stream returned by iterate is processed lazily.


Tip

When you use a debugger to debug a stream computation, you can set a breakpoint in a method that is called from one of the transformations. With most IDEs, you can also set breakpoints in lambda expressions. If you just want to know what happens at a particular point in the stream pipeline, add

.peek(x -> { return; })

and set a breakpoint on the second line.




java.util.stream.Stream 8

Stream<T> distinct()

yields a stream of the distinct elements of this stream.



Stream<T> sorted()



Stream<T> sorted(Comparator<? super T> comparator)

yields a stream whose elements are the elements of this stream in sorted order. The first method requires that the elements are instances of a class implementing Comparable.



Stream<T> peek(Consumer<? super T> action)

yields a stream with the same elements as this stream, passing each element to action as it is consumed.





1.6 Simple Reductions


Now that you have seen how to create and transform streams, we will finally get to the most important point—getting answers from the stream data. The methods covered in this section are called reductions. Reductions are terminal operations. They reduce the stream to a nonstream value that can be used in your program.



You have already seen a simple reduction: the count method that returns the number of elements of a stream.

Other simple reductions are max and min that return the largest or smallest value. There is a twist—these methods return an Optional<T> value that either wraps the answer or indicates that there is none (because the stream happened to be empty). In the olden days, it was common to return null in such a situation. But that can lead to null pointer exceptions when it happens in an incompletely tested program. The Optional type is a better way of indicating a missing return value. We discuss the Optional type in detail in the next section. Here is how you can get the maximum of a stream:

Click here to view code image

Optional<String> largest = words.max(String::compareToIgnoreCase); System.out.println("largest: " + largest.orElse(""));

The findFirst returns the first value in a nonempty collection. It is often useful when combined with filter. For example, here we find the first word that starts with the letter Q, if it exists:

Click here to view code image

Optional<String> startsWithQ = words.filter(s -> s.startsWith("Q")).findFirst();

If you are OK with any match, not just the first one, use the findAny method. This is effective when you parallelize the stream, since the stream can report any match that it finds instead of being constrained to the first one.

Click here to view code image

Optional<String> startsWithQ = words.parallel().filter(s -> s.startsWith("Q")).findAny();

If you just want to know if there is a match, use anyMatch. That method takes a predicate argument, so you won’t need to use filter.

Click here to view code image

boolean aWordStartsWithQ = words.parallel().anyMatch(s -> s.startsWith("Q"));

There are methods allMatch and noneMatch that return true if all or no elements match a predicate. These methods also benefit from being run in parallel.


java.util.stream.Stream 8

Optional<T> max(Comparator<? super T> comparator)



Optional<T> min(Comparator<? super T> comparator)

yields a maximum or minimum element of this stream, using the ordering defined by the given comparator, or an empty Optional if this stream is empty. These are terminal operations.



Optional<T> findFirst()



Optional<T> findAny()

yields the first, or any, element of this stream, or an empty Optional if this stream is empty. These are terminal operations.



boolean anyMatch(Predicate<? super T> predicate)



boolean allMatch(Predicate<? super T> predicate)



boolean noneMatch(Predicate<? super T> predicate)

returns true if any, all, or none of the elements of this stream match the given predicate. These are terminal operations.





1.7 The Optional Type


An Optional<T> object is a wrapper for either an object of type T or no object. In the former case, we say that the value is present. The Optional<T> type is intended as a safer alternative for a reference of type T that either refers to an object or is null. But it is only safer if you use it right. The next three sections shows you how.





1.7.1 Getting an Optional Value


The key to using Optional effectively is to use a method that either produces an alternative if the value is not present, or consumes the value only if it is present.



In this section, we look at the first strategy. Often, there is a default that you want to use when there was no match, perhaps the empty string:

Click here to view code image

String result = optionalString.orElse(""); // The wrapped string, or "" if none

You can also invoke code to compute the default:

Click here to view code image

String result = optionalString.orElseGet(() -> System.getProperty("myapp.default")); // The function is only called when needed

Or you can throw an exception if there is no value:

Click here to view code image

String result = optionalString.orElseThrow(IllegalStateException::new); // Supply a method that yields an exception object


java.util.Optional 8

T orElse(T other)

yields the value of this Optional, or other if this Optional is empty.



T orElseGet(Supplier<? extends T> other)

yields the value of this Optional, or the result of invoking other if this Optional is empty.



<X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier)

yields the value of this Optional, or throws the result of invoking exceptionSupplier if this Optional is empty.





1.7.2 Consuming an Optional Value


In the preceding section, you saw how to produce an alternative if no value is present. The other strategy for working with optional values is to consume the value only if it is present.



The ifPresent method accepts a function. If the optional value exists, it is passed to that function. Otherwise, nothing happens.

Click here to view code image

optionalValue.ifPresent(v -> Process v);

For example, if you want to add the value to a set if it is present, call

Click here to view code image

optionalValue.ifPresent(v -> results.add(v));

or simply

Click here to view code image

optionalValue.ifPresent(results::add);

If you want to take one action if the Optional has a value and another action if it doesn’t, use ifPresentOrElse:

Click here to view code image

optionalValue.ifPresentOrElse( v -> System.out.println("Found " + v), () -> logger.warning("No match"));


java.util.Optional 8

void ifPresent(Consumer<? super T> action)

if this Optional is nonempty, passes its value to action.



void ifPresentOrElse(Consumer<? super T> action, Runnable emptyAction) 9

if this Optional is nonempty, passes its value to action, else invokes emptyAction.





1.7.3 Pipelining Optional Values


In the preceding sections, you saw how to get a value out of an Optional object. Another useful strategy is to keep the Optional intact. You can transform the value inside an Optional by using the map method:



Click here to view code image

Optional<String> transformed = optionalString.map(String::toUpperCase);

If optionalString is empty, then transformed is also empty.

Here is another example. We add a result to a list if it is present:

Click here to view code image

optionalValue.map(results::add);

If optionalValue is empty, nothing happens.


Note

This map method is the analog of the map method of the Stream interface that you have seen in Section 1.3, “The filter, map, and flatMap Methods,” on p. 11. Simply imagine an optional value as a stream of size zero or one. The result again has size zero or one, and in the latter case, the function has been applied.



Similarly, you can use the filter method to only consider Optional values that fulfill a certain property before or after transforming it. If the property is not fulfilled, the pipeline yields an empty result:

Click here to view code image

Optional<String> transformed = optionalString .filter(s -> s.length() >= 8) .map(String::toUpperCase);

You can substitute an alternative Optional for an empty Optional with the or method. The alternative is computed lazily.

Click here to view code image

Optional<String> result = optionalString.or(() -> // Supply an Optional alternatives.stream().findFirst());

If optionalString has a value, then result is optionalString. If not, the lambda expression is evaluated, and its result is used.


java.util.Optional 8

<U> Optional<U> map(Function<? super T,? extends U> mapper)

yields an Optional whose value is obtained by applying the given function to the value of this Optional if present, or an empty Optional otherwise.



Optional<T> filter(Predicate<? super T> predicate)

yields an Optional with the value of this Optional if it fulfills the given predicate, or an empty Optional otherwise.



Optional<T> or(Supplier<? extends Optional<? extends T>> supplier) 9

yields this Optional if it is nonempty, or the one produced by the supplier otherwise.





1.7.4 How Not to Work with Optional Values


If you don’t use Optional values correctly, you have no benefit over the “something or null” approach of the past.



The get method gets the wrapped element of an Optional value if it exists, or throws a NoSuchElementException if it doesn’t. Therefore,

Click here to view code image

Optional<T> optionalValue = . . .; optionalValue.get().someMethod()

is no safer than

T value = . . .; value.someMethod();

The isPresent method reports whether an Optional<T> object has a value. But

Click here to view code image

if (optionalValue.isPresent()) optionalValue.get().someMethod();

is no easier than

Click here to view code image

if (value != null) value.someMethod();


Note

Java 10 introduces a scarier-sounding synonym for the get method. Call optionalValue.orElseThrow() to make explicit that the method will throw a NoSuchElementException if the optionalValue is empty. The hope is that programmers will only call that method when it is absolutely clear that the Optional is never empty.



Here are a few more tips for the proper use of the Optional type:

A variable of type Optional should never be null.



Don’t use fields of type Optional. The cost is an additional object. Inside a class, using null for an absent field is manageable.



Don’t put Optional objects in a set, and don’t use them as keys for a map. Collect the values instead.





java.util.Optional 8

T get()



T orElseThrow() 10

yields the value of this Optional, or throws a NoSuchElementException if it is empty.



boolean isPresent()

returns true if this Optional is not empty.





1.7.5 Creating Optional Values


So far, we have discussed how to consume an Optional object someone else created. If you want to write a method that creates an Optional object, there are several static methods for that purpose, including Optional.of(result) and Optional.empty(). For example,



Click here to view code image

public static Optional<Double> inverse(Double x) { return x == 0 ? Optional.empty() : Optional.of(1 / x); }

The ofNullable method is intended as a bridge from possibly null values to optional values. Optional.ofNullable(obj) returns Optional.of(obj) if obj is not null and Optional.empty() otherwise.


java.util.Optional 8

static <T> Optional<T> of(T value)



static <T> Optional<T> ofNullable(T value)

yields an Optional with the given value. If value is null, the first method throws a NullPointerException and the second method yields an empty Optional.



static <T> Optional<T> empty()

yields an empty Optional.





1.7.6 Composing Optional Value Functions with flatMap


Suppose you have a method f yielding an Optional<T>, and the target type T has a method g yielding an Optional<U>. If they were normal methods, you could compose them by calling s.f().g(). But that composition doesn’t work since s.f() has type Optional<T>, not T. Instead, call



Click here to view code image

Optional<U> result = s.f().flatMap(T::g);

If s.f() is present, then g is applied to it. Otherwise, an empty Optional<U> is returned.

Clearly, you can repeat that process if you have more methods or lambdas that yield Optional values. You can then build a pipeline of steps, simply by chaining calls to flatMap, that will succeed only when all parts do.

For example, consider the safe inverse method of the preceding section. Suppose we also have a safe square root:

Click here to view code image

public static Optional<Double> squareRoot(Double x) { return x < 0 ? Optional.empty() : Optional.of(Math.sqrt(x)); }

Then you can compute the square root of the inverse as

Click here to view code image

Optional<Double> result = inverse(x).flatMap(MyMath::squareRoot);

or, if you prefer,

Click here to view code image

Optional<Double> result = Optional.of(-4.0).flatMap(Demo::inverse).flatMap(Demo::squareRoot);

If either the inverse method or the squareRoot returns Optional.empty(), the result is empty.


Note

You have already seen a flatMap method in the Stream interface (see Section 1.3, “The filter, map, and flatMap Methods,” on p. 11). That method was used to compose two methods that yield streams, by flattening out the resulting stream of streams. The Optional.flatMap method works in the same way if you interpret an optional value as having zero or one element.




java.util.Optional 8

<U> Optional<U> flatMap(Function<? super T,? extends Optional<? extends U>> mapper)

yields the result of applying mapper to the value in this Optional if present, or an empty optional otherwise.





1.7.7 Turning an Optional into a Stream


The stream method turns an Optional<T> into a Stream<T> with zero or one element. Sure, why not, but why would you ever want that?



This becomes useful with methods that return an Optional result. Suppose you have a stream of user IDs and a method

Click here to view code image

Optional<User> lookup(String id)

How do you get a stream of users, skipping those IDs that are invalid?

Of course, you can filter out the invalid IDs and then apply get to the remaining ones:

Click here to view code image

Stream<String> ids = . . .; Stream<User> users = ids.map(Users::lookup) .filter(Optional::isPresent) .map(Optional::get);

But that uses the isPresent and get methods that we warned about. It is more elegant to call

Click here to view code image

Stream<User> users = ids.map(Users::lookup) .flatMap(Optional::stream);

Each call to stream returns a stream with zero or one element. The flatMap method combines them all. That means the nonexistent users are simply dropped.


Note

In this section, we consider the happy scenario in which we have a method that returns an Optional value. These days, many methods return null when there is no valid result. Suppose Users.classicLookup(id) returns a User object or null, not an Optional<User>. Then you can of course filter out the null values:

Click here to view code image

Stream<User> users = ids.map(Users::classicLookup) .filter(Objects::nonNull);

But if you prefer the flatMap approach, you can use

Click here to view code image

Stream<User> users = ids.flatMap( id -> Stream.ofNullable(Users.classicLookup(id)));

or

Click here to view code image

Stream<User> users = ids.map(Users::classicLookup) .flatMap(Stream::ofNullable);

The call Stream.ofNullable(obj) yields an empty stream if obj is null or a stream just containing obj otherwise.



The example program in Listing 1.3 demonstrates the Optional API.

Listing 1.3 optional/OptionalTest.java

Click here to view code image

1 package optional; 2 3 /** 4 * @version 1.01 2018-05-01 5 * @author Cay Horstmann 6 */ 7 8 import java.io.*; 9 import java.nio.charset.*; 10 import java.nio.file.*; 11 import java.util.*; 12 13 public class OptionalTest 14 { 15 public static void main(String[] args) throws IOException 16 { 17 var contents = new String(Files.readAllBytes( 18 Paths.get("../gutenberg/alice30.txt")), StandardCharsets.UTF_8); 19 List<String> wordList = List.of(contents.split("\\PL+")); 20 21 Optional<String> optionalValue = wordList.stream() 22 .filter(s -> s.contains("fred")) 23 .findFirst(); 24 System.out.println(optionalValue.orElse("No word") + " contains fred"); 25 26 Optional<String> optionalString = Optional.empty(); 27 String result = optionalString.orElse("N/A"); 28 System.out.println("result: " + result); 29 result = optionalString.orElseGet(() -> Locale.getDefault().getDisplayName()); 30 System.out.println("result: " + result); 31 try 32 { 33 result = optionalString.orElseThrow(IllegalStateException::new); 34 System.out.println("result: " + result); 35 } 36 catch (Throwable t) 37 { 38 t.printStackTrace(); 39 } 40 41 optionalValue = wordList.stream() 42 .filter(s -> s.contains("red")) 43 .findFirst(); 44 optionalValue.ifPresent(s -> System.out.println(s + " contains red")); 45 46 var results = new HashSet<String>(); 47 optionalValue.ifPresent(results::add); 48 Optional<Boolean> added = optionalValue.map(results::add); 49 System.out.println(added); 50 51 System.out.println(inverse(4.0).flatMap(OptionalTest::squareRoot)); 52 System.out.println(inverse(-1.0).flatMap(OptionalTest::squareRoot)); 53 System.out.println(inverse(0.0).flatMap(OptionalTest::squareRoot)); 54 Optional<Double> result2 = Optional.of(-4.0) 55 .flatMap(OptionalTest::inverse).flatMap(OptionalTest::squareRoot); 56 System.out.println(result2); 57 } 58 59 public static Optional<Double> inverse(Double x) 60 { 61 return x == 0 ? Optional.empty() : Optional.of(1 / x); 62 } 63 64 public static Optional<Double> squareRoot(Double x) 65 { 66 return x < 0 ? Optional.empty() : Optional.of(Math.sqrt(x)); 67 } 68 }




java.util.Optional 8

<U> Optional<U> flatMap(Function<? super T,Optional<U>> mapper) 9

yields the result of applying mapper to the value of this Optional, or an empty Optional if this Optional is empty.





1.8 Collecting Results


When you are done with a stream, you will often want to look at the results. You can call the iterator method, which yields an old-fashioned iterator that you can use to visit the elements.



Alternatively, you can call the forEach method to apply a function to each element:

Click here to view code image

stream.forEach(System.out::println);

On a parallel stream, the forEach method traverses elements in arbitrary order. If you want to process them in stream order, call forEachOrdered instead. Of course, you might then give up some or all of the benefits of parallelism.

But more often than not, you will want to collect the result in a data structure. Call toArray to get an array of the stream elements.

Since it is not possible to create a generic array at runtime, the expression stream.toArray() returns an Object[] array. If you want an array of the correct type, pass in the array constructor:

Click here to view code image

String[] result = stream.toArray(String[]::new); // stream.toArray() has type Object[]

For collecting stream elements to another target, there is a convenient collect method that takes an instance of the Collector interface. A collector is an object that accumulates elements and produces a result. The Collectors class provides a large number of factory methods for common collectors. To collect stream elements into a list, use the collector produced by Collectors.toList():

Click here to view code image

List<String> result = stream.collect(Collectors.toList());

Similarly, here is how you can collect stream elements into a set:

Click here to view code image

Set<String> result = stream.collect(Collectors.toSet());

If you want to control which kind of set you get, use the following call instead:

Click here to view code image

TreeSet<String> result = stream.collect(Collectors.toCollection(TreeSet::new));

Suppose you want to collect all strings in a stream by concatenating them. You can call

Click here to view code image

String result = stream.collect(Collectors.joining());

If you want a delimiter between elements, pass it to the joining method:

Click here to view code image

String result = stream.collect(Collectors.joining(", "));

If your stream contains objects other than strings, you need to first convert them to strings, like this:

Click here to view code image

String result = stream.map(Object::toString).collect(Collectors.joining(", "));

If you want to reduce the stream results to a sum, count, average, maximum, or minimum, use one of the summarizing(Int|Long|Double) methods. These methods take a function that maps the stream objects to numbers and yield a result of type (Int|Long|Double)SummaryStatistics, simultaneously computing the sum, count, average, maximum, and minimum.

Click here to view code image

IntSummaryStatistics summary = stream.collect( Collectors.summarizingInt(String::length)); double averageWordLength = summary.getAverage(); double maxWordLength = summary.getMax();

The example program in Listing 1.4 shows how to collect elements from a stream.

Listing 1.4 collecting/CollectingResults.java

Click here to view code image

1 package collecting; 2 3 /** 4 * @version 1.01 2018-05-01 5 * @author Cay Horstmann 6 */ 7 8 import java.io.*; 9 import java.nio.charset.*; 10 import java.nio.file.*; 11 import java.util.*; 12 import java.util.stream.*; 13 14 public class CollectingResults 15 { 16 public static Stream<String> noVowels() throws IOException 17 { 18 var contents = new String(Files.readAllBytes( 19 Paths.get("../gutenberg/alice30.txt")), 20 StandardCharsets.UTF_8); 21 List<String> wordList = List.of(contents.split("\\PL+")); 22 Stream<String> words = wordList.stream(); 23 return words.map(s -> s.replaceAll("[aeiouAEIOU]", "")); 24 } 25 26 public static <T> void show(String label, Set<T> set) 27 { 28 System.out.print(label + ": " + set.getClass().getName()); 29 System.out.println("[" 30 + set.stream().limit(10).map(Object::toString).collect(Collectors.joining(", ")) 31 + "]"); 32 } 33 34 public static void main(String[] args) throws IOException 35 { 36 Iterator<Integer> iter = Stream.iterate(0, n -> n + 1).limit(10).iterator(); 37 while (iter.hasNext()) 38 System.out.println(iter.next()); 39 40 Object[] numbers = Stream.iterate(0, n -> n + 1).limit(10).toArray(); 41 System.out.println("Object array:" + numbers); 42 // Note it’s an Object[] array 43 44 try 45 { 46 var number = (Integer) numbers[0]; // OK 47 System.out.println("number: " + number); 48 System.out.println("The following statement throws an exception:"); 49 var numbers2 = (Integer[]) numbers; // Throws exception 50 } 51 catch (ClassCastException ex) 52 { 53 System.out.println(ex); 54 } 55 56 Integer[] numbers3 = Stream.iterate(0, n -> n + 1) 57 .limit(10) 58 .toArray(Integer[]::new); 59 System.out.println("Integer array: " + numbers3); 60 // Note it’s an Integer[] array 61 62 Set<String> noVowelSet = noVowels().collect(Collectors.toSet()); 63 show("noVowelSet", noVowelSet); 64 65 TreeSet<String> noVowelTreeSet = noVowels().collect( 66 Collectors.toCollection(TreeSet::new)); 67 show("noVowelTreeSet", noVowelTreeSet); 68 69 String result = noVowels().limit(10).collect(Collectors.joining()); 70 System.out.println("Joining: " + result); 71 result = noVowels().limit(10) 72 .collect(Collectors.joining(", ")); 73 System.out.println("Joining with commas: " + result); 74 75 IntSummaryStatistics summary = noVowels().collect( 76 Collectors.summarizingInt(String::length)); 77 double averageWordLength = summary.getAverage(); 78 double maxWordLength = summary.getMax(); 79 System.out.println("Average word length: " + averageWordLength); 80 System.out.println("Max word length: " + maxWordLength); 81 System.out.println("forEach:"); 82 noVowels().limit(10).forEach(System.out::println); 83 } 84 }




java.util.stream.BaseStream 8

Iterator<T> iterator()

yields an iterator for obtaining the elements of this stream. This is a terminal operation.





java.util.stream.Stream 8

void forEach(Consumer<? super T> action)

invokes action on each element of the stream. This is a terminal operation.



Object[] toArray()



<A> A[] toArray(IntFunction<A[]> generator)

yields an array of objects, or of type A when passed a constructor reference A[]::new. These are terminal operations.



<R,A> R collect(Collector<? super T,A,R> collector)

collects the elements in this stream, using the given collector. The Collectors class has factory methods for many collectors.





java.util.stream.Collectors 8

static <T> Collector<T,?,List<T>> toList()



static <T> Collector<T,?,List<T>> toUnmodifiableList() 10



static <T> Collector<T,?,Set<T>> toSet()



static <T> Collector<T,?,Set<T>> toUnmodifiableSet() 10

yield collectors that collect elements in a list or set.



static <T,C extends Collection<T>> Collector<T,?,C> toCollection(Supplier<C> collectionFactory)

yields a collector that collects elements into an arbitrary collection. Pass a constructor reference such as TreeSet::new.



static Collector<CharSequence,?,String> joining()



static Collector<CharSequence,?,String> joining(CharSequence delimiter)



static Collector<CharSequence,?,String> joining(CharSequence delimiter, CharSequence prefix, CharSequence suffix)

yields a collector that joins strings. The delimiter is placed between strings, and the prefix and suffix before the first and after the last string. When not specified, these are empty.



static <T> Collector<T,?,IntSummaryStatistics> summarizingInt(ToIntFunction<? super T> mapper)



static <T> Collector<T,?,LongSummaryStatistics> summarizingLong(ToLongFunction<? super T> mapper)



static <T> Collector<T,?,DoubleSummaryStatistics> summarizingDouble(ToDoubleFunction<? super T> mapper)

yields collectors that produce an (Int|Long|Double)SummaryStatistics object, from which you can obtain the count, sum, average, maximum, and minimum of the results of applying mapper to each element.





IntSummaryStatistics 8

LongSummaryStatistics 8

DoubleSummaryStatistics 8

long getCount()

yields the count of the summarized elements.



(int|long|double) getSum()



double getAverage()

yields the sum or average of the summarized elements, or zero if there are no elements.



(int|long|double) getMax()



(int|long|double) getMin()

yields the maximum or minimum of the summarized elements, or (Integer|Long|Double).(MAX|MIN)_VALUE if there are no elements.





1.9 Collecting into Maps


Suppose you have a Stream<Person> and want to collect the elements into a map so that later you can look up people by their ID. The Collectors.toMap method has two function arguments that produce the map’s keys and values. For example,



Click here to view code image

Map<Integer, String> idToName = people.collect( Collectors.toMap(Person::getId, Person::getName));

In the common case when the values should be the actual elements, use Function.identity() for the second function.

Click here to view code image

Map<Integer, Person> idToPerson = people.collect( Collectors.toMap(Person::getId, Function.identity()));

If there is more than one element with the same key, there is a conflict, and the collector will throw an IllegalStateException. You can override that behavior by supplying a third function argument that resolves the conflict and determines the value for the key, given the existing and the new value. Your function could return the existing value, the new value, or a combination of them.

Here, we construct a map that contains, for each language in the available locales, as key its name in your default locale (such as "German"), and as value its localized name (such as "Deutsch").

Click here to view code image

Stream<Locale> locales = Stream.of(Locale.getAvailableLocales()); Map<String, String> languageNames = locales.collect( Collectors.toMap( Locale::getDisplayLanguage, loc -> loc.getDisplayLanguage(loc), (existingValue, newValue) -> existingValue));

We don’t care that the same language might occur twice (for example, German in Germany and in Switzerland), so we just keep the first entry.


Note

In this chapter, I use the Locale class as a source of an interesting data set. See Chapter 7 for more information on working with locales.



Now suppose we want to know all languages in a given country. Then we need a Map<String, Set<String>>. For example, the value for "Switzerland" is the set [French, German, Italian]. At first, we store a singleton set for each language. Whenever a new language is found for a given country, we form the union of the existing and the new set.

Click here to view code image

Map<String, Set<String>> countryLanguageSets = locales.collect( Collectors.toMap( Locale::getDisplayCountry, l -> Collections.singleton(l.getDisplayLanguage()), (a, b) -> { // Union of a and b var union = new HashSet<String>(a); union.addAll(b); return union; }));

You will see a simpler way of obtaining this map in the next section.

If you want a TreeMap, supply the constructor as the fourth argument. You must provide a merge function. Here is one of the examples from the beginning of the section, now yielding a TreeMap:

Click here to view code image

Map<Integer, Person> idToPerson = people.collect( Collectors.toMap( Person::getId, Function.identity(), (existingValue, newValue) -> { throw new IllegalStateException(); }, TreeMap::new));


Note

For each of the toMap methods, there is an equivalent toConcurrentMap method that yields a concurrent map. A single concurrent map is used in the parallel collection process. When used with a parallel stream, a shared map is more efficient than merging maps. Note that elements are no longer collected in stream order, but that doesn’t usually make a difference.



The program in Listing 1.5 gives examples of collecting stream results into maps.

Listing 1.5 collecting/CollectingIntoMaps.java

Click here to view code image

1 package collecting; 2 3 /** 4 * @version 1.00 2016-05-10 5 * @author Cay Horstmann 6 */ 7 8 import java.io.*; 9 import java.util.*; 10 import java.util.function.*; 11 import java.util.stream.*; 12 13 public class CollectingIntoMaps 14 { 15 16 public static class Person 17 { 18 private int id; 19 private String name; 20 21 public Person(int id, String name) 22 { 23 this.id = id; 24 this.name = name; 25 } 26 27 public int getId() 28 { 29 return id; 30 } 31 32 public String getName() 33 { 34 return name; 35 } 36 37 public String toString() 38 { 39 return getClass().getName() + "[id=" + id + ",name=" + name + "]"; 40 } 41 } 42 43 public static Stream<Person< people() 44 { 45 return Stream.of(new Person(1001, "Peter"), new Person(1002, "Paul"), 46 new Person(1003, "Mary")); 47 } 48 49 public static void main(String[] args) throws IOException 50 { 51 Map<Integer, String> idToName = people().collect( 52 Collectors.toMap(Person::getId, Person::getName)); 53 System.out.println("idToName: " + idToName); 54 55 Map<Integer, Person> idToPerson = people().collect( 56 Collectors.toMap(Person::getId, Function.identity())); 57 System.out.println("idToPerson: " + idToPerson.getClass().getName() 58 + idToPerson); 59 60 idToPerson = people().collect( 61 Collectors.toMap(Person::getId, Function.identity(), 62 (existingValue, newValue) -> { throw new IllegalStateException(); }, 63 TreeMap::new)); 64 System.out.println("idToPerson: " + idToPerson.getClass().getName() 65 + idToPerson); 66 67 Stream<Locale> locales = Stream.of(Locale.getAvailableLocales()); 68 Map<String, String> languageNames = locales.collect( 69 Collectors.toMap( 70 Locale::getDisplayLanguage, 71 l -> l.getDisplayLanguage(l), 72 (existingValue, newValue) -> existingValue)); 73 System.out.println("languageNames: " + languageNames); 74 75 locales = Stream.of(Locale.getAvailableLocales()); 76 Map<String, Set<String>> countryLanguageSets = locales.collect( 77 Collectors.toMap( 78 Locale::getDisplayCountry, 79 l -> Set.of(l.getDisplayLanguage()), 80 (a, b) -> 81 { // union of a and b 82 Set<String> union = new HashSet<>(a); 83 union.addAll(b); 84 return union; 85 })); 86 System.out.println("countryLanguageSets: " + countryLanguageSets); 87 } 88 }




java.util.stream.Collectors 8

static <T,K,U> Collector<T,?,Map<K,U>> toMap(Function<? super T,? extends K> keyMapper, Function<? super T,? extends U> valueMapper)



static <T,K,U> Collector<T,?,Map<K,U>> toMap(Function<? super T,? extends K> keyMapper, Function<? super T,? extends U> valueMapper, BinaryOperator<U> mergeFunction)



static <T,K,U,M extends Map<K,U>> Collector<T,?,M> toMap(Function<? super T,? extends K> keyMapper, Function<? super T,? extends U> valueMapper, BinaryOperator<U> mergeFunction, Supplier<M> mapSupplier)



static <T,K,U> Collector<T,?,Map<K,U>> toUnmodifiableMap(Function<? super T,? extends K> keyMapper, Function<? super T,? extends U> valueMapper) 10



static <T,K,U> Collector<T,?,Map<K,U>> toUnmodifiableMap(Function<? super T,? extends K> keyMapper, Function<? super T,? extends U> valueMapper, BinaryOperator<U> mergeFunction) 10



static <T,K,U> Collector<T,?,ConcurrentMap<K,U>> toConcurrentMap(Function<? super T,? extends K> keyMapper, Function<? super T,? extends U> valueMapper)



static <T,K,U> Collector<T,?,ConcurrentMap<K,U>> toConcurrentMap(Function<? super T,? extends K> keyMapper, Function<? super T,? extends U> valueMapper, BinaryOperator<U> mergeFunction)



static <T,K,U,M extends ConcurrentMap<K,U>> Collector<T,?,M> toConcurrentMap(Function<? super T,? extends K> keyMapper, Function<? super T,? extends U> valueMapper, BinaryOperator<U> mergeFunction, Supplier<M> mapSupplier)

yields a collector that produces a map, unmodifiable map, or concurrent map. The keyMapper and valueMapper functions are applied to each collected element, yielding a key/value entry of the resulting map. By default, an IllegalStateException is thrown when two elements give rise to the same key. You can instead supply a mergeFunction that merges values with the same key. By default, the result is a HashMap or ConcurrentHashMap. You can instead supply a mapSupplier that yields the desired map instance.





1.10 Grouping and Partitioning


In the preceding section, you saw how to collect all languages in a given country. But the process was a bit tedious. You had to generate a singleton set for each map value and then specify how to merge the existing and new values. Forming groups of values with the same characteristic is very common, so the groupingBy method supports it directly.



Let’s look at the problem of grouping locales by country. First, form this map:

Click here to view code image

Map<String, List<Locale>> countryToLocales = locales.collect( Collectors.groupingBy(Locale::getCountry));

The function Locale::getCountry is the classifier function of the grouping. You can now look up all locales for a given country code, for example

Click here to view code image

List<Locale> swissLocales = countryToLocales.get("CH"); // Yields locales de_CH, fr_CH, it_CH and maybe more


Note

A quick refresher on locales: Each locale has a language code (such as en for English) and a country code (such as US for the United States). The locale en_US describes English in the United States, and en_IE is English in Ireland. Some countries have multiple locales. For example, ga_IE is Gaelic in Ireland, and, as the preceding example shows, the JDK knows at least three locales in Switzerland.



When the classifier function is a predicate function (that is, a function returning a boolean value), the stream elements are partitioned into two lists: those where the function returns true and the complement. In this case, it is more efficient to use partitioningBy instead of groupingBy. For example, here we split all locales into those that use English and all others:

Click here to view code image

Map<Boolean, List<Locale>> englishAndOtherLocales = locales.collect( Collectors.partitioningBy(l -> l.getLanguage().equals("en"))); List<Locale> englishLocales = englishAndOtherLocales.get(true);


Note

If you call the groupingByConcurrent method, you get a concurrent map that, when used with a parallel stream, is concurrently populated. This is entirely analogous to the toConcurrentMap method.




java.util.stream.Collectors 8

static <T,K> Collector<T,?,Map<K,List<T>>> groupingBy(Function<? super T,? extends K> classifier)



static <T,K> Collector<T,?,ConcurrentMap<K,List<T>>> groupingByConcurrent(Function<? super T,? extends K> classifier)

yields a collector that produces a map or concurrent map whose keys are the results of applying classifier to all collected elements, and whose values are lists of elements with the same key.



static <T> Collector<T,?,Map<Boolean,List<T>>> partitioningBy(Predicate<? super T> predicate)

yields a collector that produces a map whose keys are true/false, and whose values are lists of the elements that fulfill/do not fulfill the predicate.





1.11 Downstream Collectors


The groupingBy method yields a map whose values are lists. If you want to process those lists in some way, supply a downstream collector. For example, if you want sets instead of lists, you can use the Collectors.toSet collector that you saw in the preceding section:



Click here to view code image

Map<String, Set<Locale>> countryToLocaleSet = locales.collect( groupingBy(Locale::getCountry, toSet()));


Note

In this example, as well as the remaining examples of this section, I assume a static import of java.util.stream.Collectors.* to make the expressions easier to read.



Several collectors are provided for reducing collected elements to numbers:

counting produces a count of the collected elements. For example,

Click here to view code image

Map<String, Long> countryToLocaleCounts = locales.collect( groupingBy(Locale::getCountry, counting()));

counts how many locales there are for each country.



summing(Int|Long|Double) takes a function argument, applies the function to the downstream elements, and produces their sum. For example,

Click here to view code image

Map<String, Integer> stateToCityPopulation = cities.collect( groupingBy(City::getState, summingInt(City::getPopulation)));

computes the sum of populations per state in a stream of cities.



maxBy and minBy take a comparator and produce maximum and minimum of the downstream elements. For example,

Click here to view code image

Map<String, Optional<City>> stateToLargestCity = cities.collect( groupingBy(City::getStat