[{"data":1,"prerenderedAt":7862},["ShallowReactive",2],{"post:\u002Fposts\u002Fjava\u002Fjava-functionalprogramming-notes":3,"posts:ordered":7574},{"id":4,"title":5,"body":6,"category":7556,"cover":7557,"date":7558,"description":7559,"extension":7560,"featured":7561,"meta":7562,"navigation":378,"path":7563,"seo":7564,"series":7565,"seriesOrder":79,"stem":7566,"tags":7567,"__hash__":7573},"posts\u002Fposts\u002Fjava\u002Fjava-functionalprogramming-notes.md","Java 函数式编程笔记：Lambda、函数式接口与 Streams",{"type":7,"value":8,"toc":7505},"minimark",[9,16,21,26,34,119,334,341,344,348,351,620,628,631,635,638,677,684,688,773,778,797,807,811,815,818,877,884,928,932,938,953,956,1065,1069,1072,1127,1149,1156,1165,1184,1191,1233,1236,1240,1243,1263,1270,1274,1281,1318,1354,1392,1396,1402,1492,1500,1572,1676,1764,1877,1943,1946,1950,1954,1961,1970,1973,1976,2055,2059,2257,2260,2378,2382,2389,2492,2498,2523,2527,2533,2627,2633,2640,2644,2654,2777,2780,2784,2789,2950,2962,2966,2970,2973,3074,3077,3149,3157,3161,3164,3170,3220,3235,3239,3255,3261,3406,3412,3431,3435,3438,3449,3456,3549,3555,3565,3569,3573,3709,3715,3742,3746,3764,3829,3835,3858,3862,3872,3963,3969,3982,3988,4018,4022,4026,4126,4139,4146,4194,4200,4226,4238,4243,4310,4317,4334,4375,4378,4434,4440,4463,4473,4496,4502,4505,4573,4579,4618,4623,4689,4696,4709,4764,4770,4773,4834,4847,4852,5033,5039,5049,5055,5167,5173,5179,5262,5290,5301,5474,5480,5490,5494,5498,5621,5630,5636,5701,5711,5728,5841,5847,5858,5866,5869,5879,5960,5966,5975,6032,6037,6042,6109,6115,6130,6134,6139,6212,6218,6233,6245,6248,6378,6384,6405,6416,6549,6555,6561,6625,6631,6643,6748,6760,6770,6776,6781,6880,6886,6891,6948,6954,6965,6973,7042,7048,7051,7058,7066,7121,7127,7133,7137,7140,7214,7220,7236,7240,7243,7347,7353,7383,7387,7453,7458,7495,7501],[10,11,12],"blockquote",{},[13,14,15],"p",{},"这是一份个人学习笔记：讲解部分是我按自己的理解写的，示例代码也都是自己写的，输出结果是本地跑出来的。参考主要是 Oracle 的 Java 官方文档与语言规范，理解有偏差的地方欢迎指出。",[17,18,20],"h1",{"id":19},"_01-开篇问题打印最年轻的-3-个人的全名","0.1 开篇问题：打印最年轻的 3 个人的全名",[22,23,25],"h2",{"id":24},"_011-数据","0.1.1 数据",[13,27,28,29,33],{},"假设有一个 ",[30,31,32],"code",{},"Person"," 类的列表：",[35,36,41],"pre",{"className":37,"code":38,"language":39,"meta":40,"style":40},"language-java shiki shiki-themes github-light github-dark","class Person {\n    private String firstName;\n    private String lastName;\n    private String gender;\n    private int age;\n    private int kids;\n    \u002F\u002F 构造函数与 getter 省略\n}\n","java","",[30,42,43,60,69,77,85,96,106,113],{"__ignoreMap":40},[44,45,48,52,56],"span",{"class":46,"line":47},"line",1,[44,49,51],{"class":50},"szBVR","class",[44,53,55],{"class":54},"sScJk"," Person",[44,57,59],{"class":58},"sVt8B"," {\n",[44,61,63,66],{"class":46,"line":62},2,[44,64,65],{"class":50},"    private",[44,67,68],{"class":58}," String firstName;\n",[44,70,72,74],{"class":46,"line":71},3,[44,73,65],{"class":50},[44,75,76],{"class":58}," String lastName;\n",[44,78,80,82],{"class":46,"line":79},4,[44,81,65],{"class":50},[44,83,84],{"class":58}," String gender;\n",[44,86,88,90,93],{"class":46,"line":87},5,[44,89,65],{"class":50},[44,91,92],{"class":50}," int",[44,94,95],{"class":58}," age;\n",[44,97,99,101,103],{"class":46,"line":98},6,[44,100,65],{"class":50},[44,102,92],{"class":50},[44,104,105],{"class":58}," kids;\n",[44,107,109],{"class":46,"line":108},7,[44,110,112],{"class":111},"sJ8bj","    \u002F\u002F 构造函数与 getter 省略\n",[44,114,116],{"class":46,"line":115},8,[44,117,118],{"class":58},"}\n",[35,120,122],{"className":37,"code":121,"language":39,"meta":40,"style":40},"static List\u003CPerson> people() {\n    return new ArrayList\u003C>(List.of(\n            new Person(\"Alice\", \"Wong\", \"F\", 31, 2),\n            new Person(\"Bryan\", \"Chua\", \"M\", 24, 0),\n            new Person(\"Cindy\", \"Tay\", \"F\", 45, 3),\n            new Person(\"Daniel\", \"Koh\", \"M\", 38, 2),\n            new Person(\"Ethan\", \"Goh\", \"M\", 27, 2)));\n}\n",[30,123,124,146,163,202,235,267,298,330],{"__ignoreMap":40},[44,125,126,129,132,135,137,140,143],{"class":46,"line":47},[44,127,128],{"class":50},"static",[44,130,131],{"class":58}," List",[44,133,134],{"class":50},"\u003C",[44,136,32],{"class":58},[44,138,139],{"class":50},">",[44,141,142],{"class":54}," people",[44,144,145],{"class":58},"() {\n",[44,147,148,151,154,157,160],{"class":46,"line":62},[44,149,150],{"class":50},"    return",[44,152,153],{"class":50}," new",[44,155,156],{"class":58}," ArrayList\u003C>(List.",[44,158,159],{"class":54},"of",[44,161,162],{"class":58},"(\n",[44,164,165,168,170,173,177,180,183,185,188,190,194,196,199],{"class":46,"line":71},[44,166,167],{"class":50},"            new",[44,169,55],{"class":54},[44,171,172],{"class":58},"(",[44,174,176],{"class":175},"sZZnC","\"Alice\"",[44,178,179],{"class":58},", ",[44,181,182],{"class":175},"\"Wong\"",[44,184,179],{"class":58},[44,186,187],{"class":175},"\"F\"",[44,189,179],{"class":58},[44,191,193],{"class":192},"sj4cs","31",[44,195,179],{"class":58},[44,197,198],{"class":192},"2",[44,200,201],{"class":58},"),\n",[44,203,204,206,208,210,213,215,218,220,223,225,228,230,233],{"class":46,"line":79},[44,205,167],{"class":50},[44,207,55],{"class":54},[44,209,172],{"class":58},[44,211,212],{"class":175},"\"Bryan\"",[44,214,179],{"class":58},[44,216,217],{"class":175},"\"Chua\"",[44,219,179],{"class":58},[44,221,222],{"class":175},"\"M\"",[44,224,179],{"class":58},[44,226,227],{"class":192},"24",[44,229,179],{"class":58},[44,231,232],{"class":192},"0",[44,234,201],{"class":58},[44,236,237,239,241,243,246,248,251,253,255,257,260,262,265],{"class":46,"line":87},[44,238,167],{"class":50},[44,240,55],{"class":54},[44,242,172],{"class":58},[44,244,245],{"class":175},"\"Cindy\"",[44,247,179],{"class":58},[44,249,250],{"class":175},"\"Tay\"",[44,252,179],{"class":58},[44,254,187],{"class":175},[44,256,179],{"class":58},[44,258,259],{"class":192},"45",[44,261,179],{"class":58},[44,263,264],{"class":192},"3",[44,266,201],{"class":58},[44,268,269,271,273,275,278,280,283,285,287,289,292,294,296],{"class":46,"line":98},[44,270,167],{"class":50},[44,272,55],{"class":54},[44,274,172],{"class":58},[44,276,277],{"class":175},"\"Daniel\"",[44,279,179],{"class":58},[44,281,282],{"class":175},"\"Koh\"",[44,284,179],{"class":58},[44,286,222],{"class":175},[44,288,179],{"class":58},[44,290,291],{"class":192},"38",[44,293,179],{"class":58},[44,295,198],{"class":192},[44,297,201],{"class":58},[44,299,300,302,304,306,309,311,314,316,318,320,323,325,327],{"class":46,"line":108},[44,301,167],{"class":50},[44,303,55],{"class":54},[44,305,172],{"class":58},[44,307,308],{"class":175},"\"Ethan\"",[44,310,179],{"class":58},[44,312,313],{"class":175},"\"Goh\"",[44,315,179],{"class":58},[44,317,222],{"class":175},[44,319,179],{"class":58},[44,321,322],{"class":192},"27",[44,324,179],{"class":58},[44,326,198],{"class":192},[44,328,329],{"class":58},")));\n",[44,331,332],{"class":46,"line":115},[44,333,118],{"class":58},[13,335,336,337],{},"要回答的问题是：",[338,339,340],"strong",{},"怎么打印这 5 个人里最年轻的 3 个人的全名？",[13,342,343],{},"思路可以用一句话说清：先按年龄升序排好，再取前 3 个，最后把每个人转成全名打印出来。下面看两种实现方式。",[22,345,347],{"id":346},"_012-命令式imperative写法","0.1.2 命令式（Imperative）写法",[13,349,350],{},"按老办法，排序、取前 3、拼全名要分别写出来：",[35,352,354],{"className":37,"code":353,"language":39,"meta":40,"style":40},"List\u003CPerson> persons = people();\n\n\u002F\u002F 1. 按年龄升序排序（冒泡排序）\nfor (int i = 0; i \u003C persons.size(); i++) {\n    for (int j = i + 1; j \u003C persons.size(); j++) {\n        if (persons.get(j).getAge() \u003C persons.get(i).getAge()) {\n            Person temp = persons.get(i);\n            persons.set(i, persons.get(j));\n            persons.set(j, temp);\n        }\n    }\n}\n\n\u002F\u002F 2. 取前 3 个人，3. 拼成全名\nfor (int i = 0; i \u003C 3; i++) {\n    System.out.println(persons.get(i).getFullName());\n}\n",[30,355,356,374,380,385,424,462,496,510,526,536,542,548,553,558,564,593,615],{"__ignoreMap":40},[44,357,358,361,363,366,369,371],{"class":46,"line":47},[44,359,360],{"class":58},"List\u003C",[44,362,32],{"class":50},[44,364,365],{"class":58},"> persons ",[44,367,368],{"class":50},"=",[44,370,142],{"class":54},[44,372,373],{"class":58},"();\n",[44,375,376],{"class":46,"line":62},[44,377,379],{"emptyLinePlaceholder":378},true,"\n",[44,381,382],{"class":46,"line":71},[44,383,384],{"class":111},"\u002F\u002F 1. 按年龄升序排序（冒泡排序）\n",[44,386,387,390,393,396,399,401,404,407,409,412,415,418,421],{"class":46,"line":79},[44,388,389],{"class":50},"for",[44,391,392],{"class":58}," (",[44,394,395],{"class":50},"int",[44,397,398],{"class":58}," i ",[44,400,368],{"class":50},[44,402,403],{"class":192}," 0",[44,405,406],{"class":58},"; i ",[44,408,134],{"class":50},[44,410,411],{"class":58}," persons.",[44,413,414],{"class":54},"size",[44,416,417],{"class":58},"(); i",[44,419,420],{"class":50},"++",[44,422,423],{"class":58},") {\n",[44,425,426,429,431,433,436,438,440,443,446,449,451,453,455,458,460],{"class":46,"line":87},[44,427,428],{"class":50},"    for",[44,430,392],{"class":58},[44,432,395],{"class":50},[44,434,435],{"class":58}," j ",[44,437,368],{"class":50},[44,439,398],{"class":58},[44,441,442],{"class":50},"+",[44,444,445],{"class":192}," 1",[44,447,448],{"class":58},"; j ",[44,450,134],{"class":50},[44,452,411],{"class":58},[44,454,414],{"class":54},[44,456,457],{"class":58},"(); j",[44,459,420],{"class":50},[44,461,423],{"class":58},[44,463,464,467,470,473,476,479,482,484,486,488,491,493],{"class":46,"line":98},[44,465,466],{"class":50},"        if",[44,468,469],{"class":58}," (persons.",[44,471,472],{"class":54},"get",[44,474,475],{"class":58},"(j).",[44,477,478],{"class":54},"getAge",[44,480,481],{"class":58},"() ",[44,483,134],{"class":50},[44,485,411],{"class":58},[44,487,472],{"class":54},[44,489,490],{"class":58},"(i).",[44,492,478],{"class":54},[44,494,495],{"class":58},"()) {\n",[44,497,498,501,503,505,507],{"class":46,"line":108},[44,499,500],{"class":58},"            Person temp ",[44,502,368],{"class":50},[44,504,411],{"class":58},[44,506,472],{"class":54},[44,508,509],{"class":58},"(i);\n",[44,511,512,515,518,521,523],{"class":46,"line":115},[44,513,514],{"class":58},"            persons.",[44,516,517],{"class":54},"set",[44,519,520],{"class":58},"(i, persons.",[44,522,472],{"class":54},[44,524,525],{"class":58},"(j));\n",[44,527,529,531,533],{"class":46,"line":528},9,[44,530,514],{"class":58},[44,532,517],{"class":54},[44,534,535],{"class":58},"(j, temp);\n",[44,537,539],{"class":46,"line":538},10,[44,540,541],{"class":58},"        }\n",[44,543,545],{"class":46,"line":544},11,[44,546,547],{"class":58},"    }\n",[44,549,551],{"class":46,"line":550},12,[44,552,118],{"class":58},[44,554,556],{"class":46,"line":555},13,[44,557,379],{"emptyLinePlaceholder":378},[44,559,561],{"class":46,"line":560},14,[44,562,563],{"class":111},"\u002F\u002F 2. 取前 3 个人，3. 拼成全名\n",[44,565,567,569,571,573,575,577,579,581,583,586,589,591],{"class":46,"line":566},15,[44,568,389],{"class":50},[44,570,392],{"class":58},[44,572,395],{"class":50},[44,574,398],{"class":58},[44,576,368],{"class":50},[44,578,403],{"class":192},[44,580,406],{"class":58},[44,582,134],{"class":50},[44,584,585],{"class":192}," 3",[44,587,588],{"class":58},"; i",[44,590,420],{"class":50},[44,592,423],{"class":58},[44,594,596,599,602,605,607,609,612],{"class":46,"line":595},16,[44,597,598],{"class":58},"    System.out.",[44,600,601],{"class":54},"println",[44,603,604],{"class":58},"(persons.",[44,606,472],{"class":54},[44,608,490],{"class":58},[44,610,611],{"class":54},"getFullName",[44,613,614],{"class":58},"());\n",[44,616,618],{"class":46,"line":617},17,[44,619,118],{"class":58},[35,621,626],{"className":622,"code":624,"language":625,"meta":40},[623],"language-text","Bryan Chua\nEthan Goh\nAlice Wong\n","text",[30,627,624],{"__ignoreMap":40},[13,629,630],{},"代码能跑，但要读的人先得看懂双重循环是在排序，再看懂后面在取前 3 个——「想做什么」被埋在「怎么做」里面。",[22,632,634],{"id":633},"_013-sql-的思路","0.1.3 SQL 的思路",[13,636,637],{},"如果数据在 MySQL 里，同一件事一句话就写完了：",[35,639,643],{"className":640,"code":641,"language":642,"meta":40,"style":40},"language-sql shiki shiki-themes github-light github-dark","SELECT name\nFROM persons\nORDER BY age\nLIMIT 3\n","sql",[30,644,645,653,661,669],{"__ignoreMap":40},[44,646,647,650],{"class":46,"line":47},[44,648,649],{"class":50},"SELECT",[44,651,652],{"class":50}," name\n",[44,654,655,658],{"class":46,"line":62},[44,656,657],{"class":50},"FROM",[44,659,660],{"class":58}," persons\n",[44,662,663,666],{"class":46,"line":71},[44,664,665],{"class":50},"ORDER BY",[44,667,668],{"class":58}," age\n",[44,670,671,674],{"class":46,"line":79},[44,672,673],{"class":50},"LIMIT",[44,675,676],{"class":192}," 3\n",[13,678,679,680,683],{},"只描述结果的样子：按 ",[30,681,682],{},"age"," 排序、只要 3 条。Java 里能不能也这么写？可以，这就是 Streams（流）。",[22,685,687],{"id":686},"_014-stream-写法","0.1.4 Stream 写法",[35,689,691],{"className":37,"code":690,"language":39,"meta":40,"style":40},"people().stream()\n        .sorted(Comparator.comparing(Person::getAge))\n        .limit(3)\n        .map(Person::getFullName)\n        .forEach(System.out::println);\n",[30,692,693,707,730,744,758],{"__ignoreMap":40},[44,694,695,698,701,704],{"class":46,"line":47},[44,696,697],{"class":54},"people",[44,699,700],{"class":58},"().",[44,702,703],{"class":54},"stream",[44,705,706],{"class":58},"()\n",[44,708,709,712,715,718,721,724,727],{"class":46,"line":62},[44,710,711],{"class":58},"        .",[44,713,714],{"class":54},"sorted",[44,716,717],{"class":58},"(Comparator.",[44,719,720],{"class":54},"comparing",[44,722,723],{"class":58},"(Person",[44,725,726],{"class":50},"::",[44,728,729],{"class":58},"getAge))\n",[44,731,732,734,737,739,741],{"class":46,"line":71},[44,733,711],{"class":58},[44,735,736],{"class":54},"limit",[44,738,172],{"class":58},[44,740,264],{"class":192},[44,742,743],{"class":58},")\n",[44,745,746,748,751,753,755],{"class":46,"line":79},[44,747,711],{"class":58},[44,749,750],{"class":54},"map",[44,752,723],{"class":58},[44,754,726],{"class":50},[44,756,757],{"class":58},"getFullName)\n",[44,759,760,762,765,768,770],{"class":46,"line":87},[44,761,711],{"class":58},[44,763,764],{"class":54},"forEach",[44,766,767],{"class":58},"(System.out",[44,769,726],{"class":50},[44,771,772],{"class":58},"println);\n",[35,774,776],{"className":775,"code":624,"language":625,"meta":40},[623],[30,777,624],{"__ignoreMap":40},[13,779,780,781,783,784,786,787,783,789,786,791,793,794,796],{},"和上面交给命令式的输出完全一致，但代码几乎能和 SQL 一句句对上：",[30,782,714],{}," 对应 ",[30,785,665],{},"、",[30,788,736],{},[30,790,673],{},[30,792,750],{}," 是把每个元素换成一个新值、",[30,795,764],{}," 是逐个处理。",[13,798,799,800,803,804,806],{},"这份链式调用里有 ",[30,801,802],{},"->"," 和 ",[30,805,726],{}," 这两种没见过的写法，后面几节就按「Lambda → 函数式接口 → Streams」的顺序把它们拆开。",[17,808,810],{"id":809},"_02-lambda-表达式lambda-expressions","0.2 Lambda 表达式（Lambda Expressions）",[22,812,814],{"id":813},"_021-从方法定义说起","0.2.1 从方法定义说起",[13,816,817],{},"一个方法定义由五部分组成：方法名、参数、返回类型、方法体、返回值。",[35,819,821],{"className":37,"code":820,"language":39,"meta":40,"style":40},"int max(int num1, int num2) {\n    if (num1 > num2) {\n        return num1;\n    }\n    return num2;\n}\n",[30,822,823,842,854,862,866,873],{"__ignoreMap":40},[44,824,825,827,830,832,834,837,839],{"class":46,"line":47},[44,826,395],{"class":50},[44,828,829],{"class":54}," max",[44,831,172],{"class":58},[44,833,395],{"class":50},[44,835,836],{"class":58}," num1, ",[44,838,395],{"class":50},[44,840,841],{"class":58}," num2) {\n",[44,843,844,847,850,852],{"class":46,"line":62},[44,845,846],{"class":50},"    if",[44,848,849],{"class":58}," (num1 ",[44,851,139],{"class":50},[44,853,841],{"class":58},[44,855,856,859],{"class":46,"line":71},[44,857,858],{"class":50},"        return",[44,860,861],{"class":58}," num1;\n",[44,863,864],{"class":46,"line":79},[44,865,547],{"class":58},[44,867,868,870],{"class":46,"line":87},[44,869,150],{"class":50},[44,871,872],{"class":58}," num2;\n",[44,874,875],{"class":46,"line":98},[44,876,118],{"class":58},[13,878,879,880,883],{},"在 Stream 的链式调用里，很多「方法」其实只在这一个地方用一次，专门为它取个名字、写个类显得多余。Lambda 表达式（Lambda Expression）就是",[338,881,882],{},"匿名方法（Anonymous Method）的简写形式","：把方法名和返回类型去掉，只留下参数和体。",[35,885,887],{"className":37,"code":886,"language":39,"meta":40,"style":40},"(num1, num2) -> {\n    if (num1 > num2) {\n        return num1;\n    }\n    return num2;\n}\n",[30,888,889,898,908,914,918,924],{"__ignoreMap":40},[44,890,891,894,896],{"class":46,"line":47},[44,892,893],{"class":58},"(num1, num2) ",[44,895,802],{"class":50},[44,897,59],{"class":58},[44,899,900,902,904,906],{"class":46,"line":62},[44,901,846],{"class":50},[44,903,849],{"class":58},[44,905,139],{"class":50},[44,907,841],{"class":58},[44,909,910,912],{"class":46,"line":71},[44,911,858],{"class":50},[44,913,861],{"class":58},[44,915,916],{"class":46,"line":79},[44,917,547],{"class":58},[44,919,920,922],{"class":46,"line":87},[44,921,150],{"class":50},[44,923,872],{"class":58},[44,925,926],{"class":46,"line":98},[44,927,118],{"class":58},[22,929,931],{"id":930},"_022-语法与五步简化","0.2.2 语法与五步简化",[13,933,934,935,937],{},"基本语法是「参数列表 + 箭头（",[30,936,802],{},"） + 方法体」：",[35,939,941],{"className":37,"code":940,"language":39,"meta":40,"style":40},"(parameter list) -> { statements }\n",[30,942,943],{"__ignoreMap":40},[44,944,945,948,950],{"class":46,"line":47},[44,946,947],{"class":58},"(parameter list) ",[44,949,802],{"class":50},[44,951,952],{"class":58}," { statements }\n",[13,954,955],{},"从完整的方法定义到最短的 lambda，一共可以省五样东西：",[957,958,959,975],"table",{},[960,961,962],"thead",{},[963,964,965,969,972],"tr",{},[966,967,968],"th",{},"步骤",[966,970,971],{},"去掉什么",[966,973,974],{},"例子",[976,977,978,996,1011,1031,1050],"tbody",{},[963,979,980,984,987],{},[981,982,983],"td",{},"1",[981,985,986],{},"方法名与返回类型（变成匿名方法）",[981,988,989,992,993],{},[30,990,991],{},"int add(int e1, int e2) { return e1 + e2; }"," → ",[30,994,995],{},"(e1, e2) -> { return e1 + e2; }",[963,997,998,1000,1003],{},[981,999,198],{},[981,1001,1002],{},"参数类型",[981,1004,1005,992,1008],{},[30,1006,1007],{},"(int e1, int e2) -> ...",[30,1009,1010],{},"(e1, e2) -> ...",[963,1012,1013,1015,1024],{},[981,1014,264],{},[981,1016,1017,1018,803,1021],{},"方法体只有一个表达式时，省掉 ",[30,1019,1020],{},"return",[30,1022,1023],{},"{}",[981,1025,1026,992,1028],{},[30,1027,995],{},[30,1029,1030],{},"(e1, e2) -> e1 + e2",[963,1032,1033,1036,1042],{},[981,1034,1035],{},"4",[981,1037,1038,1039],{},"只有一个参数时，省掉参数括号 ",[30,1040,1041],{},"()",[981,1043,1044,992,1047],{},[30,1045,1046],{},"(message) -> System.out.println(message)",[30,1048,1049],{},"message -> System.out.println(message)",[963,1051,1052,1055,1058],{},[981,1053,1054],{},"5",[981,1056,1057],{},"只调用一个已有方法时，改写成方法引用（Method Reference）",[981,1059,1060,992,1062],{},[30,1061,1049],{},[30,1063,1064],{},"System.out::println",[22,1066,1068],{"id":1067},"_023-省略参数类型","0.2.3 省略参数类型",[13,1070,1071],{},"参数类型通常都可以省略，由编译器（Compiler）根据 lambda 所在的位置推断（Infer）。例如：",[35,1073,1075],{"className":37,"code":1074,"language":39,"meta":40,"style":40},"people().stream()\n        .filter(p -> p.getKids() == 2)\n        .forEach(System.out::println);\n",[30,1076,1077,1087,1115],{"__ignoreMap":40},[44,1078,1079,1081,1083,1085],{"class":46,"line":47},[44,1080,697],{"class":54},[44,1082,700],{"class":58},[44,1084,703],{"class":54},[44,1086,706],{"class":58},[44,1088,1089,1091,1094,1097,1099,1102,1105,1107,1110,1113],{"class":46,"line":62},[44,1090,711],{"class":58},[44,1092,1093],{"class":54},"filter",[44,1095,1096],{"class":58},"(p ",[44,1098,802],{"class":50},[44,1100,1101],{"class":58}," p.",[44,1103,1104],{"class":54},"getKids",[44,1106,481],{"class":58},[44,1108,1109],{"class":50},"==",[44,1111,1112],{"class":192}," 2",[44,1114,743],{"class":58},[44,1116,1117,1119,1121,1123,1125],{"class":46,"line":71},[44,1118,711],{"class":58},[44,1120,764],{"class":54},[44,1122,767],{"class":58},[44,1124,726],{"class":50},[44,1126,772],{"class":58},[13,1128,1129,1132,1133,1135,1136,1138,1139,1141,1142,1145,1146],{},[30,1130,1131],{},"filter()"," 要求传入的是一个「接收元素、返回布尔值」的方法，而这个流里的元素是 ",[30,1134,32],{},"，所以 ",[30,1137,13],{}," 一定是 ",[30,1140,32],{},"，",[30,1143,1144],{},"p.getKids()"," 才能调用。",[338,1147,1148],{},"lambda 本身提供不了类型信息，是它的使用位置提供了。",[22,1150,1152,1153,1155],{"id":1151},"_024-省略-return-与花括号","0.2.4 省略 ",[30,1154,1020],{}," 与花括号",[13,1157,1158,1159,1161,1162,1164],{},"方法体只有一个表达式（Expression）时，",[30,1160,1020],{}," 关键字和 ",[30,1163,1023],{}," 都可以省掉：",[35,1166,1168],{"className":37,"code":1167,"language":39,"meta":40,"style":40},"(num1, num2) -> num1 + num2\n",[30,1169,1170],{"__ignoreMap":40},[44,1171,1172,1174,1176,1179,1181],{"class":46,"line":47},[44,1173,893],{"class":58},[44,1175,802],{"class":50},[44,1177,1178],{"class":58}," num1 ",[44,1180,442],{"class":50},[44,1182,1183],{"class":58}," num2\n",[13,1185,1186,1187,1190],{},"但方法体里有多条语句（比如包含 ",[30,1188,1189],{},"if","）时不能省，因为这时候必须明确「返回什么」：",[35,1192,1193],{"className":37,"code":886,"language":39,"meta":40,"style":40},[30,1194,1195,1203,1213,1219,1223,1229],{"__ignoreMap":40},[44,1196,1197,1199,1201],{"class":46,"line":47},[44,1198,893],{"class":58},[44,1200,802],{"class":50},[44,1202,59],{"class":58},[44,1204,1205,1207,1209,1211],{"class":46,"line":62},[44,1206,846],{"class":50},[44,1208,849],{"class":58},[44,1210,139],{"class":50},[44,1212,841],{"class":58},[44,1214,1215,1217],{"class":46,"line":71},[44,1216,858],{"class":50},[44,1218,861],{"class":58},[44,1220,1221],{"class":46,"line":79},[44,1222,547],{"class":58},[44,1224,1225,1227],{"class":46,"line":87},[44,1226,150],{"class":50},[44,1228,872],{"class":58},[44,1230,1231],{"class":46,"line":98},[44,1232,118],{"class":58},[13,1234,1235],{},"返回类型也一样是从上下文推断出来的，lambda 自己不用写。",[22,1237,1239],{"id":1238},"_025-只有一个参数时可以省括号","0.2.5 只有一个参数时可以省括号",[13,1241,1242],{},"参数列表只有一个参数时，参数括号也可以省：",[35,1244,1246],{"className":37,"code":1245,"language":39,"meta":40,"style":40},"message -> System.out.println(message)\n",[30,1247,1248],{"__ignoreMap":40},[44,1249,1250,1253,1255,1258,1260],{"class":46,"line":47},[44,1251,1252],{"class":58},"message ",[44,1254,802],{"class":50},[44,1256,1257],{"class":58}," System.out.",[44,1259,601],{"class":54},[44,1261,1262],{"class":58},"(message)\n",[13,1264,1265,1266,1269],{},"没有参数时括号不能省，要写成 ",[30,1267,1268],{},"() -> ...","。",[22,1271,1273],{"id":1272},"_026-方法引用method-reference","0.2.6 方法引用（Method Reference）",[13,1275,1276,1277,1280],{},"如果 lambda 什么都没做，只是把参数原样传给另一个已有的方法，就可以直接引用那个方法，写成 ",[30,1278,1279],{},"ClassName::methodName","：",[35,1282,1284],{"className":37,"code":1283,"language":39,"meta":40,"style":40},"\u002F\u002F 原来\nmessage -> System.out.println(message)\n\u002F\u002F 方法引用\nSystem.out::println\n",[30,1285,1286,1291,1303,1308],{"__ignoreMap":40},[44,1287,1288],{"class":46,"line":47},[44,1289,1290],{"class":111},"\u002F\u002F 原来\n",[44,1292,1293,1295,1297,1299,1301],{"class":46,"line":62},[44,1294,1252],{"class":58},[44,1296,802],{"class":50},[44,1298,1257],{"class":58},[44,1300,601],{"class":54},[44,1302,1262],{"class":58},[44,1304,1305],{"class":46,"line":71},[44,1306,1307],{"class":111},"\u002F\u002F 方法引用\n",[44,1309,1310,1313,1315],{"class":46,"line":79},[44,1311,1312],{"class":58},"System.out",[44,1314,726],{"class":50},[44,1316,1317],{"class":58},"println\n",[35,1319,1321],{"className":37,"code":1320,"language":39,"meta":40,"style":40},"\u002F\u002F 原来\np -> p.getFirstName()\n\u002F\u002F 方法引用\nPerson::getFirstName\n",[30,1322,1323,1327,1341,1345],{"__ignoreMap":40},[44,1324,1325],{"class":46,"line":47},[44,1326,1290],{"class":111},[44,1328,1329,1332,1334,1336,1339],{"class":46,"line":62},[44,1330,1331],{"class":58},"p ",[44,1333,802],{"class":50},[44,1335,1101],{"class":58},[44,1337,1338],{"class":54},"getFirstName",[44,1340,706],{"class":58},[44,1342,1343],{"class":46,"line":71},[44,1344,1307],{"class":111},[44,1346,1347,1349,1351],{"class":46,"line":79},[44,1348,32],{"class":58},[44,1350,726],{"class":50},[44,1352,1353],{"class":58},"getFirstName\n",[35,1355,1357],{"className":37,"code":1356,"language":39,"meta":40,"style":40},"\u002F\u002F 原来\n(a, b) -> a.compareByAge(b)\n\u002F\u002F 方法引用\nPerson::compareByAge\n",[30,1358,1359,1363,1379,1383],{"__ignoreMap":40},[44,1360,1361],{"class":46,"line":47},[44,1362,1290],{"class":111},[44,1364,1365,1368,1370,1373,1376],{"class":46,"line":62},[44,1366,1367],{"class":58},"(a, b) ",[44,1369,802],{"class":50},[44,1371,1372],{"class":58}," a.",[44,1374,1375],{"class":54},"compareByAge",[44,1377,1378],{"class":58},"(b)\n",[44,1380,1381],{"class":46,"line":71},[44,1382,1307],{"class":111},[44,1384,1385,1387,1389],{"class":46,"line":79},[44,1386,32],{"class":58},[44,1388,726],{"class":50},[44,1390,1391],{"class":58},"compareByAge\n",[22,1393,1395],{"id":1394},"_027-练习把方法改写成最短形式","0.2.7 练习：把方法改写成最短形式",[13,1397,1398,1401],{},[338,1399,1400],{},"题目","：把下面几个方法改写成最短形态的 lambda 或方法引用。",[35,1403,1405],{"className":37,"code":1404,"language":39,"meta":40,"style":40},"int pow(int base, int exp) {\n    int res = 1;\n    for (int i = 0; i \u003C exp; i++) {\n        res *= base;\n    }\n    return res;\n}\n",[30,1406,1407,1426,1441,1466,1477,1481,1488],{"__ignoreMap":40},[44,1408,1409,1411,1414,1416,1418,1421,1423],{"class":46,"line":47},[44,1410,395],{"class":50},[44,1412,1413],{"class":54}," pow",[44,1415,172],{"class":58},[44,1417,395],{"class":50},[44,1419,1420],{"class":58}," base, ",[44,1422,395],{"class":50},[44,1424,1425],{"class":58}," exp) {\n",[44,1427,1428,1431,1434,1436,1438],{"class":46,"line":62},[44,1429,1430],{"class":50},"    int",[44,1432,1433],{"class":58}," res ",[44,1435,368],{"class":50},[44,1437,445],{"class":192},[44,1439,1440],{"class":58},";\n",[44,1442,1443,1445,1447,1449,1451,1453,1455,1457,1459,1462,1464],{"class":46,"line":71},[44,1444,428],{"class":50},[44,1446,392],{"class":58},[44,1448,395],{"class":50},[44,1450,398],{"class":58},[44,1452,368],{"class":50},[44,1454,403],{"class":192},[44,1456,406],{"class":58},[44,1458,134],{"class":50},[44,1460,1461],{"class":58}," exp; i",[44,1463,420],{"class":50},[44,1465,423],{"class":58},[44,1467,1468,1471,1474],{"class":46,"line":79},[44,1469,1470],{"class":58},"        res ",[44,1472,1473],{"class":50},"*=",[44,1475,1476],{"class":58}," base;\n",[44,1478,1479],{"class":46,"line":87},[44,1480,547],{"class":58},[44,1482,1483,1485],{"class":46,"line":98},[44,1484,150],{"class":50},[44,1486,1487],{"class":58}," res;\n",[44,1489,1490],{"class":46,"line":108},[44,1491,118],{"class":58},[13,1493,1494,1495,803,1497,1499],{},"方法体是多条语句，所以 ",[30,1496,1023],{},[30,1498,1020],{}," 不能省：",[35,1501,1503],{"className":37,"code":1502,"language":39,"meta":40,"style":40},"(base, exp) -> {\n    int res = 1;\n    for (int i = 0; i \u003C exp; i++) {\n        res *= base;\n    }\n    return res;\n}\n",[30,1504,1505,1514,1526,1550,1558,1562,1568],{"__ignoreMap":40},[44,1506,1507,1510,1512],{"class":46,"line":47},[44,1508,1509],{"class":58},"(base, exp) ",[44,1511,802],{"class":50},[44,1513,59],{"class":58},[44,1515,1516,1518,1520,1522,1524],{"class":46,"line":62},[44,1517,1430],{"class":50},[44,1519,1433],{"class":58},[44,1521,368],{"class":50},[44,1523,445],{"class":192},[44,1525,1440],{"class":58},[44,1527,1528,1530,1532,1534,1536,1538,1540,1542,1544,1546,1548],{"class":46,"line":71},[44,1529,428],{"class":50},[44,1531,392],{"class":58},[44,1533,395],{"class":50},[44,1535,398],{"class":58},[44,1537,368],{"class":50},[44,1539,403],{"class":192},[44,1541,406],{"class":58},[44,1543,134],{"class":50},[44,1545,1461],{"class":58},[44,1547,420],{"class":50},[44,1549,423],{"class":58},[44,1551,1552,1554,1556],{"class":46,"line":79},[44,1553,1470],{"class":58},[44,1555,1473],{"class":50},[44,1557,1476],{"class":58},[44,1559,1560],{"class":46,"line":87},[44,1561,547],{"class":58},[44,1563,1564,1566],{"class":46,"line":98},[44,1565,150],{"class":50},[44,1567,1487],{"class":58},[44,1569,1570],{"class":46,"line":108},[44,1571,118],{"class":58},[35,1573,1575],{"className":37,"code":1574,"language":39,"meta":40,"style":40},"boolean isPrime(int num) {\n    for (int divisor = 2; divisor \u003C num - 1; divisor++) {\n        if (num % divisor == 0) {\n            return false;\n        }\n    }\n    return true;\n}\n",[30,1576,1577,1592,1627,1645,1655,1659,1663,1672],{"__ignoreMap":40},[44,1578,1579,1582,1585,1587,1589],{"class":46,"line":47},[44,1580,1581],{"class":50},"boolean",[44,1583,1584],{"class":54}," isPrime",[44,1586,172],{"class":58},[44,1588,395],{"class":50},[44,1590,1591],{"class":58}," num) {\n",[44,1593,1594,1596,1598,1600,1603,1605,1607,1610,1612,1615,1618,1620,1623,1625],{"class":46,"line":62},[44,1595,428],{"class":50},[44,1597,392],{"class":58},[44,1599,395],{"class":50},[44,1601,1602],{"class":58}," divisor ",[44,1604,368],{"class":50},[44,1606,1112],{"class":192},[44,1608,1609],{"class":58},"; divisor ",[44,1611,134],{"class":50},[44,1613,1614],{"class":58}," num ",[44,1616,1617],{"class":50},"-",[44,1619,445],{"class":192},[44,1621,1622],{"class":58},"; divisor",[44,1624,420],{"class":50},[44,1626,423],{"class":58},[44,1628,1629,1631,1634,1637,1639,1641,1643],{"class":46,"line":71},[44,1630,466],{"class":50},[44,1632,1633],{"class":58}," (num ",[44,1635,1636],{"class":50},"%",[44,1638,1602],{"class":58},[44,1640,1109],{"class":50},[44,1642,403],{"class":192},[44,1644,423],{"class":58},[44,1646,1647,1650,1653],{"class":46,"line":79},[44,1648,1649],{"class":50},"            return",[44,1651,1652],{"class":192}," false",[44,1654,1440],{"class":58},[44,1656,1657],{"class":46,"line":87},[44,1658,541],{"class":58},[44,1660,1661],{"class":46,"line":98},[44,1662,547],{"class":58},[44,1664,1665,1667,1670],{"class":46,"line":108},[44,1666,150],{"class":50},[44,1668,1669],{"class":192}," true",[44,1671,1440],{"class":58},[44,1673,1674],{"class":46,"line":115},[44,1675,118],{"class":58},[35,1677,1679],{"className":37,"code":1678,"language":39,"meta":40,"style":40},"num -> {\n    for (int divisor = 2; divisor \u003C num - 1; divisor++) {\n        if (num % divisor == 0) {\n            return false;\n        }\n    }\n    return true;\n}\n",[30,1680,1681,1690,1720,1736,1744,1748,1752,1760],{"__ignoreMap":40},[44,1682,1683,1686,1688],{"class":46,"line":47},[44,1684,1685],{"class":58},"num ",[44,1687,802],{"class":50},[44,1689,59],{"class":58},[44,1691,1692,1694,1696,1698,1700,1702,1704,1706,1708,1710,1712,1714,1716,1718],{"class":46,"line":62},[44,1693,428],{"class":50},[44,1695,392],{"class":58},[44,1697,395],{"class":50},[44,1699,1602],{"class":58},[44,1701,368],{"class":50},[44,1703,1112],{"class":192},[44,1705,1609],{"class":58},[44,1707,134],{"class":50},[44,1709,1614],{"class":58},[44,1711,1617],{"class":50},[44,1713,445],{"class":192},[44,1715,1622],{"class":58},[44,1717,420],{"class":50},[44,1719,423],{"class":58},[44,1721,1722,1724,1726,1728,1730,1732,1734],{"class":46,"line":71},[44,1723,466],{"class":50},[44,1725,1633],{"class":58},[44,1727,1636],{"class":50},[44,1729,1602],{"class":58},[44,1731,1109],{"class":50},[44,1733,403],{"class":192},[44,1735,423],{"class":58},[44,1737,1738,1740,1742],{"class":46,"line":79},[44,1739,1649],{"class":50},[44,1741,1652],{"class":192},[44,1743,1440],{"class":58},[44,1745,1746],{"class":46,"line":87},[44,1747,541],{"class":58},[44,1749,1750],{"class":46,"line":98},[44,1751,547],{"class":58},[44,1753,1754,1756,1758],{"class":46,"line":108},[44,1755,150],{"class":50},[44,1757,1669],{"class":192},[44,1759,1440],{"class":58},[44,1761,1762],{"class":46,"line":115},[44,1763,118],{"class":58},[35,1765,1767],{"className":37,"code":1766,"language":39,"meta":40,"style":40},"String getFullName(Person p) {\n    return p.getFirstName() + \" \" + p.getLastName();\n}\n\nvoid printFullName(Person p) {\n    System.out.println(p.getFirstName() + \" \" + p.getLastName());\n}\n\nString getFirstName(Person p) {\n    return p.getFirstName();\n}\n",[30,1768,1769,1779,1804,1808,1812,1822,1847,1851,1855,1863,1873],{"__ignoreMap":40},[44,1770,1771,1774,1776],{"class":46,"line":47},[44,1772,1773],{"class":58},"String ",[44,1775,611],{"class":54},[44,1777,1778],{"class":58},"(Person p) {\n",[44,1780,1781,1783,1785,1787,1789,1791,1794,1797,1799,1802],{"class":46,"line":62},[44,1782,150],{"class":50},[44,1784,1101],{"class":58},[44,1786,1338],{"class":54},[44,1788,481],{"class":58},[44,1790,442],{"class":50},[44,1792,1793],{"class":175}," \" \"",[44,1795,1796],{"class":50}," +",[44,1798,1101],{"class":58},[44,1800,1801],{"class":54},"getLastName",[44,1803,373],{"class":58},[44,1805,1806],{"class":46,"line":71},[44,1807,118],{"class":58},[44,1809,1810],{"class":46,"line":79},[44,1811,379],{"emptyLinePlaceholder":378},[44,1813,1814,1817,1820],{"class":46,"line":87},[44,1815,1816],{"class":50},"void",[44,1818,1819],{"class":54}," printFullName",[44,1821,1778],{"class":58},[44,1823,1824,1826,1828,1831,1833,1835,1837,1839,1841,1843,1845],{"class":46,"line":98},[44,1825,598],{"class":58},[44,1827,601],{"class":54},[44,1829,1830],{"class":58},"(p.",[44,1832,1338],{"class":54},[44,1834,481],{"class":58},[44,1836,442],{"class":50},[44,1838,1793],{"class":175},[44,1840,1796],{"class":50},[44,1842,1101],{"class":58},[44,1844,1801],{"class":54},[44,1846,614],{"class":58},[44,1848,1849],{"class":46,"line":108},[44,1850,118],{"class":58},[44,1852,1853],{"class":46,"line":115},[44,1854,379],{"emptyLinePlaceholder":378},[44,1856,1857,1859,1861],{"class":46,"line":528},[44,1858,1773],{"class":58},[44,1860,1338],{"class":54},[44,1862,1778],{"class":58},[44,1864,1865,1867,1869,1871],{"class":46,"line":538},[44,1866,150],{"class":50},[44,1868,1101],{"class":58},[44,1870,1338],{"class":54},[44,1872,373],{"class":58},[44,1874,1875],{"class":46,"line":544},[44,1876,118],{"class":58},[35,1878,1880],{"className":37,"code":1879,"language":39,"meta":40,"style":40},"p -> p.getFirstName() + \" \" + p.getLastName()\np -> System.out.println(p.getFirstName() + \" \" + p.getLastName())\nPerson::getFirstName\n",[30,1881,1882,1906,1935],{"__ignoreMap":40},[44,1883,1884,1886,1888,1890,1892,1894,1896,1898,1900,1902,1904],{"class":46,"line":47},[44,1885,1331],{"class":58},[44,1887,802],{"class":50},[44,1889,1101],{"class":58},[44,1891,1338],{"class":54},[44,1893,481],{"class":58},[44,1895,442],{"class":50},[44,1897,1793],{"class":175},[44,1899,1796],{"class":50},[44,1901,1101],{"class":58},[44,1903,1801],{"class":54},[44,1905,706],{"class":58},[44,1907,1908,1910,1912,1914,1916,1918,1920,1922,1924,1926,1928,1930,1932],{"class":46,"line":62},[44,1909,1331],{"class":58},[44,1911,802],{"class":50},[44,1913,1257],{"class":58},[44,1915,601],{"class":54},[44,1917,1830],{"class":58},[44,1919,1338],{"class":54},[44,1921,481],{"class":58},[44,1923,442],{"class":50},[44,1925,1793],{"class":175},[44,1927,1796],{"class":50},[44,1929,1101],{"class":58},[44,1931,1801],{"class":54},[44,1933,1934],{"class":58},"())\n",[44,1936,1937,1939,1941],{"class":46,"line":71},[44,1938,32],{"class":58},[44,1940,726],{"class":50},[44,1942,1353],{"class":58},[13,1944,1945],{},"最后一个能改成方法引用，是因为它只做了「调用一个已有方法，并把结果返回」这一件事。",[17,1947,1949],{"id":1948},"_03-函数式接口functional-interfaces","0.3 函数式接口（Functional Interfaces）",[22,1951,1953],{"id":1952},"_031-概念","0.3.1 概念",[13,1955,1956,1957,1960],{},"Lambda 交给编译器之后，编译器得知道「这个方法的参数是什么类型、返回什么、是几个参数」，否则没法检查代码。这个信息由",[338,1958,1959],{},"函数式接口（Functional Interface）"," 提供：它规定了这个位置该传一个什么样子的方法。",[10,1962,1963],{},[13,1964,1965,1966,1969],{},"函数式接口是",[338,1967,1968],{},"有且仅有一个抽象方法（Abstract Method）"," 的接口。这个方法叫函数式方法（Functional Method）。",[13,1971,1972],{},"为什么必须只有一个？因为 lambda 省掉了方法名，如果接口里有两个抽象方法，编译器就无法判断这份 lambda 是在实现哪一个。反过来，只要接口只有一个抽象方法，lambda 就能被映射（Map）到它上面。",[13,1974,1975],{},"看那个链式调用，每个方法要什么，其实都被对应的函数式接口写死了：",[35,1977,1979],{"className":37,"code":1978,"language":39,"meta":40,"style":40},"people().stream()\n        .sorted(Comparator.comparing(Person::getAge)) \u002F\u002F 需要一个能比较两个元素的方法\n        .limit(3)\n        .map(Person::getFullName)                     \u002F\u002F 需要一个有返回值（非 void）的方法\n        .forEach(System.out::println);                 \u002F\u002F 需要一个返回 void 的方法\n",[30,1980,1981,1991,2011,2023,2039],{"__ignoreMap":40},[44,1982,1983,1985,1987,1989],{"class":46,"line":47},[44,1984,697],{"class":54},[44,1986,700],{"class":58},[44,1988,703],{"class":54},[44,1990,706],{"class":58},[44,1992,1993,1995,1997,1999,2001,2003,2005,2008],{"class":46,"line":62},[44,1994,711],{"class":58},[44,1996,714],{"class":54},[44,1998,717],{"class":58},[44,2000,720],{"class":54},[44,2002,723],{"class":58},[44,2004,726],{"class":50},[44,2006,2007],{"class":58},"getAge)) ",[44,2009,2010],{"class":111},"\u002F\u002F 需要一个能比较两个元素的方法\n",[44,2012,2013,2015,2017,2019,2021],{"class":46,"line":71},[44,2014,711],{"class":58},[44,2016,736],{"class":54},[44,2018,172],{"class":58},[44,2020,264],{"class":192},[44,2022,743],{"class":58},[44,2024,2025,2027,2029,2031,2033,2036],{"class":46,"line":79},[44,2026,711],{"class":58},[44,2028,750],{"class":54},[44,2030,723],{"class":58},[44,2032,726],{"class":50},[44,2034,2035],{"class":58},"getFullName)                     ",[44,2037,2038],{"class":111},"\u002F\u002F 需要一个有返回值（非 void）的方法\n",[44,2040,2041,2043,2045,2047,2049,2052],{"class":46,"line":87},[44,2042,711],{"class":58},[44,2044,764],{"class":54},[44,2046,767],{"class":58},[44,2048,726],{"class":50},[44,2050,2051],{"class":58},"println);                 ",[44,2053,2054],{"class":111},"\u002F\u002F 需要一个返回 void 的方法\n",[22,2056,2058],{"id":2057},"_032-常见函数式接口一览","0.3.2 常见函数式接口一览",[957,2060,2061,2080],{},[960,2062,2063],{},[963,2064,2065,2068,2071,2074,2077],{},[966,2066,2067],{},"接口（Interface）",[966,2069,2070],{},"方法（Method）",[966,2072,2073],{},"参数（Argument）",[966,2075,2076],{},"返回（Return）",[966,2078,2079],{},"用途",[976,2081,2082,2109,2135,2161,2185,2209,2234],{},[963,2083,2084,2089,2094,2099,2103],{},[981,2085,2086],{},[30,2087,2088],{},"Consumer\u003CT>",[981,2090,2091],{},[30,2092,2093],{},"accept",[981,2095,2096],{},[30,2097,2098],{},"T",[981,2100,2101],{},[30,2102,1816],{},[981,2104,2105,2106,2108],{},"对 ",[30,2107,2098],{}," 执行一个动作，例如打印",[963,2110,2111,2116,2121,2125,2130],{},[981,2112,2113],{},[30,2114,2115],{},"Function\u003CT,R>",[981,2117,2118],{},[30,2119,2120],{},"apply",[981,2122,2123],{},[30,2124,2098],{},[981,2126,2127],{},[30,2128,2129],{},"R",[981,2131,2105,2132,2134],{},[30,2133,2098],{}," 调用方法并把结果返回",[963,2136,2137,2142,2147,2151,2155],{},[981,2138,2139],{},[30,2140,2141],{},"Predicate\u003CT>",[981,2143,2144],{},[30,2145,2146],{},"test",[981,2148,2149],{},[30,2150,2098],{},[981,2152,2153],{},[30,2154,1581],{},[981,2156,2157,2158,2160],{},"判断 ",[30,2159,2098],{}," 是否满足条件",[963,2162,2163,2168,2172,2175,2179],{},[981,2164,2165],{},[30,2166,2167],{},"Supplier\u003CT>",[981,2169,2170],{},[30,2171,472],{},[981,2173,2174],{},"无",[981,2176,2177],{},[30,2178,2098],{},[981,2180,2181,2182,2184],{},"产生一个 ",[30,2183,2098],{}," 类型的值",[963,2186,2187,2192,2196,2200,2204],{},[981,2188,2189],{},[30,2190,2191],{},"UnaryOperator\u003CT>",[981,2193,2194],{},[30,2195,2120],{},[981,2197,2198],{},[30,2199,2098],{},[981,2201,2202],{},[30,2203,2098],{},[981,2205,2105,2206,2208],{},[30,2207,2098],{}," 做运算并返回同类型的值",[963,2210,2211,2216,2220,2225,2229],{},[981,2212,2213],{},[30,2214,2215],{},"BinaryOperator\u003CT>",[981,2217,2218],{},[30,2219,2120],{},[981,2221,2222],{},[30,2223,2224],{},"T, T",[981,2226,2227],{},[30,2228,2098],{},[981,2230,2231,2232,2208],{},"对两个 ",[30,2233,2098],{},[963,2235,2236,2241,2246,2250,2254],{},[981,2237,2238],{},[30,2239,2240],{},"Comparator\u003CT>",[981,2242,2243],{},[30,2244,2245],{},"compare",[981,2247,2248],{},[30,2249,2224],{},[981,2251,2252],{},[30,2253,395],{},[981,2255,2256],{},"规定两个对象的先后顺序",[13,2258,2259],{},"接口的声明都很短，例如：",[35,2261,2263],{"className":37,"code":2262,"language":39,"meta":40,"style":40},"interface Consumer\u003CT> {\n    void accept(T t);\n}\n\ninterface Predicate\u003CT> {\n    boolean test(T t);\n}\n\ninterface BinaryOperator\u003CT> {\n    T apply(T t1, T t2);\n}\n",[30,2264,2265,2280,2298,2302,2306,2319,2333,2337,2341,2354,2374],{"__ignoreMap":40},[44,2266,2267,2270,2273,2275,2277],{"class":46,"line":47},[44,2268,2269],{"class":50},"interface",[44,2271,2272],{"class":54}," Consumer",[44,2274,134],{"class":58},[44,2276,2098],{"class":50},[44,2278,2279],{"class":58},"> {\n",[44,2281,2282,2285,2288,2291,2295],{"class":46,"line":62},[44,2283,2284],{"class":50},"    void",[44,2286,2287],{"class":54}," accept",[44,2289,2290],{"class":58},"(T ",[44,2292,2294],{"class":2293},"s4XuR","t",[44,2296,2297],{"class":58},");\n",[44,2299,2300],{"class":46,"line":71},[44,2301,118],{"class":58},[44,2303,2304],{"class":46,"line":79},[44,2305,379],{"emptyLinePlaceholder":378},[44,2307,2308,2310,2313,2315,2317],{"class":46,"line":87},[44,2309,2269],{"class":50},[44,2311,2312],{"class":54}," Predicate",[44,2314,134],{"class":58},[44,2316,2098],{"class":50},[44,2318,2279],{"class":58},[44,2320,2321,2324,2327,2329,2331],{"class":46,"line":98},[44,2322,2323],{"class":50},"    boolean",[44,2325,2326],{"class":54}," test",[44,2328,2290],{"class":58},[44,2330,2294],{"class":2293},[44,2332,2297],{"class":58},[44,2334,2335],{"class":46,"line":108},[44,2336,118],{"class":58},[44,2338,2339],{"class":46,"line":115},[44,2340,379],{"emptyLinePlaceholder":378},[44,2342,2343,2345,2348,2350,2352],{"class":46,"line":528},[44,2344,2269],{"class":50},[44,2346,2347],{"class":54}," BinaryOperator",[44,2349,134],{"class":58},[44,2351,2098],{"class":50},[44,2353,2279],{"class":58},[44,2355,2356,2359,2361,2363,2366,2369,2372],{"class":46,"line":538},[44,2357,2358],{"class":58},"    T ",[44,2360,2120],{"class":54},[44,2362,2290],{"class":58},[44,2364,2365],{"class":2293},"t1",[44,2367,2368],{"class":58},", T ",[44,2370,2371],{"class":2293},"t2",[44,2373,2297],{"class":58},[44,2375,2376],{"class":46,"line":544},[44,2377,118],{"class":58},[22,2379,2381],{"id":2380},"_033-用匿名内部类实例化","0.3.3 用匿名内部类实例化",[13,2383,2384,2385,2388],{},"函数式接口也是接口，所以老办法依然可用：用 ",[30,2386,2387],{},"new"," 实例化，并当场实现它的抽象方法——这叫匿名内部类（Anonymous Inner Class）。",[35,2390,2392],{"className":37,"code":2391,"language":39,"meta":40,"style":40},"Consumer\u003CInteger> anonymous = new Consumer\u003CInteger>() {\n    @Override\n    public void accept(Integer num) {\n        System.out.println(\"anonymous inner class: \" + num);\n    }\n};\nStream.of(1, 2).forEach(anonymous);\n",[30,2393,2394,2417,2425,2443,2460,2464,2469],{"__ignoreMap":40},[44,2395,2396,2399,2402,2405,2407,2409,2412,2414],{"class":46,"line":47},[44,2397,2398],{"class":58},"Consumer\u003C",[44,2400,2401],{"class":50},"Integer",[44,2403,2404],{"class":58},"> anonymous ",[44,2406,368],{"class":50},[44,2408,153],{"class":50},[44,2410,2411],{"class":58}," Consumer\u003C",[44,2413,2401],{"class":50},[44,2415,2416],{"class":58},">() {\n",[44,2418,2419,2422],{"class":46,"line":62},[44,2420,2421],{"class":58},"    @",[44,2423,2424],{"class":50},"Override\n",[44,2426,2427,2430,2433,2435,2438,2441],{"class":46,"line":71},[44,2428,2429],{"class":50},"    public",[44,2431,2432],{"class":50}," void",[44,2434,2287],{"class":54},[44,2436,2437],{"class":58},"(Integer ",[44,2439,2440],{"class":2293},"num",[44,2442,423],{"class":58},[44,2444,2445,2448,2450,2452,2455,2457],{"class":46,"line":79},[44,2446,2447],{"class":58},"        System.out.",[44,2449,601],{"class":54},[44,2451,172],{"class":58},[44,2453,2454],{"class":175},"\"anonymous inner class: \"",[44,2456,1796],{"class":50},[44,2458,2459],{"class":58}," num);\n",[44,2461,2462],{"class":46,"line":87},[44,2463,547],{"class":58},[44,2465,2466],{"class":46,"line":98},[44,2467,2468],{"class":58},"};\n",[44,2470,2471,2474,2476,2478,2480,2482,2484,2487,2489],{"class":46,"line":108},[44,2472,2473],{"class":58},"Stream.",[44,2475,159],{"class":54},[44,2477,172],{"class":58},[44,2479,983],{"class":192},[44,2481,179],{"class":58},[44,2483,198],{"class":192},[44,2485,2486],{"class":58},").",[44,2488,764],{"class":54},[44,2490,2491],{"class":58},"(anonymous);\n",[35,2493,2496],{"className":2494,"code":2495,"language":625,"meta":40},[623],"anonymous inner class: 1\nanonymous inner class: 2\n",[30,2497,2495],{"__ignoreMap":40},[13,2499,2500,2501,2503,2504,2506,2507,2509,2510,2512,2513,2516,2517,2519,2520,1269],{},"写法上的三个要点：",[30,2502,2387],{}," 后面跟接口名；",[30,2505,2088],{}," 是泛型接口，实例化时要指定 ",[30,2508,2098],{},"（这里是 ",[30,2511,2401],{},"）；实现的 ",[30,2514,2515],{},"accept(T)"," 在指定 ",[30,2518,2098],{}," 之后变成 ",[30,2521,2522],{},"accept(Integer)",[22,2524,2526],{"id":2525},"_034-用-lambda-或方法引用实例化","0.3.4 用 lambda 或方法引用实例化",[13,2528,2529,2530,2532],{},"因为 ",[30,2531,2088],{}," 只有一个抽象方法，上面那个匿名内部类可以一路简化成 lambda，再简化成方法引用，效果一样：",[35,2534,2536],{"className":37,"code":2535,"language":39,"meta":40,"style":40},"Consumer\u003CInteger> lambda = num -> System.out.println(\"lambda: \" + num);\nConsumer\u003CInteger> methodRef = System.out::println;\n\nStream.of(1, 2).forEach(lambda);\nStream.of(3).forEach(methodRef);\n",[30,2537,2538,2566,2585,2589,2610],{"__ignoreMap":40},[44,2539,2540,2542,2544,2547,2549,2551,2553,2555,2557,2559,2562,2564],{"class":46,"line":47},[44,2541,2398],{"class":58},[44,2543,2401],{"class":50},[44,2545,2546],{"class":58},"> lambda ",[44,2548,368],{"class":50},[44,2550,1614],{"class":58},[44,2552,802],{"class":50},[44,2554,1257],{"class":58},[44,2556,601],{"class":54},[44,2558,172],{"class":58},[44,2560,2561],{"class":175},"\"lambda: \"",[44,2563,1796],{"class":50},[44,2565,2459],{"class":58},[44,2567,2568,2570,2572,2575,2577,2580,2582],{"class":46,"line":62},[44,2569,2398],{"class":58},[44,2571,2401],{"class":50},[44,2573,2574],{"class":58},"> methodRef ",[44,2576,368],{"class":50},[44,2578,2579],{"class":58}," System.out",[44,2581,726],{"class":50},[44,2583,2584],{"class":58},"println;\n",[44,2586,2587],{"class":46,"line":71},[44,2588,379],{"emptyLinePlaceholder":378},[44,2590,2591,2593,2595,2597,2599,2601,2603,2605,2607],{"class":46,"line":79},[44,2592,2473],{"class":58},[44,2594,159],{"class":54},[44,2596,172],{"class":58},[44,2598,983],{"class":192},[44,2600,179],{"class":58},[44,2602,198],{"class":192},[44,2604,2486],{"class":58},[44,2606,764],{"class":54},[44,2608,2609],{"class":58},"(lambda);\n",[44,2611,2612,2614,2616,2618,2620,2622,2624],{"class":46,"line":87},[44,2613,2473],{"class":58},[44,2615,159],{"class":54},[44,2617,172],{"class":58},[44,2619,264],{"class":192},[44,2621,2486],{"class":58},[44,2623,764],{"class":54},[44,2625,2626],{"class":58},"(methodRef);\n",[35,2628,2631],{"className":2629,"code":2630,"language":625,"meta":40},[623],"lambda: 1\nlambda: 2\n3\n",[30,2632,2630],{"__ignoreMap":40},[13,2634,2635,2636,2639],{},"反过来说，看到 ",[30,2637,2638],{},"num -> System.out.println(num)"," 这样的 lambda，编译器要判断它对应哪个接口，靠的也是「哪个函数式接口的参数个数、参数类型和返回类型对得上」。这也是为什么前面说函数式接口是「模板」。",[22,2641,2643],{"id":2642},"_035-把实例作为方法参数","0.3.5 把实例作为方法参数",[13,2645,2646,2647,2650,2651,2653],{},"函数式接口最常见的用法就是当方法参数。比如 ",[30,2648,2649],{},"forEach()"," 要的就是一个 ",[30,2652,2088],{}," 实例，所以下面两种写法完全等价：",[35,2655,2657],{"className":37,"code":2656,"language":39,"meta":40,"style":40},"Integer[] arr = {1, 2, 3, 4};\n\nArrays.stream(arr).forEach(new Consumer\u003CInteger>() {\n    @Override\n    public void accept(Integer num) {\n        System.out.println(num);\n    }\n});\n\nArrays.stream(arr).forEach(num -> System.out.println(num));\n",[30,2658,2659,2687,2691,2713,2719,2733,2742,2746,2751,2755],{"__ignoreMap":40},[44,2660,2661,2663,2666,2668,2671,2673,2675,2677,2679,2681,2683,2685],{"class":46,"line":47},[44,2662,2401],{"class":50},[44,2664,2665],{"class":58},"[] arr ",[44,2667,368],{"class":50},[44,2669,2670],{"class":58}," {",[44,2672,983],{"class":192},[44,2674,179],{"class":58},[44,2676,198],{"class":192},[44,2678,179],{"class":58},[44,2680,264],{"class":192},[44,2682,179],{"class":58},[44,2684,1035],{"class":192},[44,2686,2468],{"class":58},[44,2688,2689],{"class":46,"line":62},[44,2690,379],{"emptyLinePlaceholder":378},[44,2692,2693,2696,2698,2701,2703,2705,2707,2709,2711],{"class":46,"line":71},[44,2694,2695],{"class":58},"Arrays.",[44,2697,703],{"class":54},[44,2699,2700],{"class":58},"(arr).",[44,2702,764],{"class":54},[44,2704,172],{"class":58},[44,2706,2387],{"class":50},[44,2708,2411],{"class":58},[44,2710,2401],{"class":50},[44,2712,2416],{"class":58},[44,2714,2715,2717],{"class":46,"line":79},[44,2716,2421],{"class":58},[44,2718,2424],{"class":50},[44,2720,2721,2723,2725,2727,2729,2731],{"class":46,"line":87},[44,2722,2429],{"class":50},[44,2724,2432],{"class":50},[44,2726,2287],{"class":54},[44,2728,2437],{"class":58},[44,2730,2440],{"class":2293},[44,2732,423],{"class":58},[44,2734,2735,2737,2739],{"class":46,"line":98},[44,2736,2447],{"class":58},[44,2738,601],{"class":54},[44,2740,2741],{"class":58},"(num);\n",[44,2743,2744],{"class":46,"line":108},[44,2745,547],{"class":58},[44,2747,2748],{"class":46,"line":115},[44,2749,2750],{"class":58},"});\n",[44,2752,2753],{"class":46,"line":528},[44,2754,379],{"emptyLinePlaceholder":378},[44,2756,2757,2759,2761,2763,2765,2768,2770,2772,2774],{"class":46,"line":538},[44,2758,2695],{"class":58},[44,2760,703],{"class":54},[44,2762,2700],{"class":58},[44,2764,764],{"class":54},[44,2766,2767],{"class":58},"(num ",[44,2769,802],{"class":50},[44,2771,1257],{"class":58},[44,2773,601],{"class":54},[44,2775,2776],{"class":58},"(num));\n",[13,2778,2779],{},"既然 lambda 更短，日常就只用 lambda 或方法引用的写法。",[22,2781,2783],{"id":2782},"_036-练习lambda-能映射到哪个函数式接口","0.3.6 练习：lambda 能映射到哪个函数式接口",[13,2785,2786,2788],{},[338,2787,1400],{},"：下列 lambda 分别可以映射到哪个（些）函数式接口？",[957,2790,2791,2804],{},[960,2792,2793],{},[963,2794,2795,2798,2801],{},[966,2796,2797],{},"lambda",[966,2799,2800],{},"答案",[966,2802,2803],{},"理由",[976,2805,2806,2822,2837,2851,2865,2879,2909,2923,2937],{},[963,2807,2808,2813,2817],{},[981,2809,2810],{},[30,2811,2812],{},"e -> e > 3 && e % 2 == 0",[981,2814,2815],{},[30,2816,2141],{},[981,2818,2819,2820],{},"一个参数、返回 ",[30,2821,1581],{},[963,2823,2824,2829,2833],{},[981,2825,2826],{},[30,2827,2828],{},"e -> System.out.println(e.getFullName())",[981,2830,2831],{},[30,2832,2088],{},[981,2834,2819,2835],{},[30,2836,1816],{},[963,2838,2839,2844,2848],{},[981,2840,2841],{},[30,2842,2843],{},"x -> x.length()",[981,2845,2846],{},[30,2847,2115],{},[981,2849,2850],{},"一个参数、返回非 void",[963,2852,2853,2858,2862],{},[981,2854,2855],{},[30,2856,2857],{},"(x, y) -> x + \", \" + y",[981,2859,2860],{},[30,2861,2215],{},[981,2863,2864],{},"两个参数、返回与参数同类型",[963,2866,2867,2872,2876],{},[981,2868,2869],{},[30,2870,2871],{},"() -> LocalDateTime.now()",[981,2873,2874],{},[30,2875,2167],{},[981,2877,2878],{},"无参数、有返回值",[963,2880,2881,2886,2893],{},[981,2882,2883],{},[30,2884,2885],{},"e -> e.toString()",[981,2887,2888,2890,2891],{},[30,2889,2115],{}," 或 ",[30,2892,2191],{},[981,2894,2895,2896,2898,2899,2902,2903,2905,2906,2908],{},"接收 ",[30,2897,2098],{}," 返回 ",[30,2900,2901],{},"String","，若 ",[30,2904,2098],{}," 就是 ",[30,2907,2901],{}," 则两者都对得上",[963,2910,2911,2916,2920],{},[981,2912,2913],{},[30,2914,2915],{},"x -> x * x",[981,2917,2918],{},[30,2919,2191],{},[981,2921,2922],{},"参数与返回值同类型",[963,2924,2925,2930,2934],{},[981,2926,2927],{},[30,2928,2929],{},"Math::abs",[981,2931,2932],{},[30,2933,2191],{},[981,2935,2936],{},"一个参数、返回同类型",[963,2938,2939,2944,2948],{},[981,2940,2941],{},[30,2942,2943],{},"() -> { List\u003CInteger> l = new ArrayList\u003C>(); l.add(1); return l; }",[981,2945,2946],{},[30,2947,2167],{},[981,2949,2878],{},[13,2951,2952,2953],{},"判断方法是固定的三连问：",[338,2954,2955,2956,2958,2959,2961],{},"几个参数？返回不返回（",[30,2957,1816],{}," 还是非 ",[30,2960,1816],{},"）？参数和返回值的类型是什么？",[17,2963,2965],{"id":2964},"_04-streams流","0.4 Streams（流）",[22,2967,2969],{"id":2968},"_041-从-for-循环到流","0.4.1 从 for 循环到流",[13,2971,2972],{},"处理集合的老套路是「遍历元素 + 对每个元素做点事」：",[35,2974,2976],{"className":37,"code":2975,"language":39,"meta":40,"style":40},"public static int sumOfEven(int[] arr) {\n    int sum = 0;\n    for (int num : arr) {\n        if (num % 2 == 0) {\n            sum += num;\n        }\n    }\n    return sum;\n}\n",[30,2977,2978,2998,3011,3027,3044,3055,3059,3063,3070],{"__ignoreMap":40},[44,2979,2980,2983,2986,2988,2991,2993,2995],{"class":46,"line":47},[44,2981,2982],{"class":50},"public",[44,2984,2985],{"class":50}," static",[44,2987,92],{"class":50},[44,2989,2990],{"class":54}," sumOfEven",[44,2992,172],{"class":58},[44,2994,395],{"class":50},[44,2996,2997],{"class":58},"[] arr) {\n",[44,2999,3000,3002,3005,3007,3009],{"class":46,"line":62},[44,3001,1430],{"class":50},[44,3003,3004],{"class":58}," sum ",[44,3006,368],{"class":50},[44,3008,403],{"class":192},[44,3010,1440],{"class":58},[44,3012,3013,3015,3017,3019,3021,3024],{"class":46,"line":71},[44,3014,428],{"class":50},[44,3016,392],{"class":58},[44,3018,395],{"class":50},[44,3020,1614],{"class":58},[44,3022,3023],{"class":50},":",[44,3025,3026],{"class":58}," arr) {\n",[44,3028,3029,3031,3033,3035,3037,3040,3042],{"class":46,"line":79},[44,3030,466],{"class":50},[44,3032,1633],{"class":58},[44,3034,1636],{"class":50},[44,3036,1112],{"class":192},[44,3038,3039],{"class":50}," ==",[44,3041,403],{"class":192},[44,3043,423],{"class":58},[44,3045,3046,3049,3052],{"class":46,"line":87},[44,3047,3048],{"class":58},"            sum ",[44,3050,3051],{"class":50},"+=",[44,3053,3054],{"class":58}," num;\n",[44,3056,3057],{"class":46,"line":98},[44,3058,541],{"class":58},[44,3060,3061],{"class":46,"line":108},[44,3062,547],{"class":58},[44,3064,3065,3067],{"class":46,"line":115},[44,3066,150],{"class":50},[44,3068,3069],{"class":58}," sum;\n",[44,3071,3072],{"class":46,"line":528},[44,3073,118],{"class":58},[13,3075,3076],{},"用 Stream（流）改写，只描述想要什么：",[35,3078,3080],{"className":37,"code":3079,"language":39,"meta":40,"style":40},"public static int sumOfEven2(int[] arr) {\n    return IntStream.of(arr)\n            .filter(x -> x % 2 == 0)\n            .sum();\n}\n",[30,3081,3082,3099,3111,3136,3145],{"__ignoreMap":40},[44,3083,3084,3086,3088,3090,3093,3095,3097],{"class":46,"line":47},[44,3085,2982],{"class":50},[44,3087,2985],{"class":50},[44,3089,92],{"class":50},[44,3091,3092],{"class":54}," sumOfEven2",[44,3094,172],{"class":58},[44,3096,395],{"class":50},[44,3098,2997],{"class":58},[44,3100,3101,3103,3106,3108],{"class":46,"line":62},[44,3102,150],{"class":50},[44,3104,3105],{"class":58}," IntStream.",[44,3107,159],{"class":54},[44,3109,3110],{"class":58},"(arr)\n",[44,3112,3113,3116,3118,3121,3123,3126,3128,3130,3132,3134],{"class":46,"line":71},[44,3114,3115],{"class":58},"            .",[44,3117,1093],{"class":54},[44,3119,3120],{"class":58},"(x ",[44,3122,802],{"class":50},[44,3124,3125],{"class":58}," x ",[44,3127,1636],{"class":50},[44,3129,1112],{"class":192},[44,3131,3039],{"class":50},[44,3133,403],{"class":192},[44,3135,743],{"class":58},[44,3137,3138,3140,3143],{"class":46,"line":79},[44,3139,3115],{"class":58},[44,3141,3142],{"class":54},"sum",[44,3144,373],{"class":58},[44,3146,3147],{"class":46,"line":87},[44,3148,118],{"class":58},[10,3150,3151],{},[13,3152,3153,3154],{},"Streams（流）是一串元素序列，我们对这串元素执行任务。",[338,3155,3156],{},"只写要做什么（what to do），至于怎么做（how to do）交给流去处理。",[22,3158,3160],{"id":3159},"_042-流流水线stream-pipeline","0.4.2 流流水线（Stream Pipeline）",[13,3162,3163],{},"一次完整的流操作由三段组成：",[35,3165,3168],{"className":3166,"code":3167,"language":625,"meta":40},[623],"Source（源） -> Create Stream（创建流） -> Intermediate Operations（中间操作） -> Terminal Operation（终止操作） -> Result（结果）\n",[30,3169,3167],{"__ignoreMap":40},[957,3171,3172,3185],{},[960,3173,3174],{},[963,3175,3176,3179,3182],{},[966,3177,3178],{},"阶段",[966,3180,3181],{},"内容",[966,3183,3184],{},"作用",[976,3186,3187,3198,3209],{},[963,3188,3189,3192,3195],{},[981,3190,3191],{},"源（Source）",[981,3193,3194],{},"通常是一个数组或一个集合",[981,3196,3197],{},"提供元素",[963,3199,3200,3203,3206],{},[981,3201,3202],{},"中间操作（Intermediate Operations）",[981,3204,3205],{},"过滤、排序、类型转换、映射……",[981,3207,3208],{},"描述对元素做哪些处理，可以有 N 个",[963,3210,3211,3214,3217],{},[981,3212,3213],{},"终止操作（Terminal Operation）",[981,3215,3216],{},"汇总结果，例如计数、求和、收集成集合",[981,3218,3219],{},"产生结果，只能有一个",[13,3221,3222,3223,3226,3227,3230,3231,3234],{},"对照着看那段求和代码：",[30,3224,3225],{},"IntStream.of(arr)"," 是创建流，",[30,3228,3229],{},"filter(...)"," 是中间操作，",[30,3232,3233],{},"sum()"," 是终止操作（把流聚合成了一个值）。",[22,3236,3238],{"id":3237},"_043-惰性-vs-急切lazy-vs-eager","0.4.3 惰性 vs 急切（Lazy vs Eager）",[3240,3241,3242,3249],"ul",{},[3243,3244,3245,3248],"li",{},[338,3246,3247],{},"中间操作是惰性的（Lazy）","：定义它的那一刻什么都不做，要等到终止操作被调用才真正执行。",[3243,3250,3251,3254],{},[338,3252,3253],{},"终止操作是急切的（Eager）","：一调用就立即执行。",[13,3256,3257,3258,3260],{},"可以留个「脚印」来验证这一点——在 ",[30,3259,1093],{}," 里打印：",[35,3262,3264],{"className":37,"code":3263,"language":39,"meta":40,"style":40},"System.out.println(\"pipeline built, but nothing printed yet\");\nStream\u003CInteger> filtered = IntStream.of(2, 7, 4).boxed()\n        .filter(n -> {\n            System.out.println(\"checking \" + n);\n            return n % 2 == 0;\n        });\nSystem.out.println(\"terminal operation starts\");\nfiltered.forEach(n -> System.out.println(\"kept \" + n));\n",[30,3265,3266,3280,3316,3329,3346,3363,3368,3381],{"__ignoreMap":40},[44,3267,3268,3271,3273,3275,3278],{"class":46,"line":47},[44,3269,3270],{"class":58},"System.out.",[44,3272,601],{"class":54},[44,3274,172],{"class":58},[44,3276,3277],{"class":175},"\"pipeline built, but nothing printed yet\"",[44,3279,2297],{"class":58},[44,3281,3282,3285,3287,3290,3292,3294,3296,3298,3300,3302,3305,3307,3309,3311,3314],{"class":46,"line":62},[44,3283,3284],{"class":58},"Stream\u003C",[44,3286,2401],{"class":50},[44,3288,3289],{"class":58},"> filtered ",[44,3291,368],{"class":50},[44,3293,3105],{"class":58},[44,3295,159],{"class":54},[44,3297,172],{"class":58},[44,3299,198],{"class":192},[44,3301,179],{"class":58},[44,3303,3304],{"class":192},"7",[44,3306,179],{"class":58},[44,3308,1035],{"class":192},[44,3310,2486],{"class":58},[44,3312,3313],{"class":54},"boxed",[44,3315,706],{"class":58},[44,3317,3318,3320,3322,3325,3327],{"class":46,"line":71},[44,3319,711],{"class":58},[44,3321,1093],{"class":54},[44,3323,3324],{"class":58},"(n ",[44,3326,802],{"class":50},[44,3328,59],{"class":58},[44,3330,3331,3334,3336,3338,3341,3343],{"class":46,"line":79},[44,3332,3333],{"class":58},"            System.out.",[44,3335,601],{"class":54},[44,3337,172],{"class":58},[44,3339,3340],{"class":175},"\"checking \"",[44,3342,1796],{"class":50},[44,3344,3345],{"class":58}," n);\n",[44,3347,3348,3350,3353,3355,3357,3359,3361],{"class":46,"line":87},[44,3349,1649],{"class":50},[44,3351,3352],{"class":58}," n ",[44,3354,1636],{"class":50},[44,3356,1112],{"class":192},[44,3358,3039],{"class":50},[44,3360,403],{"class":192},[44,3362,1440],{"class":58},[44,3364,3365],{"class":46,"line":98},[44,3366,3367],{"class":58},"        });\n",[44,3369,3370,3372,3374,3376,3379],{"class":46,"line":108},[44,3371,3270],{"class":58},[44,3373,601],{"class":54},[44,3375,172],{"class":58},[44,3377,3378],{"class":175},"\"terminal operation starts\"",[44,3380,2297],{"class":58},[44,3382,3383,3386,3388,3390,3392,3394,3396,3398,3401,3403],{"class":46,"line":115},[44,3384,3385],{"class":58},"filtered.",[44,3387,764],{"class":54},[44,3389,3324],{"class":58},[44,3391,802],{"class":50},[44,3393,1257],{"class":58},[44,3395,601],{"class":54},[44,3397,172],{"class":58},[44,3399,3400],{"class":175},"\"kept \"",[44,3402,1796],{"class":50},[44,3404,3405],{"class":58}," n));\n",[35,3407,3410],{"className":3408,"code":3409,"language":625,"meta":40},[623],"pipeline built, but nothing printed yet\nterminal operation starts\nchecking 2\nkept 2\nchecking 7\nchecking 4\nkept 4\n",[30,3411,3409],{"__ignoreMap":40},[13,3413,3414,3415,3417,3418,3420,3421,3424,3425,3427,3428,3430],{},"从输出位置能看出两件事：",[30,3416,1093],{}," 是在 ",[30,3419,764],{}," 那一行才开始执行的；而且执行顺序是",[338,3422,3423],{},"每个元素依次走完整条流水线","（",[30,3426,198],{}," 被检查后立刻被消费，然后才轮到 ",[30,3429,3304],{},"），并不是「先把所有元素过滤完，再遍历结果」。",[22,3432,3434],{"id":3433},"_044-优点与并行parallelism","0.4.4 优点与并行（Parallelism）",[13,3436,3437],{},"流的三个好处：",[3240,3439,3440,3443,3446],{},[3243,3441,3442],{},"写法更声明式（Declarative）、更简洁。",[3243,3444,3445],{},"关注问题本身，而不是实现细节。",[3243,3447,3448],{},"便于并行化。",[13,3450,3451,3452,3455],{},"在中间操作前调用 ",[30,3453,3454],{},"parallel()"," 就会改成并行处理：",[35,3457,3459],{"className":37,"code":3458,"language":39,"meta":40,"style":40},"int evenSumParallel = IntStream.of(3, 8, 5, 12)\n        .parallel()\n        .filter(n -> n % 2 == 0)\n        .sum();\nSystem.out.println(\"sum of even numbers (parallel): \" + evenSumParallel);\n",[30,3460,3461,3494,3503,3525,3533],{"__ignoreMap":40},[44,3462,3463,3465,3468,3470,3472,3474,3476,3478,3480,3483,3485,3487,3489,3492],{"class":46,"line":47},[44,3464,395],{"class":50},[44,3466,3467],{"class":58}," evenSumParallel ",[44,3469,368],{"class":50},[44,3471,3105],{"class":58},[44,3473,159],{"class":54},[44,3475,172],{"class":58},[44,3477,264],{"class":192},[44,3479,179],{"class":58},[44,3481,3482],{"class":192},"8",[44,3484,179],{"class":58},[44,3486,1054],{"class":192},[44,3488,179],{"class":58},[44,3490,3491],{"class":192},"12",[44,3493,743],{"class":58},[44,3495,3496,3498,3501],{"class":46,"line":62},[44,3497,711],{"class":58},[44,3499,3500],{"class":54},"parallel",[44,3502,706],{"class":58},[44,3504,3505,3507,3509,3511,3513,3515,3517,3519,3521,3523],{"class":46,"line":71},[44,3506,711],{"class":58},[44,3508,1093],{"class":54},[44,3510,3324],{"class":58},[44,3512,802],{"class":50},[44,3514,3352],{"class":58},[44,3516,1636],{"class":50},[44,3518,1112],{"class":192},[44,3520,3039],{"class":50},[44,3522,403],{"class":192},[44,3524,743],{"class":58},[44,3526,3527,3529,3531],{"class":46,"line":79},[44,3528,711],{"class":58},[44,3530,3142],{"class":54},[44,3532,373],{"class":58},[44,3534,3535,3537,3539,3541,3544,3546],{"class":46,"line":87},[44,3536,3270],{"class":58},[44,3538,601],{"class":54},[44,3540,172],{"class":58},[44,3542,3543],{"class":175},"\"sum of even numbers (parallel): \"",[44,3545,1796],{"class":50},[44,3547,3548],{"class":58}," evenSumParallel);\n",[35,3550,3553],{"className":3551,"code":3552,"language":625,"meta":40},[623],"sum of even numbers (parallel): 20\n",[30,3554,3552],{"__ignoreMap":40},[13,3556,3557,3558,3560,3561,3564],{},"串行版本的结果一样。值得注意的是，",[30,3559,3454],{}," 背后有线程管理（Thread Management）、任务拆分（Task Splitting）等额外开销，",[338,3562,3563],{},"数据量小时并行反而可能比串行更慢","，所以默认还是用串行，确实有大数据量和性能需求时再考虑。",[17,3566,3568],{"id":3567},"_05-创建流creating-streams","0.5 创建流（Creating Streams）",[22,3570,3572],{"id":3571},"_051-从数组创建","0.5.1 从数组创建",[35,3574,3576],{"className":37,"code":3575,"language":39,"meta":40,"style":40},"int[] numbers = {3, 10, 6, 1};\nIntStream.of(numbers).forEach(e -> System.out.print(e + \" \"));\nSystem.out.println();\n\nInteger[] boxed = {2, 9, 5};\nArrays.stream(boxed).forEach(e -> System.out.print(e + \" \"));\nSystem.out.println();\n",[30,3577,3578,3607,3638,3646,3650,3674,3701],{"__ignoreMap":40},[44,3579,3580,3582,3585,3587,3589,3591,3593,3596,3598,3601,3603,3605],{"class":46,"line":47},[44,3581,395],{"class":50},[44,3583,3584],{"class":58},"[] numbers ",[44,3586,368],{"class":50},[44,3588,2670],{"class":58},[44,3590,264],{"class":192},[44,3592,179],{"class":58},[44,3594,3595],{"class":192},"10",[44,3597,179],{"class":58},[44,3599,3600],{"class":192},"6",[44,3602,179],{"class":58},[44,3604,983],{"class":192},[44,3606,2468],{"class":58},[44,3608,3609,3612,3614,3617,3619,3622,3624,3626,3629,3631,3633,3635],{"class":46,"line":62},[44,3610,3611],{"class":58},"IntStream.",[44,3613,159],{"class":54},[44,3615,3616],{"class":58},"(numbers).",[44,3618,764],{"class":54},[44,3620,3621],{"class":58},"(e ",[44,3623,802],{"class":50},[44,3625,1257],{"class":58},[44,3627,3628],{"class":54},"print",[44,3630,3621],{"class":58},[44,3632,442],{"class":50},[44,3634,1793],{"class":175},[44,3636,3637],{"class":58},"));\n",[44,3639,3640,3642,3644],{"class":46,"line":71},[44,3641,3270],{"class":58},[44,3643,601],{"class":54},[44,3645,373],{"class":58},[44,3647,3648],{"class":46,"line":79},[44,3649,379],{"emptyLinePlaceholder":378},[44,3651,3652,3654,3657,3659,3661,3663,3665,3668,3670,3672],{"class":46,"line":87},[44,3653,2401],{"class":50},[44,3655,3656],{"class":58},"[] boxed ",[44,3658,368],{"class":50},[44,3660,2670],{"class":58},[44,3662,198],{"class":192},[44,3664,179],{"class":58},[44,3666,3667],{"class":192},"9",[44,3669,179],{"class":58},[44,3671,1054],{"class":192},[44,3673,2468],{"class":58},[44,3675,3676,3678,3680,3683,3685,3687,3689,3691,3693,3695,3697,3699],{"class":46,"line":98},[44,3677,2695],{"class":58},[44,3679,703],{"class":54},[44,3681,3682],{"class":58},"(boxed).",[44,3684,764],{"class":54},[44,3686,3621],{"class":58},[44,3688,802],{"class":50},[44,3690,1257],{"class":58},[44,3692,3628],{"class":54},[44,3694,3621],{"class":58},[44,3696,442],{"class":50},[44,3698,1793],{"class":175},[44,3700,3637],{"class":58},[44,3702,3703,3705,3707],{"class":46,"line":108},[44,3704,3270],{"class":58},[44,3706,601],{"class":54},[44,3708,373],{"class":58},[35,3710,3713],{"className":3711,"code":3712,"language":625,"meta":40},[623],"3 10 6 1 \n2 9 5 \n",[30,3714,3712],{"__ignoreMap":40},[13,3716,3717,3718,3721,3722,3725,3726,3729,3730,3733,3734,3737,3738,3741],{},"两种方法的区别在参数：",[30,3719,3720],{},"IntStream.of()"," 接收 ",[30,3723,3724],{},"int[]"," 这类基本类型数组，得到的是 ",[30,3727,3728],{},"IntStream","；",[30,3731,3732],{},"Arrays.stream()"," 接收引用类型数组（如 ",[30,3735,3736],{},"Integer[]","），得到的是 ",[30,3739,3740],{},"Stream\u003CInteger>","。选哪个取决于手上是哪种数组。",[22,3743,3745],{"id":3744},"_052-从集合创建","0.5.2 从集合创建",[13,3747,3748,3749,3752,3753,786,3756,3759,3760,3763],{},"任何实现了 ",[30,3750,3751],{},"Collection"," 接口的类（",[30,3754,3755],{},"List",[30,3757,3758],{},"Set","……）都有 ",[30,3761,3762],{},"stream()"," 方法：",[35,3765,3767],{"className":37,"code":3766,"language":39,"meta":40,"style":40},"List\u003CString> list = new ArrayList\u003C>();\nlist.add(\"first\");\nlist.add(\"second\");\nlist.stream().forEach(System.out::println);\n",[30,3768,3769,3785,3800,3813],{"__ignoreMap":40},[44,3770,3771,3773,3775,3778,3780,3782],{"class":46,"line":47},[44,3772,360],{"class":58},[44,3774,2901],{"class":50},[44,3776,3777],{"class":58},"> list ",[44,3779,368],{"class":50},[44,3781,153],{"class":50},[44,3783,3784],{"class":58}," ArrayList\u003C>();\n",[44,3786,3787,3790,3793,3795,3798],{"class":46,"line":62},[44,3788,3789],{"class":58},"list.",[44,3791,3792],{"class":54},"add",[44,3794,172],{"class":58},[44,3796,3797],{"class":175},"\"first\"",[44,3799,2297],{"class":58},[44,3801,3802,3804,3806,3808,3811],{"class":46,"line":71},[44,3803,3789],{"class":58},[44,3805,3792],{"class":54},[44,3807,172],{"class":58},[44,3809,3810],{"class":175},"\"second\"",[44,3812,2297],{"class":58},[44,3814,3815,3817,3819,3821,3823,3825,3827],{"class":46,"line":79},[44,3816,3789],{"class":58},[44,3818,703],{"class":54},[44,3820,700],{"class":58},[44,3822,764],{"class":54},[44,3824,767],{"class":58},[44,3826,726],{"class":50},[44,3828,772],{"class":58},[35,3830,3833],{"className":3831,"code":3832,"language":625,"meta":40},[623],"first\nsecond\n",[30,3834,3832],{"__ignoreMap":40},[13,3836,3837,3838,3840,3841,786,3844,3729,3847,3840,3850,3729,3853,3840,3855,1269],{},"集合家族的常见实现：",[30,3839,3755],{}," 有 ",[30,3842,3843],{},"ArrayList",[30,3845,3846],{},"LinkedList",[30,3848,3849],{},"Map",[30,3851,3852],{},"HashMap",[30,3854,3758],{},[30,3856,3857],{},"HashSet",[22,3859,3861],{"id":3860},"_053-创建有序整数流","0.5.3 创建有序整数流",[13,3863,3864,3867,3868,3871],{},[30,3865,3866],{},"IntStream.range()"," 与 ",[30,3869,3870],{},"IntStream.rangeClosed()"," 用来生成一段连续的整数：",[35,3873,3875],{"className":37,"code":3874,"language":39,"meta":40,"style":40},"IntStream.range(1, 5).forEach(e -> System.out.print(e + \" \"));\nSystem.out.println();\nIntStream.rangeClosed(1, 5).forEach(e -> System.out.print(e + \" \"));\nSystem.out.println();\n",[30,3876,3877,3912,3920,3955],{"__ignoreMap":40},[44,3878,3879,3881,3884,3886,3888,3890,3892,3894,3896,3898,3900,3902,3904,3906,3908,3910],{"class":46,"line":47},[44,3880,3611],{"class":58},[44,3882,3883],{"class":54},"range",[44,3885,172],{"class":58},[44,3887,983],{"class":192},[44,3889,179],{"class":58},[44,3891,1054],{"class":192},[44,3893,2486],{"class":58},[44,3895,764],{"class":54},[44,3897,3621],{"class":58},[44,3899,802],{"class":50},[44,3901,1257],{"class":58},[44,3903,3628],{"class":54},[44,3905,3621],{"class":58},[44,3907,442],{"class":50},[44,3909,1793],{"class":175},[44,3911,3637],{"class":58},[44,3913,3914,3916,3918],{"class":46,"line":62},[44,3915,3270],{"class":58},[44,3917,601],{"class":54},[44,3919,373],{"class":58},[44,3921,3922,3924,3927,3929,3931,3933,3935,3937,3939,3941,3943,3945,3947,3949,3951,3953],{"class":46,"line":71},[44,3923,3611],{"class":58},[44,3925,3926],{"class":54},"rangeClosed",[44,3928,172],{"class":58},[44,3930,983],{"class":192},[44,3932,179],{"class":58},[44,3934,1054],{"class":192},[44,3936,2486],{"class":58},[44,3938,764],{"class":54},[44,3940,3621],{"class":58},[44,3942,802],{"class":50},[44,3944,1257],{"class":58},[44,3946,3628],{"class":54},[44,3948,3621],{"class":58},[44,3950,442],{"class":50},[44,3952,1793],{"class":175},[44,3954,3637],{"class":58},[44,3956,3957,3959,3961],{"class":46,"line":79},[44,3958,3270],{"class":58},[44,3960,601],{"class":54},[44,3962,373],{"class":58},[35,3964,3967],{"className":3965,"code":3966,"language":625,"meta":40},[623],"1 2 3 4 \n1 2 3 4 5 \n",[30,3968,3966],{"__ignoreMap":40},[13,3970,3971,1280,3974,3977,3978,3981],{},[338,3972,3973],{},"区别只在结尾",[30,3975,3976],{},"range(1, 5)"," 是左闭右开，含 1 不含 5；",[30,3979,3980],{},"rangeClosed(1, 5)"," 两端都含。",[13,3983,3984,3987],{},[338,3985,3986],{},"练习","：打印 1 到 100 的每个整数，一行一个。",[35,3989,3991],{"className":37,"code":3990,"language":39,"meta":40,"style":40},"IntStream.rangeClosed(1, 100).forEach(System.out::println);\n",[30,3992,3993],{"__ignoreMap":40},[44,3994,3995,3997,3999,4001,4003,4005,4008,4010,4012,4014,4016],{"class":46,"line":47},[44,3996,3611],{"class":58},[44,3998,3926],{"class":54},[44,4000,172],{"class":58},[44,4002,983],{"class":192},[44,4004,179],{"class":58},[44,4006,4007],{"class":192},"100",[44,4009,2486],{"class":58},[44,4011,764],{"class":54},[44,4013,767],{"class":58},[44,4015,726],{"class":50},[44,4017,772],{"class":58},[17,4019,4021],{"id":4020},"_06-中间操作intermediate-operations","0.6 中间操作（Intermediate Operations）",[22,4023,4025],{"id":4024},"_061-常见方法","0.6.1 常见方法",[957,4027,4028,4041],{},[960,4029,4030],{},[963,4031,4032,4035,4038],{},[966,4033,4034],{},"方法",[966,4036,4037],{},"参数（函数式接口）",[966,4039,4040],{},"说明",[976,4042,4043,4056,4069,4082,4098,4112],{},[963,4044,4045,4049,4053],{},[981,4046,4047],{},[30,4048,1093],{},[981,4050,4051],{},[30,4052,2141],{},[981,4054,4055],{},"只保留满足条件的元素",[963,4057,4058,4062,4066],{},[981,4059,4060],{},[30,4061,714],{},[981,4063,4064],{},[30,4065,2240],{},[981,4067,4068],{},"按给定的比较器排序",[963,4070,4071,4075,4079],{},[981,4072,4073],{},[30,4074,750],{},[981,4076,4077],{},[30,4078,2115],{},[981,4080,4081],{},"把每个元素替换成另一个值（类型可以变）",[963,4083,4084,4089,4091],{},[981,4085,4086],{},[30,4087,4088],{},"distinct",[981,4090,2174],{},[981,4092,4093,4094,4097],{},"按 ",[30,4095,4096],{},"Object.equals(Object)"," 去重",[963,4099,4100,4104,4109],{},[981,4101,4102],{},[30,4103,736],{},[981,4105,4106],{},[30,4107,4108],{},"long",[981,4110,4111],{},"只取前 n 个元素",[963,4113,4114,4119,4123],{},[981,4115,4116],{},[30,4117,4118],{},"skip",[981,4120,4121],{},[30,4122,4108],{},[981,4124,4125],{},"丢掉前 n 个元素，返回剩下的部分（不足 n 个则返回空流）",[13,4127,4128,4129,786,4132,4135,4136,1269],{},"注意 ",[30,4130,4131],{},"limit(long num)",[30,4133,4134],{},"skip(long num)"," 的参数不是函数式接口，而是一个普通的基本类型数字——看方法签名就能分清：",[338,4137,4138],{},"参数写着函数式接口的地方，才需要传 lambda",[22,4140,4142,4143],{"id":4141},"_062-filterpredicatet","0.6.2 ",[30,4144,4145],{},"filter(Predicate\u003CT>)",[35,4147,4148],{"className":37,"code":1074,"language":39,"meta":40,"style":40},[30,4149,4150,4160,4182],{"__ignoreMap":40},[44,4151,4152,4154,4156,4158],{"class":46,"line":47},[44,4153,697],{"class":54},[44,4155,700],{"class":58},[44,4157,703],{"class":54},[44,4159,706],{"class":58},[44,4161,4162,4164,4166,4168,4170,4172,4174,4176,4178,4180],{"class":46,"line":62},[44,4163,711],{"class":58},[44,4165,1093],{"class":54},[44,4167,1096],{"class":58},[44,4169,802],{"class":50},[44,4171,1101],{"class":58},[44,4173,1104],{"class":54},[44,4175,481],{"class":58},[44,4177,1109],{"class":50},[44,4179,1112],{"class":192},[44,4181,743],{"class":58},[44,4183,4184,4186,4188,4190,4192],{"class":46,"line":71},[44,4185,711],{"class":58},[44,4187,764],{"class":54},[44,4189,767],{"class":58},[44,4191,726],{"class":50},[44,4193,772],{"class":58},[35,4195,4198],{"className":4196,"code":4197,"language":625,"meta":40},[623],"Alice Wong, 31, 2\nDaniel Koh, 38, 2\nEthan Goh, 27, 2\n",[30,4199,4197],{"__ignoreMap":40},[13,4201,4202,4203,4205,4206,992,4209,4211,4212,4214,4215,4217,4218,4220,4221,4223,4224,1269],{},"调用流方法的思路可以拆成六步：想要过滤元素 → 那就调 ",[30,4204,1131],{}," → 它要求给一个 ",[30,4207,4208],{},"Predicate",[30,4210,4208],{}," 的实例可以写成 lambda，左边的名字随便起（这里叫 ",[30,4213,13],{},"）→ ",[30,4216,13],{}," 的类型是流里的元素类型 ",[30,4219,32],{},"，所以能用 ",[30,4222,32],{}," 的任何方法 → 右边写出条件表达式，返回 ",[30,4225,1581],{},[13,4227,4228,4229,4231,4232,786,4235,1269],{},"其他方法也都类似，只是第 2、4、6 步不同。",[30,4230,4208],{}," 的其他常见写法还有 ",[30,4233,4234],{},"x -> x % 2 == 0",[30,4236,4237],{},"amount -> amount > 10",[13,4239,4240,4242],{},[338,4241,3986],{},"：用流打印小于 100 的所有偶数自然数。",[35,4244,4246],{"className":37,"code":4245,"language":39,"meta":40,"style":40},"IntStream.range(1, 100)\n        .filter(number -> number % 2 == 0)\n        .forEach(number -> System.out.print(number + \" \"));\n",[30,4247,4248,4264,4288],{"__ignoreMap":40},[44,4249,4250,4252,4254,4256,4258,4260,4262],{"class":46,"line":47},[44,4251,3611],{"class":58},[44,4253,3883],{"class":54},[44,4255,172],{"class":58},[44,4257,983],{"class":192},[44,4259,179],{"class":58},[44,4261,4007],{"class":192},[44,4263,743],{"class":58},[44,4265,4266,4268,4270,4273,4275,4278,4280,4282,4284,4286],{"class":46,"line":62},[44,4267,711],{"class":58},[44,4269,1093],{"class":54},[44,4271,4272],{"class":58},"(number ",[44,4274,802],{"class":50},[44,4276,4277],{"class":58}," number ",[44,4279,1636],{"class":50},[44,4281,1112],{"class":192},[44,4283,3039],{"class":50},[44,4285,403],{"class":192},[44,4287,743],{"class":58},[44,4289,4290,4292,4294,4296,4298,4300,4302,4304,4306,4308],{"class":46,"line":71},[44,4291,711],{"class":58},[44,4293,764],{"class":54},[44,4295,4272],{"class":58},[44,4297,802],{"class":50},[44,4299,1257],{"class":58},[44,4301,3628],{"class":54},[44,4303,4272],{"class":58},[44,4305,442],{"class":50},[44,4307,1793],{"class":175},[44,4309,3637],{"class":58},[22,4311,4313,4314],{"id":4312},"_063-sortedcomparatort","0.6.3 ",[30,4315,4316],{},"sorted(Comparator\u003CT>)",[13,4318,4319,4321,4322,4325,4326,4329,4330,4333],{},[30,4320,2240],{}," 决定两个对象的先后顺序，它的抽象方法是 ",[30,4323,4324],{},"compare(T o1, T o2)","，约定是：",[30,4327,4328],{},"o1 \u003C o2"," 返回负数、",[30,4331,4332],{},"o1 > o2"," 返回正数、相等返回 0。",[35,4335,4337],{"className":37,"code":4336,"language":39,"meta":40,"style":40},"interface Comparator\u003CT> {\n    int compare(T o1, T o2);\n}\n",[30,4338,4339,4352,4371],{"__ignoreMap":40},[44,4340,4341,4343,4346,4348,4350],{"class":46,"line":47},[44,4342,2269],{"class":50},[44,4344,4345],{"class":54}," Comparator",[44,4347,134],{"class":58},[44,4349,2098],{"class":50},[44,4351,2279],{"class":58},[44,4353,4354,4356,4359,4361,4364,4366,4369],{"class":46,"line":62},[44,4355,1430],{"class":50},[44,4357,4358],{"class":54}," compare",[44,4360,2290],{"class":58},[44,4362,4363],{"class":2293},"o1",[44,4365,2368],{"class":58},[44,4367,4368],{"class":2293},"o2",[44,4370,2297],{"class":58},[44,4372,4373],{"class":46,"line":71},[44,4374,118],{"class":58},[13,4376,4377],{},"所以一个比较器可以直接写成 lambda。下面按名升序排序：",[35,4379,4381],{"className":37,"code":4380,"language":39,"meta":40,"style":40},"people().stream()\n        .sorted((p1, p2) -> p1.getFirstName().compareTo(p2.getFirstName()))\n        .forEach(System.out::println);\n",[30,4382,4383,4393,4422],{"__ignoreMap":40},[44,4384,4385,4387,4389,4391],{"class":46,"line":47},[44,4386,697],{"class":54},[44,4388,700],{"class":58},[44,4390,703],{"class":54},[44,4392,706],{"class":58},[44,4394,4395,4397,4399,4402,4404,4407,4409,4411,4414,4417,4419],{"class":46,"line":62},[44,4396,711],{"class":58},[44,4398,714],{"class":54},[44,4400,4401],{"class":58},"((p1, p2) ",[44,4403,802],{"class":50},[44,4405,4406],{"class":58}," p1.",[44,4408,1338],{"class":54},[44,4410,700],{"class":58},[44,4412,4413],{"class":54},"compareTo",[44,4415,4416],{"class":58},"(p2.",[44,4418,1338],{"class":54},[44,4420,4421],{"class":58},"()))\n",[44,4423,4424,4426,4428,4430,4432],{"class":46,"line":71},[44,4425,711],{"class":58},[44,4427,764],{"class":54},[44,4429,767],{"class":58},[44,4431,726],{"class":50},[44,4433,772],{"class":58},[35,4435,4438],{"className":4436,"code":4437,"language":625,"meta":40},[623],"Alice Wong, 31, 2\nBryan Chua, 24, 0\nCindy Tay, 45, 3\nDaniel Koh, 38, 2\nEthan Goh, 27, 2\n",[30,4439,4437],{"__ignoreMap":40},[13,4441,4442,4443,4445,4446,4449,4450,4453,4454,786,4456,4459,4460,4462],{},"这里借用了 ",[30,4444,2901],{}," 自带的 ",[30,4447,4448],{},"compareTo()","：它已经按「字母顺序在前返回负数、在后返回正数、相同返回 0」的约定实现好了，正好满足 ",[30,4451,4452],{},"Comparator"," 要求的返回值语义。（",[30,4455,2401],{},[30,4457,4458],{},"Double"," 等包装类也都有可用的 ",[30,4461,4448],{},"。）",[13,4464,4465,4466,4468,4469,4472],{},"因为「取某个属性来比较」太常用，",[30,4467,4452],{}," 提供了 ",[30,4470,4471],{},"comparing()"," 工厂方法，等价且更短：",[35,4474,4476],{"className":37,"code":4475,"language":39,"meta":40,"style":40},".sorted(Comparator.comparing(Person::getFirstName))\n",[30,4477,4478],{"__ignoreMap":40},[44,4479,4480,4483,4485,4487,4489,4491,4493],{"class":46,"line":47},[44,4481,4482],{"class":58},".",[44,4484,714],{"class":54},[44,4486,717],{"class":58},[44,4488,720],{"class":54},[44,4490,723],{"class":58},[44,4492,726],{"class":50},[44,4494,4495],{"class":58},"getFirstName))\n",[13,4497,4498,4499,4501],{},"它接收一个 ",[30,4500,2115],{},"（一个参数、有非 void 返回值），流里每个元素经过这个函数取出的键值，会被当作比较依据。",[13,4503,4504],{},"多个字段、倒序也都支持：",[35,4506,4508],{"className":37,"code":4507,"language":39,"meta":40,"style":40},"people().stream()\n        .sorted(Comparator.comparing(Person::getKids)\n                .thenComparing(Person::getAge)\n                .reversed())\n        .forEach(System.out::println);\n",[30,4509,4510,4520,4537,4552,4561],{"__ignoreMap":40},[44,4511,4512,4514,4516,4518],{"class":46,"line":47},[44,4513,697],{"class":54},[44,4515,700],{"class":58},[44,4517,703],{"class":54},[44,4519,706],{"class":58},[44,4521,4522,4524,4526,4528,4530,4532,4534],{"class":46,"line":62},[44,4523,711],{"class":58},[44,4525,714],{"class":54},[44,4527,717],{"class":58},[44,4529,720],{"class":54},[44,4531,723],{"class":58},[44,4533,726],{"class":50},[44,4535,4536],{"class":58},"getKids)\n",[44,4538,4539,4542,4545,4547,4549],{"class":46,"line":71},[44,4540,4541],{"class":58},"                .",[44,4543,4544],{"class":54},"thenComparing",[44,4546,723],{"class":58},[44,4548,726],{"class":50},[44,4550,4551],{"class":58},"getAge)\n",[44,4553,4554,4556,4559],{"class":46,"line":79},[44,4555,4541],{"class":58},[44,4557,4558],{"class":54},"reversed",[44,4560,1934],{"class":58},[44,4562,4563,4565,4567,4569,4571],{"class":46,"line":87},[44,4564,711],{"class":58},[44,4566,764],{"class":54},[44,4568,767],{"class":58},[44,4570,726],{"class":50},[44,4572,772],{"class":58},[35,4574,4577],{"className":4575,"code":4576,"language":625,"meta":40},[623],"Cindy Tay, 45, 3\nDaniel Koh, 38, 2\nAlice Wong, 31, 2\nEthan Goh, 27, 2\nBryan Chua, 24, 0\n",[30,4578,4576],{"__ignoreMap":40},[13,4580,4581,4582,4585,4586,4588,4589,4591,4592,4594,4595,4597,4598,4600,4601,4604,4605,4607,4608,4611,4612,4614,4615,4617],{},"链条读作：先按 ",[30,4583,4584],{},"kids"," 升序，",[30,4587,4584],{}," 相同时按 ",[30,4590,682],{}," 升序（",[30,4593,4544],{},"），最后把整体顺序反过来（",[30,4596,4558],{},"）。所以 ",[30,4599,4584],{}," 最多的 ",[30,4602,4603],{},"Cindy"," 在最前，",[30,4606,4584],{}," 为 0 的 ",[30,4609,4610],{},"Bryan"," 排在最后；中间三个人 ",[30,4613,4584],{}," 都是 2，被 ",[30,4616,4558],{}," 反成了按年龄递减。",[13,4619,4620,4622],{},[338,4621,3986],{},"：先按名、再按姓排序。",[35,4624,4626],{"className":37,"code":4625,"language":39,"meta":40,"style":40},"people().stream()\n        .sorted(Comparator.comparing(Person::getFirstName)\n                .thenComparing(Person::getLastName))\n        .forEach(p -> System.out.println(p.getFullName()));\n",[30,4627,4628,4638,4655,4668],{"__ignoreMap":40},[44,4629,4630,4632,4634,4636],{"class":46,"line":47},[44,4631,697],{"class":54},[44,4633,700],{"class":58},[44,4635,703],{"class":54},[44,4637,706],{"class":58},[44,4639,4640,4642,4644,4646,4648,4650,4652],{"class":46,"line":62},[44,4641,711],{"class":58},[44,4643,714],{"class":54},[44,4645,717],{"class":58},[44,4647,720],{"class":54},[44,4649,723],{"class":58},[44,4651,726],{"class":50},[44,4653,4654],{"class":58},"getFirstName)\n",[44,4656,4657,4659,4661,4663,4665],{"class":46,"line":71},[44,4658,4541],{"class":58},[44,4660,4544],{"class":54},[44,4662,723],{"class":58},[44,4664,726],{"class":50},[44,4666,4667],{"class":58},"getLastName))\n",[44,4669,4670,4672,4674,4676,4678,4680,4682,4684,4686],{"class":46,"line":79},[44,4671,711],{"class":58},[44,4673,764],{"class":54},[44,4675,1096],{"class":58},[44,4677,802],{"class":50},[44,4679,1257],{"class":58},[44,4681,601],{"class":54},[44,4683,1830],{"class":58},[44,4685,611],{"class":54},[44,4687,4688],{"class":58},"()));\n",[22,4690,4692,4693],{"id":4691},"_064-mapfunctiontr","0.6.4 ",[30,4694,4695],{},"map(Function\u003CT,R>)",[13,4697,4698,4701,4702,4704,4705,4708],{},[30,4699,4700],{},"map()"," 接收一个 ",[30,4703,2115],{},"，把每个元素",[338,4706,4707],{},"转换成另一个值","，类型也可以变：",[35,4710,4712],{"className":37,"code":4711,"language":39,"meta":40,"style":40},"people().stream()\n        .sorted(Comparator.comparing(Person::getAge))\n        .map(Person::getFullName)\n        .forEach(System.out::println);\n",[30,4713,4714,4724,4740,4752],{"__ignoreMap":40},[44,4715,4716,4718,4720,4722],{"class":46,"line":47},[44,4717,697],{"class":54},[44,4719,700],{"class":58},[44,4721,703],{"class":54},[44,4723,706],{"class":58},[44,4725,4726,4728,4730,4732,4734,4736,4738],{"class":46,"line":62},[44,4727,711],{"class":58},[44,4729,714],{"class":54},[44,4731,717],{"class":58},[44,4733,720],{"class":54},[44,4735,723],{"class":58},[44,4737,726],{"class":50},[44,4739,729],{"class":58},[44,4741,4742,4744,4746,4748,4750],{"class":46,"line":71},[44,4743,711],{"class":58},[44,4745,750],{"class":54},[44,4747,723],{"class":58},[44,4749,726],{"class":50},[44,4751,757],{"class":58},[44,4753,4754,4756,4758,4760,4762],{"class":46,"line":79},[44,4755,711],{"class":58},[44,4757,764],{"class":54},[44,4759,767],{"class":58},[44,4761,726],{"class":50},[44,4763,772],{"class":58},[35,4765,4768],{"className":4766,"code":4767,"language":625,"meta":40},[623],"Bryan Chua\nEthan Goh\nAlice Wong\nDaniel Koh\nCindy Tay\n",[30,4769,4767],{"__ignoreMap":40},[13,4771,4772],{},"同一个流表面上只是「拿着元素」，但其实每个操作都会改变「流里的元素是什么类型」，写代码时最好对每一步的输入输出都有数：",[957,4774,4775,4787],{},[960,4776,4777],{},[963,4778,4779,4781,4784],{},[966,4780,4034],{},[966,4782,4783],{},"输入",[966,4785,4786],{},"输出",[976,4788,4789,4804,4819],{},[963,4790,4791,4796,4800],{},[981,4792,4793],{},[30,4794,4795],{},"sorted(...)",[981,4797,4798],{},[30,4799,32],{},[981,4801,4802],{},[30,4803,32],{},[963,4805,4806,4811,4815],{},[981,4807,4808],{},[30,4809,4810],{},"map(Person::getFullName)",[981,4812,4813],{},[30,4814,32],{},[981,4816,4817],{},[30,4818,2901],{},[963,4820,4821,4826,4830],{},[981,4822,4823],{},[30,4824,4825],{},"forEach(System.out::println)",[981,4827,4828],{},[30,4829,2901],{},[981,4831,4832],{},[30,4833,1816],{},[13,4835,4836,4837,4840,4841,4843,4844,4846],{},"弄混了很容易出问题，例如把 ",[30,4838,4839],{},"sorted()"," 放到 ",[30,4842,4700],{}," 后面，就比较不了 ",[30,4845,32],{}," 的属性了，得改成比较字符串。",[13,4848,4849,4851],{},[30,4850,750],{}," 对基本类型流和引用类型流都能用：",[35,4853,4855],{"className":37,"code":4854,"language":39,"meta":40,"style":40},"int[] numbers = {0, 1, 2, 3, 4, 5};\nIntStream.of(numbers)\n        .map(number -> number * 2)\n        .forEach(number -> System.out.print(number + \" \"));\nSystem.out.println();\n\nString[] words = {\"kiwi\", \"banana\", \"pear\"};\nArrays.stream(words)\n        .map(String::toUpperCase)\n        .forEach(w -> System.out.print(w + \" \"));\nSystem.out.println();\n",[30,4856,4857,4891,4900,4919,4941,4949,4953,4979,4988,5002,5025],{"__ignoreMap":40},[44,4858,4859,4861,4863,4865,4867,4869,4871,4873,4875,4877,4879,4881,4883,4885,4887,4889],{"class":46,"line":47},[44,4860,395],{"class":50},[44,4862,3584],{"class":58},[44,4864,368],{"class":50},[44,4866,2670],{"class":58},[44,4868,232],{"class":192},[44,4870,179],{"class":58},[44,4872,983],{"class":192},[44,4874,179],{"class":58},[44,4876,198],{"class":192},[44,4878,179],{"class":58},[44,4880,264],{"class":192},[44,4882,179],{"class":58},[44,4884,1035],{"class":192},[44,4886,179],{"class":58},[44,4888,1054],{"class":192},[44,4890,2468],{"class":58},[44,4892,4893,4895,4897],{"class":46,"line":62},[44,4894,3611],{"class":58},[44,4896,159],{"class":54},[44,4898,4899],{"class":58},"(numbers)\n",[44,4901,4902,4904,4906,4908,4910,4912,4915,4917],{"class":46,"line":71},[44,4903,711],{"class":58},[44,4905,750],{"class":54},[44,4907,4272],{"class":58},[44,4909,802],{"class":50},[44,4911,4277],{"class":58},[44,4913,4914],{"class":50},"*",[44,4916,1112],{"class":192},[44,4918,743],{"class":58},[44,4920,4921,4923,4925,4927,4929,4931,4933,4935,4937,4939],{"class":46,"line":79},[44,4922,711],{"class":58},[44,4924,764],{"class":54},[44,4926,4272],{"class":58},[44,4928,802],{"class":50},[44,4930,1257],{"class":58},[44,4932,3628],{"class":54},[44,4934,4272],{"class":58},[44,4936,442],{"class":50},[44,4938,1793],{"class":175},[44,4940,3637],{"class":58},[44,4942,4943,4945,4947],{"class":46,"line":87},[44,4944,3270],{"class":58},[44,4946,601],{"class":54},[44,4948,373],{"class":58},[44,4950,4951],{"class":46,"line":98},[44,4952,379],{"emptyLinePlaceholder":378},[44,4954,4955,4957,4960,4962,4964,4967,4969,4972,4974,4977],{"class":46,"line":108},[44,4956,2901],{"class":50},[44,4958,4959],{"class":58},"[] words ",[44,4961,368],{"class":50},[44,4963,2670],{"class":58},[44,4965,4966],{"class":175},"\"kiwi\"",[44,4968,179],{"class":58},[44,4970,4971],{"class":175},"\"banana\"",[44,4973,179],{"class":58},[44,4975,4976],{"class":175},"\"pear\"",[44,4978,2468],{"class":58},[44,4980,4981,4983,4985],{"class":46,"line":115},[44,4982,2695],{"class":58},[44,4984,703],{"class":54},[44,4986,4987],{"class":58},"(words)\n",[44,4989,4990,4992,4994,4997,4999],{"class":46,"line":528},[44,4991,711],{"class":58},[44,4993,750],{"class":54},[44,4995,4996],{"class":58},"(String",[44,4998,726],{"class":50},[44,5000,5001],{"class":58},"toUpperCase)\n",[44,5003,5004,5006,5008,5011,5013,5015,5017,5019,5021,5023],{"class":46,"line":538},[44,5005,711],{"class":58},[44,5007,764],{"class":54},[44,5009,5010],{"class":58},"(w ",[44,5012,802],{"class":50},[44,5014,1257],{"class":58},[44,5016,3628],{"class":54},[44,5018,5010],{"class":58},[44,5020,442],{"class":50},[44,5022,1793],{"class":175},[44,5024,3637],{"class":58},[44,5026,5027,5029,5031],{"class":46,"line":544},[44,5028,3270],{"class":58},[44,5030,601],{"class":54},[44,5032,373],{"class":58},[35,5034,5037],{"className":5035,"code":5036,"language":625,"meta":40},[623],"0 2 4 6 8 10 \nKIWI BANANA PEAR \n",[30,5038,5036],{"__ignoreMap":40},[22,5040,5042,5043,3867,5045,5048],{"id":5041},"_065-map-与-maptoint-的区别","0.6.5 ",[30,5044,750],{},[30,5046,5047],{},"mapToInt"," 的区别",[13,5050,5051,5052,1280],{},"要把一个对象流转成数字流，用 ",[30,5053,5054],{},"mapToInt()",[35,5056,5058],{"className":37,"code":5057,"language":39,"meta":40,"style":40},"String[] names = {\"Alice\", \"Bryan\", \"Cindy\", \"Daniel\", \"Ethan\"};\nint maxLength = Stream.of(names)\n        .mapToInt(name -> name.length())\n        .max()\n        .getAsInt();\nSystem.out.println(\"longest first name has \" + maxLength + \" letters\");\n",[30,5059,5060,5091,5108,5127,5136,5145],{"__ignoreMap":40},[44,5061,5062,5064,5067,5069,5071,5073,5075,5077,5079,5081,5083,5085,5087,5089],{"class":46,"line":47},[44,5063,2901],{"class":50},[44,5065,5066],{"class":58},"[] names ",[44,5068,368],{"class":50},[44,5070,2670],{"class":58},[44,5072,176],{"class":175},[44,5074,179],{"class":58},[44,5076,212],{"class":175},[44,5078,179],{"class":58},[44,5080,245],{"class":175},[44,5082,179],{"class":58},[44,5084,277],{"class":175},[44,5086,179],{"class":58},[44,5088,308],{"class":175},[44,5090,2468],{"class":58},[44,5092,5093,5095,5098,5100,5103,5105],{"class":46,"line":62},[44,5094,395],{"class":50},[44,5096,5097],{"class":58}," maxLength ",[44,5099,368],{"class":50},[44,5101,5102],{"class":58}," Stream.",[44,5104,159],{"class":54},[44,5106,5107],{"class":58},"(names)\n",[44,5109,5110,5112,5114,5117,5119,5122,5125],{"class":46,"line":71},[44,5111,711],{"class":58},[44,5113,5047],{"class":54},[44,5115,5116],{"class":58},"(name ",[44,5118,802],{"class":50},[44,5120,5121],{"class":58}," name.",[44,5123,5124],{"class":54},"length",[44,5126,1934],{"class":58},[44,5128,5129,5131,5134],{"class":46,"line":79},[44,5130,711],{"class":58},[44,5132,5133],{"class":54},"max",[44,5135,706],{"class":58},[44,5137,5138,5140,5143],{"class":46,"line":87},[44,5139,711],{"class":58},[44,5141,5142],{"class":54},"getAsInt",[44,5144,373],{"class":58},[44,5146,5147,5149,5151,5153,5156,5158,5160,5162,5165],{"class":46,"line":98},[44,5148,3270],{"class":58},[44,5150,601],{"class":54},[44,5152,172],{"class":58},[44,5154,5155],{"class":175},"\"longest first name has \"",[44,5157,1796],{"class":50},[44,5159,5097],{"class":58},[44,5161,442],{"class":50},[44,5163,5164],{"class":175}," \" letters\"",[44,5166,2297],{"class":58},[35,5168,5171],{"className":5169,"code":5170,"language":625,"meta":40},[623],"longest first name has 6 letters\n",[30,5172,5170],{"__ignoreMap":40},[13,5174,5175,5176,5178],{},"为什么不能直接 ",[30,5177,4700],{},"？因为两者返回的流不同：",[35,5180,5182],{"className":37,"code":5181,"language":39,"meta":40,"style":40},"\u002F\u002F mapToInt() 返回 IntStream，可以用 IntStream 的 max()\nStream.of(names).mapToInt(name -> name.length()).max().getAsInt();\n\n\u002F\u002F map() 返回 Stream\u003CInteger>，只能用带 Comparator 的 Stream.max()\nStream.of(names).map(name -> name.length()).max(Integer::compareTo).get();\n",[30,5183,5184,5189,5219,5223,5228],{"__ignoreMap":40},[44,5185,5186],{"class":46,"line":47},[44,5187,5188],{"class":111},"\u002F\u002F mapToInt() 返回 IntStream，可以用 IntStream 的 max()\n",[44,5190,5191,5193,5195,5198,5200,5202,5204,5206,5208,5211,5213,5215,5217],{"class":46,"line":62},[44,5192,2473],{"class":58},[44,5194,159],{"class":54},[44,5196,5197],{"class":58},"(names).",[44,5199,5047],{"class":54},[44,5201,5116],{"class":58},[44,5203,802],{"class":50},[44,5205,5121],{"class":58},[44,5207,5124],{"class":54},[44,5209,5210],{"class":58},"()).",[44,5212,5133],{"class":54},[44,5214,700],{"class":58},[44,5216,5142],{"class":54},[44,5218,373],{"class":58},[44,5220,5221],{"class":46,"line":71},[44,5222,379],{"emptyLinePlaceholder":378},[44,5224,5225],{"class":46,"line":79},[44,5226,5227],{"class":111},"\u002F\u002F map() 返回 Stream\u003CInteger>，只能用带 Comparator 的 Stream.max()\n",[44,5229,5230,5232,5234,5236,5238,5240,5242,5244,5246,5248,5250,5253,5255,5258,5260],{"class":46,"line":87},[44,5231,2473],{"class":58},[44,5233,159],{"class":54},[44,5235,5197],{"class":58},[44,5237,750],{"class":54},[44,5239,5116],{"class":58},[44,5241,802],{"class":50},[44,5243,5121],{"class":58},[44,5245,5124],{"class":54},[44,5247,5210],{"class":58},[44,5249,5133],{"class":54},[44,5251,5252],{"class":58},"(Integer",[44,5254,726],{"class":50},[44,5256,5257],{"class":58},"compareTo).",[44,5259,472],{"class":54},[44,5261,373],{"class":58},[13,5263,5264,5267,5268,5271,5272,5275,5276,5279,5280,5271,5283,5286,5287],{},[30,5265,5266],{},"IntStream.max()"," 不需要参数，返回 ",[30,5269,5270],{},"OptionalInt","，用 ",[30,5273,5274],{},"getAsInt()"," 取值；",[30,5277,5278],{},"Stream.max(Comparator\u003CT>)"," 需要一个比较器，返回 ",[30,5281,5282],{},"Optional",[30,5284,5285],{},"get()"," 取值。",[338,5288,5289],{},"用方法之前先看清它返回的是什么流。",[22,5291,5293,5294,5296,5297,5296,5299],{"id":5292},"_066-distinct-limit-skip","0.6.6 ",[30,5295,4088],{}," \u002F ",[30,5298,736],{},[30,5300,4118],{},[35,5302,5304],{"className":37,"code":5303,"language":39,"meta":40,"style":40},"int[] dup = {4, 4, 1, 9, 1, 7, 3};\nIntStream.of(dup).distinct().forEach(e -> System.out.print(e + \" \"));\nIntStream.of(dup).sorted().forEach(e -> System.out.print(e + \" \"));\nIntStream.of(dup).limit(3).forEach(e -> System.out.print(e + \" \"));\nIntStream.of(dup).skip(3).forEach(e -> System.out.print(e + \" \"));\n",[30,5305,5306,5345,5376,5406,5440],{"__ignoreMap":40},[44,5307,5308,5310,5313,5315,5317,5319,5321,5323,5325,5327,5329,5331,5333,5335,5337,5339,5341,5343],{"class":46,"line":47},[44,5309,395],{"class":50},[44,5311,5312],{"class":58},"[] dup ",[44,5314,368],{"class":50},[44,5316,2670],{"class":58},[44,5318,1035],{"class":192},[44,5320,179],{"class":58},[44,5322,1035],{"class":192},[44,5324,179],{"class":58},[44,5326,983],{"class":192},[44,5328,179],{"class":58},[44,5330,3667],{"class":192},[44,5332,179],{"class":58},[44,5334,983],{"class":192},[44,5336,179],{"class":58},[44,5338,3304],{"class":192},[44,5340,179],{"class":58},[44,5342,264],{"class":192},[44,5344,2468],{"class":58},[44,5346,5347,5349,5351,5354,5356,5358,5360,5362,5364,5366,5368,5370,5372,5374],{"class":46,"line":62},[44,5348,3611],{"class":58},[44,5350,159],{"class":54},[44,5352,5353],{"class":58},"(dup).",[44,5355,4088],{"class":54},[44,5357,700],{"class":58},[44,5359,764],{"class":54},[44,5361,3621],{"class":58},[44,5363,802],{"class":50},[44,5365,1257],{"class":58},[44,5367,3628],{"class":54},[44,5369,3621],{"class":58},[44,5371,442],{"class":50},[44,5373,1793],{"class":175},[44,5375,3637],{"class":58},[44,5377,5378,5380,5382,5384,5386,5388,5390,5392,5394,5396,5398,5400,5402,5404],{"class":46,"line":71},[44,5379,3611],{"class":58},[44,5381,159],{"class":54},[44,5383,5353],{"class":58},[44,5385,714],{"class":54},[44,5387,700],{"class":58},[44,5389,764],{"class":54},[44,5391,3621],{"class":58},[44,5393,802],{"class":50},[44,5395,1257],{"class":58},[44,5397,3628],{"class":54},[44,5399,3621],{"class":58},[44,5401,442],{"class":50},[44,5403,1793],{"class":175},[44,5405,3637],{"class":58},[44,5407,5408,5410,5412,5414,5416,5418,5420,5422,5424,5426,5428,5430,5432,5434,5436,5438],{"class":46,"line":79},[44,5409,3611],{"class":58},[44,5411,159],{"class":54},[44,5413,5353],{"class":58},[44,5415,736],{"class":54},[44,5417,172],{"class":58},[44,5419,264],{"class":192},[44,5421,2486],{"class":58},[44,5423,764],{"class":54},[44,5425,3621],{"class":58},[44,5427,802],{"class":50},[44,5429,1257],{"class":58},[44,5431,3628],{"class":54},[44,5433,3621],{"class":58},[44,5435,442],{"class":50},[44,5437,1793],{"class":175},[44,5439,3637],{"class":58},[44,5441,5442,5444,5446,5448,5450,5452,5454,5456,5458,5460,5462,5464,5466,5468,5470,5472],{"class":46,"line":87},[44,5443,3611],{"class":58},[44,5445,159],{"class":54},[44,5447,5353],{"class":58},[44,5449,4118],{"class":54},[44,5451,172],{"class":58},[44,5453,264],{"class":192},[44,5455,2486],{"class":58},[44,5457,764],{"class":54},[44,5459,3621],{"class":58},[44,5461,802],{"class":50},[44,5463,1257],{"class":58},[44,5465,3628],{"class":54},[44,5467,3621],{"class":58},[44,5469,442],{"class":50},[44,5471,1793],{"class":175},[44,5473,3637],{"class":58},[35,5475,5478],{"className":5476,"code":5477,"language":625,"meta":40},[623],"distinct: 4 1 9 7 3 \nsorted:   1 1 3 4 4 7 9 \nlimit(3): 4 4 1 \nskip(3):  9 1 7 3 \n",[30,5479,5477],{"__ignoreMap":40},[13,5481,5482,5484,5485,803,5487,5489],{},[30,5483,4088],{}," 保留第一次出现的顺序，",[30,5486,736],{},[30,5488,4118],{}," 都是按流的顺序数的。",[17,5491,5493],{"id":5492},"_07-终止操作terminal-operations","0.7 终止操作（Terminal Operations）",[22,5495,5497],{"id":5496},"_071-常见方法","0.7.1 常见方法",[957,5499,5500,5511],{},[960,5501,5502],{},[963,5503,5504,5506,5509],{},[966,5505,4034],{},[966,5507,5508],{},"参数",[966,5510,4040],{},[976,5512,5513,5526,5541,5556,5570,5583,5595,5606],{},[963,5514,5515,5519,5523],{},[981,5516,5517],{},[30,5518,764],{},[981,5520,5521],{},[30,5522,2088],{},[981,5524,5525],{},"对每个元素执行一个动作",[963,5527,5528,5533,5538],{},[981,5529,5530],{},[30,5531,5532],{},"reduce",[981,5534,5535],{},[30,5536,5537],{},"T, BinaryOperator\u003CT>",[981,5539,5540],{},"带着初值（identity）做归约，返回归约后的值",[963,5542,5543,5547,5551],{},[981,5544,5545],{},[30,5546,5532],{},[981,5548,5549],{},[30,5550,2215],{},[981,5552,5553,5554],{},"不带初值做归约，返回 ",[30,5555,5282],{},[963,5557,5558,5563,5567],{},[981,5559,5560],{},[30,5561,5562],{},"min",[981,5564,5565],{},[30,5566,2240],{},[981,5568,5569],{},"按比较器取最小元素",[963,5571,5572,5576,5580],{},[981,5573,5574],{},[30,5575,5133],{},[981,5577,5578],{},[30,5579,2240],{},[981,5581,5582],{},"按比较器取最大元素",[963,5584,5585,5590,5592],{},[981,5586,5587],{},[30,5588,5589],{},"average",[981,5591,2174],{},[981,5593,5594],{},"求数字流所有元素的平均值",[963,5596,5597,5601,5603],{},[981,5598,5599],{},[30,5600,3142],{},[981,5602,2174],{},[981,5604,5605],{},"求数字流的和",[963,5607,5608,5613,5618],{},[981,5609,5610],{},[30,5611,5612],{},"collect",[981,5614,5615],{},[30,5616,5617],{},"Collector",[981,5619,5620],{},"把元素收集进容器（List、Set、Map……）",[22,5622,5624,5625,3867,5627],{"id":5623},"_072-foreach-与-mapforeach","0.7.2 ",[30,5626,764],{},[30,5628,5629],{},"Map.forEach",[13,5631,5632,5635],{},[30,5633,5634],{},"forEach(Consumer\u003CT>)"," 就是「遍历每个元素并做点什么」：",[35,5637,5639],{"className":37,"code":5638,"language":39,"meta":40,"style":40},"List\u003CString> list = new ArrayList\u003C>();\nlist.add(\"first\");\nlist.add(\"second\");\nlist.forEach(e -> System.out.print(e + \" \"));\n",[30,5640,5641,5655,5667,5679],{"__ignoreMap":40},[44,5642,5643,5645,5647,5649,5651,5653],{"class":46,"line":47},[44,5644,360],{"class":58},[44,5646,2901],{"class":50},[44,5648,3777],{"class":58},[44,5650,368],{"class":50},[44,5652,153],{"class":50},[44,5654,3784],{"class":58},[44,5656,5657,5659,5661,5663,5665],{"class":46,"line":62},[44,5658,3789],{"class":58},[44,5660,3792],{"class":54},[44,5662,172],{"class":58},[44,5664,3797],{"class":175},[44,5666,2297],{"class":58},[44,5668,5669,5671,5673,5675,5677],{"class":46,"line":71},[44,5670,3789],{"class":58},[44,5672,3792],{"class":54},[44,5674,172],{"class":58},[44,5676,3810],{"class":175},[44,5678,2297],{"class":58},[44,5680,5681,5683,5685,5687,5689,5691,5693,5695,5697,5699],{"class":46,"line":79},[44,5682,3789],{"class":58},[44,5684,764],{"class":54},[44,5686,3621],{"class":58},[44,5688,802],{"class":50},[44,5690,1257],{"class":58},[44,5692,3628],{"class":54},[44,5694,3621],{"class":58},[44,5696,442],{"class":50},[44,5698,1793],{"class":175},[44,5700,3637],{"class":58},[13,5702,5703,5704,5707,5708,5710],{},"顺带一提：",[338,5705,5706],{},"不是所有场合都需要流","。集合自己也有 ",[30,5709,2649],{},"，行为和流的一样。",[13,5712,5713,5714,5720,5721,5723,5724,5727],{},"这是这一节里唯一一个不用流、而是",[338,5715,5716,5717,5719],{},"直接用在 ",[30,5718,3849],{}," 上","的方法——",[30,5722,3849],{}," 的 ",[30,5725,5726],{},"forEach(BiConsumer\u003CK,V>)"," 接收两个参数：",[35,5729,5731],{"className":37,"code":5730,"language":39,"meta":40,"style":40},"Map\u003CString, Integer> nameToLength = new HashMap\u003C>();\nnameToLength.put(\"Alice\", 5);\nnameToLength.put(\"Bryan\", 5);\nnameToLength.put(\"Daniel\", 6);\n\nnameToLength.forEach((name, length) -> System.out.print(name + \"=\" + length + \" \"));\n",[30,5732,5733,5754,5772,5788,5804,5808],{"__ignoreMap":40},[44,5734,5735,5738,5740,5742,5744,5747,5749,5751],{"class":46,"line":47},[44,5736,5737],{"class":58},"Map\u003C",[44,5739,2901],{"class":50},[44,5741,179],{"class":58},[44,5743,2401],{"class":50},[44,5745,5746],{"class":58},"> nameToLength ",[44,5748,368],{"class":50},[44,5750,153],{"class":50},[44,5752,5753],{"class":58}," HashMap\u003C>();\n",[44,5755,5756,5759,5762,5764,5766,5768,5770],{"class":46,"line":62},[44,5757,5758],{"class":58},"nameToLength.",[44,5760,5761],{"class":54},"put",[44,5763,172],{"class":58},[44,5765,176],{"class":175},[44,5767,179],{"class":58},[44,5769,1054],{"class":192},[44,5771,2297],{"class":58},[44,5773,5774,5776,5778,5780,5782,5784,5786],{"class":46,"line":71},[44,5775,5758],{"class":58},[44,5777,5761],{"class":54},[44,5779,172],{"class":58},[44,5781,212],{"class":175},[44,5783,179],{"class":58},[44,5785,1054],{"class":192},[44,5787,2297],{"class":58},[44,5789,5790,5792,5794,5796,5798,5800,5802],{"class":46,"line":79},[44,5791,5758],{"class":58},[44,5793,5761],{"class":54},[44,5795,172],{"class":58},[44,5797,277],{"class":175},[44,5799,179],{"class":58},[44,5801,3600],{"class":192},[44,5803,2297],{"class":58},[44,5805,5806],{"class":46,"line":87},[44,5807,379],{"emptyLinePlaceholder":378},[44,5809,5810,5812,5814,5817,5819,5821,5823,5825,5827,5830,5832,5835,5837,5839],{"class":46,"line":98},[44,5811,5758],{"class":58},[44,5813,764],{"class":54},[44,5815,5816],{"class":58},"((name, length) ",[44,5818,802],{"class":50},[44,5820,1257],{"class":58},[44,5822,3628],{"class":54},[44,5824,5116],{"class":58},[44,5826,442],{"class":50},[44,5828,5829],{"class":175}," \"=\"",[44,5831,1796],{"class":50},[44,5833,5834],{"class":58}," length ",[44,5836,442],{"class":50},[44,5838,1793],{"class":175},[44,5840,3637],{"class":58},[35,5842,5845],{"className":5843,"code":5844,"language":625,"meta":40},[623],"Bryan=5 Alice=5 Daniel=6 \n",[30,5846,5844],{"__ignoreMap":40},[13,5848,5849,5850,5853,5854,5857],{},"注意参数的",[338,5851,5852],{},"位置","才是有意义的：第一个参数是键（Key）、第二个是值（Value），字母叫什么无所谓。反过来写成 ",[30,5855,5856],{},"(v, k)"," 不会报错，但含义就全反了。",[22,5859,5861,5862,5865],{"id":5860},"_073-reduce带初值和不带初值","0.7.3 ",[30,5863,5864],{},"reduce()","：带初值和不带初值",[13,5867,5868],{},"归约（Reduce）指「把流里所有元素合并成单个值\u002F对象」。两个重载的签名不同，用法也不同。",[13,5870,5871,5872,5875,5876,1280],{},"带初值的版本 ",[30,5873,5874],{},"reduce(T identity, BinaryOperator\u003CT>)","，两个参数分别叫累加器（Accumulator）和当前元素，而",[338,5877,5878],{},"参数类型与返回值类型必须相同",[35,5880,5882],{"className":37,"code":5881,"language":39,"meta":40,"style":40},"double[] decimals = {1.1, 2.2, 3.3};\ndouble sum1 = Arrays.stream(decimals)\n        .reduce(0, (subtotal, e) -> subtotal + e);\nSystem.out.println(sum1);\n",[30,5883,5884,5911,5928,5951],{"__ignoreMap":40},[44,5885,5886,5889,5892,5894,5896,5899,5901,5904,5906,5909],{"class":46,"line":47},[44,5887,5888],{"class":50},"double",[44,5890,5891],{"class":58},"[] decimals ",[44,5893,368],{"class":50},[44,5895,2670],{"class":58},[44,5897,5898],{"class":192},"1.1",[44,5900,179],{"class":58},[44,5902,5903],{"class":192},"2.2",[44,5905,179],{"class":58},[44,5907,5908],{"class":192},"3.3",[44,5910,2468],{"class":58},[44,5912,5913,5915,5918,5920,5923,5925],{"class":46,"line":62},[44,5914,5888],{"class":50},[44,5916,5917],{"class":58}," sum1 ",[44,5919,368],{"class":50},[44,5921,5922],{"class":58}," Arrays.",[44,5924,703],{"class":54},[44,5926,5927],{"class":58},"(decimals)\n",[44,5929,5930,5932,5934,5936,5938,5941,5943,5946,5948],{"class":46,"line":71},[44,5931,711],{"class":58},[44,5933,5532],{"class":54},[44,5935,172],{"class":58},[44,5937,232],{"class":192},[44,5939,5940],{"class":58},", (subtotal, e) ",[44,5942,802],{"class":50},[44,5944,5945],{"class":58}," subtotal ",[44,5947,442],{"class":50},[44,5949,5950],{"class":58}," e);\n",[44,5952,5953,5955,5957],{"class":46,"line":79},[44,5954,3270],{"class":58},[44,5956,601],{"class":54},[44,5958,5959],{"class":58},"(sum1);\n",[35,5961,5964],{"className":5962,"code":5963,"language":625,"meta":40},[623],"6.6\n",[30,5965,5963],{"__ignoreMap":40},[13,5967,5968,5969,5972,5973,1280],{},"不带初值的版本 ",[30,5970,5971],{},"reduce(BinaryOperator\u003CT>)"," 只是把那两个参数直接当成两个元素来合并，因为流可能为空，所以返回的是 ",[30,5974,5282],{},[35,5976,5978],{"className":37,"code":5977,"language":39,"meta":40,"style":40},"double sum2 = Arrays.stream(decimals)\n        .reduce((n1, n2) -> n1 + n2)\n        .getAsDouble();\nSystem.out.println(sum2);\n",[30,5979,5980,5995,6014,6023],{"__ignoreMap":40},[44,5981,5982,5984,5987,5989,5991,5993],{"class":46,"line":47},[44,5983,5888],{"class":50},[44,5985,5986],{"class":58}," sum2 ",[44,5988,368],{"class":50},[44,5990,5922],{"class":58},[44,5992,703],{"class":54},[44,5994,5927],{"class":58},[44,5996,5997,5999,6001,6004,6006,6009,6011],{"class":46,"line":62},[44,5998,711],{"class":58},[44,6000,5532],{"class":54},[44,6002,6003],{"class":58},"((n1, n2) ",[44,6005,802],{"class":50},[44,6007,6008],{"class":58}," n1 ",[44,6010,442],{"class":50},[44,6012,6013],{"class":58}," n2)\n",[44,6015,6016,6018,6021],{"class":46,"line":71},[44,6017,711],{"class":58},[44,6019,6020],{"class":54},"getAsDouble",[44,6022,373],{"class":58},[44,6024,6025,6027,6029],{"class":46,"line":79},[44,6026,3270],{"class":58},[44,6028,601],{"class":54},[44,6030,6031],{"class":58},"(sum2);\n",[35,6033,6035],{"className":6034,"code":5963,"language":625,"meta":40},[623],[30,6036,5963],{"__ignoreMap":40},[13,6038,6039,6041],{},[338,6040,3986],{},"：求一个整数数组里所有元素的乘积。",[35,6043,6045],{"className":37,"code":6044,"language":39,"meta":40,"style":40},"int product = IntStream.of(1, 2, 3, 4)\n        .reduce(1, (subproduct, e) -> subproduct * e);\nSystem.out.println(product);\n",[30,6046,6047,6078,6100],{"__ignoreMap":40},[44,6048,6049,6051,6054,6056,6058,6060,6062,6064,6066,6068,6070,6072,6074,6076],{"class":46,"line":47},[44,6050,395],{"class":50},[44,6052,6053],{"class":58}," product ",[44,6055,368],{"class":50},[44,6057,3105],{"class":58},[44,6059,159],{"class":54},[44,6061,172],{"class":58},[44,6063,983],{"class":192},[44,6065,179],{"class":58},[44,6067,198],{"class":192},[44,6069,179],{"class":58},[44,6071,264],{"class":192},[44,6073,179],{"class":58},[44,6075,1035],{"class":192},[44,6077,743],{"class":58},[44,6079,6080,6082,6084,6086,6088,6091,6093,6096,6098],{"class":46,"line":62},[44,6081,711],{"class":58},[44,6083,5532],{"class":54},[44,6085,172],{"class":58},[44,6087,983],{"class":192},[44,6089,6090],{"class":58},", (subproduct, e) ",[44,6092,802],{"class":50},[44,6094,6095],{"class":58}," subproduct ",[44,6097,4914],{"class":50},[44,6099,5950],{"class":58},[44,6101,6102,6104,6106],{"class":46,"line":71},[44,6103,3270],{"class":58},[44,6105,601],{"class":54},[44,6107,6108],{"class":58},"(product);\n",[35,6110,6113],{"className":6111,"code":6112,"language":625,"meta":40},[623],"24\n",[30,6114,6112],{"__ignoreMap":40},[13,6116,6117,6118,6120,6121,6123,6124,6126,6127,6129],{},"初值写 ",[30,6119,983],{}," 而不是 ",[30,6122,232],{},"：乘法里 ",[30,6125,983],{}," 是「什么都不改」的中性值，写 ",[30,6128,232],{}," 结果永远是 0。",[22,6131,6133],{"id":6132},"_074-对引用类型做归约","0.7.4 对引用类型做归约",[13,6135,6136,6138],{},[30,6137,5532],{}," 同样适用于对象。例如找出孩子最多的人：",[35,6140,6142],{"className":37,"code":6141,"language":39,"meta":40,"style":40},"Person mostKids = people().stream()\n        .reduce((p1, p2) -> p1.getKids() > p2.getKids() ? p1 : p2)\n        .get();\nSystem.out.println(mostKids);\n",[30,6143,6144,6159,6195,6203],{"__ignoreMap":40},[44,6145,6146,6149,6151,6153,6155,6157],{"class":46,"line":47},[44,6147,6148],{"class":58},"Person mostKids ",[44,6150,368],{"class":50},[44,6152,142],{"class":54},[44,6154,700],{"class":58},[44,6156,703],{"class":54},[44,6158,706],{"class":58},[44,6160,6161,6163,6165,6167,6169,6171,6173,6175,6177,6180,6182,6184,6187,6190,6192],{"class":46,"line":62},[44,6162,711],{"class":58},[44,6164,5532],{"class":54},[44,6166,4401],{"class":58},[44,6168,802],{"class":50},[44,6170,4406],{"class":58},[44,6172,1104],{"class":54},[44,6174,481],{"class":58},[44,6176,139],{"class":50},[44,6178,6179],{"class":58}," p2.",[44,6181,1104],{"class":54},[44,6183,481],{"class":58},[44,6185,6186],{"class":50},"?",[44,6188,6189],{"class":58}," p1 ",[44,6191,3023],{"class":50},[44,6193,6194],{"class":58}," p2)\n",[44,6196,6197,6199,6201],{"class":46,"line":71},[44,6198,711],{"class":58},[44,6200,472],{"class":54},[44,6202,373],{"class":58},[44,6204,6205,6207,6209],{"class":46,"line":79},[44,6206,3270],{"class":58},[44,6208,601],{"class":54},[44,6210,6211],{"class":58},"(mostKids);\n",[35,6213,6216],{"className":6214,"code":6215,"language":625,"meta":40},[623],"Cindy Tay, 45, 3\n",[30,6217,6215],{"__ignoreMap":40},[13,6219,6220,6221,6224,6225,6228,6229,6232],{},"不带初值时，归约的初值就是流里的第一个元素（相当于 ",[30,6222,6223],{},"persons.get(0)","），然后逐个和后面的元素比较。对象上的 ",[30,6226,6227],{},"toString()"," 我实现成 ",[30,6230,6231],{},"getFullName() + \", \" + age + \", \" + kids","，所以打印成这样。",[22,6234,6236,6237,5296,6239,5296,6241,5296,6243],{"id":6235},"_075-min-max-average-sum","0.7.5 ",[30,6238,5562],{},[30,6240,5133],{},[30,6242,5589],{},[30,6244,3142],{},[13,6246,6247],{},"对数字流来说，这几个聚合函数非常好用：",[35,6249,6251],{"className":37,"code":6250,"language":39,"meta":40,"style":40},"int[] values = {3, 1, 4, 2, 5};\nSystem.out.println(\"min: \" + IntStream.of(values).min().getAsInt()\n        + \", max: \" + IntStream.of(values).max().getAsInt()\n        + \", average: \" + IntStream.of(values).average().getAsDouble()\n        + \", sum: \" + IntStream.of(values).sum());\n",[30,6252,6253,6284,6312,6336,6359],{"__ignoreMap":40},[44,6254,6255,6257,6260,6262,6264,6266,6268,6270,6272,6274,6276,6278,6280,6282],{"class":46,"line":47},[44,6256,395],{"class":50},[44,6258,6259],{"class":58},"[] values ",[44,6261,368],{"class":50},[44,6263,2670],{"class":58},[44,6265,264],{"class":192},[44,6267,179],{"class":58},[44,6269,983],{"class":192},[44,6271,179],{"class":58},[44,6273,1035],{"class":192},[44,6275,179],{"class":58},[44,6277,198],{"class":192},[44,6279,179],{"class":58},[44,6281,1054],{"class":192},[44,6283,2468],{"class":58},[44,6285,6286,6288,6290,6292,6295,6297,6299,6301,6304,6306,6308,6310],{"class":46,"line":62},[44,6287,3270],{"class":58},[44,6289,601],{"class":54},[44,6291,172],{"class":58},[44,6293,6294],{"class":175},"\"min: \"",[44,6296,1796],{"class":50},[44,6298,3105],{"class":58},[44,6300,159],{"class":54},[44,6302,6303],{"class":58},"(values).",[44,6305,5562],{"class":54},[44,6307,700],{"class":58},[44,6309,5142],{"class":54},[44,6311,706],{"class":58},[44,6313,6314,6317,6320,6322,6324,6326,6328,6330,6332,6334],{"class":46,"line":71},[44,6315,6316],{"class":50},"        +",[44,6318,6319],{"class":175}," \", max: \"",[44,6321,1796],{"class":50},[44,6323,3105],{"class":58},[44,6325,159],{"class":54},[44,6327,6303],{"class":58},[44,6329,5133],{"class":54},[44,6331,700],{"class":58},[44,6333,5142],{"class":54},[44,6335,706],{"class":58},[44,6337,6338,6340,6343,6345,6347,6349,6351,6353,6355,6357],{"class":46,"line":79},[44,6339,6316],{"class":50},[44,6341,6342],{"class":175}," \", average: \"",[44,6344,1796],{"class":50},[44,6346,3105],{"class":58},[44,6348,159],{"class":54},[44,6350,6303],{"class":58},[44,6352,5589],{"class":54},[44,6354,700],{"class":58},[44,6356,6020],{"class":54},[44,6358,706],{"class":58},[44,6360,6361,6363,6366,6368,6370,6372,6374,6376],{"class":46,"line":87},[44,6362,6316],{"class":50},[44,6364,6365],{"class":175}," \", sum: \"",[44,6367,1796],{"class":50},[44,6369,3105],{"class":58},[44,6371,159],{"class":54},[44,6373,6303],{"class":58},[44,6375,3142],{"class":54},[44,6377,614],{"class":58},[35,6379,6382],{"className":6380,"code":6381,"language":625,"meta":40},[623],"min: 1, max: 5, average: 3.0, sum: 15\n",[30,6383,6381],{"__ignoreMap":40},[13,6385,6386,6389,6390,2898,6393,1141,6395,2898,6398,6401,6402,6404],{},[30,6387,6388],{},"min()","\u002F",[30,6391,6392],{},"max()",[30,6394,5270],{},[30,6396,6397],{},"average()",[30,6399,6400],{},"OptionalDouble","（流为空时它们没有值可给），",[30,6403,3233],{}," 直接返回数字。",[13,6406,6407,6408,6389,6410,6412,6413,6415],{},"对引用类型，",[30,6409,6388],{},[30,6411,6392],{}," 就必须自己提供 ",[30,6414,2240],{},"——可以写成 lambda：",[35,6417,6419],{"className":37,"code":6418,"language":39,"meta":40,"style":40},"String minName = people().stream()\n        .map(Person::getFirstName)\n        .min((s1, s2) -> s1.compareToIgnoreCase(s2))\n        .get();\nString maxName = people().stream()\n        .map(Person::getFirstName)\n        .max(String::compareToIgnoreCase)\n        .get();\nSystem.out.println(\"first name min: \" + minName + \", max: \" + maxName);\n",[30,6420,6421,6436,6448,6468,6476,6491,6503,6516,6524],{"__ignoreMap":40},[44,6422,6423,6426,6428,6430,6432,6434],{"class":46,"line":47},[44,6424,6425],{"class":58},"String minName ",[44,6427,368],{"class":50},[44,6429,142],{"class":54},[44,6431,700],{"class":58},[44,6433,703],{"class":54},[44,6435,706],{"class":58},[44,6437,6438,6440,6442,6444,6446],{"class":46,"line":62},[44,6439,711],{"class":58},[44,6441,750],{"class":54},[44,6443,723],{"class":58},[44,6445,726],{"class":50},[44,6447,4654],{"class":58},[44,6449,6450,6452,6454,6457,6459,6462,6465],{"class":46,"line":71},[44,6451,711],{"class":58},[44,6453,5562],{"class":54},[44,6455,6456],{"class":58},"((s1, s2) ",[44,6458,802],{"class":50},[44,6460,6461],{"class":58}," s1.",[44,6463,6464],{"class":54},"compareToIgnoreCase",[44,6466,6467],{"class":58},"(s2))\n",[44,6469,6470,6472,6474],{"class":46,"line":79},[44,6471,711],{"class":58},[44,6473,472],{"class":54},[44,6475,373],{"class":58},[44,6477,6478,6481,6483,6485,6487,6489],{"class":46,"line":87},[44,6479,6480],{"class":58},"String maxName ",[44,6482,368],{"class":50},[44,6484,142],{"class":54},[44,6486,700],{"class":58},[44,6488,703],{"class":54},[44,6490,706],{"class":58},[44,6492,6493,6495,6497,6499,6501],{"class":46,"line":98},[44,6494,711],{"class":58},[44,6496,750],{"class":54},[44,6498,723],{"class":58},[44,6500,726],{"class":50},[44,6502,4654],{"class":58},[44,6504,6505,6507,6509,6511,6513],{"class":46,"line":108},[44,6506,711],{"class":58},[44,6508,5133],{"class":54},[44,6510,4996],{"class":58},[44,6512,726],{"class":50},[44,6514,6515],{"class":58},"compareToIgnoreCase)\n",[44,6517,6518,6520,6522],{"class":46,"line":115},[44,6519,711],{"class":58},[44,6521,472],{"class":54},[44,6523,373],{"class":58},[44,6525,6526,6528,6530,6532,6535,6537,6540,6542,6544,6546],{"class":46,"line":528},[44,6527,3270],{"class":58},[44,6529,601],{"class":54},[44,6531,172],{"class":58},[44,6533,6534],{"class":175},"\"first name min: \"",[44,6536,1796],{"class":50},[44,6538,6539],{"class":58}," minName ",[44,6541,442],{"class":50},[44,6543,6319],{"class":175},[44,6545,1796],{"class":50},[44,6547,6548],{"class":58}," maxName);\n",[35,6550,6553],{"className":6551,"code":6552,"language":625,"meta":40},[623],"first name min: Alice, max: Ethan\n",[30,6554,6552],{"__ignoreMap":40},[13,6556,6557,6558,1280],{},"也可以用之前见过的 ",[30,6559,6560],{},"Comparator.comparing()",[35,6562,6564],{"className":37,"code":6563,"language":39,"meta":40,"style":40},"Person oldest = people().stream()\n        .max(Comparator.comparing(Person::getAge))\n        .get();\nSystem.out.println(\"oldest: \" + oldest.getFullName());\n",[30,6565,6566,6581,6597,6605],{"__ignoreMap":40},[44,6567,6568,6571,6573,6575,6577,6579],{"class":46,"line":47},[44,6569,6570],{"class":58},"Person oldest ",[44,6572,368],{"class":50},[44,6574,142],{"class":54},[44,6576,700],{"class":58},[44,6578,703],{"class":54},[44,6580,706],{"class":58},[44,6582,6583,6585,6587,6589,6591,6593,6595],{"class":46,"line":62},[44,6584,711],{"class":58},[44,6586,5133],{"class":54},[44,6588,717],{"class":58},[44,6590,720],{"class":54},[44,6592,723],{"class":58},[44,6594,726],{"class":50},[44,6596,729],{"class":58},[44,6598,6599,6601,6603],{"class":46,"line":71},[44,6600,711],{"class":58},[44,6602,472],{"class":54},[44,6604,373],{"class":58},[44,6606,6607,6609,6611,6613,6616,6618,6621,6623],{"class":46,"line":79},[44,6608,3270],{"class":58},[44,6610,601],{"class":54},[44,6612,172],{"class":58},[44,6614,6615],{"class":175},"\"oldest: \"",[44,6617,1796],{"class":50},[44,6619,6620],{"class":58}," oldest.",[44,6622,611],{"class":54},[44,6624,614],{"class":58},[35,6626,6629],{"className":6627,"code":6628,"language":625,"meta":40},[623],"oldest: Cindy Tay\n",[30,6630,6628],{"__ignoreMap":40},[13,6632,6633,6635,6636,6638,6639,6642],{},[338,6634,3986],{},"：用 ",[30,6637,6392],{}," 打印孩子最多的人的名字。除了 ",[30,6640,6641],{},"Comparator.comparing(Person::getKids)","，也可以手写比较逻辑，只要遵守「大于返回正数、小于返回负数、相等返回 0」：",[35,6644,6646],{"className":37,"code":6645,"language":39,"meta":40,"style":40},".max((p1, p2) -> {\n    if (p1.getKids() > p2.getKids()) {\n        return 1;\n    } else if (p1.getKids() \u003C p2.getKids()) {\n        return -1;\n    } else {\n        return 0;\n    }\n})\n",[30,6647,6648,6660,6679,6687,6712,6723,6731,6739,6743],{"__ignoreMap":40},[44,6649,6650,6652,6654,6656,6658],{"class":46,"line":47},[44,6651,4482],{"class":58},[44,6653,5133],{"class":54},[44,6655,4401],{"class":58},[44,6657,802],{"class":50},[44,6659,59],{"class":58},[44,6661,6662,6664,6667,6669,6671,6673,6675,6677],{"class":46,"line":62},[44,6663,846],{"class":50},[44,6665,6666],{"class":58}," (p1.",[44,6668,1104],{"class":54},[44,6670,481],{"class":58},[44,6672,139],{"class":50},[44,6674,6179],{"class":58},[44,6676,1104],{"class":54},[44,6678,495],{"class":58},[44,6680,6681,6683,6685],{"class":46,"line":71},[44,6682,858],{"class":50},[44,6684,445],{"class":192},[44,6686,1440],{"class":58},[44,6688,6689,6692,6695,6698,6700,6702,6704,6706,6708,6710],{"class":46,"line":79},[44,6690,6691],{"class":58},"    } ",[44,6693,6694],{"class":50},"else",[44,6696,6697],{"class":50}," if",[44,6699,6666],{"class":58},[44,6701,1104],{"class":54},[44,6703,481],{"class":58},[44,6705,134],{"class":50},[44,6707,6179],{"class":58},[44,6709,1104],{"class":54},[44,6711,495],{"class":58},[44,6713,6714,6716,6719,6721],{"class":46,"line":87},[44,6715,858],{"class":50},[44,6717,6718],{"class":50}," -",[44,6720,983],{"class":192},[44,6722,1440],{"class":58},[44,6724,6725,6727,6729],{"class":46,"line":98},[44,6726,6691],{"class":58},[44,6728,6694],{"class":50},[44,6730,59],{"class":58},[44,6732,6733,6735,6737],{"class":46,"line":108},[44,6734,858],{"class":50},[44,6736,403],{"class":192},[44,6738,1440],{"class":58},[44,6740,6741],{"class":46,"line":115},[44,6742,547],{"class":58},[44,6744,6745],{"class":46,"line":528},[44,6746,6747],{"class":58},"})\n",[13,6749,6750,6751,6753,6754,6756,6757,1269],{},"在 ",[30,6752,4584],{}," 这种 ",[30,6755,395],{}," 属性上，这段逻辑可以简写成 ",[30,6758,6759],{},"(p1, p2) -> p1.getKids() - p2.getKids()",[22,6761,6763,6764,3867,6767],{"id":6762},"_076-collect-与-collectors","0.7.6 ",[30,6765,6766],{},"collect()",[30,6768,6769],{},"Collectors",[13,6771,6772,6775],{},[30,6773,6774],{},"collect(Collector)"," 把流里的元素归约进可变容器（Mutable Container），例如 List、Set、Map。",[13,6777,6778,6779,1280],{},"收集成 ",[30,6780,3755],{},[35,6782,6784],{"className":37,"code":6783,"language":39,"meta":40,"style":40},"String[] names = {\"Alice\", \"Bryan\", \"Cindy\", \"Daniel\", \"Ethan\"};\nList\u003CString> longNames = Arrays.stream(names)\n        .filter(name -> name.length() >= 6)\n        .collect(Collectors.toList());\nSystem.out.println(longNames);\n",[30,6785,6786,6816,6833,6857,6871],{"__ignoreMap":40},[44,6787,6788,6790,6792,6794,6796,6798,6800,6802,6804,6806,6808,6810,6812,6814],{"class":46,"line":47},[44,6789,2901],{"class":50},[44,6791,5066],{"class":58},[44,6793,368],{"class":50},[44,6795,2670],{"class":58},[44,6797,176],{"class":175},[44,6799,179],{"class":58},[44,6801,212],{"class":175},[44,6803,179],{"class":58},[44,6805,245],{"class":175},[44,6807,179],{"class":58},[44,6809,277],{"class":175},[44,6811,179],{"class":58},[44,6813,308],{"class":175},[44,6815,2468],{"class":58},[44,6817,6818,6820,6822,6825,6827,6829,6831],{"class":46,"line":62},[44,6819,360],{"class":58},[44,6821,2901],{"class":50},[44,6823,6824],{"class":58},"> longNames ",[44,6826,368],{"class":50},[44,6828,5922],{"class":58},[44,6830,703],{"class":54},[44,6832,5107],{"class":58},[44,6834,6835,6837,6839,6841,6843,6845,6847,6849,6852,6855],{"class":46,"line":71},[44,6836,711],{"class":58},[44,6838,1093],{"class":54},[44,6840,5116],{"class":58},[44,6842,802],{"class":50},[44,6844,5121],{"class":58},[44,6846,5124],{"class":54},[44,6848,481],{"class":58},[44,6850,6851],{"class":50},">=",[44,6853,6854],{"class":192}," 6",[44,6856,743],{"class":58},[44,6858,6859,6861,6863,6866,6869],{"class":46,"line":79},[44,6860,711],{"class":58},[44,6862,5612],{"class":54},[44,6864,6865],{"class":58},"(Collectors.",[44,6867,6868],{"class":54},"toList",[44,6870,614],{"class":58},[44,6872,6873,6875,6877],{"class":46,"line":87},[44,6874,3270],{"class":58},[44,6876,601],{"class":54},[44,6878,6879],{"class":58},"(longNames);\n",[35,6881,6884],{"className":6882,"code":6883,"language":625,"meta":40},[623],"[Daniel]\n",[30,6885,6883],{"__ignoreMap":40},[13,6887,6778,6888,6890],{},[30,6889,3758],{},"（可以去重）：",[35,6892,6894],{"className":37,"code":6893,"language":39,"meta":40,"style":40},"Set\u003CString> upperNames = Arrays.stream(names)\n        .map(String::toUpperCase)\n        .collect(Collectors.toSet());\nSystem.out.println(upperNames);\n",[30,6895,6896,6914,6926,6939],{"__ignoreMap":40},[44,6897,6898,6901,6903,6906,6908,6910,6912],{"class":46,"line":47},[44,6899,6900],{"class":58},"Set\u003C",[44,6902,2901],{"class":50},[44,6904,6905],{"class":58},"> upperNames ",[44,6907,368],{"class":50},[44,6909,5922],{"class":58},[44,6911,703],{"class":54},[44,6913,5107],{"class":58},[44,6915,6916,6918,6920,6922,6924],{"class":46,"line":62},[44,6917,711],{"class":58},[44,6919,750],{"class":54},[44,6921,4996],{"class":58},[44,6923,726],{"class":50},[44,6925,5001],{"class":58},[44,6927,6928,6930,6932,6934,6937],{"class":46,"line":71},[44,6929,711],{"class":58},[44,6931,5612],{"class":54},[44,6933,6865],{"class":58},[44,6935,6936],{"class":54},"toSet",[44,6938,614],{"class":58},[44,6940,6941,6943,6945],{"class":46,"line":79},[44,6942,3270],{"class":58},[44,6944,601],{"class":54},[44,6946,6947],{"class":58},"(upperNames);\n",[35,6949,6952],{"className":6950,"code":6951,"language":625,"meta":40},[623],"[ETHAN, ALICE, DANIEL, BRYAN, CINDY]\n",[30,6953,6951],{"__ignoreMap":40},[13,6955,6956,6958,6959,1141,6961,6964],{},[30,6957,3758],{}," 是 ",[30,6960,3857],{},[338,6962,6963],{},"不保证顺序","，所以输出顺序和原数组不一致是正常的，不必按顺序去对。",[13,6966,6778,6967,5271,6969,6972],{},[30,6968,3849],{},[30,6970,6971],{},"Collectors.toMap()","，要回答两个问题：键是什么、值是什么：",[35,6974,6976],{"className":37,"code":6975,"language":39,"meta":40,"style":40},"Map\u003CString, Integer> nameToLength = Arrays.stream(names)\n        .collect(Collectors.toMap(\n                name -> name,\n                name -> name.length()));\nSystem.out.println(nameToLength);\n",[30,6977,6978,6998,7011,7021,7033],{"__ignoreMap":40},[44,6979,6980,6982,6984,6986,6988,6990,6992,6994,6996],{"class":46,"line":47},[44,6981,5737],{"class":58},[44,6983,2901],{"class":50},[44,6985,179],{"class":58},[44,6987,2401],{"class":50},[44,6989,5746],{"class":58},[44,6991,368],{"class":50},[44,6993,5922],{"class":58},[44,6995,703],{"class":54},[44,6997,5107],{"class":58},[44,6999,7000,7002,7004,7006,7009],{"class":46,"line":62},[44,7001,711],{"class":58},[44,7003,5612],{"class":54},[44,7005,6865],{"class":58},[44,7007,7008],{"class":54},"toMap",[44,7010,162],{"class":58},[44,7012,7013,7016,7018],{"class":46,"line":71},[44,7014,7015],{"class":58},"                name ",[44,7017,802],{"class":50},[44,7019,7020],{"class":58}," name,\n",[44,7022,7023,7025,7027,7029,7031],{"class":46,"line":79},[44,7024,7015],{"class":58},[44,7026,802],{"class":50},[44,7028,5121],{"class":58},[44,7030,5124],{"class":54},[44,7032,4688],{"class":58},[44,7034,7035,7037,7039],{"class":46,"line":87},[44,7036,3270],{"class":58},[44,7038,601],{"class":54},[44,7040,7041],{"class":58},"(nameToLength);\n",[35,7043,7046],{"className":7044,"code":7045,"language":625,"meta":40},[623],"{Ethan=5, Bryan=5, Alice=5, Daniel=6, Cindy=5}\n",[30,7047,7045],{"__ignoreMap":40},[13,7049,7050],{},"第一个 lambda 说明键就是元素本身，第二个说明值是这个元素的长度。",[22,7052,7054,7055],{"id":7053},"_077-分组groupingby","0.7.7 分组：",[30,7056,7057],{},"groupingBy",[13,7059,7060,7063,7064,1280],{},[30,7061,7062],{},"Collectors.groupingBy()"," 按某个属性把元素分组，得到「属性值 → 该组元素列表」的 ",[30,7065,3849],{},[35,7067,7069],{"className":37,"code":7068,"language":39,"meta":40,"style":40},"Map\u003CString, List\u003CPerson>> genderToPersons = people().stream()\n        .collect(Collectors.groupingBy(Person::getGender));\nSystem.out.println(genderToPersons);\n",[30,7070,7071,7095,7112],{"__ignoreMap":40},[44,7072,7073,7075,7077,7080,7082,7085,7087,7089,7091,7093],{"class":46,"line":47},[44,7074,5737],{"class":58},[44,7076,2901],{"class":50},[44,7078,7079],{"class":58},", List\u003C",[44,7081,32],{"class":50},[44,7083,7084],{"class":58},">> genderToPersons ",[44,7086,368],{"class":50},[44,7088,142],{"class":54},[44,7090,700],{"class":58},[44,7092,703],{"class":54},[44,7094,706],{"class":58},[44,7096,7097,7099,7101,7103,7105,7107,7109],{"class":46,"line":62},[44,7098,711],{"class":58},[44,7100,5612],{"class":54},[44,7102,6865],{"class":58},[44,7104,7057],{"class":54},[44,7106,723],{"class":58},[44,7108,726],{"class":50},[44,7110,7111],{"class":58},"getGender));\n",[44,7113,7114,7116,7118],{"class":46,"line":71},[44,7115,3270],{"class":58},[44,7117,601],{"class":54},[44,7119,7120],{"class":58},"(genderToPersons);\n",[35,7122,7125],{"className":7123,"code":7124,"language":625,"meta":40},[623],"{F=[Alice Wong, 31, 2, Cindy Tay, 45, 3], M=[Bryan Chua, 24, 0, Daniel Koh, 38, 2, Ethan Goh, 27, 2]}\n",[30,7126,7124],{"__ignoreMap":40},[13,7128,7129,7130,7132],{},"同样地，",[30,7131,3852],{}," 不保证组的顺序。",[22,7134,7136],{"id":7135},"_078-分组后再汇总","0.7.8 分组后再汇总",[13,7138,7139],{},"分组的第二个参数可以再给一个收集器，用来对每组做汇总。例如统计每个性别的孩子总数：",[35,7141,7143],{"className":37,"code":7142,"language":39,"meta":40,"style":40},"Map\u003CString, Integer> genderToTotalKids = people().stream()\n        .collect(Collectors.groupingBy(\n                Person::getGender,\n                Collectors.summingInt(Person::getKids)));\nSystem.out.println(genderToTotalKids);\n",[30,7144,7145,7168,7180,7190,7205],{"__ignoreMap":40},[44,7146,7147,7149,7151,7153,7155,7158,7160,7162,7164,7166],{"class":46,"line":47},[44,7148,5737],{"class":58},[44,7150,2901],{"class":50},[44,7152,179],{"class":58},[44,7154,2401],{"class":50},[44,7156,7157],{"class":58},"> genderToTotalKids ",[44,7159,368],{"class":50},[44,7161,142],{"class":54},[44,7163,700],{"class":58},[44,7165,703],{"class":54},[44,7167,706],{"class":58},[44,7169,7170,7172,7174,7176,7178],{"class":46,"line":62},[44,7171,711],{"class":58},[44,7173,5612],{"class":54},[44,7175,6865],{"class":58},[44,7177,7057],{"class":54},[44,7179,162],{"class":58},[44,7181,7182,7185,7187],{"class":46,"line":71},[44,7183,7184],{"class":58},"                Person",[44,7186,726],{"class":50},[44,7188,7189],{"class":58},"getGender,\n",[44,7191,7192,7195,7198,7200,7202],{"class":46,"line":79},[44,7193,7194],{"class":58},"                Collectors.",[44,7196,7197],{"class":54},"summingInt",[44,7199,723],{"class":58},[44,7201,726],{"class":50},[44,7203,7204],{"class":58},"getKids)));\n",[44,7206,7207,7209,7211],{"class":46,"line":87},[44,7208,3270],{"class":58},[44,7210,601],{"class":54},[44,7212,7213],{"class":58},"(genderToTotalKids);\n",[35,7215,7218],{"className":7216,"code":7217,"language":625,"meta":40},[623],"{F=5, M=4}\n",[30,7219,7217],{"__ignoreMap":40},[13,7221,7222,7225,7226,1141,7229,7225,7232,7235],{},[30,7223,7224],{},"F"," 组是 ",[30,7227,7228],{},"Alice(2) + Cindy(3) = 5",[30,7230,7231],{},"M",[30,7233,7234],{},"Bryan(0) + Daniel(2) + Ethan(2) = 4","。第二个参数换成分组里的其他汇总收集器（求平均、计数、取最大……）就能做别的统计。",[17,7237,7239],{"id":7238},"_08-函数式接口与流方法的对应关系","0.8 函数式接口与流方法的对应关系",[13,7241,7242],{},"把前面出现过的函数式接口和它们服务的流方法对起来看，整个体系就清楚了：",[957,7244,7245,7257],{},[960,7246,7247],{},[963,7248,7249,7252,7255],{},[966,7250,7251],{},"函数式接口",[966,7253,7254],{},"出现的方法",[966,7256,974],{},[976,7258,7259,7277,7298,7317,7332],{},[963,7260,7261,7265,7269],{},[981,7262,7263],{},[30,7264,2141],{},[981,7266,7267],{},[30,7268,1093],{},[981,7270,7271,786,7274],{},[30,7272,7273],{},".filter(x -> x % 2 == 0)",[30,7275,7276],{},".filter(p -> p.getAge() > 40)",[963,7278,7279,7283,7290],{},[981,7280,7281],{},[30,7282,2115],{},[981,7284,7285,786,7287],{},[30,7286,750],{},[30,7288,7289],{},"Comparator.comparing",[981,7291,7292,786,7295],{},[30,7293,7294],{},".map(p -> p.getFirstName() + \" \" + p.getLastName())",[30,7296,7297],{},".map(String::toUpperCase)",[963,7299,7300,7304,7312],{},[981,7301,7302],{},[30,7303,2240],{},[981,7305,7306,786,7308,786,7310],{},[30,7307,714],{},[30,7309,5562],{},[30,7311,5133],{},[981,7313,7314],{},[30,7315,7316],{},".sorted((p1, p2) -> p1.getFirstName().compareTo(p2.getFirstName()))",[963,7318,7319,7323,7327],{},[981,7320,7321],{},[30,7322,2088],{},[981,7324,7325],{},[30,7326,764],{},[981,7328,7329],{},[30,7330,7331],{},".forEach(e -> System.out.print(e + \" \"))",[963,7333,7334,7338,7342],{},[981,7335,7336],{},[30,7337,2215],{},[981,7339,7340],{},[30,7341,5532],{},[981,7343,7344],{},[30,7345,7346],{},".reduce((n1, n2) -> n1 + n2)",[13,7348,7349,7350],{},"也可以反过来记：",[338,7351,7352],{},"每个函数式接口都在回答三个问题——几个参数？返回什么？参数和返回值的类型是什么？",[3240,7354,7355,7362,7367,7372,7378],{},[3243,7356,7357,7359,7360,1269],{},[30,7358,2141],{},"：1 个参数，返回 ",[30,7361,1581],{},[3243,7363,7364,7366],{},[30,7365,2115],{},"：1 个参数，返回非 void 的值。",[3243,7368,7369,7371],{},[30,7370,2240],{},"：2 个参数，返回正数\u002F负数\u002F0。",[3243,7373,7374,7359,7376,1269],{},[30,7375,2088],{},[30,7377,1816],{},[3243,7379,7380,7382],{},[30,7381,2215],{},"：2 个参数，参数类型与返回类型相同。",[17,7384,7386],{"id":7385},"_09-回到开篇问题","0.9 回到开篇问题",[35,7388,7389],{"className":37,"code":690,"language":39,"meta":40,"style":40},[30,7390,7391,7401,7417,7429,7441],{"__ignoreMap":40},[44,7392,7393,7395,7397,7399],{"class":46,"line":47},[44,7394,697],{"class":54},[44,7396,700],{"class":58},[44,7398,703],{"class":54},[44,7400,706],{"class":58},[44,7402,7403,7405,7407,7409,7411,7413,7415],{"class":46,"line":62},[44,7404,711],{"class":58},[44,7406,714],{"class":54},[44,7408,717],{"class":58},[44,7410,720],{"class":54},[44,7412,723],{"class":58},[44,7414,726],{"class":50},[44,7416,729],{"class":58},[44,7418,7419,7421,7423,7425,7427],{"class":46,"line":71},[44,7420,711],{"class":58},[44,7422,736],{"class":54},[44,7424,172],{"class":58},[44,7426,264],{"class":192},[44,7428,743],{"class":58},[44,7430,7431,7433,7435,7437,7439],{"class":46,"line":79},[44,7432,711],{"class":58},[44,7434,750],{"class":54},[44,7436,723],{"class":58},[44,7438,726],{"class":50},[44,7440,757],{"class":58},[44,7442,7443,7445,7447,7449,7451],{"class":46,"line":87},[44,7444,711],{"class":58},[44,7446,764],{"class":54},[44,7448,767],{"class":58},[44,7450,726],{"class":50},[44,7452,772],{"class":58},[35,7454,7456],{"className":7455,"code":624,"language":625,"meta":40},[623],[30,7457,624],{"__ignoreMap":40},[13,7459,7460,7461,7463,7464,1141,7466,7468,7469,7472,7473,7475,7476,7463,7478,7480,7481,7483,7484,3729,7486,7463,7488,7491,7492,7494],{},"现在这份代码里的每个部分都能解释了：",[30,7462,714],{}," 要一个 ",[30,7465,4452],{},[30,7467,7289],{}," 用一个 ",[30,7470,7471],{},"Function"," 取排序依据；",[30,7474,736],{}," 是个普通数字参数；",[30,7477,750],{},[30,7479,7471],{},"，把 ",[30,7482,32],{}," 变成 ",[30,7485,2901],{},[30,7487,764],{},[30,7489,7490],{},"Consumer","，而 ",[30,7493,1064],{}," 是方法引用。",[13,7496,7497,7498],{},"和最初的命令式版本相比，这里没有下标、没有临时变量、也没有显写的循环——命令式的循环被换成了声明式的聚合操作（Aggregate Operations）：",[338,7499,7500],{},"说清楚要什么，让流去算。",[7502,7503,7504],"style",{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}",{"title":40,"searchDepth":71,"depth":71,"links":7506},[7507,7508,7509,7510,7511,7512,7513,7514,7516,7517,7518,7519,7520,7521,7522,7523,7524,7525,7526,7527,7528,7529,7530,7531,7532,7533,7535,7537,7539,7541,7543,7544,7546,7548,7549,7551,7553,7555],{"id":24,"depth":62,"text":25},{"id":346,"depth":62,"text":347},{"id":633,"depth":62,"text":634},{"id":686,"depth":62,"text":687},{"id":813,"depth":62,"text":814},{"id":930,"depth":62,"text":931},{"id":1067,"depth":62,"text":1068},{"id":1151,"depth":62,"text":7515},"0.2.4 省略 return 与花括号",{"id":1238,"depth":62,"text":1239},{"id":1272,"depth":62,"text":1273},{"id":1394,"depth":62,"text":1395},{"id":1952,"depth":62,"text":1953},{"id":2057,"depth":62,"text":2058},{"id":2380,"depth":62,"text":2381},{"id":2525,"depth":62,"text":2526},{"id":2642,"depth":62,"text":2643},{"id":2782,"depth":62,"text":2783},{"id":2968,"depth":62,"text":2969},{"id":3159,"depth":62,"text":3160},{"id":3237,"depth":62,"text":3238},{"id":3433,"depth":62,"text":3434},{"id":3571,"depth":62,"text":3572},{"id":3744,"depth":62,"text":3745},{"id":3860,"depth":62,"text":3861},{"id":4024,"depth":62,"text":4025},{"id":4141,"depth":62,"text":7534},"0.6.2 filter(Predicate\u003CT>)",{"id":4312,"depth":62,"text":7536},"0.6.3 sorted(Comparator\u003CT>)",{"id":4691,"depth":62,"text":7538},"0.6.4 map(Function\u003CT,R>)",{"id":5041,"depth":62,"text":7540},"0.6.5 map 与 mapToInt 的区别",{"id":5292,"depth":62,"text":7542},"0.6.6 distinct \u002F limit \u002F skip",{"id":5496,"depth":62,"text":5497},{"id":5623,"depth":62,"text":7545},"0.7.2 forEach 与 Map.forEach",{"id":5860,"depth":62,"text":7547},"0.7.3 reduce()：带初值和不带初值",{"id":6132,"depth":62,"text":6133},{"id":6235,"depth":62,"text":7550},"0.7.5 min \u002F max \u002F average \u002F sum",{"id":6762,"depth":62,"text":7552},"0.7.6 collect() 与 Collectors",{"id":7053,"depth":62,"text":7554},"0.7.7 分组：groupingBy",{"id":7135,"depth":62,"text":7136},"学习",null,"2026-10-13","学习 Java 函数式编程时的个人笔记：Lambda 表达式的五步简写与方法引用、常见函数式接口、Stream 流水线与惰性求值，以及创建流、中间操作、终止操作与收集器的用法。示例代码均为自写，运行结果来自本地实测。","md",false,{},"\u002Fposts\u002Fjava\u002Fjava-functionalprogramming-notes",{"title":5,"description":7559},"Java 学习笔记","posts\u002Fjava\u002Fjava-functionalprogramming-notes",[7568,7569,7570,7571,7572],"Java","函数式编程","Lambda","Stream","学习笔记","vLQD3dZvHtW8W3rhvYVcGV83a6WK9mU1_Sa_pbOP4Dw",[7575,7582,7588,7594,7600,7606,7612,7618,7624,7630,7636,7642,7647,7652,7657,7662,7667,7672,7677,7682,7687,7692,7697,7702,7707,7712,7717,7722,7727,7733,7737,7741,7745,7749,7753,7757,7761,7765,7769,7773,7777,7778,7782,7788,7792,7796,7800,7804,7808,7812,7816,7821,7825,7829,7834,7839,7845,7849,7854,7858],{"path":7576,"title":7577,"description":7578,"date":7579,"series":7580,"seriesOrder":7581},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-28","软件分析与设计笔记 28：draw.io 作图手册：五张图的符号位置、落笔顺序与自检","不模拟软件界面，只讲清三件事：每个 UML 符号在 draw.io 里搜什么词、一张图该按什么顺序落笔、画完拿什么标准自查。覆盖用例图、活动图、顺序图、类图、界面草图五种图纸，末尾附「最容易画反的十二笔」总检表。","2026-11-19","软件分析与设计笔记",28,{"path":7583,"title":7584,"description":7585,"date":7586,"series":7580,"seriesOrder":7587},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-27","软件分析与设计笔记 27：Paper 1 应试：从读案例到交卷的一次完整演算","拿一份完整的案例，从头到尾走一遍：怎么读案例列角色、怎么在四个小问之间分配时间、每一张图从哪一句话推出来、两张表怎么填、交卷前拿什么清单自查。附 C25 真题与样卷的题型分值对照。","2026-11-18",27,{"path":7589,"title":7590,"description":7591,"date":7592,"series":7580,"seriesOrder":7593},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-26","软件分析与设计笔记 26：总结：把整条链路串起来","系列收尾：从需求到代码的完整链路回顾、每一环的产出与验收方式、六条贯穿全系列的原则，以及一份可以直接拿去做项目自查的清单。","2026-11-17",26,{"path":7595,"title":7596,"description":7597,"date":7598,"series":7580,"seriesOrder":7599},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-25","软件分析与设计笔记 25：代码重构：坏味道与手法","重构不改变外部行为、只改善内部结构：四种常见坏味道与对应手法、为什么动手之前必须先有测试、什么时候该重构，以及重构和前面那些设计模型是什么关系。","2026-11-16",25,{"path":7601,"title":7602,"description":7603,"date":7604,"series":7580,"seriesOrder":7605},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-24","软件分析与设计笔记 24：设计原则：内聚、耦合与多态","好设计怎么衡量：内聚与耦合分别在说什么、一次从「差设计」到「多态重设计」的完整对照、抽象类与接口的选择依据，以及复用为什么会从优势变成风险。","2026-11-15",24,{"path":7607,"title":7608,"description":7609,"date":7610,"series":7580,"seriesOrder":7611},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-23","软件分析与设计笔记 23：设计建模：从对象模型到能实现的方案","分析模型能用但还不能直接开工：对象模型怎么映射到关系模型、继承映射的三种方案怎么选、数据访问层与 Data Broker 解决什么问题，以及三层架构与 MVC 的分工。","2026-11-14",23,{"path":7613,"title":7614,"description":7615,"date":7616,"series":7580,"seriesOrder":7617},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-22","软件分析与设计笔记 22：分析建模：边界、实体与控制对象","不经过分析直接写代码会踩什么坑、三种分析对象各管一段、怎么用顺序图完成一次用例实现，以及从顺序图导出类的属性、操作与关系的方法。","2026-11-13",22,{"path":7619,"title":7620,"description":7621,"date":7622,"series":7580,"seriesOrder":7623},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-21","软件分析与设计笔记 21：UML：类图与顺序图","为什么需要一种共同的画法：类符号三格怎么画、五种关系的符号与设计含义、每种关系落到 Java 是什么写法，顺序图的三要素怎么摆；以及这三种最容易找不到的符号在 draw.io 里各搜什么词。","2026-11-12",21,{"path":7625,"title":7626,"description":7627,"date":7628,"series":7580,"seriesOrder":7629},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-20","软件分析与设计笔记 20：面向对象概念：对象、关系与多态","面向对象不是「用某个语言写代码」：对象与类的区别、封装真正在保护什么、五种关系（依赖、关联、聚合、组合、继承）各自何时用，以及多态为什么能减少条件判断。","2026-11-11",20,{"path":7631,"title":7632,"description":7633,"date":7634,"series":7580,"seriesOrder":7635},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-19","软件分析与设计笔记 19：规范化与逻辑数据模型","一次改地址引发的数据不一致，讲清规范化的真正动机；UNF 到 3NF 每一步在解决什么问题，派生字段为什么不能存，优化为什么和规范化方向相反，以及逻辑数据模型在 3NF 之上补了什么。","2026-11-10",19,{"path":7637,"title":7638,"description":7639,"date":7640,"series":7580,"seriesOrder":7641},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-18","软件分析与设计笔记 18：数据库分析：实体、关系与数据字典","为什么不能拿到需求就动手建表：实体与关系怎么判定、基数为什么必须由业务方确认、数据命名要遵守什么规则，以及数据字典四列里最容易被空着的那一列。","2026-11-09",18,{"path":7643,"title":7644,"description":7645,"date":7646,"series":7580,"seriesOrder":617},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-17","软件分析与设计笔记 17：用生成式 AI 做设计与开发：能用的场景与要防的坑","生成式 AI 在工作流里真正站得住的三个场景 —— 造示例数据、解释陌生代码、起草初稿；它为什么会复现社会偏见、为什么会编得像真的，以及哪些内容绝不能直接采用。","2026-11-08",{"path":7648,"title":7649,"description":7650,"date":7651,"series":7580,"seriesOrder":595},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-16","软件分析与设计笔记 16：从 persona 到 URS：一份交付物的完整骨架","把前面十几篇串成一份能交出去的东西：用户需求规格说明书的九个部分、每一章来自哪个环节、由谁确认，以及面向决策者的提案该讲清哪三件事。","2026-11-07",{"path":7653,"title":7654,"description":7655,"date":7656,"series":7580,"seriesOrder":566},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-15","软件分析与设计笔记 15：非功能需求：把「快、稳、安全」变成数字","为什么「系统要快」不是一条需求：非功能需求怎么量化，决定性能的用户量、交易量、数据量三张表怎么估，安全需求要写清什么，以及可靠性的「不可用」该怎么定义。","2026-11-06",{"path":7658,"title":7659,"description":7660,"date":7661,"series":7580,"seriesOrder":560},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-14","软件分析与设计笔记 14：软件测试：层级、方法与等价类划分","「我们测过了」这句话问题在哪：为什么静态评审和动态测试缺一不可，五个测试层级各自验证什么，白盒与黑盒的差别，以及用等价类划分和边界值把无限输入变成有限用例的方法。","2026-11-05",{"path":7663,"title":7664,"description":7665,"date":7666,"series":7580,"seriesOrder":555},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-13","软件分析与设计笔记 13：需求获取：五种调查方法与一场访谈","需求从哪里来：五种事实调查方法各自适合回答什么、代价是什么，访谈从准备到记录的全过程，问卷设计里最容易犯的错，以及访谈记录必须写下来的那几项信息。","2026-11-04",{"path":7668,"title":7669,"description":7670,"date":7671,"series":7580,"seriesOrder":550},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-12","软件分析与设计笔记 12：前端设计：把流程变成屏幕","界面设计不是「画得好看」：从活动图走到屏幕的三步，CRUD 任务流怎么当检查表，界面必须回答的三个问题，信息架构与五种导航类型，视觉一致性与留白，以及移动端与 Web 端的差别。","2026-11-03",{"path":7673,"title":7674,"description":7675,"date":7676,"series":7580,"seriesOrder":544},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-11","软件分析与设计笔记 11：Agentic AI 与业务流程自动化","AI agent 到底是什么：五项能力与四个核心组件，什么场景该用定时任务、什么该用程序、什么才值得用 agent，两种实现路径的差别，必须提前防住的四类失败模式，以及设计方法从提示词到编排的演进。","2026-11-02",{"path":7678,"title":7679,"description":7680,"date":7681,"series":7580,"seriesOrder":538},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-10","软件分析与设计笔记 10：精炼用例模型：include、extend 与泛化","三种精炼用例关系的判断标准：include 什么时候用、它和 extend 的区别到底在哪、参与者泛化与用例泛化各解决什么问题，以及精炼到什么程度就该停手。","2026-11-01",{"path":7683,"title":7684,"description":7685,"date":7686,"series":7580,"seriesOrder":528},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-09","软件分析与设计笔记 09：功能需求（二）：从流程到业务实体","活动图画完之后，怎么从流程里筛出真正的业务实体：三条判断标准、哪些名词其实不该建实体、属性从哪里来，以及领域模型和后面的数据模型分别负责什么。","2026-10-31",{"path":7688,"title":7689,"description":7690,"date":7691,"series":7580,"seriesOrder":115},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-08","软件分析与设计笔记 08：功能需求（一）：用例模型与活动图","从用户故事到用例的翻译过程：什么才算「好」的参与者，系统边界怎么划，用例描述必须写清哪四件事，活动图怎么把分支、并行与泳道画清楚，以及这两个图在 draw.io 里各用哪个形状、最容易画错哪一笔。","2026-10-30",{"path":7693,"title":7694,"description":7695,"date":7696,"series":7580,"seriesOrder":108},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-07","软件分析与设计笔记 07：用户故事：把需求写成可验收的一句话","用户故事的三段结构，怎么从用户画像和数据地图里把它提取出来，一条故事多大算合适、时序为什么也是需求的一部分，以及验收标准怎么写才算真的可验收。","2026-10-29",{"path":7698,"title":7699,"description":7700,"date":7701,"series":7580,"seriesOrder":98},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-06","软件分析与设计笔记 06：范围界定（数据侧）：用数据校验你的判断","访谈结论和数字对不上时该怎么办：分析的四个层次分别在回答什么问题、为什么不能跳级、数据准备与搜集怎么做成一张应用地图，以及数据最容易在哪几个地方骗你。","2026-10-28",{"path":7703,"title":7704,"description":7705,"date":7706,"series":7580,"seriesOrder":87},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-05","软件分析与设计笔记 05：范围界定（用户侧）：用户画像与客户旅程图","为什么「所有用户」是一个陷阱，项目边界该怎么划，用户画像要写哪五个要素、最容易写成什么废纸，客户旅程图怎么把阶段、想法和情绪画在同一张纸上，以及怎么从这两样东西一步步推到可验证的需求。","2026-10-27",{"path":7708,"title":7709,"description":7710,"date":7711,"series":7580,"seriesOrder":79},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-04","软件分析与设计笔记 04：接手陌生案例的第一步：三问与一张表","拿到一个完全陌生的业务案例，怎么从零开始不跑偏：只问 Who、What、Why 三个问题，把「问题—事实—假设—理由」记成一张四列表，说清为什么假设必须写明理由，以及带着问题进访谈为什么比空手去有效得多。","2026-10-26",{"path":7713,"title":7714,"description":7715,"date":7716,"series":7580,"seriesOrder":71},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-03","软件分析与设计笔记 03：Agile 与 Scrum：迭代、角色与产出物","从「三周做完交付，客户说这不是我要的」讲起，说清瀑布式与迭代式的根本差别，Scrum 的三个角色和三种产出物，一个 sprint 里四次固定活动各自在解决什么问题，以及敏捷里最常见的几个误解。","2026-10-25",{"path":7718,"title":7719,"description":7720,"date":7721,"series":7580,"seriesOrder":62},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-02","软件分析与设计笔记 02：设计思维、用户驱动与数据驱动","从「客服要一个能一次看完所有排期的界面」这个翻车案例讲起，说清设计思维到底是什么、用户驱动与数据驱动各自能看见什么又看不见什么、为什么真正的落点是把两者合起来的 data-informed design，以及访谈中怎么问出有用的信息。","2026-10-24",{"path":7723,"title":7724,"description":7725,"date":7726,"series":7580,"seriesOrder":47},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-01","软件分析与设计笔记 01：课程地图与从需求到设计的主干","这一篇不写具体方法，先把整条链路铺开：软件分析与设计到底要解决什么问题，从需求到设计分成哪两段，中间有哪十几个环节，一份完整的交付物里应该有什么，以及后面 27 篇笔记怎么排、新人该按什么顺序读。","2026-10-23",{"path":7728,"title":7729,"description":7730,"date":7731,"series":7732,"seriesOrder":47},"\u002Fposts\u002Fjavaspring\u002Fspring-notes-01","Spring 学习笔记 01：IoC、依赖注入与第一个 Spring Boot 应用","从「手写 new 出来的三层结构」的痛点讲起，说清 IoC 容器、Bean 与依赖注入是怎么回事，再用 Maven 与 Spring Initializr 跑起第一个能返回 JSON 的应用。","2026-10-15","Spring 学习笔记",{"path":7734,"title":7735,"description":7736,"date":7731,"series":7732,"seriesOrder":62},"\u002Fposts\u002Fjavaspring\u002Fspring-notes-02","Spring 学习笔记 02：Spring Data JPA 基础——实体映射与 Repository","用 ORM 把类映射到表：@Entity、@Id、@GeneratedValue 怎么用，CrudRepository 三层接口怎么选，H2 与 MySQL 怎么配，以及 ddl-auto 到底该写哪个值。",{"path":7738,"title":7739,"description":7740,"date":7731,"series":7732,"seriesOrder":71},"\u002Fposts\u002Fjavaspring\u002Fspring-notes-03","Spring 学习笔记 03：JPA 查询——派生方法、JPQL 与分页排序","Repository 自带的方法只够按主键查，这篇讲三种自己造查询的方式：方法名派生、@Query 写 JPQL、原生 SQL，以及 Pageable 分页排序和投影返回。",{"path":7742,"title":7743,"description":7744,"date":7731,"series":7732,"seriesOrder":79},"\u002Fposts\u002Fjavaspring\u002Fspring-notes-04","Spring 学习笔记 04：JPA 关联映射与仓储测试","一对多、多对多怎么映射，谁负责维护外键，FetchType 与 CascadeType 该怎么选，顺带用 @DataJpaTest 与 AssertJ 把仓储层测起来。",{"path":7746,"title":7747,"description":7748,"date":7731,"series":7732,"seriesOrder":87},"\u002Fposts\u002Fjavaspring\u002Fspring-notes-05","Spring 学习笔记 05：Spring MVC 与 Thymeleaf 入门","一次页面请求在 Spring MVC 里走了哪几步，@Controller 怎么把数据交给模板，@PathVariable 与 @RequestParam 怎么取参数，以及控制器为什么必须是无状态的。",{"path":7750,"title":7751,"description":7752,"date":7731,"series":7732,"seriesOrder":98},"\u002Fposts\u002Fjavaspring\u002Fspring-notes-06","Spring 学习笔记 06：表单绑定与服务端校验","把表单字段绑到对象上，用 Bean Validation 注解做服务端校验，把错误信息显示回用户刚填的那一页，再补上自定义校验注解与全局异常处理。",{"path":7754,"title":7755,"description":7756,"date":7731,"series":7732,"seriesOrder":108},"\u002Fposts\u002Fjavaspring\u002Fspring-notes-07","Spring 学习笔记 07：Thymeleaf 片段复用与国际化","用 th:fragment 与 th:insert\u002Fth:replace 把导航栏、页脚抽成片段，页面套用统一布局，再用 messages.properties 与 LocaleResolver 做中英文切换。",{"path":7758,"title":7759,"description":7760,"date":7731,"series":7732,"seriesOrder":115},"\u002Fposts\u002Fjavaspring\u002Fspring-notes-08","Spring 学习笔记 08：HTTP Session 与会话状态管理","HTTP 无状态带来的问题，Cookie 与 Session 怎么分工，Spring 里读写会话的几种方式，以及会话固定、超时、多实例共享这些必须想清楚的事。",{"path":7762,"title":7763,"description":7764,"date":7731,"series":7732,"seriesOrder":528},"\u002Fposts\u002Fjavaspring\u002Fspring-notes-09","Spring 学习笔记 09：拦截器 HandlerInterceptor","登录检查、权限判断、请求耗时统计这类横切逻辑该放哪：HandlerInterceptor 的三个回调各在什么时机执行，怎么注册与排除路径，以及它和过滤器的分工。",{"path":7766,"title":7767,"description":7768,"date":7731,"series":7732,"seriesOrder":538},"\u002Fposts\u002Fjavaspring\u002Fspring-notes-10","Spring 学习笔记 10：Service 层与事务","一组数据库操作要么全成功要么全失败，靠 @Transactional 怎么做到；传播行为、隔离级别、回滚规则怎么选，以及自调用失效、乐观锁悲观锁这些实战里的坑。",{"path":7770,"title":7771,"description":7772,"date":7731,"series":7732,"seriesOrder":544},"\u002Fposts\u002Fjavaspring\u002Fspring-notes-11","Spring 学习笔记 11：REST API 基础——把 Service 暴露成接口","同一套 Service 怎么同时服务页面和客户端：@RestController 的路由与方法语义、状态码怎么给、DTO 怎么定，以及 MockMvc 端到端和切片测试的两种写法与各自的坑。",{"path":7774,"title":7775,"description":7776,"date":7731,"series":7732,"seriesOrder":550},"\u002Fposts\u002Fjavaspring\u002Fspring-notes-12","Spring 学习笔记 12：REST 进阶——版本、更新、跨域与文档化","接口能跑通之后要处理的几件事：URL 与响应结构怎么定、版本怎么演进、更新怎么做到可安全重试、CORS 怎么配、HATEOAS 与 Spring Data REST 该不该用。",{"path":7563,"title":5,"description":7559,"date":7558,"series":7565,"seriesOrder":79},{"path":7779,"title":7780,"description":7781,"date":7558,"series":7565,"seriesOrder":71},"\u002Fposts\u002Fjava\u002Fjava-generics-notes","Java 泛型笔记：泛型方法、有界类型与泛型类","学习 Java 泛型时的个人笔记：从重复的重载方法与多态尝试讲起，再到泛型方法与类型推断、有界泛型类型、Comparable 与 Number 边界，以及泛型类在集合中的用法。示例代码均为自写，运行结果来自本地实测。",{"path":7783,"title":7784,"description":7785,"date":7786,"series":7787,"seriesOrder":79},"\u002Fposts\u002Fmysql\u002Fmysql-learning-04","MySQL 学习笔记 04：数据操纵（增删改）","MySQL 学习笔记第四篇：INSERT 插入（含批量插入与 INSERT ... SELECT）、UPDATE 更新、DELETE 删除，以及各自的坑。","2026-10-10","SQL 学习笔记",{"path":7789,"title":7790,"description":7791,"date":7786,"series":7787,"seriesOrder":87},"\u002Fposts\u002Fmysql\u002Fmysql-learning-05","MySQL 学习笔记 05：DDL 概览","MySQL 学习笔记第五篇：数据定义语言概览——Schema、Table、View、Index、Constraints 这几类对象分别对应哪些 CREATE \u002F ALTER \u002F DROP 命令。",{"path":7793,"title":7794,"description":7795,"date":7786,"series":7787,"seriesOrder":98},"\u002Fposts\u002Fmysql\u002Fmysql-learning-06","MySQL 学习笔记 06：CREATE TABLE 与数据类型","MySQL 学习笔记第六篇：CREATE TABLE 的完整语法、常用数据类型的选型建议，以及外键的两种定义方式。",{"path":7797,"title":7798,"description":7799,"date":7786,"series":7787,"seriesOrder":108},"\u002Fposts\u002Fmysql\u002Fmysql-learning-07","MySQL 学习笔记 07：索引","MySQL 学习笔记第七篇：索引的作用与 B+ 树结构、单列与复合索引的创建、索引的代价与适用场景。",{"path":7801,"title":7802,"description":7803,"date":7786,"series":7787,"seriesOrder":115},"\u002Fposts\u002Fmysql\u002Fmysql-learning-08","MySQL 学习笔记 08：DROP 与 ALTER","MySQL 学习笔记第八篇：DROP 删除数据库对象、ALTER 修改表结构，以及常见的坑。",{"path":7805,"title":7806,"description":7807,"date":7786,"series":7787,"seriesOrder":528},"\u002Fposts\u002Fmysql\u002Fmysql-learning-09","MySQL 学习笔记 09：约束与数据完整性","MySQL 学习笔记第九篇：SQL 约束——NOT NULL、CHECK、PRIMARY KEY、FOREIGN KEY 等如何保证数据完整性，以及它们的对比。",{"path":7809,"title":7810,"description":7811,"date":7786,"series":7787,"seriesOrder":538},"\u002Fposts\u002Fmysql\u002Fmysql-learning-10","MySQL 学习笔记 10：视图","MySQL 学习笔记第十篇：视图（Views）的定义与特性、和表的对比、创建与使用，以及可更新视图的限制。",{"path":7813,"title":7814,"description":7815,"date":7786,"series":7787,"seriesOrder":544},"\u002Fposts\u002Fmysql\u002Fmysql-learning-11","MySQL 学习笔记 11：存储过程","MySQL 学习笔记第十一篇：存储过程（Stored Procedures）的概念、创建与调用、带参数的写法，以及优缺点。",{"path":7817,"title":7818,"description":7819,"date":7820,"series":7787,"seriesOrder":47},"\u002Fposts\u002Fmysql\u002Fmysql-learning-01","MySQL 学习笔记 01：MySQL 基础知识","MySQL 学习笔记第一篇：MySQL Server 与 Workbench 的安装，数据库与 DBMS 的基本概念，关系模型、ACID 事务、常见数据类型，以及后续的学习路线。","2026-10-09",{"path":7822,"title":7823,"description":7824,"date":7820,"series":7787,"seriesOrder":62},"\u002Fposts\u002Fmysql\u002Fmysql-learning-02","MySQL 学习笔记 02：SELECT 查询与过滤","MySQL 学习笔记第二篇：SELECT 的基本语法与逻辑执行顺序，WHERE 过滤（比较、LIKE、IN、BETWEEN、NULL）、各种 JOIN、UNION 与子查询。",{"path":7826,"title":7827,"description":7828,"date":7820,"series":7787,"seriesOrder":71},"\u002Fposts\u002Fmysql\u002Fmysql-learning-03","MySQL 学习笔记 03：聚合、分组与输出控制","MySQL 学习笔记第三篇：COUNT\u002FSUM\u002FAVG 等聚合函数、GROUP BY 分组、HAVING 过滤、ORDER BY 与 LIMIT，以及一条 SELECT 的完整逻辑执行顺序。",{"path":7830,"title":7831,"description":7832,"date":7833,"series":7565,"seriesOrder":62},"\u002Fposts\u002Fjava\u002Fjava-oop-notes","Java 面向对象笔记：类、继承、多态与异常","学习 Java 面向对象编程时的个人笔记：类与对象、封装与访问修饰符、继承与多态、集合、抽象类与接口、异常处理。示例代码均为自写，运行结果来自本地实测。","2026-10-06",{"path":7835,"title":7836,"description":7837,"date":7838,"series":7565,"seriesOrder":47},"\u002Fposts\u002Fjava\u002Fjava-basics-notes","Java 基础语法笔记：从环境安装到方法与数组","学习 Java 基础语法时的个人笔记：JDK 安装、输入输出、变量与运算符、条件与循环、字符串、数组与方法。示例代码均为自写，运行结果来自本地实测。","2026-10-05",{"path":7840,"title":7841,"description":7842,"date":7843,"series":7844,"seriesOrder":47},"\u002Fposts\u002Fgit\u002Fgit-install-and-basics","Git 安装与基本使用","从装好 Git 到跑通日常提交流程，把最常用的几条命令和它们背后的三个区域讲清楚。","2026-10-04","Git 入门",{"path":7846,"title":7847,"description":7848,"date":7843,"series":7844,"seriesOrder":62},"\u002Fposts\u002Fgit\u002Flocal-remote-repository","本地远程仓库的部署","用自己的机器当 Git 服务器：建一个裸仓库、走 SSH 连上去，把本地提交推上去。",{"path":7850,"title":7851,"description":7852,"date":7843,"series":7853,"seriesOrder":47},"\u002Fposts\u002Fguide\u002Fguide-file-layout","文件都放在哪","文章、图片、封面分别放在仓库的什么位置，哪些目录可以放手改、哪些碰了会出问题。","博客使用指南",{"path":7855,"title":7856,"description":7857,"date":7843,"series":7853,"seriesOrder":62},"\u002Fposts\u002Fguide\u002Fguide-frontmatter","frontmatter 每个字段是什么意思","文章开头那段被横线包住的字段，逐个说明作用、默认行为和写错的后果。",{"path":7859,"title":7860,"description":7861,"date":7843,"series":7853,"seriesOrder":71},"\u002Fposts\u002Fguide\u002Fguide-markdown","正文怎么写 —— Markdown 与排版效果","标题、链接、图片、代码、表格分别怎么写，以及本站对它们做了哪些自定义处理。",1793268152067]