-
Notifications
You must be signed in to change notification settings - Fork 0
/
db.json
1 lines (1 loc) · 951 KB
/
db.json
1
{"meta":{"version":1,"warehouse":"4.0.1"},"models":{"Asset":[{"_id":"themes/maupassant/source/css/copycode.scss","path":"css/copycode.scss","modified":0,"renderable":1},{"_id":"themes/maupassant/source/css/copyright.scss","path":"css/copyright.scss","modified":0,"renderable":1},{"_id":"themes/maupassant/source/css/donate.scss","path":"css/donate.scss","modified":0,"renderable":1},{"_id":"themes/maupassant/source/css/search.scss","path":"css/search.scss","modified":0,"renderable":1},{"_id":"themes/maupassant/source/css/style.scss","path":"css/style.scss","modified":0,"renderable":1},{"_id":"themes/maupassant/source/img/alipay.svg","path":"img/alipay.svg","modified":0,"renderable":1},{"_id":"themes/maupassant/source/img/bitcoin.svg","path":"img/bitcoin.svg","modified":0,"renderable":1},{"_id":"themes/maupassant/source/img/like.svg","path":"img/like.svg","modified":0,"renderable":1},{"_id":"themes/maupassant/source/img/wechat.svg","path":"img/wechat.svg","modified":0,"renderable":1},{"_id":"themes/maupassant/source/img/paypal.svg","path":"img/paypal.svg","modified":0,"renderable":1},{"_id":"themes/maupassant/source/img/github.svg","path":"img/github.svg","modified":0,"renderable":1},{"_id":"themes/maupassant/source/js/codeblock-resizer.js","path":"js/codeblock-resizer.js","modified":0,"renderable":1},{"_id":"themes/maupassant/source/js/copycode.js","path":"js/copycode.js","modified":0,"renderable":1},{"_id":"themes/maupassant/source/js/copyright.js","path":"js/copyright.js","modified":0,"renderable":1},{"_id":"themes/maupassant/source/js/donate.js","path":"js/donate.js","modified":0,"renderable":1},{"_id":"themes/maupassant/source/js/love.js","path":"js/love.js","modified":0,"renderable":1},{"_id":"themes/maupassant/source/js/search.js","path":"js/search.js","modified":0,"renderable":1},{"_id":"themes/maupassant/source/js/fancybox.js","path":"js/fancybox.js","modified":0,"renderable":1},{"_id":"themes/maupassant/source/js/share.js","path":"js/share.js","modified":0,"renderable":1},{"_id":"themes/maupassant/source/js/totop.js","path":"js/totop.js","modified":0,"renderable":1},{"_id":"themes/maupassant/source/js/smartresize.js","path":"js/smartresize.js","modified":0,"renderable":1}],"Cache":[{"_id":"source/_posts/JVM面试题Part1.md","hash":"b8ed0f39b192fb8cd50e86bed670b7323a2c3fe3","modified":1650447350862},{"_id":"source/_posts/JVM面试题Part2.md","hash":"8a1e2cd81a1c28ed25ef0fe741fbbfbedbf22c08","modified":1650447350862},{"_id":"source/_posts/Java并发面试题1.md","hash":"c16177fdacd8ea318a6020f95ad2a49d5bbc4583","modified":1650447350862},{"_id":"source/_posts/Java集合面试题.md","hash":"6031892f8a231faca79e55457666262c7689aa9a","modified":1650447350863},{"_id":"source/_posts/Part1.md","hash":"5ca43ff9f266a8ec127d7417d8c0ff2b7d513dd7","modified":1650447350863},{"_id":"source/_posts/Spark面试题Part1.md","hash":"4b9c3870e9eaf602dcb325551fab88fee4d2dcf9","modified":1650447350863},{"_id":"source/_posts/Scala学习Part1.md","hash":"ace61cb79ed9341e5284d35b0a0f9fa42cb85c08","modified":1650447350863},{"_id":"source/_posts/Scala学习Part3.md","hash":"7120d8ad402e8d0337c5cd5ffd0d43546a8e88fd","modified":1650447350863},{"_id":"source/_posts/hexo.md","hash":"0f5d21ce7d7491aa19a4a242d1f15e003355cbd6","modified":1650468110822},{"_id":"source/_posts/oojs.md","hash":"6295cd10dbfbeeb6ba7d6e96e4ef9b6da292eaac","modified":1650447350863},{"_id":"source/_posts/三大经典排序算法.md","hash":"c16bad737c60272514e694d88b4c3e9bc8b38a19","modified":1650447350863},{"_id":"source/_posts/Scala学习2.md","hash":"b2d7000473c5ccc17004bf91058ab2961ab4ba3b","modified":1650447350863},{"_id":"source/_posts/夏花对话录1.md","hash":"cd4ca1c451decc0452b92b234ea1be66f0cc0779","modified":1650447350863},{"_id":"source/_posts/大数加法.md","hash":"82b40dbb618d960ddf454581e04cd6dfac9f5158","modified":1650447350864},{"_id":"source/_posts/感冒123.md","hash":"5f35d77e1c16f1faabe2d5606a6f4effdb0964be","modified":1650447350864},{"_id":"source/_posts/理解Java中的动态代理Part1.md","hash":"90832cba38ba0852912533732cef8a70597a0046","modified":1650447350864},{"_id":"source/_posts/秋枫.md","hash":"2b06004c0656fe741c2a9f9dea868ed3c3d6743d","modified":1650447350864},{"_id":"source/_posts/经典算法之划分.md","hash":"bacdb62baa72db575bc143db72d122d20e4b8235","modified":1650447350864},{"_id":"source/_posts/经典算法之二分查找.md","hash":"3accb86ac5e79d8a35c34d514cbc3e0ca4e47e79","modified":1650447350864},{"_id":"source/_posts/经典算法之快选求TOPK.md","hash":"6e09d223aabb96be3b19cd8bd17f9da4ce16648e","modified":1650447350864},{"_id":"source/_posts/链表环检算法.md","hash":"c046550ef12a59ae8a4729e04bdf4b04d8e97375","modified":1650447350864},{"_id":"source/_posts/经典算法之求众数.md","hash":"de782bae5591e8d05b9725035cf9d9aa87c3dde2","modified":1650447350864},{"_id":"source/_posts/链表相关的算法.md","hash":"54c7cbea8b8d1fb7e8a8d7fe6267737c29904c9f","modified":1650447350865},{"_id":"source/_posts/稻盛的活法.md","hash":"969ffcbb0fe0d9472c51c1c5894e6bf2a9bd965b","modified":1650447350864},{"_id":"source/about/index.md","hash":"5a7f5b1bb963389e416a4e91c01aa8aaaeb3d900","modified":1650447350865},{"_id":"themes/maupassant/.gitignore","hash":"16945417d10c15c950306794dbb0d970b5a199fe","modified":1650463795300},{"_id":"themes/maupassant/.travis.yml","hash":"0339959f29deddc365e8fe8bd85da524410b9a23","modified":1650463795300},{"_id":"themes/maupassant/LICENSE","hash":"f0ac2f92770650c9835183f79010c0d307b34acd","modified":1650463795300},{"_id":"themes/maupassant/README.md","hash":"8cc8e522e7b0b9bf41bfe54f447213737134370b","modified":1650463795301},{"_id":"themes/maupassant/_config.yml","hash":"083521b948b120771d1de1661c8ef0a97efb7367","modified":1650465177829},{"_id":"themes/maupassant/package.json","hash":"f092433469eb87362e831326425a6a5c3c9fea0d","modified":1650463795305},{"_id":"themes/maupassant/languages/de-DE.yml","hash":"5d3556a885e355a8c2da65ef3e7b3ee36a628bfa","modified":1650463795301},{"_id":"themes/maupassant/languages/en.yml","hash":"9c979a2f107536399bbe2be572c2d0bebcdd9d95","modified":1650463795301},{"_id":"themes/maupassant/languages/fr-FR.yml","hash":"b47906ec0abf867fb3e3360bc046b7afb68aee25","modified":1650463795301},{"_id":"themes/maupassant/languages/es-ES.yml","hash":"58e1d04bcd1834fa9d2960e18e027abbbccbedc9","modified":1650463795301},{"_id":"themes/maupassant/languages/ko.yml","hash":"909a33e0befa6978e8e72157c6b415b48551ee31","modified":1650463795301},{"_id":"themes/maupassant/languages/ru.yml","hash":"2476a631f4d3c668de04af85a6c2c97ba2a57e96","modified":1650463795301},{"_id":"themes/maupassant/languages/zh-TW.yml","hash":"e9747f9b3ec1314a3cae44a9a90e7649af739633","modified":1650463795302},{"_id":"themes/maupassant/languages/zh-CN.yml","hash":"78cc1794a3ce3e186c462c1a70f097d0c05cd210","modified":1650463795302},{"_id":"themes/maupassant/layout/archive.pug","hash":"9bf5245929529576b5d6678142276adf3c221a6d","modified":1650463795304},{"_id":"themes/maupassant/layout/base-without-sidebar.pug","hash":"6b1ff15ae71223ef2cae1a56e40d2354cf40ff31","modified":1650463795304},{"_id":"themes/maupassant/layout/index.pug","hash":"65a88ef97932b45b36c862ae94ce6914a5ce58f8","modified":1650463795304},{"_id":"themes/maupassant/layout/base.pug","hash":"ebfbb48e5f4b6810d5ea0b9e1bb252196ff698e8","modified":1650463795304},{"_id":"themes/maupassant/layout/page.pug","hash":"9b72086ff877de064f804a59684140af09470484","modified":1650463795304},{"_id":"themes/maupassant/layout/single-column.pug","hash":"0593f261dc208bb0b5c4232eb41eff597a291bd9","modified":1650463795305},{"_id":"themes/maupassant/layout/post.pug","hash":"d6672907918d43863ba88fbcbd4c9f3644270c99","modified":1650463795305},{"_id":"themes/maupassant/layout/tagcloud.pug","hash":"bbb51f5a863873f24f40ac3499f15a48bfa6112b","modified":1650463795305},{"_id":"themes/maupassant/layout/timeline.pug","hash":"cef82a79f57e4e491f2934d990da939c4bebceb6","modified":1650463795305},{"_id":"themes/maupassant/layout/_partial/after_footer.pug","hash":"3e84c147d741634d5f4d463359f271e8919bdc91","modified":1650463795302},{"_id":"themes/maupassant/layout/_partial/comments.pug","hash":"67e61cf3a8442cd89d3ae406346fced3cc8ccbde","modified":1650463795302},{"_id":"themes/maupassant/layout/_partial/darkmode.pug","hash":"82567449d68025cc7fee5259d4769f5ee015aa26","modified":1650463795302},{"_id":"themes/maupassant/layout/_partial/footer.pug","hash":"650781b5bc8c632658ad6880ba663b1e3bfb5798","modified":1650463795302},{"_id":"themes/maupassant/layout/_partial/head.pug","hash":"95ad97023ceb466c61f72d25e8ae301c4802a0fd","modified":1650463795302},{"_id":"themes/maupassant/layout/_partial/helpers.pug","hash":"acdf9e2d52ee86c831fa15ce1570930c5779bc78","modified":1650463795302},{"_id":"themes/maupassant/layout/_partial/mathjax.pug","hash":"b54b56faff9e47ab3ca3cdd55056c73e60776f3c","modified":1650463795302},{"_id":"themes/maupassant/layout/_partial/paginator.pug","hash":"53f9cb77448e84a98da5eb688e2e12b173c555bb","modified":1650463795303},{"_id":"themes/maupassant/layout/_partial/mathjax2.pug","hash":"d6ac5dc4e9c7a1b866f1f92d88988cfb35aded4c","modified":1650463795303},{"_id":"themes/maupassant/layout/_partial/post_nav.pug","hash":"a2d698c84bb6da08195fe870dbd7215f65388d3f","modified":1650463795303},{"_id":"themes/maupassant/layout/_partial/tag.pug","hash":"1629fcab71affa9bac8112f4a46fc68e1567b505","modified":1650463795303},{"_id":"themes/maupassant/layout/_partial/totop.pug","hash":"8225bbc3cdb9648bc2e6872e5c616a9a1e4def4f","modified":1650463795303},{"_id":"themes/maupassant/layout/_partial/wordcount.pug","hash":"7dde69ef8f86745b83ba5f03c75717a782752f2b","modified":1650463795303},{"_id":"themes/maupassant/layout/_widget/category.pug","hash":"f2e9f6ff02b858b507f61768753b54846491f87a","modified":1650463795303},{"_id":"themes/maupassant/layout/_widget/copyright.pug","hash":"17e68ea3e87f128819d16ec30cd506a51fe80a7f","modified":1650463795303},{"_id":"themes/maupassant/layout/_widget/donate.pug","hash":"859eddafd2762072bc5af850038ff377578b0ce4","modified":1650463795303},{"_id":"themes/maupassant/layout/_widget/links.pug","hash":"3f6048423887f359bb97d17621e961495d209a7c","modified":1650463795304},{"_id":"themes/maupassant/layout/_widget/recent_comments.pug","hash":"3b364ea62f2851acf1fae7713a9f51c36311931a","modified":1650463795304},{"_id":"themes/maupassant/layout/_widget/tag.pug","hash":"9b73975ac67b471ae91803b8477932d2c5e5a4f2","modified":1650463795304},{"_id":"themes/maupassant/layout/_widget/recent_posts.pug","hash":"5a86fcd97933c665b5afef701d8b30cfd2952691","modified":1650463795304},{"_id":"themes/maupassant/layout/_widget/search.pug","hash":"a141293ce93b312f4db9f28207d02ee578ede359","modified":1650463795304},{"_id":"themes/maupassant/source/css/copycode.scss","hash":"e2463b8dacf629e180a1b6cd80667ca8044292eb","modified":1650463795305},{"_id":"themes/maupassant/source/css/copyright.scss","hash":"a418da11a88d1feb14500df42ed97a64da6a7611","modified":1650463795305},{"_id":"themes/maupassant/source/css/search.scss","hash":"9406e138d7bb6a9ef4a067eba1dafa627519c8a7","modified":1650463795305},{"_id":"themes/maupassant/source/css/donate.scss","hash":"95b2fd65042afecc0b5530847c369bcc11d74bd0","modified":1650463795305},{"_id":"themes/maupassant/source/img/alipay.svg","hash":"3d94d2f9b09e352802628c9225578e1086f5fef3","modified":1650463795306},{"_id":"themes/maupassant/source/img/like.svg","hash":"e6e4bd1af076be9358316cac89efdc22ef4a5064","modified":1650463795307},{"_id":"themes/maupassant/source/css/style.scss","hash":"5305f4c8e728169dcc1b485acdc37d226f297852","modified":1650463795306},{"_id":"themes/maupassant/source/img/bitcoin.svg","hash":"590b6b6462896168d08b30dfe2de5f08950d5553","modified":1650463795306},{"_id":"themes/maupassant/source/img/wechat.svg","hash":"19c1f68ec8c0b8e9f7855e7a6e78077f70a1aedc","modified":1650463795307},{"_id":"themes/maupassant/source/img/paypal.svg","hash":"09786c983a10bc570dcd05b87cec601e9d01eb00","modified":1650463795307},{"_id":"themes/maupassant/source/img/github.svg","hash":"277798d16cb6106e45ef74f6b9972011fa43401b","modified":1650463795306},{"_id":"themes/maupassant/source/js/codeblock-resizer.js","hash":"5d0b786d60bf225d9eabcc9cece2719ff4d9b6cd","modified":1650463795307},{"_id":"themes/maupassant/source/js/copyright.js","hash":"7b1bd775ea22abf33d57f78628f436bf656e439a","modified":1650463795307},{"_id":"themes/maupassant/source/js/love.js","hash":"5cf89f622bf891cf1986845eb92eadef6f358eb7","modified":1650463795308},{"_id":"themes/maupassant/source/js/donate.js","hash":"bdddd8d9847462d020f7a511e7e12c046223195d","modified":1650463795307},{"_id":"themes/maupassant/source/js/search.js","hash":"6fdfd143646d12b8dbef9b5809cea768192f08aa","modified":1650463795308},{"_id":"themes/maupassant/source/js/fancybox.js","hash":"13c4781570339f4fba76a3d7f202e442817dd605","modified":1650463795307},{"_id":"themes/maupassant/source/js/totop.js","hash":"7dbf8fcf582a4fb6eb9b2c60d6de9f9c2091ec4c","modified":1650463795308},{"_id":"themes/maupassant/source/js/copycode.js","hash":"fde1f153bab1f00ae8930668094c00aa9ff3c7a3","modified":1650463795307},{"_id":"themes/maupassant/source/js/smartresize.js","hash":"3ef157fd877167e3290f42c67a624ea375a46c24","modified":1650463795308},{"_id":"themes/maupassant/source/js/share.js","hash":"a2f9de374523dc7f2ddb90ed5f24b668c20d9272","modified":1650463795308},{"_id":"public/about/index.html","hash":"ba991c31ea08f1435095e0e0fd3a1fd25e919f07","modified":1650691334629},{"_id":"public/2022/04/20/hexo/index.html","hash":"15ea7a02e1ef4faabe8045f7d38ddfc73bad1cc4","modified":1650691334629},{"_id":"public/2021/09/14/夏花对话录1/index.html","hash":"485dfc0bef7c68ba77142b26ab094d806c2c42bd","modified":1650691334629},{"_id":"public/2021/09/22/感冒123/index.html","hash":"32e871e0add7a32e4636d520d3d636393919d71c","modified":1650691334629},{"_id":"public/2021/09/20/稻盛的活法/index.html","hash":"b046aeb6138bc53a2dc407e79595674b306c5595","modified":1650691334629},{"_id":"public/2021/09/08/Spark面试题Part1/index.html","hash":"9f0eb5224a0706e6118f457dc6297784dd0dea99","modified":1650691334629},{"_id":"public/2021/09/07/Scala学习Part1/index.html","hash":"e7c8230f835967effd0e37758cd43126cf5b4dae","modified":1650691334629},{"_id":"public/2021/09/07/Scala学习2/index.html","hash":"afda51d0100edf293d3a3a81b544faa490d856d0","modified":1650691334629},{"_id":"public/2021/09/09/Scala学习Part3/index.html","hash":"58c989ec4f76173b3b06ae7e3a3fbc682d3eaf81","modified":1650691334629},{"_id":"public/2021/07/26/Part1/index.html","hash":"572f7cd141194bdd0fcffee98adc055e629213a8","modified":1650691334629},{"_id":"public/2021/09/04/秋枫/index.html","hash":"936bcfd3dec0b22160f5bc8b8e4bb9855b1235b8","modified":1650691334629},{"_id":"public/2021/07/25/理解Java中的动态代理Part1/index.html","hash":"1e8d85e649428556db0406b83fc3fcf387812aff","modified":1650691334629},{"_id":"public/2021/07/23/链表相关的算法/index.html","hash":"0502bb246c60e2b8c2677e813033fb82130d8240","modified":1650691334629},{"_id":"public/2021/07/21/经典算法之划分/index.html","hash":"f1fc8a45f1f39337b8ed172398e28eebe69d5542","modified":1650691334629},{"_id":"public/2021/07/21/经典算法之求众数/index.html","hash":"8047a6fdb230bee0e1dafdef0601afbabb23ab08","modified":1650691334629},{"_id":"public/2021/07/20/链表环检算法/index.html","hash":"5f22ac5eceabb80588a9e024998ec0e8187ce88a","modified":1650691334629},{"_id":"public/2021/07/20/经典算法之快选求TOPK/index.html","hash":"7fd62a846e6708b11ab6f0c03dbf123b9fb5b1bf","modified":1650691334629},{"_id":"public/2021/07/19/大数加法/index.html","hash":"f8012e2b2680ed4d48f4dc6338690807d48cd7cb","modified":1650691334629},{"_id":"public/2021/07/17/经典算法之二分查找/index.html","hash":"a8f5c31b6be974499368dee15a344ab78edaaa4e","modified":1650691334629},{"_id":"public/2021/07/12/Java并发面试题1/index.html","hash":"977523e43440a257dc8a2f7ad8f5bbf385d4aa60","modified":1650691334629},{"_id":"public/2021/07/16/三大经典排序算法/index.html","hash":"024deb5623f5b4adc24ac554ec600cede8117e8a","modified":1650691334629},{"_id":"public/2021/07/11/JVM面试题Part2/index.html","hash":"a95d9c7324fad9bd9fa375f253ee75ed81985a3e","modified":1650691334629},{"_id":"public/2021/07/10/JVM面试题Part1/index.html","hash":"c01e89497937cef60f19f9bca1bf013a80a39017","modified":1650691334629},{"_id":"public/2021/07/05/oojs/index.html","hash":"eb607fffc02d3f09cf26a665948121779e6495ef","modified":1650691334629},{"_id":"public/archives/index.html","hash":"015bc6441f67823ebdfcbf297c9cb4fde7e3f6aa","modified":1650691334629},{"_id":"public/archives/page/2/index.html","hash":"9c2cbb3e9dbef382869926ef219cd936977b2d27","modified":1650691334629},{"_id":"public/archives/page/3/index.html","hash":"9d7b0f84efc7847794ea400475feac7fe8c7f95d","modified":1650691334629},{"_id":"public/2021/07/11/Java集合面试题/index.html","hash":"eae814acc3689115a4a36a720eb34ebe1d21295d","modified":1650691334629},{"_id":"public/archives/2021/index.html","hash":"596a80990a99749761ed60b9d495e64059e16765","modified":1650691334629},{"_id":"public/archives/2021/page/2/index.html","hash":"d7e6acecd65dfacbfda4afeadabbd3c61f6404ea","modified":1650691334629},{"_id":"public/archives/2021/page/3/index.html","hash":"2812e99e1d7e55f6c06f70f161f4fccdf3f80721","modified":1650691334629},{"_id":"public/archives/2021/07/index.html","hash":"cb3b4a29995b2f8d8d182e116462b50f0e8223e5","modified":1650691334629},{"_id":"public/archives/2021/07/page/2/index.html","hash":"64b4423e44d6780f494a124b4e032dc4647640cc","modified":1650691334629},{"_id":"public/archives/2021/09/index.html","hash":"c9e5d98f3f4aecd9cc3a8b61c8703af629481bac","modified":1650691334629},{"_id":"public/archives/2022/index.html","hash":"ba011148b20ed115985180379f32847aec320f8e","modified":1650691334629},{"_id":"public/archives/2022/04/index.html","hash":"ba011148b20ed115985180379f32847aec320f8e","modified":1650691334629},{"_id":"public/tags/JAVA/index.html","hash":"65c24e25c014ea307a19ad8a8a66c2c19a13da76","modified":1650691334629},{"_id":"public/tags/JAVA/page/2/index.html","hash":"57005f9e8c7db7942ff8adc2d943c8326045d799","modified":1650691334629},{"_id":"public/tags/面试题/index.html","hash":"37c8347cd5b9d834a12927b85e2e71502abfc70f","modified":1650691334629},{"_id":"public/tags/Hbase/index.html","hash":"580e16540782041f3a41f159f124318c3b69a813","modified":1650691334629},{"_id":"public/tags/大数据/index.html","hash":"07c4bf7b0769290c82f2d0a90f4763d8d340b2a7","modified":1650691334629},{"_id":"public/tags/Spark/index.html","hash":"3dfd2f0972e19a89b22a66c0a2d16f3ea267ecd1","modified":1650691334629},{"_id":"public/tags/Scala/index.html","hash":"8b30c148c62d5a098c9681b79ba6ca0416330924","modified":1650691334629},{"_id":"public/tags/博客/index.html","hash":"39e9ba57eaeaa0df7f8ad1b71fe23aab25fbf45c","modified":1650691334629},{"_id":"public/tags/JS/index.html","hash":"25d5930f2b409f3cdc270a52383bd4f8b8b8ee97","modified":1650691334629},{"_id":"public/tags/算法/index.html","hash":"141b690e7bad7e4f4b8bddf7be19b3ae8151c478","modified":1650691334629},{"_id":"public/tags/旅行/index.html","hash":"9eef6266641cfb5600dc483517443b157ae1db80","modified":1650691334629},{"_id":"public/tags/语录/index.html","hash":"a505d23780311e6c6dd6bc92c6ee75b43bd6f792","modified":1650691334629},{"_id":"public/tags/生活/index.html","hash":"1bd9d4ecc0900267364cdbc86e90797efc3f238c","modified":1650691334629},{"_id":"public/index.html","hash":"2063aee94bed30f9e588355aff72858a144c0f16","modified":1650691714308},{"_id":"public/page/2/index.html","hash":"1fbf4cfdd7a96d33584ea7580b084a6f7365d707","modified":1650691334629},{"_id":"public/page/3/index.html","hash":"23bb0716824d22259d4dca41a3bcbaa896d33cfd","modified":1650691334629},{"_id":"public/categories/大数据/index.html","hash":"6464d361cb5e04eeea91a63a883d0b491cbdf522","modified":1650691334629},{"_id":"public/categories/学习/index.html","hash":"beb79b2f168e3adb77f644a746cbe0034058e8b1","modified":1650691334629},{"_id":"public/categories/生活/index.html","hash":"9b5df701df8edc71e54057bc0906e2d922bdd949","modified":1650691334629},{"_id":"public/categories/面试题/index.html","hash":"f8069075f1be5910f06104f84a020993c8d7d337","modified":1650691334629},{"_id":"public/categories/算法/index.html","hash":"141b690e7bad7e4f4b8bddf7be19b3ae8151c478","modified":1650691334629},{"_id":"public/categories/JS/index.html","hash":"25d5930f2b409f3cdc270a52383bd4f8b8b8ee97","modified":1650691334629},{"_id":"public/categories/基础/index.html","hash":"49742c47e5afa96be5e577843586c2d38b330af8","modified":1650691334629},{"_id":"public/img/alipay.svg","hash":"3d94d2f9b09e352802628c9225578e1086f5fef3","modified":1650468123512},{"_id":"public/img/bitcoin.svg","hash":"590b6b6462896168d08b30dfe2de5f08950d5553","modified":1650468123512},{"_id":"public/img/like.svg","hash":"e6e4bd1af076be9358316cac89efdc22ef4a5064","modified":1650468123512},{"_id":"public/img/github.svg","hash":"277798d16cb6106e45ef74f6b9972011fa43401b","modified":1650468123512},{"_id":"public/img/paypal.svg","hash":"09786c983a10bc570dcd05b87cec601e9d01eb00","modified":1650468123512},{"_id":"public/img/wechat.svg","hash":"19c1f68ec8c0b8e9f7855e7a6e78077f70a1aedc","modified":1650468123512},{"_id":"public/css/copyright.css","hash":"a6f68c735d459babe3f4f5c4412a635d8debaed5","modified":1650468123512},{"_id":"public/css/copycode.css","hash":"6abe2cb370b88a9884f5a10e29624b2f8ef24c89","modified":1650468123512},{"_id":"public/css/donate.css","hash":"f5126ddcc9dab3490c01e0ee26dd3ed46bac9988","modified":1650468123512},{"_id":"public/css/search.css","hash":"22fba8b3e985236a417e82486f07542562e06ee7","modified":1650468123512},{"_id":"public/js/copycode.js","hash":"fde1f153bab1f00ae8930668094c00aa9ff3c7a3","modified":1650468123512},{"_id":"public/js/codeblock-resizer.js","hash":"5d0b786d60bf225d9eabcc9cece2719ff4d9b6cd","modified":1650468123512},{"_id":"public/js/donate.js","hash":"bdddd8d9847462d020f7a511e7e12c046223195d","modified":1650468123512},{"_id":"public/js/love.js","hash":"5cf89f622bf891cf1986845eb92eadef6f358eb7","modified":1650468123512},{"_id":"public/js/copyright.js","hash":"7b1bd775ea22abf33d57f78628f436bf656e439a","modified":1650468123512},{"_id":"public/js/smartresize.js","hash":"3ef157fd877167e3290f42c67a624ea375a46c24","modified":1650468123512},{"_id":"public/js/totop.js","hash":"7dbf8fcf582a4fb6eb9b2c60d6de9f9c2091ec4c","modified":1650468123512},{"_id":"public/js/search.js","hash":"6fdfd143646d12b8dbef9b5809cea768192f08aa","modified":1650468123512},{"_id":"public/js/share.js","hash":"a2f9de374523dc7f2ddb90ed5f24b668c20d9272","modified":1650468123512},{"_id":"public/js/fancybox.js","hash":"13c4781570339f4fba76a3d7f202e442817dd605","modified":1650468123512},{"_id":"public/css/style.css","hash":"4735627811dd6de51dd1a3c7da9e62102d98c355","modified":1650468123512},{"_id":"source/_posts/hive-metastore-on-minio.md","hash":"a762bea0d431c0c8b131119a2b088be60bffb3af","modified":1650691710426},{"_id":"source/_posts/spark-minio.md","hash":"f4fb8daa9b1c444c5182d2d7d9d40b873b2723fc","modified":1650691710430},{"_id":"public/2022/04/23/hive-metastore-on-minio/index.html","hash":"252ee94fef6fc1cc5810d865f62ec589c96c53da","modified":1650691714308},{"_id":"public/2022/04/23/spark-minio/index.html","hash":"2e4f2cf1378dbe100258341d45cbd36e67dfbb41","modified":1650691714308},{"_id":"public/categories/Hive/index.html","hash":"1e81dffa4bb3078d5d1b191b3e5b95d8e2e9a3f7","modified":1650691334629},{"_id":"public/categories/Spark/index.html","hash":"3094bf3906273d54475fc5763f3c1608c013a3ad","modified":1650691334629},{"_id":"public/tags/Hive/index.html","hash":"1e81dffa4bb3078d5d1b191b3e5b95d8e2e9a3f7","modified":1650691334629}],"Category":[{"name":"面试题","_id":"cl27q3uuw0003n94oagqk7zob"},{"name":"大数据","_id":"cl27q3uv5000ln94of9qs8r19"},{"name":"学习","_id":"cl27q3uv7000rn94o92ih2tge"},{"name":"生活","_id":"cl27q3uva0015n94ogfc67m5q"},{"name":"JS","_id":"cl27q3uvc001bn94o9bpw8ro1"},{"name":"算法","_id":"cl27q3uve001jn94o3o3q778e"},{"name":"基础","_id":"cl27q3uvj002an94oars8g1td"},{"name":"Hive","_id":"cl2bf01vx0002r74oc8uu0tcv"},{"name":"Spark","_id":"cl2bf01w40006r74of8x15gwb"}],"Data":[],"Page":[{"title":"about","date":"2021-07-05T14:01:26.000Z","_content":"毕业于河南农业大学,信息管理与信息系统专业,外企码农一枚","source":"about/index.md","raw":"---\ntitle: about\ndate: 2021-07-05 22:01:26\n---\n毕业于河南农业大学,信息管理与信息系统专业,外企码农一枚","updated":"2022-04-20T09:35:50.865Z","path":"about/index.html","comments":1,"layout":"page","_id":"cl27q3uup0000n94oguu64bwz","content":"<p>毕业于河南农业大学,信息管理与信息系统专业,外企码农一枚</p>\n","site":{"data":{}},"excerpt":"","more":"<p>毕业于河南农业大学,信息管理与信息系统专业,外企码农一枚</p>\n"}],"Post":[{"title":"JVM面试题Part2","date":"2021-07-10T16:00:00.000Z","comments":1,"toc":true,"_content":"### 垃圾收集器\n> 谈谈你对G1的理解?\nG1的目标是替换掉CMS。和CMS有很多共同点。\n使用G1收集器,JAVA堆被划分成多个大小相等的独立的Region.\n保留了新生代和老年代,它们都是一部分Region的集合。\n\nG1收集器号称可以建立可预测的停顿时间模型,因为它可以有计划地避免在整个java堆中进行全区域\n的垃圾收集,G1跟踪各个Region里面的垃圾堆积的价大小(回收所得的空间与所需时间的经验值),\n在后台维护一个优先列表,每次根据允许的收集时间,优先收集价值大的Region,这也是Garbage First\n的来由。\n使用Region划分内存以及优先级的区域回收方式,保证了它可以在有限的时间内可以获取高的收集效率。\n> G1是如何避免全堆扫描的? 就是某Region中的对象,在其他Region中可能会有引用的情况下。\n它使用Remembered Set来避免全堆扫描。G1的每一个Region都有一个与之对应的Remember Set,\nJVM发现程序在对Reference类型的数据进行写操作时,会产生一个Write Barrier 暂时中断写操作,\n检查Reference引用的对象是否在不同的Region之中,如果是,则通过CardTable把相关引用信息记录到\n被引用对象的所属对象的Remember Set之中,当进行内存回收时,GC的根点节的枚举范围加入Remembered set,\n可保证不对全堆扫描也不会有遗漏。\n\nG1收集器的运作大致可以划分为以下几个步骤:\n1. 初始标记\n2. 并发标记\n3. 最终标记\n4. 筛选回收\n不同于CMS之处是,它的筛选回收是stop the world的,但其实是可以和用户线程一起工作的,但是为了更好地\n控制收集时间,而且停顿用户线程可极大地提高收集效率。\n\nJava8在Server模式下模认的垃圾回收器是什么?kafka,spark他们运行的默认收集器是什么配置?\n> java -XX:+PrintCommandLineFlags -version \n 查看输出,你可以发现-XX:+UseParallelGC,在深入理解Java虚拟机中有描述,这代表着新生代使用的是Parallel\n scavenge加老年代使用的Serial Old的收集器组合进行内存回收。\n 这也是Server模式下默认的配置。\n\n### JVM类加载机制\n1. 如果一个类中有一行代码:\n ```java``` public static int value=123;\n 则这个类被加载后,它的value属性是多少?\n 在加载验证准备阶段过后,它的值是0,而不是123,因为这个时候未执行任何java方法,而把value赋值为123的putstatic指令\n 是在程序被编译后,放到类的构造类<clinit>()方法之中,所以这个动作只有在初始化阶段才会执行。\n 但是被final,constant修饰的值因为在编译阶段就会被替换成常量,与这种情况不同。\n 类加载中的解析是指JVM将常量池内的符号引用替换为直接引用的过程。\n 常量池中有常量(字面量)和符号引用,符号引用常见的有:类和接口的全限定名,字段的名称和描述符以及方法的名称和描述符。\n Java代码编译之后没有像C++的链接过程,它是在JVM加载class文件时候进行动态链接的,也就是说,在class文件中不会保存\n 各个方法字段的最终内存布局信息,因此需要在类创建时翻译到具体的内存地址之中。\n\n2. 讲一下你理解的类初始化情况?\n 初始化阶段就是执行类构造器<clinit>()方法的过程。\n * <clinit>()方法是由编译器自动收集类中所有变量的赋值动作和静态语句块中的语句合并而成的,编译器收集的顺序是由语句在\n 源文件中出现的顺序决定的。\n * <clinit>()方法与类的实例构造器<init>()方法不同,不会显式地调用父类构造器,但是JVM会保证在调用子类的<clinit>()方法\n 执行之前,父类的<clinit>()方法已经执行完毕。所以父类中定义的静态语句块优先于子类的变量赋值操作。\n > 注意一点,JVM会保证多线程在进行类初始化的时候,会加锁保证只有一个线程可以操作,所以如果在<clinit>()方法中有耗时很长\n 的操作,可能就会造成多个线程阻塞,这种情形比较隐蔽。\n 类什么时候会被初始化?\n * new 一个类的时候\n * 访问一个类的静态变量或静态方法的时候\n * 反射触发类的方法获取值的时候\n * 作为Java的启动类启动的时候\n\n3. JVM中有哪些类加载器?\n 对JVM来说,只有两种类加载器,一种是类启动加载器(bootstrap classloader),由c++实现,是JVM自身的一部分。另一种就是其他类的\n 加载器,由java语言实现,继承于ClassLoader类。\n 启动类加载器加载java_home/lib下面的类。\n 其他类可以分为扩展类加载器和应用程序类加载器。\n 扩展类加载器加载java_home/ext下面的类。\n 应用程序加载器加载classpath下面的类,如果应用程序中没有自定义的类加载器,这个就是默认的类加载器。\n\n4. 讲一下你理解的双亲委派模型?\n 双亲委派模型要求除了顶层的启动类加载器之外,其余的类加载器都要有自己的父类加载器。不一定使用继承,可以使用组合方式。\n 它的工作过程是:如果一个类加载器收到了加载类的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给自己的父类\n 加载器去完成,每一个层次的类加载器都是如此,一直传到顶层的启动类加载器,如果当父类加载器反馈自己无法完成这个加载请求(\n 在自己的搜索范围内没有找到所需的类)时,子类加载器才会加载。\n 它有助于Java程序的稳定,Java类会因它的类加载器而存在一种优先级的关系。Object类就在顶层,不会被Application类加载器加载。\n 不会扰乱整个类型体系。实现双亲委派模型的代码是在ClassLoader类的loadClass()中实现的。\n\n5. 线程上下文类加载器对双亲委派模型有什么影响?\n 它在一定程序上破坏了双亲派模型,但对于JNDI,JDBC等情况下,又需要它。\n 它可以通过Thread类的setContextClassLoader()方法来进行设置,如果线程创建时还没未设置,它将会从父线程中继承,如果全局范围内\n 没有设置过,那就是ApplicationClassLoader.\n SPI(SERVICE PROVIDER INTERFACE)\n\n6. Class.forName和ClassLoader.loadClass()有什么区别?\n Class.forName加载类后对类进行了初始化操作,而后者装载类之后没有进行链接。\n 如果程序依赖类的初始化,则需要使用Class.forName。","source":"_posts/JVM面试题Part2.md","raw":"---\ntitle: JVM面试题Part2\ndate: 2021-07-11\ncomments: true #是否可评论\ntoc: true #是否显示文章目录\ncategories: \"面试题\" #分类\ntags: #标签\n - JAVA\n---\n### 垃圾收集器\n> 谈谈你对G1的理解?\nG1的目标是替换掉CMS。和CMS有很多共同点。\n使用G1收集器,JAVA堆被划分成多个大小相等的独立的Region.\n保留了新生代和老年代,它们都是一部分Region的集合。\n\nG1收集器号称可以建立可预测的停顿时间模型,因为它可以有计划地避免在整个java堆中进行全区域\n的垃圾收集,G1跟踪各个Region里面的垃圾堆积的价大小(回收所得的空间与所需时间的经验值),\n在后台维护一个优先列表,每次根据允许的收集时间,优先收集价值大的Region,这也是Garbage First\n的来由。\n使用Region划分内存以及优先级的区域回收方式,保证了它可以在有限的时间内可以获取高的收集效率。\n> G1是如何避免全堆扫描的? 就是某Region中的对象,在其他Region中可能会有引用的情况下。\n它使用Remembered Set来避免全堆扫描。G1的每一个Region都有一个与之对应的Remember Set,\nJVM发现程序在对Reference类型的数据进行写操作时,会产生一个Write Barrier 暂时中断写操作,\n检查Reference引用的对象是否在不同的Region之中,如果是,则通过CardTable把相关引用信息记录到\n被引用对象的所属对象的Remember Set之中,当进行内存回收时,GC的根点节的枚举范围加入Remembered set,\n可保证不对全堆扫描也不会有遗漏。\n\nG1收集器的运作大致可以划分为以下几个步骤:\n1. 初始标记\n2. 并发标记\n3. 最终标记\n4. 筛选回收\n不同于CMS之处是,它的筛选回收是stop the world的,但其实是可以和用户线程一起工作的,但是为了更好地\n控制收集时间,而且停顿用户线程可极大地提高收集效率。\n\nJava8在Server模式下模认的垃圾回收器是什么?kafka,spark他们运行的默认收集器是什么配置?\n> java -XX:+PrintCommandLineFlags -version \n 查看输出,你可以发现-XX:+UseParallelGC,在深入理解Java虚拟机中有描述,这代表着新生代使用的是Parallel\n scavenge加老年代使用的Serial Old的收集器组合进行内存回收。\n 这也是Server模式下默认的配置。\n\n### JVM类加载机制\n1. 如果一个类中有一行代码:\n ```java``` public static int value=123;\n 则这个类被加载后,它的value属性是多少?\n 在加载验证准备阶段过后,它的值是0,而不是123,因为这个时候未执行任何java方法,而把value赋值为123的putstatic指令\n 是在程序被编译后,放到类的构造类<clinit>()方法之中,所以这个动作只有在初始化阶段才会执行。\n 但是被final,constant修饰的值因为在编译阶段就会被替换成常量,与这种情况不同。\n 类加载中的解析是指JVM将常量池内的符号引用替换为直接引用的过程。\n 常量池中有常量(字面量)和符号引用,符号引用常见的有:类和接口的全限定名,字段的名称和描述符以及方法的名称和描述符。\n Java代码编译之后没有像C++的链接过程,它是在JVM加载class文件时候进行动态链接的,也就是说,在class文件中不会保存\n 各个方法字段的最终内存布局信息,因此需要在类创建时翻译到具体的内存地址之中。\n\n2. 讲一下你理解的类初始化情况?\n 初始化阶段就是执行类构造器<clinit>()方法的过程。\n * <clinit>()方法是由编译器自动收集类中所有变量的赋值动作和静态语句块中的语句合并而成的,编译器收集的顺序是由语句在\n 源文件中出现的顺序决定的。\n * <clinit>()方法与类的实例构造器<init>()方法不同,不会显式地调用父类构造器,但是JVM会保证在调用子类的<clinit>()方法\n 执行之前,父类的<clinit>()方法已经执行完毕。所以父类中定义的静态语句块优先于子类的变量赋值操作。\n > 注意一点,JVM会保证多线程在进行类初始化的时候,会加锁保证只有一个线程可以操作,所以如果在<clinit>()方法中有耗时很长\n 的操作,可能就会造成多个线程阻塞,这种情形比较隐蔽。\n 类什么时候会被初始化?\n * new 一个类的时候\n * 访问一个类的静态变量或静态方法的时候\n * 反射触发类的方法获取值的时候\n * 作为Java的启动类启动的时候\n\n3. JVM中有哪些类加载器?\n 对JVM来说,只有两种类加载器,一种是类启动加载器(bootstrap classloader),由c++实现,是JVM自身的一部分。另一种就是其他类的\n 加载器,由java语言实现,继承于ClassLoader类。\n 启动类加载器加载java_home/lib下面的类。\n 其他类可以分为扩展类加载器和应用程序类加载器。\n 扩展类加载器加载java_home/ext下面的类。\n 应用程序加载器加载classpath下面的类,如果应用程序中没有自定义的类加载器,这个就是默认的类加载器。\n\n4. 讲一下你理解的双亲委派模型?\n 双亲委派模型要求除了顶层的启动类加载器之外,其余的类加载器都要有自己的父类加载器。不一定使用继承,可以使用组合方式。\n 它的工作过程是:如果一个类加载器收到了加载类的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给自己的父类\n 加载器去完成,每一个层次的类加载器都是如此,一直传到顶层的启动类加载器,如果当父类加载器反馈自己无法完成这个加载请求(\n 在自己的搜索范围内没有找到所需的类)时,子类加载器才会加载。\n 它有助于Java程序的稳定,Java类会因它的类加载器而存在一种优先级的关系。Object类就在顶层,不会被Application类加载器加载。\n 不会扰乱整个类型体系。实现双亲委派模型的代码是在ClassLoader类的loadClass()中实现的。\n\n5. 线程上下文类加载器对双亲委派模型有什么影响?\n 它在一定程序上破坏了双亲派模型,但对于JNDI,JDBC等情况下,又需要它。\n 它可以通过Thread类的setContextClassLoader()方法来进行设置,如果线程创建时还没未设置,它将会从父线程中继承,如果全局范围内\n 没有设置过,那就是ApplicationClassLoader.\n SPI(SERVICE PROVIDER INTERFACE)\n\n6. Class.forName和ClassLoader.loadClass()有什么区别?\n Class.forName加载类后对类进行了初始化操作,而后者装载类之后没有进行链接。\n 如果程序依赖类的初始化,则需要使用Class.forName。","slug":"JVM面试题Part2","published":1,"updated":"2022-04-20T09:35:50.862Z","layout":"post","photos":[],"link":"","_id":"cl27q3uur0001n94obrda5ot9","content":"<h3 id=\"垃圾收集器\"><a href=\"#垃圾收集器\" class=\"headerlink\" title=\"垃圾收集器\"></a>垃圾收集器</h3><blockquote>\n<p>谈谈你对G1的理解?<br>G1的目标是替换掉CMS。和CMS有很多共同点。<br>使用G1收集器,JAVA堆被划分成多个大小相等的独立的Region.<br>保留了新生代和老年代,它们都是一部分Region的集合。</p>\n</blockquote>\n<p>G1收集器号称可以建立可预测的停顿时间模型,因为它可以有计划地避免在整个java堆中进行全区域<br>的垃圾收集,G1跟踪各个Region里面的垃圾堆积的价大小(回收所得的空间与所需时间的经验值),<br>在后台维护一个优先列表,每次根据允许的收集时间,优先收集价值大的Region,这也是Garbage First<br>的来由。<br>使用Region划分内存以及优先级的区域回收方式,保证了它可以在有限的时间内可以获取高的收集效率。</p>\n<blockquote>\n<p>G1是如何避免全堆扫描的? 就是某Region中的对象,在其他Region中可能会有引用的情况下。<br>它使用Remembered Set来避免全堆扫描。G1的每一个Region都有一个与之对应的Remember Set,<br>JVM发现程序在对Reference类型的数据进行写操作时,会产生一个Write Barrier 暂时中断写操作,<br>检查Reference引用的对象是否在不同的Region之中,如果是,则通过CardTable把相关引用信息记录到<br>被引用对象的所属对象的Remember Set之中,当进行内存回收时,GC的根点节的枚举范围加入Remembered set,<br>可保证不对全堆扫描也不会有遗漏。</p>\n</blockquote>\n<p>G1收集器的运作大致可以划分为以下几个步骤:</p>\n<ol>\n<li>初始标记</li>\n<li>并发标记</li>\n<li>最终标记</li>\n<li>筛选回收<br>不同于CMS之处是,它的筛选回收是stop the world的,但其实是可以和用户线程一起工作的,但是为了更好地<br>控制收集时间,而且停顿用户线程可极大地提高收集效率。</li>\n</ol>\n<p>Java8在Server模式下模认的垃圾回收器是什么?kafka,spark他们运行的默认收集器是什么配置?</p>\n<blockquote>\n<p> java -XX:+PrintCommandLineFlags -version<br> 查看输出,你可以发现-XX:+UseParallelGC,在深入理解Java虚拟机中有描述,这代表着新生代使用的是Parallel<br> scavenge加老年代使用的Serial Old的收集器组合进行内存回收。<br> 这也是Server模式下默认的配置。</p>\n</blockquote>\n<h3 id=\"JVM类加载机制\"><a href=\"#JVM类加载机制\" class=\"headerlink\" title=\"JVM类加载机制\"></a>JVM类加载机制</h3><ol>\n<li><p>如果一个类中有一行代码:<br><code>java</code> public static int value=123;<br>则这个类被加载后,它的value属性是多少?<br>在加载验证准备阶段过后,它的值是0,而不是123,因为这个时候未执行任何java方法,而把value赋值为123的putstatic指令<br>是在程序被编译后,放到类的构造类<clinit>()方法之中,所以这个动作只有在初始化阶段才会执行。<br>但是被final,constant修饰的值因为在编译阶段就会被替换成常量,与这种情况不同。<br>类加载中的解析是指JVM将常量池内的符号引用替换为直接引用的过程。<br>常量池中有常量(字面量)和符号引用,符号引用常见的有:类和接口的全限定名,字段的名称和描述符以及方法的名称和描述符。<br>Java代码编译之后没有像C++的链接过程,它是在JVM加载class文件时候进行动态链接的,也就是说,在class文件中不会保存<br>各个方法字段的最终内存布局信息,因此需要在类创建时翻译到具体的内存地址之中。</p>\n</li>\n<li><p>讲一下你理解的类初始化情况?<br>初始化阶段就是执行类构造器<clinit>()方法的过程。</p>\n<ul>\n<li><clinit>()方法是由编译器自动收集类中所有变量的赋值动作和静态语句块中的语句合并而成的,编译器收集的顺序是由语句在<br>源文件中出现的顺序决定的。</li>\n<li><clinit>()方法与类的实例构造器<init>()方法不同,不会显式地调用父类构造器,但是JVM会保证在调用子类的<clinit>()方法<br>执行之前,父类的<clinit>()方法已经执行完毕。所以父类中定义的静态语句块优先于子类的变量赋值操作。<blockquote>\n<p>注意一点,JVM会保证多线程在进行类初始化的时候,会加锁保证只有一个线程可以操作,所以如果在<clinit>()方法中有耗时很长<br>的操作,可能就会造成多个线程阻塞,这种情形比较隐蔽。<br>类什么时候会被初始化?</p>\n</blockquote>\n</li>\n<li>new 一个类的时候</li>\n<li>访问一个类的静态变量或静态方法的时候</li>\n<li>反射触发类的方法获取值的时候</li>\n<li>作为Java的启动类启动的时候</li>\n</ul>\n</li>\n<li><p>JVM中有哪些类加载器?<br> 对JVM来说,只有两种类加载器,一种是类启动加载器(bootstrap classloader),由c++实现,是JVM自身的一部分。另一种就是其他类的<br> 加载器,由java语言实现,继承于ClassLoader类。<br> 启动类加载器加载java_home/lib下面的类。<br> 其他类可以分为扩展类加载器和应用程序类加载器。<br> 扩展类加载器加载java_home/ext下面的类。<br> 应用程序加载器加载classpath下面的类,如果应用程序中没有自定义的类加载器,这个就是默认的类加载器。</p>\n</li>\n<li><p>讲一下你理解的双亲委派模型?<br> 双亲委派模型要求除了顶层的启动类加载器之外,其余的类加载器都要有自己的父类加载器。不一定使用继承,可以使用组合方式。<br> 它的工作过程是:如果一个类加载器收到了加载类的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给自己的父类<br> 加载器去完成,每一个层次的类加载器都是如此,一直传到顶层的启动类加载器,如果当父类加载器反馈自己无法完成这个加载请求(<br> 在自己的搜索范围内没有找到所需的类)时,子类加载器才会加载。<br> 它有助于Java程序的稳定,Java类会因它的类加载器而存在一种优先级的关系。Object类就在顶层,不会被Application类加载器加载。<br> 不会扰乱整个类型体系。实现双亲委派模型的代码是在ClassLoader类的loadClass()中实现的。</p>\n</li>\n<li><p>线程上下文类加载器对双亲委派模型有什么影响?<br>它在一定程序上破坏了双亲派模型,但对于JNDI,JDBC等情况下,又需要它。<br>它可以通过Thread类的setContextClassLoader()方法来进行设置,如果线程创建时还没未设置,它将会从父线程中继承,如果全局范围内<br>没有设置过,那就是ApplicationClassLoader.<br>SPI(SERVICE PROVIDER INTERFACE)</p>\n</li>\n<li><p>Class.forName和ClassLoader.loadClass()有什么区别?<br>Class.forName加载类后对类进行了初始化操作,而后者装载类之后没有进行链接。<br>如果程序依赖类的初始化,则需要使用Class.forName。</p>\n</li>\n</ol>\n","site":{"data":{}},"excerpt":"","more":"<h3 id=\"垃圾收集器\"><a href=\"#垃圾收集器\" class=\"headerlink\" title=\"垃圾收集器\"></a>垃圾收集器</h3><blockquote>\n<p>谈谈你对G1的理解?<br>G1的目标是替换掉CMS。和CMS有很多共同点。<br>使用G1收集器,JAVA堆被划分成多个大小相等的独立的Region.<br>保留了新生代和老年代,它们都是一部分Region的集合。</p>\n</blockquote>\n<p>G1收集器号称可以建立可预测的停顿时间模型,因为它可以有计划地避免在整个java堆中进行全区域<br>的垃圾收集,G1跟踪各个Region里面的垃圾堆积的价大小(回收所得的空间与所需时间的经验值),<br>在后台维护一个优先列表,每次根据允许的收集时间,优先收集价值大的Region,这也是Garbage First<br>的来由。<br>使用Region划分内存以及优先级的区域回收方式,保证了它可以在有限的时间内可以获取高的收集效率。</p>\n<blockquote>\n<p>G1是如何避免全堆扫描的? 就是某Region中的对象,在其他Region中可能会有引用的情况下。<br>它使用Remembered Set来避免全堆扫描。G1的每一个Region都有一个与之对应的Remember Set,<br>JVM发现程序在对Reference类型的数据进行写操作时,会产生一个Write Barrier 暂时中断写操作,<br>检查Reference引用的对象是否在不同的Region之中,如果是,则通过CardTable把相关引用信息记录到<br>被引用对象的所属对象的Remember Set之中,当进行内存回收时,GC的根点节的枚举范围加入Remembered set,<br>可保证不对全堆扫描也不会有遗漏。</p>\n</blockquote>\n<p>G1收集器的运作大致可以划分为以下几个步骤:</p>\n<ol>\n<li>初始标记</li>\n<li>并发标记</li>\n<li>最终标记</li>\n<li>筛选回收<br>不同于CMS之处是,它的筛选回收是stop the world的,但其实是可以和用户线程一起工作的,但是为了更好地<br>控制收集时间,而且停顿用户线程可极大地提高收集效率。</li>\n</ol>\n<p>Java8在Server模式下模认的垃圾回收器是什么?kafka,spark他们运行的默认收集器是什么配置?</p>\n<blockquote>\n<p> java -XX:+PrintCommandLineFlags -version<br> 查看输出,你可以发现-XX:+UseParallelGC,在深入理解Java虚拟机中有描述,这代表着新生代使用的是Parallel<br> scavenge加老年代使用的Serial Old的收集器组合进行内存回收。<br> 这也是Server模式下默认的配置。</p>\n</blockquote>\n<h3 id=\"JVM类加载机制\"><a href=\"#JVM类加载机制\" class=\"headerlink\" title=\"JVM类加载机制\"></a>JVM类加载机制</h3><ol>\n<li><p>如果一个类中有一行代码:<br><code>java</code> public static int value=123;<br>则这个类被加载后,它的value属性是多少?<br>在加载验证准备阶段过后,它的值是0,而不是123,因为这个时候未执行任何java方法,而把value赋值为123的putstatic指令<br>是在程序被编译后,放到类的构造类<clinit>()方法之中,所以这个动作只有在初始化阶段才会执行。<br>但是被final,constant修饰的值因为在编译阶段就会被替换成常量,与这种情况不同。<br>类加载中的解析是指JVM将常量池内的符号引用替换为直接引用的过程。<br>常量池中有常量(字面量)和符号引用,符号引用常见的有:类和接口的全限定名,字段的名称和描述符以及方法的名称和描述符。<br>Java代码编译之后没有像C++的链接过程,它是在JVM加载class文件时候进行动态链接的,也就是说,在class文件中不会保存<br>各个方法字段的最终内存布局信息,因此需要在类创建时翻译到具体的内存地址之中。</p>\n</li>\n<li><p>讲一下你理解的类初始化情况?<br>初始化阶段就是执行类构造器<clinit>()方法的过程。</p>\n<ul>\n<li><clinit>()方法是由编译器自动收集类中所有变量的赋值动作和静态语句块中的语句合并而成的,编译器收集的顺序是由语句在<br>源文件中出现的顺序决定的。</li>\n<li><clinit>()方法与类的实例构造器<init>()方法不同,不会显式地调用父类构造器,但是JVM会保证在调用子类的<clinit>()方法<br>执行之前,父类的<clinit>()方法已经执行完毕。所以父类中定义的静态语句块优先于子类的变量赋值操作。<blockquote>\n<p>注意一点,JVM会保证多线程在进行类初始化的时候,会加锁保证只有一个线程可以操作,所以如果在<clinit>()方法中有耗时很长<br>的操作,可能就会造成多个线程阻塞,这种情形比较隐蔽。<br>类什么时候会被初始化?</p>\n</blockquote>\n</li>\n<li>new 一个类的时候</li>\n<li>访问一个类的静态变量或静态方法的时候</li>\n<li>反射触发类的方法获取值的时候</li>\n<li>作为Java的启动类启动的时候</li>\n</ul>\n</li>\n<li><p>JVM中有哪些类加载器?<br> 对JVM来说,只有两种类加载器,一种是类启动加载器(bootstrap classloader),由c++实现,是JVM自身的一部分。另一种就是其他类的<br> 加载器,由java语言实现,继承于ClassLoader类。<br> 启动类加载器加载java_home/lib下面的类。<br> 其他类可以分为扩展类加载器和应用程序类加载器。<br> 扩展类加载器加载java_home/ext下面的类。<br> 应用程序加载器加载classpath下面的类,如果应用程序中没有自定义的类加载器,这个就是默认的类加载器。</p>\n</li>\n<li><p>讲一下你理解的双亲委派模型?<br> 双亲委派模型要求除了顶层的启动类加载器之外,其余的类加载器都要有自己的父类加载器。不一定使用继承,可以使用组合方式。<br> 它的工作过程是:如果一个类加载器收到了加载类的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给自己的父类<br> 加载器去完成,每一个层次的类加载器都是如此,一直传到顶层的启动类加载器,如果当父类加载器反馈自己无法完成这个加载请求(<br> 在自己的搜索范围内没有找到所需的类)时,子类加载器才会加载。<br> 它有助于Java程序的稳定,Java类会因它的类加载器而存在一种优先级的关系。Object类就在顶层,不会被Application类加载器加载。<br> 不会扰乱整个类型体系。实现双亲委派模型的代码是在ClassLoader类的loadClass()中实现的。</p>\n</li>\n<li><p>线程上下文类加载器对双亲委派模型有什么影响?<br>它在一定程序上破坏了双亲派模型,但对于JNDI,JDBC等情况下,又需要它。<br>它可以通过Thread类的setContextClassLoader()方法来进行设置,如果线程创建时还没未设置,它将会从父线程中继承,如果全局范围内<br>没有设置过,那就是ApplicationClassLoader.<br>SPI(SERVICE PROVIDER INTERFACE)</p>\n</li>\n<li><p>Class.forName和ClassLoader.loadClass()有什么区别?<br>Class.forName加载类后对类进行了初始化操作,而后者装载类之后没有进行链接。<br>如果程序依赖类的初始化,则需要使用Class.forName。</p>\n</li>\n</ol>\n"},{"title":"JVM面试题Part1","date":"2021-07-10T13:11:48.000Z","toc":true,"_content":"### 1. JVM内存描述?\n程序计数器,VM栈,本地栈和堆,metaspace(方法区)。\n\n**程序计数器**,> 是一块较小的内存空间,当前线程执行的字节码的行号指示器。\n字节码解释器工作时就是能过改变这个计数器的值来获取下一条要执行的字节码指令,\n分支,循环,跳转和异常的处理,线程恢复等基础功能都要依赖这个计数器完成。\n\n它是JVM规范中规定的唯一没有内存溢出情况的区域。\n\n**VM栈** 虚拟机栈描述的是JAVA方法执行的内存模型,每一个方法在执行的同时都会\n创建一个栈用于存储局部变量表,操作数栈,动态链接,方法出口等信息。\n它有两种溢出的可能:\n1. 栈溢出:线程请求的的栈深度大于虚拟机所允许的深度。例如递归循环终止条件不正确的设计。\n2. 内存溢出:虚拟机栈可以动态扩展,如果扩展时无法申请到足够的内存,就会抛出oom异常。\n\n**本地栈** 为执行本地方法服务,也会有上述两种溢出情况。\n\n**方法区(metaspace)**: 类的信息,常量池,静态变量,JIT编译后的代码数据。\n它有一个别名叫NonHeap.如果内存不够,也会抛出OOM异常\n\n**堆**: 用于存放对象实例,所有的对象实例和数组都要在堆上分配,但由于逃逸分析技术等的发展,\n现在也不绝对。\n\n垃圾收集器管理的主要区域就是堆。为了更快更好地收集,现在多是分代处理机制。\n有新生代和老年代,新生代又分为(eden空间,from survivor, to surfvivor)。\n新生代和老年代采用不同的垃圾收集算法。\n如果没有足够的内存来分配实例 ,并且也无法扩展时,会抛出OOM异常。\n\n**直接内存** java从1.4开始引入了NIO,使用channel,buffer的IO方式,可以使用native的函数直接\n分配堆外内存,然后通过存储在java堆中的directbytebuffer对象作为这块内存的引用进行操作。\n在一些场景中可以提高性能,因为避免了在java堆和native堆中来回复制这些数据。\n这块区域如果内存不够也会抛出OOMError的异常。\n\n### 2. 对象的创建 \n> 请描述下java中对象创建的过程?\n使用new关键字加上对象名,可以创建对象实例。\n对于new指令,首先检查指令参数(类名)是否在常量池中,如果可以在常量池中定位一个符号引用,\n检查这个符号引用代表的类是否已经加载解析和初始化过,如果没有,则需要加载校验初始化这个类。\n然后就是分配内存,对象所需要的内存大小在类加载完成后就可以完全确定。\n\n> jvm如何知道堆中哪些块是free的呢?\n使用serial,parnew等带有compact算法的收集器时,系统采用的分配方式是指针碰撞,而使用\nCMS这种标记清除算法的收集器时,通常采用空闲列表(记录哪些块是空闲的)。\n内存分配的这个操作也不是线程安全的,如何实现的呢?\njvm使用CAS配上失败重试的方式保证操作的原子性。\n另一种内存分配动作按照线程划分在不同的空间之中进行,即每一个线程在Java堆中分配一小块内存,称为\n本地线程缓冲(TLAB).只有TLAB用完了,才需要同步锁定。虚拟机是否使用TLAB,可以使用参数\n-XX:+/-UseTLAB来设定。\n内存分配完成后,JVM会把分配的空间都初始化成零值(不包含对象头)。\n然后对对象进行必要的设置,如这个对象是哪个类的实例,对象的hashcode,gc分类信息,锁信息,这些配置\n都在对象头中。\n然后会调用类中的init方法。(静态方法优先执行)\n\n对象在内存中存储的布局有三个区域:对象头,实例数据和对象填充。\n对象头还有一个类型指针,即对象指向它的类元数据的指针,标明这个对象是哪个类的实例。\n由于hotspot的JVM自动内存管理系统要求对象起始地址必须是8字节的整数倍,所以当\n对象实例数据部分没有对齐时,就需要通过对齐填充来补全。\n对象的访问定位,我们需要使用栈上的reference来定位堆上的实例数据。\n对象访问方式取决于JVM的实现,目前主流的访问方式有句柄和直接指针两种。\n直接指针的方式栈中存储的就是对象的地址,直接快速,但是GC操作后如果移动了对象就需要更新这个地址。\nHotspot JVM是使用这种方式来访问对象的。\n\n### 3. 垃圾回收算法\n> 如何判断哪些对象该被回收?\n采用可达性分析的方式,从GC Roots出发,向下搜索,所走过的路径称为引用链,当一个对象到GC Roots之间\n没有引用链时,就确定该对象是不可用的。\n\nJava中,可作为`GC Roots`的对象有下面几种:\n* 虚拟机栈中引用的对象\n* 方法区(metaspace)中类静态属性引用的对象\n* 方法区中常量引用的对象\n* 本地方法栈中JNI引用的对象\n\n> **垃圾收集算法**\n1. 标记清除算法 缺点:效率低,会产生空间碎片。\n2. 复制算法 :追求高效,IBM研究表明,很多新生对象存活的时间很短,所以现在新生代中Eden和Survivor(from,to)\n分的比例是8:1,所以每次新生代的空间使用率是90%,只有10%是浪费的。当Survivor空间不够用的,需要依赖老年代的内存。\n3. 标记整理算法 : 老年代中的对象存活的时间长,不能采用复制算法。标记整理算法的标记和标记清除一样,然后让存活的对象\n向都向一端移动,然后直接清理掉端边界以外的内存。\n4. 分代算法\n为了提高效率,根据对象生存周期划分成新生代和老年代,针对不同特点采用不同的算法。\n新生代使用复制算法,老年代使用标记整理或标记清除算法。\n> GC为什么需要stop the world?\n可达性分析执行时,如果不stw,则无法保证扫描结果的正确性。\n> 当进入GC时,线程会怎么办?\n在方法调用,循环跳转,异常跳转等地方设置安全点。\n在代码中不会发生引用变化的地方,称为安全区域。\n在安全点设置中断标记,如果为真,则线程主动中断挂起。\n在线程离开安全区域的时候,检查gc roots是否检查完成,如果没有,则等待。\n\n### 4. 垃圾收集器\n1. **Serial收集器**\n> 单线程完成收集,要求STW。仍然是client模式下新生代的默认配置。\n2. **ParNew收集器**\nParNew收集器是Serial收集器的多线程版本。它也是现在Server模式下首先的新生代收集器。\n是因为老年代现在多是选用CMS收集器,而只有ParNew/Serial才可以和它搭配使用。\n\n3. **Parallel Scavenge收集器** \n目标是达到一个可控制的吞吐量(即用户线程运行时间/(GC时间+用户线程运行时间);\nCMS目标是达到较短的停顿时间,适合需要与用户交互的程序,给用户良好的体验。\n而高吞吐量可以高效地使用cpu的时间,尽快完成程序的运算任务,主要适合在后台运算而不需要\n太多交互的任务。\n-XX:MaxGCPauseMills:最大GC停顿时间\n-XX:GCTimeRatio:GC时间占比\n\n4. **Serial old收集器**\n 单线程收集器,标记整理算法,主要意义也是给Client模式下的JVM使用。\n它可以作为CMS收集器的后备预案。\n\n5. **Parallel old收集器**\n是parallel scavenge收集器的老年代版本,使用多线程和标记整理算法。\n\n6. **CMS收集器** \n目标是获取最短回收停顿时间。\n运作过程分为四个步骤:\n> 初始标记->并发标记->重新标记->并发清除\n**初始标记**仅是标记和GC Roots 能直接关联到的对象。\n**重新标记**:为了修正并发标记期间因为用户程序继续运作而导致标记产生变动的那一部分。\n由于整个过程中耗时最长的并发标记和并发清除过程收集器和用户线程都可以一起工作,\n总体来说它停顿时间比较短。\n它的缺点:\n1. 对CPU资源比较敏感 大于4个的情况下,使用25%的CPU资源。\n2. CMS收集器无法处理浮动垃圾,可能会出现Concurrent mode failure 失败而导致一次\nFullGC的产生。\njdk1.6开始,CMS收集器的启动的阈值是92%, 要是CMS运行期间预留的内存无法满足程序需要,\n就会出现concurrent mode failure,这时就会使用serial old来进行老年代的垃圾收集。\n3. 空间碎片过多时,就会给大对象的分配带来困难,往往出现老年代还有很大的空余,但是没有足够大\n的连续的空间来分配给当前对象,不得不提前触发一次FULL GC的产生。\n\n","source":"_posts/JVM面试题Part1.md","raw":"---\ntitle: JVM面试题Part1\ndate: 2021-07-10 21:11:48\ntoc: true #是否显示文章目录\ncategories: \"面试题\" #分类\ntags: #标签\n\t- JAVA\n---\n### 1. JVM内存描述?\n程序计数器,VM栈,本地栈和堆,metaspace(方法区)。\n\n**程序计数器**,> 是一块较小的内存空间,当前线程执行的字节码的行号指示器。\n字节码解释器工作时就是能过改变这个计数器的值来获取下一条要执行的字节码指令,\n分支,循环,跳转和异常的处理,线程恢复等基础功能都要依赖这个计数器完成。\n\n它是JVM规范中规定的唯一没有内存溢出情况的区域。\n\n**VM栈** 虚拟机栈描述的是JAVA方法执行的内存模型,每一个方法在执行的同时都会\n创建一个栈用于存储局部变量表,操作数栈,动态链接,方法出口等信息。\n它有两种溢出的可能:\n1. 栈溢出:线程请求的的栈深度大于虚拟机所允许的深度。例如递归循环终止条件不正确的设计。\n2. 内存溢出:虚拟机栈可以动态扩展,如果扩展时无法申请到足够的内存,就会抛出oom异常。\n\n**本地栈** 为执行本地方法服务,也会有上述两种溢出情况。\n\n**方法区(metaspace)**: 类的信息,常量池,静态变量,JIT编译后的代码数据。\n它有一个别名叫NonHeap.如果内存不够,也会抛出OOM异常\n\n**堆**: 用于存放对象实例,所有的对象实例和数组都要在堆上分配,但由于逃逸分析技术等的发展,\n现在也不绝对。\n\n垃圾收集器管理的主要区域就是堆。为了更快更好地收集,现在多是分代处理机制。\n有新生代和老年代,新生代又分为(eden空间,from survivor, to surfvivor)。\n新生代和老年代采用不同的垃圾收集算法。\n如果没有足够的内存来分配实例 ,并且也无法扩展时,会抛出OOM异常。\n\n**直接内存** java从1.4开始引入了NIO,使用channel,buffer的IO方式,可以使用native的函数直接\n分配堆外内存,然后通过存储在java堆中的directbytebuffer对象作为这块内存的引用进行操作。\n在一些场景中可以提高性能,因为避免了在java堆和native堆中来回复制这些数据。\n这块区域如果内存不够也会抛出OOMError的异常。\n\n### 2. 对象的创建 \n> 请描述下java中对象创建的过程?\n使用new关键字加上对象名,可以创建对象实例。\n对于new指令,首先检查指令参数(类名)是否在常量池中,如果可以在常量池中定位一个符号引用,\n检查这个符号引用代表的类是否已经加载解析和初始化过,如果没有,则需要加载校验初始化这个类。\n然后就是分配内存,对象所需要的内存大小在类加载完成后就可以完全确定。\n\n> jvm如何知道堆中哪些块是free的呢?\n使用serial,parnew等带有compact算法的收集器时,系统采用的分配方式是指针碰撞,而使用\nCMS这种标记清除算法的收集器时,通常采用空闲列表(记录哪些块是空闲的)。\n内存分配的这个操作也不是线程安全的,如何实现的呢?\njvm使用CAS配上失败重试的方式保证操作的原子性。\n另一种内存分配动作按照线程划分在不同的空间之中进行,即每一个线程在Java堆中分配一小块内存,称为\n本地线程缓冲(TLAB).只有TLAB用完了,才需要同步锁定。虚拟机是否使用TLAB,可以使用参数\n-XX:+/-UseTLAB来设定。\n内存分配完成后,JVM会把分配的空间都初始化成零值(不包含对象头)。\n然后对对象进行必要的设置,如这个对象是哪个类的实例,对象的hashcode,gc分类信息,锁信息,这些配置\n都在对象头中。\n然后会调用类中的init方法。(静态方法优先执行)\n\n对象在内存中存储的布局有三个区域:对象头,实例数据和对象填充。\n对象头还有一个类型指针,即对象指向它的类元数据的指针,标明这个对象是哪个类的实例。\n由于hotspot的JVM自动内存管理系统要求对象起始地址必须是8字节的整数倍,所以当\n对象实例数据部分没有对齐时,就需要通过对齐填充来补全。\n对象的访问定位,我们需要使用栈上的reference来定位堆上的实例数据。\n对象访问方式取决于JVM的实现,目前主流的访问方式有句柄和直接指针两种。\n直接指针的方式栈中存储的就是对象的地址,直接快速,但是GC操作后如果移动了对象就需要更新这个地址。\nHotspot JVM是使用这种方式来访问对象的。\n\n### 3. 垃圾回收算法\n> 如何判断哪些对象该被回收?\n采用可达性分析的方式,从GC Roots出发,向下搜索,所走过的路径称为引用链,当一个对象到GC Roots之间\n没有引用链时,就确定该对象是不可用的。\n\nJava中,可作为`GC Roots`的对象有下面几种:\n* 虚拟机栈中引用的对象\n* 方法区(metaspace)中类静态属性引用的对象\n* 方法区中常量引用的对象\n* 本地方法栈中JNI引用的对象\n\n> **垃圾收集算法**\n1. 标记清除算法 缺点:效率低,会产生空间碎片。\n2. 复制算法 :追求高效,IBM研究表明,很多新生对象存活的时间很短,所以现在新生代中Eden和Survivor(from,to)\n分的比例是8:1,所以每次新生代的空间使用率是90%,只有10%是浪费的。当Survivor空间不够用的,需要依赖老年代的内存。\n3. 标记整理算法 : 老年代中的对象存活的时间长,不能采用复制算法。标记整理算法的标记和标记清除一样,然后让存活的对象\n向都向一端移动,然后直接清理掉端边界以外的内存。\n4. 分代算法\n为了提高效率,根据对象生存周期划分成新生代和老年代,针对不同特点采用不同的算法。\n新生代使用复制算法,老年代使用标记整理或标记清除算法。\n> GC为什么需要stop the world?\n可达性分析执行时,如果不stw,则无法保证扫描结果的正确性。\n> 当进入GC时,线程会怎么办?\n在方法调用,循环跳转,异常跳转等地方设置安全点。\n在代码中不会发生引用变化的地方,称为安全区域。\n在安全点设置中断标记,如果为真,则线程主动中断挂起。\n在线程离开安全区域的时候,检查gc roots是否检查完成,如果没有,则等待。\n\n### 4. 垃圾收集器\n1. **Serial收集器**\n> 单线程完成收集,要求STW。仍然是client模式下新生代的默认配置。\n2. **ParNew收集器**\nParNew收集器是Serial收集器的多线程版本。它也是现在Server模式下首先的新生代收集器。\n是因为老年代现在多是选用CMS收集器,而只有ParNew/Serial才可以和它搭配使用。\n\n3. **Parallel Scavenge收集器** \n目标是达到一个可控制的吞吐量(即用户线程运行时间/(GC时间+用户线程运行时间);\nCMS目标是达到较短的停顿时间,适合需要与用户交互的程序,给用户良好的体验。\n而高吞吐量可以高效地使用cpu的时间,尽快完成程序的运算任务,主要适合在后台运算而不需要\n太多交互的任务。\n-XX:MaxGCPauseMills:最大GC停顿时间\n-XX:GCTimeRatio:GC时间占比\n\n4. **Serial old收集器**\n 单线程收集器,标记整理算法,主要意义也是给Client模式下的JVM使用。\n它可以作为CMS收集器的后备预案。\n\n5. **Parallel old收集器**\n是parallel scavenge收集器的老年代版本,使用多线程和标记整理算法。\n\n6. **CMS收集器** \n目标是获取最短回收停顿时间。\n运作过程分为四个步骤:\n> 初始标记->并发标记->重新标记->并发清除\n**初始标记**仅是标记和GC Roots 能直接关联到的对象。\n**重新标记**:为了修正并发标记期间因为用户程序继续运作而导致标记产生变动的那一部分。\n由于整个过程中耗时最长的并发标记和并发清除过程收集器和用户线程都可以一起工作,\n总体来说它停顿时间比较短。\n它的缺点:\n1. 对CPU资源比较敏感 大于4个的情况下,使用25%的CPU资源。\n2. CMS收集器无法处理浮动垃圾,可能会出现Concurrent mode failure 失败而导致一次\nFullGC的产生。\njdk1.6开始,CMS收集器的启动的阈值是92%, 要是CMS运行期间预留的内存无法满足程序需要,\n就会出现concurrent mode failure,这时就会使用serial old来进行老年代的垃圾收集。\n3. 空间碎片过多时,就会给大对象的分配带来困难,往往出现老年代还有很大的空余,但是没有足够大\n的连续的空间来分配给当前对象,不得不提前触发一次FULL GC的产生。\n\n","slug":"JVM面试题Part1","published":1,"updated":"2022-04-20T09:35:50.862Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cl27q3uuu0002n94o8acn1so7","content":"<h3 id=\"1-JVM内存描述?\"><a href=\"#1-JVM内存描述?\" class=\"headerlink\" title=\"1. JVM内存描述?\"></a>1. JVM内存描述?</h3><p>程序计数器,VM栈,本地栈和堆,metaspace(方法区)。</p>\n<p><strong>程序计数器</strong>,> 是一块较小的内存空间,当前线程执行的字节码的行号指示器。<br>字节码解释器工作时就是能过改变这个计数器的值来获取下一条要执行的字节码指令,<br>分支,循环,跳转和异常的处理,线程恢复等基础功能都要依赖这个计数器完成。</p>\n<p>它是JVM规范中规定的唯一没有内存溢出情况的区域。</p>\n<p><strong>VM栈</strong> 虚拟机栈描述的是JAVA方法执行的内存模型,每一个方法在执行的同时都会<br>创建一个栈用于存储局部变量表,操作数栈,动态链接,方法出口等信息。<br>它有两种溢出的可能:</p>\n<ol>\n<li>栈溢出:线程请求的的栈深度大于虚拟机所允许的深度。例如递归循环终止条件不正确的设计。</li>\n<li>内存溢出:虚拟机栈可以动态扩展,如果扩展时无法申请到足够的内存,就会抛出oom异常。</li>\n</ol>\n<p><strong>本地栈</strong> 为执行本地方法服务,也会有上述两种溢出情况。</p>\n<p><strong>方法区(metaspace)</strong>: 类的信息,常量池,静态变量,JIT编译后的代码数据。<br>它有一个别名叫NonHeap.如果内存不够,也会抛出OOM异常</p>\n<p><strong>堆</strong>: 用于存放对象实例,所有的对象实例和数组都要在堆上分配,但由于逃逸分析技术等的发展,<br>现在也不绝对。</p>\n<p>垃圾收集器管理的主要区域就是堆。为了更快更好地收集,现在多是分代处理机制。<br>有新生代和老年代,新生代又分为(eden空间,from survivor, to surfvivor)。<br>新生代和老年代采用不同的垃圾收集算法。<br>如果没有足够的内存来分配实例 ,并且也无法扩展时,会抛出OOM异常。</p>\n<p><strong>直接内存</strong> java从1.4开始引入了NIO,使用channel,buffer的IO方式,可以使用native的函数直接<br>分配堆外内存,然后通过存储在java堆中的directbytebuffer对象作为这块内存的引用进行操作。<br>在一些场景中可以提高性能,因为避免了在java堆和native堆中来回复制这些数据。<br>这块区域如果内存不够也会抛出OOMError的异常。</p>\n<h3 id=\"2-对象的创建\"><a href=\"#2-对象的创建\" class=\"headerlink\" title=\"2. 对象的创建\"></a>2. 对象的创建</h3><blockquote>\n<p>请描述下java中对象创建的过程?<br>使用new关键字加上对象名,可以创建对象实例。<br>对于new指令,首先检查指令参数(类名)是否在常量池中,如果可以在常量池中定位一个符号引用,<br>检查这个符号引用代表的类是否已经加载解析和初始化过,如果没有,则需要加载校验初始化这个类。<br>然后就是分配内存,对象所需要的内存大小在类加载完成后就可以完全确定。</p>\n</blockquote>\n<blockquote>\n<p>jvm如何知道堆中哪些块是free的呢?<br>使用serial,parnew等带有compact算法的收集器时,系统采用的分配方式是指针碰撞,而使用<br>CMS这种标记清除算法的收集器时,通常采用空闲列表(记录哪些块是空闲的)。<br>内存分配的这个操作也不是线程安全的,如何实现的呢?<br>jvm使用CAS配上失败重试的方式保证操作的原子性。<br>另一种内存分配动作按照线程划分在不同的空间之中进行,即每一个线程在Java堆中分配一小块内存,称为<br>本地线程缓冲(TLAB).只有TLAB用完了,才需要同步锁定。虚拟机是否使用TLAB,可以使用参数<br>-XX:+/-UseTLAB来设定。<br>内存分配完成后,JVM会把分配的空间都初始化成零值(不包含对象头)。<br>然后对对象进行必要的设置,如这个对象是哪个类的实例,对象的hashcode,gc分类信息,锁信息,这些配置<br>都在对象头中。<br>然后会调用类中的init方法。(静态方法优先执行)</p>\n</blockquote>\n<p>对象在内存中存储的布局有三个区域:对象头,实例数据和对象填充。<br>对象头还有一个类型指针,即对象指向它的类元数据的指针,标明这个对象是哪个类的实例。<br>由于hotspot的JVM自动内存管理系统要求对象起始地址必须是8字节的整数倍,所以当<br>对象实例数据部分没有对齐时,就需要通过对齐填充来补全。<br>对象的访问定位,我们需要使用栈上的reference来定位堆上的实例数据。<br>对象访问方式取决于JVM的实现,目前主流的访问方式有句柄和直接指针两种。<br>直接指针的方式栈中存储的就是对象的地址,直接快速,但是GC操作后如果移动了对象就需要更新这个地址。<br>Hotspot JVM是使用这种方式来访问对象的。</p>\n<h3 id=\"3-垃圾回收算法\"><a href=\"#3-垃圾回收算法\" class=\"headerlink\" title=\"3. 垃圾回收算法\"></a>3. 垃圾回收算法</h3><blockquote>\n<p>如何判断哪些对象该被回收?<br>采用可达性分析的方式,从GC Roots出发,向下搜索,所走过的路径称为引用链,当一个对象到GC Roots之间<br>没有引用链时,就确定该对象是不可用的。</p>\n</blockquote>\n<p>Java中,可作为<code>GC Roots</code>的对象有下面几种:</p>\n<ul>\n<li>虚拟机栈中引用的对象</li>\n<li>方法区(metaspace)中类静态属性引用的对象</li>\n<li>方法区中常量引用的对象</li>\n<li>本地方法栈中JNI引用的对象</li>\n</ul>\n<blockquote>\n<p><strong>垃圾收集算法</strong></p>\n</blockquote>\n<ol>\n<li>标记清除算法 缺点:效率低,会产生空间碎片。</li>\n<li>复制算法 :追求高效,IBM研究表明,很多新生对象存活的时间很短,所以现在新生代中Eden和Survivor(from,to)<br>分的比例是8:1,所以每次新生代的空间使用率是90%,只有10%是浪费的。当Survivor空间不够用的,需要依赖老年代的内存。</li>\n<li>标记整理算法 : 老年代中的对象存活的时间长,不能采用复制算法。标记整理算法的标记和标记清除一样,然后让存活的对象<br>向都向一端移动,然后直接清理掉端边界以外的内存。</li>\n<li>分代算法<br>为了提高效率,根据对象生存周期划分成新生代和老年代,针对不同特点采用不同的算法。<br>新生代使用复制算法,老年代使用标记整理或标记清除算法。<blockquote>\n<p>GC为什么需要stop the world?<br>可达性分析执行时,如果不stw,则无法保证扫描结果的正确性。<br>当进入GC时,线程会怎么办?<br>在方法调用,循环跳转,异常跳转等地方设置安全点。<br>在代码中不会发生引用变化的地方,称为安全区域。<br>在安全点设置中断标记,如果为真,则线程主动中断挂起。<br>在线程离开安全区域的时候,检查gc roots是否检查完成,如果没有,则等待。</p>\n</blockquote>\n</li>\n</ol>\n<h3 id=\"4-垃圾收集器\"><a href=\"#4-垃圾收集器\" class=\"headerlink\" title=\"4. 垃圾收集器\"></a>4. 垃圾收集器</h3><ol>\n<li><p><strong>Serial收集器</strong></p>\n<blockquote>\n<p>单线程完成收集,要求STW。仍然是client模式下新生代的默认配置。</p>\n</blockquote>\n</li>\n<li><p><strong>ParNew收集器</strong><br>ParNew收集器是Serial收集器的多线程版本。它也是现在Server模式下首先的新生代收集器。<br>是因为老年代现在多是选用CMS收集器,而只有ParNew/Serial才可以和它搭配使用。</p>\n</li>\n<li><p><strong>Parallel Scavenge收集器</strong><br>目标是达到一个可控制的吞吐量(即用户线程运行时间/(GC时间+用户线程运行时间);<br>CMS目标是达到较短的停顿时间,适合需要与用户交互的程序,给用户良好的体验。<br>而高吞吐量可以高效地使用cpu的时间,尽快完成程序的运算任务,主要适合在后台运算而不需要<br>太多交互的任务。</p>\n</li>\n</ol>\n<p>-XX:MaxGCPauseMills:最大GC停顿时间<br>-XX:GCTimeRatio:GC时间占比</p>\n<ol start=\"4\">\n<li><p><strong>Serial old收集器</strong><br>单线程收集器,标记整理算法,主要意义也是给Client模式下的JVM使用。<br>它可以作为CMS收集器的后备预案。</p>\n</li>\n<li><p><strong>Parallel old收集器</strong><br>是parallel scavenge收集器的老年代版本,使用多线程和标记整理算法。</p>\n</li>\n<li><p><strong>CMS收集器</strong><br>目标是获取最短回收停顿时间。<br>运作过程分为四个步骤:</p>\n<blockquote>\n<p>初始标记->并发标记->重新标记->并发清除</p>\n</blockquote>\n</li>\n</ol>\n<p><strong>初始标记</strong>仅是标记和GC Roots 能直接关联到的对象。<br><strong>重新标记</strong>:为了修正并发标记期间因为用户程序继续运作而导致标记产生变动的那一部分。<br>由于整个过程中耗时最长的并发标记和并发清除过程收集器和用户线程都可以一起工作,<br>总体来说它停顿时间比较短。<br>它的缺点:</p>\n<ol>\n<li>对CPU资源比较敏感 大于4个的情况下,使用25%的CPU资源。</li>\n<li>CMS收集器无法处理浮动垃圾,可能会出现Concurrent mode failure 失败而导致一次<br>FullGC的产生。<br>jdk1.6开始,CMS收集器的启动的阈值是92%, 要是CMS运行期间预留的内存无法满足程序需要,<br>就会出现concurrent mode failure,这时就会使用serial old来进行老年代的垃圾收集。</li>\n<li>空间碎片过多时,就会给大对象的分配带来困难,往往出现老年代还有很大的空余,但是没有足够大<br>的连续的空间来分配给当前对象,不得不提前触发一次FULL GC的产生。</li>\n</ol>\n","site":{"data":{}},"excerpt":"","more":"<h3 id=\"1-JVM内存描述?\"><a href=\"#1-JVM内存描述?\" class=\"headerlink\" title=\"1. JVM内存描述?\"></a>1. JVM内存描述?</h3><p>程序计数器,VM栈,本地栈和堆,metaspace(方法区)。</p>\n<p><strong>程序计数器</strong>,> 是一块较小的内存空间,当前线程执行的字节码的行号指示器。<br>字节码解释器工作时就是能过改变这个计数器的值来获取下一条要执行的字节码指令,<br>分支,循环,跳转和异常的处理,线程恢复等基础功能都要依赖这个计数器完成。</p>\n<p>它是JVM规范中规定的唯一没有内存溢出情况的区域。</p>\n<p><strong>VM栈</strong> 虚拟机栈描述的是JAVA方法执行的内存模型,每一个方法在执行的同时都会<br>创建一个栈用于存储局部变量表,操作数栈,动态链接,方法出口等信息。<br>它有两种溢出的可能:</p>\n<ol>\n<li>栈溢出:线程请求的的栈深度大于虚拟机所允许的深度。例如递归循环终止条件不正确的设计。</li>\n<li>内存溢出:虚拟机栈可以动态扩展,如果扩展时无法申请到足够的内存,就会抛出oom异常。</li>\n</ol>\n<p><strong>本地栈</strong> 为执行本地方法服务,也会有上述两种溢出情况。</p>\n<p><strong>方法区(metaspace)</strong>: 类的信息,常量池,静态变量,JIT编译后的代码数据。<br>它有一个别名叫NonHeap.如果内存不够,也会抛出OOM异常</p>\n<p><strong>堆</strong>: 用于存放对象实例,所有的对象实例和数组都要在堆上分配,但由于逃逸分析技术等的发展,<br>现在也不绝对。</p>\n<p>垃圾收集器管理的主要区域就是堆。为了更快更好地收集,现在多是分代处理机制。<br>有新生代和老年代,新生代又分为(eden空间,from survivor, to surfvivor)。<br>新生代和老年代采用不同的垃圾收集算法。<br>如果没有足够的内存来分配实例 ,并且也无法扩展时,会抛出OOM异常。</p>\n<p><strong>直接内存</strong> java从1.4开始引入了NIO,使用channel,buffer的IO方式,可以使用native的函数直接<br>分配堆外内存,然后通过存储在java堆中的directbytebuffer对象作为这块内存的引用进行操作。<br>在一些场景中可以提高性能,因为避免了在java堆和native堆中来回复制这些数据。<br>这块区域如果内存不够也会抛出OOMError的异常。</p>\n<h3 id=\"2-对象的创建\"><a href=\"#2-对象的创建\" class=\"headerlink\" title=\"2. 对象的创建\"></a>2. 对象的创建</h3><blockquote>\n<p>请描述下java中对象创建的过程?<br>使用new关键字加上对象名,可以创建对象实例。<br>对于new指令,首先检查指令参数(类名)是否在常量池中,如果可以在常量池中定位一个符号引用,<br>检查这个符号引用代表的类是否已经加载解析和初始化过,如果没有,则需要加载校验初始化这个类。<br>然后就是分配内存,对象所需要的内存大小在类加载完成后就可以完全确定。</p>\n</blockquote>\n<blockquote>\n<p>jvm如何知道堆中哪些块是free的呢?<br>使用serial,parnew等带有compact算法的收集器时,系统采用的分配方式是指针碰撞,而使用<br>CMS这种标记清除算法的收集器时,通常采用空闲列表(记录哪些块是空闲的)。<br>内存分配的这个操作也不是线程安全的,如何实现的呢?<br>jvm使用CAS配上失败重试的方式保证操作的原子性。<br>另一种内存分配动作按照线程划分在不同的空间之中进行,即每一个线程在Java堆中分配一小块内存,称为<br>本地线程缓冲(TLAB).只有TLAB用完了,才需要同步锁定。虚拟机是否使用TLAB,可以使用参数<br>-XX:+/-UseTLAB来设定。<br>内存分配完成后,JVM会把分配的空间都初始化成零值(不包含对象头)。<br>然后对对象进行必要的设置,如这个对象是哪个类的实例,对象的hashcode,gc分类信息,锁信息,这些配置<br>都在对象头中。<br>然后会调用类中的init方法。(静态方法优先执行)</p>\n</blockquote>\n<p>对象在内存中存储的布局有三个区域:对象头,实例数据和对象填充。<br>对象头还有一个类型指针,即对象指向它的类元数据的指针,标明这个对象是哪个类的实例。<br>由于hotspot的JVM自动内存管理系统要求对象起始地址必须是8字节的整数倍,所以当<br>对象实例数据部分没有对齐时,就需要通过对齐填充来补全。<br>对象的访问定位,我们需要使用栈上的reference来定位堆上的实例数据。<br>对象访问方式取决于JVM的实现,目前主流的访问方式有句柄和直接指针两种。<br>直接指针的方式栈中存储的就是对象的地址,直接快速,但是GC操作后如果移动了对象就需要更新这个地址。<br>Hotspot JVM是使用这种方式来访问对象的。</p>\n<h3 id=\"3-垃圾回收算法\"><a href=\"#3-垃圾回收算法\" class=\"headerlink\" title=\"3. 垃圾回收算法\"></a>3. 垃圾回收算法</h3><blockquote>\n<p>如何判断哪些对象该被回收?<br>采用可达性分析的方式,从GC Roots出发,向下搜索,所走过的路径称为引用链,当一个对象到GC Roots之间<br>没有引用链时,就确定该对象是不可用的。</p>\n</blockquote>\n<p>Java中,可作为<code>GC Roots</code>的对象有下面几种:</p>\n<ul>\n<li>虚拟机栈中引用的对象</li>\n<li>方法区(metaspace)中类静态属性引用的对象</li>\n<li>方法区中常量引用的对象</li>\n<li>本地方法栈中JNI引用的对象</li>\n</ul>\n<blockquote>\n<p><strong>垃圾收集算法</strong></p>\n</blockquote>\n<ol>\n<li>标记清除算法 缺点:效率低,会产生空间碎片。</li>\n<li>复制算法 :追求高效,IBM研究表明,很多新生对象存活的时间很短,所以现在新生代中Eden和Survivor(from,to)<br>分的比例是8:1,所以每次新生代的空间使用率是90%,只有10%是浪费的。当Survivor空间不够用的,需要依赖老年代的内存。</li>\n<li>标记整理算法 : 老年代中的对象存活的时间长,不能采用复制算法。标记整理算法的标记和标记清除一样,然后让存活的对象<br>向都向一端移动,然后直接清理掉端边界以外的内存。</li>\n<li>分代算法<br>为了提高效率,根据对象生存周期划分成新生代和老年代,针对不同特点采用不同的算法。<br>新生代使用复制算法,老年代使用标记整理或标记清除算法。<blockquote>\n<p>GC为什么需要stop the world?<br>可达性分析执行时,如果不stw,则无法保证扫描结果的正确性。<br>当进入GC时,线程会怎么办?<br>在方法调用,循环跳转,异常跳转等地方设置安全点。<br>在代码中不会发生引用变化的地方,称为安全区域。<br>在安全点设置中断标记,如果为真,则线程主动中断挂起。<br>在线程离开安全区域的时候,检查gc roots是否检查完成,如果没有,则等待。</p>\n</blockquote>\n</li>\n</ol>\n<h3 id=\"4-垃圾收集器\"><a href=\"#4-垃圾收集器\" class=\"headerlink\" title=\"4. 垃圾收集器\"></a>4. 垃圾收集器</h3><ol>\n<li><p><strong>Serial收集器</strong></p>\n<blockquote>\n<p>单线程完成收集,要求STW。仍然是client模式下新生代的默认配置。</p>\n</blockquote>\n</li>\n<li><p><strong>ParNew收集器</strong><br>ParNew收集器是Serial收集器的多线程版本。它也是现在Server模式下首先的新生代收集器。<br>是因为老年代现在多是选用CMS收集器,而只有ParNew/Serial才可以和它搭配使用。</p>\n</li>\n<li><p><strong>Parallel Scavenge收集器</strong><br>目标是达到一个可控制的吞吐量(即用户线程运行时间/(GC时间+用户线程运行时间);<br>CMS目标是达到较短的停顿时间,适合需要与用户交互的程序,给用户良好的体验。<br>而高吞吐量可以高效地使用cpu的时间,尽快完成程序的运算任务,主要适合在后台运算而不需要<br>太多交互的任务。</p>\n</li>\n</ol>\n<p>-XX:MaxGCPauseMills:最大GC停顿时间<br>-XX:GCTimeRatio:GC时间占比</p>\n<ol start=\"4\">\n<li><p><strong>Serial old收集器</strong><br>单线程收集器,标记整理算法,主要意义也是给Client模式下的JVM使用。<br>它可以作为CMS收集器的后备预案。</p>\n</li>\n<li><p><strong>Parallel old收集器</strong><br>是parallel scavenge收集器的老年代版本,使用多线程和标记整理算法。</p>\n</li>\n<li><p><strong>CMS收集器</strong><br>目标是获取最短回收停顿时间。<br>运作过程分为四个步骤:</p>\n<blockquote>\n<p>初始标记->并发标记->重新标记->并发清除</p>\n</blockquote>\n</li>\n</ol>\n<p><strong>初始标记</strong>仅是标记和GC Roots 能直接关联到的对象。<br><strong>重新标记</strong>:为了修正并发标记期间因为用户程序继续运作而导致标记产生变动的那一部分。<br>由于整个过程中耗时最长的并发标记和并发清除过程收集器和用户线程都可以一起工作,<br>总体来说它停顿时间比较短。<br>它的缺点:</p>\n<ol>\n<li>对CPU资源比较敏感 大于4个的情况下,使用25%的CPU资源。</li>\n<li>CMS收集器无法处理浮动垃圾,可能会出现Concurrent mode failure 失败而导致一次<br>FullGC的产生。<br>jdk1.6开始,CMS收集器的启动的阈值是92%, 要是CMS运行期间预留的内存无法满足程序需要,<br>就会出现concurrent mode failure,这时就会使用serial old来进行老年代的垃圾收集。</li>\n<li>空间碎片过多时,就会给大对象的分配带来困难,往往出现老年代还有很大的空余,但是没有足够大<br>的连续的空间来分配给当前对象,不得不提前触发一次FULL GC的产生。</li>\n</ol>\n"},{"title":"Java并发面试题1","date":"2021-07-11T16:00:00.000Z","comments":1,"toc":true,"_content":"### Java内存模型 (JMM)\n JMM用来屏蔽各种硬件和操作系统的内存访问差异,以实现让java程序在各种平台下都能达到一致的内存访问效果。\n JMM的主要目标是定义程序中各个变量的访问规则,即在JVM中将变量存储到内存和从内存中取出变量这样的底层细节。\n JMM规定了所有的变量都存储在主内存,每一条线程都还有自己的工作内存,线程的工作内存保存了被该线程使用到的\n 变量的主内存副本拷贝,线程对变量的所有操作都必须在工作内存中进行,而不能直接读写主内存中的变量。\n\n> 1. 请一下你对volatile关键字的理解?\n 当一个变量被volatile修饰后,第一保证此变量对所有线程的可见性,当一个线程修改了这个变量的值,新值对于\n 其他线程就是立即可见的。\n 实现的原理是缓存一致性协议,修改完的数据需要同步到主内存去,这个数据要经过消息总线,其他cpu监控总线上的消息,\n 如果这个volatile的值被更新,则自己的缓存中的相对应的值就变成无效,后再用的时候就需要从主内存中拉取最新的值。\n 第二个语义是禁止指令重排序的优化,实现的原理是他会在操作前加一个lock前缀,这相当于一个内存屏障,指令\n 重排序时不允许把后面的指令排序到内存屏障之前)。\n 它不能保证原子性,原子性方面还是需要synchronized的配合使用。\n\n> 2. 请讲一下你对synchronized关键字的理解?\n 它可以实现可见性,原子性和有序性。\n 它经过编译后,会在同步块的前后分别形成monitorenter 和 monitorexit这两个字节码指令,这两个字节码指令需要一个\n reference类型的参数来表明锁定和解锁的对象。如果没有指定,取决于它修饰的对象,取类还是类的实例作为锁对象。\n monitorenter指令获取对象的锁是可重入的,锁计数器可增减,减到0就是锁释放了。\n 它和JUC中的可重入锁在新的1.6以后的版本中性能几乎无异,但是可重入锁有以下特点:\n 1. 等待可中断 持有锁的线程长期不释放锁的时候,正在等待的线程可以选择放弃等待。\n 2. 公平锁 多个线程在等待同一个锁时,必须按照申请锁的时间顺序来依次获取锁,synchronized和默认参数的可重入锁都是\n 非公平锁,非公平锁的效率比较高。\n 3. 锁可以绑定多个条件 使用newCondition()方法即可。\n 为什么说Synchronized是重量级锁?\n 因为它使用monitor对应的底层操作系统的mutex lock,操作系统实现线程切换需要从用户态转到内核态,这个成本非常高,状态\n 之间的转换需要很长的时间。依赖操作系统的mutex lock实现的锁就是重量级锁。\n 后面jdk对synchronized做了优化,无锁,偏向锁,轻量级锁在一定程度上优化了它的效率,但并发情况下还是难免会升级成重\n 量级锁的。\n\n> 3. 请讲一下你理解的ThreadLocal 变量 有什么用?\n 我理解的场景,譬如有一个变量,你可能在多个类中都会访问,但是这些类都在一个线程中执行,那你就可以把这个变量放到ThreadLocal里面,\n 这样非常方便访问。\n 这个关键字的作用是什么?什么场景下会使用?它为每一个线程维护了变量副本,实现了线程安全。\n 这种场景,譬如web服务中的controller等就可以使用这种,因为它们都在同一个线程中被访问。\n 最常用的场景是session管理 ,数据库的连接对象。\n spark的TaskContext 在创建每一个task的时候都需要这个参数,在多线程并发的情况下,设置成ThreadLocal变量,可不互不干扰。\n val taskContext: ThreadLocal[TaskContext] = new ThreadLocal[TaskContext]\n 它是怎么实现的呢?\n 每一个线程都有一个类型为ThreadLocalMap的成员变量threadLocals,它是由ThreadLocal<>来维护的。\n 它的Key是threadLocal对象,它的值就是set时传入的参数值。\n\n> 4. 聊一下你对线程池的理解?你工作中怎么使用的?\n 工作中是使用ExecutorService获取Executors返回的线程池,常用的是固定大小的线程池或含有调度功能的线性池。\n 它又是调用的ThreadPoolExecutors。\n 你提到的ThreadPoolExecutor它有哪些参数?\n (corePoolSize,MaxmiumPoolSize,KeepAlive, TimeUnit, workQueue)\n corepoolsize代表线程池中线程数量,MaximumPoolSize代表线程池中最大的线程数量。\n workQueue:表示任务队列,存储提交的未被执行的任务。\n 它的工作流程如下:\n 1. 新任务来的时候,如果正在运行的线程数量小于corepoolsize,则立刻创建新线程运行该任务。\n 2. 如果正在运行的线程数量大于等于corepoolsize,则任务被添加到workqueue.\n 3. 如果workqueue满了,而且正在运行的线程数量小于maximumpoolsize的话,还是要创建非核心线程来运行此任务。\n 4. 如果workqueue满了,而且正在运行的线程数量大于或等于maximumpoolsize的话,根据拒绝策略处理。\n 5. 如果一个线程无事可做,超过一定的时间,线程池会判断,如果运行的线程数量大于corepoolsize,则它会被停掉,\n 即会慢慢地回到corepoolsize的大小。\n 请讲一下它有哪些拒绝策略呢?\n 1. AbortPolicy: 直接抛出异常,阻止系统正常运行。\n 2. CallerRunPolicy: 在调用者线程中运行,这样会影响任务提交线程的运行情况。\n 3. DiscardOldestPolicy: 抛弃工作队列中最前面的作业。\n 4. DiscardPolicy: 抛弃当前新加来的作业。\n\n> 5. 谈谈你对JUC中线程同步的几个数据结构?\n **CountDownLatch** 它可以实现类似计数器的功能,如果有一个作业需要四个线程完成后才可以继续,则可以使用这个\n CountDownLatch来完成。调用countDown方法来挂起当前线程。\n **CyclicBarrier** 它可以让一组线程等待到同一状态后再同时执行,当所有等待线程释放锁之后,它是可以被重用的。\n 通过await()方法来挂起当前线程,当所有的线程都执行完此方法后可同时执行后面的代码。\n **Semaphore** 信号量,可以控制同时访问共享资源的线程数量,常用于线程池,连接池等实现。使用accquire()方法\n 来获取一个资源,没有就等待,使用release()方法来释放一个资源。\n\n> 6. 谈谈你对CAS的理解?\n CAS(COMPARE AND SWAP) 硬件层面支持这个指令后,JVM使用CAS来实现乐观锁。\n 它接三个参数,要更新的变量,旧的值,新值。当多个线程都用CAS来更新一个变量值的时候,只有一个会胜出,失败的仅\n 告知失败,但会继续尝试,不会挂起。\n CAS遭遇到ABA问题,可以通过版本号或时间来标记来解决这个问题,stampedReference。\n 它不可以保证代码块的原子性,只能保证一个操作的原子性。\n 它循环重试的过程中一直使用CPU资源。\n\n> 7. 谈谈你对AQS的理解?\n 它定义了一套多线程访问共享资源的同步器框架,许多同步类的实现都依赖于它,如semaphore,countdownlatch等。\n 它维护了一个volatile int state(代表共享资源)和一个FIFO的线程队列(争用资源被阻塞时进入此队列),基于\n 它可以设计独占锁或者是共享锁。\n 它只是一个框架,具体资源的获取和释放,资源的数量都由具体类去实现。\n 它有tryAccuire,tryRelease和tryAccureshare,Tryreleaseshare方法来获取和释放资源 。\n\n\n\n","source":"_posts/Java并发面试题1.md","raw":"---\ntitle: Java并发面试题1\ndate: 2021-07-12\ncomments: true #是否可评论\ntoc: true #是否显示文章目录\ncategories: \"面试题\" #分类\ntags: #标签\n - JAVA\n - 面试题\n---\n### Java内存模型 (JMM)\n JMM用来屏蔽各种硬件和操作系统的内存访问差异,以实现让java程序在各种平台下都能达到一致的内存访问效果。\n JMM的主要目标是定义程序中各个变量的访问规则,即在JVM中将变量存储到内存和从内存中取出变量这样的底层细节。\n JMM规定了所有的变量都存储在主内存,每一条线程都还有自己的工作内存,线程的工作内存保存了被该线程使用到的\n 变量的主内存副本拷贝,线程对变量的所有操作都必须在工作内存中进行,而不能直接读写主内存中的变量。\n\n> 1. 请一下你对volatile关键字的理解?\n 当一个变量被volatile修饰后,第一保证此变量对所有线程的可见性,当一个线程修改了这个变量的值,新值对于\n 其他线程就是立即可见的。\n 实现的原理是缓存一致性协议,修改完的数据需要同步到主内存去,这个数据要经过消息总线,其他cpu监控总线上的消息,\n 如果这个volatile的值被更新,则自己的缓存中的相对应的值就变成无效,后再用的时候就需要从主内存中拉取最新的值。\n 第二个语义是禁止指令重排序的优化,实现的原理是他会在操作前加一个lock前缀,这相当于一个内存屏障,指令\n 重排序时不允许把后面的指令排序到内存屏障之前)。\n 它不能保证原子性,原子性方面还是需要synchronized的配合使用。\n\n> 2. 请讲一下你对synchronized关键字的理解?\n 它可以实现可见性,原子性和有序性。\n 它经过编译后,会在同步块的前后分别形成monitorenter 和 monitorexit这两个字节码指令,这两个字节码指令需要一个\n reference类型的参数来表明锁定和解锁的对象。如果没有指定,取决于它修饰的对象,取类还是类的实例作为锁对象。\n monitorenter指令获取对象的锁是可重入的,锁计数器可增减,减到0就是锁释放了。\n 它和JUC中的可重入锁在新的1.6以后的版本中性能几乎无异,但是可重入锁有以下特点:\n 1. 等待可中断 持有锁的线程长期不释放锁的时候,正在等待的线程可以选择放弃等待。\n 2. 公平锁 多个线程在等待同一个锁时,必须按照申请锁的时间顺序来依次获取锁,synchronized和默认参数的可重入锁都是\n 非公平锁,非公平锁的效率比较高。\n 3. 锁可以绑定多个条件 使用newCondition()方法即可。\n 为什么说Synchronized是重量级锁?\n 因为它使用monitor对应的底层操作系统的mutex lock,操作系统实现线程切换需要从用户态转到内核态,这个成本非常高,状态\n 之间的转换需要很长的时间。依赖操作系统的mutex lock实现的锁就是重量级锁。\n 后面jdk对synchronized做了优化,无锁,偏向锁,轻量级锁在一定程度上优化了它的效率,但并发情况下还是难免会升级成重\n 量级锁的。\n\n> 3. 请讲一下你理解的ThreadLocal 变量 有什么用?\n 我理解的场景,譬如有一个变量,你可能在多个类中都会访问,但是这些类都在一个线程中执行,那你就可以把这个变量放到ThreadLocal里面,\n 这样非常方便访问。\n 这个关键字的作用是什么?什么场景下会使用?它为每一个线程维护了变量副本,实现了线程安全。\n 这种场景,譬如web服务中的controller等就可以使用这种,因为它们都在同一个线程中被访问。\n 最常用的场景是session管理 ,数据库的连接对象。\n spark的TaskContext 在创建每一个task的时候都需要这个参数,在多线程并发的情况下,设置成ThreadLocal变量,可不互不干扰。\n val taskContext: ThreadLocal[TaskContext] = new ThreadLocal[TaskContext]\n 它是怎么实现的呢?\n 每一个线程都有一个类型为ThreadLocalMap的成员变量threadLocals,它是由ThreadLocal<>来维护的。\n 它的Key是threadLocal对象,它的值就是set时传入的参数值。\n\n> 4. 聊一下你对线程池的理解?你工作中怎么使用的?\n 工作中是使用ExecutorService获取Executors返回的线程池,常用的是固定大小的线程池或含有调度功能的线性池。\n 它又是调用的ThreadPoolExecutors。\n 你提到的ThreadPoolExecutor它有哪些参数?\n (corePoolSize,MaxmiumPoolSize,KeepAlive, TimeUnit, workQueue)\n corepoolsize代表线程池中线程数量,MaximumPoolSize代表线程池中最大的线程数量。\n workQueue:表示任务队列,存储提交的未被执行的任务。\n 它的工作流程如下:\n 1. 新任务来的时候,如果正在运行的线程数量小于corepoolsize,则立刻创建新线程运行该任务。\n 2. 如果正在运行的线程数量大于等于corepoolsize,则任务被添加到workqueue.\n 3. 如果workqueue满了,而且正在运行的线程数量小于maximumpoolsize的话,还是要创建非核心线程来运行此任务。\n 4. 如果workqueue满了,而且正在运行的线程数量大于或等于maximumpoolsize的话,根据拒绝策略处理。\n 5. 如果一个线程无事可做,超过一定的时间,线程池会判断,如果运行的线程数量大于corepoolsize,则它会被停掉,\n 即会慢慢地回到corepoolsize的大小。\n 请讲一下它有哪些拒绝策略呢?\n 1. AbortPolicy: 直接抛出异常,阻止系统正常运行。\n 2. CallerRunPolicy: 在调用者线程中运行,这样会影响任务提交线程的运行情况。\n 3. DiscardOldestPolicy: 抛弃工作队列中最前面的作业。\n 4. DiscardPolicy: 抛弃当前新加来的作业。\n\n> 5. 谈谈你对JUC中线程同步的几个数据结构?\n **CountDownLatch** 它可以实现类似计数器的功能,如果有一个作业需要四个线程完成后才可以继续,则可以使用这个\n CountDownLatch来完成。调用countDown方法来挂起当前线程。\n **CyclicBarrier** 它可以让一组线程等待到同一状态后再同时执行,当所有等待线程释放锁之后,它是可以被重用的。\n 通过await()方法来挂起当前线程,当所有的线程都执行完此方法后可同时执行后面的代码。\n **Semaphore** 信号量,可以控制同时访问共享资源的线程数量,常用于线程池,连接池等实现。使用accquire()方法\n 来获取一个资源,没有就等待,使用release()方法来释放一个资源。\n\n> 6. 谈谈你对CAS的理解?\n CAS(COMPARE AND SWAP) 硬件层面支持这个指令后,JVM使用CAS来实现乐观锁。\n 它接三个参数,要更新的变量,旧的值,新值。当多个线程都用CAS来更新一个变量值的时候,只有一个会胜出,失败的仅\n 告知失败,但会继续尝试,不会挂起。\n CAS遭遇到ABA问题,可以通过版本号或时间来标记来解决这个问题,stampedReference。\n 它不可以保证代码块的原子性,只能保证一个操作的原子性。\n 它循环重试的过程中一直使用CPU资源。\n\n> 7. 谈谈你对AQS的理解?\n 它定义了一套多线程访问共享资源的同步器框架,许多同步类的实现都依赖于它,如semaphore,countdownlatch等。\n 它维护了一个volatile int state(代表共享资源)和一个FIFO的线程队列(争用资源被阻塞时进入此队列),基于\n 它可以设计独占锁或者是共享锁。\n 它只是一个框架,具体资源的获取和释放,资源的数量都由具体类去实现。\n 它有tryAccuire,tryRelease和tryAccureshare,Tryreleaseshare方法来获取和释放资源 。\n\n\n\n","slug":"Java并发面试题1","published":1,"updated":"2022-04-20T09:35:50.862Z","layout":"post","photos":[],"link":"","_id":"cl27q3uux0005n94ochtcf2k5","content":"<h3 id=\"Java内存模型-JMM\"><a href=\"#Java内存模型-JMM\" class=\"headerlink\" title=\"Java内存模型 (JMM)\"></a>Java内存模型 (JMM)</h3><pre><code>JMM用来屏蔽各种硬件和操作系统的内存访问差异,以实现让java程序在各种平台下都能达到一致的内存访问效果。\nJMM的主要目标是定义程序中各个变量的访问规则,即在JVM中将变量存储到内存和从内存中取出变量这样的底层细节。\nJMM规定了所有的变量都存储在主内存,每一条线程都还有自己的工作内存,线程的工作内存保存了被该线程使用到的\n变量的主内存副本拷贝,线程对变量的所有操作都必须在工作内存中进行,而不能直接读写主内存中的变量。\n</code></pre>\n<blockquote>\n<ol>\n<li>请一下你对volatile关键字的理解?<br> 当一个变量被volatile修饰后,第一保证此变量对所有线程的可见性,当一个线程修改了这个变量的值,新值对于<br> 其他线程就是立即可见的。<br> 实现的原理是缓存一致性协议,修改完的数据需要同步到主内存去,这个数据要经过消息总线,其他cpu监控总线上的消息,<br> 如果这个volatile的值被更新,则自己的缓存中的相对应的值就变成无效,后再用的时候就需要从主内存中拉取最新的值。<br> 第二个语义是禁止指令重排序的优化,实现的原理是他会在操作前加一个lock前缀,这相当于一个内存屏障,指令<br> 重排序时不允许把后面的指令排序到内存屏障之前)。<br> 它不能保证原子性,原子性方面还是需要synchronized的配合使用。</li>\n</ol>\n</blockquote>\n<blockquote>\n<ol start=\"2\">\n<li>请讲一下你对synchronized关键字的理解?<br> 它可以实现可见性,原子性和有序性。<br> 它经过编译后,会在同步块的前后分别形成monitorenter 和 monitorexit这两个字节码指令,这两个字节码指令需要一个<br> reference类型的参数来表明锁定和解锁的对象。如果没有指定,取决于它修饰的对象,取类还是类的实例作为锁对象。<br> monitorenter指令获取对象的锁是可重入的,锁计数器可增减,减到0就是锁释放了。<br> 它和JUC中的可重入锁在新的1.6以后的版本中性能几乎无异,但是可重入锁有以下特点:<ol>\n<li>等待可中断 持有锁的线程长期不释放锁的时候,正在等待的线程可以选择放弃等待。</li>\n<li>公平锁 多个线程在等待同一个锁时,必须按照申请锁的时间顺序来依次获取锁,synchronized和默认参数的可重入锁都是<br>非公平锁,非公平锁的效率比较高。</li>\n<li>锁可以绑定多个条件 使用newCondition()方法即可。<br>为什么说Synchronized是重量级锁?<br>因为它使用monitor对应的底层操作系统的mutex lock,操作系统实现线程切换需要从用户态转到内核态,这个成本非常高,状态<br>之间的转换需要很长的时间。依赖操作系统的mutex lock实现的锁就是重量级锁。<br>后面jdk对synchronized做了优化,无锁,偏向锁,轻量级锁在一定程度上优化了它的效率,但并发情况下还是难免会升级成重<br>量级锁的。</li>\n</ol>\n</li>\n</ol>\n</blockquote>\n<blockquote>\n<ol start=\"3\">\n<li>请讲一下你理解的ThreadLocal 变量 有什么用?<br> 我理解的场景,譬如有一个变量,你可能在多个类中都会访问,但是这些类都在一个线程中执行,那你就可以把这个变量放到ThreadLocal里面,<br> 这样非常方便访问。<br> 这个关键字的作用是什么?什么场景下会使用?它为每一个线程维护了变量副本,实现了线程安全。<br> 这种场景,譬如web服务中的controller等就可以使用这种,因为它们都在同一个线程中被访问。<br> 最常用的场景是session管理 ,数据库的连接对象。<br> spark的TaskContext 在创建每一个task的时候都需要这个参数,在多线程并发的情况下,设置成ThreadLocal变量,可不互不干扰。<br> val taskContext: ThreadLocal[TaskContext] = new ThreadLocal[TaskContext]<br> 它是怎么实现的呢?<br> 每一个线程都有一个类型为ThreadLocalMap的成员变量threadLocals,它是由ThreadLocal<>来维护的。<br> 它的Key是threadLocal对象,它的值就是set时传入的参数值。</li>\n</ol>\n</blockquote>\n<blockquote>\n<ol start=\"4\">\n<li>聊一下你对线程池的理解?你工作中怎么使用的?<br> 工作中是使用ExecutorService获取Executors返回的线程池,常用的是固定大小的线程池或含有调度功能的线性池。<br> 它又是调用的ThreadPoolExecutors。<br> 你提到的ThreadPoolExecutor它有哪些参数?<br> (corePoolSize,MaxmiumPoolSize,KeepAlive, TimeUnit, workQueue)<br> corepoolsize代表线程池中线程数量,MaximumPoolSize代表线程池中最大的线程数量。<br> workQueue:表示任务队列,存储提交的未被执行的任务。<br> 它的工作流程如下:<ol>\n<li>新任务来的时候,如果正在运行的线程数量小于corepoolsize,则立刻创建新线程运行该任务。</li>\n<li>如果正在运行的线程数量大于等于corepoolsize,则任务被添加到workqueue.</li>\n<li>如果workqueue满了,而且正在运行的线程数量小于maximumpoolsize的话,还是要创建非核心线程来运行此任务。</li>\n<li>如果workqueue满了,而且正在运行的线程数量大于或等于maximumpoolsize的话,根据拒绝策略处理。</li>\n<li>如果一个线程无事可做,超过一定的时间,线程池会判断,如果运行的线程数量大于corepoolsize,则它会被停掉,<br>即会慢慢地回到corepoolsize的大小。<br>请讲一下它有哪些拒绝策略呢?</li>\n<li>AbortPolicy: 直接抛出异常,阻止系统正常运行。</li>\n<li>CallerRunPolicy: 在调用者线程中运行,这样会影响任务提交线程的运行情况。</li>\n<li>DiscardOldestPolicy: 抛弃工作队列中最前面的作业。</li>\n<li>DiscardPolicy: 抛弃当前新加来的作业。</li>\n</ol>\n</li>\n</ol>\n</blockquote>\n<blockquote>\n<ol start=\"5\">\n<li>谈谈你对JUC中线程同步的几个数据结构?<br> <strong>CountDownLatch</strong> 它可以实现类似计数器的功能,如果有一个作业需要四个线程完成后才可以继续,则可以使用这个<br> CountDownLatch来完成。调用countDown方法来挂起当前线程。<br> <strong>CyclicBarrier</strong> 它可以让一组线程等待到同一状态后再同时执行,当所有等待线程释放锁之后,它是可以被重用的。<br> 通过await()方法来挂起当前线程,当所有的线程都执行完此方法后可同时执行后面的代码。<br> <strong>Semaphore</strong> 信号量,可以控制同时访问共享资源的线程数量,常用于线程池,连接池等实现。使用accquire()方法<br> 来获取一个资源,没有就等待,使用release()方法来释放一个资源。</li>\n</ol>\n</blockquote>\n<blockquote>\n<ol start=\"6\">\n<li>谈谈你对CAS的理解?<br> CAS(COMPARE AND SWAP) 硬件层面支持这个指令后,JVM使用CAS来实现乐观锁。<br> 它接三个参数,要更新的变量,旧的值,新值。当多个线程都用CAS来更新一个变量值的时候,只有一个会胜出,失败的仅<br> 告知失败,但会继续尝试,不会挂起。<br> CAS遭遇到ABA问题,可以通过版本号或时间来标记来解决这个问题,stampedReference。<br> 它不可以保证代码块的原子性,只能保证一个操作的原子性。<br> 它循环重试的过程中一直使用CPU资源。</li>\n</ol>\n</blockquote>\n<blockquote>\n<ol start=\"7\">\n<li>谈谈你对AQS的理解?<br> 它定义了一套多线程访问共享资源的同步器框架,许多同步类的实现都依赖于它,如semaphore,countdownlatch等。<br> 它维护了一个volatile int state(代表共享资源)和一个FIFO的线程队列(争用资源被阻塞时进入此队列),基于<br> 它可以设计独占锁或者是共享锁。<br> 它只是一个框架,具体资源的获取和释放,资源的数量都由具体类去实现。<br> 它有tryAccuire,tryRelease和tryAccureshare,Tryreleaseshare方法来获取和释放资源 。</li>\n</ol>\n</blockquote>\n","site":{"data":{}},"excerpt":"","more":"<h3 id=\"Java内存模型-JMM\"><a href=\"#Java内存模型-JMM\" class=\"headerlink\" title=\"Java内存模型 (JMM)\"></a>Java内存模型 (JMM)</h3><pre><code>JMM用来屏蔽各种硬件和操作系统的内存访问差异,以实现让java程序在各种平台下都能达到一致的内存访问效果。\nJMM的主要目标是定义程序中各个变量的访问规则,即在JVM中将变量存储到内存和从内存中取出变量这样的底层细节。\nJMM规定了所有的变量都存储在主内存,每一条线程都还有自己的工作内存,线程的工作内存保存了被该线程使用到的\n变量的主内存副本拷贝,线程对变量的所有操作都必须在工作内存中进行,而不能直接读写主内存中的变量。\n</code></pre>\n<blockquote>\n<ol>\n<li>请一下你对volatile关键字的理解?<br> 当一个变量被volatile修饰后,第一保证此变量对所有线程的可见性,当一个线程修改了这个变量的值,新值对于<br> 其他线程就是立即可见的。<br> 实现的原理是缓存一致性协议,修改完的数据需要同步到主内存去,这个数据要经过消息总线,其他cpu监控总线上的消息,<br> 如果这个volatile的值被更新,则自己的缓存中的相对应的值就变成无效,后再用的时候就需要从主内存中拉取最新的值。<br> 第二个语义是禁止指令重排序的优化,实现的原理是他会在操作前加一个lock前缀,这相当于一个内存屏障,指令<br> 重排序时不允许把后面的指令排序到内存屏障之前)。<br> 它不能保证原子性,原子性方面还是需要synchronized的配合使用。</li>\n</ol>\n</blockquote>\n<blockquote>\n<ol start=\"2\">\n<li>请讲一下你对synchronized关键字的理解?<br> 它可以实现可见性,原子性和有序性。<br> 它经过编译后,会在同步块的前后分别形成monitorenter 和 monitorexit这两个字节码指令,这两个字节码指令需要一个<br> reference类型的参数来表明锁定和解锁的对象。如果没有指定,取决于它修饰的对象,取类还是类的实例作为锁对象。<br> monitorenter指令获取对象的锁是可重入的,锁计数器可增减,减到0就是锁释放了。<br> 它和JUC中的可重入锁在新的1.6以后的版本中性能几乎无异,但是可重入锁有以下特点:<ol>\n<li>等待可中断 持有锁的线程长期不释放锁的时候,正在等待的线程可以选择放弃等待。</li>\n<li>公平锁 多个线程在等待同一个锁时,必须按照申请锁的时间顺序来依次获取锁,synchronized和默认参数的可重入锁都是<br>非公平锁,非公平锁的效率比较高。</li>\n<li>锁可以绑定多个条件 使用newCondition()方法即可。<br>为什么说Synchronized是重量级锁?<br>因为它使用monitor对应的底层操作系统的mutex lock,操作系统实现线程切换需要从用户态转到内核态,这个成本非常高,状态<br>之间的转换需要很长的时间。依赖操作系统的mutex lock实现的锁就是重量级锁。<br>后面jdk对synchronized做了优化,无锁,偏向锁,轻量级锁在一定程度上优化了它的效率,但并发情况下还是难免会升级成重<br>量级锁的。</li>\n</ol>\n</li>\n</ol>\n</blockquote>\n<blockquote>\n<ol start=\"3\">\n<li>请讲一下你理解的ThreadLocal 变量 有什么用?<br> 我理解的场景,譬如有一个变量,你可能在多个类中都会访问,但是这些类都在一个线程中执行,那你就可以把这个变量放到ThreadLocal里面,<br> 这样非常方便访问。<br> 这个关键字的作用是什么?什么场景下会使用?它为每一个线程维护了变量副本,实现了线程安全。<br> 这种场景,譬如web服务中的controller等就可以使用这种,因为它们都在同一个线程中被访问。<br> 最常用的场景是session管理 ,数据库的连接对象。<br> spark的TaskContext 在创建每一个task的时候都需要这个参数,在多线程并发的情况下,设置成ThreadLocal变量,可不互不干扰。<br> val taskContext: ThreadLocal[TaskContext] = new ThreadLocal[TaskContext]<br> 它是怎么实现的呢?<br> 每一个线程都有一个类型为ThreadLocalMap的成员变量threadLocals,它是由ThreadLocal<>来维护的。<br> 它的Key是threadLocal对象,它的值就是set时传入的参数值。</li>\n</ol>\n</blockquote>\n<blockquote>\n<ol start=\"4\">\n<li>聊一下你对线程池的理解?你工作中怎么使用的?<br> 工作中是使用ExecutorService获取Executors返回的线程池,常用的是固定大小的线程池或含有调度功能的线性池。<br> 它又是调用的ThreadPoolExecutors。<br> 你提到的ThreadPoolExecutor它有哪些参数?<br> (corePoolSize,MaxmiumPoolSize,KeepAlive, TimeUnit, workQueue)<br> corepoolsize代表线程池中线程数量,MaximumPoolSize代表线程池中最大的线程数量。<br> workQueue:表示任务队列,存储提交的未被执行的任务。<br> 它的工作流程如下:<ol>\n<li>新任务来的时候,如果正在运行的线程数量小于corepoolsize,则立刻创建新线程运行该任务。</li>\n<li>如果正在运行的线程数量大于等于corepoolsize,则任务被添加到workqueue.</li>\n<li>如果workqueue满了,而且正在运行的线程数量小于maximumpoolsize的话,还是要创建非核心线程来运行此任务。</li>\n<li>如果workqueue满了,而且正在运行的线程数量大于或等于maximumpoolsize的话,根据拒绝策略处理。</li>\n<li>如果一个线程无事可做,超过一定的时间,线程池会判断,如果运行的线程数量大于corepoolsize,则它会被停掉,<br>即会慢慢地回到corepoolsize的大小。<br>请讲一下它有哪些拒绝策略呢?</li>\n<li>AbortPolicy: 直接抛出异常,阻止系统正常运行。</li>\n<li>CallerRunPolicy: 在调用者线程中运行,这样会影响任务提交线程的运行情况。</li>\n<li>DiscardOldestPolicy: 抛弃工作队列中最前面的作业。</li>\n<li>DiscardPolicy: 抛弃当前新加来的作业。</li>\n</ol>\n</li>\n</ol>\n</blockquote>\n<blockquote>\n<ol start=\"5\">\n<li>谈谈你对JUC中线程同步的几个数据结构?<br> <strong>CountDownLatch</strong> 它可以实现类似计数器的功能,如果有一个作业需要四个线程完成后才可以继续,则可以使用这个<br> CountDownLatch来完成。调用countDown方法来挂起当前线程。<br> <strong>CyclicBarrier</strong> 它可以让一组线程等待到同一状态后再同时执行,当所有等待线程释放锁之后,它是可以被重用的。<br> 通过await()方法来挂起当前线程,当所有的线程都执行完此方法后可同时执行后面的代码。<br> <strong>Semaphore</strong> 信号量,可以控制同时访问共享资源的线程数量,常用于线程池,连接池等实现。使用accquire()方法<br> 来获取一个资源,没有就等待,使用release()方法来释放一个资源。</li>\n</ol>\n</blockquote>\n<blockquote>\n<ol start=\"6\">\n<li>谈谈你对CAS的理解?<br> CAS(COMPARE AND SWAP) 硬件层面支持这个指令后,JVM使用CAS来实现乐观锁。<br> 它接三个参数,要更新的变量,旧的值,新值。当多个线程都用CAS来更新一个变量值的时候,只有一个会胜出,失败的仅<br> 告知失败,但会继续尝试,不会挂起。<br> CAS遭遇到ABA问题,可以通过版本号或时间来标记来解决这个问题,stampedReference。<br> 它不可以保证代码块的原子性,只能保证一个操作的原子性。<br> 它循环重试的过程中一直使用CPU资源。</li>\n</ol>\n</blockquote>\n<blockquote>\n<ol start=\"7\">\n<li>谈谈你对AQS的理解?<br> 它定义了一套多线程访问共享资源的同步器框架,许多同步类的实现都依赖于它,如semaphore,countdownlatch等。<br> 它维护了一个volatile int state(代表共享资源)和一个FIFO的线程队列(争用资源被阻塞时进入此队列),基于<br> 它可以设计独占锁或者是共享锁。<br> 它只是一个框架,具体资源的获取和释放,资源的数量都由具体类去实现。<br> 它有tryAccuire,tryRelease和tryAccureshare,Tryreleaseshare方法来获取和释放资源 。</li>\n</ol>\n</blockquote>\n"},{"title":"Java集合面试题1","date":"2021-07-10T16:00:00.000Z","comments":1,"toc":true,"_content":"\n#### hashmap需要扩容的时候会影响性能,为什么不一开始给一个大的初始化值呢?\n 因为它迭代的性能取决于hashmap的size和元素的数量,如果初始值设得过大,\n 则在它上面的迭代就会性能很差。\n 同样的道理,load factor也不能设得太低,太低就会更早地扩容。\n\n#### Java8的hashmap中使用了红黑树,为什么链表超过8个长度并且整个容量大于64的时候转成树呢?\n 假定数据的key的hashcode符合泊松分布,超过8个长度的时候,\n 是小于千万分之一的概率。如果达到,说明数据有严重的分布问题。\n 以防性能太差,就转成红黑树,至少可以保证logn的读写性能。\n 容量太小,转成树反而还没有链表的查询快。转换构造树的时间也很耗时。 \n\n#### Java8中HashMap为什么容量大小必须是2的N次方呢?\n 算对象所在hashmap中的位置是这么做的,(h=key.hashcode()) ^ (h>>>16) ,即使用了key生成的hashcode的高位右位十六位再和\n 原来的hashcode进行计算,这样让高位也参与了运算,这样的扰动函数,降低了碰撞的机会。\n 通过把容量保持在2的N次次,我们在扩容后,Node的新的位置要么在旧的位置要么是旧的容量+旧的位置,这样hash就不用算了。只需要一个\n 判断就可以,提高了效率。(e.hash & oldCap) == 0, 如果hash和旧的容量与之后等于0,则说明e.hash就小于oldCap,则不变。否则\n 新的位置就是旧的位置加oldCap。\n 扩容的时候只移动大约一半的数据,并且不会造成扩容之后碰撞更加严重的情况\n 例如: hash值为4和8的值存放在size为4的数组中,则两个元素都存放在0下标的数据中,当以2倍扩容时,size变为8,\n 8存放在0下标位置上,而4移动到下标为4的位置上(4+0),这样不仅达到了扩容的效果,还减少了hash碰撞,一举两得。\n 在hashTable中求元素位置的时候使用的方式 (n-1) & hash的方式等价于取余,但是效率更高,这要求length是2的N次方。\n\n#### HashMap是不是线程安全的?多线程的时候它可能会出现什么问题?\n 它的put,resize方法都不是线程安全的。\n 多线程的时候可能会出现并发修改异常或者丢失数据等问题。\n 一个在循环迭代,一个删除元素会引发并发修改异常,如果两个线程同时插入一个hashcode相同的元素,在插入的时候可能会丢失。java8中\n 使用在链表尾部插入元素。\n jdk1.7中在resize中因为transfer的时候逆转链表可能引起链表成环,读取的时候死循环的问题在1.8中解决了,1.8不对链表逆转。\n\n 如何让它变成线程安全的呢?\n 1. 在创建的时候把它转成线程安全的,使用Collections.SynchronizedMap(m)的方式。它的原理是什么呢?\n 2. 使用JUC中的ConcurrentHashMap来替换它。\n 3. 用hashtable\n 4. 自己使用synchronized的关键字。\n\n#### 请一下ConcurrentHashMap的原理?\n ConcurrentHashMap在1.7中是使用(分段锁) 对整个桶数组进行了分割分段(Segment),每一把锁只锁容器其中一部分数据,\n 多线程访问容器里不同数据段的数据,就不会存在锁竞争,提高并发访问率。\n Java8中使用Node数组+链表 / 红黑树, 利用CAS+Synchronized来保证并发更新的安全,\n 底层依然采用数组+链表+红黑树的存储结构。\n\n#### HashSet如何检查重复的?\n 首先是使用对象的hashcode进行判断,如果相同,再使用Equals方法进行判断,因为可能存在碰撞,所以还是需要具体的值对比一下。\n\n#### 请一下hashMap的原理?\n JDK1.8 之前 HashMap 底层是 数组和链表 结合在一起使用也就是 链表散列。HashMap 通过 key 的 \n hashCode 经过扰动函数处理过后得到 hash 值,然后通过 (n - 1) & hash 判断当前元素存放的位置\n (这里的 n 指的是数组的长度),如果当前位置存在元素的话,就判断该元素与要存入的元素的\n hash 值以及 key 是否相同,如果相同的话,直接覆盖,不相同就通过拉链法解决冲突。\n\n 所谓扰动函数指的就是 HashMap 的 hash 方法。使用 hash 方法也就是扰动函数是为了防止一些实现比较差的\n hashCode() 方法 换句话说使用扰动函数之后可以减少碰撞。\n JDK1.8之后引入了红黑树,算hashcode只使用了一次扰支函数,仍是使用(n-1) & hash的方式来判断元素的存储位置。\n 保证容量是2的N次方,方便了resize寻找元素的位置。\n ","source":"_posts/Java集合面试题.md","raw":"---\ntitle: Java集合面试题1\ndate: 2021-07-11\ncomments: true #是否可评论\ntoc: true #是否显示文章目录\ncategories: \"面试题\" #分类\ntags: #标签\n - JAVA\n - 面试题\n---\n\n#### hashmap需要扩容的时候会影响性能,为什么不一开始给一个大的初始化值呢?\n 因为它迭代的性能取决于hashmap的size和元素的数量,如果初始值设得过大,\n 则在它上面的迭代就会性能很差。\n 同样的道理,load factor也不能设得太低,太低就会更早地扩容。\n\n#### Java8的hashmap中使用了红黑树,为什么链表超过8个长度并且整个容量大于64的时候转成树呢?\n 假定数据的key的hashcode符合泊松分布,超过8个长度的时候,\n 是小于千万分之一的概率。如果达到,说明数据有严重的分布问题。\n 以防性能太差,就转成红黑树,至少可以保证logn的读写性能。\n 容量太小,转成树反而还没有链表的查询快。转换构造树的时间也很耗时。 \n\n#### Java8中HashMap为什么容量大小必须是2的N次方呢?\n 算对象所在hashmap中的位置是这么做的,(h=key.hashcode()) ^ (h>>>16) ,即使用了key生成的hashcode的高位右位十六位再和\n 原来的hashcode进行计算,这样让高位也参与了运算,这样的扰动函数,降低了碰撞的机会。\n 通过把容量保持在2的N次次,我们在扩容后,Node的新的位置要么在旧的位置要么是旧的容量+旧的位置,这样hash就不用算了。只需要一个\n 判断就可以,提高了效率。(e.hash & oldCap) == 0, 如果hash和旧的容量与之后等于0,则说明e.hash就小于oldCap,则不变。否则\n 新的位置就是旧的位置加oldCap。\n 扩容的时候只移动大约一半的数据,并且不会造成扩容之后碰撞更加严重的情况\n 例如: hash值为4和8的值存放在size为4的数组中,则两个元素都存放在0下标的数据中,当以2倍扩容时,size变为8,\n 8存放在0下标位置上,而4移动到下标为4的位置上(4+0),这样不仅达到了扩容的效果,还减少了hash碰撞,一举两得。\n 在hashTable中求元素位置的时候使用的方式 (n-1) & hash的方式等价于取余,但是效率更高,这要求length是2的N次方。\n\n#### HashMap是不是线程安全的?多线程的时候它可能会出现什么问题?\n 它的put,resize方法都不是线程安全的。\n 多线程的时候可能会出现并发修改异常或者丢失数据等问题。\n 一个在循环迭代,一个删除元素会引发并发修改异常,如果两个线程同时插入一个hashcode相同的元素,在插入的时候可能会丢失。java8中\n 使用在链表尾部插入元素。\n jdk1.7中在resize中因为transfer的时候逆转链表可能引起链表成环,读取的时候死循环的问题在1.8中解决了,1.8不对链表逆转。\n\n 如何让它变成线程安全的呢?\n 1. 在创建的时候把它转成线程安全的,使用Collections.SynchronizedMap(m)的方式。它的原理是什么呢?\n 2. 使用JUC中的ConcurrentHashMap来替换它。\n 3. 用hashtable\n 4. 自己使用synchronized的关键字。\n\n#### 请一下ConcurrentHashMap的原理?\n ConcurrentHashMap在1.7中是使用(分段锁) 对整个桶数组进行了分割分段(Segment),每一把锁只锁容器其中一部分数据,\n 多线程访问容器里不同数据段的数据,就不会存在锁竞争,提高并发访问率。\n Java8中使用Node数组+链表 / 红黑树, 利用CAS+Synchronized来保证并发更新的安全,\n 底层依然采用数组+链表+红黑树的存储结构。\n\n#### HashSet如何检查重复的?\n 首先是使用对象的hashcode进行判断,如果相同,再使用Equals方法进行判断,因为可能存在碰撞,所以还是需要具体的值对比一下。\n\n#### 请一下hashMap的原理?\n JDK1.8 之前 HashMap 底层是 数组和链表 结合在一起使用也就是 链表散列。HashMap 通过 key 的 \n hashCode 经过扰动函数处理过后得到 hash 值,然后通过 (n - 1) & hash 判断当前元素存放的位置\n (这里的 n 指的是数组的长度),如果当前位置存在元素的话,就判断该元素与要存入的元素的\n hash 值以及 key 是否相同,如果相同的话,直接覆盖,不相同就通过拉链法解决冲突。\n\n 所谓扰动函数指的就是 HashMap 的 hash 方法。使用 hash 方法也就是扰动函数是为了防止一些实现比较差的\n hashCode() 方法 换句话说使用扰动函数之后可以减少碰撞。\n JDK1.8之后引入了红黑树,算hashcode只使用了一次扰支函数,仍是使用(n-1) & hash的方式来判断元素的存储位置。\n 保证容量是2的N次方,方便了resize寻找元素的位置。\n ","slug":"Java集合面试题","published":1,"updated":"2022-04-20T09:35:50.863Z","layout":"post","photos":[],"link":"","_id":"cl27q3uuy0006n94odzu85sbo","content":"<h4 id=\"hashmap需要扩容的时候会影响性能,为什么不一开始给一个大的初始化值呢?\"><a href=\"#hashmap需要扩容的时候会影响性能,为什么不一开始给一个大的初始化值呢?\" class=\"headerlink\" title=\"hashmap需要扩容的时候会影响性能,为什么不一开始给一个大的初始化值呢?\"></a>hashmap需要扩容的时候会影响性能,为什么不一开始给一个大的初始化值呢?</h4><pre><code>因为它迭代的性能取决于hashmap的size和元素的数量,如果初始值设得过大,\n则在它上面的迭代就会性能很差。\n同样的道理,load factor也不能设得太低,太低就会更早地扩容。\n</code></pre>\n<h4 id=\"Java8的hashmap中使用了红黑树,为什么链表超过8个长度并且整个容量大于64的时候转成树呢?\"><a href=\"#Java8的hashmap中使用了红黑树,为什么链表超过8个长度并且整个容量大于64的时候转成树呢?\" class=\"headerlink\" title=\"Java8的hashmap中使用了红黑树,为什么链表超过8个长度并且整个容量大于64的时候转成树呢?\"></a>Java8的hashmap中使用了红黑树,为什么链表超过8个长度并且整个容量大于64的时候转成树呢?</h4><pre><code> 假定数据的key的hashcode符合泊松分布,超过8个长度的时候,\n 是小于千万分之一的概率。如果达到,说明数据有严重的分布问题。\n 以防性能太差,就转成红黑树,至少可以保证logn的读写性能。\n 容量太小,转成树反而还没有链表的查询快。转换构造树的时间也很耗时。 \n</code></pre>\n<h4 id=\"Java8中HashMap为什么容量大小必须是2的N次方呢?\"><a href=\"#Java8中HashMap为什么容量大小必须是2的N次方呢?\" class=\"headerlink\" title=\"Java8中HashMap为什么容量大小必须是2的N次方呢?\"></a>Java8中HashMap为什么容量大小必须是2的N次方呢?</h4><pre><code> 算对象所在hashmap中的位置是这么做的,(h=key.hashcode()) ^ (h>>>16) ,即使用了key生成的hashcode的高位右位十六位再和\n 原来的hashcode进行计算,这样让高位也参与了运算,这样的扰动函数,降低了碰撞的机会。\n 通过把容量保持在2的N次次,我们在扩容后,Node的新的位置要么在旧的位置要么是旧的容量+旧的位置,这样hash就不用算了。只需要一个\n 判断就可以,提高了效率。(e.hash & oldCap) == 0, 如果hash和旧的容量与之后等于0,则说明e.hash就小于oldCap,则不变。否则\n 新的位置就是旧的位置加oldCap。\n 扩容的时候只移动大约一半的数据,并且不会造成扩容之后碰撞更加严重的情况\n 例如: hash值为4和8的值存放在size为4的数组中,则两个元素都存放在0下标的数据中,当以2倍扩容时,size变为8,\n 8存放在0下标位置上,而4移动到下标为4的位置上(4+0),这样不仅达到了扩容的效果,还减少了hash碰撞,一举两得。\n 在hashTable中求元素位置的时候使用的方式 (n-1) & hash的方式等价于取余,但是效率更高,这要求length是2的N次方。\n</code></pre>\n<h4 id=\"HashMap是不是线程安全的?多线程的时候它可能会出现什么问题\"><a href=\"#HashMap是不是线程安全的?多线程的时候它可能会出现什么问题\" class=\"headerlink\" title=\"HashMap是不是线程安全的?多线程的时候它可能会出现什么问题?\"></a>HashMap是不是线程安全的?多线程的时候它可能会出现什么问题?</h4><pre><code> 它的put,resize方法都不是线程安全的。\n 多线程的时候可能会出现并发修改异常或者丢失数据等问题。\n 一个在循环迭代,一个删除元素会引发并发修改异常,如果两个线程同时插入一个hashcode相同的元素,在插入的时候可能会丢失。java8中\n 使用在链表尾部插入元素。\n jdk1.7中在resize中因为transfer的时候逆转链表可能引起链表成环,读取的时候死循环的问题在1.8中解决了,1.8不对链表逆转。\n\n 如何让它变成线程安全的呢?\n 1. 在创建的时候把它转成线程安全的,使用Collections.SynchronizedMap(m)的方式。它的原理是什么呢?\n 2. 使用JUC中的ConcurrentHashMap来替换它。\n 3. 用hashtable\n 4. 自己使用synchronized的关键字。\n</code></pre>\n<h4 id=\"请一下ConcurrentHashMap的原理?\"><a href=\"#请一下ConcurrentHashMap的原理?\" class=\"headerlink\" title=\"请一下ConcurrentHashMap的原理?\"></a>请一下ConcurrentHashMap的原理?</h4><pre><code>ConcurrentHashMap在1.7中是使用(分段锁) 对整个桶数组进行了分割分段(Segment),每一把锁只锁容器其中一部分数据,\n多线程访问容器里不同数据段的数据,就不会存在锁竞争,提高并发访问率。\nJava8中使用Node数组+链表 / 红黑树, 利用CAS+Synchronized来保证并发更新的安全,\n底层依然采用数组+链表+红黑树的存储结构。\n</code></pre>\n<h4 id=\"HashSet如何检查重复的?\"><a href=\"#HashSet如何检查重复的?\" class=\"headerlink\" title=\"HashSet如何检查重复的?\"></a>HashSet如何检查重复的?</h4><pre><code> 首先是使用对象的hashcode进行判断,如果相同,再使用Equals方法进行判断,因为可能存在碰撞,所以还是需要具体的值对比一下。\n</code></pre>\n<h4 id=\"请一下hashMap的原理?\"><a href=\"#请一下hashMap的原理?\" class=\"headerlink\" title=\"请一下hashMap的原理?\"></a>请一下hashMap的原理?</h4><pre><code> JDK1.8 之前 HashMap 底层是 数组和链表 结合在一起使用也就是 链表散列。HashMap 通过 key 的 \n hashCode 经过扰动函数处理过后得到 hash 值,然后通过 (n - 1) & hash 判断当前元素存放的位置\n (这里的 n 指的是数组的长度),如果当前位置存在元素的话,就判断该元素与要存入的元素的\n hash 值以及 key 是否相同,如果相同的话,直接覆盖,不相同就通过拉链法解决冲突。\n\n 所谓扰动函数指的就是 HashMap 的 hash 方法。使用 hash 方法也就是扰动函数是为了防止一些实现比较差的\n hashCode() 方法 换句话说使用扰动函数之后可以减少碰撞。\n JDK1.8之后引入了红黑树,算hashcode只使用了一次扰支函数,仍是使用(n-1) & hash的方式来判断元素的存储位置。\n 保证容量是2的N次方,方便了resize寻找元素的位置。\n</code></pre>\n","site":{"data":{}},"excerpt":"","more":"<h4 id=\"hashmap需要扩容的时候会影响性能,为什么不一开始给一个大的初始化值呢?\"><a href=\"#hashmap需要扩容的时候会影响性能,为什么不一开始给一个大的初始化值呢?\" class=\"headerlink\" title=\"hashmap需要扩容的时候会影响性能,为什么不一开始给一个大的初始化值呢?\"></a>hashmap需要扩容的时候会影响性能,为什么不一开始给一个大的初始化值呢?</h4><pre><code>因为它迭代的性能取决于hashmap的size和元素的数量,如果初始值设得过大,\n则在它上面的迭代就会性能很差。\n同样的道理,load factor也不能设得太低,太低就会更早地扩容。\n</code></pre>\n<h4 id=\"Java8的hashmap中使用了红黑树,为什么链表超过8个长度并且整个容量大于64的时候转成树呢?\"><a href=\"#Java8的hashmap中使用了红黑树,为什么链表超过8个长度并且整个容量大于64的时候转成树呢?\" class=\"headerlink\" title=\"Java8的hashmap中使用了红黑树,为什么链表超过8个长度并且整个容量大于64的时候转成树呢?\"></a>Java8的hashmap中使用了红黑树,为什么链表超过8个长度并且整个容量大于64的时候转成树呢?</h4><pre><code> 假定数据的key的hashcode符合泊松分布,超过8个长度的时候,\n 是小于千万分之一的概率。如果达到,说明数据有严重的分布问题。\n 以防性能太差,就转成红黑树,至少可以保证logn的读写性能。\n 容量太小,转成树反而还没有链表的查询快。转换构造树的时间也很耗时。 \n</code></pre>\n<h4 id=\"Java8中HashMap为什么容量大小必须是2的N次方呢?\"><a href=\"#Java8中HashMap为什么容量大小必须是2的N次方呢?\" class=\"headerlink\" title=\"Java8中HashMap为什么容量大小必须是2的N次方呢?\"></a>Java8中HashMap为什么容量大小必须是2的N次方呢?</h4><pre><code> 算对象所在hashmap中的位置是这么做的,(h=key.hashcode()) ^ (h>>>16) ,即使用了key生成的hashcode的高位右位十六位再和\n 原来的hashcode进行计算,这样让高位也参与了运算,这样的扰动函数,降低了碰撞的机会。\n 通过把容量保持在2的N次次,我们在扩容后,Node的新的位置要么在旧的位置要么是旧的容量+旧的位置,这样hash就不用算了。只需要一个\n 判断就可以,提高了效率。(e.hash & oldCap) == 0, 如果hash和旧的容量与之后等于0,则说明e.hash就小于oldCap,则不变。否则\n 新的位置就是旧的位置加oldCap。\n 扩容的时候只移动大约一半的数据,并且不会造成扩容之后碰撞更加严重的情况\n 例如: hash值为4和8的值存放在size为4的数组中,则两个元素都存放在0下标的数据中,当以2倍扩容时,size变为8,\n 8存放在0下标位置上,而4移动到下标为4的位置上(4+0),这样不仅达到了扩容的效果,还减少了hash碰撞,一举两得。\n 在hashTable中求元素位置的时候使用的方式 (n-1) & hash的方式等价于取余,但是效率更高,这要求length是2的N次方。\n</code></pre>\n<h4 id=\"HashMap是不是线程安全的?多线程的时候它可能会出现什么问题\"><a href=\"#HashMap是不是线程安全的?多线程的时候它可能会出现什么问题\" class=\"headerlink\" title=\"HashMap是不是线程安全的?多线程的时候它可能会出现什么问题?\"></a>HashMap是不是线程安全的?多线程的时候它可能会出现什么问题?</h4><pre><code> 它的put,resize方法都不是线程安全的。\n 多线程的时候可能会出现并发修改异常或者丢失数据等问题。\n 一个在循环迭代,一个删除元素会引发并发修改异常,如果两个线程同时插入一个hashcode相同的元素,在插入的时候可能会丢失。java8中\n 使用在链表尾部插入元素。\n jdk1.7中在resize中因为transfer的时候逆转链表可能引起链表成环,读取的时候死循环的问题在1.8中解决了,1.8不对链表逆转。\n\n 如何让它变成线程安全的呢?\n 1. 在创建的时候把它转成线程安全的,使用Collections.SynchronizedMap(m)的方式。它的原理是什么呢?\n 2. 使用JUC中的ConcurrentHashMap来替换它。\n 3. 用hashtable\n 4. 自己使用synchronized的关键字。\n</code></pre>\n<h4 id=\"请一下ConcurrentHashMap的原理?\"><a href=\"#请一下ConcurrentHashMap的原理?\" class=\"headerlink\" title=\"请一下ConcurrentHashMap的原理?\"></a>请一下ConcurrentHashMap的原理?</h4><pre><code>ConcurrentHashMap在1.7中是使用(分段锁) 对整个桶数组进行了分割分段(Segment),每一把锁只锁容器其中一部分数据,\n多线程访问容器里不同数据段的数据,就不会存在锁竞争,提高并发访问率。\nJava8中使用Node数组+链表 / 红黑树, 利用CAS+Synchronized来保证并发更新的安全,\n底层依然采用数组+链表+红黑树的存储结构。\n</code></pre>\n<h4 id=\"HashSet如何检查重复的?\"><a href=\"#HashSet如何检查重复的?\" class=\"headerlink\" title=\"HashSet如何检查重复的?\"></a>HashSet如何检查重复的?</h4><pre><code> 首先是使用对象的hashcode进行判断,如果相同,再使用Equals方法进行判断,因为可能存在碰撞,所以还是需要具体的值对比一下。\n</code></pre>\n<h4 id=\"请一下hashMap的原理?\"><a href=\"#请一下hashMap的原理?\" class=\"headerlink\" title=\"请一下hashMap的原理?\"></a>请一下hashMap的原理?</h4><pre><code> JDK1.8 之前 HashMap 底层是 数组和链表 结合在一起使用也就是 链表散列。HashMap 通过 key 的 \n hashCode 经过扰动函数处理过后得到 hash 值,然后通过 (n - 1) & hash 判断当前元素存放的位置\n (这里的 n 指的是数组的长度),如果当前位置存在元素的话,就判断该元素与要存入的元素的\n hash 值以及 key 是否相同,如果相同的话,直接覆盖,不相同就通过拉链法解决冲突。\n\n 所谓扰动函数指的就是 HashMap 的 hash 方法。使用 hash 方法也就是扰动函数是为了防止一些实现比较差的\n hashCode() 方法 换句话说使用扰动函数之后可以减少碰撞。\n JDK1.8之后引入了红黑树,算hashcode只使用了一次扰支函数,仍是使用(n-1) & hash的方式来判断元素的存储位置。\n 保证容量是2的N次方,方便了resize寻找元素的位置。\n</code></pre>\n"},{"title":"大数据之Hbase Part1","date":"2021-07-25T16:00:00.000Z","comments":1,"toc":true,"_content":"##### Hbase的特性\n> 强一致性读写: HBase 不是 \"最终一致性(eventually consistent)\" 数据存储. \n 这让它很适合高速计数聚合类任务。\n 自动分片(Automatic sharding): HBase 表通过region分布在集群中。\n 数据增长时,region会自动分割并重新分布。\n RegionServer 自动故障转移\n Hadoop/HDFS 集成: HBase 支持本机外HDFS 作为它的分布式文件系统。\n MapReduce: HBase 通过MapReduce支持大并发处理, HBase 可以同时做源和目标.\n Java 客户端 API: HBase 支持易于使用的 Java API 进行编程访问.\n Thrift/REST API: HBase 也支持Thrift 和 REST 作为非Java 前端.\n Block Cache 和 Bloom Filters: 对于大容量查询优化, \n HBase支持 Block Cache 和 Bloom Filters。\n 运维管理: HBase提供内置网页用于运维视角和JMX 度量.\n##### Hbase的感性认识\nhbase实操练习 可以看官方文档,先操作一遍,增强感性的认识\n在HBase中,namespace命名空间指对一组表的逻辑分组,类似RDBMS中的database,方便对表在业务上划分。\nhelp 'namespace' 可以打出来各种帮助命令,方便使用 还有help 'ddl',help 'dml'等命令。\nHbase一行数据可以类比RDBMS中的一行,表示一个实体,一个实体的所有信息都可以放到一行中。\n在hbase中,操作的时候对每一个列操作,所以多次操作的时候,指定同一个rowkey,表示的是同一个对象。\n需求:创建一个表,并写入一条记录,name:student.evan,age:30,phone:130xxx\n在Hbase中操作如下。\n``` bash\ncreate_namespace 'evan' #创建一个自己用的namespace\nlist_namespace #列出来查看一下\ncreate 'evan:test1', 'cf1' #在这个namespace下创建一个表,只有一个列族 cf1\nput 'evan:test1', 'row1', 'cf1:name', 'student.evan' #给表evan:test1插入一条数据,行键row1,列名为name\nput 'evan:test1', 'row1', 'cf1:age', '30' #给表evan:test1插入一条数据,行键row1,列名为name,值30\nput 'evan:test1', 'row1', 'cf1:phone', '130xxx' #给表evan:test1插入一条数据,行键row1,列名为name,值130xxx\nscan 'evan:test1' #查看表中的数据\n```\n##### 逻辑模型和物理模型\n**逻辑模型**\nHbase是google开放的论文BigTable的开源实现,它原定的使命是用来存储网络爬虫抓取的网站数据,用于搜索引擎使用。\n它表中的每一行可以有不同的列,它不需要指定列,需要提前指定列族。它存储的数据没有数据类型,都是存储成字节数组。\n行有行键,每一行的行键是唯一的,相同行键的插入操作被认为是同一行的操作。\n列中的值可以有多个版本,有时间戳来标识不同的版本。\n可以把Hbase理解为无限嵌套的HashMap。\n**物理模型**\nHbase的物理存储是按列族存放的,一个列族的数据会被同一个Region管理,物理上放在一起。\n空白的Cell在物理上是不存储的,返回是空值。\n##### 数据模型的重要概念\nHbase中原生不支持join,它是列存储的,可以把大量的信息存储到一个表,传统的数据库表设计规范不适用于它。\nHbase中的表相对比较少,一个表中可以包括原有RDBMS中多个表的数据,通常是大宽表。\n可以把它理解为kv数据库。\nHbase没有列定义,没有字段类型,这就是它被称为无模式数据库的原因。\n**行键(rowkey)**\n行键是不可分割的字节数组,是按字典顺序由低到高存储在表中的。\n为了高效检索数据,需要仔细设计rowkey以提高查询性能,避免热点分布。\n**列族**\n列族是列的集合,创建表的时候只需要先定义好列族。在写入数据的时候指定列即可。\n在物理上,一个列族的成员在文件系统上是存储在一起的,存储优化是针对列族级别的。\n创建表的时候,至少需要指定一个列族,新的列族可以后面动态加入,但需要先disable表。\n不要创建过多的列族,因为跨列族的访问是非常低效的。因为是不同的物理存储位置。\n**单元格(cell)**\n单元格由rowkey,column family,col,timestamp唯一确定。它的内容是不可分割的字节数组。\n每一个单元格都保存着同一份数据的不同版本,不同时间版本按照时间顺序倒序排序(最新在最新面)。\nTTL(time to live),用于设置单元格的生存周期,如果过期,则会被删除。\n早于指定TTL值的数据会在下一次大合并时被删除。\n##### 访问方式\n1. 可以使用hbase shell客户端访问。\n2. 如果hbase rest网关启动了,就可以使用curl 命令来访问rest api进行CRUD。\n3. 使用Java api /python api/ mapreduce来访问。\n4. 使用phoenix 写sql来查询hbase\n Phoenix查询引擎会将sql查询转换成一个或多个hbase scan,并行执行以生成标准的\n jdbc结果集。它支持DDL,DML,紧跟ANSI sql标准。\n5. Hbase可以整合为hive的表,用hive sql来访问\n``` sql\n CREATE TABLE hbase_table_1( key int, value string)\n STORED BY 'org.apache.hadoop.hive.hbase.HBaseStorageHandler'\n WITH SERDEPROPERTIES (\n \"hbase.columns.mapping\"=\"cf:c1\",\n \"hbase.table.name\"=\"hbase_table_1\"\n )\n```\n##### 整体架构\n**核心组件**\n客户端和zookeeper,主节点HMaster和Region节点RegionServer。\nzk负责多HMaster的选举,服务器之间的状态同步。存储Hbase元数据信息,实时监控RegionSever,\n存储所有Region的寻址入口。\nHMaster主要负责 Table和Region的管理工作:\n1. 管理用户对Table的增删改查\n2. 管理RegionSever的负载均衡,调整Region分布\n3. Region的重分配和迁移工作\nRegionServer 主要负责响应客户的IO请求,向文件系统读写数据。\n**目录表(Catalog Tables)**\n目录表 -ROOT- 和 .META. 作为 HBase 表存在。他们被HBase shell的 list 命令过滤掉了,\n但他们和其他表一样存在。 \n -ROOT- 保存 .META. 表存在哪里的踪迹。\n.META. 保存系统中所有region列表。在哪个regsion server存放。\n**客户端**\nHBase客户端的 HTable类负责寻找相应的RegionServers来处理行。他是先查询 .META. 和 -ROOT 目录表。\n然后再确定region的位置。定位到所需要的区域后,客户端会直接去访问相应的region(不经过master),\n发起读写请求。这些信息会缓存在客户端,这样就不用每发起一个请求就去查一下。\n**存储文件**\nHstore是Hbase存储的核心,它由两部分组成,memstore和StoreFile。\n用户写入的数据会首先放到MemStore当中,满了以后会flush到StoreFile(Hfile),StoreFiles文件数增长到\n一定的阈值,会触发Compact操作,将多个Storefiles合并成一个StoreFile,在Major Compact中会进行版本\n合并和数据删除。由此可知,Hbase只增加数据,所有的更新和删除都是在Compact中进行的,这保证了它的高速写。\n单个StoreFile大小达到一定阈值后,会触发Split操作,同时会把当前Region分裂成两个Region,父Region下线,\n新分裂的两个子Region会被分配到相应的HRgionServer上。\n**Hlog和数据恢复**\n每一个RegionServer中都有一个Hlog对象,Hlog对象是一个实现了WAL的类,用户的操作在写入memStore的同时,\n会写入到Hlog文件中,它会定期滚动出新,删除旧的文件。\n当RegionServer意外中止后,Hmaster会通过zk感知到,首先处理遗留的Hlog文件,将其中不同Region的Log数据\n进行拆分,分别放到相应的Region目录下,然后再将失效的Region重新分配。\n领取到这些Region的RegionServer在加载Region的过程中,会发现有历史Hlog需要处理,会将Hlog中的数据回放到\nMemStore中,然后flush到StoreFile,完成数据恢复。\n","source":"_posts/Part1.md","raw":"---\ntitle: 大数据之Hbase Part1\ndate: 2021-07-26\ncomments: true #是否可评论\ntoc: true #是否显示文章目录\ncategories: \"大数据\" #分类\ntags: #标签\n - Hbase\n - 大数据\n---\n##### Hbase的特性\n> 强一致性读写: HBase 不是 \"最终一致性(eventually consistent)\" 数据存储. \n 这让它很适合高速计数聚合类任务。\n 自动分片(Automatic sharding): HBase 表通过region分布在集群中。\n 数据增长时,region会自动分割并重新分布。\n RegionServer 自动故障转移\n Hadoop/HDFS 集成: HBase 支持本机外HDFS 作为它的分布式文件系统。\n MapReduce: HBase 通过MapReduce支持大并发处理, HBase 可以同时做源和目标.\n Java 客户端 API: HBase 支持易于使用的 Java API 进行编程访问.\n Thrift/REST API: HBase 也支持Thrift 和 REST 作为非Java 前端.\n Block Cache 和 Bloom Filters: 对于大容量查询优化, \n HBase支持 Block Cache 和 Bloom Filters。\n 运维管理: HBase提供内置网页用于运维视角和JMX 度量.\n##### Hbase的感性认识\nhbase实操练习 可以看官方文档,先操作一遍,增强感性的认识\n在HBase中,namespace命名空间指对一组表的逻辑分组,类似RDBMS中的database,方便对表在业务上划分。\nhelp 'namespace' 可以打出来各种帮助命令,方便使用 还有help 'ddl',help 'dml'等命令。\nHbase一行数据可以类比RDBMS中的一行,表示一个实体,一个实体的所有信息都可以放到一行中。\n在hbase中,操作的时候对每一个列操作,所以多次操作的时候,指定同一个rowkey,表示的是同一个对象。\n需求:创建一个表,并写入一条记录,name:student.evan,age:30,phone:130xxx\n在Hbase中操作如下。\n``` bash\ncreate_namespace 'evan' #创建一个自己用的namespace\nlist_namespace #列出来查看一下\ncreate 'evan:test1', 'cf1' #在这个namespace下创建一个表,只有一个列族 cf1\nput 'evan:test1', 'row1', 'cf1:name', 'student.evan' #给表evan:test1插入一条数据,行键row1,列名为name\nput 'evan:test1', 'row1', 'cf1:age', '30' #给表evan:test1插入一条数据,行键row1,列名为name,值30\nput 'evan:test1', 'row1', 'cf1:phone', '130xxx' #给表evan:test1插入一条数据,行键row1,列名为name,值130xxx\nscan 'evan:test1' #查看表中的数据\n```\n##### 逻辑模型和物理模型\n**逻辑模型**\nHbase是google开放的论文BigTable的开源实现,它原定的使命是用来存储网络爬虫抓取的网站数据,用于搜索引擎使用。\n它表中的每一行可以有不同的列,它不需要指定列,需要提前指定列族。它存储的数据没有数据类型,都是存储成字节数组。\n行有行键,每一行的行键是唯一的,相同行键的插入操作被认为是同一行的操作。\n列中的值可以有多个版本,有时间戳来标识不同的版本。\n可以把Hbase理解为无限嵌套的HashMap。\n**物理模型**\nHbase的物理存储是按列族存放的,一个列族的数据会被同一个Region管理,物理上放在一起。\n空白的Cell在物理上是不存储的,返回是空值。\n##### 数据模型的重要概念\nHbase中原生不支持join,它是列存储的,可以把大量的信息存储到一个表,传统的数据库表设计规范不适用于它。\nHbase中的表相对比较少,一个表中可以包括原有RDBMS中多个表的数据,通常是大宽表。\n可以把它理解为kv数据库。\nHbase没有列定义,没有字段类型,这就是它被称为无模式数据库的原因。\n**行键(rowkey)**\n行键是不可分割的字节数组,是按字典顺序由低到高存储在表中的。\n为了高效检索数据,需要仔细设计rowkey以提高查询性能,避免热点分布。\n**列族**\n列族是列的集合,创建表的时候只需要先定义好列族。在写入数据的时候指定列即可。\n在物理上,一个列族的成员在文件系统上是存储在一起的,存储优化是针对列族级别的。\n创建表的时候,至少需要指定一个列族,新的列族可以后面动态加入,但需要先disable表。\n不要创建过多的列族,因为跨列族的访问是非常低效的。因为是不同的物理存储位置。\n**单元格(cell)**\n单元格由rowkey,column family,col,timestamp唯一确定。它的内容是不可分割的字节数组。\n每一个单元格都保存着同一份数据的不同版本,不同时间版本按照时间顺序倒序排序(最新在最新面)。\nTTL(time to live),用于设置单元格的生存周期,如果过期,则会被删除。\n早于指定TTL值的数据会在下一次大合并时被删除。\n##### 访问方式\n1. 可以使用hbase shell客户端访问。\n2. 如果hbase rest网关启动了,就可以使用curl 命令来访问rest api进行CRUD。\n3. 使用Java api /python api/ mapreduce来访问。\n4. 使用phoenix 写sql来查询hbase\n Phoenix查询引擎会将sql查询转换成一个或多个hbase scan,并行执行以生成标准的\n jdbc结果集。它支持DDL,DML,紧跟ANSI sql标准。\n5. Hbase可以整合为hive的表,用hive sql来访问\n``` sql\n CREATE TABLE hbase_table_1( key int, value string)\n STORED BY 'org.apache.hadoop.hive.hbase.HBaseStorageHandler'\n WITH SERDEPROPERTIES (\n \"hbase.columns.mapping\"=\"cf:c1\",\n \"hbase.table.name\"=\"hbase_table_1\"\n )\n```\n##### 整体架构\n**核心组件**\n客户端和zookeeper,主节点HMaster和Region节点RegionServer。\nzk负责多HMaster的选举,服务器之间的状态同步。存储Hbase元数据信息,实时监控RegionSever,\n存储所有Region的寻址入口。\nHMaster主要负责 Table和Region的管理工作:\n1. 管理用户对Table的增删改查\n2. 管理RegionSever的负载均衡,调整Region分布\n3. Region的重分配和迁移工作\nRegionServer 主要负责响应客户的IO请求,向文件系统读写数据。\n**目录表(Catalog Tables)**\n目录表 -ROOT- 和 .META. 作为 HBase 表存在。他们被HBase shell的 list 命令过滤掉了,\n但他们和其他表一样存在。 \n -ROOT- 保存 .META. 表存在哪里的踪迹。\n.META. 保存系统中所有region列表。在哪个regsion server存放。\n**客户端**\nHBase客户端的 HTable类负责寻找相应的RegionServers来处理行。他是先查询 .META. 和 -ROOT 目录表。\n然后再确定region的位置。定位到所需要的区域后,客户端会直接去访问相应的region(不经过master),\n发起读写请求。这些信息会缓存在客户端,这样就不用每发起一个请求就去查一下。\n**存储文件**\nHstore是Hbase存储的核心,它由两部分组成,memstore和StoreFile。\n用户写入的数据会首先放到MemStore当中,满了以后会flush到StoreFile(Hfile),StoreFiles文件数增长到\n一定的阈值,会触发Compact操作,将多个Storefiles合并成一个StoreFile,在Major Compact中会进行版本\n合并和数据删除。由此可知,Hbase只增加数据,所有的更新和删除都是在Compact中进行的,这保证了它的高速写。\n单个StoreFile大小达到一定阈值后,会触发Split操作,同时会把当前Region分裂成两个Region,父Region下线,\n新分裂的两个子Region会被分配到相应的HRgionServer上。\n**Hlog和数据恢复**\n每一个RegionServer中都有一个Hlog对象,Hlog对象是一个实现了WAL的类,用户的操作在写入memStore的同时,\n会写入到Hlog文件中,它会定期滚动出新,删除旧的文件。\n当RegionServer意外中止后,Hmaster会通过zk感知到,首先处理遗留的Hlog文件,将其中不同Region的Log数据\n进行拆分,分别放到相应的Region目录下,然后再将失效的Region重新分配。\n领取到这些Region的RegionServer在加载Region的过程中,会发现有历史Hlog需要处理,会将Hlog中的数据回放到\nMemStore中,然后flush到StoreFile,完成数据恢复。\n","slug":"Part1","published":1,"updated":"2022-04-20T09:35:50.863Z","layout":"post","photos":[],"link":"","_id":"cl27q3uuy0007n94o5wthbwbf","content":"<h5 id=\"Hbase的特性\"><a href=\"#Hbase的特性\" class=\"headerlink\" title=\"Hbase的特性\"></a>Hbase的特性</h5><blockquote>\n<p> 强一致性读写: HBase 不是 “最终一致性(eventually consistent)” 数据存储.<br> 这让它很适合高速计数聚合类任务。<br> 自动分片(Automatic sharding): HBase 表通过region分布在集群中。<br> 数据增长时,region会自动分割并重新分布。<br> RegionServer 自动故障转移<br> Hadoop/HDFS 集成: HBase 支持本机外HDFS 作为它的分布式文件系统。<br> MapReduce: HBase 通过MapReduce支持大并发处理, HBase 可以同时做源和目标.<br> Java 客户端 API: HBase 支持易于使用的 Java API 进行编程访问.<br> Thrift/REST API: HBase 也支持Thrift 和 REST 作为非Java 前端.<br> Block Cache 和 Bloom Filters: 对于大容量查询优化,<br> HBase支持 Block Cache 和 Bloom Filters。<br> 运维管理: HBase提供内置网页用于运维视角和JMX 度量.</p>\n</blockquote>\n<h5 id=\"Hbase的感性认识\"><a href=\"#Hbase的感性认识\" class=\"headerlink\" title=\"Hbase的感性认识\"></a>Hbase的感性认识</h5><p>hbase实操练习 可以看官方文档,先操作一遍,增强感性的认识<br>在HBase中,namespace命名空间指对一组表的逻辑分组,类似RDBMS中的database,方便对表在业务上划分。<br>help ‘namespace’ 可以打出来各种帮助命令,方便使用 还有help ‘ddl’,help ‘dml’等命令。<br>Hbase一行数据可以类比RDBMS中的一行,表示一个实体,一个实体的所有信息都可以放到一行中。<br>在hbase中,操作的时候对每一个列操作,所以多次操作的时候,指定同一个rowkey,表示的是同一个对象。<br>需求:创建一个表,并写入一条记录,name:student.evan,age:30,phone:130xxx<br>在Hbase中操作如下。</p>\n<figure class=\"highlight bash\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">create_namespace <span class=\"string\">'evan'</span> <span class=\"comment\">#创建一个自己用的namespace</span></span><br><span class=\"line\">list_namespace <span class=\"comment\">#列出来查看一下</span></span><br><span class=\"line\">create <span class=\"string\">'evan:test1'</span>, <span class=\"string\">'cf1'</span> <span class=\"comment\">#在这个namespace下创建一个表,只有一个列族 cf1</span></span><br><span class=\"line\">put <span class=\"string\">'evan:test1'</span>, <span class=\"string\">'row1'</span>, <span class=\"string\">'cf1:name'</span>, <span class=\"string\">'student.evan'</span> <span class=\"comment\">#给表evan:test1插入一条数据,行键row1,列名为name</span></span><br><span class=\"line\">put <span class=\"string\">'evan:test1'</span>, <span class=\"string\">'row1'</span>, <span class=\"string\">'cf1:age'</span>, <span class=\"string\">'30'</span> <span class=\"comment\">#给表evan:test1插入一条数据,行键row1,列名为name,值30</span></span><br><span class=\"line\">put <span class=\"string\">'evan:test1'</span>, <span class=\"string\">'row1'</span>, <span class=\"string\">'cf1:phone'</span>, <span class=\"string\">'130xxx'</span> <span class=\"comment\">#给表evan:test1插入一条数据,行键row1,列名为name,值130xxx</span></span><br><span class=\"line\">scan <span class=\"string\">'evan:test1'</span> <span class=\"comment\">#查看表中的数据</span></span><br></pre></td></tr></table></figure>\n<h5 id=\"逻辑模型和物理模型\"><a href=\"#逻辑模型和物理模型\" class=\"headerlink\" title=\"逻辑模型和物理模型\"></a>逻辑模型和物理模型</h5><p><strong>逻辑模型</strong><br>Hbase是google开放的论文BigTable的开源实现,它原定的使命是用来存储网络爬虫抓取的网站数据,用于搜索引擎使用。<br>它表中的每一行可以有不同的列,它不需要指定列,需要提前指定列族。它存储的数据没有数据类型,都是存储成字节数组。<br>行有行键,每一行的行键是唯一的,相同行键的插入操作被认为是同一行的操作。<br>列中的值可以有多个版本,有时间戳来标识不同的版本。<br>可以把Hbase理解为无限嵌套的HashMap。<br><strong>物理模型</strong><br>Hbase的物理存储是按列族存放的,一个列族的数据会被同一个Region管理,物理上放在一起。<br>空白的Cell在物理上是不存储的,返回是空值。</p>\n<h5 id=\"数据模型的重要概念\"><a href=\"#数据模型的重要概念\" class=\"headerlink\" title=\"数据模型的重要概念\"></a>数据模型的重要概念</h5><p>Hbase中原生不支持join,它是列存储的,可以把大量的信息存储到一个表,传统的数据库表设计规范不适用于它。<br>Hbase中的表相对比较少,一个表中可以包括原有RDBMS中多个表的数据,通常是大宽表。<br>可以把它理解为kv数据库。<br>Hbase没有列定义,没有字段类型,这就是它被称为无模式数据库的原因。<br><strong>行键(rowkey)</strong><br>行键是不可分割的字节数组,是按字典顺序由低到高存储在表中的。<br>为了高效检索数据,需要仔细设计rowkey以提高查询性能,避免热点分布。<br><strong>列族</strong><br>列族是列的集合,创建表的时候只需要先定义好列族。在写入数据的时候指定列即可。<br>在物理上,一个列族的成员在文件系统上是存储在一起的,存储优化是针对列族级别的。<br>创建表的时候,至少需要指定一个列族,新的列族可以后面动态加入,但需要先disable表。<br>不要创建过多的列族,因为跨列族的访问是非常低效的。因为是不同的物理存储位置。<br><strong>单元格(cell)</strong><br>单元格由rowkey,column family,col,timestamp唯一确定。它的内容是不可分割的字节数组。<br>每一个单元格都保存着同一份数据的不同版本,不同时间版本按照时间顺序倒序排序(最新在最新面)。<br>TTL(time to live),用于设置单元格的生存周期,如果过期,则会被删除。<br>早于指定TTL值的数据会在下一次大合并时被删除。</p>\n<h5 id=\"访问方式\"><a href=\"#访问方式\" class=\"headerlink\" title=\"访问方式\"></a>访问方式</h5><ol>\n<li>可以使用hbase shell客户端访问。</li>\n<li>如果hbase rest网关启动了,就可以使用curl 命令来访问rest api进行CRUD。</li>\n<li>使用Java api /python api/ mapreduce来访问。</li>\n<li>使用phoenix 写sql来查询hbase<br>Phoenix查询引擎会将sql查询转换成一个或多个hbase scan,并行执行以生成标准的<br>jdbc结果集。它支持DDL,DML,紧跟ANSI sql标准。</li>\n<li>Hbase可以整合为hive的表,用hive sql来访问<figure class=\"highlight sql\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"keyword\">CREATE</span> <span class=\"keyword\">TABLE</span> hbase_table_1( key <span class=\"type\">int</span>, <span class=\"keyword\">value</span> string)</span><br><span class=\"line\">STORED <span class=\"keyword\">BY</span> <span class=\"string\">'org.apache.hadoop.hive.hbase.HBaseStorageHandler'</span></span><br><span class=\"line\"><span class=\"keyword\">WITH</span> SERDEPROPERTIES (</span><br><span class=\"line\"> "hbase.columns.mapping"<span class=\"operator\">=</span>"cf:c1",</span><br><span class=\"line\"> "hbase.table.name"<span class=\"operator\">=</span>"hbase_table_1"</span><br><span class=\"line\">)</span><br></pre></td></tr></table></figure>\n<h5 id=\"整体架构\"><a href=\"#整体架构\" class=\"headerlink\" title=\"整体架构\"></a>整体架构</h5></li>\n</ol>\n<p><strong>核心组件</strong><br>客户端和zookeeper,主节点HMaster和Region节点RegionServer。<br>zk负责多HMaster的选举,服务器之间的状态同步。存储Hbase元数据信息,实时监控RegionSever,<br>存储所有Region的寻址入口。<br>HMaster主要负责 Table和Region的管理工作:</p>\n<ol>\n<li>管理用户对Table的增删改查</li>\n<li>管理RegionSever的负载均衡,调整Region分布</li>\n<li>Region的重分配和迁移工作<br>RegionServer 主要负责响应客户的IO请求,向文件系统读写数据。</li>\n</ol>\n<p><strong>目录表(Catalog Tables)</strong><br>目录表 -ROOT- 和 .META. 作为 HBase 表存在。他们被HBase shell的 list 命令过滤掉了,<br>但他们和其他表一样存在。<br> -ROOT- 保存 .META. 表存在哪里的踪迹。<br>.META. 保存系统中所有region列表。在哪个regsion server存放。<br><strong>客户端</strong><br>HBase客户端的 HTable类负责寻找相应的RegionServers来处理行。他是先查询 .META. 和 -ROOT 目录表。<br>然后再确定region的位置。定位到所需要的区域后,客户端会直接去访问相应的region(不经过master),<br>发起读写请求。这些信息会缓存在客户端,这样就不用每发起一个请求就去查一下。<br><strong>存储文件</strong><br>Hstore是Hbase存储的核心,它由两部分组成,memstore和StoreFile。<br>用户写入的数据会首先放到MemStore当中,满了以后会flush到StoreFile(Hfile),StoreFiles文件数增长到<br>一定的阈值,会触发Compact操作,将多个Storefiles合并成一个StoreFile,在Major Compact中会进行版本<br>合并和数据删除。由此可知,Hbase只增加数据,所有的更新和删除都是在Compact中进行的,这保证了它的高速写。<br>单个StoreFile大小达到一定阈值后,会触发Split操作,同时会把当前Region分裂成两个Region,父Region下线,<br>新分裂的两个子Region会被分配到相应的HRgionServer上。<br><strong>Hlog和数据恢复</strong><br>每一个RegionServer中都有一个Hlog对象,Hlog对象是一个实现了WAL的类,用户的操作在写入memStore的同时,<br>会写入到Hlog文件中,它会定期滚动出新,删除旧的文件。<br>当RegionServer意外中止后,Hmaster会通过zk感知到,首先处理遗留的Hlog文件,将其中不同Region的Log数据<br>进行拆分,分别放到相应的Region目录下,然后再将失效的Region重新分配。<br>领取到这些Region的RegionServer在加载Region的过程中,会发现有历史Hlog需要处理,会将Hlog中的数据回放到<br>MemStore中,然后flush到StoreFile,完成数据恢复。</p>\n","site":{"data":{}},"excerpt":"","more":"<h5 id=\"Hbase的特性\"><a href=\"#Hbase的特性\" class=\"headerlink\" title=\"Hbase的特性\"></a>Hbase的特性</h5><blockquote>\n<p> 强一致性读写: HBase 不是 “最终一致性(eventually consistent)” 数据存储.<br> 这让它很适合高速计数聚合类任务。<br> 自动分片(Automatic sharding): HBase 表通过region分布在集群中。<br> 数据增长时,region会自动分割并重新分布。<br> RegionServer 自动故障转移<br> Hadoop/HDFS 集成: HBase 支持本机外HDFS 作为它的分布式文件系统。<br> MapReduce: HBase 通过MapReduce支持大并发处理, HBase 可以同时做源和目标.<br> Java 客户端 API: HBase 支持易于使用的 Java API 进行编程访问.<br> Thrift/REST API: HBase 也支持Thrift 和 REST 作为非Java 前端.<br> Block Cache 和 Bloom Filters: 对于大容量查询优化,<br> HBase支持 Block Cache 和 Bloom Filters。<br> 运维管理: HBase提供内置网页用于运维视角和JMX 度量.</p>\n</blockquote>\n<h5 id=\"Hbase的感性认识\"><a href=\"#Hbase的感性认识\" class=\"headerlink\" title=\"Hbase的感性认识\"></a>Hbase的感性认识</h5><p>hbase实操练习 可以看官方文档,先操作一遍,增强感性的认识<br>在HBase中,namespace命名空间指对一组表的逻辑分组,类似RDBMS中的database,方便对表在业务上划分。<br>help ‘namespace’ 可以打出来各种帮助命令,方便使用 还有help ‘ddl’,help ‘dml’等命令。<br>Hbase一行数据可以类比RDBMS中的一行,表示一个实体,一个实体的所有信息都可以放到一行中。<br>在hbase中,操作的时候对每一个列操作,所以多次操作的时候,指定同一个rowkey,表示的是同一个对象。<br>需求:创建一个表,并写入一条记录,name:student.evan,age:30,phone:130xxx<br>在Hbase中操作如下。</p>\n<figure class=\"highlight bash\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">create_namespace <span class=\"string\">'evan'</span> <span class=\"comment\">#创建一个自己用的namespace</span></span><br><span class=\"line\">list_namespace <span class=\"comment\">#列出来查看一下</span></span><br><span class=\"line\">create <span class=\"string\">'evan:test1'</span>, <span class=\"string\">'cf1'</span> <span class=\"comment\">#在这个namespace下创建一个表,只有一个列族 cf1</span></span><br><span class=\"line\">put <span class=\"string\">'evan:test1'</span>, <span class=\"string\">'row1'</span>, <span class=\"string\">'cf1:name'</span>, <span class=\"string\">'student.evan'</span> <span class=\"comment\">#给表evan:test1插入一条数据,行键row1,列名为name</span></span><br><span class=\"line\">put <span class=\"string\">'evan:test1'</span>, <span class=\"string\">'row1'</span>, <span class=\"string\">'cf1:age'</span>, <span class=\"string\">'30'</span> <span class=\"comment\">#给表evan:test1插入一条数据,行键row1,列名为name,值30</span></span><br><span class=\"line\">put <span class=\"string\">'evan:test1'</span>, <span class=\"string\">'row1'</span>, <span class=\"string\">'cf1:phone'</span>, <span class=\"string\">'130xxx'</span> <span class=\"comment\">#给表evan:test1插入一条数据,行键row1,列名为name,值130xxx</span></span><br><span class=\"line\">scan <span class=\"string\">'evan:test1'</span> <span class=\"comment\">#查看表中的数据</span></span><br></pre></td></tr></table></figure>\n<h5 id=\"逻辑模型和物理模型\"><a href=\"#逻辑模型和物理模型\" class=\"headerlink\" title=\"逻辑模型和物理模型\"></a>逻辑模型和物理模型</h5><p><strong>逻辑模型</strong><br>Hbase是google开放的论文BigTable的开源实现,它原定的使命是用来存储网络爬虫抓取的网站数据,用于搜索引擎使用。<br>它表中的每一行可以有不同的列,它不需要指定列,需要提前指定列族。它存储的数据没有数据类型,都是存储成字节数组。<br>行有行键,每一行的行键是唯一的,相同行键的插入操作被认为是同一行的操作。<br>列中的值可以有多个版本,有时间戳来标识不同的版本。<br>可以把Hbase理解为无限嵌套的HashMap。<br><strong>物理模型</strong><br>Hbase的物理存储是按列族存放的,一个列族的数据会被同一个Region管理,物理上放在一起。<br>空白的Cell在物理上是不存储的,返回是空值。</p>\n<h5 id=\"数据模型的重要概念\"><a href=\"#数据模型的重要概念\" class=\"headerlink\" title=\"数据模型的重要概念\"></a>数据模型的重要概念</h5><p>Hbase中原生不支持join,它是列存储的,可以把大量的信息存储到一个表,传统的数据库表设计规范不适用于它。<br>Hbase中的表相对比较少,一个表中可以包括原有RDBMS中多个表的数据,通常是大宽表。<br>可以把它理解为kv数据库。<br>Hbase没有列定义,没有字段类型,这就是它被称为无模式数据库的原因。<br><strong>行键(rowkey)</strong><br>行键是不可分割的字节数组,是按字典顺序由低到高存储在表中的。<br>为了高效检索数据,需要仔细设计rowkey以提高查询性能,避免热点分布。<br><strong>列族</strong><br>列族是列的集合,创建表的时候只需要先定义好列族。在写入数据的时候指定列即可。<br>在物理上,一个列族的成员在文件系统上是存储在一起的,存储优化是针对列族级别的。<br>创建表的时候,至少需要指定一个列族,新的列族可以后面动态加入,但需要先disable表。<br>不要创建过多的列族,因为跨列族的访问是非常低效的。因为是不同的物理存储位置。<br><strong>单元格(cell)</strong><br>单元格由rowkey,column family,col,timestamp唯一确定。它的内容是不可分割的字节数组。<br>每一个单元格都保存着同一份数据的不同版本,不同时间版本按照时间顺序倒序排序(最新在最新面)。<br>TTL(time to live),用于设置单元格的生存周期,如果过期,则会被删除。<br>早于指定TTL值的数据会在下一次大合并时被删除。</p>\n<h5 id=\"访问方式\"><a href=\"#访问方式\" class=\"headerlink\" title=\"访问方式\"></a>访问方式</h5><ol>\n<li>可以使用hbase shell客户端访问。</li>\n<li>如果hbase rest网关启动了,就可以使用curl 命令来访问rest api进行CRUD。</li>\n<li>使用Java api /python api/ mapreduce来访问。</li>\n<li>使用phoenix 写sql来查询hbase<br>Phoenix查询引擎会将sql查询转换成一个或多个hbase scan,并行执行以生成标准的<br>jdbc结果集。它支持DDL,DML,紧跟ANSI sql标准。</li>\n<li>Hbase可以整合为hive的表,用hive sql来访问<figure class=\"highlight sql\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"keyword\">CREATE</span> <span class=\"keyword\">TABLE</span> hbase_table_1( key <span class=\"type\">int</span>, <span class=\"keyword\">value</span> string)</span><br><span class=\"line\">STORED <span class=\"keyword\">BY</span> <span class=\"string\">'org.apache.hadoop.hive.hbase.HBaseStorageHandler'</span></span><br><span class=\"line\"><span class=\"keyword\">WITH</span> SERDEPROPERTIES (</span><br><span class=\"line\"> "hbase.columns.mapping"<span class=\"operator\">=</span>"cf:c1",</span><br><span class=\"line\"> "hbase.table.name"<span class=\"operator\">=</span>"hbase_table_1"</span><br><span class=\"line\">)</span><br></pre></td></tr></table></figure>\n<h5 id=\"整体架构\"><a href=\"#整体架构\" class=\"headerlink\" title=\"整体架构\"></a>整体架构</h5></li>\n</ol>\n<p><strong>核心组件</strong><br>客户端和zookeeper,主节点HMaster和Region节点RegionServer。<br>zk负责多HMaster的选举,服务器之间的状态同步。存储Hbase元数据信息,实时监控RegionSever,<br>存储所有Region的寻址入口。<br>HMaster主要负责 Table和Region的管理工作:</p>\n<ol>\n<li>管理用户对Table的增删改查</li>\n<li>管理RegionSever的负载均衡,调整Region分布</li>\n<li>Region的重分配和迁移工作<br>RegionServer 主要负责响应客户的IO请求,向文件系统读写数据。</li>\n</ol>\n<p><strong>目录表(Catalog Tables)</strong><br>目录表 -ROOT- 和 .META. 作为 HBase 表存在。他们被HBase shell的 list 命令过滤掉了,<br>但他们和其他表一样存在。<br> -ROOT- 保存 .META. 表存在哪里的踪迹。<br>.META. 保存系统中所有region列表。在哪个regsion server存放。<br><strong>客户端</strong><br>HBase客户端的 HTable类负责寻找相应的RegionServers来处理行。他是先查询 .META. 和 -ROOT 目录表。<br>然后再确定region的位置。定位到所需要的区域后,客户端会直接去访问相应的region(不经过master),<br>发起读写请求。这些信息会缓存在客户端,这样就不用每发起一个请求就去查一下。<br><strong>存储文件</strong><br>Hstore是Hbase存储的核心,它由两部分组成,memstore和StoreFile。<br>用户写入的数据会首先放到MemStore当中,满了以后会flush到StoreFile(Hfile),StoreFiles文件数增长到<br>一定的阈值,会触发Compact操作,将多个Storefiles合并成一个StoreFile,在Major Compact中会进行版本<br>合并和数据删除。由此可知,Hbase只增加数据,所有的更新和删除都是在Compact中进行的,这保证了它的高速写。<br>单个StoreFile大小达到一定阈值后,会触发Split操作,同时会把当前Region分裂成两个Region,父Region下线,<br>新分裂的两个子Region会被分配到相应的HRgionServer上。<br><strong>Hlog和数据恢复</strong><br>每一个RegionServer中都有一个Hlog对象,Hlog对象是一个实现了WAL的类,用户的操作在写入memStore的同时,<br>会写入到Hlog文件中,它会定期滚动出新,删除旧的文件。<br>当RegionServer意外中止后,Hmaster会通过zk感知到,首先处理遗留的Hlog文件,将其中不同Region的Log数据<br>进行拆分,分别放到相应的Region目录下,然后再将失效的Region重新分配。<br>领取到这些Region的RegionServer在加载Region的过程中,会发现有历史Hlog需要处理,会将Hlog中的数据回放到<br>MemStore中,然后flush到StoreFile,完成数据恢复。</p>\n"},{"title":"Spark面试题Part1","date":"2021-09-08T13:20:16.000Z","comments":1,"toc":true,"_content":"spark core部分的重点\nRDD的理解\nshuffle部分的理解\nspark作业的提交流程(DagScheduler,TaskScheduler,stage划分等)\n\n#### RDD\n```scala\n * A Resilient Distributed Dataset (RDD), the basic abstraction in Spark. Represents an immutable,\n * partitioned collection of elements that can be operated on in parallel. This class contains the\n * basic operations available on all RDDs, such as `map`, `filter`, and `persist`. In addition,\n * [[org.apache.spark.rdd.PairRDDFunctions]] contains operations available only on RDDs of key-value\n * pairs, such as `groupByKey` and `join`;\n * [[org.apache.spark.rdd.DoubleRDDFunctions]] contains operations available only on RDDs of\n * Doubles; and\n * [[org.apache.spark.rdd.SequenceFileRDDFunctions]] contains operations available on RDDs that\n * can be saved as SequenceFiles.\n * All operations are automatically available on any RDD of the right type (e.g. RDD[(Int, Int)])\n * through implicit.\n\n * Internally, each RDD is characterized by five main properties:\n *\n * - A list of partitions\n * - A function for computing each split\n * - A list of dependencies on other RDDs\n * - Optionally, a Partitioner for key-value RDDs (e.g. to say that the RDD is hash-partitioned)\n * - Optionally, a list of preferred locations to compute each split on (e.g. block locations for\n * an HDFS file)\n```\n#### Spark作业的提交流程\nspark-submit\n-> 构据提交程序的主类,通过反射构造这个类实例\n-> 主类中的main静态方法开始运行\n在这里面你构造的 SparkContext对象\n它需要用到dagScheduler, taskScheudler, SparkEnv.\nmain代码里面 构造一个初始rdd,或多个rdd,\n然后再做一些转换,最后触发一个或多个action.\n由action开始构建一个job,分成stages,然后去运行。\nsparkContext.submitjob -> dagScheduler.submitJob\nDAGSchedulerEventProcessLoop\neventProcessLoop.post(JobSubmitted(\njobId, rdd, func2, partitions.toArray, callSite, waiter,\nUtils.cloneProperties(properties)))\ndagScheduler.handleJobSubmitted(jobId, rdd, func, partitions,\ncallSite, listener, properties)\nfinalStage = createResultStage(finalRDD, func, partitions, jobId, callSite)\nval parents = getOrCreateParentStages(shuffleDeps, jobId)\nval stage = new ResultStage(id, rdd, func, partitions, parents, jobId,\nval job = new ActiveJob(jobId, finalStage, callSite, listener, properties)\n/** Submits stage, but first recursively submits any missing parents. */\nval missing = getMissingParentStages(stage).sortBy(_.id)\nfor (parent <- missing) {\nsubmitStage(parent)\n}\n如果父RDD的一个Partition被一个子RDD的Partition所使用就是窄依赖,否则的话就是宽依赖。\n\n// For ShuffleMapTask, serialize and broadcast (rdd, shuffleDep).\n// For ResultTask, serialize and broadcast (rdd, func).\ntaskBinary = sc.broadcast(taskBinaryBytes)\n根据stage的属性和partitions来构造tasks[]\ntaskScheduler.submitTasks(new TaskSet(\ntasks.toArray, stage.id, stage.latestInfo.attemptNumber, jobId, properties,\nstage.resourceProfileId))\ntaskScheduler,taskset,maxTaskFailures构建成TasksetManager\n设置任务集调度策略,调度模式有FAIR,FIFO两种,默认的是FIFO,将tasksetManager添加到\nFIFOSchedulableBuilder中。\n\n任务构造完成之后,分配到哪些节点?哪些节点有足够的资源。\n资源分配,使用LocalBackend的reviveOffer方法,它的处理步骤如下:\n1.使用ExecutorId,ExecutorHostName,freeCores创建WorkerOffer\n2.调用TaskSchedulerImpl的resourceOffers方法分配资源\n3.调用Executor的launchTask方法运行任务\nExecutor的launchTask方法,标志着任务执行阶段的开始。\n1.创建TaskRunner,并将其与taskId,taskName以及serializedTask添加到runningTasks中。\n2.TaskRunner实现了Runnable接口,最`1 后使用线程池来执行TaskRunner。\nrun方法的处理动作包含状态更新,任务反序列化和任务运行。\n将driver提交的task在Executor上通过反序列化,更新依赖达到还原效果。\nval(taskFiles, taskJars, taskBytes) = Task.deserializeWithDependencies(sd)\nval value=task.run(taskId.toInt)\n\n#### Shuffle过程分析\nshuffle过程用于连接map任务的输出和reduce任务的输入,map任务的中间输出结果按照key值\n哈希后分配给某一个reduce任务。\nshuffle包含map任务端的shuffle write和reduce任务端的shuffle read两部分。\nwriter部分的任务个数由finalRdd的partition个数决定。\nreader部分的任务个数由spark.sql.shuffle.partitions个数决定。\nwrite阶段会把状态与shuffle输出的数据数量位置信息封装到MapStatus对象,然后发送到driver.\nreader阶段会从driver请求mapstatus,然后读取数据。\n\nspark1.2之前使用hashsuffleManager来处理shuffle过程,现更新为sortShuffleManager来处理。\n因为早期的HashshuffleManager在shuffle write阶段生成的中间结果文件过多,会给IO造成过多的压力。\n1.map任务会为每一个reduce task创建一个bucket,map阶段最终会创建M*R个bucket。\n2.reduce任务从本地或者远端的map任务所在的BlockManager获取相应的bucket作为输入。\n早期shuffle过程存在的问题:\n1.map任务的中间结果先存入内存,后写入磁盘,对内存的开销很大,当一个节点上的map输出很大时,\n容易造成OOM.\n2.生成的中间文件过多,磁盘IO将成为性能瓶颈。\nspark shuffle做的优化如下:\n1.将map任务给每个partition的reduce任务输出的bucket合并到同一个文件当中。\n2.map任务逐条输出计算结果,使用appendOnlyMap缓存与聚合算法对中间结果进行聚合,减少中间结果\n占用的内存大小。\n3.对SizeTrackingAppendOnlyMap和SizeTrackingPairBuffer等缓存进行溢出判断,当超出\nthreshold时将数据写入磁盘,防止内存溢出。\n4.reduce任务对拉取到的map任务中间结果逐条读是不是一次性读取到内存,并在内存中进行聚合和排序,\n减少了对内存的使用。\nshuffleSortManager 有两种模式,一种是bypass模式,一种是普通模式。\nbypass模式启动的动件是shuffle read task个数小于200(bypassMergeThreshold),同时mapsideCombine\n是false的情况下,才会开启bypass,bypass模式基本可以理解为和之前shuffle模式一样,只是最后会合并生成的临时文件,\n生成一个数据文件和一个索引文件。\n普通模式的sortShuffleSortManager的工作流程:\n在该模式下,数据会先写入一个内存数据结构中,此时根据不同的shuffle算子,可能选用不同的数据结构。\n如果是reduceByKey这种聚合类的shuffle算子,那么会选用Map数据结构,一边通过Map进行聚合,一边写入内存;\n如果是join这种普通的shuffle算子,那么会选用Array数据结构,直接写入内存。接着每写一条数据进入内存数据结构之后,\n就会判断一下,是否达到了某个临界阈值。如果达到临界阈值的话,那么就会尝试将内存数据结构中的数据溢写到磁盘,\n然后清空内存数据结构。\n\n在溢写到磁盘文件之前,会先根据key对内存数据结构中已有的数据进行排序。排序过后,会分批将数据写入磁盘文件。\n默认的batch数量是10000条,排序好的数据,会以每批1万条数据的形式分批写入磁盘文件。\n写入磁盘文件是通过Java的BufferedOutputStream实现的。首先会将数据缓冲在内存中,\n当内存缓冲满溢之后再一次写入磁盘文件中,这样可以减少磁盘IO次数,提升性能。\n\n一个task将所有数据写入内存数据结构的过程中,会发生多次磁盘溢写操作,也就会产生多个临时文件。\n最后会将之前所有的临时磁盘文件都进行合并,这就是merge过程,此时会将之前所有临时磁盘文件中的数据读取出来,\n然后依次写入最终的磁盘文件之中。此外,由于一个task就只对应一个磁盘文件,\n也就意味着该task为下游stage的task准备的数据都在这一个文件中,因此还会单独写一份索引文件,\n其中标识了下游各个task的数据在文件中的start offset与end offset。\n\nSortShuffleManager由于有一个磁盘文件merge的过程,因此大大减少了文件数量。\n\n```scala\n * Sort-based shuffle has two different write paths for producing its map output files:\n * - Serialized sorting: used when all three of the following conditions hold:\n * 1. The shuffle dependency specifies no map-side combine.\n * 2. The shuffle serializer supports relocation of serialized values (this is currently\n * supported by KryoSerializer and Spark SQL's custom serializers).\n * 3. The shuffle produces fewer than or equal to 16777216 output partitions.\n * - Deserialized sorting: used to handle all other cases.\n ExternalSorter\n *\n * @param aggregator optional Aggregator with combine functions to use for merging data\n * @param partitioner optional Partitioner; if given, sort by partition ID and then key\n * @param ordering optional Ordering to sort keys within each partition; should be a total ordering\n * @param serializer serializer to use when spilling to disk\n```\n\n#### Spark OOM分析\nRDD已经可以序列化到磁盘,为什么我们Spark作业还会OOM?\n合理地调整spark有关mergesort spill相关的参数\n","source":"_posts/Spark面试题Part1.md","raw":"---\ntitle: Spark面试题Part1\ndate: 2021-09-08 21:20:16\ncomments: true #是否可评论\ntoc: true #是否显示文章目录\ncategories: \"面试题\" #分类\ntags: #标签\n - Spark\n - 面试题\n---\nspark core部分的重点\nRDD的理解\nshuffle部分的理解\nspark作业的提交流程(DagScheduler,TaskScheduler,stage划分等)\n\n#### RDD\n```scala\n * A Resilient Distributed Dataset (RDD), the basic abstraction in Spark. Represents an immutable,\n * partitioned collection of elements that can be operated on in parallel. This class contains the\n * basic operations available on all RDDs, such as `map`, `filter`, and `persist`. In addition,\n * [[org.apache.spark.rdd.PairRDDFunctions]] contains operations available only on RDDs of key-value\n * pairs, such as `groupByKey` and `join`;\n * [[org.apache.spark.rdd.DoubleRDDFunctions]] contains operations available only on RDDs of\n * Doubles; and\n * [[org.apache.spark.rdd.SequenceFileRDDFunctions]] contains operations available on RDDs that\n * can be saved as SequenceFiles.\n * All operations are automatically available on any RDD of the right type (e.g. RDD[(Int, Int)])\n * through implicit.\n\n * Internally, each RDD is characterized by five main properties:\n *\n * - A list of partitions\n * - A function for computing each split\n * - A list of dependencies on other RDDs\n * - Optionally, a Partitioner for key-value RDDs (e.g. to say that the RDD is hash-partitioned)\n * - Optionally, a list of preferred locations to compute each split on (e.g. block locations for\n * an HDFS file)\n```\n#### Spark作业的提交流程\nspark-submit\n-> 构据提交程序的主类,通过反射构造这个类实例\n-> 主类中的main静态方法开始运行\n在这里面你构造的 SparkContext对象\n它需要用到dagScheduler, taskScheudler, SparkEnv.\nmain代码里面 构造一个初始rdd,或多个rdd,\n然后再做一些转换,最后触发一个或多个action.\n由action开始构建一个job,分成stages,然后去运行。\nsparkContext.submitjob -> dagScheduler.submitJob\nDAGSchedulerEventProcessLoop\neventProcessLoop.post(JobSubmitted(\njobId, rdd, func2, partitions.toArray, callSite, waiter,\nUtils.cloneProperties(properties)))\ndagScheduler.handleJobSubmitted(jobId, rdd, func, partitions,\ncallSite, listener, properties)\nfinalStage = createResultStage(finalRDD, func, partitions, jobId, callSite)\nval parents = getOrCreateParentStages(shuffleDeps, jobId)\nval stage = new ResultStage(id, rdd, func, partitions, parents, jobId,\nval job = new ActiveJob(jobId, finalStage, callSite, listener, properties)\n/** Submits stage, but first recursively submits any missing parents. */\nval missing = getMissingParentStages(stage).sortBy(_.id)\nfor (parent <- missing) {\nsubmitStage(parent)\n}\n如果父RDD的一个Partition被一个子RDD的Partition所使用就是窄依赖,否则的话就是宽依赖。\n\n// For ShuffleMapTask, serialize and broadcast (rdd, shuffleDep).\n// For ResultTask, serialize and broadcast (rdd, func).\ntaskBinary = sc.broadcast(taskBinaryBytes)\n根据stage的属性和partitions来构造tasks[]\ntaskScheduler.submitTasks(new TaskSet(\ntasks.toArray, stage.id, stage.latestInfo.attemptNumber, jobId, properties,\nstage.resourceProfileId))\ntaskScheduler,taskset,maxTaskFailures构建成TasksetManager\n设置任务集调度策略,调度模式有FAIR,FIFO两种,默认的是FIFO,将tasksetManager添加到\nFIFOSchedulableBuilder中。\n\n任务构造完成之后,分配到哪些节点?哪些节点有足够的资源。\n资源分配,使用LocalBackend的reviveOffer方法,它的处理步骤如下:\n1.使用ExecutorId,ExecutorHostName,freeCores创建WorkerOffer\n2.调用TaskSchedulerImpl的resourceOffers方法分配资源\n3.调用Executor的launchTask方法运行任务\nExecutor的launchTask方法,标志着任务执行阶段的开始。\n1.创建TaskRunner,并将其与taskId,taskName以及serializedTask添加到runningTasks中。\n2.TaskRunner实现了Runnable接口,最`1 后使用线程池来执行TaskRunner。\nrun方法的处理动作包含状态更新,任务反序列化和任务运行。\n将driver提交的task在Executor上通过反序列化,更新依赖达到还原效果。\nval(taskFiles, taskJars, taskBytes) = Task.deserializeWithDependencies(sd)\nval value=task.run(taskId.toInt)\n\n#### Shuffle过程分析\nshuffle过程用于连接map任务的输出和reduce任务的输入,map任务的中间输出结果按照key值\n哈希后分配给某一个reduce任务。\nshuffle包含map任务端的shuffle write和reduce任务端的shuffle read两部分。\nwriter部分的任务个数由finalRdd的partition个数决定。\nreader部分的任务个数由spark.sql.shuffle.partitions个数决定。\nwrite阶段会把状态与shuffle输出的数据数量位置信息封装到MapStatus对象,然后发送到driver.\nreader阶段会从driver请求mapstatus,然后读取数据。\n\nspark1.2之前使用hashsuffleManager来处理shuffle过程,现更新为sortShuffleManager来处理。\n因为早期的HashshuffleManager在shuffle write阶段生成的中间结果文件过多,会给IO造成过多的压力。\n1.map任务会为每一个reduce task创建一个bucket,map阶段最终会创建M*R个bucket。\n2.reduce任务从本地或者远端的map任务所在的BlockManager获取相应的bucket作为输入。\n早期shuffle过程存在的问题:\n1.map任务的中间结果先存入内存,后写入磁盘,对内存的开销很大,当一个节点上的map输出很大时,\n容易造成OOM.\n2.生成的中间文件过多,磁盘IO将成为性能瓶颈。\nspark shuffle做的优化如下:\n1.将map任务给每个partition的reduce任务输出的bucket合并到同一个文件当中。\n2.map任务逐条输出计算结果,使用appendOnlyMap缓存与聚合算法对中间结果进行聚合,减少中间结果\n占用的内存大小。\n3.对SizeTrackingAppendOnlyMap和SizeTrackingPairBuffer等缓存进行溢出判断,当超出\nthreshold时将数据写入磁盘,防止内存溢出。\n4.reduce任务对拉取到的map任务中间结果逐条读是不是一次性读取到内存,并在内存中进行聚合和排序,\n减少了对内存的使用。\nshuffleSortManager 有两种模式,一种是bypass模式,一种是普通模式。\nbypass模式启动的动件是shuffle read task个数小于200(bypassMergeThreshold),同时mapsideCombine\n是false的情况下,才会开启bypass,bypass模式基本可以理解为和之前shuffle模式一样,只是最后会合并生成的临时文件,\n生成一个数据文件和一个索引文件。\n普通模式的sortShuffleSortManager的工作流程:\n在该模式下,数据会先写入一个内存数据结构中,此时根据不同的shuffle算子,可能选用不同的数据结构。\n如果是reduceByKey这种聚合类的shuffle算子,那么会选用Map数据结构,一边通过Map进行聚合,一边写入内存;\n如果是join这种普通的shuffle算子,那么会选用Array数据结构,直接写入内存。接着每写一条数据进入内存数据结构之后,\n就会判断一下,是否达到了某个临界阈值。如果达到临界阈值的话,那么就会尝试将内存数据结构中的数据溢写到磁盘,\n然后清空内存数据结构。\n\n在溢写到磁盘文件之前,会先根据key对内存数据结构中已有的数据进行排序。排序过后,会分批将数据写入磁盘文件。\n默认的batch数量是10000条,排序好的数据,会以每批1万条数据的形式分批写入磁盘文件。\n写入磁盘文件是通过Java的BufferedOutputStream实现的。首先会将数据缓冲在内存中,\n当内存缓冲满溢之后再一次写入磁盘文件中,这样可以减少磁盘IO次数,提升性能。\n\n一个task将所有数据写入内存数据结构的过程中,会发生多次磁盘溢写操作,也就会产生多个临时文件。\n最后会将之前所有的临时磁盘文件都进行合并,这就是merge过程,此时会将之前所有临时磁盘文件中的数据读取出来,\n然后依次写入最终的磁盘文件之中。此外,由于一个task就只对应一个磁盘文件,\n也就意味着该task为下游stage的task准备的数据都在这一个文件中,因此还会单独写一份索引文件,\n其中标识了下游各个task的数据在文件中的start offset与end offset。\n\nSortShuffleManager由于有一个磁盘文件merge的过程,因此大大减少了文件数量。\n\n```scala\n * Sort-based shuffle has two different write paths for producing its map output files:\n * - Serialized sorting: used when all three of the following conditions hold:\n * 1. The shuffle dependency specifies no map-side combine.\n * 2. The shuffle serializer supports relocation of serialized values (this is currently\n * supported by KryoSerializer and Spark SQL's custom serializers).\n * 3. The shuffle produces fewer than or equal to 16777216 output partitions.\n * - Deserialized sorting: used to handle all other cases.\n ExternalSorter\n *\n * @param aggregator optional Aggregator with combine functions to use for merging data\n * @param partitioner optional Partitioner; if given, sort by partition ID and then key\n * @param ordering optional Ordering to sort keys within each partition; should be a total ordering\n * @param serializer serializer to use when spilling to disk\n```\n\n#### Spark OOM分析\nRDD已经可以序列化到磁盘,为什么我们Spark作业还会OOM?\n合理地调整spark有关mergesort spill相关的参数\n","slug":"Spark面试题Part1","published":1,"updated":"2022-04-20T09:35:50.863Z","layout":"post","photos":[],"link":"","_id":"cl27q3uv0000bn94o1wfu11qd","content":"<p>spark core部分的重点<br>RDD的理解<br>shuffle部分的理解<br>spark作业的提交流程(DagScheduler,TaskScheduler,stage划分等)</p>\n<h4 id=\"RDD\"><a href=\"#RDD\" class=\"headerlink\" title=\"RDD\"></a>RDD</h4><figure class=\"highlight scala\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br><span class=\"line\">11</span><br><span class=\"line\">12</span><br><span class=\"line\">13</span><br><span class=\"line\">14</span><br><span class=\"line\">15</span><br><span class=\"line\">16</span><br><span class=\"line\">17</span><br><span class=\"line\">18</span><br><span class=\"line\">19</span><br><span class=\"line\">20</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">* <span class=\"type\">A</span> <span class=\"type\">Resilient</span> <span class=\"type\">Distributed</span> <span class=\"type\">Dataset</span> (<span class=\"type\">RDD</span>), the basic abstraction in <span class=\"type\">Spark</span>. <span class=\"type\">Represents</span> an immutable,</span><br><span class=\"line\">* partitioned collection of elements that can be operated on in parallel. <span class=\"type\">This</span> <span class=\"class\"><span class=\"keyword\">class</span> <span class=\"title\">contains</span> <span class=\"title\">the</span></span></span><br><span class=\"line\">* basic operations available on all <span class=\"type\">RDDs</span>, such as `map`, `filter`, and `persist`. <span class=\"type\">In</span> addition,</span><br><span class=\"line\">* [[org.apache.spark.rdd.<span class=\"type\">PairRDDFunctions</span>]] contains operations available only on <span class=\"type\">RDDs</span> of key-value</span><br><span class=\"line\">* pairs, such as `groupByKey` and `join`;</span><br><span class=\"line\">* [[org.apache.spark.rdd.<span class=\"type\">DoubleRDDFunctions</span>]] contains operations available only on <span class=\"type\">RDDs</span> of</span><br><span class=\"line\">* <span class=\"type\">Doubles</span>; and</span><br><span class=\"line\">* [[org.apache.spark.rdd.<span class=\"type\">SequenceFileRDDFunctions</span>]] contains operations available on <span class=\"type\">RDDs</span> that</span><br><span class=\"line\">* can be saved as <span class=\"type\">SequenceFiles</span>.</span><br><span class=\"line\">* <span class=\"type\">All</span> operations are automatically available on any <span class=\"type\">RDD</span> of the right <span class=\"class\"><span class=\"keyword\">type</span> (<span class=\"params\">e.g. <span class=\"type\">RDD</span>[(<span class=\"type\">Int</span>, <span class=\"type\">Int</span></span>)])</span></span><br><span class=\"line\">* through <span class=\"keyword\">implicit</span>.</span><br><span class=\"line\"></span><br><span class=\"line\">* <span class=\"type\">Internally</span>, each <span class=\"type\">RDD</span> is characterized by five main properties:</span><br><span class=\"line\">*</span><br><span class=\"line\">* - <span class=\"type\">A</span> list of partitions</span><br><span class=\"line\">* - <span class=\"type\">A</span> function <span class=\"keyword\">for</span> computing each split</span><br><span class=\"line\">* - <span class=\"type\">A</span> list of dependencies on other <span class=\"type\">RDDs</span></span><br><span class=\"line\">* - <span class=\"type\">Optionally</span>, a <span class=\"type\">Partitioner</span> <span class=\"keyword\">for</span> key-value <span class=\"type\">RDDs</span> (e.g. to say that the <span class=\"type\">RDD</span> is hash-partitioned)</span><br><span class=\"line\">* - <span class=\"type\">Optionally</span>, a list of preferred locations to compute each split on (e.g. block locations <span class=\"keyword\">for</span></span><br><span class=\"line\">* an <span class=\"type\">HDFS</span> file)</span><br></pre></td></tr></table></figure>\n<h4 id=\"Spark作业的提交流程\"><a href=\"#Spark作业的提交流程\" class=\"headerlink\" title=\"Spark作业的提交流程\"></a>Spark作业的提交流程</h4><p>spark-submit<br>-> 构据提交程序的主类,通过反射构造这个类实例<br>-> 主类中的main静态方法开始运行<br>在这里面你构造的 SparkContext对象<br>它需要用到dagScheduler, taskScheudler, SparkEnv.<br>main代码里面 构造一个初始rdd,或多个rdd,<br>然后再做一些转换,最后触发一个或多个action.<br>由action开始构建一个job,分成stages,然后去运行。<br>sparkContext.submitjob -> dagScheduler.submitJob<br>DAGSchedulerEventProcessLoop<br>eventProcessLoop.post(JobSubmitted(<br>jobId, rdd, func2, partitions.toArray, callSite, waiter,<br>Utils.cloneProperties(properties)))<br>dagScheduler.handleJobSubmitted(jobId, rdd, func, partitions,<br>callSite, listener, properties)<br>finalStage = createResultStage(finalRDD, func, partitions, jobId, callSite)<br>val parents = getOrCreateParentStages(shuffleDeps, jobId)<br>val stage = new ResultStage(id, rdd, func, partitions, parents, jobId,<br>val job = new ActiveJob(jobId, finalStage, callSite, listener, properties)<br>/** Submits stage, but first recursively submits any missing parents. */<br>val missing = getMissingParentStages(stage).sortBy(_.id)<br>for (parent <- missing) {<br>submitStage(parent)<br>}<br>如果父RDD的一个Partition被一个子RDD的Partition所使用就是窄依赖,否则的话就是宽依赖。</p>\n<p>// For ShuffleMapTask, serialize and broadcast (rdd, shuffleDep).<br>// For ResultTask, serialize and broadcast (rdd, func).<br>taskBinary = sc.broadcast(taskBinaryBytes)<br>根据stage的属性和partitions来构造tasks[]<br>taskScheduler.submitTasks(new TaskSet(<br>tasks.toArray, stage.id, stage.latestInfo.attemptNumber, jobId, properties,<br>stage.resourceProfileId))<br>taskScheduler,taskset,maxTaskFailures构建成TasksetManager<br>设置任务集调度策略,调度模式有FAIR,FIFO两种,默认的是FIFO,将tasksetManager添加到<br>FIFOSchedulableBuilder中。</p>\n<p>任务构造完成之后,分配到哪些节点?哪些节点有足够的资源。<br>资源分配,使用LocalBackend的reviveOffer方法,它的处理步骤如下:<br>1.使用ExecutorId,ExecutorHostName,freeCores创建WorkerOffer<br>2.调用TaskSchedulerImpl的resourceOffers方法分配资源<br>3.调用Executor的launchTask方法运行任务<br>Executor的launchTask方法,标志着任务执行阶段的开始。<br>1.创建TaskRunner,并将其与taskId,taskName以及serializedTask添加到runningTasks中。<br>2.TaskRunner实现了Runnable接口,最`1 后使用线程池来执行TaskRunner。<br>run方法的处理动作包含状态更新,任务反序列化和任务运行。<br>将driver提交的task在Executor上通过反序列化,更新依赖达到还原效果。<br>val(taskFiles, taskJars, taskBytes) = Task.deserializeWithDependencies(sd)<br>val value=task.run(taskId.toInt)</p>\n<h4 id=\"Shuffle过程分析\"><a href=\"#Shuffle过程分析\" class=\"headerlink\" title=\"Shuffle过程分析\"></a>Shuffle过程分析</h4><p>shuffle过程用于连接map任务的输出和reduce任务的输入,map任务的中间输出结果按照key值<br>哈希后分配给某一个reduce任务。<br>shuffle包含map任务端的shuffle write和reduce任务端的shuffle read两部分。<br>writer部分的任务个数由finalRdd的partition个数决定。<br>reader部分的任务个数由spark.sql.shuffle.partitions个数决定。<br>write阶段会把状态与shuffle输出的数据数量位置信息封装到MapStatus对象,然后发送到driver.<br>reader阶段会从driver请求mapstatus,然后读取数据。</p>\n<p>spark1.2之前使用hashsuffleManager来处理shuffle过程,现更新为sortShuffleManager来处理。<br>因为早期的HashshuffleManager在shuffle write阶段生成的中间结果文件过多,会给IO造成过多的压力。<br>1.map任务会为每一个reduce task创建一个bucket,map阶段最终会创建M*R个bucket。<br>2.reduce任务从本地或者远端的map任务所在的BlockManager获取相应的bucket作为输入。<br>早期shuffle过程存在的问题:<br>1.map任务的中间结果先存入内存,后写入磁盘,对内存的开销很大,当一个节点上的map输出很大时,<br>容易造成OOM.<br>2.生成的中间文件过多,磁盘IO将成为性能瓶颈。<br>spark shuffle做的优化如下:<br>1.将map任务给每个partition的reduce任务输出的bucket合并到同一个文件当中。<br>2.map任务逐条输出计算结果,使用appendOnlyMap缓存与聚合算法对中间结果进行聚合,减少中间结果<br>占用的内存大小。<br>3.对SizeTrackingAppendOnlyMap和SizeTrackingPairBuffer等缓存进行溢出判断,当超出<br>threshold时将数据写入磁盘,防止内存溢出。<br>4.reduce任务对拉取到的map任务中间结果逐条读是不是一次性读取到内存,并在内存中进行聚合和排序,<br>减少了对内存的使用。<br>shuffleSortManager 有两种模式,一种是bypass模式,一种是普通模式。<br>bypass模式启动的动件是shuffle read task个数小于200(bypassMergeThreshold),同时mapsideCombine<br>是false的情况下,才会开启bypass,bypass模式基本可以理解为和之前shuffle模式一样,只是最后会合并生成的临时文件,<br>生成一个数据文件和一个索引文件。<br>普通模式的sortShuffleSortManager的工作流程:<br>在该模式下,数据会先写入一个内存数据结构中,此时根据不同的shuffle算子,可能选用不同的数据结构。<br>如果是reduceByKey这种聚合类的shuffle算子,那么会选用Map数据结构,一边通过Map进行聚合,一边写入内存;<br>如果是join这种普通的shuffle算子,那么会选用Array数据结构,直接写入内存。接着每写一条数据进入内存数据结构之后,<br>就会判断一下,是否达到了某个临界阈值。如果达到临界阈值的话,那么就会尝试将内存数据结构中的数据溢写到磁盘,<br>然后清空内存数据结构。</p>\n<p>在溢写到磁盘文件之前,会先根据key对内存数据结构中已有的数据进行排序。排序过后,会分批将数据写入磁盘文件。<br>默认的batch数量是10000条,排序好的数据,会以每批1万条数据的形式分批写入磁盘文件。<br>写入磁盘文件是通过Java的BufferedOutputStream实现的。首先会将数据缓冲在内存中,<br>当内存缓冲满溢之后再一次写入磁盘文件中,这样可以减少磁盘IO次数,提升性能。</p>\n<p>一个task将所有数据写入内存数据结构的过程中,会发生多次磁盘溢写操作,也就会产生多个临时文件。<br>最后会将之前所有的临时磁盘文件都进行合并,这就是merge过程,此时会将之前所有临时磁盘文件中的数据读取出来,<br>然后依次写入最终的磁盘文件之中。此外,由于一个task就只对应一个磁盘文件,<br>也就意味着该task为下游stage的task准备的数据都在这一个文件中,因此还会单独写一份索引文件,<br>其中标识了下游各个task的数据在文件中的start offset与end offset。</p>\n<p>SortShuffleManager由于有一个磁盘文件merge的过程,因此大大减少了文件数量。</p>\n<figure class=\"highlight scala\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br><span class=\"line\">11</span><br><span class=\"line\">12</span><br><span class=\"line\">13</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">* <span class=\"type\">Sort</span>-based shuffle has two different write paths <span class=\"keyword\">for</span> producing its map output files:</span><br><span class=\"line\">* - <span class=\"type\">Serialized</span> sorting: used when all three of the following conditions hold:</span><br><span class=\"line\">* <span class=\"number\">1.</span> <span class=\"type\">The</span> shuffle dependency specifies no map-side combine.</span><br><span class=\"line\">* <span class=\"number\">2.</span> <span class=\"type\">The</span> shuffle serializer supports relocation of serialized values (<span class=\"keyword\">this</span> is currently</span><br><span class=\"line\">* supported by <span class=\"type\">KryoSerializer</span> and <span class=\"type\">Spark</span> <span class=\"type\">SQL</span>'s custom serializers).</span><br><span class=\"line\">* <span class=\"number\">3.</span> <span class=\"type\">The</span> shuffle produces fewer than or equal to <span class=\"number\">16777216</span> output partitions.</span><br><span class=\"line\">* - <span class=\"type\">Deserialized</span> sorting: used to handle all other cases.</span><br><span class=\"line\"><span class=\"type\">ExternalSorter</span></span><br><span class=\"line\"> *</span><br><span class=\"line\">* <span class=\"meta\">@param</span> aggregator optional <span class=\"type\">Aggregator</span> <span class=\"keyword\">with</span> combine functions to use <span class=\"keyword\">for</span> merging data</span><br><span class=\"line\">* <span class=\"meta\">@param</span> partitioner optional <span class=\"type\">Partitioner</span>; <span class=\"keyword\">if</span> <span class=\"keyword\">given</span>, sort by partition <span class=\"type\">ID</span> and <span class=\"keyword\">then</span> key</span><br><span class=\"line\">* <span class=\"meta\">@param</span> ordering optional <span class=\"type\">Ordering</span> to sort keys within each partition; should be a total ordering</span><br><span class=\"line\">* <span class=\"meta\">@param</span> serializer serializer to use when spilling to disk</span><br></pre></td></tr></table></figure>\n\n<h4 id=\"Spark-OOM分析\"><a href=\"#Spark-OOM分析\" class=\"headerlink\" title=\"Spark OOM分析\"></a>Spark OOM分析</h4><p>RDD已经可以序列化到磁盘,为什么我们Spark作业还会OOM?<br>合理地调整spark有关mergesort spill相关的参数</p>\n","site":{"data":{}},"excerpt":"","more":"<p>spark core部分的重点<br>RDD的理解<br>shuffle部分的理解<br>spark作业的提交流程(DagScheduler,TaskScheduler,stage划分等)</p>\n<h4 id=\"RDD\"><a href=\"#RDD\" class=\"headerlink\" title=\"RDD\"></a>RDD</h4><figure class=\"highlight scala\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br><span class=\"line\">11</span><br><span class=\"line\">12</span><br><span class=\"line\">13</span><br><span class=\"line\">14</span><br><span class=\"line\">15</span><br><span class=\"line\">16</span><br><span class=\"line\">17</span><br><span class=\"line\">18</span><br><span class=\"line\">19</span><br><span class=\"line\">20</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">* <span class=\"type\">A</span> <span class=\"type\">Resilient</span> <span class=\"type\">Distributed</span> <span class=\"type\">Dataset</span> (<span class=\"type\">RDD</span>), the basic abstraction in <span class=\"type\">Spark</span>. <span class=\"type\">Represents</span> an immutable,</span><br><span class=\"line\">* partitioned collection of elements that can be operated on in parallel. <span class=\"type\">This</span> <span class=\"class\"><span class=\"keyword\">class</span> <span class=\"title\">contains</span> <span class=\"title\">the</span></span></span><br><span class=\"line\">* basic operations available on all <span class=\"type\">RDDs</span>, such as `map`, `filter`, and `persist`. <span class=\"type\">In</span> addition,</span><br><span class=\"line\">* [[org.apache.spark.rdd.<span class=\"type\">PairRDDFunctions</span>]] contains operations available only on <span class=\"type\">RDDs</span> of key-value</span><br><span class=\"line\">* pairs, such as `groupByKey` and `join`;</span><br><span class=\"line\">* [[org.apache.spark.rdd.<span class=\"type\">DoubleRDDFunctions</span>]] contains operations available only on <span class=\"type\">RDDs</span> of</span><br><span class=\"line\">* <span class=\"type\">Doubles</span>; and</span><br><span class=\"line\">* [[org.apache.spark.rdd.<span class=\"type\">SequenceFileRDDFunctions</span>]] contains operations available on <span class=\"type\">RDDs</span> that</span><br><span class=\"line\">* can be saved as <span class=\"type\">SequenceFiles</span>.</span><br><span class=\"line\">* <span class=\"type\">All</span> operations are automatically available on any <span class=\"type\">RDD</span> of the right <span class=\"class\"><span class=\"keyword\">type</span> (<span class=\"params\">e.g. <span class=\"type\">RDD</span>[(<span class=\"type\">Int</span>, <span class=\"type\">Int</span></span>)])</span></span><br><span class=\"line\">* through <span class=\"keyword\">implicit</span>.</span><br><span class=\"line\"></span><br><span class=\"line\">* <span class=\"type\">Internally</span>, each <span class=\"type\">RDD</span> is characterized by five main properties:</span><br><span class=\"line\">*</span><br><span class=\"line\">* - <span class=\"type\">A</span> list of partitions</span><br><span class=\"line\">* - <span class=\"type\">A</span> function <span class=\"keyword\">for</span> computing each split</span><br><span class=\"line\">* - <span class=\"type\">A</span> list of dependencies on other <span class=\"type\">RDDs</span></span><br><span class=\"line\">* - <span class=\"type\">Optionally</span>, a <span class=\"type\">Partitioner</span> <span class=\"keyword\">for</span> key-value <span class=\"type\">RDDs</span> (e.g. to say that the <span class=\"type\">RDD</span> is hash-partitioned)</span><br><span class=\"line\">* - <span class=\"type\">Optionally</span>, a list of preferred locations to compute each split on (e.g. block locations <span class=\"keyword\">for</span></span><br><span class=\"line\">* an <span class=\"type\">HDFS</span> file)</span><br></pre></td></tr></table></figure>\n<h4 id=\"Spark作业的提交流程\"><a href=\"#Spark作业的提交流程\" class=\"headerlink\" title=\"Spark作业的提交流程\"></a>Spark作业的提交流程</h4><p>spark-submit<br>-> 构据提交程序的主类,通过反射构造这个类实例<br>-> 主类中的main静态方法开始运行<br>在这里面你构造的 SparkContext对象<br>它需要用到dagScheduler, taskScheudler, SparkEnv.<br>main代码里面 构造一个初始rdd,或多个rdd,<br>然后再做一些转换,最后触发一个或多个action.<br>由action开始构建一个job,分成stages,然后去运行。<br>sparkContext.submitjob -> dagScheduler.submitJob<br>DAGSchedulerEventProcessLoop<br>eventProcessLoop.post(JobSubmitted(<br>jobId, rdd, func2, partitions.toArray, callSite, waiter,<br>Utils.cloneProperties(properties)))<br>dagScheduler.handleJobSubmitted(jobId, rdd, func, partitions,<br>callSite, listener, properties)<br>finalStage = createResultStage(finalRDD, func, partitions, jobId, callSite)<br>val parents = getOrCreateParentStages(shuffleDeps, jobId)<br>val stage = new ResultStage(id, rdd, func, partitions, parents, jobId,<br>val job = new ActiveJob(jobId, finalStage, callSite, listener, properties)<br>/** Submits stage, but first recursively submits any missing parents. */<br>val missing = getMissingParentStages(stage).sortBy(_.id)<br>for (parent <- missing) {<br>submitStage(parent)<br>}<br>如果父RDD的一个Partition被一个子RDD的Partition所使用就是窄依赖,否则的话就是宽依赖。</p>\n<p>// For ShuffleMapTask, serialize and broadcast (rdd, shuffleDep).<br>// For ResultTask, serialize and broadcast (rdd, func).<br>taskBinary = sc.broadcast(taskBinaryBytes)<br>根据stage的属性和partitions来构造tasks[]<br>taskScheduler.submitTasks(new TaskSet(<br>tasks.toArray, stage.id, stage.latestInfo.attemptNumber, jobId, properties,<br>stage.resourceProfileId))<br>taskScheduler,taskset,maxTaskFailures构建成TasksetManager<br>设置任务集调度策略,调度模式有FAIR,FIFO两种,默认的是FIFO,将tasksetManager添加到<br>FIFOSchedulableBuilder中。</p>\n<p>任务构造完成之后,分配到哪些节点?哪些节点有足够的资源。<br>资源分配,使用LocalBackend的reviveOffer方法,它的处理步骤如下:<br>1.使用ExecutorId,ExecutorHostName,freeCores创建WorkerOffer<br>2.调用TaskSchedulerImpl的resourceOffers方法分配资源<br>3.调用Executor的launchTask方法运行任务<br>Executor的launchTask方法,标志着任务执行阶段的开始。<br>1.创建TaskRunner,并将其与taskId,taskName以及serializedTask添加到runningTasks中。<br>2.TaskRunner实现了Runnable接口,最`1 后使用线程池来执行TaskRunner。<br>run方法的处理动作包含状态更新,任务反序列化和任务运行。<br>将driver提交的task在Executor上通过反序列化,更新依赖达到还原效果。<br>val(taskFiles, taskJars, taskBytes) = Task.deserializeWithDependencies(sd)<br>val value=task.run(taskId.toInt)</p>\n<h4 id=\"Shuffle过程分析\"><a href=\"#Shuffle过程分析\" class=\"headerlink\" title=\"Shuffle过程分析\"></a>Shuffle过程分析</h4><p>shuffle过程用于连接map任务的输出和reduce任务的输入,map任务的中间输出结果按照key值<br>哈希后分配给某一个reduce任务。<br>shuffle包含map任务端的shuffle write和reduce任务端的shuffle read两部分。<br>writer部分的任务个数由finalRdd的partition个数决定。<br>reader部分的任务个数由spark.sql.shuffle.partitions个数决定。<br>write阶段会把状态与shuffle输出的数据数量位置信息封装到MapStatus对象,然后发送到driver.<br>reader阶段会从driver请求mapstatus,然后读取数据。</p>\n<p>spark1.2之前使用hashsuffleManager来处理shuffle过程,现更新为sortShuffleManager来处理。<br>因为早期的HashshuffleManager在shuffle write阶段生成的中间结果文件过多,会给IO造成过多的压力。<br>1.map任务会为每一个reduce task创建一个bucket,map阶段最终会创建M*R个bucket。<br>2.reduce任务从本地或者远端的map任务所在的BlockManager获取相应的bucket作为输入。<br>早期shuffle过程存在的问题:<br>1.map任务的中间结果先存入内存,后写入磁盘,对内存的开销很大,当一个节点上的map输出很大时,<br>容易造成OOM.<br>2.生成的中间文件过多,磁盘IO将成为性能瓶颈。<br>spark shuffle做的优化如下:<br>1.将map任务给每个partition的reduce任务输出的bucket合并到同一个文件当中。<br>2.map任务逐条输出计算结果,使用appendOnlyMap缓存与聚合算法对中间结果进行聚合,减少中间结果<br>占用的内存大小。<br>3.对SizeTrackingAppendOnlyMap和SizeTrackingPairBuffer等缓存进行溢出判断,当超出<br>threshold时将数据写入磁盘,防止内存溢出。<br>4.reduce任务对拉取到的map任务中间结果逐条读是不是一次性读取到内存,并在内存中进行聚合和排序,<br>减少了对内存的使用。<br>shuffleSortManager 有两种模式,一种是bypass模式,一种是普通模式。<br>bypass模式启动的动件是shuffle read task个数小于200(bypassMergeThreshold),同时mapsideCombine<br>是false的情况下,才会开启bypass,bypass模式基本可以理解为和之前shuffle模式一样,只是最后会合并生成的临时文件,<br>生成一个数据文件和一个索引文件。<br>普通模式的sortShuffleSortManager的工作流程:<br>在该模式下,数据会先写入一个内存数据结构中,此时根据不同的shuffle算子,可能选用不同的数据结构。<br>如果是reduceByKey这种聚合类的shuffle算子,那么会选用Map数据结构,一边通过Map进行聚合,一边写入内存;<br>如果是join这种普通的shuffle算子,那么会选用Array数据结构,直接写入内存。接着每写一条数据进入内存数据结构之后,<br>就会判断一下,是否达到了某个临界阈值。如果达到临界阈值的话,那么就会尝试将内存数据结构中的数据溢写到磁盘,<br>然后清空内存数据结构。</p>\n<p>在溢写到磁盘文件之前,会先根据key对内存数据结构中已有的数据进行排序。排序过后,会分批将数据写入磁盘文件。<br>默认的batch数量是10000条,排序好的数据,会以每批1万条数据的形式分批写入磁盘文件。<br>写入磁盘文件是通过Java的BufferedOutputStream实现的。首先会将数据缓冲在内存中,<br>当内存缓冲满溢之后再一次写入磁盘文件中,这样可以减少磁盘IO次数,提升性能。</p>\n<p>一个task将所有数据写入内存数据结构的过程中,会发生多次磁盘溢写操作,也就会产生多个临时文件。<br>最后会将之前所有的临时磁盘文件都进行合并,这就是merge过程,此时会将之前所有临时磁盘文件中的数据读取出来,<br>然后依次写入最终的磁盘文件之中。此外,由于一个task就只对应一个磁盘文件,<br>也就意味着该task为下游stage的task准备的数据都在这一个文件中,因此还会单独写一份索引文件,<br>其中标识了下游各个task的数据在文件中的start offset与end offset。</p>\n<p>SortShuffleManager由于有一个磁盘文件merge的过程,因此大大减少了文件数量。</p>\n<figure class=\"highlight scala\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br><span class=\"line\">11</span><br><span class=\"line\">12</span><br><span class=\"line\">13</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">* <span class=\"type\">Sort</span>-based shuffle has two different write paths <span class=\"keyword\">for</span> producing its map output files:</span><br><span class=\"line\">* - <span class=\"type\">Serialized</span> sorting: used when all three of the following conditions hold:</span><br><span class=\"line\">* <span class=\"number\">1.</span> <span class=\"type\">The</span> shuffle dependency specifies no map-side combine.</span><br><span class=\"line\">* <span class=\"number\">2.</span> <span class=\"type\">The</span> shuffle serializer supports relocation of serialized values (<span class=\"keyword\">this</span> is currently</span><br><span class=\"line\">* supported by <span class=\"type\">KryoSerializer</span> and <span class=\"type\">Spark</span> <span class=\"type\">SQL</span>'s custom serializers).</span><br><span class=\"line\">* <span class=\"number\">3.</span> <span class=\"type\">The</span> shuffle produces fewer than or equal to <span class=\"number\">16777216</span> output partitions.</span><br><span class=\"line\">* - <span class=\"type\">Deserialized</span> sorting: used to handle all other cases.</span><br><span class=\"line\"><span class=\"type\">ExternalSorter</span></span><br><span class=\"line\"> *</span><br><span class=\"line\">* <span class=\"meta\">@param</span> aggregator optional <span class=\"type\">Aggregator</span> <span class=\"keyword\">with</span> combine functions to use <span class=\"keyword\">for</span> merging data</span><br><span class=\"line\">* <span class=\"meta\">@param</span> partitioner optional <span class=\"type\">Partitioner</span>; <span class=\"keyword\">if</span> <span class=\"keyword\">given</span>, sort by partition <span class=\"type\">ID</span> and <span class=\"keyword\">then</span> key</span><br><span class=\"line\">* <span class=\"meta\">@param</span> ordering optional <span class=\"type\">Ordering</span> to sort keys within each partition; should be a total ordering</span><br><span class=\"line\">* <span class=\"meta\">@param</span> serializer serializer to use when spilling to disk</span><br></pre></td></tr></table></figure>\n\n<h4 id=\"Spark-OOM分析\"><a href=\"#Spark-OOM分析\" class=\"headerlink\" title=\"Spark OOM分析\"></a>Spark OOM分析</h4><p>RDD已经可以序列化到磁盘,为什么我们Spark作业还会OOM?<br>合理地调整spark有关mergesort spill相关的参数</p>\n"},{"title":"Scala学习Part1","date":"2021-09-06T16:00:00.000Z","comments":1,"toc":true,"_content":"#### 基础语法\n1. 条件表达式\n 在scala中if/else表达式有值,这个值就是跟在if or else之后表达式的值。\n 在scala中,每个表达式都有一个类型。没有返回值的表达式是Unit类型,类似java中的void。\n ```scala\n val s = if(x > 0) 1 else -1\n ```\n2. 块表达式和赋值\n {}块的值取决于最后一个表达式。\n 在scala中,赋值动作本身是没有值的,它们的值是Unit类型。\n ```scala\n x = y = 1 //不要这样做\n ```\n3. 循环\n for (i <- 1 to n )\n for ( i <- 1 to 10\n j <- i to 10\n )\n Scala中没有提供 break or continue语句来退出循环。\n 但提供了一个scala.util.control.Breaks._中的break可以用来退出循环。\n val vec = for(i <- 1 to 10 ) yield i % 3\n 这种情况叫for推导式。\n4. 默认参数和带名参数和变长参数\n ```scala\n //默认参数和带名参数\n def decorate(str:String, left:String = \"[\" , right:String =\"]\") = left + str + right\n //变长参数\n def sum(args: Int*) = {\n var result = 0\n for ( arg <- args )\n result += arg\n result\n }\n val s = sum(1,2,3)\n // val s = sum(1 to 5 ) error\n val s2 = sum( 1 to 5 :_*) //将1 to 5 当作参数序列处理\n def recursiveSum(args : Int*) : Int = {\n if (args.length == 0 ) 0\n else args.head + recursiveSum(args.tail :_* )\n }\n ```\n 使用的时候,可以调用参数名:\n decorate(left=\"<<<\", str=\"Hello\" , right=\">>>\")\n\n\n5. 懒值 lazy val\n 当val 被声明为lazy时,它的初始化将被推迟,直到我们首次对它取值。\n lazy val words = scala.io.Source.fromFile(\"path/to/file.txt\").mkString\n 如果我们不访问words,那么文件并不会被真正的读取。\n\n6. 异常捕获\n ```scala\n try{\n process(new URL(\"http://www.baidu.com\"))\n } catch{\n case _: MalformatURLException => println(\"bad url:\" + url)\n case ex: Exception => ex.printStackTrace()\n } finally{\n /do something\n }\n ```\n #### 数组相关操作\n 若长度固定可以使用Array,若长度有变化可以使用ArrayBuffer。\n 使用()来访问元素。\n 使用for(ele <- arr)来遍历元素。\n 在JVM中,scala中的Array以Java的数组方式实现。\n 类似Java中的ArrayList,Scala中有ArrayBuffer。\n ```scala\n val nums=new Array[Int](10)\n val s = Array(\"Hello\", \"world\")\n import scala.collection.mutable.ArrayBuffer\n val b = ArrayBuffer[Int]()\n //b.insert(2,6)\n //b.remove(2,6)\n ```\n\n #### 映射和元组\n ```scala\n //构造的不可变的Map[String,Int]\n val scores = Map(\"alice\" -> 10, \"bob\" -> 3 , \"Cindy\" -> 8)\n// val scores = new scala.collection.mutable.HashMap[String,Int]\n for ((k,v) <- scores ) println(k)\n ```\n 如果想按插入的顺序来访问所有键,使用LinkedHashMap,\n val months = scala.collection.mutable.LinkedHashMap(\"Jan\" -> 1 ....)\n\n #### 类\n 用@BeanProperty注解来生成JavaBeans的getXXX/setXXX方法。\n scala中,类并不声明为public,scala源文件可以包含多个类,所有这些类都具有公有可见性。\n Java的Beans规范把java属性定义为一对getFoo/setFoo方法。\n ```scala\n import scala.reflect.BeanProperty\n class Person {\n @BeanProperty var name: String = _\n def this(name:String) {\n this()\n this.name = name\n }\n }\n ```\n 将会生成四个方法:\n 1. name: String\n 2. name_ = (newValue : String):Unit\n 3. getName() : String\n 4. setName(newValue: String):Unit\n\n嵌套类\n在scala中,你几乎可以在任何语法结构中内嵌任何语法结构。\n你可以在函数中定义函数,在类中定义类。\n\n#### 类和对象\nScala中没有静态方法,它有一个类似的特性,叫单例对象。\n通常,一个类对应有一个伴生对象,伴生对象的名字和类的名字相同,\n类和伴生对象之间可以相互访问私有的方法和属性,它们必须存在于同一个源文件中。\n伴生类生成的类中带一个$符号。\nScala 中伴生对象采用 object 关键字声明,伴生对象中声明的全是静态内容,可以通过伴生对象名称直接调用。\napply方法\n调用object对象是默认执行的方法就是apply方法,这个方法通常用来返回伴生类的对象。\n每个scala程序都必须从一个对象的main方法开始,可以使用App Trait或自己写main.\n```scala\nobject Hello extends App {\n println(\"hello world\")\n}\n\nobject Test {\n def main(args: Array[String]) {\n println(\"hello world\")\n }\n}\n```","source":"_posts/Scala学习Part1.md","raw":"---\ntitle: Scala学习Part1\ndate: 2021-09-07\ncomments: true #是否可评论\ntoc: true #是否显示文章目录\ncategories: \"学习\" #分类\ntags: #标签\n - Scala\n---\n#### 基础语法\n1. 条件表达式\n 在scala中if/else表达式有值,这个值就是跟在if or else之后表达式的值。\n 在scala中,每个表达式都有一个类型。没有返回值的表达式是Unit类型,类似java中的void。\n ```scala\n val s = if(x > 0) 1 else -1\n ```\n2. 块表达式和赋值\n {}块的值取决于最后一个表达式。\n 在scala中,赋值动作本身是没有值的,它们的值是Unit类型。\n ```scala\n x = y = 1 //不要这样做\n ```\n3. 循环\n for (i <- 1 to n )\n for ( i <- 1 to 10\n j <- i to 10\n )\n Scala中没有提供 break or continue语句来退出循环。\n 但提供了一个scala.util.control.Breaks._中的break可以用来退出循环。\n val vec = for(i <- 1 to 10 ) yield i % 3\n 这种情况叫for推导式。\n4. 默认参数和带名参数和变长参数\n ```scala\n //默认参数和带名参数\n def decorate(str:String, left:String = \"[\" , right:String =\"]\") = left + str + right\n //变长参数\n def sum(args: Int*) = {\n var result = 0\n for ( arg <- args )\n result += arg\n result\n }\n val s = sum(1,2,3)\n // val s = sum(1 to 5 ) error\n val s2 = sum( 1 to 5 :_*) //将1 to 5 当作参数序列处理\n def recursiveSum(args : Int*) : Int = {\n if (args.length == 0 ) 0\n else args.head + recursiveSum(args.tail :_* )\n }\n ```\n 使用的时候,可以调用参数名:\n decorate(left=\"<<<\", str=\"Hello\" , right=\">>>\")\n\n\n5. 懒值 lazy val\n 当val 被声明为lazy时,它的初始化将被推迟,直到我们首次对它取值。\n lazy val words = scala.io.Source.fromFile(\"path/to/file.txt\").mkString\n 如果我们不访问words,那么文件并不会被真正的读取。\n\n6. 异常捕获\n ```scala\n try{\n process(new URL(\"http://www.baidu.com\"))\n } catch{\n case _: MalformatURLException => println(\"bad url:\" + url)\n case ex: Exception => ex.printStackTrace()\n } finally{\n /do something\n }\n ```\n #### 数组相关操作\n 若长度固定可以使用Array,若长度有变化可以使用ArrayBuffer。\n 使用()来访问元素。\n 使用for(ele <- arr)来遍历元素。\n 在JVM中,scala中的Array以Java的数组方式实现。\n 类似Java中的ArrayList,Scala中有ArrayBuffer。\n ```scala\n val nums=new Array[Int](10)\n val s = Array(\"Hello\", \"world\")\n import scala.collection.mutable.ArrayBuffer\n val b = ArrayBuffer[Int]()\n //b.insert(2,6)\n //b.remove(2,6)\n ```\n\n #### 映射和元组\n ```scala\n //构造的不可变的Map[String,Int]\n val scores = Map(\"alice\" -> 10, \"bob\" -> 3 , \"Cindy\" -> 8)\n// val scores = new scala.collection.mutable.HashMap[String,Int]\n for ((k,v) <- scores ) println(k)\n ```\n 如果想按插入的顺序来访问所有键,使用LinkedHashMap,\n val months = scala.collection.mutable.LinkedHashMap(\"Jan\" -> 1 ....)\n\n #### 类\n 用@BeanProperty注解来生成JavaBeans的getXXX/setXXX方法。\n scala中,类并不声明为public,scala源文件可以包含多个类,所有这些类都具有公有可见性。\n Java的Beans规范把java属性定义为一对getFoo/setFoo方法。\n ```scala\n import scala.reflect.BeanProperty\n class Person {\n @BeanProperty var name: String = _\n def this(name:String) {\n this()\n this.name = name\n }\n }\n ```\n 将会生成四个方法:\n 1. name: String\n 2. name_ = (newValue : String):Unit\n 3. getName() : String\n 4. setName(newValue: String):Unit\n\n嵌套类\n在scala中,你几乎可以在任何语法结构中内嵌任何语法结构。\n你可以在函数中定义函数,在类中定义类。\n\n#### 类和对象\nScala中没有静态方法,它有一个类似的特性,叫单例对象。\n通常,一个类对应有一个伴生对象,伴生对象的名字和类的名字相同,\n类和伴生对象之间可以相互访问私有的方法和属性,它们必须存在于同一个源文件中。\n伴生类生成的类中带一个$符号。\nScala 中伴生对象采用 object 关键字声明,伴生对象中声明的全是静态内容,可以通过伴生对象名称直接调用。\napply方法\n调用object对象是默认执行的方法就是apply方法,这个方法通常用来返回伴生类的对象。\n每个scala程序都必须从一个对象的main方法开始,可以使用App Trait或自己写main.\n```scala\nobject Hello extends App {\n println(\"hello world\")\n}\n\nobject Test {\n def main(args: Array[String]) {\n println(\"hello world\")\n }\n}\n```","slug":"Scala学习Part1","published":1,"updated":"2022-04-20T09:35:50.863Z","layout":"post","photos":[],"link":"","_id":"cl27q3uv2000cn94o63964j8h","content":"<h4 id=\"基础语法\"><a href=\"#基础语法\" class=\"headerlink\" title=\"基础语法\"></a>基础语法</h4><ol>\n<li>条件表达式<br>在scala中if/else表达式有值,这个值就是跟在if or else之后表达式的值。<br>在scala中,每个表达式都有一个类型。没有返回值的表达式是Unit类型,类似java中的void。<figure class=\"highlight scala\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"keyword\">val</span> s = <span class=\"keyword\">if</span>(x > <span class=\"number\">0</span>) <span class=\"number\">1</span> <span class=\"keyword\">else</span> <span class=\"number\">-1</span></span><br></pre></td></tr></table></figure></li>\n<li>块表达式和赋值<br>{}块的值取决于最后一个表达式。<br>在scala中,赋值动作本身是没有值的,它们的值是Unit类型。<figure class=\"highlight scala\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">x = y = <span class=\"number\">1</span> <span class=\"comment\">//不要这样做</span></span><br></pre></td></tr></table></figure></li>\n<li>循环<br>for (i <- 1 to n )<br>for ( i <- 1 to 10<pre><code> j <- i to 10\n</code></pre>\n)<br>Scala中没有提供 break or continue语句来退出循环。<br>但提供了一个scala.util.control.Breaks._中的break可以用来退出循环。<br>val vec = for(i <- 1 to 10 ) yield i % 3<br>这种情况叫for推导式。</li>\n<li>默认参数和带名参数和变长参数<figure class=\"highlight scala\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br><span class=\"line\">11</span><br><span class=\"line\">12</span><br><span class=\"line\">13</span><br><span class=\"line\">14</span><br><span class=\"line\">15</span><br><span class=\"line\">16</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"comment\">//默认参数和带名参数</span></span><br><span class=\"line\"><span class=\"function\"><span class=\"keyword\">def</span> <span class=\"title\">decorate</span></span>(str:<span class=\"type\">String</span>, left:<span class=\"type\">String</span> = <span class=\"string\">"["</span> , right:<span class=\"type\">String</span> =<span class=\"string\">"]"</span>) = left + str + right</span><br><span class=\"line\"><span class=\"comment\">//变长参数</span></span><br><span class=\"line\"><span class=\"function\"><span class=\"keyword\">def</span> <span class=\"title\">sum</span></span>(args: <span class=\"type\">Int</span>*) = {</span><br><span class=\"line\"> <span class=\"keyword\">var</span> result = <span class=\"number\">0</span></span><br><span class=\"line\"> <span class=\"keyword\">for</span> ( arg <- args )</span><br><span class=\"line\"> result += arg</span><br><span class=\"line\"> result</span><br><span class=\"line\">}</span><br><span class=\"line\"><span class=\"keyword\">val</span> s = sum(<span class=\"number\">1</span>,<span class=\"number\">2</span>,<span class=\"number\">3</span>)</span><br><span class=\"line\"><span class=\"comment\">// val s = sum(1 to 5 ) error</span></span><br><span class=\"line\"><span class=\"keyword\">val</span> s2 = sum( <span class=\"number\">1</span> to <span class=\"number\">5</span> :_*) <span class=\"comment\">//将1 to 5 当作参数序列处理</span></span><br><span class=\"line\"><span class=\"function\"><span class=\"keyword\">def</span> <span class=\"title\">recursiveSum</span></span>(args : <span class=\"type\">Int</span>*) : <span class=\"type\">Int</span> = {</span><br><span class=\"line\"> <span class=\"keyword\">if</span> (args.length == <span class=\"number\">0</span> ) <span class=\"number\">0</span></span><br><span class=\"line\"> <span class=\"keyword\">else</span> args.head + recursiveSum(args.tail :_* )</span><br><span class=\"line\">}</span><br></pre></td></tr></table></figure>\n使用的时候,可以调用参数名:<br>decorate(left=”<<<”, str=”Hello” , right=”>>>”)</li>\n</ol>\n<ol start=\"5\">\n<li><p>懒值 lazy val<br>当val 被声明为lazy时,它的初始化将被推迟,直到我们首次对它取值。<br>lazy val words = scala.io.Source.fromFile(“path/to/file.txt”).mkString<br>如果我们不访问words,那么文件并不会被真正的读取。</p>\n</li>\n<li><p>异常捕获</p>\n<figure class=\"highlight scala\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"keyword\">try</span>{</span><br><span class=\"line\"> process(<span class=\"keyword\">new</span> <span class=\"type\">URL</span>(<span class=\"string\">"http://www.baidu.com"</span>))</span><br><span class=\"line\">} <span class=\"keyword\">catch</span>{</span><br><span class=\"line\"> <span class=\"keyword\">case</span> _: <span class=\"type\">MalformatURLException</span> => println(<span class=\"string\">"bad url:"</span> + url)</span><br><span class=\"line\"> <span class=\"keyword\">case</span> ex: <span class=\"type\">Exception</span> => ex.printStackTrace()</span><br><span class=\"line\">} <span class=\"keyword\">finally</span>{</span><br><span class=\"line\"> /<span class=\"keyword\">do</span> something</span><br><span class=\"line\">}</span><br></pre></td></tr></table></figure>\n<h4 id=\"数组相关操作\"><a href=\"#数组相关操作\" class=\"headerlink\" title=\"数组相关操作\"></a>数组相关操作</h4><p>若长度固定可以使用Array,若长度有变化可以使用ArrayBuffer。<br>使用()来访问元素。<br>使用for(ele <- arr)来遍历元素。<br>在JVM中,scala中的Array以Java的数组方式实现。<br>类似Java中的ArrayList,Scala中有ArrayBuffer。</p>\n<figure class=\"highlight scala\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"keyword\">val</span> nums=<span class=\"keyword\">new</span> <span class=\"type\">Array</span>[<span class=\"type\">Int</span>](<span class=\"number\">10</span>)</span><br><span class=\"line\"><span class=\"keyword\">val</span> s = <span class=\"type\">Array</span>(<span class=\"string\">"Hello"</span>, <span class=\"string\">"world"</span>)</span><br><span class=\"line\"><span class=\"keyword\">import</span> scala.collection.mutable.<span class=\"type\">ArrayBuffer</span></span><br><span class=\"line\"><span class=\"keyword\">val</span> b = <span class=\"type\">ArrayBuffer</span>[<span class=\"type\">Int</span>]()</span><br><span class=\"line\"><span class=\"comment\">//b.insert(2,6)</span></span><br><span class=\"line\"><span class=\"comment\">//b.remove(2,6)</span></span><br></pre></td></tr></table></figure></li>\n</ol>\n<h4 id=\"映射和元组\"><a href=\"#映射和元组\" class=\"headerlink\" title=\"映射和元组\"></a>映射和元组</h4> <figure class=\"highlight scala\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"> <span class=\"comment\">//构造的不可变的Map[String,Int]</span></span><br><span class=\"line\"> <span class=\"keyword\">val</span> scores = <span class=\"type\">Map</span>(<span class=\"string\">"alice"</span> -> <span class=\"number\">10</span>, <span class=\"string\">"bob"</span> -> <span class=\"number\">3</span> , <span class=\"string\">"Cindy"</span> -> <span class=\"number\">8</span>)</span><br><span class=\"line\"><span class=\"comment\">// val scores = new scala.collection.mutable.HashMap[String,Int]</span></span><br><span class=\"line\"> <span class=\"keyword\">for</span> ((k,v) <- scores ) println(k)</span><br></pre></td></tr></table></figure>\n<p> 如果想按插入的顺序来访问所有键,使用LinkedHashMap,<br> val months = scala.collection.mutable.LinkedHashMap(“Jan” -> 1 ….)</p>\n<h4 id=\"类\"><a href=\"#类\" class=\"headerlink\" title=\"类\"></a>类</h4><p> 用@BeanProperty注解来生成JavaBeans的getXXX/setXXX方法。<br> scala中,类并不声明为public,scala源文件可以包含多个类,所有这些类都具有公有可见性。<br> Java的Beans规范把java属性定义为一对getFoo/setFoo方法。<br> <figure class=\"highlight scala\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"keyword\">import</span> scala.reflect.<span class=\"type\">BeanProperty</span></span><br><span class=\"line\"><span class=\"class\"><span class=\"keyword\">class</span> <span class=\"title\">Person</span> </span>{</span><br><span class=\"line\"> <span class=\"meta\">@BeanProperty</span> <span class=\"keyword\">var</span> name: <span class=\"type\">String</span> = _</span><br><span class=\"line\"> <span class=\"function\"><span class=\"keyword\">def</span> <span class=\"title\">this</span></span>(name:<span class=\"type\">String</span>) {</span><br><span class=\"line\"> <span class=\"keyword\">this</span>()</span><br><span class=\"line\"> <span class=\"keyword\">this</span>.name = name</span><br><span class=\"line\"> }</span><br><span class=\"line\">}</span><br></pre></td></tr></table></figure><br> 将会生成四个方法:</p>\n<ol>\n<li>name: String</li>\n<li>name_ = (newValue : String):Unit</li>\n<li>getName() : String</li>\n<li>setName(newValue: String):Unit</li>\n</ol>\n<p>嵌套类<br>在scala中,你几乎可以在任何语法结构中内嵌任何语法结构。<br>你可以在函数中定义函数,在类中定义类。</p>\n<h4 id=\"类和对象\"><a href=\"#类和对象\" class=\"headerlink\" title=\"类和对象\"></a>类和对象</h4><p>Scala中没有静态方法,它有一个类似的特性,叫单例对象。<br>通常,一个类对应有一个伴生对象,伴生对象的名字和类的名字相同,<br>类和伴生对象之间可以相互访问私有的方法和属性,它们必须存在于同一个源文件中。<br>伴生类生成的类中带一个$符号。<br>Scala 中伴生对象采用 object 关键字声明,伴生对象中声明的全是静态内容,可以通过伴生对象名称直接调用。<br>apply方法<br>调用object对象是默认执行的方法就是apply方法,这个方法通常用来返回伴生类的对象。<br>每个scala程序都必须从一个对象的main方法开始,可以使用App Trait或自己写main.</p>\n<figure class=\"highlight scala\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"class\"><span class=\"keyword\">object</span> <span class=\"title\">Hello</span> <span class=\"keyword\">extends</span> <span class=\"title\">App</span> </span>{</span><br><span class=\"line\"> println(<span class=\"string\">"hello world"</span>)</span><br><span class=\"line\">}</span><br><span class=\"line\"></span><br><span class=\"line\"><span class=\"class\"><span class=\"keyword\">object</span> <span class=\"title\">Test</span> </span>{</span><br><span class=\"line\"> <span class=\"function\"><span class=\"keyword\">def</span> <span class=\"title\">main</span></span>(args: <span class=\"type\">Array</span>[<span class=\"type\">String</span>]) {</span><br><span class=\"line\"> println(<span class=\"string\">"hello world"</span>)</span><br><span class=\"line\"> }</span><br><span class=\"line\">}</span><br></pre></td></tr></table></figure>","site":{"data":{}},"excerpt":"","more":"<h4 id=\"基础语法\"><a href=\"#基础语法\" class=\"headerlink\" title=\"基础语法\"></a>基础语法</h4><ol>\n<li>条件表达式<br>在scala中if/else表达式有值,这个值就是跟在if or else之后表达式的值。<br>在scala中,每个表达式都有一个类型。没有返回值的表达式是Unit类型,类似java中的void。<figure class=\"highlight scala\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"keyword\">val</span> s = <span class=\"keyword\">if</span>(x > <span class=\"number\">0</span>) <span class=\"number\">1</span> <span class=\"keyword\">else</span> <span class=\"number\">-1</span></span><br></pre></td></tr></table></figure></li>\n<li>块表达式和赋值<br>{}块的值取决于最后一个表达式。<br>在scala中,赋值动作本身是没有值的,它们的值是Unit类型。<figure class=\"highlight scala\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">x = y = <span class=\"number\">1</span> <span class=\"comment\">//不要这样做</span></span><br></pre></td></tr></table></figure></li>\n<li>循环<br>for (i <- 1 to n )<br>for ( i <- 1 to 10<pre><code> j <- i to 10\n</code></pre>\n)<br>Scala中没有提供 break or continue语句来退出循环。<br>但提供了一个scala.util.control.Breaks._中的break可以用来退出循环。<br>val vec = for(i <- 1 to 10 ) yield i % 3<br>这种情况叫for推导式。</li>\n<li>默认参数和带名参数和变长参数<figure class=\"highlight scala\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br><span class=\"line\">11</span><br><span class=\"line\">12</span><br><span class=\"line\">13</span><br><span class=\"line\">14</span><br><span class=\"line\">15</span><br><span class=\"line\">16</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"comment\">//默认参数和带名参数</span></span><br><span class=\"line\"><span class=\"function\"><span class=\"keyword\">def</span> <span class=\"title\">decorate</span></span>(str:<span class=\"type\">String</span>, left:<span class=\"type\">String</span> = <span class=\"string\">"["</span> , right:<span class=\"type\">String</span> =<span class=\"string\">"]"</span>) = left + str + right</span><br><span class=\"line\"><span class=\"comment\">//变长参数</span></span><br><span class=\"line\"><span class=\"function\"><span class=\"keyword\">def</span> <span class=\"title\">sum</span></span>(args: <span class=\"type\">Int</span>*) = {</span><br><span class=\"line\"> <span class=\"keyword\">var</span> result = <span class=\"number\">0</span></span><br><span class=\"line\"> <span class=\"keyword\">for</span> ( arg <- args )</span><br><span class=\"line\"> result += arg</span><br><span class=\"line\"> result</span><br><span class=\"line\">}</span><br><span class=\"line\"><span class=\"keyword\">val</span> s = sum(<span class=\"number\">1</span>,<span class=\"number\">2</span>,<span class=\"number\">3</span>)</span><br><span class=\"line\"><span class=\"comment\">// val s = sum(1 to 5 ) error</span></span><br><span class=\"line\"><span class=\"keyword\">val</span> s2 = sum( <span class=\"number\">1</span> to <span class=\"number\">5</span> :_*) <span class=\"comment\">//将1 to 5 当作参数序列处理</span></span><br><span class=\"line\"><span class=\"function\"><span class=\"keyword\">def</span> <span class=\"title\">recursiveSum</span></span>(args : <span class=\"type\">Int</span>*) : <span class=\"type\">Int</span> = {</span><br><span class=\"line\"> <span class=\"keyword\">if</span> (args.length == <span class=\"number\">0</span> ) <span class=\"number\">0</span></span><br><span class=\"line\"> <span class=\"keyword\">else</span> args.head + recursiveSum(args.tail :_* )</span><br><span class=\"line\">}</span><br></pre></td></tr></table></figure>\n使用的时候,可以调用参数名:<br>decorate(left=”<<<”, str=”Hello” , right=”>>>”)</li>\n</ol>\n<ol start=\"5\">\n<li><p>懒值 lazy val<br>当val 被声明为lazy时,它的初始化将被推迟,直到我们首次对它取值。<br>lazy val words = scala.io.Source.fromFile(“path/to/file.txt”).mkString<br>如果我们不访问words,那么文件并不会被真正的读取。</p>\n</li>\n<li><p>异常捕获</p>\n<figure class=\"highlight scala\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"keyword\">try</span>{</span><br><span class=\"line\"> process(<span class=\"keyword\">new</span> <span class=\"type\">URL</span>(<span class=\"string\">"http://www.baidu.com"</span>))</span><br><span class=\"line\">} <span class=\"keyword\">catch</span>{</span><br><span class=\"line\"> <span class=\"keyword\">case</span> _: <span class=\"type\">MalformatURLException</span> => println(<span class=\"string\">"bad url:"</span> + url)</span><br><span class=\"line\"> <span class=\"keyword\">case</span> ex: <span class=\"type\">Exception</span> => ex.printStackTrace()</span><br><span class=\"line\">} <span class=\"keyword\">finally</span>{</span><br><span class=\"line\"> /<span class=\"keyword\">do</span> something</span><br><span class=\"line\">}</span><br></pre></td></tr></table></figure>\n<h4 id=\"数组相关操作\"><a href=\"#数组相关操作\" class=\"headerlink\" title=\"数组相关操作\"></a>数组相关操作</h4><p>若长度固定可以使用Array,若长度有变化可以使用ArrayBuffer。<br>使用()来访问元素。<br>使用for(ele <- arr)来遍历元素。<br>在JVM中,scala中的Array以Java的数组方式实现。<br>类似Java中的ArrayList,Scala中有ArrayBuffer。</p>\n<figure class=\"highlight scala\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"keyword\">val</span> nums=<span class=\"keyword\">new</span> <span class=\"type\">Array</span>[<span class=\"type\">Int</span>](<span class=\"number\">10</span>)</span><br><span class=\"line\"><span class=\"keyword\">val</span> s = <span class=\"type\">Array</span>(<span class=\"string\">"Hello"</span>, <span class=\"string\">"world"</span>)</span><br><span class=\"line\"><span class=\"keyword\">import</span> scala.collection.mutable.<span class=\"type\">ArrayBuffer</span></span><br><span class=\"line\"><span class=\"keyword\">val</span> b = <span class=\"type\">ArrayBuffer</span>[<span class=\"type\">Int</span>]()</span><br><span class=\"line\"><span class=\"comment\">//b.insert(2,6)</span></span><br><span class=\"line\"><span class=\"comment\">//b.remove(2,6)</span></span><br></pre></td></tr></table></figure></li>\n</ol>\n<h4 id=\"映射和元组\"><a href=\"#映射和元组\" class=\"headerlink\" title=\"映射和元组\"></a>映射和元组</h4> <figure class=\"highlight scala\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"> <span class=\"comment\">//构造的不可变的Map[String,Int]</span></span><br><span class=\"line\"> <span class=\"keyword\">val</span> scores = <span class=\"type\">Map</span>(<span class=\"string\">"alice"</span> -> <span class=\"number\">10</span>, <span class=\"string\">"bob"</span> -> <span class=\"number\">3</span> , <span class=\"string\">"Cindy"</span> -> <span class=\"number\">8</span>)</span><br><span class=\"line\"><span class=\"comment\">// val scores = new scala.collection.mutable.HashMap[String,Int]</span></span><br><span class=\"line\"> <span class=\"keyword\">for</span> ((k,v) <- scores ) println(k)</span><br></pre></td></tr></table></figure>\n<p> 如果想按插入的顺序来访问所有键,使用LinkedHashMap,<br> val months = scala.collection.mutable.LinkedHashMap(“Jan” -> 1 ….)</p>\n<h4 id=\"类\"><a href=\"#类\" class=\"headerlink\" title=\"类\"></a>类</h4><p> 用@BeanProperty注解来生成JavaBeans的getXXX/setXXX方法。<br> scala中,类并不声明为public,scala源文件可以包含多个类,所有这些类都具有公有可见性。<br> Java的Beans规范把java属性定义为一对getFoo/setFoo方法。<br> <figure class=\"highlight scala\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"keyword\">import</span> scala.reflect.<span class=\"type\">BeanProperty</span></span><br><span class=\"line\"><span class=\"class\"><span class=\"keyword\">class</span> <span class=\"title\">Person</span> </span>{</span><br><span class=\"line\"> <span class=\"meta\">@BeanProperty</span> <span class=\"keyword\">var</span> name: <span class=\"type\">String</span> = _</span><br><span class=\"line\"> <span class=\"function\"><span class=\"keyword\">def</span> <span class=\"title\">this</span></span>(name:<span class=\"type\">String</span>) {</span><br><span class=\"line\"> <span class=\"keyword\">this</span>()</span><br><span class=\"line\"> <span class=\"keyword\">this</span>.name = name</span><br><span class=\"line\"> }</span><br><span class=\"line\">}</span><br></pre></td></tr></table></figure><br> 将会生成四个方法:</p>\n<ol>\n<li>name: String</li>\n<li>name_ = (newValue : String):Unit</li>\n<li>getName() : String</li>\n<li>setName(newValue: String):Unit</li>\n</ol>\n<p>嵌套类<br>在scala中,你几乎可以在任何语法结构中内嵌任何语法结构。<br>你可以在函数中定义函数,在类中定义类。</p>\n<h4 id=\"类和对象\"><a href=\"#类和对象\" class=\"headerlink\" title=\"类和对象\"></a>类和对象</h4><p>Scala中没有静态方法,它有一个类似的特性,叫单例对象。<br>通常,一个类对应有一个伴生对象,伴生对象的名字和类的名字相同,<br>类和伴生对象之间可以相互访问私有的方法和属性,它们必须存在于同一个源文件中。<br>伴生类生成的类中带一个$符号。<br>Scala 中伴生对象采用 object 关键字声明,伴生对象中声明的全是静态内容,可以通过伴生对象名称直接调用。<br>apply方法<br>调用object对象是默认执行的方法就是apply方法,这个方法通常用来返回伴生类的对象。<br>每个scala程序都必须从一个对象的main方法开始,可以使用App Trait或自己写main.</p>\n<figure class=\"highlight scala\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"class\"><span class=\"keyword\">object</span> <span class=\"title\">Hello</span> <span class=\"keyword\">extends</span> <span class=\"title\">App</span> </span>{</span><br><span class=\"line\"> println(<span class=\"string\">"hello world"</span>)</span><br><span class=\"line\">}</span><br><span class=\"line\"></span><br><span class=\"line\"><span class=\"class\"><span class=\"keyword\">object</span> <span class=\"title\">Test</span> </span>{</span><br><span class=\"line\"> <span class=\"function\"><span class=\"keyword\">def</span> <span class=\"title\">main</span></span>(args: <span class=\"type\">Array</span>[<span class=\"type\">String</span>]) {</span><br><span class=\"line\"> println(<span class=\"string\">"hello world"</span>)</span><br><span class=\"line\"> }</span><br><span class=\"line\">}</span><br></pre></td></tr></table></figure>"},{"title":"Scala学习Part3","date":"2021-09-09T01:09:09.000Z","comments":1,"toc":true,"_content":"scala中的方法和函数,在scala中你可以直接使用函数,但无法直接使用方法,\n方法是从属于类,函数是scala中的头等公民。\n#### 集合\n所有集合都扩展自Iterable特质。\n集合有三大类,分别是Seq,Set,Map。\n对于几乎所有集合类,scala都提供了可变和不可变版本。\n每个scala集合特质都有一个带apply方法的伴生对象,这个apply方法可以用来\n构建该集合中的实例。\n在scala中,列表要么是Nil(空表),要么是一个head元素加上一个tail,而tail本身又是\n一个列表。\n```scala\nval l=9::4:;2::Nil\ndef sum(lst: List[Int]):Int = if (lst == NIl) 0 else lst.head + sum(lst.tail)\ndef sum2( lst : List[Int]) :Int = lst match {\n case Nil => 0\n case h :: t => h + sum(t)\n}\nval lst = (1,7,2,9)\nlst.reduceLeft(_ - _ ) // 1 - 7 - 2 - 9 左结合 \nlst.reduceRight(_-_) // 1 - (7 - (2-9)) 右结合 \nlst.foldLeft(0)(_-_) //柯里化让scala可以根据初始值的类型推断出返回值的类型定义\n```\nfold可以作为循环的替代,这样做并不一定是好的,但觉得循环和改值可以被消除是一件有趣的事。\n#### 模式匹配和样例类(case class)\n##### case class\n样例类是一种特殊的类,被优化用于模式匹配。\n```scala\nsealed abstract class Amount\ncase class Dollar(value:Double) extends Amount\ncase class Currency(value: Double, unit:String) extends Amount\ncase object Nothing extends Amount\namt match {\n case Dollar(v) =>\"$\" +v\n case Currency(_,u) => \"Oh noes, I got\" + u\n case Nothing => \"\"\n}\nabstract class Item\ncase class Article(description:String, price: Double) extends Item\ncase class Bundle(description:String , discount: Double, items: Item*) extends Item\ncase bundle(_,_,Article(descr,_),_*)=>...\n//用@表示法将嵌套的值绑定到变量\ncase Bundle(_,_,art @Article(_,_), rest @ _*)\n```\n当声明case class时,如下事情自动发生:\n1. 构造器中每一个参数都成val,除非显式声明为var\n2. 在伴生对象中提供apply,unapply方法分别用于构造对象和模式匹配。\n3. 将生成toString,equals,hashCode,copy方法\n\n密封类的所有子类必须在该类相同的文件中定义,这样在编译期间它所有的子类都是可知的,因而编译器可以检查\n模式语句的完整性,这是一个好的实践方法。\n##### 模拟枚举\n```scala\nsealed abstract class TrafficLightColor\ncase object Red extends TrafficLightColor\ncase object Yellow extends TrafficLightColor\ncase object Green extends TrafficLightColor\ncolor match {\n case Red => \"stop\"\n case Yellow => \"hurry up\"\n case Green => \"go\"\n}\n```\n##### 偏函数\n被包在花括号内的一组case语句是一个偏函数,一个并非对所有输入值都有定义的函数。它是\nPartialFunction[A,B]类的一个实例,A是参数类型,B是返回值类型。\n```scala\n//PartialFunction不可省略\nval f:PartialFunction[Char,Int] = {case '+' => 1; case '-'=> -1} \nf('-')\nf('+')\nf.isDefinedAt('0')//false\nf('0') //throw MatchError\n\"-3+4\".collect(f) //Vector(-1,1)\n```\n#### 隐式转换和隐式参数\n##### **隐式转换**\n隐式转换可以丰富现有类的功能。\n隐式转换函数指的是那种以implicit关键字声明的带有单个参数的函数。\n这样的函数会被自动应用,将值从一个类型转成另一个类型。\n```scala\nval contents = new File(\"readme\").read\nclass RichFile(val from:File) {\n def read = Source.fromFile(from.getPath).mkString\n}\n```\nscala会考虑如下的隐式转换函数:\n1. 位于源或目标类型的伴生对象中的隐式函数。\n2. 位于当前作用域可以以单个标识符指代的隐式函数\n可以将引入局部化来避免不想要的转换的发生。\n\n##### **隐式参数**\n函数或方法可以带有一个标记为implicit的参数列表,这种情况下,编译器将会检查找到缺省值,\n提供给函数或方法。\n```scala\ncase class Delimiters(left:String, right:String)\ndef quote(what:String) (implicit delims:Delimiters) = \ndelims.left + what + delims.right\nquote(\"bonjour le monde\")\n//没有传递第二个隐式参数 \nimplicit val quoteDelimiters = Delimiters(\"<\",\">\")\n```","source":"_posts/Scala学习Part3.md","raw":"---\ntitle: Scala学习Part3\ndate: 2021-09-09 09:09:09\ncomments: true #是否可评论\ntoc: true #是否显示文章目录\ncategories: \"学习\" #分类\ntags: #标签\n - Scala\n---\nscala中的方法和函数,在scala中你可以直接使用函数,但无法直接使用方法,\n方法是从属于类,函数是scala中的头等公民。\n#### 集合\n所有集合都扩展自Iterable特质。\n集合有三大类,分别是Seq,Set,Map。\n对于几乎所有集合类,scala都提供了可变和不可变版本。\n每个scala集合特质都有一个带apply方法的伴生对象,这个apply方法可以用来\n构建该集合中的实例。\n在scala中,列表要么是Nil(空表),要么是一个head元素加上一个tail,而tail本身又是\n一个列表。\n```scala\nval l=9::4:;2::Nil\ndef sum(lst: List[Int]):Int = if (lst == NIl) 0 else lst.head + sum(lst.tail)\ndef sum2( lst : List[Int]) :Int = lst match {\n case Nil => 0\n case h :: t => h + sum(t)\n}\nval lst = (1,7,2,9)\nlst.reduceLeft(_ - _ ) // 1 - 7 - 2 - 9 左结合 \nlst.reduceRight(_-_) // 1 - (7 - (2-9)) 右结合 \nlst.foldLeft(0)(_-_) //柯里化让scala可以根据初始值的类型推断出返回值的类型定义\n```\nfold可以作为循环的替代,这样做并不一定是好的,但觉得循环和改值可以被消除是一件有趣的事。\n#### 模式匹配和样例类(case class)\n##### case class\n样例类是一种特殊的类,被优化用于模式匹配。\n```scala\nsealed abstract class Amount\ncase class Dollar(value:Double) extends Amount\ncase class Currency(value: Double, unit:String) extends Amount\ncase object Nothing extends Amount\namt match {\n case Dollar(v) =>\"$\" +v\n case Currency(_,u) => \"Oh noes, I got\" + u\n case Nothing => \"\"\n}\nabstract class Item\ncase class Article(description:String, price: Double) extends Item\ncase class Bundle(description:String , discount: Double, items: Item*) extends Item\ncase bundle(_,_,Article(descr,_),_*)=>...\n//用@表示法将嵌套的值绑定到变量\ncase Bundle(_,_,art @Article(_,_), rest @ _*)\n```\n当声明case class时,如下事情自动发生:\n1. 构造器中每一个参数都成val,除非显式声明为var\n2. 在伴生对象中提供apply,unapply方法分别用于构造对象和模式匹配。\n3. 将生成toString,equals,hashCode,copy方法\n\n密封类的所有子类必须在该类相同的文件中定义,这样在编译期间它所有的子类都是可知的,因而编译器可以检查\n模式语句的完整性,这是一个好的实践方法。\n##### 模拟枚举\n```scala\nsealed abstract class TrafficLightColor\ncase object Red extends TrafficLightColor\ncase object Yellow extends TrafficLightColor\ncase object Green extends TrafficLightColor\ncolor match {\n case Red => \"stop\"\n case Yellow => \"hurry up\"\n case Green => \"go\"\n}\n```\n##### 偏函数\n被包在花括号内的一组case语句是一个偏函数,一个并非对所有输入值都有定义的函数。它是\nPartialFunction[A,B]类的一个实例,A是参数类型,B是返回值类型。\n```scala\n//PartialFunction不可省略\nval f:PartialFunction[Char,Int] = {case '+' => 1; case '-'=> -1} \nf('-')\nf('+')\nf.isDefinedAt('0')//false\nf('0') //throw MatchError\n\"-3+4\".collect(f) //Vector(-1,1)\n```\n#### 隐式转换和隐式参数\n##### **隐式转换**\n隐式转换可以丰富现有类的功能。\n隐式转换函数指的是那种以implicit关键字声明的带有单个参数的函数。\n这样的函数会被自动应用,将值从一个类型转成另一个类型。\n```scala\nval contents = new File(\"readme\").read\nclass RichFile(val from:File) {\n def read = Source.fromFile(from.getPath).mkString\n}\n```\nscala会考虑如下的隐式转换函数:\n1. 位于源或目标类型的伴生对象中的隐式函数。\n2. 位于当前作用域可以以单个标识符指代的隐式函数\n可以将引入局部化来避免不想要的转换的发生。\n\n##### **隐式参数**\n函数或方法可以带有一个标记为implicit的参数列表,这种情况下,编译器将会检查找到缺省值,\n提供给函数或方法。\n```scala\ncase class Delimiters(left:String, right:String)\ndef quote(what:String) (implicit delims:Delimiters) = \ndelims.left + what + delims.right\nquote(\"bonjour le monde\")\n//没有传递第二个隐式参数 \nimplicit val quoteDelimiters = Delimiters(\"<\",\">\")\n```","slug":"Scala学习Part3","published":1,"updated":"2022-04-20T09:35:50.863Z","layout":"post","photos":[],"link":"","_id":"cl27q3uv3000hn94ohmkj81sp","content":"<p>scala中的方法和函数,在scala中你可以直接使用函数,但无法直接使用方法,<br>方法是从属于类,函数是scala中的头等公民。</p>\n<h4 id=\"集合\"><a href=\"#集合\" class=\"headerlink\" title=\"集合\"></a>集合</h4><p>所有集合都扩展自Iterable特质。<br>集合有三大类,分别是Seq,Set,Map。<br>对于几乎所有集合类,scala都提供了可变和不可变版本。<br>每个scala集合特质都有一个带apply方法的伴生对象,这个apply方法可以用来<br>构建该集合中的实例。<br>在scala中,列表要么是Nil(空表),要么是一个head元素加上一个tail,而tail本身又是<br>一个列表。</p>\n<figure class=\"highlight scala\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"keyword\">val</span> l=<span class=\"number\">9</span>::<span class=\"number\">4</span>:;<span class=\"number\">2</span>::<span class=\"type\">Nil</span></span><br><span class=\"line\"><span class=\"function\"><span class=\"keyword\">def</span> <span class=\"title\">sum</span></span>(lst: <span class=\"type\">List</span>[<span class=\"type\">Int</span>]):<span class=\"type\">Int</span> = <span class=\"keyword\">if</span> (lst == <span class=\"type\">NIl</span>) <span class=\"number\">0</span> <span class=\"keyword\">else</span> lst.head + sum(lst.tail)</span><br><span class=\"line\"><span class=\"function\"><span class=\"keyword\">def</span> <span class=\"title\">sum2</span></span>( lst : <span class=\"type\">List</span>[<span class=\"type\">Int</span>]) :<span class=\"type\">Int</span> = lst <span class=\"keyword\">match</span> {</span><br><span class=\"line\"> <span class=\"keyword\">case</span> <span class=\"type\">Nil</span> => <span class=\"number\">0</span></span><br><span class=\"line\"> <span class=\"keyword\">case</span> h :: t => h + sum(t)</span><br><span class=\"line\">}</span><br><span class=\"line\"><span class=\"keyword\">val</span> lst = (<span class=\"number\">1</span>,<span class=\"number\">7</span>,<span class=\"number\">2</span>,<span class=\"number\">9</span>)</span><br><span class=\"line\">lst.reduceLeft(_ - _ ) <span class=\"comment\">// 1 - 7 - 2 - 9 左结合 </span></span><br><span class=\"line\">lst.reduceRight(_-_) <span class=\"comment\">// 1 - (7 - (2-9)) 右结合 </span></span><br><span class=\"line\">lst.foldLeft(<span class=\"number\">0</span>)(_-_) <span class=\"comment\">//柯里化让scala可以根据初始值的类型推断出返回值的类型定义</span></span><br></pre></td></tr></table></figure>\n<p>fold可以作为循环的替代,这样做并不一定是好的,但觉得循环和改值可以被消除是一件有趣的事。</p>\n<h4 id=\"模式匹配和样例类-case-class\"><a href=\"#模式匹配和样例类-case-class\" class=\"headerlink\" title=\"模式匹配和样例类(case class)\"></a>模式匹配和样例类(case class)</h4><h5 id=\"case-class\"><a href=\"#case-class\" class=\"headerlink\" title=\"case class\"></a>case class</h5><p>样例类是一种特殊的类,被优化用于模式匹配。</p>\n<figure class=\"highlight scala\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br><span class=\"line\">11</span><br><span class=\"line\">12</span><br><span class=\"line\">13</span><br><span class=\"line\">14</span><br><span class=\"line\">15</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"keyword\">sealed</span> <span class=\"keyword\">abstract</span> <span class=\"class\"><span class=\"keyword\">class</span> <span class=\"title\">Amount</span></span></span><br><span class=\"line\"><span class=\"keyword\">case</span> <span class=\"class\"><span class=\"keyword\">class</span> <span class=\"title\">Dollar</span>(<span class=\"params\">value:<span class=\"type\">Double</span></span>) <span class=\"keyword\">extends</span> <span class=\"title\">Amount</span></span></span><br><span class=\"line\"><span class=\"keyword\">case</span> <span class=\"class\"><span class=\"keyword\">class</span> <span class=\"title\">Currency</span>(<span class=\"params\">value: <span class=\"type\">Double</span>, unit:<span class=\"type\">String</span></span>) <span class=\"keyword\">extends</span> <span class=\"title\">Amount</span></span></span><br><span class=\"line\"><span class=\"keyword\">case</span> <span class=\"class\"><span class=\"keyword\">object</span> <span class=\"title\">Nothing</span> <span class=\"keyword\">extends</span> <span class=\"title\">Amount</span></span></span><br><span class=\"line\">amt <span class=\"keyword\">match</span> {</span><br><span class=\"line\"> <span class=\"keyword\">case</span> <span class=\"type\">Dollar</span>(v) =><span class=\"string\">"$"</span> +v</span><br><span class=\"line\"> <span class=\"keyword\">case</span> <span class=\"type\">Currency</span>(_,u) => <span class=\"string\">"Oh noes, I got"</span> + u</span><br><span class=\"line\"> <span class=\"keyword\">case</span> <span class=\"type\">Nothing</span> => <span class=\"string\">""</span></span><br><span class=\"line\">}</span><br><span class=\"line\"><span class=\"keyword\">abstract</span> <span class=\"class\"><span class=\"keyword\">class</span> <span class=\"title\">Item</span></span></span><br><span class=\"line\"><span class=\"keyword\">case</span> <span class=\"class\"><span class=\"keyword\">class</span> <span class=\"title\">Article</span>(<span class=\"params\">description:<span class=\"type\">String</span>, price: <span class=\"type\">Double</span></span>) <span class=\"keyword\">extends</span> <span class=\"title\">Item</span></span></span><br><span class=\"line\"><span class=\"keyword\">case</span> <span class=\"class\"><span class=\"keyword\">class</span> <span class=\"title\">Bundle</span>(<span class=\"params\">description:<span class=\"type\">String</span> , discount: <span class=\"type\">Double</span>, items: <span class=\"type\">Item</span>*</span>) <span class=\"keyword\">extends</span> <span class=\"title\">Item</span></span></span><br><span class=\"line\"><span class=\"keyword\">case</span> bundle(_,_,<span class=\"type\">Article</span>(descr,_),_*)=>...</span><br><span class=\"line\"><span class=\"comment\">//用@表示法将嵌套的值绑定到变量</span></span><br><span class=\"line\"><span class=\"keyword\">case</span> <span class=\"type\">Bundle</span>(_,_,art <span class=\"meta\">@Article</span>(_,_), rest @ _*)</span><br></pre></td></tr></table></figure>\n<p>当声明case class时,如下事情自动发生:</p>\n<ol>\n<li>构造器中每一个参数都成val,除非显式声明为var</li>\n<li>在伴生对象中提供apply,unapply方法分别用于构造对象和模式匹配。</li>\n<li>将生成toString,equals,hashCode,copy方法</li>\n</ol>\n<p>密封类的所有子类必须在该类相同的文件中定义,这样在编译期间它所有的子类都是可知的,因而编译器可以检查<br>模式语句的完整性,这是一个好的实践方法。</p>\n<h5 id=\"模拟枚举\"><a href=\"#模拟枚举\" class=\"headerlink\" title=\"模拟枚举\"></a>模拟枚举</h5><figure class=\"highlight scala\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"keyword\">sealed</span> <span class=\"keyword\">abstract</span> <span class=\"class\"><span class=\"keyword\">class</span> <span class=\"title\">TrafficLightColor</span></span></span><br><span class=\"line\"><span class=\"keyword\">case</span> <span class=\"class\"><span class=\"keyword\">object</span> <span class=\"title\">Red</span> <span class=\"keyword\">extends</span> <span class=\"title\">TrafficLightColor</span></span></span><br><span class=\"line\"><span class=\"keyword\">case</span> <span class=\"class\"><span class=\"keyword\">object</span> <span class=\"title\">Yellow</span> <span class=\"keyword\">extends</span> <span class=\"title\">TrafficLightColor</span></span></span><br><span class=\"line\"><span class=\"keyword\">case</span> <span class=\"class\"><span class=\"keyword\">object</span> <span class=\"title\">Green</span> <span class=\"keyword\">extends</span> <span class=\"title\">TrafficLightColor</span></span></span><br><span class=\"line\">color <span class=\"keyword\">match</span> {</span><br><span class=\"line\"> <span class=\"keyword\">case</span> <span class=\"type\">Red</span> => <span class=\"string\">"stop"</span></span><br><span class=\"line\"> <span class=\"keyword\">case</span> <span class=\"type\">Yellow</span> => <span class=\"string\">"hurry up"</span></span><br><span class=\"line\"> <span class=\"keyword\">case</span> <span class=\"type\">Green</span> => <span class=\"string\">"go"</span></span><br><span class=\"line\">}</span><br></pre></td></tr></table></figure>\n<h5 id=\"偏函数\"><a href=\"#偏函数\" class=\"headerlink\" title=\"偏函数\"></a>偏函数</h5><p>被包在花括号内的一组case语句是一个偏函数,一个并非对所有输入值都有定义的函数。它是<br>PartialFunction[A,B]类的一个实例,A是参数类型,B是返回值类型。</p>\n<figure class=\"highlight scala\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"comment\">//PartialFunction不可省略</span></span><br><span class=\"line\"><span class=\"keyword\">val</span> f:<span class=\"type\">PartialFunction</span>[<span class=\"type\">Char</span>,<span class=\"type\">Int</span>] = {<span class=\"keyword\">case</span> '+' => <span class=\"number\">1</span>; <span class=\"keyword\">case</span> '-'=> <span class=\"number\">-1</span>} </span><br><span class=\"line\">f('-')</span><br><span class=\"line\">f('+')</span><br><span class=\"line\">f.isDefinedAt('<span class=\"number\">0</span>')<span class=\"comment\">//false</span></span><br><span class=\"line\">f('<span class=\"number\">0</span>') <span class=\"comment\">//throw MatchError</span></span><br><span class=\"line\"><span class=\"string\">"-3+4"</span>.collect(f) <span class=\"comment\">//Vector(-1,1)</span></span><br></pre></td></tr></table></figure>\n<h4 id=\"隐式转换和隐式参数\"><a href=\"#隐式转换和隐式参数\" class=\"headerlink\" title=\"隐式转换和隐式参数\"></a>隐式转换和隐式参数</h4><h5 id=\"隐式转换\"><a href=\"#隐式转换\" class=\"headerlink\" title=\"隐式转换\"></a><strong>隐式转换</strong></h5><p>隐式转换可以丰富现有类的功能。<br>隐式转换函数指的是那种以implicit关键字声明的带有单个参数的函数。<br>这样的函数会被自动应用,将值从一个类型转成另一个类型。</p>\n<figure class=\"highlight scala\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"keyword\">val</span> contents = <span class=\"keyword\">new</span> <span class=\"type\">File</span>(<span class=\"string\">"readme"</span>).read</span><br><span class=\"line\"><span class=\"class\"><span class=\"keyword\">class</span> <span class=\"title\">RichFile</span>(<span class=\"params\">val from:<span class=\"type\">File</span></span>) </span>{</span><br><span class=\"line\"> <span class=\"function\"><span class=\"keyword\">def</span> <span class=\"title\">read</span> </span>= <span class=\"type\">Source</span>.fromFile(from.getPath).mkString</span><br><span class=\"line\">}</span><br></pre></td></tr></table></figure>\n<p>scala会考虑如下的隐式转换函数:</p>\n<ol>\n<li>位于源或目标类型的伴生对象中的隐式函数。</li>\n<li>位于当前作用域可以以单个标识符指代的隐式函数<br>可以将引入局部化来避免不想要的转换的发生。</li>\n</ol>\n<h5 id=\"隐式参数\"><a href=\"#隐式参数\" class=\"headerlink\" title=\"隐式参数\"></a><strong>隐式参数</strong></h5><p>函数或方法可以带有一个标记为implicit的参数列表,这种情况下,编译器将会检查找到缺省值,<br>提供给函数或方法。</p>\n<figure class=\"highlight scala\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"keyword\">case</span> <span class=\"class\"><span class=\"keyword\">class</span> <span class=\"title\">Delimiters</span>(<span class=\"params\">left:<span class=\"type\">String</span>, right:<span class=\"type\">String</span></span>)</span></span><br><span class=\"line\"><span class=\"function\"><span class=\"keyword\">def</span> <span class=\"title\">quote</span></span>(what:<span class=\"type\">String</span>) (<span class=\"keyword\">implicit</span> delims:<span class=\"type\">Delimiters</span>) = </span><br><span class=\"line\">delims.left + what + delims.right</span><br><span class=\"line\">quote(<span class=\"string\">"bonjour le monde"</span>)</span><br><span class=\"line\"><span class=\"comment\">//没有传递第二个隐式参数 </span></span><br><span class=\"line\"><span class=\"keyword\">implicit</span> <span class=\"keyword\">val</span> quoteDelimiters = <span class=\"type\">Delimiters</span>(<span class=\"string\">"<"</span>,<span class=\"string\">">"</span>)</span><br></pre></td></tr></table></figure>","site":{"data":{}},"excerpt":"","more":"<p>scala中的方法和函数,在scala中你可以直接使用函数,但无法直接使用方法,<br>方法是从属于类,函数是scala中的头等公民。</p>\n<h4 id=\"集合\"><a href=\"#集合\" class=\"headerlink\" title=\"集合\"></a>集合</h4><p>所有集合都扩展自Iterable特质。<br>集合有三大类,分别是Seq,Set,Map。<br>对于几乎所有集合类,scala都提供了可变和不可变版本。<br>每个scala集合特质都有一个带apply方法的伴生对象,这个apply方法可以用来<br>构建该集合中的实例。<br>在scala中,列表要么是Nil(空表),要么是一个head元素加上一个tail,而tail本身又是<br>一个列表。</p>\n<figure class=\"highlight scala\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"keyword\">val</span> l=<span class=\"number\">9</span>::<span class=\"number\">4</span>:;<span class=\"number\">2</span>::<span class=\"type\">Nil</span></span><br><span class=\"line\"><span class=\"function\"><span class=\"keyword\">def</span> <span class=\"title\">sum</span></span>(lst: <span class=\"type\">List</span>[<span class=\"type\">Int</span>]):<span class=\"type\">Int</span> = <span class=\"keyword\">if</span> (lst == <span class=\"type\">NIl</span>) <span class=\"number\">0</span> <span class=\"keyword\">else</span> lst.head + sum(lst.tail)</span><br><span class=\"line\"><span class=\"function\"><span class=\"keyword\">def</span> <span class=\"title\">sum2</span></span>( lst : <span class=\"type\">List</span>[<span class=\"type\">Int</span>]) :<span class=\"type\">Int</span> = lst <span class=\"keyword\">match</span> {</span><br><span class=\"line\"> <span class=\"keyword\">case</span> <span class=\"type\">Nil</span> => <span class=\"number\">0</span></span><br><span class=\"line\"> <span class=\"keyword\">case</span> h :: t => h + sum(t)</span><br><span class=\"line\">}</span><br><span class=\"line\"><span class=\"keyword\">val</span> lst = (<span class=\"number\">1</span>,<span class=\"number\">7</span>,<span class=\"number\">2</span>,<span class=\"number\">9</span>)</span><br><span class=\"line\">lst.reduceLeft(_ - _ ) <span class=\"comment\">// 1 - 7 - 2 - 9 左结合 </span></span><br><span class=\"line\">lst.reduceRight(_-_) <span class=\"comment\">// 1 - (7 - (2-9)) 右结合 </span></span><br><span class=\"line\">lst.foldLeft(<span class=\"number\">0</span>)(_-_) <span class=\"comment\">//柯里化让scala可以根据初始值的类型推断出返回值的类型定义</span></span><br></pre></td></tr></table></figure>\n<p>fold可以作为循环的替代,这样做并不一定是好的,但觉得循环和改值可以被消除是一件有趣的事。</p>\n<h4 id=\"模式匹配和样例类-case-class\"><a href=\"#模式匹配和样例类-case-class\" class=\"headerlink\" title=\"模式匹配和样例类(case class)\"></a>模式匹配和样例类(case class)</h4><h5 id=\"case-class\"><a href=\"#case-class\" class=\"headerlink\" title=\"case class\"></a>case class</h5><p>样例类是一种特殊的类,被优化用于模式匹配。</p>\n<figure class=\"highlight scala\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br><span class=\"line\">11</span><br><span class=\"line\">12</span><br><span class=\"line\">13</span><br><span class=\"line\">14</span><br><span class=\"line\">15</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"keyword\">sealed</span> <span class=\"keyword\">abstract</span> <span class=\"class\"><span class=\"keyword\">class</span> <span class=\"title\">Amount</span></span></span><br><span class=\"line\"><span class=\"keyword\">case</span> <span class=\"class\"><span class=\"keyword\">class</span> <span class=\"title\">Dollar</span>(<span class=\"params\">value:<span class=\"type\">Double</span></span>) <span class=\"keyword\">extends</span> <span class=\"title\">Amount</span></span></span><br><span class=\"line\"><span class=\"keyword\">case</span> <span class=\"class\"><span class=\"keyword\">class</span> <span class=\"title\">Currency</span>(<span class=\"params\">value: <span class=\"type\">Double</span>, unit:<span class=\"type\">String</span></span>) <span class=\"keyword\">extends</span> <span class=\"title\">Amount</span></span></span><br><span class=\"line\"><span class=\"keyword\">case</span> <span class=\"class\"><span class=\"keyword\">object</span> <span class=\"title\">Nothing</span> <span class=\"keyword\">extends</span> <span class=\"title\">Amount</span></span></span><br><span class=\"line\">amt <span class=\"keyword\">match</span> {</span><br><span class=\"line\"> <span class=\"keyword\">case</span> <span class=\"type\">Dollar</span>(v) =><span class=\"string\">"$"</span> +v</span><br><span class=\"line\"> <span class=\"keyword\">case</span> <span class=\"type\">Currency</span>(_,u) => <span class=\"string\">"Oh noes, I got"</span> + u</span><br><span class=\"line\"> <span class=\"keyword\">case</span> <span class=\"type\">Nothing</span> => <span class=\"string\">""</span></span><br><span class=\"line\">}</span><br><span class=\"line\"><span class=\"keyword\">abstract</span> <span class=\"class\"><span class=\"keyword\">class</span> <span class=\"title\">Item</span></span></span><br><span class=\"line\"><span class=\"keyword\">case</span> <span class=\"class\"><span class=\"keyword\">class</span> <span class=\"title\">Article</span>(<span class=\"params\">description:<span class=\"type\">String</span>, price: <span class=\"type\">Double</span></span>) <span class=\"keyword\">extends</span> <span class=\"title\">Item</span></span></span><br><span class=\"line\"><span class=\"keyword\">case</span> <span class=\"class\"><span class=\"keyword\">class</span> <span class=\"title\">Bundle</span>(<span class=\"params\">description:<span class=\"type\">String</span> , discount: <span class=\"type\">Double</span>, items: <span class=\"type\">Item</span>*</span>) <span class=\"keyword\">extends</span> <span class=\"title\">Item</span></span></span><br><span class=\"line\"><span class=\"keyword\">case</span> bundle(_,_,<span class=\"type\">Article</span>(descr,_),_*)=>...</span><br><span class=\"line\"><span class=\"comment\">//用@表示法将嵌套的值绑定到变量</span></span><br><span class=\"line\"><span class=\"keyword\">case</span> <span class=\"type\">Bundle</span>(_,_,art <span class=\"meta\">@Article</span>(_,_), rest @ _*)</span><br></pre></td></tr></table></figure>\n<p>当声明case class时,如下事情自动发生:</p>\n<ol>\n<li>构造器中每一个参数都成val,除非显式声明为var</li>\n<li>在伴生对象中提供apply,unapply方法分别用于构造对象和模式匹配。</li>\n<li>将生成toString,equals,hashCode,copy方法</li>\n</ol>\n<p>密封类的所有子类必须在该类相同的文件中定义,这样在编译期间它所有的子类都是可知的,因而编译器可以检查<br>模式语句的完整性,这是一个好的实践方法。</p>\n<h5 id=\"模拟枚举\"><a href=\"#模拟枚举\" class=\"headerlink\" title=\"模拟枚举\"></a>模拟枚举</h5><figure class=\"highlight scala\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"keyword\">sealed</span> <span class=\"keyword\">abstract</span> <span class=\"class\"><span class=\"keyword\">class</span> <span class=\"title\">TrafficLightColor</span></span></span><br><span class=\"line\"><span class=\"keyword\">case</span> <span class=\"class\"><span class=\"keyword\">object</span> <span class=\"title\">Red</span> <span class=\"keyword\">extends</span> <span class=\"title\">TrafficLightColor</span></span></span><br><span class=\"line\"><span class=\"keyword\">case</span> <span class=\"class\"><span class=\"keyword\">object</span> <span class=\"title\">Yellow</span> <span class=\"keyword\">extends</span> <span class=\"title\">TrafficLightColor</span></span></span><br><span class=\"line\"><span class=\"keyword\">case</span> <span class=\"class\"><span class=\"keyword\">object</span> <span class=\"title\">Green</span> <span class=\"keyword\">extends</span> <span class=\"title\">TrafficLightColor</span></span></span><br><span class=\"line\">color <span class=\"keyword\">match</span> {</span><br><span class=\"line\"> <span class=\"keyword\">case</span> <span class=\"type\">Red</span> => <span class=\"string\">"stop"</span></span><br><span class=\"line\"> <span class=\"keyword\">case</span> <span class=\"type\">Yellow</span> => <span class=\"string\">"hurry up"</span></span><br><span class=\"line\"> <span class=\"keyword\">case</span> <span class=\"type\">Green</span> => <span class=\"string\">"go"</span></span><br><span class=\"line\">}</span><br></pre></td></tr></table></figure>\n<h5 id=\"偏函数\"><a href=\"#偏函数\" class=\"headerlink\" title=\"偏函数\"></a>偏函数</h5><p>被包在花括号内的一组case语句是一个偏函数,一个并非对所有输入值都有定义的函数。它是<br>PartialFunction[A,B]类的一个实例,A是参数类型,B是返回值类型。</p>\n<figure class=\"highlight scala\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"comment\">//PartialFunction不可省略</span></span><br><span class=\"line\"><span class=\"keyword\">val</span> f:<span class=\"type\">PartialFunction</span>[<span class=\"type\">Char</span>,<span class=\"type\">Int</span>] = {<span class=\"keyword\">case</span> '+' => <span class=\"number\">1</span>; <span class=\"keyword\">case</span> '-'=> <span class=\"number\">-1</span>} </span><br><span class=\"line\">f('-')</span><br><span class=\"line\">f('+')</span><br><span class=\"line\">f.isDefinedAt('<span class=\"number\">0</span>')<span class=\"comment\">//false</span></span><br><span class=\"line\">f('<span class=\"number\">0</span>') <span class=\"comment\">//throw MatchError</span></span><br><span class=\"line\"><span class=\"string\">"-3+4"</span>.collect(f) <span class=\"comment\">//Vector(-1,1)</span></span><br></pre></td></tr></table></figure>\n<h4 id=\"隐式转换和隐式参数\"><a href=\"#隐式转换和隐式参数\" class=\"headerlink\" title=\"隐式转换和隐式参数\"></a>隐式转换和隐式参数</h4><h5 id=\"隐式转换\"><a href=\"#隐式转换\" class=\"headerlink\" title=\"隐式转换\"></a><strong>隐式转换</strong></h5><p>隐式转换可以丰富现有类的功能。<br>隐式转换函数指的是那种以implicit关键字声明的带有单个参数的函数。<br>这样的函数会被自动应用,将值从一个类型转成另一个类型。</p>\n<figure class=\"highlight scala\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"keyword\">val</span> contents = <span class=\"keyword\">new</span> <span class=\"type\">File</span>(<span class=\"string\">"readme"</span>).read</span><br><span class=\"line\"><span class=\"class\"><span class=\"keyword\">class</span> <span class=\"title\">RichFile</span>(<span class=\"params\">val from:<span class=\"type\">File</span></span>) </span>{</span><br><span class=\"line\"> <span class=\"function\"><span class=\"keyword\">def</span> <span class=\"title\">read</span> </span>= <span class=\"type\">Source</span>.fromFile(from.getPath).mkString</span><br><span class=\"line\">}</span><br></pre></td></tr></table></figure>\n<p>scala会考虑如下的隐式转换函数:</p>\n<ol>\n<li>位于源或目标类型的伴生对象中的隐式函数。</li>\n<li>位于当前作用域可以以单个标识符指代的隐式函数<br>可以将引入局部化来避免不想要的转换的发生。</li>\n</ol>\n<h5 id=\"隐式参数\"><a href=\"#隐式参数\" class=\"headerlink\" title=\"隐式参数\"></a><strong>隐式参数</strong></h5><p>函数或方法可以带有一个标记为implicit的参数列表,这种情况下,编译器将会检查找到缺省值,<br>提供给函数或方法。</p>\n<figure class=\"highlight scala\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"keyword\">case</span> <span class=\"class\"><span class=\"keyword\">class</span> <span class=\"title\">Delimiters</span>(<span class=\"params\">left:<span class=\"type\">String</span>, right:<span class=\"type\">String</span></span>)</span></span><br><span class=\"line\"><span class=\"function\"><span class=\"keyword\">def</span> <span class=\"title\">quote</span></span>(what:<span class=\"type\">String</span>) (<span class=\"keyword\">implicit</span> delims:<span class=\"type\">Delimiters</span>) = </span><br><span class=\"line\">delims.left + what + delims.right</span><br><span class=\"line\">quote(<span class=\"string\">"bonjour le monde"</span>)</span><br><span class=\"line\"><span class=\"comment\">//没有传递第二个隐式参数 </span></span><br><span class=\"line\"><span class=\"keyword\">implicit</span> <span class=\"keyword\">val</span> quoteDelimiters = <span class=\"type\">Delimiters</span>(<span class=\"string\">"<"</span>,<span class=\"string\">">"</span>)</span><br></pre></td></tr></table></figure>"},{"title":"Hexo博客","date":"2022-04-19T16:00:00.000Z","toc":true,"_content":" 使用git pages和hexo来搭建自己的博客,是一件幸福的事。\n\n1. 安装nodejs https://nodejs.org/en/ 下载安装pkg\n 安装nodejs的时候一开始安装的16,在install moudle的时候报错,删除后重新安装14,才可以使用,\n 这点需要注意。\n\n2. npm 安装hexo\n npm配置国内的访问地址 \n ```bash\n npm config set registry https://registry.npm.taobao.org \n sudo npm install -g hexo-cli \n npm install hexo -g #安装Hexo\n hexo n \"我的博客\" == hexo new \"我的博客\" #新建文章\n hexo g == hexo generate #生成\n hexo s == hexo server #启动服务预览\n hexo d == hexo deploy #部署\n\n hexo server #Hexo会监视文件变动并自动更新,无须重启服务器\n hexo server -s #静态模式\n ```\n\n3. 需要重新安装主题,主题没有默认提交到github\n ```bash\n git clone https://github.com/tufu9441/maupassant-hexo.git themes/maupassant\n npm install hexo-renderer-pug --save\n npm install hexo-renderer-sass --save\n ```\n\n4. 图床\n 我还是使用github,先把img上传到github上去,然后在md文档中使用。可以参考下面的例子:\n ```bash\n ![老边沟枫叶](https://raw.githubusercontent.com/student2028/blog_markdown/master/img/fengye1.jpeg)\n ```\n5. hexo d执行的时候,需要提前把本机的.ssh中的pub配置到github上,同时需要执行下面两行命令。\n ```bash\n git config --global user.email \"dailyyaoxh@126.com\"\n git config --global user.name \"student2028\"\n ```\n","source":"_posts/hexo.md","raw":"---\ntitle: Hexo博客\ndate: 2022-04-20\ntoc: true #是否显示文章目录\ncategories: \"生活\" #分类\ntags: #标签\n - 博客\n---\n 使用git pages和hexo来搭建自己的博客,是一件幸福的事。\n\n1. 安装nodejs https://nodejs.org/en/ 下载安装pkg\n 安装nodejs的时候一开始安装的16,在install moudle的时候报错,删除后重新安装14,才可以使用,\n 这点需要注意。\n\n2. npm 安装hexo\n npm配置国内的访问地址 \n ```bash\n npm config set registry https://registry.npm.taobao.org \n sudo npm install -g hexo-cli \n npm install hexo -g #安装Hexo\n hexo n \"我的博客\" == hexo new \"我的博客\" #新建文章\n hexo g == hexo generate #生成\n hexo s == hexo server #启动服务预览\n hexo d == hexo deploy #部署\n\n hexo server #Hexo会监视文件变动并自动更新,无须重启服务器\n hexo server -s #静态模式\n ```\n\n3. 需要重新安装主题,主题没有默认提交到github\n ```bash\n git clone https://github.com/tufu9441/maupassant-hexo.git themes/maupassant\n npm install hexo-renderer-pug --save\n npm install hexo-renderer-sass --save\n ```\n\n4. 图床\n 我还是使用github,先把img上传到github上去,然后在md文档中使用。可以参考下面的例子:\n ```bash\n ![老边沟枫叶](https://raw.githubusercontent.com/student2028/blog_markdown/master/img/fengye1.jpeg)\n ```\n5. hexo d执行的时候,需要提前把本机的.ssh中的pub配置到github上,同时需要执行下面两行命令。\n ```bash\n git config --global user.email \"dailyyaoxh@126.com\"\n git config --global user.name \"student2028\"\n ```\n","slug":"hexo","published":1,"updated":"2022-04-20T15:21:50.822Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cl27q3uv4000jn94ohkt19a72","content":"<p> 使用git pages和hexo来搭建自己的博客,是一件幸福的事。</p>\n<ol>\n<li><p>安装nodejs <a href=\"https://nodejs.org/en/\">https://nodejs.org/en/</a> 下载安装pkg<br>安装nodejs的时候一开始安装的16,在install moudle的时候报错,删除后重新安装14,才可以使用,<br>这点需要注意。</p>\n</li>\n<li><p>npm 安装hexo<br>npm配置国内的访问地址 </p>\n <figure class=\"highlight bash\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">npm config <span class=\"built_in\">set</span> registry https://registry.npm.taobao.org </span><br><span class=\"line\"> sudo npm install -g hexo-cli </span><br><span class=\"line\"> npm install hexo -g <span class=\"comment\">#安装Hexo</span></span><br><span class=\"line\"> hexo n <span class=\"string\">"我的博客"</span> == hexo new <span class=\"string\">"我的博客"</span> <span class=\"comment\">#新建文章</span></span><br><span class=\"line\"> hexo g == hexo generate <span class=\"comment\">#生成</span></span><br><span class=\"line\"> hexo s == hexo server <span class=\"comment\">#启动服务预览</span></span><br><span class=\"line\"> hexo d == hexo deploy <span class=\"comment\">#部署</span></span><br><span class=\"line\"></span><br><span class=\"line\"> hexo server <span class=\"comment\">#Hexo会监视文件变动并自动更新,无须重启服务器</span></span><br><span class=\"line\"> hexo server -s <span class=\"comment\">#静态模式</span></span><br></pre></td></tr></table></figure></li>\n<li><p>需要重新安装主题,主题没有默认提交到github</p>\n<figure class=\"highlight bash\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">git <span class=\"built_in\">clone</span> https://github.com/tufu9441/maupassant-hexo.git themes/maupassant</span><br><span class=\"line\">npm install hexo-renderer-pug --save</span><br><span class=\"line\">npm install hexo-renderer-sass --save</span><br></pre></td></tr></table></figure></li>\n<li><p>图床<br>我还是使用github,先把img上传到github上去,然后在md文档中使用。可以参考下面的例子:</p>\n<figure class=\"highlight bash\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">![老边沟枫叶](https://raw.githubusercontent.com/student2028/blog_markdown/master/img/fengye1.jpeg)</span><br></pre></td></tr></table></figure></li>\n<li><p>hexo d执行的时候,需要提前把本机的.ssh中的pub配置到github上,同时需要执行下面两行命令。</p>\n<figure class=\"highlight bash\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">git config --global user.email <span class=\"string\">"dailyyaoxh@126.com"</span></span><br><span class=\"line\">git config --global user.name <span class=\"string\">"student2028"</span></span><br></pre></td></tr></table></figure></li>\n</ol>\n","site":{"data":{}},"excerpt":"","more":"<p> 使用git pages和hexo来搭建自己的博客,是一件幸福的事。</p>\n<ol>\n<li><p>安装nodejs <a href=\"https://nodejs.org/en/\">https://nodejs.org/en/</a> 下载安装pkg<br>安装nodejs的时候一开始安装的16,在install moudle的时候报错,删除后重新安装14,才可以使用,<br>这点需要注意。</p>\n</li>\n<li><p>npm 安装hexo<br>npm配置国内的访问地址 </p>\n <figure class=\"highlight bash\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">npm config <span class=\"built_in\">set</span> registry https://registry.npm.taobao.org </span><br><span class=\"line\"> sudo npm install -g hexo-cli </span><br><span class=\"line\"> npm install hexo -g <span class=\"comment\">#安装Hexo</span></span><br><span class=\"line\"> hexo n <span class=\"string\">"我的博客"</span> == hexo new <span class=\"string\">"我的博客"</span> <span class=\"comment\">#新建文章</span></span><br><span class=\"line\"> hexo g == hexo generate <span class=\"comment\">#生成</span></span><br><span class=\"line\"> hexo s == hexo server <span class=\"comment\">#启动服务预览</span></span><br><span class=\"line\"> hexo d == hexo deploy <span class=\"comment\">#部署</span></span><br><span class=\"line\"></span><br><span class=\"line\"> hexo server <span class=\"comment\">#Hexo会监视文件变动并自动更新,无须重启服务器</span></span><br><span class=\"line\"> hexo server -s <span class=\"comment\">#静态模式</span></span><br></pre></td></tr></table></figure></li>\n<li><p>需要重新安装主题,主题没有默认提交到github</p>\n<figure class=\"highlight bash\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">git <span class=\"built_in\">clone</span> https://github.com/tufu9441/maupassant-hexo.git themes/maupassant</span><br><span class=\"line\">npm install hexo-renderer-pug --save</span><br><span class=\"line\">npm install hexo-renderer-sass --save</span><br></pre></td></tr></table></figure></li>\n<li><p>图床<br>我还是使用github,先把img上传到github上去,然后在md文档中使用。可以参考下面的例子:</p>\n<figure class=\"highlight bash\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">![老边沟枫叶](https://raw.githubusercontent.com/student2028/blog_markdown/master/img/fengye1.jpeg)</span><br></pre></td></tr></table></figure></li>\n<li><p>hexo d执行的时候,需要提前把本机的.ssh中的pub配置到github上,同时需要执行下面两行命令。</p>\n<figure class=\"highlight bash\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">git config --global user.email <span class=\"string\">"dailyyaoxh@126.com"</span></span><br><span class=\"line\">git config --global user.name <span class=\"string\">"student2028"</span></span><br></pre></td></tr></table></figure></li>\n</ol>\n"},{"title":"面向对象的JavaScript","date":"2021-07-04T16:00:00.000Z","comments":1,"toc":true,"_content":"### 面向对象的JavaScript\n\n#### 1.1 JavaScript中的对象\n\n##### 1.1.1 对象字面量\n\n> 对象字面量是由一对花括号和其中的键值对组成的。\n\n##### 1.1.2 类\n\n> 在Javascript中,只需要定义一个普通的函数,我们就能获取一个类。\n\n###### 1.1.2.1 通过原型添加属性和方法\n\n> Javascript中的每一个函数,都有一个prototype的属性,这个属性指向一个对象。\n>\n> 我们用new 来创建一个类的对象实例时,实例中所包含的属性和方法都来自prototype所指向的\n>\n> 这个对象。\n\n```javascript\n//定义一个名为Accommodation的构造函数\nfunction Accommodation(){}\n\n//为这个类添加属性\nAccommodation.prototype.floors = 0;\nAccommodation.prototype.rooms = 0;\nAccommodation.prototype.sharedEntrance = false;\n//为这个类添加方法\nAccommodation.prototype.lock = function() {}\nAccommodation.prototype.unlock = function() {}\n\n```\n\n##### 1.1.2.2 通过作用域添加属性和方法\n\n> 函数体内定义的任何变量和函数,其作用域都限于该函数体内。\n>\n> 在所有嵌套函数中都可以访问定义在其父函数中的变量。\n\n```javascript\n//定义在任何函数之外的变量在全局作用域内,可以在任何位置访问\nvar myLibrary = {\n myName: \"Evan\"};\nfunction doSomething(){\n var innerVar = 123;\n myLibrary.myName = 'Hello';\n function doSomethingElse() {\n innerVar = 1234;\n }\n \n doSomethingElse();\n alert(innerVar);//1234\n}\n```\n\n> javascript开发者一般会结合使用prototype和this关键字来定义对象实例的属性和方法,其中前者用来定义方法,后者用来定义属性。\n>\n> 实现链式调用,只需要在类中的每个方法最后返回 this 关键字即可。\n\n##### 1.1.2.3 继承\n\n> javascript使用原型链来实现继承,就是prototype.\n\n```javascript\nfunction Accommodation(){}\n\nAccommodation.prototype.lock = function() {}\nAccommodation.prototype.unlock = function() {}\n\n//定义一个构造函数,它将成为Accommodation的子类\nfunction House(defaults) {\n defaults = defaults || {};\n this.floors = 2;\n this.rooms = defaults.rooms || 7;\n}\n//让House类继承Accommodatiob的所有内容 但是要把它的constructor改为自己的\nHouse.prototype = new Accommodation();\nHouse.prototype.constructor = House;\n\nvar house = new House();\n```\n\n##### 1.1.2.4 多态\n\n> 在构造一个新的子类继承并扩展一个类的时候,你可能需要将某一个方法替换为同名的新方法,这就是多态。\n\n```javascript\n//定义父类\nfunction Accommodation(){\n this.isLocked = false;\n this.isAlarmed = false;\n}\n//为所有的Accommodation添加方法,执行一些常见的操作\nAccommodation.prototype.lock = function() {\n this.isLocked = true;\n};\n\nAccommodation.prototype.unlock = function() {\n this.isLocked = false;\n};\n\nAccommodation.prototype.alarm = function() {\n this.isAlarmed = true;\n};\n\nAccommodation.prototype.deactivateAlarm = function() {\n this.isAlarmed = false;\n};\n\n//定义一个子类House\nfunction House(){}\nHouse.prototype = new Accommodation();\nHouse.prototype.lock = function() {\n //执行父类的lock方法,可以用函数call将上下文传递给该方法\n Accommodation.prototype.lock.call(this);\n //code here\n this.alarm()\n}\n\n```\n\n#### 1.2 公有私有与受保护的属性和方法\n\n> 在构造函数中通过var定义的变量其作用域局限于该构造函数内,在prototype上定义的方法无法访问这个变量。要想通过公有的方法来访问私有变量,则需要创建同时包含两个作用域的新作用域。为此,我们可以创建一个自我执行的函数,称为<strong><u>闭包</u></strong>。该函数完全包含了类的定义,包括私有变量与原型方法。\n>\n> <strong>闭包 可以理解为函数引用了外部的变量,非自己function内部定义的变量,并且把这个变量包了起来。</strong>\n\n```javascript\n//我们将类定义在一个自我执行的函数里,这个函数返回我们创建的类,并保存在一个变量\nvar Accommodation = (function(){\n\n function Accommodation() {}\n //此处定义私有变量,使用下划线前缀\n var _isLocked = false,\n _isAlarmed = false,\n _alarmMessage = 'Alarm activated';\n \n //私有的函数\n function _alarm(){\n _isAlarmed = true;\n }\n \n function _disableAlarm(){\n _isAlarmed = false;\n }\n \n //所有定义在原型上的方法都是公有的\n Accommodation.prototype.lock = function() {\n _isLocked = true;\n }\n \n Accommodation.prototype.unlock = function() {\n _isLocked = false;\n }\n \n //定义一个getter函数\n Accommodation.prototype.getIsLocked = function() {\n return _isLocked;\n }\n \n Accommodation.prototype.setAlarmMessage = function(message) {\n _alarmMessage = message;\n }\n return Accommodation;\n}());\n\n```","source":"_posts/oojs.md","raw":"---\ntitle: 面向对象的JavaScript\ndate: 2021-07-05\ncomments: true #是否可评论\ntoc: true #是否显示文章目录\ncategories: \"JS\" #分类\ntags: #标签\n\t- JS\n---\n### 面向对象的JavaScript\n\n#### 1.1 JavaScript中的对象\n\n##### 1.1.1 对象字面量\n\n> 对象字面量是由一对花括号和其中的键值对组成的。\n\n##### 1.1.2 类\n\n> 在Javascript中,只需要定义一个普通的函数,我们就能获取一个类。\n\n###### 1.1.2.1 通过原型添加属性和方法\n\n> Javascript中的每一个函数,都有一个prototype的属性,这个属性指向一个对象。\n>\n> 我们用new 来创建一个类的对象实例时,实例中所包含的属性和方法都来自prototype所指向的\n>\n> 这个对象。\n\n```javascript\n//定义一个名为Accommodation的构造函数\nfunction Accommodation(){}\n\n//为这个类添加属性\nAccommodation.prototype.floors = 0;\nAccommodation.prototype.rooms = 0;\nAccommodation.prototype.sharedEntrance = false;\n//为这个类添加方法\nAccommodation.prototype.lock = function() {}\nAccommodation.prototype.unlock = function() {}\n\n```\n\n##### 1.1.2.2 通过作用域添加属性和方法\n\n> 函数体内定义的任何变量和函数,其作用域都限于该函数体内。\n>\n> 在所有嵌套函数中都可以访问定义在其父函数中的变量。\n\n```javascript\n//定义在任何函数之外的变量在全局作用域内,可以在任何位置访问\nvar myLibrary = {\n myName: \"Evan\"};\nfunction doSomething(){\n var innerVar = 123;\n myLibrary.myName = 'Hello';\n function doSomethingElse() {\n innerVar = 1234;\n }\n \n doSomethingElse();\n alert(innerVar);//1234\n}\n```\n\n> javascript开发者一般会结合使用prototype和this关键字来定义对象实例的属性和方法,其中前者用来定义方法,后者用来定义属性。\n>\n> 实现链式调用,只需要在类中的每个方法最后返回 this 关键字即可。\n\n##### 1.1.2.3 继承\n\n> javascript使用原型链来实现继承,就是prototype.\n\n```javascript\nfunction Accommodation(){}\n\nAccommodation.prototype.lock = function() {}\nAccommodation.prototype.unlock = function() {}\n\n//定义一个构造函数,它将成为Accommodation的子类\nfunction House(defaults) {\n defaults = defaults || {};\n this.floors = 2;\n this.rooms = defaults.rooms || 7;\n}\n//让House类继承Accommodatiob的所有内容 但是要把它的constructor改为自己的\nHouse.prototype = new Accommodation();\nHouse.prototype.constructor = House;\n\nvar house = new House();\n```\n\n##### 1.1.2.4 多态\n\n> 在构造一个新的子类继承并扩展一个类的时候,你可能需要将某一个方法替换为同名的新方法,这就是多态。\n\n```javascript\n//定义父类\nfunction Accommodation(){\n this.isLocked = false;\n this.isAlarmed = false;\n}\n//为所有的Accommodation添加方法,执行一些常见的操作\nAccommodation.prototype.lock = function() {\n this.isLocked = true;\n};\n\nAccommodation.prototype.unlock = function() {\n this.isLocked = false;\n};\n\nAccommodation.prototype.alarm = function() {\n this.isAlarmed = true;\n};\n\nAccommodation.prototype.deactivateAlarm = function() {\n this.isAlarmed = false;\n};\n\n//定义一个子类House\nfunction House(){}\nHouse.prototype = new Accommodation();\nHouse.prototype.lock = function() {\n //执行父类的lock方法,可以用函数call将上下文传递给该方法\n Accommodation.prototype.lock.call(this);\n //code here\n this.alarm()\n}\n\n```\n\n#### 1.2 公有私有与受保护的属性和方法\n\n> 在构造函数中通过var定义的变量其作用域局限于该构造函数内,在prototype上定义的方法无法访问这个变量。要想通过公有的方法来访问私有变量,则需要创建同时包含两个作用域的新作用域。为此,我们可以创建一个自我执行的函数,称为<strong><u>闭包</u></strong>。该函数完全包含了类的定义,包括私有变量与原型方法。\n>\n> <strong>闭包 可以理解为函数引用了外部的变量,非自己function内部定义的变量,并且把这个变量包了起来。</strong>\n\n```javascript\n//我们将类定义在一个自我执行的函数里,这个函数返回我们创建的类,并保存在一个变量\nvar Accommodation = (function(){\n\n function Accommodation() {}\n //此处定义私有变量,使用下划线前缀\n var _isLocked = false,\n _isAlarmed = false,\n _alarmMessage = 'Alarm activated';\n \n //私有的函数\n function _alarm(){\n _isAlarmed = true;\n }\n \n function _disableAlarm(){\n _isAlarmed = false;\n }\n \n //所有定义在原型上的方法都是公有的\n Accommodation.prototype.lock = function() {\n _isLocked = true;\n }\n \n Accommodation.prototype.unlock = function() {\n _isLocked = false;\n }\n \n //定义一个getter函数\n Accommodation.prototype.getIsLocked = function() {\n return _isLocked;\n }\n \n Accommodation.prototype.setAlarmMessage = function(message) {\n _alarmMessage = message;\n }\n return Accommodation;\n}());\n\n```","slug":"oojs","published":1,"updated":"2022-04-20T09:35:50.863Z","layout":"post","photos":[],"link":"","_id":"cl27q3uv6000nn94oetlv0mtg","content":"<h3 id=\"面向对象的JavaScript\"><a href=\"#面向对象的JavaScript\" class=\"headerlink\" title=\"面向对象的JavaScript\"></a>面向对象的JavaScript</h3><h4 id=\"1-1-JavaScript中的对象\"><a href=\"#1-1-JavaScript中的对象\" class=\"headerlink\" title=\"1.1 JavaScript中的对象\"></a>1.1 JavaScript中的对象</h4><h5 id=\"1-1-1-对象字面量\"><a href=\"#1-1-1-对象字面量\" class=\"headerlink\" title=\"1.1.1 对象字面量\"></a>1.1.1 对象字面量</h5><blockquote>\n<p>对象字面量是由一对花括号和其中的键值对组成的。</p>\n</blockquote>\n<h5 id=\"1-1-2-类\"><a href=\"#1-1-2-类\" class=\"headerlink\" title=\"1.1.2 类\"></a>1.1.2 类</h5><blockquote>\n<p> 在Javascript中,只需要定义一个普通的函数,我们就能获取一个类。</p>\n</blockquote>\n<h6 id=\"1-1-2-1-通过原型添加属性和方法\"><a href=\"#1-1-2-1-通过原型添加属性和方法\" class=\"headerlink\" title=\"1.1.2.1 通过原型添加属性和方法\"></a>1.1.2.1 通过原型添加属性和方法</h6><blockquote>\n<p>Javascript中的每一个函数,都有一个prototype的属性,这个属性指向一个对象。</p>\n<p>我们用new 来创建一个类的对象实例时,实例中所包含的属性和方法都来自prototype所指向的</p>\n<p>这个对象。</p>\n</blockquote>\n<figure class=\"highlight javascript\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br><span class=\"line\">11</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"comment\">//定义一个名为Accommodation的构造函数</span></span><br><span class=\"line\"><span class=\"keyword\">function</span> <span class=\"title function_\">Accommodation</span>(<span class=\"params\"></span>){}</span><br><span class=\"line\"></span><br><span class=\"line\"><span class=\"comment\">//为这个类添加属性</span></span><br><span class=\"line\"><span class=\"title class_\">Accommodation</span>.<span class=\"property\"><span class=\"keyword\">prototype</span></span>.<span class=\"property\">floors</span> = <span class=\"number\">0</span>;</span><br><span class=\"line\"><span class=\"title class_\">Accommodation</span>.<span class=\"property\"><span class=\"keyword\">prototype</span></span>.<span class=\"property\">rooms</span> = <span class=\"number\">0</span>;</span><br><span class=\"line\"><span class=\"title class_\">Accommodation</span>.<span class=\"property\"><span class=\"keyword\">prototype</span></span>.<span class=\"property\">sharedEntrance</span> = <span class=\"literal\">false</span>;</span><br><span class=\"line\"><span class=\"comment\">//为这个类添加方法</span></span><br><span class=\"line\"><span class=\"title class_\">Accommodation</span>.<span class=\"property\"><span class=\"keyword\">prototype</span></span>.<span class=\"property\">lock</span> = <span class=\"keyword\">function</span>(<span class=\"params\"></span>) {}</span><br><span class=\"line\"><span class=\"title class_\">Accommodation</span>.<span class=\"property\"><span class=\"keyword\">prototype</span></span>.<span class=\"property\">unlock</span> = <span class=\"keyword\">function</span>(<span class=\"params\"></span>) {}</span><br><span class=\"line\"></span><br></pre></td></tr></table></figure>\n\n<h5 id=\"1-1-2-2-通过作用域添加属性和方法\"><a href=\"#1-1-2-2-通过作用域添加属性和方法\" class=\"headerlink\" title=\"1.1.2.2 通过作用域添加属性和方法\"></a>1.1.2.2 通过作用域添加属性和方法</h5><blockquote>\n<p>函数体内定义的任何变量和函数,其作用域都限于该函数体内。</p>\n<p>在所有嵌套函数中都可以访问定义在其父函数中的变量。</p>\n</blockquote>\n<figure class=\"highlight javascript\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br><span class=\"line\">11</span><br><span class=\"line\">12</span><br><span class=\"line\">13</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"comment\">//定义在任何函数之外的变量在全局作用域内,可以在任何位置访问</span></span><br><span class=\"line\"><span class=\"keyword\">var</span> myLibrary = {</span><br><span class=\"line\"> <span class=\"attr\">myName</span>: <span class=\"string\">"Evan"</span>};</span><br><span class=\"line\"><span class=\"keyword\">function</span> <span class=\"title function_\">doSomething</span>(<span class=\"params\"></span>){</span><br><span class=\"line\"> <span class=\"keyword\">var</span> innerVar = <span class=\"number\">123</span>;</span><br><span class=\"line\"> myLibrary.<span class=\"property\">myName</span> = <span class=\"string\">'Hello'</span>;</span><br><span class=\"line\"> <span class=\"keyword\">function</span> <span class=\"title function_\">doSomethingElse</span>(<span class=\"params\"></span>) {</span><br><span class=\"line\"> innerVar = <span class=\"number\">1234</span>;</span><br><span class=\"line\"> }</span><br><span class=\"line\"> </span><br><span class=\"line\"> <span class=\"title function_\">doSomethingElse</span>();</span><br><span class=\"line\"> <span class=\"title function_\">alert</span>(innerVar);<span class=\"comment\">//1234</span></span><br><span class=\"line\">}</span><br></pre></td></tr></table></figure>\n\n<blockquote>\n<p>javascript开发者一般会结合使用prototype和this关键字来定义对象实例的属性和方法,其中前者用来定义方法,后者用来定义属性。</p>\n<p>实现链式调用,只需要在类中的每个方法最后返回 this 关键字即可。</p>\n</blockquote>\n<h5 id=\"1-1-2-3-继承\"><a href=\"#1-1-2-3-继承\" class=\"headerlink\" title=\"1.1.2.3 继承\"></a>1.1.2.3 继承</h5><blockquote>\n<p>javascript使用原型链来实现继承,就是prototype.</p>\n</blockquote>\n<figure class=\"highlight javascript\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br><span class=\"line\">11</span><br><span class=\"line\">12</span><br><span class=\"line\">13</span><br><span class=\"line\">14</span><br><span class=\"line\">15</span><br><span class=\"line\">16</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"keyword\">function</span> <span class=\"title function_\">Accommodation</span>(<span class=\"params\"></span>){}</span><br><span class=\"line\"></span><br><span class=\"line\"><span class=\"title class_\">Accommodation</span>.<span class=\"property\"><span class=\"keyword\">prototype</span></span>.<span class=\"property\">lock</span> = <span class=\"keyword\">function</span>(<span class=\"params\"></span>) {}</span><br><span class=\"line\"><span class=\"title class_\">Accommodation</span>.<span class=\"property\"><span class=\"keyword\">prototype</span></span>.<span class=\"property\">unlock</span> = <span class=\"keyword\">function</span>(<span class=\"params\"></span>) {}</span><br><span class=\"line\"></span><br><span class=\"line\"><span class=\"comment\">//定义一个构造函数,它将成为Accommodation的子类</span></span><br><span class=\"line\"><span class=\"keyword\">function</span> <span class=\"title function_\">House</span>(<span class=\"params\">defaults</span>) {</span><br><span class=\"line\"> defaults = defaults || {};</span><br><span class=\"line\"> <span class=\"variable language_\">this</span>.<span class=\"property\">floors</span> = <span class=\"number\">2</span>;</span><br><span class=\"line\"> <span class=\"variable language_\">this</span>.<span class=\"property\">rooms</span> = defaults.<span class=\"property\">rooms</span> || <span class=\"number\">7</span>;</span><br><span class=\"line\">}</span><br><span class=\"line\"><span class=\"comment\">//让House类继承Accommodatiob的所有内容 但是要把它的constructor改为自己的</span></span><br><span class=\"line\"><span class=\"title class_\">House</span>.<span class=\"property\"><span class=\"keyword\">prototype</span></span> = <span class=\"keyword\">new</span> <span class=\"title class_\">Accommodation</span>();</span><br><span class=\"line\"><span class=\"title class_\">House</span>.<span class=\"property\"><span class=\"keyword\">prototype</span></span>.<span class=\"property\">constructor</span> = <span class=\"title class_\">House</span>;</span><br><span class=\"line\"></span><br><span class=\"line\"><span class=\"keyword\">var</span> house = <span class=\"keyword\">new</span> <span class=\"title class_\">House</span>();</span><br></pre></td></tr></table></figure>\n\n<h5 id=\"1-1-2-4-多态\"><a href=\"#1-1-2-4-多态\" class=\"headerlink\" title=\"1.1.2.4 多态\"></a>1.1.2.4 多态</h5><blockquote>\n<p>在构造一个新的子类继承并扩展一个类的时候,你可能需要将某一个方法替换为同名的新方法,这就是多态。</p>\n</blockquote>\n<figure class=\"highlight javascript\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br><span class=\"line\">11</span><br><span class=\"line\">12</span><br><span class=\"line\">13</span><br><span class=\"line\">14</span><br><span class=\"line\">15</span><br><span class=\"line\">16</span><br><span class=\"line\">17</span><br><span class=\"line\">18</span><br><span class=\"line\">19</span><br><span class=\"line\">20</span><br><span class=\"line\">21</span><br><span class=\"line\">22</span><br><span class=\"line\">23</span><br><span class=\"line\">24</span><br><span class=\"line\">25</span><br><span class=\"line\">26</span><br><span class=\"line\">27</span><br><span class=\"line\">28</span><br><span class=\"line\">29</span><br><span class=\"line\">30</span><br><span class=\"line\">31</span><br><span class=\"line\">32</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"comment\">//定义父类</span></span><br><span class=\"line\"><span class=\"keyword\">function</span> <span class=\"title function_\">Accommodation</span>(<span class=\"params\"></span>){</span><br><span class=\"line\"> <span class=\"variable language_\">this</span>.<span class=\"property\">isLocked</span> = <span class=\"literal\">false</span>;</span><br><span class=\"line\"> <span class=\"variable language_\">this</span>.<span class=\"property\">isAlarmed</span> = <span class=\"literal\">false</span>;</span><br><span class=\"line\">}</span><br><span class=\"line\"><span class=\"comment\">//为所有的Accommodation添加方法,执行一些常见的操作</span></span><br><span class=\"line\"><span class=\"title class_\">Accommodation</span>.<span class=\"property\"><span class=\"keyword\">prototype</span></span>.<span class=\"property\">lock</span> = <span class=\"keyword\">function</span>(<span class=\"params\"></span>) {</span><br><span class=\"line\"> <span class=\"variable language_\">this</span>.<span class=\"property\">isLocked</span> = <span class=\"literal\">true</span>;</span><br><span class=\"line\">};</span><br><span class=\"line\"></span><br><span class=\"line\"><span class=\"title class_\">Accommodation</span>.<span class=\"property\"><span class=\"keyword\">prototype</span></span>.<span class=\"property\">unlock</span> = <span class=\"keyword\">function</span>(<span class=\"params\"></span>) {</span><br><span class=\"line\"> <span class=\"variable language_\">this</span>.<span class=\"property\">isLocked</span> = <span class=\"literal\">false</span>;</span><br><span class=\"line\">};</span><br><span class=\"line\"></span><br><span class=\"line\"><span class=\"title class_\">Accommodation</span>.<span class=\"property\"><span class=\"keyword\">prototype</span></span>.<span class=\"property\">alarm</span> = <span class=\"keyword\">function</span>(<span class=\"params\"></span>) {</span><br><span class=\"line\"> <span class=\"variable language_\">this</span>.<span class=\"property\">isAlarmed</span> = <span class=\"literal\">true</span>;</span><br><span class=\"line\">};</span><br><span class=\"line\"></span><br><span class=\"line\"><span class=\"title class_\">Accommodation</span>.<span class=\"property\"><span class=\"keyword\">prototype</span></span>.<span class=\"property\">deactivateAlarm</span> = <span class=\"keyword\">function</span>(<span class=\"params\"></span>) {</span><br><span class=\"line\"> <span class=\"variable language_\">this</span>.<span class=\"property\">isAlarmed</span> = <span class=\"literal\">false</span>;</span><br><span class=\"line\">};</span><br><span class=\"line\"></span><br><span class=\"line\"><span class=\"comment\">//定义一个子类House</span></span><br><span class=\"line\"><span class=\"keyword\">function</span> <span class=\"title function_\">House</span>(<span class=\"params\"></span>){}</span><br><span class=\"line\"><span class=\"title class_\">House</span>.<span class=\"property\"><span class=\"keyword\">prototype</span></span> = <span class=\"keyword\">new</span> <span class=\"title class_\">Accommodation</span>();</span><br><span class=\"line\"><span class=\"title class_\">House</span>.<span class=\"property\"><span class=\"keyword\">prototype</span></span>.<span class=\"property\">lock</span> = <span class=\"keyword\">function</span>(<span class=\"params\"></span>) {</span><br><span class=\"line\"> <span class=\"comment\">//执行父类的lock方法,可以用函数call将上下文传递给该方法</span></span><br><span class=\"line\"> <span class=\"title class_\">Accommodation</span>.<span class=\"property\"><span class=\"keyword\">prototype</span></span>.<span class=\"property\">lock</span>.<span class=\"title function_\">call</span>(<span class=\"variable language_\">this</span>);</span><br><span class=\"line\"> <span class=\"comment\">//code here</span></span><br><span class=\"line\"> <span class=\"variable language_\">this</span>.<span class=\"title function_\">alarm</span>()</span><br><span class=\"line\">}</span><br><span class=\"line\"></span><br></pre></td></tr></table></figure>\n\n<h4 id=\"1-2-公有私有与受保护的属性和方法\"><a href=\"#1-2-公有私有与受保护的属性和方法\" class=\"headerlink\" title=\"1.2 公有私有与受保护的属性和方法\"></a>1.2 公有私有与受保护的属性和方法</h4><blockquote>\n<p>在构造函数中通过var定义的变量其作用域局限于该构造函数内,在prototype上定义的方法无法访问这个变量。要想通过公有的方法来访问私有变量,则需要创建同时包含两个作用域的新作用域。为此,我们可以创建一个自我执行的函数,称为<strong><u>闭包</u></strong>。该函数完全包含了类的定义,包括私有变量与原型方法。</p>\n<p><strong>闭包 可以理解为函数引用了外部的变量,非自己function内部定义的变量,并且把这个变量包了起来。</strong></p>\n</blockquote>\n<figure class=\"highlight javascript\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br><span class=\"line\">11</span><br><span class=\"line\">12</span><br><span class=\"line\">13</span><br><span class=\"line\">14</span><br><span class=\"line\">15</span><br><span class=\"line\">16</span><br><span class=\"line\">17</span><br><span class=\"line\">18</span><br><span class=\"line\">19</span><br><span class=\"line\">20</span><br><span class=\"line\">21</span><br><span class=\"line\">22</span><br><span class=\"line\">23</span><br><span class=\"line\">24</span><br><span class=\"line\">25</span><br><span class=\"line\">26</span><br><span class=\"line\">27</span><br><span class=\"line\">28</span><br><span class=\"line\">29</span><br><span class=\"line\">30</span><br><span class=\"line\">31</span><br><span class=\"line\">32</span><br><span class=\"line\">33</span><br><span class=\"line\">34</span><br><span class=\"line\">35</span><br><span class=\"line\">36</span><br><span class=\"line\">37</span><br><span class=\"line\">38</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"comment\">//我们将类定义在一个自我执行的函数里,这个函数返回我们创建的类,并保存在一个变量</span></span><br><span class=\"line\"><span class=\"keyword\">var</span> <span class=\"title class_\">Accommodation</span> = (<span class=\"keyword\">function</span>(<span class=\"params\"></span>){</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"keyword\">function</span> <span class=\"title function_\">Accommodation</span>(<span class=\"params\"></span>) {}</span><br><span class=\"line\"> <span class=\"comment\">//此处定义私有变量,使用下划线前缀</span></span><br><span class=\"line\"> <span class=\"keyword\">var</span> _isLocked = <span class=\"literal\">false</span>,</span><br><span class=\"line\"> _isAlarmed = <span class=\"literal\">false</span>,</span><br><span class=\"line\"> _alarmMessage = <span class=\"string\">'Alarm activated'</span>;</span><br><span class=\"line\"> </span><br><span class=\"line\"> <span class=\"comment\">//私有的函数</span></span><br><span class=\"line\"> <span class=\"keyword\">function</span> <span class=\"title function_\">_alarm</span>(<span class=\"params\"></span>){</span><br><span class=\"line\"> _isAlarmed = <span class=\"literal\">true</span>;</span><br><span class=\"line\"> }</span><br><span class=\"line\"> </span><br><span class=\"line\"> <span class=\"keyword\">function</span> <span class=\"title function_\">_disableAlarm</span>(<span class=\"params\"></span>){</span><br><span class=\"line\"> _isAlarmed = <span class=\"literal\">false</span>;</span><br><span class=\"line\"> }</span><br><span class=\"line\"> </span><br><span class=\"line\"> <span class=\"comment\">//所有定义在原型上的方法都是公有的</span></span><br><span class=\"line\"> <span class=\"title class_\">Accommodation</span>.<span class=\"property\"><span class=\"keyword\">prototype</span></span>.<span class=\"property\">lock</span> = <span class=\"keyword\">function</span>(<span class=\"params\"></span>) {</span><br><span class=\"line\"> _isLocked = <span class=\"literal\">true</span>;</span><br><span class=\"line\"> }</span><br><span class=\"line\"> </span><br><span class=\"line\"> <span class=\"title class_\">Accommodation</span>.<span class=\"property\"><span class=\"keyword\">prototype</span></span>.<span class=\"property\">unlock</span> = <span class=\"keyword\">function</span>(<span class=\"params\"></span>) {</span><br><span class=\"line\"> _isLocked = <span class=\"literal\">false</span>;</span><br><span class=\"line\"> }</span><br><span class=\"line\"> </span><br><span class=\"line\"> <span class=\"comment\">//定义一个getter函数</span></span><br><span class=\"line\"> <span class=\"title class_\">Accommodation</span>.<span class=\"property\"><span class=\"keyword\">prototype</span></span>.<span class=\"property\">getIsLocked</span> = <span class=\"keyword\">function</span>(<span class=\"params\"></span>) {</span><br><span class=\"line\"> <span class=\"keyword\">return</span> _isLocked;</span><br><span class=\"line\"> }</span><br><span class=\"line\"> </span><br><span class=\"line\"> <span class=\"title class_\">Accommodation</span>.<span class=\"property\"><span class=\"keyword\">prototype</span></span>.<span class=\"property\">setAlarmMessage</span> = <span class=\"keyword\">function</span>(<span class=\"params\">message</span>) {</span><br><span class=\"line\"> _alarmMessage = message;</span><br><span class=\"line\"> }</span><br><span class=\"line\"> <span class=\"keyword\">return</span> <span class=\"title class_\">Accommodation</span>;</span><br><span class=\"line\">}());</span><br><span class=\"line\"></span><br></pre></td></tr></table></figure>","site":{"data":{}},"excerpt":"","more":"<h3 id=\"面向对象的JavaScript\"><a href=\"#面向对象的JavaScript\" class=\"headerlink\" title=\"面向对象的JavaScript\"></a>面向对象的JavaScript</h3><h4 id=\"1-1-JavaScript中的对象\"><a href=\"#1-1-JavaScript中的对象\" class=\"headerlink\" title=\"1.1 JavaScript中的对象\"></a>1.1 JavaScript中的对象</h4><h5 id=\"1-1-1-对象字面量\"><a href=\"#1-1-1-对象字面量\" class=\"headerlink\" title=\"1.1.1 对象字面量\"></a>1.1.1 对象字面量</h5><blockquote>\n<p>对象字面量是由一对花括号和其中的键值对组成的。</p>\n</blockquote>\n<h5 id=\"1-1-2-类\"><a href=\"#1-1-2-类\" class=\"headerlink\" title=\"1.1.2 类\"></a>1.1.2 类</h5><blockquote>\n<p> 在Javascript中,只需要定义一个普通的函数,我们就能获取一个类。</p>\n</blockquote>\n<h6 id=\"1-1-2-1-通过原型添加属性和方法\"><a href=\"#1-1-2-1-通过原型添加属性和方法\" class=\"headerlink\" title=\"1.1.2.1 通过原型添加属性和方法\"></a>1.1.2.1 通过原型添加属性和方法</h6><blockquote>\n<p>Javascript中的每一个函数,都有一个prototype的属性,这个属性指向一个对象。</p>\n<p>我们用new 来创建一个类的对象实例时,实例中所包含的属性和方法都来自prototype所指向的</p>\n<p>这个对象。</p>\n</blockquote>\n<figure class=\"highlight javascript\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br><span class=\"line\">11</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"comment\">//定义一个名为Accommodation的构造函数</span></span><br><span class=\"line\"><span class=\"keyword\">function</span> <span class=\"title function_\">Accommodation</span>(<span class=\"params\"></span>){}</span><br><span class=\"line\"></span><br><span class=\"line\"><span class=\"comment\">//为这个类添加属性</span></span><br><span class=\"line\"><span class=\"title class_\">Accommodation</span>.<span class=\"property\"><span class=\"keyword\">prototype</span></span>.<span class=\"property\">floors</span> = <span class=\"number\">0</span>;</span><br><span class=\"line\"><span class=\"title class_\">Accommodation</span>.<span class=\"property\"><span class=\"keyword\">prototype</span></span>.<span class=\"property\">rooms</span> = <span class=\"number\">0</span>;</span><br><span class=\"line\"><span class=\"title class_\">Accommodation</span>.<span class=\"property\"><span class=\"keyword\">prototype</span></span>.<span class=\"property\">sharedEntrance</span> = <span class=\"literal\">false</span>;</span><br><span class=\"line\"><span class=\"comment\">//为这个类添加方法</span></span><br><span class=\"line\"><span class=\"title class_\">Accommodation</span>.<span class=\"property\"><span class=\"keyword\">prototype</span></span>.<span class=\"property\">lock</span> = <span class=\"keyword\">function</span>(<span class=\"params\"></span>) {}</span><br><span class=\"line\"><span class=\"title class_\">Accommodation</span>.<span class=\"property\"><span class=\"keyword\">prototype</span></span>.<span class=\"property\">unlock</span> = <span class=\"keyword\">function</span>(<span class=\"params\"></span>) {}</span><br><span class=\"line\"></span><br></pre></td></tr></table></figure>\n\n<h5 id=\"1-1-2-2-通过作用域添加属性和方法\"><a href=\"#1-1-2-2-通过作用域添加属性和方法\" class=\"headerlink\" title=\"1.1.2.2 通过作用域添加属性和方法\"></a>1.1.2.2 通过作用域添加属性和方法</h5><blockquote>\n<p>函数体内定义的任何变量和函数,其作用域都限于该函数体内。</p>\n<p>在所有嵌套函数中都可以访问定义在其父函数中的变量。</p>\n</blockquote>\n<figure class=\"highlight javascript\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br><span class=\"line\">11</span><br><span class=\"line\">12</span><br><span class=\"line\">13</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"comment\">//定义在任何函数之外的变量在全局作用域内,可以在任何位置访问</span></span><br><span class=\"line\"><span class=\"keyword\">var</span> myLibrary = {</span><br><span class=\"line\"> <span class=\"attr\">myName</span>: <span class=\"string\">"Evan"</span>};</span><br><span class=\"line\"><span class=\"keyword\">function</span> <span class=\"title function_\">doSomething</span>(<span class=\"params\"></span>){</span><br><span class=\"line\"> <span class=\"keyword\">var</span> innerVar = <span class=\"number\">123</span>;</span><br><span class=\"line\"> myLibrary.<span class=\"property\">myName</span> = <span class=\"string\">'Hello'</span>;</span><br><span class=\"line\"> <span class=\"keyword\">function</span> <span class=\"title function_\">doSomethingElse</span>(<span class=\"params\"></span>) {</span><br><span class=\"line\"> innerVar = <span class=\"number\">1234</span>;</span><br><span class=\"line\"> }</span><br><span class=\"line\"> </span><br><span class=\"line\"> <span class=\"title function_\">doSomethingElse</span>();</span><br><span class=\"line\"> <span class=\"title function_\">alert</span>(innerVar);<span class=\"comment\">//1234</span></span><br><span class=\"line\">}</span><br></pre></td></tr></table></figure>\n\n<blockquote>\n<p>javascript开发者一般会结合使用prototype和this关键字来定义对象实例的属性和方法,其中前者用来定义方法,后者用来定义属性。</p>\n<p>实现链式调用,只需要在类中的每个方法最后返回 this 关键字即可。</p>\n</blockquote>\n<h5 id=\"1-1-2-3-继承\"><a href=\"#1-1-2-3-继承\" class=\"headerlink\" title=\"1.1.2.3 继承\"></a>1.1.2.3 继承</h5><blockquote>\n<p>javascript使用原型链来实现继承,就是prototype.</p>\n</blockquote>\n<figure class=\"highlight javascript\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br><span class=\"line\">11</span><br><span class=\"line\">12</span><br><span class=\"line\">13</span><br><span class=\"line\">14</span><br><span class=\"line\">15</span><br><span class=\"line\">16</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"keyword\">function</span> <span class=\"title function_\">Accommodation</span>(<span class=\"params\"></span>){}</span><br><span class=\"line\"></span><br><span class=\"line\"><span class=\"title class_\">Accommodation</span>.<span class=\"property\"><span class=\"keyword\">prototype</span></span>.<span class=\"property\">lock</span> = <span class=\"keyword\">function</span>(<span class=\"params\"></span>) {}</span><br><span class=\"line\"><span class=\"title class_\">Accommodation</span>.<span class=\"property\"><span class=\"keyword\">prototype</span></span>.<span class=\"property\">unlock</span> = <span class=\"keyword\">function</span>(<span class=\"params\"></span>) {}</span><br><span class=\"line\"></span><br><span class=\"line\"><span class=\"comment\">//定义一个构造函数,它将成为Accommodation的子类</span></span><br><span class=\"line\"><span class=\"keyword\">function</span> <span class=\"title function_\">House</span>(<span class=\"params\">defaults</span>) {</span><br><span class=\"line\"> defaults = defaults || {};</span><br><span class=\"line\"> <span class=\"variable language_\">this</span>.<span class=\"property\">floors</span> = <span class=\"number\">2</span>;</span><br><span class=\"line\"> <span class=\"variable language_\">this</span>.<span class=\"property\">rooms</span> = defaults.<span class=\"property\">rooms</span> || <span class=\"number\">7</span>;</span><br><span class=\"line\">}</span><br><span class=\"line\"><span class=\"comment\">//让House类继承Accommodatiob的所有内容 但是要把它的constructor改为自己的</span></span><br><span class=\"line\"><span class=\"title class_\">House</span>.<span class=\"property\"><span class=\"keyword\">prototype</span></span> = <span class=\"keyword\">new</span> <span class=\"title class_\">Accommodation</span>();</span><br><span class=\"line\"><span class=\"title class_\">House</span>.<span class=\"property\"><span class=\"keyword\">prototype</span></span>.<span class=\"property\">constructor</span> = <span class=\"title class_\">House</span>;</span><br><span class=\"line\"></span><br><span class=\"line\"><span class=\"keyword\">var</span> house = <span class=\"keyword\">new</span> <span class=\"title class_\">House</span>();</span><br></pre></td></tr></table></figure>\n\n<h5 id=\"1-1-2-4-多态\"><a href=\"#1-1-2-4-多态\" class=\"headerlink\" title=\"1.1.2.4 多态\"></a>1.1.2.4 多态</h5><blockquote>\n<p>在构造一个新的子类继承并扩展一个类的时候,你可能需要将某一个方法替换为同名的新方法,这就是多态。</p>\n</blockquote>\n<figure class=\"highlight javascript\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br><span class=\"line\">11</span><br><span class=\"line\">12</span><br><span class=\"line\">13</span><br><span class=\"line\">14</span><br><span class=\"line\">15</span><br><span class=\"line\">16</span><br><span class=\"line\">17</span><br><span class=\"line\">18</span><br><span class=\"line\">19</span><br><span class=\"line\">20</span><br><span class=\"line\">21</span><br><span class=\"line\">22</span><br><span class=\"line\">23</span><br><span class=\"line\">24</span><br><span class=\"line\">25</span><br><span class=\"line\">26</span><br><span class=\"line\">27</span><br><span class=\"line\">28</span><br><span class=\"line\">29</span><br><span class=\"line\">30</span><br><span class=\"line\">31</span><br><span class=\"line\">32</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"comment\">//定义父类</span></span><br><span class=\"line\"><span class=\"keyword\">function</span> <span class=\"title function_\">Accommodation</span>(<span class=\"params\"></span>){</span><br><span class=\"line\"> <span class=\"variable language_\">this</span>.<span class=\"property\">isLocked</span> = <span class=\"literal\">false</span>;</span><br><span class=\"line\"> <span class=\"variable language_\">this</span>.<span class=\"property\">isAlarmed</span> = <span class=\"literal\">false</span>;</span><br><span class=\"line\">}</span><br><span class=\"line\"><span class=\"comment\">//为所有的Accommodation添加方法,执行一些常见的操作</span></span><br><span class=\"line\"><span class=\"title class_\">Accommodation</span>.<span class=\"property\"><span class=\"keyword\">prototype</span></span>.<span class=\"property\">lock</span> = <span class=\"keyword\">function</span>(<span class=\"params\"></span>) {</span><br><span class=\"line\"> <span class=\"variable language_\">this</span>.<span class=\"property\">isLocked</span> = <span class=\"literal\">true</span>;</span><br><span class=\"line\">};</span><br><span class=\"line\"></span><br><span class=\"line\"><span class=\"title class_\">Accommodation</span>.<span class=\"property\"><span class=\"keyword\">prototype</span></span>.<span class=\"property\">unlock</span> = <span class=\"keyword\">function</span>(<span class=\"params\"></span>) {</span><br><span class=\"line\"> <span class=\"variable language_\">this</span>.<span class=\"property\">isLocked</span> = <span class=\"literal\">false</span>;</span><br><span class=\"line\">};</span><br><span class=\"line\"></span><br><span class=\"line\"><span class=\"title class_\">Accommodation</span>.<span class=\"property\"><span class=\"keyword\">prototype</span></span>.<span class=\"property\">alarm</span> = <span class=\"keyword\">function</span>(<span class=\"params\"></span>) {</span><br><span class=\"line\"> <span class=\"variable language_\">this</span>.<span class=\"property\">isAlarmed</span> = <span class=\"literal\">true</span>;</span><br><span class=\"line\">};</span><br><span class=\"line\"></span><br><span class=\"line\"><span class=\"title class_\">Accommodation</span>.<span class=\"property\"><span class=\"keyword\">prototype</span></span>.<span class=\"property\">deactivateAlarm</span> = <span class=\"keyword\">function</span>(<span class=\"params\"></span>) {</span><br><span class=\"line\"> <span class=\"variable language_\">this</span>.<span class=\"property\">isAlarmed</span> = <span class=\"literal\">false</span>;</span><br><span class=\"line\">};</span><br><span class=\"line\"></span><br><span class=\"line\"><span class=\"comment\">//定义一个子类House</span></span><br><span class=\"line\"><span class=\"keyword\">function</span> <span class=\"title function_\">House</span>(<span class=\"params\"></span>){}</span><br><span class=\"line\"><span class=\"title class_\">House</span>.<span class=\"property\"><span class=\"keyword\">prototype</span></span> = <span class=\"keyword\">new</span> <span class=\"title class_\">Accommodation</span>();</span><br><span class=\"line\"><span class=\"title class_\">House</span>.<span class=\"property\"><span class=\"keyword\">prototype</span></span>.<span class=\"property\">lock</span> = <span class=\"keyword\">function</span>(<span class=\"params\"></span>) {</span><br><span class=\"line\"> <span class=\"comment\">//执行父类的lock方法,可以用函数call将上下文传递给该方法</span></span><br><span class=\"line\"> <span class=\"title class_\">Accommodation</span>.<span class=\"property\"><span class=\"keyword\">prototype</span></span>.<span class=\"property\">lock</span>.<span class=\"title function_\">call</span>(<span class=\"variable language_\">this</span>);</span><br><span class=\"line\"> <span class=\"comment\">//code here</span></span><br><span class=\"line\"> <span class=\"variable language_\">this</span>.<span class=\"title function_\">alarm</span>()</span><br><span class=\"line\">}</span><br><span class=\"line\"></span><br></pre></td></tr></table></figure>\n\n<h4 id=\"1-2-公有私有与受保护的属性和方法\"><a href=\"#1-2-公有私有与受保护的属性和方法\" class=\"headerlink\" title=\"1.2 公有私有与受保护的属性和方法\"></a>1.2 公有私有与受保护的属性和方法</h4><blockquote>\n<p>在构造函数中通过var定义的变量其作用域局限于该构造函数内,在prototype上定义的方法无法访问这个变量。要想通过公有的方法来访问私有变量,则需要创建同时包含两个作用域的新作用域。为此,我们可以创建一个自我执行的函数,称为<strong><u>闭包</u></strong>。该函数完全包含了类的定义,包括私有变量与原型方法。</p>\n<p><strong>闭包 可以理解为函数引用了外部的变量,非自己function内部定义的变量,并且把这个变量包了起来。</strong></p>\n</blockquote>\n<figure class=\"highlight javascript\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br><span class=\"line\">11</span><br><span class=\"line\">12</span><br><span class=\"line\">13</span><br><span class=\"line\">14</span><br><span class=\"line\">15</span><br><span class=\"line\">16</span><br><span class=\"line\">17</span><br><span class=\"line\">18</span><br><span class=\"line\">19</span><br><span class=\"line\">20</span><br><span class=\"line\">21</span><br><span class=\"line\">22</span><br><span class=\"line\">23</span><br><span class=\"line\">24</span><br><span class=\"line\">25</span><br><span class=\"line\">26</span><br><span class=\"line\">27</span><br><span class=\"line\">28</span><br><span class=\"line\">29</span><br><span class=\"line\">30</span><br><span class=\"line\">31</span><br><span class=\"line\">32</span><br><span class=\"line\">33</span><br><span class=\"line\">34</span><br><span class=\"line\">35</span><br><span class=\"line\">36</span><br><span class=\"line\">37</span><br><span class=\"line\">38</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"comment\">//我们将类定义在一个自我执行的函数里,这个函数返回我们创建的类,并保存在一个变量</span></span><br><span class=\"line\"><span class=\"keyword\">var</span> <span class=\"title class_\">Accommodation</span> = (<span class=\"keyword\">function</span>(<span class=\"params\"></span>){</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"keyword\">function</span> <span class=\"title function_\">Accommodation</span>(<span class=\"params\"></span>) {}</span><br><span class=\"line\"> <span class=\"comment\">//此处定义私有变量,使用下划线前缀</span></span><br><span class=\"line\"> <span class=\"keyword\">var</span> _isLocked = <span class=\"literal\">false</span>,</span><br><span class=\"line\"> _isAlarmed = <span class=\"literal\">false</span>,</span><br><span class=\"line\"> _alarmMessage = <span class=\"string\">'Alarm activated'</span>;</span><br><span class=\"line\"> </span><br><span class=\"line\"> <span class=\"comment\">//私有的函数</span></span><br><span class=\"line\"> <span class=\"keyword\">function</span> <span class=\"title function_\">_alarm</span>(<span class=\"params\"></span>){</span><br><span class=\"line\"> _isAlarmed = <span class=\"literal\">true</span>;</span><br><span class=\"line\"> }</span><br><span class=\"line\"> </span><br><span class=\"line\"> <span class=\"keyword\">function</span> <span class=\"title function_\">_disableAlarm</span>(<span class=\"params\"></span>){</span><br><span class=\"line\"> _isAlarmed = <span class=\"literal\">false</span>;</span><br><span class=\"line\"> }</span><br><span class=\"line\"> </span><br><span class=\"line\"> <span class=\"comment\">//所有定义在原型上的方法都是公有的</span></span><br><span class=\"line\"> <span class=\"title class_\">Accommodation</span>.<span class=\"property\"><span class=\"keyword\">prototype</span></span>.<span class=\"property\">lock</span> = <span class=\"keyword\">function</span>(<span class=\"params\"></span>) {</span><br><span class=\"line\"> _isLocked = <span class=\"literal\">true</span>;</span><br><span class=\"line\"> }</span><br><span class=\"line\"> </span><br><span class=\"line\"> <span class=\"title class_\">Accommodation</span>.<span class=\"property\"><span class=\"keyword\">prototype</span></span>.<span class=\"property\">unlock</span> = <span class=\"keyword\">function</span>(<span class=\"params\"></span>) {</span><br><span class=\"line\"> _isLocked = <span class=\"literal\">false</span>;</span><br><span class=\"line\"> }</span><br><span class=\"line\"> </span><br><span class=\"line\"> <span class=\"comment\">//定义一个getter函数</span></span><br><span class=\"line\"> <span class=\"title class_\">Accommodation</span>.<span class=\"property\"><span class=\"keyword\">prototype</span></span>.<span class=\"property\">getIsLocked</span> = <span class=\"keyword\">function</span>(<span class=\"params\"></span>) {</span><br><span class=\"line\"> <span class=\"keyword\">return</span> _isLocked;</span><br><span class=\"line\"> }</span><br><span class=\"line\"> </span><br><span class=\"line\"> <span class=\"title class_\">Accommodation</span>.<span class=\"property\"><span class=\"keyword\">prototype</span></span>.<span class=\"property\">setAlarmMessage</span> = <span class=\"keyword\">function</span>(<span class=\"params\">message</span>) {</span><br><span class=\"line\"> _alarmMessage = message;</span><br><span class=\"line\"> }</span><br><span class=\"line\"> <span class=\"keyword\">return</span> <span class=\"title class_\">Accommodation</span>;</span><br><span class=\"line\">}());</span><br><span class=\"line\"></span><br></pre></td></tr></table></figure>"},{"title":"三大经典排序算法","date":"2021-07-16T00:42:44.000Z","comments":1,"toc":true,"_content":"\n#### 三大经典排序算法\n每天早上要做的第一件重要的事就是刷题,第一步就是写一下这三个经典排序热身。\n快排,堆排和归并排序。\n```java\npackage org.example.basic;\n\nimport java.util.Arrays;\n\npublic class SortTest {\n\n /**\n * 测试 写出基于整型数组的 归并 快排 堆排 三大经典排序算法\n */\n public static void main(String[] args) {\n\n int[] nums = {5, 3, 7, 1, 4, 2, 6, 9, 8};\n\n System.out.println(Arrays.toString(nums));\n\n //partition(nums, 0, nums.length - 1);\n int N = nums.length;\n //quickSort(nums, 0, N - 1);\n //sink(nums, 0, N);\n //heapSort(nums);\n mergeSort(nums, new int[N], 0, N - 1);\n System.out.println(Arrays.toString(nums));\n }\n\n //快排 一个partition 一个quickSort\n\n static void swap(int[] nums, int i, int j) {\n int t = nums[i];\n nums[i] = nums[j];\n nums[j] = t;\n }\n\n /**\n * 把left 当作 基准比较的值 即pivot = nums[left]\n * 划分一次 左边的小 中间是pivot 右边是大\n * 所以主要就是交换 和 比较\n */\n static int partition(int[] nums, int left, int right) {\n int i = left;\n int j = left + 1;\n while (j <= right) {\n if (nums[j] < nums[left]) {\n swap(nums, j, ++i);\n }\n j++;\n }\n swap(nums, left, i);\n return i;\n }\n\n static void quickSort(int[] nums, int left, int right) {\n if (left >= right) return;\n int pivot = partition(nums, left, right);\n quickSort(nums, left, pivot - 1);\n quickSort(nums, pivot + 1, right);\n }\n\n /**\n * sink 即元素下沉 我们堆排序是拿第一个元素和最后一个元素交换,然后再下沉得到一个有序的堆\n * k 表示第k个元素 N表示数组的长度\n */\n static void sink(int[] nums, int k, int N) {\n while (k * 2 + 1 < N) {\n int i = k * 2 + 1;\n if ((i + 1) < N && nums[i] < nums[i + 1]) i++;\n if (nums[k] > nums[i]) break;\n swap(nums, k, i);\n k = i;\n }\n }\n\n static void heapSort(int[] nums) {\n int N = nums.length;\n for (int i = N / 2; i >= 0; i--) {\n sink(nums, i, N);\n }\n for (int j = N - 1; j > 0; j--) {\n swap(nums, 0, j);\n sink(nums, 0, j);\n }\n }\n\n static void mergeSort(int[] nums, int[] temp, int left, int right) {\n if (left >= right) return;\n int mid = left + ((right - left) >> 2);\n mergeSort(nums, temp, left, mid );\n mergeSort(nums, temp, mid + 1, right);\n\n int start1 = left;\n int end1 = mid;\n int start2 = mid + 1;\n int end2 = right;\n int k = left;\n\n while(start1 <= end1 && start2 <= end2) temp[k++] = nums[start1]<nums[start2] ? nums[start1++] : nums[start2++];\n while(start1 <= end1) temp[k++] = nums[start1++];\n while(start2 <= end2) temp[k++] = nums[start2++];\n for(k=left;k<=right;k++)nums[k] = temp[k];\n\n }\n\n}\n```\n ","source":"_posts/三大经典排序算法.md","raw":"---\ntitle: 三大经典排序算法\ndate: 2021-07-16 08:42:44\ncomments: true #是否可评论\ntoc: true #是否显示文章目录\ncategories: \"算法\" #分类\ntags: #标签\n - JAVA\n - 算法\n---\n\n#### 三大经典排序算法\n每天早上要做的第一件重要的事就是刷题,第一步就是写一下这三个经典排序热身。\n快排,堆排和归并排序。\n```java\npackage org.example.basic;\n\nimport java.util.Arrays;\n\npublic class SortTest {\n\n /**\n * 测试 写出基于整型数组的 归并 快排 堆排 三大经典排序算法\n */\n public static void main(String[] args) {\n\n int[] nums = {5, 3, 7, 1, 4, 2, 6, 9, 8};\n\n System.out.println(Arrays.toString(nums));\n\n //partition(nums, 0, nums.length - 1);\n int N = nums.length;\n //quickSort(nums, 0, N - 1);\n //sink(nums, 0, N);\n //heapSort(nums);\n mergeSort(nums, new int[N], 0, N - 1);\n System.out.println(Arrays.toString(nums));\n }\n\n //快排 一个partition 一个quickSort\n\n static void swap(int[] nums, int i, int j) {\n int t = nums[i];\n nums[i] = nums[j];\n nums[j] = t;\n }\n\n /**\n * 把left 当作 基准比较的值 即pivot = nums[left]\n * 划分一次 左边的小 中间是pivot 右边是大\n * 所以主要就是交换 和 比较\n */\n static int partition(int[] nums, int left, int right) {\n int i = left;\n int j = left + 1;\n while (j <= right) {\n if (nums[j] < nums[left]) {\n swap(nums, j, ++i);\n }\n j++;\n }\n swap(nums, left, i);\n return i;\n }\n\n static void quickSort(int[] nums, int left, int right) {\n if (left >= right) return;\n int pivot = partition(nums, left, right);\n quickSort(nums, left, pivot - 1);\n quickSort(nums, pivot + 1, right);\n }\n\n /**\n * sink 即元素下沉 我们堆排序是拿第一个元素和最后一个元素交换,然后再下沉得到一个有序的堆\n * k 表示第k个元素 N表示数组的长度\n */\n static void sink(int[] nums, int k, int N) {\n while (k * 2 + 1 < N) {\n int i = k * 2 + 1;\n if ((i + 1) < N && nums[i] < nums[i + 1]) i++;\n if (nums[k] > nums[i]) break;\n swap(nums, k, i);\n k = i;\n }\n }\n\n static void heapSort(int[] nums) {\n int N = nums.length;\n for (int i = N / 2; i >= 0; i--) {\n sink(nums, i, N);\n }\n for (int j = N - 1; j > 0; j--) {\n swap(nums, 0, j);\n sink(nums, 0, j);\n }\n }\n\n static void mergeSort(int[] nums, int[] temp, int left, int right) {\n if (left >= right) return;\n int mid = left + ((right - left) >> 2);\n mergeSort(nums, temp, left, mid );\n mergeSort(nums, temp, mid + 1, right);\n\n int start1 = left;\n int end1 = mid;\n int start2 = mid + 1;\n int end2 = right;\n int k = left;\n\n while(start1 <= end1 && start2 <= end2) temp[k++] = nums[start1]<nums[start2] ? nums[start1++] : nums[start2++];\n while(start1 <= end1) temp[k++] = nums[start1++];\n while(start2 <= end2) temp[k++] = nums[start2++];\n for(k=left;k<=right;k++)nums[k] = temp[k];\n\n }\n\n}\n```\n ","slug":"三大经典排序算法","published":1,"updated":"2022-04-20T09:35:50.863Z","layout":"post","photos":[],"link":"","_id":"cl27q3uv6000pn94o9myi0mhc","content":"<h4 id=\"三大经典排序算法\"><a href=\"#三大经典排序算法\" class=\"headerlink\" title=\"三大经典排序算法\"></a>三大经典排序算法</h4><p>每天早上要做的第一件重要的事就是刷题,第一步就是写一下这三个经典排序热身。<br>快排,堆排和归并排序。</p>\n<figure class=\"highlight java\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br><span class=\"line\">11</span><br><span class=\"line\">12</span><br><span class=\"line\">13</span><br><span class=\"line\">14</span><br><span class=\"line\">15</span><br><span class=\"line\">16</span><br><span class=\"line\">17</span><br><span class=\"line\">18</span><br><span class=\"line\">19</span><br><span class=\"line\">20</span><br><span class=\"line\">21</span><br><span class=\"line\">22</span><br><span class=\"line\">23</span><br><span class=\"line\">24</span><br><span class=\"line\">25</span><br><span class=\"line\">26</span><br><span class=\"line\">27</span><br><span class=\"line\">28</span><br><span class=\"line\">29</span><br><span class=\"line\">30</span><br><span class=\"line\">31</span><br><span class=\"line\">32</span><br><span class=\"line\">33</span><br><span class=\"line\">34</span><br><span class=\"line\">35</span><br><span class=\"line\">36</span><br><span class=\"line\">37</span><br><span class=\"line\">38</span><br><span class=\"line\">39</span><br><span class=\"line\">40</span><br><span class=\"line\">41</span><br><span class=\"line\">42</span><br><span class=\"line\">43</span><br><span class=\"line\">44</span><br><span class=\"line\">45</span><br><span class=\"line\">46</span><br><span class=\"line\">47</span><br><span class=\"line\">48</span><br><span class=\"line\">49</span><br><span class=\"line\">50</span><br><span class=\"line\">51</span><br><span class=\"line\">52</span><br><span class=\"line\">53</span><br><span class=\"line\">54</span><br><span class=\"line\">55</span><br><span class=\"line\">56</span><br><span class=\"line\">57</span><br><span class=\"line\">58</span><br><span class=\"line\">59</span><br><span class=\"line\">60</span><br><span class=\"line\">61</span><br><span class=\"line\">62</span><br><span class=\"line\">63</span><br><span class=\"line\">64</span><br><span class=\"line\">65</span><br><span class=\"line\">66</span><br><span class=\"line\">67</span><br><span class=\"line\">68</span><br><span class=\"line\">69</span><br><span class=\"line\">70</span><br><span class=\"line\">71</span><br><span class=\"line\">72</span><br><span class=\"line\">73</span><br><span class=\"line\">74</span><br><span class=\"line\">75</span><br><span class=\"line\">76</span><br><span class=\"line\">77</span><br><span class=\"line\">78</span><br><span class=\"line\">79</span><br><span class=\"line\">80</span><br><span class=\"line\">81</span><br><span class=\"line\">82</span><br><span class=\"line\">83</span><br><span class=\"line\">84</span><br><span class=\"line\">85</span><br><span class=\"line\">86</span><br><span class=\"line\">87</span><br><span class=\"line\">88</span><br><span class=\"line\">89</span><br><span class=\"line\">90</span><br><span class=\"line\">91</span><br><span class=\"line\">92</span><br><span class=\"line\">93</span><br><span class=\"line\">94</span><br><span class=\"line\">95</span><br><span class=\"line\">96</span><br><span class=\"line\">97</span><br><span class=\"line\">98</span><br><span class=\"line\">99</span><br><span class=\"line\">100</span><br><span class=\"line\">101</span><br><span class=\"line\">102</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"keyword\">package</span> org.example.basic;</span><br><span class=\"line\"></span><br><span class=\"line\"><span class=\"keyword\">import</span> java.util.Arrays;</span><br><span class=\"line\"></span><br><span class=\"line\"><span class=\"keyword\">public</span> <span class=\"keyword\">class</span> <span class=\"title class_\">SortTest</span> {</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"comment\">/**</span></span><br><span class=\"line\"><span class=\"comment\"> * 测试 写出基于整型数组的 归并 快排 堆排 三大经典排序算法</span></span><br><span class=\"line\"><span class=\"comment\"> */</span></span><br><span class=\"line\"> <span class=\"keyword\">public</span> <span class=\"keyword\">static</span> <span class=\"keyword\">void</span> <span class=\"title function_\">main</span><span class=\"params\">(String[] args)</span> {</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"type\">int</span>[] nums = {<span class=\"number\">5</span>, <span class=\"number\">3</span>, <span class=\"number\">7</span>, <span class=\"number\">1</span>, <span class=\"number\">4</span>, <span class=\"number\">2</span>, <span class=\"number\">6</span>, <span class=\"number\">9</span>, <span class=\"number\">8</span>};</span><br><span class=\"line\"></span><br><span class=\"line\"> System.out.println(Arrays.toString(nums));</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"comment\">//partition(nums, 0, nums.length - 1);</span></span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">N</span> <span class=\"operator\">=</span> nums.length;</span><br><span class=\"line\"> <span class=\"comment\">//quickSort(nums, 0, N - 1);</span></span><br><span class=\"line\"> <span class=\"comment\">//sink(nums, 0, N);</span></span><br><span class=\"line\"> <span class=\"comment\">//heapSort(nums);</span></span><br><span class=\"line\"> mergeSort(nums, <span class=\"keyword\">new</span> <span class=\"title class_\">int</span>[N], <span class=\"number\">0</span>, N - <span class=\"number\">1</span>);</span><br><span class=\"line\"> System.out.println(Arrays.toString(nums));</span><br><span class=\"line\"> }</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"comment\">//快排 一个partition 一个quickSort</span></span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"keyword\">static</span> <span class=\"keyword\">void</span> <span class=\"title function_\">swap</span><span class=\"params\">(<span class=\"type\">int</span>[] nums, <span class=\"type\">int</span> i, <span class=\"type\">int</span> j)</span> {</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">t</span> <span class=\"operator\">=</span> nums[i];</span><br><span class=\"line\"> nums[i] = nums[j];</span><br><span class=\"line\"> nums[j] = t;</span><br><span class=\"line\"> }</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"comment\">/**</span></span><br><span class=\"line\"><span class=\"comment\"> * 把left 当作 基准比较的值 即pivot = nums[left]</span></span><br><span class=\"line\"><span class=\"comment\"> * 划分一次 左边的小 中间是pivot 右边是大</span></span><br><span class=\"line\"><span class=\"comment\"> * 所以主要就是交换 和 比较</span></span><br><span class=\"line\"><span class=\"comment\"> */</span></span><br><span class=\"line\"> <span class=\"keyword\">static</span> <span class=\"type\">int</span> <span class=\"title function_\">partition</span><span class=\"params\">(<span class=\"type\">int</span>[] nums, <span class=\"type\">int</span> left, <span class=\"type\">int</span> right)</span> {</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">i</span> <span class=\"operator\">=</span> left;</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">j</span> <span class=\"operator\">=</span> left + <span class=\"number\">1</span>;</span><br><span class=\"line\"> <span class=\"keyword\">while</span> (j <= right) {</span><br><span class=\"line\"> <span class=\"keyword\">if</span> (nums[j] < nums[left]) {</span><br><span class=\"line\"> swap(nums, j, ++i);</span><br><span class=\"line\"> }</span><br><span class=\"line\"> j++;</span><br><span class=\"line\"> }</span><br><span class=\"line\"> swap(nums, left, i);</span><br><span class=\"line\"> <span class=\"keyword\">return</span> i;</span><br><span class=\"line\"> }</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"keyword\">static</span> <span class=\"keyword\">void</span> <span class=\"title function_\">quickSort</span><span class=\"params\">(<span class=\"type\">int</span>[] nums, <span class=\"type\">int</span> left, <span class=\"type\">int</span> right)</span> {</span><br><span class=\"line\"> <span class=\"keyword\">if</span> (left >= right) <span class=\"keyword\">return</span>;</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">pivot</span> <span class=\"operator\">=</span> partition(nums, left, right);</span><br><span class=\"line\"> quickSort(nums, left, pivot - <span class=\"number\">1</span>);</span><br><span class=\"line\"> quickSort(nums, pivot + <span class=\"number\">1</span>, right);</span><br><span class=\"line\"> }</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"comment\">/**</span></span><br><span class=\"line\"><span class=\"comment\"> * sink 即元素下沉 我们堆排序是拿第一个元素和最后一个元素交换,然后再下沉得到一个有序的堆</span></span><br><span class=\"line\"><span class=\"comment\"> * k 表示第k个元素 N表示数组的长度</span></span><br><span class=\"line\"><span class=\"comment\"> */</span></span><br><span class=\"line\"> <span class=\"keyword\">static</span> <span class=\"keyword\">void</span> <span class=\"title function_\">sink</span><span class=\"params\">(<span class=\"type\">int</span>[] nums, <span class=\"type\">int</span> k, <span class=\"type\">int</span> N)</span> {</span><br><span class=\"line\"> <span class=\"keyword\">while</span> (k * <span class=\"number\">2</span> + <span class=\"number\">1</span> < N) {</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">i</span> <span class=\"operator\">=</span> k * <span class=\"number\">2</span> + <span class=\"number\">1</span>;</span><br><span class=\"line\"> <span class=\"keyword\">if</span> ((i + <span class=\"number\">1</span>) < N && nums[i] < nums[i + <span class=\"number\">1</span>]) i++;</span><br><span class=\"line\"> <span class=\"keyword\">if</span> (nums[k] > nums[i]) <span class=\"keyword\">break</span>;</span><br><span class=\"line\"> swap(nums, k, i);</span><br><span class=\"line\"> k = i;</span><br><span class=\"line\"> }</span><br><span class=\"line\"> }</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"keyword\">static</span> <span class=\"keyword\">void</span> <span class=\"title function_\">heapSort</span><span class=\"params\">(<span class=\"type\">int</span>[] nums)</span> {</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">N</span> <span class=\"operator\">=</span> nums.length;</span><br><span class=\"line\"> <span class=\"keyword\">for</span> (<span class=\"type\">int</span> <span class=\"variable\">i</span> <span class=\"operator\">=</span> N / <span class=\"number\">2</span>; i >= <span class=\"number\">0</span>; i--) {</span><br><span class=\"line\"> sink(nums, i, N);</span><br><span class=\"line\"> }</span><br><span class=\"line\"> <span class=\"keyword\">for</span> (<span class=\"type\">int</span> <span class=\"variable\">j</span> <span class=\"operator\">=</span> N - <span class=\"number\">1</span>; j > <span class=\"number\">0</span>; j--) {</span><br><span class=\"line\"> swap(nums, <span class=\"number\">0</span>, j);</span><br><span class=\"line\"> sink(nums, <span class=\"number\">0</span>, j);</span><br><span class=\"line\"> }</span><br><span class=\"line\"> }</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"keyword\">static</span> <span class=\"keyword\">void</span> <span class=\"title function_\">mergeSort</span><span class=\"params\">(<span class=\"type\">int</span>[] nums, <span class=\"type\">int</span>[] temp, <span class=\"type\">int</span> left, <span class=\"type\">int</span> right)</span> {</span><br><span class=\"line\"> <span class=\"keyword\">if</span> (left >= right) <span class=\"keyword\">return</span>;</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">mid</span> <span class=\"operator\">=</span> left + ((right - left) >> <span class=\"number\">2</span>);</span><br><span class=\"line\"> mergeSort(nums, temp, left, mid );</span><br><span class=\"line\"> mergeSort(nums, temp, mid + <span class=\"number\">1</span>, right);</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">start1</span> <span class=\"operator\">=</span> left;</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">end1</span> <span class=\"operator\">=</span> mid;</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">start2</span> <span class=\"operator\">=</span> mid + <span class=\"number\">1</span>;</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">end2</span> <span class=\"operator\">=</span> right;</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">k</span> <span class=\"operator\">=</span> left;</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"keyword\">while</span>(start1 <= end1 && start2 <= end2) temp[k++] = nums[start1]<nums[start2] ? nums[start1++] : nums[start2++];</span><br><span class=\"line\"> <span class=\"keyword\">while</span>(start1 <= end1) temp[k++] = nums[start1++];</span><br><span class=\"line\"> <span class=\"keyword\">while</span>(start2 <= end2) temp[k++] = nums[start2++];</span><br><span class=\"line\"> <span class=\"keyword\">for</span>(k=left;k<=right;k++)nums[k] = temp[k];</span><br><span class=\"line\"></span><br><span class=\"line\"> }</span><br><span class=\"line\"></span><br><span class=\"line\">}</span><br></pre></td></tr></table></figure>\n","site":{"data":{}},"excerpt":"","more":"<h4 id=\"三大经典排序算法\"><a href=\"#三大经典排序算法\" class=\"headerlink\" title=\"三大经典排序算法\"></a>三大经典排序算法</h4><p>每天早上要做的第一件重要的事就是刷题,第一步就是写一下这三个经典排序热身。<br>快排,堆排和归并排序。</p>\n<figure class=\"highlight java\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br><span class=\"line\">11</span><br><span class=\"line\">12</span><br><span class=\"line\">13</span><br><span class=\"line\">14</span><br><span class=\"line\">15</span><br><span class=\"line\">16</span><br><span class=\"line\">17</span><br><span class=\"line\">18</span><br><span class=\"line\">19</span><br><span class=\"line\">20</span><br><span class=\"line\">21</span><br><span class=\"line\">22</span><br><span class=\"line\">23</span><br><span class=\"line\">24</span><br><span class=\"line\">25</span><br><span class=\"line\">26</span><br><span class=\"line\">27</span><br><span class=\"line\">28</span><br><span class=\"line\">29</span><br><span class=\"line\">30</span><br><span class=\"line\">31</span><br><span class=\"line\">32</span><br><span class=\"line\">33</span><br><span class=\"line\">34</span><br><span class=\"line\">35</span><br><span class=\"line\">36</span><br><span class=\"line\">37</span><br><span class=\"line\">38</span><br><span class=\"line\">39</span><br><span class=\"line\">40</span><br><span class=\"line\">41</span><br><span class=\"line\">42</span><br><span class=\"line\">43</span><br><span class=\"line\">44</span><br><span class=\"line\">45</span><br><span class=\"line\">46</span><br><span class=\"line\">47</span><br><span class=\"line\">48</span><br><span class=\"line\">49</span><br><span class=\"line\">50</span><br><span class=\"line\">51</span><br><span class=\"line\">52</span><br><span class=\"line\">53</span><br><span class=\"line\">54</span><br><span class=\"line\">55</span><br><span class=\"line\">56</span><br><span class=\"line\">57</span><br><span class=\"line\">58</span><br><span class=\"line\">59</span><br><span class=\"line\">60</span><br><span class=\"line\">61</span><br><span class=\"line\">62</span><br><span class=\"line\">63</span><br><span class=\"line\">64</span><br><span class=\"line\">65</span><br><span class=\"line\">66</span><br><span class=\"line\">67</span><br><span class=\"line\">68</span><br><span class=\"line\">69</span><br><span class=\"line\">70</span><br><span class=\"line\">71</span><br><span class=\"line\">72</span><br><span class=\"line\">73</span><br><span class=\"line\">74</span><br><span class=\"line\">75</span><br><span class=\"line\">76</span><br><span class=\"line\">77</span><br><span class=\"line\">78</span><br><span class=\"line\">79</span><br><span class=\"line\">80</span><br><span class=\"line\">81</span><br><span class=\"line\">82</span><br><span class=\"line\">83</span><br><span class=\"line\">84</span><br><span class=\"line\">85</span><br><span class=\"line\">86</span><br><span class=\"line\">87</span><br><span class=\"line\">88</span><br><span class=\"line\">89</span><br><span class=\"line\">90</span><br><span class=\"line\">91</span><br><span class=\"line\">92</span><br><span class=\"line\">93</span><br><span class=\"line\">94</span><br><span class=\"line\">95</span><br><span class=\"line\">96</span><br><span class=\"line\">97</span><br><span class=\"line\">98</span><br><span class=\"line\">99</span><br><span class=\"line\">100</span><br><span class=\"line\">101</span><br><span class=\"line\">102</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"keyword\">package</span> org.example.basic;</span><br><span class=\"line\"></span><br><span class=\"line\"><span class=\"keyword\">import</span> java.util.Arrays;</span><br><span class=\"line\"></span><br><span class=\"line\"><span class=\"keyword\">public</span> <span class=\"keyword\">class</span> <span class=\"title class_\">SortTest</span> {</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"comment\">/**</span></span><br><span class=\"line\"><span class=\"comment\"> * 测试 写出基于整型数组的 归并 快排 堆排 三大经典排序算法</span></span><br><span class=\"line\"><span class=\"comment\"> */</span></span><br><span class=\"line\"> <span class=\"keyword\">public</span> <span class=\"keyword\">static</span> <span class=\"keyword\">void</span> <span class=\"title function_\">main</span><span class=\"params\">(String[] args)</span> {</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"type\">int</span>[] nums = {<span class=\"number\">5</span>, <span class=\"number\">3</span>, <span class=\"number\">7</span>, <span class=\"number\">1</span>, <span class=\"number\">4</span>, <span class=\"number\">2</span>, <span class=\"number\">6</span>, <span class=\"number\">9</span>, <span class=\"number\">8</span>};</span><br><span class=\"line\"></span><br><span class=\"line\"> System.out.println(Arrays.toString(nums));</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"comment\">//partition(nums, 0, nums.length - 1);</span></span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">N</span> <span class=\"operator\">=</span> nums.length;</span><br><span class=\"line\"> <span class=\"comment\">//quickSort(nums, 0, N - 1);</span></span><br><span class=\"line\"> <span class=\"comment\">//sink(nums, 0, N);</span></span><br><span class=\"line\"> <span class=\"comment\">//heapSort(nums);</span></span><br><span class=\"line\"> mergeSort(nums, <span class=\"keyword\">new</span> <span class=\"title class_\">int</span>[N], <span class=\"number\">0</span>, N - <span class=\"number\">1</span>);</span><br><span class=\"line\"> System.out.println(Arrays.toString(nums));</span><br><span class=\"line\"> }</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"comment\">//快排 一个partition 一个quickSort</span></span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"keyword\">static</span> <span class=\"keyword\">void</span> <span class=\"title function_\">swap</span><span class=\"params\">(<span class=\"type\">int</span>[] nums, <span class=\"type\">int</span> i, <span class=\"type\">int</span> j)</span> {</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">t</span> <span class=\"operator\">=</span> nums[i];</span><br><span class=\"line\"> nums[i] = nums[j];</span><br><span class=\"line\"> nums[j] = t;</span><br><span class=\"line\"> }</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"comment\">/**</span></span><br><span class=\"line\"><span class=\"comment\"> * 把left 当作 基准比较的值 即pivot = nums[left]</span></span><br><span class=\"line\"><span class=\"comment\"> * 划分一次 左边的小 中间是pivot 右边是大</span></span><br><span class=\"line\"><span class=\"comment\"> * 所以主要就是交换 和 比较</span></span><br><span class=\"line\"><span class=\"comment\"> */</span></span><br><span class=\"line\"> <span class=\"keyword\">static</span> <span class=\"type\">int</span> <span class=\"title function_\">partition</span><span class=\"params\">(<span class=\"type\">int</span>[] nums, <span class=\"type\">int</span> left, <span class=\"type\">int</span> right)</span> {</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">i</span> <span class=\"operator\">=</span> left;</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">j</span> <span class=\"operator\">=</span> left + <span class=\"number\">1</span>;</span><br><span class=\"line\"> <span class=\"keyword\">while</span> (j <= right) {</span><br><span class=\"line\"> <span class=\"keyword\">if</span> (nums[j] < nums[left]) {</span><br><span class=\"line\"> swap(nums, j, ++i);</span><br><span class=\"line\"> }</span><br><span class=\"line\"> j++;</span><br><span class=\"line\"> }</span><br><span class=\"line\"> swap(nums, left, i);</span><br><span class=\"line\"> <span class=\"keyword\">return</span> i;</span><br><span class=\"line\"> }</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"keyword\">static</span> <span class=\"keyword\">void</span> <span class=\"title function_\">quickSort</span><span class=\"params\">(<span class=\"type\">int</span>[] nums, <span class=\"type\">int</span> left, <span class=\"type\">int</span> right)</span> {</span><br><span class=\"line\"> <span class=\"keyword\">if</span> (left >= right) <span class=\"keyword\">return</span>;</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">pivot</span> <span class=\"operator\">=</span> partition(nums, left, right);</span><br><span class=\"line\"> quickSort(nums, left, pivot - <span class=\"number\">1</span>);</span><br><span class=\"line\"> quickSort(nums, pivot + <span class=\"number\">1</span>, right);</span><br><span class=\"line\"> }</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"comment\">/**</span></span><br><span class=\"line\"><span class=\"comment\"> * sink 即元素下沉 我们堆排序是拿第一个元素和最后一个元素交换,然后再下沉得到一个有序的堆</span></span><br><span class=\"line\"><span class=\"comment\"> * k 表示第k个元素 N表示数组的长度</span></span><br><span class=\"line\"><span class=\"comment\"> */</span></span><br><span class=\"line\"> <span class=\"keyword\">static</span> <span class=\"keyword\">void</span> <span class=\"title function_\">sink</span><span class=\"params\">(<span class=\"type\">int</span>[] nums, <span class=\"type\">int</span> k, <span class=\"type\">int</span> N)</span> {</span><br><span class=\"line\"> <span class=\"keyword\">while</span> (k * <span class=\"number\">2</span> + <span class=\"number\">1</span> < N) {</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">i</span> <span class=\"operator\">=</span> k * <span class=\"number\">2</span> + <span class=\"number\">1</span>;</span><br><span class=\"line\"> <span class=\"keyword\">if</span> ((i + <span class=\"number\">1</span>) < N && nums[i] < nums[i + <span class=\"number\">1</span>]) i++;</span><br><span class=\"line\"> <span class=\"keyword\">if</span> (nums[k] > nums[i]) <span class=\"keyword\">break</span>;</span><br><span class=\"line\"> swap(nums, k, i);</span><br><span class=\"line\"> k = i;</span><br><span class=\"line\"> }</span><br><span class=\"line\"> }</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"keyword\">static</span> <span class=\"keyword\">void</span> <span class=\"title function_\">heapSort</span><span class=\"params\">(<span class=\"type\">int</span>[] nums)</span> {</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">N</span> <span class=\"operator\">=</span> nums.length;</span><br><span class=\"line\"> <span class=\"keyword\">for</span> (<span class=\"type\">int</span> <span class=\"variable\">i</span> <span class=\"operator\">=</span> N / <span class=\"number\">2</span>; i >= <span class=\"number\">0</span>; i--) {</span><br><span class=\"line\"> sink(nums, i, N);</span><br><span class=\"line\"> }</span><br><span class=\"line\"> <span class=\"keyword\">for</span> (<span class=\"type\">int</span> <span class=\"variable\">j</span> <span class=\"operator\">=</span> N - <span class=\"number\">1</span>; j > <span class=\"number\">0</span>; j--) {</span><br><span class=\"line\"> swap(nums, <span class=\"number\">0</span>, j);</span><br><span class=\"line\"> sink(nums, <span class=\"number\">0</span>, j);</span><br><span class=\"line\"> }</span><br><span class=\"line\"> }</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"keyword\">static</span> <span class=\"keyword\">void</span> <span class=\"title function_\">mergeSort</span><span class=\"params\">(<span class=\"type\">int</span>[] nums, <span class=\"type\">int</span>[] temp, <span class=\"type\">int</span> left, <span class=\"type\">int</span> right)</span> {</span><br><span class=\"line\"> <span class=\"keyword\">if</span> (left >= right) <span class=\"keyword\">return</span>;</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">mid</span> <span class=\"operator\">=</span> left + ((right - left) >> <span class=\"number\">2</span>);</span><br><span class=\"line\"> mergeSort(nums, temp, left, mid );</span><br><span class=\"line\"> mergeSort(nums, temp, mid + <span class=\"number\">1</span>, right);</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">start1</span> <span class=\"operator\">=</span> left;</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">end1</span> <span class=\"operator\">=</span> mid;</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">start2</span> <span class=\"operator\">=</span> mid + <span class=\"number\">1</span>;</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">end2</span> <span class=\"operator\">=</span> right;</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">k</span> <span class=\"operator\">=</span> left;</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"keyword\">while</span>(start1 <= end1 && start2 <= end2) temp[k++] = nums[start1]<nums[start2] ? nums[start1++] : nums[start2++];</span><br><span class=\"line\"> <span class=\"keyword\">while</span>(start1 <= end1) temp[k++] = nums[start1++];</span><br><span class=\"line\"> <span class=\"keyword\">while</span>(start2 <= end2) temp[k++] = nums[start2++];</span><br><span class=\"line\"> <span class=\"keyword\">for</span>(k=left;k<=right;k++)nums[k] = temp[k];</span><br><span class=\"line\"></span><br><span class=\"line\"> }</span><br><span class=\"line\"></span><br><span class=\"line\">}</span><br></pre></td></tr></table></figure>\n"},{"title":"Scala学习Part2","date":"2021-09-06T16:00:00.000Z","comments":1,"toc":true,"_content":"#### 文件和正则表达式\nSource.fromFile(...).getLines.toArray输出文件中的所有行。\nSource.fromFile(...).mkString以字符串的形式输出文件内容。\n将字符串转成数字,可以使用toInt or toDouble的方式。\n使用Java的PrintWriter来写入文本文件。\n\"regex\".r是一个Regex对象。\n如果你的正则表达式中包含反斜杠和引号的话,用\"\"\"...\"\"\"。\n如果正则模式包含分组,你可以用下面的语法来提取内容:\nfor(regex(v1,v2...vn) <- string>)\n```scala\nimport scala.io.Source\nval source=Source.fromFile(\"myfile.txt\",\"UTF-8\")\nval lineIterator = source.getLines\nval source1 = Source.fromURL(\"http://www.baidu.com\",\"UTF-8\")\nval source2 = Source.fromString(\"hello,world!\")\n//close source after you use\n```\n\n##### 进程控制 \nscala.sys.process包提供了用于与shell程序交互的工具。你可以使用scala编写shell脚本,\n利用scala提供的威力。\n```scala\nimport sys.process._\n\"ls -al..\"!\n//这里面完成了一个字符串到processBuilder对象的隐式转换\n```\n!返回的结果就是被执行程序的返回值,程序执行成功的话就是0,否则就显示返回的非0值。\n如果使用!! 则就会以字符串的形式返回执行的结果。\n\n##### 正则表达式\nscala.util.matching.Regex类是scala中的正则。\n```scala\nval numPattern = \"[0-9]+\".r\nfor( matchString <- numPattern.findAllIn(\"99 bottles, 98 bottles\"))\nprintln(matchString)\n\nval numitemPattern = \"([0-9]+) ([a-z]+)\".r\nval numitemPattern(num, item) = \"99 bottles\"\n```\n\n#### 特质\nscala中的特质类似java中的接口。\n特质中未被实现的方法默认就是抽象方法。\n```scala\ntrait Logger {\n def log(msg: String) //这是个抽象方法\n}\n```\nscala中的特定不要求所有的方法都必须是抽象的,这点在java8以后也支持了,java8支持接口中含有\n默认的实现。\n缺少构造器参数是特质与类之间唯一的技术差别。除此之外,特质可以具备类的所有物质,比如具体的和抽象的\n字段,以及超类。\n\n#### 操作符练习\n提供操作符用于构造html表格。例如:\nTable() | \"java\" | \"scala\" || \"go\" | \"rust\" ||\"jvm\" | \".net\"\n```scala\npackage org.example\n\nclass Table {\n\n var s: String = \"\"\n\n def |(str: String): Table = {\n this.s = this.s + \"<td>\" + str + \"</td>\"\n this\n }\n\n def ||(str: String): Table = {\n this.s = this.s + \"</tr><td>\" + str + \"</td>\"\n this\n }\n\n override def toString(): String = {\n \"<table><tr>\" + this.s + \"</tr><table>\"\n }\n}\n\nobject Table {\n def apply(): Table = {\n new Table()\n }\n def main(args :Array[String]) = {\n val str = Table() | \"java\" | \"scala\" || \"go\" | \"rust\" ||\"jvm\" | \".net\"\n println(str)\n }\n}\n\n```","source":"_posts/Scala学习2.md","raw":"\n---\ntitle: Scala学习Part2\ndate: 2021-09-07\ncomments: true #是否可评论\ntoc: true #是否显示文章目录\ncategories: \"学习\" #分类\ntags: #标签\n - Scala\n---\n#### 文件和正则表达式\nSource.fromFile(...).getLines.toArray输出文件中的所有行。\nSource.fromFile(...).mkString以字符串的形式输出文件内容。\n将字符串转成数字,可以使用toInt or toDouble的方式。\n使用Java的PrintWriter来写入文本文件。\n\"regex\".r是一个Regex对象。\n如果你的正则表达式中包含反斜杠和引号的话,用\"\"\"...\"\"\"。\n如果正则模式包含分组,你可以用下面的语法来提取内容:\nfor(regex(v1,v2...vn) <- string>)\n```scala\nimport scala.io.Source\nval source=Source.fromFile(\"myfile.txt\",\"UTF-8\")\nval lineIterator = source.getLines\nval source1 = Source.fromURL(\"http://www.baidu.com\",\"UTF-8\")\nval source2 = Source.fromString(\"hello,world!\")\n//close source after you use\n```\n\n##### 进程控制 \nscala.sys.process包提供了用于与shell程序交互的工具。你可以使用scala编写shell脚本,\n利用scala提供的威力。\n```scala\nimport sys.process._\n\"ls -al..\"!\n//这里面完成了一个字符串到processBuilder对象的隐式转换\n```\n!返回的结果就是被执行程序的返回值,程序执行成功的话就是0,否则就显示返回的非0值。\n如果使用!! 则就会以字符串的形式返回执行的结果。\n\n##### 正则表达式\nscala.util.matching.Regex类是scala中的正则。\n```scala\nval numPattern = \"[0-9]+\".r\nfor( matchString <- numPattern.findAllIn(\"99 bottles, 98 bottles\"))\nprintln(matchString)\n\nval numitemPattern = \"([0-9]+) ([a-z]+)\".r\nval numitemPattern(num, item) = \"99 bottles\"\n```\n\n#### 特质\nscala中的特质类似java中的接口。\n特质中未被实现的方法默认就是抽象方法。\n```scala\ntrait Logger {\n def log(msg: String) //这是个抽象方法\n}\n```\nscala中的特定不要求所有的方法都必须是抽象的,这点在java8以后也支持了,java8支持接口中含有\n默认的实现。\n缺少构造器参数是特质与类之间唯一的技术差别。除此之外,特质可以具备类的所有物质,比如具体的和抽象的\n字段,以及超类。\n\n#### 操作符练习\n提供操作符用于构造html表格。例如:\nTable() | \"java\" | \"scala\" || \"go\" | \"rust\" ||\"jvm\" | \".net\"\n```scala\npackage org.example\n\nclass Table {\n\n var s: String = \"\"\n\n def |(str: String): Table = {\n this.s = this.s + \"<td>\" + str + \"</td>\"\n this\n }\n\n def ||(str: String): Table = {\n this.s = this.s + \"</tr><td>\" + str + \"</td>\"\n this\n }\n\n override def toString(): String = {\n \"<table><tr>\" + this.s + \"</tr><table>\"\n }\n}\n\nobject Table {\n def apply(): Table = {\n new Table()\n }\n def main(args :Array[String]) = {\n val str = Table() | \"java\" | \"scala\" || \"go\" | \"rust\" ||\"jvm\" | \".net\"\n println(str)\n }\n}\n\n```","slug":"Scala学习2","published":1,"updated":"2022-04-20T09:35:50.863Z","layout":"post","photos":[],"link":"","_id":"cl27q3uv8000tn94o78y392lt","content":"<h4 id=\"文件和正则表达式\"><a href=\"#文件和正则表达式\" class=\"headerlink\" title=\"文件和正则表达式\"></a>文件和正则表达式</h4><p>Source.fromFile(…).getLines.toArray输出文件中的所有行。<br>Source.fromFile(…).mkString以字符串的形式输出文件内容。<br>将字符串转成数字,可以使用toInt or toDouble的方式。<br>使用Java的PrintWriter来写入文本文件。<br>“regex”.r是一个Regex对象。<br>如果你的正则表达式中包含反斜杠和引号的话,用”””…”””。<br>如果正则模式包含分组,你可以用下面的语法来提取内容:<br>for(regex(v1,v2…vn) <- string>)</p>\n<figure class=\"highlight scala\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"keyword\">import</span> scala.io.<span class=\"type\">Source</span></span><br><span class=\"line\"><span class=\"keyword\">val</span> source=<span class=\"type\">Source</span>.fromFile(<span class=\"string\">"myfile.txt"</span>,<span class=\"string\">"UTF-8"</span>)</span><br><span class=\"line\"><span class=\"keyword\">val</span> lineIterator = source.getLines</span><br><span class=\"line\"><span class=\"keyword\">val</span> source1 = <span class=\"type\">Source</span>.fromURL(<span class=\"string\">"http://www.baidu.com"</span>,<span class=\"string\">"UTF-8"</span>)</span><br><span class=\"line\"><span class=\"keyword\">val</span> source2 = <span class=\"type\">Source</span>.fromString(<span class=\"string\">"hello,world!"</span>)</span><br><span class=\"line\"><span class=\"comment\">//close source after you use</span></span><br></pre></td></tr></table></figure>\n\n<h5 id=\"进程控制\"><a href=\"#进程控制\" class=\"headerlink\" title=\"进程控制\"></a>进程控制</h5><p>scala.sys.process包提供了用于与shell程序交互的工具。你可以使用scala编写shell脚本,<br>利用scala提供的威力。</p>\n<figure class=\"highlight scala\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"keyword\">import</span> sys.process._</span><br><span class=\"line\"><span class=\"string\">"ls -al.."</span>!</span><br><span class=\"line\"><span class=\"comment\">//这里面完成了一个字符串到processBuilder对象的隐式转换</span></span><br></pre></td></tr></table></figure>\n<p>!返回的结果就是被执行程序的返回值,程序执行成功的话就是0,否则就显示返回的非0值。<br>如果使用!! 则就会以字符串的形式返回执行的结果。</p>\n<h5 id=\"正则表达式\"><a href=\"#正则表达式\" class=\"headerlink\" title=\"正则表达式\"></a>正则表达式</h5><p>scala.util.matching.Regex类是scala中的正则。</p>\n<figure class=\"highlight scala\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"keyword\">val</span> numPattern = <span class=\"string\">"[0-9]+"</span>.r</span><br><span class=\"line\"><span class=\"keyword\">for</span>( matchString <- numPattern.findAllIn(<span class=\"string\">"99 bottles, 98 bottles"</span>))</span><br><span class=\"line\">println(matchString)</span><br><span class=\"line\"></span><br><span class=\"line\"><span class=\"keyword\">val</span> numitemPattern = <span class=\"string\">"([0-9]+) ([a-z]+)"</span>.r</span><br><span class=\"line\"><span class=\"keyword\">val</span> numitemPattern(num, item) = <span class=\"string\">"99 bottles"</span></span><br></pre></td></tr></table></figure>\n\n<h4 id=\"特质\"><a href=\"#特质\" class=\"headerlink\" title=\"特质\"></a>特质</h4><p>scala中的特质类似java中的接口。<br>特质中未被实现的方法默认就是抽象方法。</p>\n<figure class=\"highlight scala\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"class\"><span class=\"keyword\">trait</span> <span class=\"title\">Logger</span> </span>{</span><br><span class=\"line\"> <span class=\"function\"><span class=\"keyword\">def</span> <span class=\"title\">log</span></span>(msg: <span class=\"type\">String</span>) <span class=\"comment\">//这是个抽象方法</span></span><br><span class=\"line\">}</span><br></pre></td></tr></table></figure>\n<p>scala中的特定不要求所有的方法都必须是抽象的,这点在java8以后也支持了,java8支持接口中含有<br>默认的实现。<br>缺少构造器参数是特质与类之间唯一的技术差别。除此之外,特质可以具备类的所有物质,比如具体的和抽象的<br>字段,以及超类。</p>\n<h4 id=\"操作符练习\"><a href=\"#操作符练习\" class=\"headerlink\" title=\"操作符练习\"></a>操作符练习</h4><p>提供操作符用于构造html表格。例如:<br>Table() | “java” | “scala” || “go” | “rust” ||”jvm” | “.net”</p>\n<figure class=\"highlight scala\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br><span class=\"line\">11</span><br><span class=\"line\">12</span><br><span class=\"line\">13</span><br><span class=\"line\">14</span><br><span class=\"line\">15</span><br><span class=\"line\">16</span><br><span class=\"line\">17</span><br><span class=\"line\">18</span><br><span class=\"line\">19</span><br><span class=\"line\">20</span><br><span class=\"line\">21</span><br><span class=\"line\">22</span><br><span class=\"line\">23</span><br><span class=\"line\">24</span><br><span class=\"line\">25</span><br><span class=\"line\">26</span><br><span class=\"line\">27</span><br><span class=\"line\">28</span><br><span class=\"line\">29</span><br><span class=\"line\">30</span><br><span class=\"line\">31</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"keyword\">package</span> org.example</span><br><span class=\"line\"></span><br><span class=\"line\"><span class=\"class\"><span class=\"keyword\">class</span> <span class=\"title\">Table</span> </span>{</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"keyword\">var</span> s: <span class=\"type\">String</span> = <span class=\"string\">""</span></span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"function\"><span class=\"keyword\">def</span> <span class=\"title\">|</span></span>(str: <span class=\"type\">String</span>): <span class=\"type\">Table</span> = {</span><br><span class=\"line\"> <span class=\"keyword\">this</span>.s = <span class=\"keyword\">this</span>.s + <span class=\"string\">"<td>"</span> + str + <span class=\"string\">"</td>"</span></span><br><span class=\"line\"> <span class=\"keyword\">this</span></span><br><span class=\"line\"> }</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"function\"><span class=\"keyword\">def</span> <span class=\"title\">||</span></span>(str: <span class=\"type\">String</span>): <span class=\"type\">Table</span> = {</span><br><span class=\"line\"> <span class=\"keyword\">this</span>.s = <span class=\"keyword\">this</span>.s + <span class=\"string\">"</tr><td>"</span> + str + <span class=\"string\">"</td>"</span></span><br><span class=\"line\"> <span class=\"keyword\">this</span></span><br><span class=\"line\"> }</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"keyword\">override</span> <span class=\"function\"><span class=\"keyword\">def</span> <span class=\"title\">toString</span></span>(): <span class=\"type\">String</span> = {</span><br><span class=\"line\"> <span class=\"string\">"<table><tr>"</span> + <span class=\"keyword\">this</span>.s + <span class=\"string\">"</tr><table>"</span></span><br><span class=\"line\"> }</span><br><span class=\"line\">}</span><br><span class=\"line\"></span><br><span class=\"line\"><span class=\"class\"><span class=\"keyword\">object</span> <span class=\"title\">Table</span> </span>{</span><br><span class=\"line\"> <span class=\"function\"><span class=\"keyword\">def</span> <span class=\"title\">apply</span></span>(): <span class=\"type\">Table</span> = {</span><br><span class=\"line\"> <span class=\"keyword\">new</span> <span class=\"type\">Table</span>()</span><br><span class=\"line\"> }</span><br><span class=\"line\"> <span class=\"function\"><span class=\"keyword\">def</span> <span class=\"title\">main</span></span>(args :<span class=\"type\">Array</span>[<span class=\"type\">String</span>]) = {</span><br><span class=\"line\"> <span class=\"keyword\">val</span> str = <span class=\"type\">Table</span>() | <span class=\"string\">"java"</span> | <span class=\"string\">"scala"</span> || <span class=\"string\">"go"</span> | <span class=\"string\">"rust"</span> ||<span class=\"string\">"jvm"</span> | <span class=\"string\">".net"</span></span><br><span class=\"line\"> println(str)</span><br><span class=\"line\"> }</span><br><span class=\"line\">}</span><br><span class=\"line\"></span><br></pre></td></tr></table></figure>","site":{"data":{}},"excerpt":"","more":"<h4 id=\"文件和正则表达式\"><a href=\"#文件和正则表达式\" class=\"headerlink\" title=\"文件和正则表达式\"></a>文件和正则表达式</h4><p>Source.fromFile(…).getLines.toArray输出文件中的所有行。<br>Source.fromFile(…).mkString以字符串的形式输出文件内容。<br>将字符串转成数字,可以使用toInt or toDouble的方式。<br>使用Java的PrintWriter来写入文本文件。<br>“regex”.r是一个Regex对象。<br>如果你的正则表达式中包含反斜杠和引号的话,用”””…”””。<br>如果正则模式包含分组,你可以用下面的语法来提取内容:<br>for(regex(v1,v2…vn) <- string>)</p>\n<figure class=\"highlight scala\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"keyword\">import</span> scala.io.<span class=\"type\">Source</span></span><br><span class=\"line\"><span class=\"keyword\">val</span> source=<span class=\"type\">Source</span>.fromFile(<span class=\"string\">"myfile.txt"</span>,<span class=\"string\">"UTF-8"</span>)</span><br><span class=\"line\"><span class=\"keyword\">val</span> lineIterator = source.getLines</span><br><span class=\"line\"><span class=\"keyword\">val</span> source1 = <span class=\"type\">Source</span>.fromURL(<span class=\"string\">"http://www.baidu.com"</span>,<span class=\"string\">"UTF-8"</span>)</span><br><span class=\"line\"><span class=\"keyword\">val</span> source2 = <span class=\"type\">Source</span>.fromString(<span class=\"string\">"hello,world!"</span>)</span><br><span class=\"line\"><span class=\"comment\">//close source after you use</span></span><br></pre></td></tr></table></figure>\n\n<h5 id=\"进程控制\"><a href=\"#进程控制\" class=\"headerlink\" title=\"进程控制\"></a>进程控制</h5><p>scala.sys.process包提供了用于与shell程序交互的工具。你可以使用scala编写shell脚本,<br>利用scala提供的威力。</p>\n<figure class=\"highlight scala\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"keyword\">import</span> sys.process._</span><br><span class=\"line\"><span class=\"string\">"ls -al.."</span>!</span><br><span class=\"line\"><span class=\"comment\">//这里面完成了一个字符串到processBuilder对象的隐式转换</span></span><br></pre></td></tr></table></figure>\n<p>!返回的结果就是被执行程序的返回值,程序执行成功的话就是0,否则就显示返回的非0值。<br>如果使用!! 则就会以字符串的形式返回执行的结果。</p>\n<h5 id=\"正则表达式\"><a href=\"#正则表达式\" class=\"headerlink\" title=\"正则表达式\"></a>正则表达式</h5><p>scala.util.matching.Regex类是scala中的正则。</p>\n<figure class=\"highlight scala\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"keyword\">val</span> numPattern = <span class=\"string\">"[0-9]+"</span>.r</span><br><span class=\"line\"><span class=\"keyword\">for</span>( matchString <- numPattern.findAllIn(<span class=\"string\">"99 bottles, 98 bottles"</span>))</span><br><span class=\"line\">println(matchString)</span><br><span class=\"line\"></span><br><span class=\"line\"><span class=\"keyword\">val</span> numitemPattern = <span class=\"string\">"([0-9]+) ([a-z]+)"</span>.r</span><br><span class=\"line\"><span class=\"keyword\">val</span> numitemPattern(num, item) = <span class=\"string\">"99 bottles"</span></span><br></pre></td></tr></table></figure>\n\n<h4 id=\"特质\"><a href=\"#特质\" class=\"headerlink\" title=\"特质\"></a>特质</h4><p>scala中的特质类似java中的接口。<br>特质中未被实现的方法默认就是抽象方法。</p>\n<figure class=\"highlight scala\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"class\"><span class=\"keyword\">trait</span> <span class=\"title\">Logger</span> </span>{</span><br><span class=\"line\"> <span class=\"function\"><span class=\"keyword\">def</span> <span class=\"title\">log</span></span>(msg: <span class=\"type\">String</span>) <span class=\"comment\">//这是个抽象方法</span></span><br><span class=\"line\">}</span><br></pre></td></tr></table></figure>\n<p>scala中的特定不要求所有的方法都必须是抽象的,这点在java8以后也支持了,java8支持接口中含有<br>默认的实现。<br>缺少构造器参数是特质与类之间唯一的技术差别。除此之外,特质可以具备类的所有物质,比如具体的和抽象的<br>字段,以及超类。</p>\n<h4 id=\"操作符练习\"><a href=\"#操作符练习\" class=\"headerlink\" title=\"操作符练习\"></a>操作符练习</h4><p>提供操作符用于构造html表格。例如:<br>Table() | “java” | “scala” || “go” | “rust” ||”jvm” | “.net”</p>\n<figure class=\"highlight scala\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br><span class=\"line\">11</span><br><span class=\"line\">12</span><br><span class=\"line\">13</span><br><span class=\"line\">14</span><br><span class=\"line\">15</span><br><span class=\"line\">16</span><br><span class=\"line\">17</span><br><span class=\"line\">18</span><br><span class=\"line\">19</span><br><span class=\"line\">20</span><br><span class=\"line\">21</span><br><span class=\"line\">22</span><br><span class=\"line\">23</span><br><span class=\"line\">24</span><br><span class=\"line\">25</span><br><span class=\"line\">26</span><br><span class=\"line\">27</span><br><span class=\"line\">28</span><br><span class=\"line\">29</span><br><span class=\"line\">30</span><br><span class=\"line\">31</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"keyword\">package</span> org.example</span><br><span class=\"line\"></span><br><span class=\"line\"><span class=\"class\"><span class=\"keyword\">class</span> <span class=\"title\">Table</span> </span>{</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"keyword\">var</span> s: <span class=\"type\">String</span> = <span class=\"string\">""</span></span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"function\"><span class=\"keyword\">def</span> <span class=\"title\">|</span></span>(str: <span class=\"type\">String</span>): <span class=\"type\">Table</span> = {</span><br><span class=\"line\"> <span class=\"keyword\">this</span>.s = <span class=\"keyword\">this</span>.s + <span class=\"string\">"<td>"</span> + str + <span class=\"string\">"</td>"</span></span><br><span class=\"line\"> <span class=\"keyword\">this</span></span><br><span class=\"line\"> }</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"function\"><span class=\"keyword\">def</span> <span class=\"title\">||</span></span>(str: <span class=\"type\">String</span>): <span class=\"type\">Table</span> = {</span><br><span class=\"line\"> <span class=\"keyword\">this</span>.s = <span class=\"keyword\">this</span>.s + <span class=\"string\">"</tr><td>"</span> + str + <span class=\"string\">"</td>"</span></span><br><span class=\"line\"> <span class=\"keyword\">this</span></span><br><span class=\"line\"> }</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"keyword\">override</span> <span class=\"function\"><span class=\"keyword\">def</span> <span class=\"title\">toString</span></span>(): <span class=\"type\">String</span> = {</span><br><span class=\"line\"> <span class=\"string\">"<table><tr>"</span> + <span class=\"keyword\">this</span>.s + <span class=\"string\">"</tr><table>"</span></span><br><span class=\"line\"> }</span><br><span class=\"line\">}</span><br><span class=\"line\"></span><br><span class=\"line\"><span class=\"class\"><span class=\"keyword\">object</span> <span class=\"title\">Table</span> </span>{</span><br><span class=\"line\"> <span class=\"function\"><span class=\"keyword\">def</span> <span class=\"title\">apply</span></span>(): <span class=\"type\">Table</span> = {</span><br><span class=\"line\"> <span class=\"keyword\">new</span> <span class=\"type\">Table</span>()</span><br><span class=\"line\"> }</span><br><span class=\"line\"> <span class=\"function\"><span class=\"keyword\">def</span> <span class=\"title\">main</span></span>(args :<span class=\"type\">Array</span>[<span class=\"type\">String</span>]) = {</span><br><span class=\"line\"> <span class=\"keyword\">val</span> str = <span class=\"type\">Table</span>() | <span class=\"string\">"java"</span> | <span class=\"string\">"scala"</span> || <span class=\"string\">"go"</span> | <span class=\"string\">"rust"</span> ||<span class=\"string\">"jvm"</span> | <span class=\"string\">".net"</span></span><br><span class=\"line\"> println(str)</span><br><span class=\"line\"> }</span><br><span class=\"line\">}</span><br><span class=\"line\"></span><br></pre></td></tr></table></figure>"},{"title":"夏花对话录1","date":"2021-09-13T23:21:03.000Z","toc":true,"_content":"\n\n\n#### 对话录\n\n##### 睡前小语\n亲爱的\n有时候不说想你 \n好像就真的没那么想\n说想了吧\n就真的更想你了\n(夏夏)\n\n亲爱的 这就是爱情\n一个人就在那里\n不想她 你也知道她在\n想她的时候\n便心生欢喜\n(小结巴)\n\n有时候我觉得爱情很矫情\n就好像小王子里面的玫瑰花\n非得怎么怎么样\n\n有时候我不愿意助长她这种娇气\n我希望她昂扬 向上\n在岁月中沉淀\n在风雨中成长\n(夏夏)\n\n有时候我觉得爱情可珍贵\n她让人迎难而上\n给人希望和力量 \n你看 \n她多像尘世里摇曳着的\n一朵高洁的花\n(小结巴)\n\n##### 日常对白1\n小仙女上线啦\n你要不要亲我一口?(夏夏)\n\n好啊,我溃疡好啦\n前天晚上为什么睡得那么晚?\n\n一口不够\n多来几口\n前天我们同事聚餐K歌啦\n\n亲吻中....\n\n周末也可以说一声去哪里嘛\n结束了就是想给你说呀\n谁知道你睡了\n激动的心情都没人分享[大哭]\n\n为什么激动\n唱嗨了就激动了呀\n哪些歌让你唱嗨啦[坏笑]\n你应该给我打电话\n\n突然好想你\n美人吟\n心动\n我喜欢上你时的内心活动\n\n我自作多情以为你突然好想我[坏笑][偷笑]\n发现是首歌\n\n\n##### 日常对白2 \n大脸猫猫,我想吃鱼\n只吃鱼头\n鱼身是你\n\n我可不管 你说好了\n你说的我信啦\n你的余生是我的\n\nthe rest of the fish body is yours\n我不懂 反正你的余生是我的\n拒绝画大饼\n行胜于言\n\n......\n\n亲爱的\n要时时刻刻都心里有我哦\n我感应得到的[愉快]\n\n我喜欢你这么说\n你不怕我的爱把你包围你无法呼吸[坏笑]\n我心里有你 我现在做规划做决定都会考虑你的[呲牙]\n谢谢亲爱的~\n\n亲爱的 你心里可要有我啊\n鱼身都是你,心里当然都是你啦\n不要给我画饼 行胜于言\n我画的那能叫饼吗?\n那叫梦想\n\n我错啦 我居然奢望男女平等\n领导就是领导\n\n##### 夏夏语录\n有空吗?\n来恋个爱[爱心]\n\n晚安安\n今天又是你得不到的女人[偷笑]\n\n再长真成多肉少女了[偷笑]\n最近为了催贝拉长肉,亲身示范的代价[破涕为笑]\n\n我家进门左边也是一颗桂花树,有二十多年了\n右边是一棵银杏树,也有十几年了\n银杏后面还有一棵栀子花,我小时候就在,估计我得叫姐[偷笑]\n\n代表月亮消灭你\n又想拐走我的心\n\n我没有怀疑你\n我怀疑的是人性\n即兴创作,欢迎推广\n\n虽然每天都想你,但想你和想你也是不一样的~\n\n我也想你,很想很想的那种\n亲爱的我舌头好了\n可以亲亲啦[亲亲][亲亲]\n\n(可爱的夏夏)\n今天食堂有毛血旺\n我怀疑有人要害我\n我好不容易消下去的痘痘\n估计要死灰复燃了\n因为不吃是不可能的。。。\n\n\n##### 我的温柔\n\n花草树木,人间星河\n无一是你 无一不是你\n\n\n我喜欢这秋天\n秋高气爽的感觉\n穿一袭长衫\n牵着你的手去公园里散步\n任凉风吹动你的头发\n听你说着可甜可咸的话\n你看着湖面,我看着你\n\n在你下班的路上,我们相汇在广渠门站\n然后下了车沿着河边的长廊\n牵着手,走啊走\n偶尔会遇到迎面而来的路人\n虽然你是擅长走路的\n但我仍想背着你走一段\n我背着你,你背着包\n享受着你趴在我背上的感觉\n你会在我耳边说上一些悄悄话\n当路人过来的时候埋头在我背上\n或朝着河里望上一眼\n一步一步往家的方向走去\n\n##### 夏夏插花作品展\n![作品一](https://raw.githubusercontent.com/student2028/blog_markdown/master/img/chahua1.jpeg)\n![作品二](https://raw.githubusercontent.com/student2028/blog_markdown/master/img/chahua2.jpeg)\n![作品三](https://raw.githubusercontent.com/student2028/blog_markdown/master/img/chahua3.jpeg)\n![语录一](https://raw.githubusercontent.com/student2028/blog_markdown/master/img/yulu1.png)\n","source":"_posts/夏花对话录1.md","raw":"---\ntitle: 夏花对话录1\ndate: 2021-09-14 07:21:03\ntoc: true #是否显示文章目录\ncategories: \"生活\" #分类\ntags: #标签\n - 语录\n---\n\n\n\n#### 对话录\n\n##### 睡前小语\n亲爱的\n有时候不说想你 \n好像就真的没那么想\n说想了吧\n就真的更想你了\n(夏夏)\n\n亲爱的 这就是爱情\n一个人就在那里\n不想她 你也知道她在\n想她的时候\n便心生欢喜\n(小结巴)\n\n有时候我觉得爱情很矫情\n就好像小王子里面的玫瑰花\n非得怎么怎么样\n\n有时候我不愿意助长她这种娇气\n我希望她昂扬 向上\n在岁月中沉淀\n在风雨中成长\n(夏夏)\n\n有时候我觉得爱情可珍贵\n她让人迎难而上\n给人希望和力量 \n你看 \n她多像尘世里摇曳着的\n一朵高洁的花\n(小结巴)\n\n##### 日常对白1\n小仙女上线啦\n你要不要亲我一口?(夏夏)\n\n好啊,我溃疡好啦\n前天晚上为什么睡得那么晚?\n\n一口不够\n多来几口\n前天我们同事聚餐K歌啦\n\n亲吻中....\n\n周末也可以说一声去哪里嘛\n结束了就是想给你说呀\n谁知道你睡了\n激动的心情都没人分享[大哭]\n\n为什么激动\n唱嗨了就激动了呀\n哪些歌让你唱嗨啦[坏笑]\n你应该给我打电话\n\n突然好想你\n美人吟\n心动\n我喜欢上你时的内心活动\n\n我自作多情以为你突然好想我[坏笑][偷笑]\n发现是首歌\n\n\n##### 日常对白2 \n大脸猫猫,我想吃鱼\n只吃鱼头\n鱼身是你\n\n我可不管 你说好了\n你说的我信啦\n你的余生是我的\n\nthe rest of the fish body is yours\n我不懂 反正你的余生是我的\n拒绝画大饼\n行胜于言\n\n......\n\n亲爱的\n要时时刻刻都心里有我哦\n我感应得到的[愉快]\n\n我喜欢你这么说\n你不怕我的爱把你包围你无法呼吸[坏笑]\n我心里有你 我现在做规划做决定都会考虑你的[呲牙]\n谢谢亲爱的~\n\n亲爱的 你心里可要有我啊\n鱼身都是你,心里当然都是你啦\n不要给我画饼 行胜于言\n我画的那能叫饼吗?\n那叫梦想\n\n我错啦 我居然奢望男女平等\n领导就是领导\n\n##### 夏夏语录\n有空吗?\n来恋个爱[爱心]\n\n晚安安\n今天又是你得不到的女人[偷笑]\n\n再长真成多肉少女了[偷笑]\n最近为了催贝拉长肉,亲身示范的代价[破涕为笑]\n\n我家进门左边也是一颗桂花树,有二十多年了\n右边是一棵银杏树,也有十几年了\n银杏后面还有一棵栀子花,我小时候就在,估计我得叫姐[偷笑]\n\n代表月亮消灭你\n又想拐走我的心\n\n我没有怀疑你\n我怀疑的是人性\n即兴创作,欢迎推广\n\n虽然每天都想你,但想你和想你也是不一样的~\n\n我也想你,很想很想的那种\n亲爱的我舌头好了\n可以亲亲啦[亲亲][亲亲]\n\n(可爱的夏夏)\n今天食堂有毛血旺\n我怀疑有人要害我\n我好不容易消下去的痘痘\n估计要死灰复燃了\n因为不吃是不可能的。。。\n\n\n##### 我的温柔\n\n花草树木,人间星河\n无一是你 无一不是你\n\n\n我喜欢这秋天\n秋高气爽的感觉\n穿一袭长衫\n牵着你的手去公园里散步\n任凉风吹动你的头发\n听你说着可甜可咸的话\n你看着湖面,我看着你\n\n在你下班的路上,我们相汇在广渠门站\n然后下了车沿着河边的长廊\n牵着手,走啊走\n偶尔会遇到迎面而来的路人\n虽然你是擅长走路的\n但我仍想背着你走一段\n我背着你,你背着包\n享受着你趴在我背上的感觉\n你会在我耳边说上一些悄悄话\n当路人过来的时候埋头在我背上\n或朝着河里望上一眼\n一步一步往家的方向走去\n\n##### 夏夏插花作品展\n![作品一](https://raw.githubusercontent.com/student2028/blog_markdown/master/img/chahua1.jpeg)\n![作品二](https://raw.githubusercontent.com/student2028/blog_markdown/master/img/chahua2.jpeg)\n![作品三](https://raw.githubusercontent.com/student2028/blog_markdown/master/img/chahua3.jpeg)\n![语录一](https://raw.githubusercontent.com/student2028/blog_markdown/master/img/yulu1.png)\n","slug":"夏花对话录1","published":1,"updated":"2022-04-20T09:35:50.863Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cl27q3uv9000vn94ofoehhccc","content":"<h4 id=\"对话录\"><a href=\"#对话录\" class=\"headerlink\" title=\"对话录\"></a>对话录</h4><h5 id=\"睡前小语\"><a href=\"#睡前小语\" class=\"headerlink\" title=\"睡前小语\"></a>睡前小语</h5><p>亲爱的<br>有时候不说想你<br>好像就真的没那么想<br>说想了吧<br>就真的更想你了<br>(夏夏)</p>\n<p>亲爱的 这就是爱情<br>一个人就在那里<br>不想她 你也知道她在<br>想她的时候<br>便心生欢喜<br>(小结巴)</p>\n<p>有时候我觉得爱情很矫情<br>就好像小王子里面的玫瑰花<br>非得怎么怎么样</p>\n<p>有时候我不愿意助长她这种娇气<br>我希望她昂扬 向上<br>在岁月中沉淀<br>在风雨中成长<br>(夏夏)</p>\n<p>有时候我觉得爱情可珍贵<br>她让人迎难而上<br>给人希望和力量<br>你看<br>她多像尘世里摇曳着的<br>一朵高洁的花<br>(小结巴)</p>\n<h5 id=\"日常对白1\"><a href=\"#日常对白1\" class=\"headerlink\" title=\"日常对白1\"></a>日常对白1</h5><p>小仙女上线啦<br>你要不要亲我一口?(夏夏)</p>\n<p>好啊,我溃疡好啦<br>前天晚上为什么睡得那么晚?</p>\n<p>一口不够<br>多来几口<br>前天我们同事聚餐K歌啦</p>\n<p>亲吻中….</p>\n<p>周末也可以说一声去哪里嘛<br>结束了就是想给你说呀<br>谁知道你睡了<br>激动的心情都没人分享[大哭]</p>\n<p>为什么激动<br>唱嗨了就激动了呀<br>哪些歌让你唱嗨啦[坏笑]<br>你应该给我打电话</p>\n<p>突然好想你<br>美人吟<br>心动<br>我喜欢上你时的内心活动</p>\n<p>我自作多情以为你突然好想我[坏笑][偷笑]<br>发现是首歌</p>\n<h5 id=\"日常对白2\"><a href=\"#日常对白2\" class=\"headerlink\" title=\"日常对白2\"></a>日常对白2</h5><p>大脸猫猫,我想吃鱼<br>只吃鱼头<br>鱼身是你</p>\n<p>我可不管 你说好了<br>你说的我信啦<br>你的余生是我的</p>\n<p>the rest of the fish body is yours<br>我不懂 反正你的余生是我的<br>拒绝画大饼<br>行胜于言</p>\n<p>……</p>\n<p>亲爱的<br>要时时刻刻都心里有我哦<br>我感应得到的[愉快]</p>\n<p>我喜欢你这么说<br>你不怕我的爱把你包围你无法呼吸[坏笑]<br>我心里有你 我现在做规划做决定都会考虑你的[呲牙]<br>谢谢亲爱的~</p>\n<p>亲爱的 你心里可要有我啊<br>鱼身都是你,心里当然都是你啦<br>不要给我画饼 行胜于言<br>我画的那能叫饼吗?<br>那叫梦想</p>\n<p>我错啦 我居然奢望男女平等<br>领导就是领导</p>\n<h5 id=\"夏夏语录\"><a href=\"#夏夏语录\" class=\"headerlink\" title=\"夏夏语录\"></a>夏夏语录</h5><p>有空吗?<br>来恋个爱[爱心]</p>\n<p>晚安安<br>今天又是你得不到的女人[偷笑]</p>\n<p>再长真成多肉少女了[偷笑]<br>最近为了催贝拉长肉,亲身示范的代价[破涕为笑]</p>\n<p>我家进门左边也是一颗桂花树,有二十多年了<br>右边是一棵银杏树,也有十几年了<br>银杏后面还有一棵栀子花,我小时候就在,估计我得叫姐[偷笑]</p>\n<p>代表月亮消灭你<br>又想拐走我的心</p>\n<p>我没有怀疑你<br>我怀疑的是人性<br>即兴创作,欢迎推广</p>\n<p>虽然每天都想你,但想你和想你也是不一样的~</p>\n<p>我也想你,很想很想的那种<br>亲爱的我舌头好了<br>可以亲亲啦[亲亲][亲亲]</p>\n<p>(可爱的夏夏)<br>今天食堂有毛血旺<br>我怀疑有人要害我<br>我好不容易消下去的痘痘<br>估计要死灰复燃了<br>因为不吃是不可能的。。。</p>\n<h5 id=\"我的温柔\"><a href=\"#我的温柔\" class=\"headerlink\" title=\"我的温柔\"></a>我的温柔</h5><p>花草树木,人间星河<br>无一是你 无一不是你</p>\n<p>我喜欢这秋天<br>秋高气爽的感觉<br>穿一袭长衫<br>牵着你的手去公园里散步<br>任凉风吹动你的头发<br>听你说着可甜可咸的话<br>你看着湖面,我看着你</p>\n<p>在你下班的路上,我们相汇在广渠门站<br>然后下了车沿着河边的长廊<br>牵着手,走啊走<br>偶尔会遇到迎面而来的路人<br>虽然你是擅长走路的<br>但我仍想背着你走一段<br>我背着你,你背着包<br>享受着你趴在我背上的感觉<br>你会在我耳边说上一些悄悄话<br>当路人过来的时候埋头在我背上<br>或朝着河里望上一眼<br>一步一步往家的方向走去</p>\n<h5 id=\"夏夏插花作品展\"><a href=\"#夏夏插花作品展\" class=\"headerlink\" title=\"夏夏插花作品展\"></a>夏夏插花作品展</h5><p><img src=\"https://raw.githubusercontent.com/student2028/blog_markdown/master/img/chahua1.jpeg\" alt=\"作品一\"><br><img src=\"https://raw.githubusercontent.com/student2028/blog_markdown/master/img/chahua2.jpeg\" alt=\"作品二\"><br><img src=\"https://raw.githubusercontent.com/student2028/blog_markdown/master/img/chahua3.jpeg\" alt=\"作品三\"><br><img src=\"https://raw.githubusercontent.com/student2028/blog_markdown/master/img/yulu1.png\" alt=\"语录一\"></p>\n","site":{"data":{}},"excerpt":"","more":"<h4 id=\"对话录\"><a href=\"#对话录\" class=\"headerlink\" title=\"对话录\"></a>对话录</h4><h5 id=\"睡前小语\"><a href=\"#睡前小语\" class=\"headerlink\" title=\"睡前小语\"></a>睡前小语</h5><p>亲爱的<br>有时候不说想你<br>好像就真的没那么想<br>说想了吧<br>就真的更想你了<br>(夏夏)</p>\n<p>亲爱的 这就是爱情<br>一个人就在那里<br>不想她 你也知道她在<br>想她的时候<br>便心生欢喜<br>(小结巴)</p>\n<p>有时候我觉得爱情很矫情<br>就好像小王子里面的玫瑰花<br>非得怎么怎么样</p>\n<p>有时候我不愿意助长她这种娇气<br>我希望她昂扬 向上<br>在岁月中沉淀<br>在风雨中成长<br>(夏夏)</p>\n<p>有时候我觉得爱情可珍贵<br>她让人迎难而上<br>给人希望和力量<br>你看<br>她多像尘世里摇曳着的<br>一朵高洁的花<br>(小结巴)</p>\n<h5 id=\"日常对白1\"><a href=\"#日常对白1\" class=\"headerlink\" title=\"日常对白1\"></a>日常对白1</h5><p>小仙女上线啦<br>你要不要亲我一口?(夏夏)</p>\n<p>好啊,我溃疡好啦<br>前天晚上为什么睡得那么晚?</p>\n<p>一口不够<br>多来几口<br>前天我们同事聚餐K歌啦</p>\n<p>亲吻中….</p>\n<p>周末也可以说一声去哪里嘛<br>结束了就是想给你说呀<br>谁知道你睡了<br>激动的心情都没人分享[大哭]</p>\n<p>为什么激动<br>唱嗨了就激动了呀<br>哪些歌让你唱嗨啦[坏笑]<br>你应该给我打电话</p>\n<p>突然好想你<br>美人吟<br>心动<br>我喜欢上你时的内心活动</p>\n<p>我自作多情以为你突然好想我[坏笑][偷笑]<br>发现是首歌</p>\n<h5 id=\"日常对白2\"><a href=\"#日常对白2\" class=\"headerlink\" title=\"日常对白2\"></a>日常对白2</h5><p>大脸猫猫,我想吃鱼<br>只吃鱼头<br>鱼身是你</p>\n<p>我可不管 你说好了<br>你说的我信啦<br>你的余生是我的</p>\n<p>the rest of the fish body is yours<br>我不懂 反正你的余生是我的<br>拒绝画大饼<br>行胜于言</p>\n<p>……</p>\n<p>亲爱的<br>要时时刻刻都心里有我哦<br>我感应得到的[愉快]</p>\n<p>我喜欢你这么说<br>你不怕我的爱把你包围你无法呼吸[坏笑]<br>我心里有你 我现在做规划做决定都会考虑你的[呲牙]<br>谢谢亲爱的~</p>\n<p>亲爱的 你心里可要有我啊<br>鱼身都是你,心里当然都是你啦<br>不要给我画饼 行胜于言<br>我画的那能叫饼吗?<br>那叫梦想</p>\n<p>我错啦 我居然奢望男女平等<br>领导就是领导</p>\n<h5 id=\"夏夏语录\"><a href=\"#夏夏语录\" class=\"headerlink\" title=\"夏夏语录\"></a>夏夏语录</h5><p>有空吗?<br>来恋个爱[爱心]</p>\n<p>晚安安<br>今天又是你得不到的女人[偷笑]</p>\n<p>再长真成多肉少女了[偷笑]<br>最近为了催贝拉长肉,亲身示范的代价[破涕为笑]</p>\n<p>我家进门左边也是一颗桂花树,有二十多年了<br>右边是一棵银杏树,也有十几年了<br>银杏后面还有一棵栀子花,我小时候就在,估计我得叫姐[偷笑]</p>\n<p>代表月亮消灭你<br>又想拐走我的心</p>\n<p>我没有怀疑你<br>我怀疑的是人性<br>即兴创作,欢迎推广</p>\n<p>虽然每天都想你,但想你和想你也是不一样的~</p>\n<p>我也想你,很想很想的那种<br>亲爱的我舌头好了<br>可以亲亲啦[亲亲][亲亲]</p>\n<p>(可爱的夏夏)<br>今天食堂有毛血旺<br>我怀疑有人要害我<br>我好不容易消下去的痘痘<br>估计要死灰复燃了<br>因为不吃是不可能的。。。</p>\n<h5 id=\"我的温柔\"><a href=\"#我的温柔\" class=\"headerlink\" title=\"我的温柔\"></a>我的温柔</h5><p>花草树木,人间星河<br>无一是你 无一不是你</p>\n<p>我喜欢这秋天<br>秋高气爽的感觉<br>穿一袭长衫<br>牵着你的手去公园里散步<br>任凉风吹动你的头发<br>听你说着可甜可咸的话<br>你看着湖面,我看着你</p>\n<p>在你下班的路上,我们相汇在广渠门站<br>然后下了车沿着河边的长廊<br>牵着手,走啊走<br>偶尔会遇到迎面而来的路人<br>虽然你是擅长走路的<br>但我仍想背着你走一段<br>我背着你,你背着包<br>享受着你趴在我背上的感觉<br>你会在我耳边说上一些悄悄话<br>当路人过来的时候埋头在我背上<br>或朝着河里望上一眼<br>一步一步往家的方向走去</p>\n<h5 id=\"夏夏插花作品展\"><a href=\"#夏夏插花作品展\" class=\"headerlink\" title=\"夏夏插花作品展\"></a>夏夏插花作品展</h5><p><img src=\"https://raw.githubusercontent.com/student2028/blog_markdown/master/img/chahua1.jpeg\" alt=\"作品一\"><br><img src=\"https://raw.githubusercontent.com/student2028/blog_markdown/master/img/chahua2.jpeg\" alt=\"作品二\"><br><img src=\"https://raw.githubusercontent.com/student2028/blog_markdown/master/img/chahua3.jpeg\" alt=\"作品三\"><br><img src=\"https://raw.githubusercontent.com/student2028/blog_markdown/master/img/yulu1.png\" alt=\"语录一\"></p>\n"},{"title":"感冒123","date":"2021-09-22T02:32:26.000Z","_content":"#### 感冒123\n参考知乎等资料,汇集有关感冒的一些知识,有助于你感冒时药物的选择和生活准备。\n[远志](https://www.zhihu.com/question/19645175/answer/365062442)\n\n事实上,没有感冒快速恢复这么一说。\n因为不管你是吃药或者不吃药,感冒都差不多需要1个星期左右的时间才能好。\n为什么有时候我们感觉要不了几天,感冒的症状就减轻了很多?\n这是因为我们吃了药之后,能帮助我们缓解症状,降低身体的应激反应。\n本来难受的身体吃了药之后轻松不少,很多朋友就以为是好了,但其实病毒还存在我们体内。\n\n##### 为什么会感冒?\n1. 感冒是身体遭遇外界干扰时,所产生的一系列反应\n \n人之所以会发生感冒,有两个原因:\n一是外界的病毒或细菌来势汹汹,繁殖太快,通过面部的口、鼻进入到我们身体后,引发了感冒。\n另外一个原因是,吹了冷风,或者过于疲惫,熬夜等等原因,让身体免疫力下降,打破了身体运转的平衡。\n原本身体能压制的病毒,得到迅速繁殖,也就引发感冒。\n\n2. 中医所说的风寒、风热是怎么引起的呢?\n\n中医只是更细致的把你的症状进行了一次分类,其作用是让大家能根据自己的症状,对症下药。\n人感冒后,流清鼻涕,特别怕冷,咳白痰等症状很像是人吹了冷风,受寒后所产生的反应。\n所以这个时候要为自己疏风散寒,喝一些姜汤等辛温的食物驱散寒气;\n而流黄鼻涕,发热重,咳黄痰像是受了热所产生的反应,可以适当喝一些薄荷水下火的食物,把热给去掉。\n\n##### 如何快速恢复?\n根据实验统计,95%的人感冒是病毒感冒,而非细菌感冒。\n根据现有的医疗水平,不论是中医还是西医,暂时没有找到有效的药物能够直接杀死病毒。\n药物的作用是帮助我们缓解症状,降低身体的应激反应。\n1. 吃药并不能治愈感冒,只能帮助我们缓解症状\n2. 感冒之所以能好,要靠我们身体里的防御装置——免疫力\n3. 感冒虽然无法通过吃药治愈,但还有两个方法,可以帮助我们加快恢复\n调理脾胃和多喝热水。\n**调理脾胃**:吃优质高蛋白,易消化的,如鱼肉,鸡蛋。吃菜和水果。为免疫系统提供能量来源。\n**多喝热水** \n我们身体免疫力在和病毒作战的时候,其实会排出很多垃圾,也就是身体新陈代谢出来的废物。\n感冒时不停流鼻涕,就是身体排泄废物的一个方式。而多喝热水,就是帮助身体加速排泄的速度,\n让这些“废物”能尽快通过小便的方式,排出体外。\n\n##### 提高免疫力\n想要快速恢复健康,最根本的方法还是在于提高身体免疫力。\n提高免疫力有两个方面,增强身体的稳定性和灵活性。\n想要给身体稳固底子,最好的方法是保持规律的运动。一个星期不跑步,突然抽一天时间跑5km,\n这样猛然来一下的运动方式,不算规律运动,对身体反而不太好。\n**所谓规律运动,是说一个星期至少运动4次,每次运动时间不少于半个小时。保持至少半年时间,\n让你身体内的肠道菌群,微生物能整个慢慢适应这个节奏,进行调整,这样才算真正为身体打好底子.**\n\n其次是增强身体的灵活性,也就是抗波动能力。身体适应规律的运动节奏之后,适当提高强度,刺激一下。\n比如身体习惯了每天跑2km,不感觉到累了,就可以抽一天时间跑个4km,刺激一下我们的身体状态。\n之后再慢慢调整为每天跑2.5km或者3km,身体习惯之后,再突然提高强度。\n如此循环,就能稳健的增强身体的免疫力。\n\n根据数据表明,5~10天是正常人应对感冒的周期。\n如果你身体比较好,免疫力较强,感冒痊愈的时间可能只需要4、5天;\n但如果身体免疫力比较差,10天乃至一直反复不好,都是正常的。\n如果你感冒超过7天的时间,并且症状并未减轻,建议最好前往医院,让医生进行检查。\n有可能你是细菌感冒,需要服用适量的抗生素进行治疗。\n\n##### 对症下药\n风寒: **正柴胡** 藿香正气 仁和药业的感冒软胶囊 感冒疏风片 \n风热: **莲花清瘟胶囊** 复方感冒灵 板蓝根\n感冒喉咙痛很正常,扁桃体发炎正常,要吃消炎药,**蒲地蓝**\n高烧(38度以上)吃药就好了,建议吃**布洛芬**\n\n1. 全身肌肉酸痛、牙龈肿痛、鼻涕多,可选用感冒通胶囊。\n2. 发热m伴有肌肉酸痛,鼻塞、咳嗽,可以选用新康泰克、日夜百服宁、泰诺及白加黑片等。\n3. 没有发热,鼻塞等症状,而是以流涕咳嗽为主,那么就可以选择惠菲宁。\n\n##### 注意事项\n**感冒期间,身体要全力对付病毒,食欲下降,要注意饮食,减少肠胃负担。**\n不要吃甜食,食物要清淡易消化,但是要营养丰富。\n不要吃过甜的水果,如葡萄,西瓜,芒果等富含糖类的水果。\n不要吃芹菜,茼蒿,韭菜等富含粗纤维的食物,增加肠胃负担。\n不要吃浓茶浓咖啡,有可能引发胃部不适。\n得了感冒第一时间区分是流感还是普通感冒\n感冒后体温不超过39度。可以断定是普通感冒。\n","source":"_posts/感冒123.md","raw":"---\ntitle: 感冒123\ndate: 2021-09-22 10:32:26\ncategories: \"生活\" #分类\ntags: #标签\n - 生活\n---\n#### 感冒123\n参考知乎等资料,汇集有关感冒的一些知识,有助于你感冒时药物的选择和生活准备。\n[远志](https://www.zhihu.com/question/19645175/answer/365062442)\n\n事实上,没有感冒快速恢复这么一说。\n因为不管你是吃药或者不吃药,感冒都差不多需要1个星期左右的时间才能好。\n为什么有时候我们感觉要不了几天,感冒的症状就减轻了很多?\n这是因为我们吃了药之后,能帮助我们缓解症状,降低身体的应激反应。\n本来难受的身体吃了药之后轻松不少,很多朋友就以为是好了,但其实病毒还存在我们体内。\n\n##### 为什么会感冒?\n1. 感冒是身体遭遇外界干扰时,所产生的一系列反应\n \n人之所以会发生感冒,有两个原因:\n一是外界的病毒或细菌来势汹汹,繁殖太快,通过面部的口、鼻进入到我们身体后,引发了感冒。\n另外一个原因是,吹了冷风,或者过于疲惫,熬夜等等原因,让身体免疫力下降,打破了身体运转的平衡。\n原本身体能压制的病毒,得到迅速繁殖,也就引发感冒。\n\n2. 中医所说的风寒、风热是怎么引起的呢?\n\n中医只是更细致的把你的症状进行了一次分类,其作用是让大家能根据自己的症状,对症下药。\n人感冒后,流清鼻涕,特别怕冷,咳白痰等症状很像是人吹了冷风,受寒后所产生的反应。\n所以这个时候要为自己疏风散寒,喝一些姜汤等辛温的食物驱散寒气;\n而流黄鼻涕,发热重,咳黄痰像是受了热所产生的反应,可以适当喝一些薄荷水下火的食物,把热给去掉。\n\n##### 如何快速恢复?\n根据实验统计,95%的人感冒是病毒感冒,而非细菌感冒。\n根据现有的医疗水平,不论是中医还是西医,暂时没有找到有效的药物能够直接杀死病毒。\n药物的作用是帮助我们缓解症状,降低身体的应激反应。\n1. 吃药并不能治愈感冒,只能帮助我们缓解症状\n2. 感冒之所以能好,要靠我们身体里的防御装置——免疫力\n3. 感冒虽然无法通过吃药治愈,但还有两个方法,可以帮助我们加快恢复\n调理脾胃和多喝热水。\n**调理脾胃**:吃优质高蛋白,易消化的,如鱼肉,鸡蛋。吃菜和水果。为免疫系统提供能量来源。\n**多喝热水** \n我们身体免疫力在和病毒作战的时候,其实会排出很多垃圾,也就是身体新陈代谢出来的废物。\n感冒时不停流鼻涕,就是身体排泄废物的一个方式。而多喝热水,就是帮助身体加速排泄的速度,\n让这些“废物”能尽快通过小便的方式,排出体外。\n\n##### 提高免疫力\n想要快速恢复健康,最根本的方法还是在于提高身体免疫力。\n提高免疫力有两个方面,增强身体的稳定性和灵活性。\n想要给身体稳固底子,最好的方法是保持规律的运动。一个星期不跑步,突然抽一天时间跑5km,\n这样猛然来一下的运动方式,不算规律运动,对身体反而不太好。\n**所谓规律运动,是说一个星期至少运动4次,每次运动时间不少于半个小时。保持至少半年时间,\n让你身体内的肠道菌群,微生物能整个慢慢适应这个节奏,进行调整,这样才算真正为身体打好底子.**\n\n其次是增强身体的灵活性,也就是抗波动能力。身体适应规律的运动节奏之后,适当提高强度,刺激一下。\n比如身体习惯了每天跑2km,不感觉到累了,就可以抽一天时间跑个4km,刺激一下我们的身体状态。\n之后再慢慢调整为每天跑2.5km或者3km,身体习惯之后,再突然提高强度。\n如此循环,就能稳健的增强身体的免疫力。\n\n根据数据表明,5~10天是正常人应对感冒的周期。\n如果你身体比较好,免疫力较强,感冒痊愈的时间可能只需要4、5天;\n但如果身体免疫力比较差,10天乃至一直反复不好,都是正常的。\n如果你感冒超过7天的时间,并且症状并未减轻,建议最好前往医院,让医生进行检查。\n有可能你是细菌感冒,需要服用适量的抗生素进行治疗。\n\n##### 对症下药\n风寒: **正柴胡** 藿香正气 仁和药业的感冒软胶囊 感冒疏风片 \n风热: **莲花清瘟胶囊** 复方感冒灵 板蓝根\n感冒喉咙痛很正常,扁桃体发炎正常,要吃消炎药,**蒲地蓝**\n高烧(38度以上)吃药就好了,建议吃**布洛芬**\n\n1. 全身肌肉酸痛、牙龈肿痛、鼻涕多,可选用感冒通胶囊。\n2. 发热m伴有肌肉酸痛,鼻塞、咳嗽,可以选用新康泰克、日夜百服宁、泰诺及白加黑片等。\n3. 没有发热,鼻塞等症状,而是以流涕咳嗽为主,那么就可以选择惠菲宁。\n\n##### 注意事项\n**感冒期间,身体要全力对付病毒,食欲下降,要注意饮食,减少肠胃负担。**\n不要吃甜食,食物要清淡易消化,但是要营养丰富。\n不要吃过甜的水果,如葡萄,西瓜,芒果等富含糖类的水果。\n不要吃芹菜,茼蒿,韭菜等富含粗纤维的食物,增加肠胃负担。\n不要吃浓茶浓咖啡,有可能引发胃部不适。\n得了感冒第一时间区分是流感还是普通感冒\n感冒后体温不超过39度。可以断定是普通感冒。\n","slug":"感冒123","published":1,"updated":"2022-04-20T09:35:50.864Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cl27q3uv90010n94o9gvjftdr","content":"<h4 id=\"感冒123\"><a href=\"#感冒123\" class=\"headerlink\" title=\"感冒123\"></a>感冒123</h4><p>参考知乎等资料,汇集有关感冒的一些知识,有助于你感冒时药物的选择和生活准备。<br><a href=\"https://www.zhihu.com/question/19645175/answer/365062442\">远志</a></p>\n<p>事实上,没有感冒快速恢复这么一说。<br>因为不管你是吃药或者不吃药,感冒都差不多需要1个星期左右的时间才能好。<br>为什么有时候我们感觉要不了几天,感冒的症状就减轻了很多?<br>这是因为我们吃了药之后,能帮助我们缓解症状,降低身体的应激反应。<br>本来难受的身体吃了药之后轻松不少,很多朋友就以为是好了,但其实病毒还存在我们体内。</p>\n<h5 id=\"为什么会感冒?\"><a href=\"#为什么会感冒?\" class=\"headerlink\" title=\"为什么会感冒?\"></a>为什么会感冒?</h5><ol>\n<li>感冒是身体遭遇外界干扰时,所产生的一系列反应</li>\n</ol>\n<p>人之所以会发生感冒,有两个原因:<br>一是外界的病毒或细菌来势汹汹,繁殖太快,通过面部的口、鼻进入到我们身体后,引发了感冒。<br>另外一个原因是,吹了冷风,或者过于疲惫,熬夜等等原因,让身体免疫力下降,打破了身体运转的平衡。<br>原本身体能压制的病毒,得到迅速繁殖,也就引发感冒。</p>\n<ol start=\"2\">\n<li>中医所说的风寒、风热是怎么引起的呢?</li>\n</ol>\n<p>中医只是更细致的把你的症状进行了一次分类,其作用是让大家能根据自己的症状,对症下药。<br>人感冒后,流清鼻涕,特别怕冷,咳白痰等症状很像是人吹了冷风,受寒后所产生的反应。<br>所以这个时候要为自己疏风散寒,喝一些姜汤等辛温的食物驱散寒气;<br>而流黄鼻涕,发热重,咳黄痰像是受了热所产生的反应,可以适当喝一些薄荷水下火的食物,把热给去掉。</p>\n<h5 id=\"如何快速恢复?\"><a href=\"#如何快速恢复?\" class=\"headerlink\" title=\"如何快速恢复?\"></a>如何快速恢复?</h5><p>根据实验统计,95%的人感冒是病毒感冒,而非细菌感冒。<br>根据现有的医疗水平,不论是中医还是西医,暂时没有找到有效的药物能够直接杀死病毒。<br>药物的作用是帮助我们缓解症状,降低身体的应激反应。</p>\n<ol>\n<li>吃药并不能治愈感冒,只能帮助我们缓解症状</li>\n<li>感冒之所以能好,要靠我们身体里的防御装置——免疫力</li>\n<li>感冒虽然无法通过吃药治愈,但还有两个方法,可以帮助我们加快恢复<br>调理脾胃和多喝热水。</li>\n</ol>\n<p><strong>调理脾胃</strong>:吃优质高蛋白,易消化的,如鱼肉,鸡蛋。吃菜和水果。为免疫系统提供能量来源。<br><strong>多喝热水</strong><br>我们身体免疫力在和病毒作战的时候,其实会排出很多垃圾,也就是身体新陈代谢出来的废物。<br>感冒时不停流鼻涕,就是身体排泄废物的一个方式。而多喝热水,就是帮助身体加速排泄的速度,<br>让这些“废物”能尽快通过小便的方式,排出体外。</p>\n<h5 id=\"提高免疫力\"><a href=\"#提高免疫力\" class=\"headerlink\" title=\"提高免疫力\"></a>提高免疫力</h5><p>想要快速恢复健康,最根本的方法还是在于提高身体免疫力。<br>提高免疫力有两个方面,增强身体的稳定性和灵活性。<br>想要给身体稳固底子,最好的方法是保持规律的运动。一个星期不跑步,突然抽一天时间跑5km,<br>这样猛然来一下的运动方式,不算规律运动,对身体反而不太好。<br><strong>所谓规律运动,是说一个星期至少运动4次,每次运动时间不少于半个小时。保持至少半年时间,<br>让你身体内的肠道菌群,微生物能整个慢慢适应这个节奏,进行调整,这样才算真正为身体打好底子.</strong></p>\n<p>其次是增强身体的灵活性,也就是抗波动能力。身体适应规律的运动节奏之后,适当提高强度,刺激一下。<br>比如身体习惯了每天跑2km,不感觉到累了,就可以抽一天时间跑个4km,刺激一下我们的身体状态。<br>之后再慢慢调整为每天跑2.5km或者3km,身体习惯之后,再突然提高强度。<br>如此循环,就能稳健的增强身体的免疫力。</p>\n<p>根据数据表明,5~10天是正常人应对感冒的周期。<br>如果你身体比较好,免疫力较强,感冒痊愈的时间可能只需要4、5天;<br>但如果身体免疫力比较差,10天乃至一直反复不好,都是正常的。<br>如果你感冒超过7天的时间,并且症状并未减轻,建议最好前往医院,让医生进行检查。<br>有可能你是细菌感冒,需要服用适量的抗生素进行治疗。</p>\n<h5 id=\"对症下药\"><a href=\"#对症下药\" class=\"headerlink\" title=\"对症下药\"></a>对症下药</h5><p>风寒: <strong>正柴胡</strong> 藿香正气 仁和药业的感冒软胶囊 感冒疏风片<br>风热: <strong>莲花清瘟胶囊</strong> 复方感冒灵 板蓝根<br>感冒喉咙痛很正常,扁桃体发炎正常,要吃消炎药,<strong>蒲地蓝</strong><br>高烧(38度以上)吃药就好了,建议吃<strong>布洛芬</strong></p>\n<ol>\n<li>全身肌肉酸痛、牙龈肿痛、鼻涕多,可选用感冒通胶囊。</li>\n<li>发热m伴有肌肉酸痛,鼻塞、咳嗽,可以选用新康泰克、日夜百服宁、泰诺及白加黑片等。</li>\n<li>没有发热,鼻塞等症状,而是以流涕咳嗽为主,那么就可以选择惠菲宁。</li>\n</ol>\n<h5 id=\"注意事项\"><a href=\"#注意事项\" class=\"headerlink\" title=\"注意事项\"></a>注意事项</h5><p><strong>感冒期间,身体要全力对付病毒,食欲下降,要注意饮食,减少肠胃负担。</strong><br>不要吃甜食,食物要清淡易消化,但是要营养丰富。<br>不要吃过甜的水果,如葡萄,西瓜,芒果等富含糖类的水果。<br>不要吃芹菜,茼蒿,韭菜等富含粗纤维的食物,增加肠胃负担。<br>不要吃浓茶浓咖啡,有可能引发胃部不适。<br>得了感冒第一时间区分是流感还是普通感冒<br>感冒后体温不超过39度。可以断定是普通感冒。</p>\n","site":{"data":{}},"excerpt":"","more":"<h4 id=\"感冒123\"><a href=\"#感冒123\" class=\"headerlink\" title=\"感冒123\"></a>感冒123</h4><p>参考知乎等资料,汇集有关感冒的一些知识,有助于你感冒时药物的选择和生活准备。<br><a href=\"https://www.zhihu.com/question/19645175/answer/365062442\">远志</a></p>\n<p>事实上,没有感冒快速恢复这么一说。<br>因为不管你是吃药或者不吃药,感冒都差不多需要1个星期左右的时间才能好。<br>为什么有时候我们感觉要不了几天,感冒的症状就减轻了很多?<br>这是因为我们吃了药之后,能帮助我们缓解症状,降低身体的应激反应。<br>本来难受的身体吃了药之后轻松不少,很多朋友就以为是好了,但其实病毒还存在我们体内。</p>\n<h5 id=\"为什么会感冒?\"><a href=\"#为什么会感冒?\" class=\"headerlink\" title=\"为什么会感冒?\"></a>为什么会感冒?</h5><ol>\n<li>感冒是身体遭遇外界干扰时,所产生的一系列反应</li>\n</ol>\n<p>人之所以会发生感冒,有两个原因:<br>一是外界的病毒或细菌来势汹汹,繁殖太快,通过面部的口、鼻进入到我们身体后,引发了感冒。<br>另外一个原因是,吹了冷风,或者过于疲惫,熬夜等等原因,让身体免疫力下降,打破了身体运转的平衡。<br>原本身体能压制的病毒,得到迅速繁殖,也就引发感冒。</p>\n<ol start=\"2\">\n<li>中医所说的风寒、风热是怎么引起的呢?</li>\n</ol>\n<p>中医只是更细致的把你的症状进行了一次分类,其作用是让大家能根据自己的症状,对症下药。<br>人感冒后,流清鼻涕,特别怕冷,咳白痰等症状很像是人吹了冷风,受寒后所产生的反应。<br>所以这个时候要为自己疏风散寒,喝一些姜汤等辛温的食物驱散寒气;<br>而流黄鼻涕,发热重,咳黄痰像是受了热所产生的反应,可以适当喝一些薄荷水下火的食物,把热给去掉。</p>\n<h5 id=\"如何快速恢复?\"><a href=\"#如何快速恢复?\" class=\"headerlink\" title=\"如何快速恢复?\"></a>如何快速恢复?</h5><p>根据实验统计,95%的人感冒是病毒感冒,而非细菌感冒。<br>根据现有的医疗水平,不论是中医还是西医,暂时没有找到有效的药物能够直接杀死病毒。<br>药物的作用是帮助我们缓解症状,降低身体的应激反应。</p>\n<ol>\n<li>吃药并不能治愈感冒,只能帮助我们缓解症状</li>\n<li>感冒之所以能好,要靠我们身体里的防御装置——免疫力</li>\n<li>感冒虽然无法通过吃药治愈,但还有两个方法,可以帮助我们加快恢复<br>调理脾胃和多喝热水。</li>\n</ol>\n<p><strong>调理脾胃</strong>:吃优质高蛋白,易消化的,如鱼肉,鸡蛋。吃菜和水果。为免疫系统提供能量来源。<br><strong>多喝热水</strong><br>我们身体免疫力在和病毒作战的时候,其实会排出很多垃圾,也就是身体新陈代谢出来的废物。<br>感冒时不停流鼻涕,就是身体排泄废物的一个方式。而多喝热水,就是帮助身体加速排泄的速度,<br>让这些“废物”能尽快通过小便的方式,排出体外。</p>\n<h5 id=\"提高免疫力\"><a href=\"#提高免疫力\" class=\"headerlink\" title=\"提高免疫力\"></a>提高免疫力</h5><p>想要快速恢复健康,最根本的方法还是在于提高身体免疫力。<br>提高免疫力有两个方面,增强身体的稳定性和灵活性。<br>想要给身体稳固底子,最好的方法是保持规律的运动。一个星期不跑步,突然抽一天时间跑5km,<br>这样猛然来一下的运动方式,不算规律运动,对身体反而不太好。<br><strong>所谓规律运动,是说一个星期至少运动4次,每次运动时间不少于半个小时。保持至少半年时间,<br>让你身体内的肠道菌群,微生物能整个慢慢适应这个节奏,进行调整,这样才算真正为身体打好底子.</strong></p>\n<p>其次是增强身体的灵活性,也就是抗波动能力。身体适应规律的运动节奏之后,适当提高强度,刺激一下。<br>比如身体习惯了每天跑2km,不感觉到累了,就可以抽一天时间跑个4km,刺激一下我们的身体状态。<br>之后再慢慢调整为每天跑2.5km或者3km,身体习惯之后,再突然提高强度。<br>如此循环,就能稳健的增强身体的免疫力。</p>\n<p>根据数据表明,5~10天是正常人应对感冒的周期。<br>如果你身体比较好,免疫力较强,感冒痊愈的时间可能只需要4、5天;<br>但如果身体免疫力比较差,10天乃至一直反复不好,都是正常的。<br>如果你感冒超过7天的时间,并且症状并未减轻,建议最好前往医院,让医生进行检查。<br>有可能你是细菌感冒,需要服用适量的抗生素进行治疗。</p>\n<h5 id=\"对症下药\"><a href=\"#对症下药\" class=\"headerlink\" title=\"对症下药\"></a>对症下药</h5><p>风寒: <strong>正柴胡</strong> 藿香正气 仁和药业的感冒软胶囊 感冒疏风片<br>风热: <strong>莲花清瘟胶囊</strong> 复方感冒灵 板蓝根<br>感冒喉咙痛很正常,扁桃体发炎正常,要吃消炎药,<strong>蒲地蓝</strong><br>高烧(38度以上)吃药就好了,建议吃<strong>布洛芬</strong></p>\n<ol>\n<li>全身肌肉酸痛、牙龈肿痛、鼻涕多,可选用感冒通胶囊。</li>\n<li>发热m伴有肌肉酸痛,鼻塞、咳嗽,可以选用新康泰克、日夜百服宁、泰诺及白加黑片等。</li>\n<li>没有发热,鼻塞等症状,而是以流涕咳嗽为主,那么就可以选择惠菲宁。</li>\n</ol>\n<h5 id=\"注意事项\"><a href=\"#注意事项\" class=\"headerlink\" title=\"注意事项\"></a>注意事项</h5><p><strong>感冒期间,身体要全力对付病毒,食欲下降,要注意饮食,减少肠胃负担。</strong><br>不要吃甜食,食物要清淡易消化,但是要营养丰富。<br>不要吃过甜的水果,如葡萄,西瓜,芒果等富含糖类的水果。<br>不要吃芹菜,茼蒿,韭菜等富含粗纤维的食物,增加肠胃负担。<br>不要吃浓茶浓咖啡,有可能引发胃部不适。<br>得了感冒第一时间区分是流感还是普通感冒<br>感冒后体温不超过39度。可以断定是普通感冒。</p>\n"},{"title":"大数加法","date":"2021-07-19T01:22:05.000Z","comments":1,"toc":true,"_content":"JAVA中有BigInteger类,可以用于表示极大的整数,如果我们不使用它,使用字符串来表示两个大数的加法,\n怎么实现呢?\n可以使用char数组来表示一个大整数,让char[i]-'0'就可以得到它的整数值。\n然后加法是从最低位开始计算的,所以要从数组的尾部开始计算。\n因为加法是有进位的,一定要考虑好进位。\nsum = a + b ,则进位carray = sum > 10 ? 1 : 0 或者 carray = sum / 10;\nsum = sum % 10;\n使用一个StringBuilder来把算出的每一位结果值存储起来,最后需要反转一下就是我们要的结果。\n关于这一点我们也可以使用栈或者不断new一个新的字符串来实现,都是可以的。\n理解了这一些,写代码就非常容易了。\n``` java\n\nimport java.math.BigInteger;\n\npublic class Test {\n\n\n public static void main(String[] args) {\n\n\n String add1 = \"123123421342134213\";\n String add2 = \"867987614413246574646\";\n\n String sum = new BigInteger(add1).add(new BigInteger(add2)).toString();\n System.out.println(sum);\n System.out.println(bigNumAdd(add1, add2));\n System.out.println(bigNumAdd(add1, add2).equalsIgnoreCase(sum));\n\n\n }\n\n static String bigNumAdd(String add1, String add2) {\n char[] num1 = add1.toCharArray();\n char[] num2 = add2.toCharArray();\n\n int N1 = num1.length - 1;\n int N2 = num2.length - 1;\n\n int sum = 0;\n int carry = 0;\n StringBuilder ret = new StringBuilder();\n\n while(N1 > -1 || N2 > -1) {\n sum = (N1 > -1 ? num1[N1]-'0' : 0) + (N2 > -1 ? num2[N2] - '0' : 0) + carry;\n carry = sum / 10;\n sum = sum % 10;\n ret.append(sum);\n N1 --;\n N2 --;\n }\n \n return ret.reverse().toString();\n }\n}\n```\n\n\n","source":"_posts/大数加法.md","raw":"---\ntitle: 大数加法\ndate: 2021-07-19 09:22:05\ncomments: true #是否可评论\ntoc: true #是否显示文章目录\ncategories: \"算法\" #分类\ntags: #标签\n - JAVA\n - 算法\n---\nJAVA中有BigInteger类,可以用于表示极大的整数,如果我们不使用它,使用字符串来表示两个大数的加法,\n怎么实现呢?\n可以使用char数组来表示一个大整数,让char[i]-'0'就可以得到它的整数值。\n然后加法是从最低位开始计算的,所以要从数组的尾部开始计算。\n因为加法是有进位的,一定要考虑好进位。\nsum = a + b ,则进位carray = sum > 10 ? 1 : 0 或者 carray = sum / 10;\nsum = sum % 10;\n使用一个StringBuilder来把算出的每一位结果值存储起来,最后需要反转一下就是我们要的结果。\n关于这一点我们也可以使用栈或者不断new一个新的字符串来实现,都是可以的。\n理解了这一些,写代码就非常容易了。\n``` java\n\nimport java.math.BigInteger;\n\npublic class Test {\n\n\n public static void main(String[] args) {\n\n\n String add1 = \"123123421342134213\";\n String add2 = \"867987614413246574646\";\n\n String sum = new BigInteger(add1).add(new BigInteger(add2)).toString();\n System.out.println(sum);\n System.out.println(bigNumAdd(add1, add2));\n System.out.println(bigNumAdd(add1, add2).equalsIgnoreCase(sum));\n\n\n }\n\n static String bigNumAdd(String add1, String add2) {\n char[] num1 = add1.toCharArray();\n char[] num2 = add2.toCharArray();\n\n int N1 = num1.length - 1;\n int N2 = num2.length - 1;\n\n int sum = 0;\n int carry = 0;\n StringBuilder ret = new StringBuilder();\n\n while(N1 > -1 || N2 > -1) {\n sum = (N1 > -1 ? num1[N1]-'0' : 0) + (N2 > -1 ? num2[N2] - '0' : 0) + carry;\n carry = sum / 10;\n sum = sum % 10;\n ret.append(sum);\n N1 --;\n N2 --;\n }\n \n return ret.reverse().toString();\n }\n}\n```\n\n\n","slug":"大数加法","published":1,"updated":"2022-04-20T09:35:50.864Z","layout":"post","photos":[],"link":"","_id":"cl27q3uva0012n94ogt7mggyr","content":"<p>JAVA中有BigInteger类,可以用于表示极大的整数,如果我们不使用它,使用字符串来表示两个大数的加法,<br>怎么实现呢?<br>可以使用char数组来表示一个大整数,让char[i]-‘0’就可以得到它的整数值。<br>然后加法是从最低位开始计算的,所以要从数组的尾部开始计算。<br>因为加法是有进位的,一定要考虑好进位。<br>sum = a + b ,则进位carray = sum > 10 ? 1 : 0 或者 carray = sum / 10;<br>sum = sum % 10;<br>使用一个StringBuilder来把算出的每一位结果值存储起来,最后需要反转一下就是我们要的结果。<br>关于这一点我们也可以使用栈或者不断new一个新的字符串来实现,都是可以的。<br>理解了这一些,写代码就非常容易了。</p>\n<figure class=\"highlight java\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br><span class=\"line\">11</span><br><span class=\"line\">12</span><br><span class=\"line\">13</span><br><span class=\"line\">14</span><br><span class=\"line\">15</span><br><span class=\"line\">16</span><br><span class=\"line\">17</span><br><span class=\"line\">18</span><br><span class=\"line\">19</span><br><span class=\"line\">20</span><br><span class=\"line\">21</span><br><span class=\"line\">22</span><br><span class=\"line\">23</span><br><span class=\"line\">24</span><br><span class=\"line\">25</span><br><span class=\"line\">26</span><br><span class=\"line\">27</span><br><span class=\"line\">28</span><br><span class=\"line\">29</span><br><span class=\"line\">30</span><br><span class=\"line\">31</span><br><span class=\"line\">32</span><br><span class=\"line\">33</span><br><span class=\"line\">34</span><br><span class=\"line\">35</span><br><span class=\"line\">36</span><br><span class=\"line\">37</span><br><span class=\"line\">38</span><br><span class=\"line\">39</span><br><span class=\"line\">40</span><br><span class=\"line\">41</span><br><span class=\"line\">42</span><br><span class=\"line\">43</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"></span><br><span class=\"line\"><span class=\"keyword\">import</span> java.math.BigInteger;</span><br><span class=\"line\"></span><br><span class=\"line\"><span class=\"keyword\">public</span> <span class=\"keyword\">class</span> <span class=\"title class_\">Test</span> {</span><br><span class=\"line\"></span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"keyword\">public</span> <span class=\"keyword\">static</span> <span class=\"keyword\">void</span> <span class=\"title function_\">main</span><span class=\"params\">(String[] args)</span> {</span><br><span class=\"line\"></span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"type\">String</span> <span class=\"variable\">add1</span> <span class=\"operator\">=</span> <span class=\"string\">"123123421342134213"</span>;</span><br><span class=\"line\"> <span class=\"type\">String</span> <span class=\"variable\">add2</span> <span class=\"operator\">=</span> <span class=\"string\">"867987614413246574646"</span>;</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"type\">String</span> <span class=\"variable\">sum</span> <span class=\"operator\">=</span> <span class=\"keyword\">new</span> <span class=\"title class_\">BigInteger</span>(add1).add(<span class=\"keyword\">new</span> <span class=\"title class_\">BigInteger</span>(add2)).toString();</span><br><span class=\"line\"> System.out.println(sum);</span><br><span class=\"line\"> System.out.println(bigNumAdd(add1, add2));</span><br><span class=\"line\"> System.out.println(bigNumAdd(add1, add2).equalsIgnoreCase(sum));</span><br><span class=\"line\"></span><br><span class=\"line\"></span><br><span class=\"line\"> }</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"keyword\">static</span> String <span class=\"title function_\">bigNumAdd</span><span class=\"params\">(String add1, String add2)</span> {</span><br><span class=\"line\"> <span class=\"type\">char</span>[] num1 = add1.toCharArray();</span><br><span class=\"line\"> <span class=\"type\">char</span>[] num2 = add2.toCharArray();</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">N1</span> <span class=\"operator\">=</span> num1.length - <span class=\"number\">1</span>;</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">N2</span> <span class=\"operator\">=</span> num2.length - <span class=\"number\">1</span>;</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">sum</span> <span class=\"operator\">=</span> <span class=\"number\">0</span>;</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">carry</span> <span class=\"operator\">=</span> <span class=\"number\">0</span>;</span><br><span class=\"line\"> <span class=\"type\">StringBuilder</span> <span class=\"variable\">ret</span> <span class=\"operator\">=</span> <span class=\"keyword\">new</span> <span class=\"title class_\">StringBuilder</span>();</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"keyword\">while</span>(N1 > -<span class=\"number\">1</span> || N2 > -<span class=\"number\">1</span>) {</span><br><span class=\"line\"> sum = (N1 > -<span class=\"number\">1</span> ? num1[N1]-<span class=\"string\">'0'</span> : <span class=\"number\">0</span>) + (N2 > -<span class=\"number\">1</span> ? num2[N2] - <span class=\"string\">'0'</span> : <span class=\"number\">0</span>) + carry;</span><br><span class=\"line\"> carry = sum / <span class=\"number\">10</span>;</span><br><span class=\"line\"> sum = sum % <span class=\"number\">10</span>;</span><br><span class=\"line\"> ret.append(sum);</span><br><span class=\"line\"> N1 --;</span><br><span class=\"line\"> N2 --;</span><br><span class=\"line\"> }</span><br><span class=\"line\"> </span><br><span class=\"line\"> <span class=\"keyword\">return</span> ret.reverse().toString();</span><br><span class=\"line\"> }</span><br><span class=\"line\">}</span><br></pre></td></tr></table></figure>\n\n\n","site":{"data":{}},"excerpt":"","more":"<p>JAVA中有BigInteger类,可以用于表示极大的整数,如果我们不使用它,使用字符串来表示两个大数的加法,<br>怎么实现呢?<br>可以使用char数组来表示一个大整数,让char[i]-‘0’就可以得到它的整数值。<br>然后加法是从最低位开始计算的,所以要从数组的尾部开始计算。<br>因为加法是有进位的,一定要考虑好进位。<br>sum = a + b ,则进位carray = sum > 10 ? 1 : 0 或者 carray = sum / 10;<br>sum = sum % 10;<br>使用一个StringBuilder来把算出的每一位结果值存储起来,最后需要反转一下就是我们要的结果。<br>关于这一点我们也可以使用栈或者不断new一个新的字符串来实现,都是可以的。<br>理解了这一些,写代码就非常容易了。</p>\n<figure class=\"highlight java\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br><span class=\"line\">11</span><br><span class=\"line\">12</span><br><span class=\"line\">13</span><br><span class=\"line\">14</span><br><span class=\"line\">15</span><br><span class=\"line\">16</span><br><span class=\"line\">17</span><br><span class=\"line\">18</span><br><span class=\"line\">19</span><br><span class=\"line\">20</span><br><span class=\"line\">21</span><br><span class=\"line\">22</span><br><span class=\"line\">23</span><br><span class=\"line\">24</span><br><span class=\"line\">25</span><br><span class=\"line\">26</span><br><span class=\"line\">27</span><br><span class=\"line\">28</span><br><span class=\"line\">29</span><br><span class=\"line\">30</span><br><span class=\"line\">31</span><br><span class=\"line\">32</span><br><span class=\"line\">33</span><br><span class=\"line\">34</span><br><span class=\"line\">35</span><br><span class=\"line\">36</span><br><span class=\"line\">37</span><br><span class=\"line\">38</span><br><span class=\"line\">39</span><br><span class=\"line\">40</span><br><span class=\"line\">41</span><br><span class=\"line\">42</span><br><span class=\"line\">43</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"></span><br><span class=\"line\"><span class=\"keyword\">import</span> java.math.BigInteger;</span><br><span class=\"line\"></span><br><span class=\"line\"><span class=\"keyword\">public</span> <span class=\"keyword\">class</span> <span class=\"title class_\">Test</span> {</span><br><span class=\"line\"></span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"keyword\">public</span> <span class=\"keyword\">static</span> <span class=\"keyword\">void</span> <span class=\"title function_\">main</span><span class=\"params\">(String[] args)</span> {</span><br><span class=\"line\"></span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"type\">String</span> <span class=\"variable\">add1</span> <span class=\"operator\">=</span> <span class=\"string\">"123123421342134213"</span>;</span><br><span class=\"line\"> <span class=\"type\">String</span> <span class=\"variable\">add2</span> <span class=\"operator\">=</span> <span class=\"string\">"867987614413246574646"</span>;</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"type\">String</span> <span class=\"variable\">sum</span> <span class=\"operator\">=</span> <span class=\"keyword\">new</span> <span class=\"title class_\">BigInteger</span>(add1).add(<span class=\"keyword\">new</span> <span class=\"title class_\">BigInteger</span>(add2)).toString();</span><br><span class=\"line\"> System.out.println(sum);</span><br><span class=\"line\"> System.out.println(bigNumAdd(add1, add2));</span><br><span class=\"line\"> System.out.println(bigNumAdd(add1, add2).equalsIgnoreCase(sum));</span><br><span class=\"line\"></span><br><span class=\"line\"></span><br><span class=\"line\"> }</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"keyword\">static</span> String <span class=\"title function_\">bigNumAdd</span><span class=\"params\">(String add1, String add2)</span> {</span><br><span class=\"line\"> <span class=\"type\">char</span>[] num1 = add1.toCharArray();</span><br><span class=\"line\"> <span class=\"type\">char</span>[] num2 = add2.toCharArray();</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">N1</span> <span class=\"operator\">=</span> num1.length - <span class=\"number\">1</span>;</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">N2</span> <span class=\"operator\">=</span> num2.length - <span class=\"number\">1</span>;</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">sum</span> <span class=\"operator\">=</span> <span class=\"number\">0</span>;</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">carry</span> <span class=\"operator\">=</span> <span class=\"number\">0</span>;</span><br><span class=\"line\"> <span class=\"type\">StringBuilder</span> <span class=\"variable\">ret</span> <span class=\"operator\">=</span> <span class=\"keyword\">new</span> <span class=\"title class_\">StringBuilder</span>();</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"keyword\">while</span>(N1 > -<span class=\"number\">1</span> || N2 > -<span class=\"number\">1</span>) {</span><br><span class=\"line\"> sum = (N1 > -<span class=\"number\">1</span> ? num1[N1]-<span class=\"string\">'0'</span> : <span class=\"number\">0</span>) + (N2 > -<span class=\"number\">1</span> ? num2[N2] - <span class=\"string\">'0'</span> : <span class=\"number\">0</span>) + carry;</span><br><span class=\"line\"> carry = sum / <span class=\"number\">10</span>;</span><br><span class=\"line\"> sum = sum % <span class=\"number\">10</span>;</span><br><span class=\"line\"> ret.append(sum);</span><br><span class=\"line\"> N1 --;</span><br><span class=\"line\"> N2 --;</span><br><span class=\"line\"> }</span><br><span class=\"line\"> </span><br><span class=\"line\"> <span class=\"keyword\">return</span> ret.reverse().toString();</span><br><span class=\"line\"> }</span><br><span class=\"line\">}</span><br></pre></td></tr></table></figure>\n\n\n"},{"title":"经典算法之二分查找","date":"2021-07-17T06:42:51.000Z","comments":1,"toc":true,"_content":"二分查找算法,可以使用递归,也可以直接使用循环。\n是一种非常经典的算法,可有多个变种,或者在其他复杂算法中嵌套使用。\n\n``` java\n //找不到返回-1 找到返回数字在数组中的下标 这是递归版本\n static int biSearch(int[] nums, int k, int left, int right) {\n if (left > right) return -1;\n int mid = left + ((right - left) >> 1);\n if (nums[mid] == k) return mid;\n if (nums[mid] > k) return biSearch(nums, k, left, mid - 1);\n if (nums[mid] < k) return biSearch(nums, k, mid + 1, right);\n return -1;\n }\n\n //这是循环版本\n static int biSearch2(int[] nums, int k) {\n int left = 0;\n int right = nums.length - 1;\n while (left <= right) {\n int mid = (left + right) >> 1;\n if (nums[mid] == k) return mid;\n if (nums[mid] > k) right = mid - 1;\n if (nums[mid] < k) left = mid + 1;\n }\n return -1;\n }\n\n //find the last one or first one\n //这是变种,可以查找含有多个重复元素的第一个或最后一个\n static int biSearch3(int[] nums, int k) {\n int left = 0;\n int right = nums.length - 1;\n while (left <= right) {\n int mid = (left + right) >> 1;\n if (nums[mid] == k) {\n //while (mid > 0 && nums[mid - 1] == k) mid--;\n while(mid < right && nums[mid + 1] == k) mid ++;\n return mid;\n }\n if (nums[mid] > k) right = mid - 1;\n if (nums[mid] < k) left = mid + 1;\n }\n return -1;\n }\n```\n","source":"_posts/经典算法之二分查找.md","raw":"---\ntitle: 经典算法之二分查找\ndate: 2021-07-17 14:42:51\ncomments: true #是否可评论\ntoc: true #是否显示文章目录\ncategories: \"算法\" #分类\ntags: #标签\n - JAVA\n - 算法\n---\n二分查找算法,可以使用递归,也可以直接使用循环。\n是一种非常经典的算法,可有多个变种,或者在其他复杂算法中嵌套使用。\n\n``` java\n //找不到返回-1 找到返回数字在数组中的下标 这是递归版本\n static int biSearch(int[] nums, int k, int left, int right) {\n if (left > right) return -1;\n int mid = left + ((right - left) >> 1);\n if (nums[mid] == k) return mid;\n if (nums[mid] > k) return biSearch(nums, k, left, mid - 1);\n if (nums[mid] < k) return biSearch(nums, k, mid + 1, right);\n return -1;\n }\n\n //这是循环版本\n static int biSearch2(int[] nums, int k) {\n int left = 0;\n int right = nums.length - 1;\n while (left <= right) {\n int mid = (left + right) >> 1;\n if (nums[mid] == k) return mid;\n if (nums[mid] > k) right = mid - 1;\n if (nums[mid] < k) left = mid + 1;\n }\n return -1;\n }\n\n //find the last one or first one\n //这是变种,可以查找含有多个重复元素的第一个或最后一个\n static int biSearch3(int[] nums, int k) {\n int left = 0;\n int right = nums.length - 1;\n while (left <= right) {\n int mid = (left + right) >> 1;\n if (nums[mid] == k) {\n //while (mid > 0 && nums[mid - 1] == k) mid--;\n while(mid < right && nums[mid + 1] == k) mid ++;\n return mid;\n }\n if (nums[mid] > k) right = mid - 1;\n if (nums[mid] < k) left = mid + 1;\n }\n return -1;\n }\n```\n","slug":"经典算法之二分查找","published":1,"updated":"2022-04-20T09:35:50.864Z","layout":"post","photos":[],"link":"","_id":"cl27q3uvb0017n94o6z6v7div","content":"<p>二分查找算法,可以使用递归,也可以直接使用循环。<br>是一种非常经典的算法,可有多个变种,或者在其他复杂算法中嵌套使用。</p>\n<figure class=\"highlight java\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br><span class=\"line\">11</span><br><span class=\"line\">12</span><br><span class=\"line\">13</span><br><span class=\"line\">14</span><br><span class=\"line\">15</span><br><span class=\"line\">16</span><br><span class=\"line\">17</span><br><span class=\"line\">18</span><br><span class=\"line\">19</span><br><span class=\"line\">20</span><br><span class=\"line\">21</span><br><span class=\"line\">22</span><br><span class=\"line\">23</span><br><span class=\"line\">24</span><br><span class=\"line\">25</span><br><span class=\"line\">26</span><br><span class=\"line\">27</span><br><span class=\"line\">28</span><br><span class=\"line\">29</span><br><span class=\"line\">30</span><br><span class=\"line\">31</span><br><span class=\"line\">32</span><br><span class=\"line\">33</span><br><span class=\"line\">34</span><br><span class=\"line\">35</span><br><span class=\"line\">36</span><br><span class=\"line\">37</span><br><span class=\"line\">38</span><br><span class=\"line\">39</span><br><span class=\"line\">40</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"comment\">//找不到返回-1 找到返回数字在数组中的下标 这是递归版本</span></span><br><span class=\"line\"><span class=\"keyword\">static</span> <span class=\"type\">int</span> <span class=\"title function_\">biSearch</span><span class=\"params\">(<span class=\"type\">int</span>[] nums, <span class=\"type\">int</span> k, <span class=\"type\">int</span> left, <span class=\"type\">int</span> right)</span> {</span><br><span class=\"line\"> <span class=\"keyword\">if</span> (left > right) <span class=\"keyword\">return</span> -<span class=\"number\">1</span>;</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">mid</span> <span class=\"operator\">=</span> left + ((right - left) >> <span class=\"number\">1</span>);</span><br><span class=\"line\"> <span class=\"keyword\">if</span> (nums[mid] == k) <span class=\"keyword\">return</span> mid;</span><br><span class=\"line\"> <span class=\"keyword\">if</span> (nums[mid] > k) <span class=\"keyword\">return</span> biSearch(nums, k, left, mid - <span class=\"number\">1</span>);</span><br><span class=\"line\"> <span class=\"keyword\">if</span> (nums[mid] < k) <span class=\"keyword\">return</span> biSearch(nums, k, mid + <span class=\"number\">1</span>, right);</span><br><span class=\"line\"> <span class=\"keyword\">return</span> -<span class=\"number\">1</span>;</span><br><span class=\"line\">}</span><br><span class=\"line\"></span><br><span class=\"line\"><span class=\"comment\">//这是循环版本</span></span><br><span class=\"line\"><span class=\"keyword\">static</span> <span class=\"type\">int</span> <span class=\"title function_\">biSearch2</span><span class=\"params\">(<span class=\"type\">int</span>[] nums, <span class=\"type\">int</span> k)</span> {</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">left</span> <span class=\"operator\">=</span> <span class=\"number\">0</span>;</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">right</span> <span class=\"operator\">=</span> nums.length - <span class=\"number\">1</span>;</span><br><span class=\"line\"> <span class=\"keyword\">while</span> (left <= right) {</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">mid</span> <span class=\"operator\">=</span> (left + right) >> <span class=\"number\">1</span>;</span><br><span class=\"line\"> <span class=\"keyword\">if</span> (nums[mid] == k) <span class=\"keyword\">return</span> mid;</span><br><span class=\"line\"> <span class=\"keyword\">if</span> (nums[mid] > k) right = mid - <span class=\"number\">1</span>;</span><br><span class=\"line\"> <span class=\"keyword\">if</span> (nums[mid] < k) left = mid + <span class=\"number\">1</span>;</span><br><span class=\"line\"> }</span><br><span class=\"line\"> <span class=\"keyword\">return</span> -<span class=\"number\">1</span>;</span><br><span class=\"line\">}</span><br><span class=\"line\"></span><br><span class=\"line\"><span class=\"comment\">//find the last one or first one</span></span><br><span class=\"line\"><span class=\"comment\">//这是变种,可以查找含有多个重复元素的第一个或最后一个</span></span><br><span class=\"line\"><span class=\"keyword\">static</span> <span class=\"type\">int</span> <span class=\"title function_\">biSearch3</span><span class=\"params\">(<span class=\"type\">int</span>[] nums, <span class=\"type\">int</span> k)</span> {</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">left</span> <span class=\"operator\">=</span> <span class=\"number\">0</span>;</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">right</span> <span class=\"operator\">=</span> nums.length - <span class=\"number\">1</span>;</span><br><span class=\"line\"> <span class=\"keyword\">while</span> (left <= right) {</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">mid</span> <span class=\"operator\">=</span> (left + right) >> <span class=\"number\">1</span>;</span><br><span class=\"line\"> <span class=\"keyword\">if</span> (nums[mid] == k) {</span><br><span class=\"line\"> <span class=\"comment\">//while (mid > 0 && nums[mid - 1] == k) mid--;</span></span><br><span class=\"line\"> <span class=\"keyword\">while</span>(mid < right && nums[mid + <span class=\"number\">1</span>] == k) mid ++;</span><br><span class=\"line\"> <span class=\"keyword\">return</span> mid;</span><br><span class=\"line\"> }</span><br><span class=\"line\"> <span class=\"keyword\">if</span> (nums[mid] > k) right = mid - <span class=\"number\">1</span>;</span><br><span class=\"line\"> <span class=\"keyword\">if</span> (nums[mid] < k) left = mid + <span class=\"number\">1</span>;</span><br><span class=\"line\"> }</span><br><span class=\"line\"> <span class=\"keyword\">return</span> -<span class=\"number\">1</span>;</span><br><span class=\"line\">}</span><br></pre></td></tr></table></figure>\n","site":{"data":{}},"excerpt":"","more":"<p>二分查找算法,可以使用递归,也可以直接使用循环。<br>是一种非常经典的算法,可有多个变种,或者在其他复杂算法中嵌套使用。</p>\n<figure class=\"highlight java\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br><span class=\"line\">11</span><br><span class=\"line\">12</span><br><span class=\"line\">13</span><br><span class=\"line\">14</span><br><span class=\"line\">15</span><br><span class=\"line\">16</span><br><span class=\"line\">17</span><br><span class=\"line\">18</span><br><span class=\"line\">19</span><br><span class=\"line\">20</span><br><span class=\"line\">21</span><br><span class=\"line\">22</span><br><span class=\"line\">23</span><br><span class=\"line\">24</span><br><span class=\"line\">25</span><br><span class=\"line\">26</span><br><span class=\"line\">27</span><br><span class=\"line\">28</span><br><span class=\"line\">29</span><br><span class=\"line\">30</span><br><span class=\"line\">31</span><br><span class=\"line\">32</span><br><span class=\"line\">33</span><br><span class=\"line\">34</span><br><span class=\"line\">35</span><br><span class=\"line\">36</span><br><span class=\"line\">37</span><br><span class=\"line\">38</span><br><span class=\"line\">39</span><br><span class=\"line\">40</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"comment\">//找不到返回-1 找到返回数字在数组中的下标 这是递归版本</span></span><br><span class=\"line\"><span class=\"keyword\">static</span> <span class=\"type\">int</span> <span class=\"title function_\">biSearch</span><span class=\"params\">(<span class=\"type\">int</span>[] nums, <span class=\"type\">int</span> k, <span class=\"type\">int</span> left, <span class=\"type\">int</span> right)</span> {</span><br><span class=\"line\"> <span class=\"keyword\">if</span> (left > right) <span class=\"keyword\">return</span> -<span class=\"number\">1</span>;</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">mid</span> <span class=\"operator\">=</span> left + ((right - left) >> <span class=\"number\">1</span>);</span><br><span class=\"line\"> <span class=\"keyword\">if</span> (nums[mid] == k) <span class=\"keyword\">return</span> mid;</span><br><span class=\"line\"> <span class=\"keyword\">if</span> (nums[mid] > k) <span class=\"keyword\">return</span> biSearch(nums, k, left, mid - <span class=\"number\">1</span>);</span><br><span class=\"line\"> <span class=\"keyword\">if</span> (nums[mid] < k) <span class=\"keyword\">return</span> biSearch(nums, k, mid + <span class=\"number\">1</span>, right);</span><br><span class=\"line\"> <span class=\"keyword\">return</span> -<span class=\"number\">1</span>;</span><br><span class=\"line\">}</span><br><span class=\"line\"></span><br><span class=\"line\"><span class=\"comment\">//这是循环版本</span></span><br><span class=\"line\"><span class=\"keyword\">static</span> <span class=\"type\">int</span> <span class=\"title function_\">biSearch2</span><span class=\"params\">(<span class=\"type\">int</span>[] nums, <span class=\"type\">int</span> k)</span> {</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">left</span> <span class=\"operator\">=</span> <span class=\"number\">0</span>;</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">right</span> <span class=\"operator\">=</span> nums.length - <span class=\"number\">1</span>;</span><br><span class=\"line\"> <span class=\"keyword\">while</span> (left <= right) {</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">mid</span> <span class=\"operator\">=</span> (left + right) >> <span class=\"number\">1</span>;</span><br><span class=\"line\"> <span class=\"keyword\">if</span> (nums[mid] == k) <span class=\"keyword\">return</span> mid;</span><br><span class=\"line\"> <span class=\"keyword\">if</span> (nums[mid] > k) right = mid - <span class=\"number\">1</span>;</span><br><span class=\"line\"> <span class=\"keyword\">if</span> (nums[mid] < k) left = mid + <span class=\"number\">1</span>;</span><br><span class=\"line\"> }</span><br><span class=\"line\"> <span class=\"keyword\">return</span> -<span class=\"number\">1</span>;</span><br><span class=\"line\">}</span><br><span class=\"line\"></span><br><span class=\"line\"><span class=\"comment\">//find the last one or first one</span></span><br><span class=\"line\"><span class=\"comment\">//这是变种,可以查找含有多个重复元素的第一个或最后一个</span></span><br><span class=\"line\"><span class=\"keyword\">static</span> <span class=\"type\">int</span> <span class=\"title function_\">biSearch3</span><span class=\"params\">(<span class=\"type\">int</span>[] nums, <span class=\"type\">int</span> k)</span> {</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">left</span> <span class=\"operator\">=</span> <span class=\"number\">0</span>;</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">right</span> <span class=\"operator\">=</span> nums.length - <span class=\"number\">1</span>;</span><br><span class=\"line\"> <span class=\"keyword\">while</span> (left <= right) {</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">mid</span> <span class=\"operator\">=</span> (left + right) >> <span class=\"number\">1</span>;</span><br><span class=\"line\"> <span class=\"keyword\">if</span> (nums[mid] == k) {</span><br><span class=\"line\"> <span class=\"comment\">//while (mid > 0 && nums[mid - 1] == k) mid--;</span></span><br><span class=\"line\"> <span class=\"keyword\">while</span>(mid < right && nums[mid + <span class=\"number\">1</span>] == k) mid ++;</span><br><span class=\"line\"> <span class=\"keyword\">return</span> mid;</span><br><span class=\"line\"> }</span><br><span class=\"line\"> <span class=\"keyword\">if</span> (nums[mid] > k) right = mid - <span class=\"number\">1</span>;</span><br><span class=\"line\"> <span class=\"keyword\">if</span> (nums[mid] < k) left = mid + <span class=\"number\">1</span>;</span><br><span class=\"line\"> }</span><br><span class=\"line\"> <span class=\"keyword\">return</span> -<span class=\"number\">1</span>;</span><br><span class=\"line\">}</span><br></pre></td></tr></table></figure>\n"},{"title":"秋枫","date":"2021-09-03T16:00:00.000Z","toc":true,"_content":"![关门山枫叶](https://raw.githubusercontent.com/student2028/blog_markdown/master/img/fengye2.jpeg)\n#### 本溪关门山\n2021年秋天,夏夏转给我一个去本溪赏枫的文章,后面她表达了想去山里住两天,同时看看枫叶。\n她之前在枫叶国外派生活过两年,想来对枫叶有别样的情怀。\n我看了知乎、小红书、马峰窝、携程上面的攻略。\n\n##### 交通方式 \n北京到本溪\nG3693(复)->北京朝阳到本溪高铁->8:52-12:56 历时4小时04分\nG3691(复)->北京朝阳到本溪高铁->13:05-17:07 历时4小时02分\n本溪市到国家森林公园\nA.火车站有拼车20一位到小市客运站,然后拼车到关门山\nB.本溪火车站,坐16路公交车,坐到东芬客运站,那有到小市的客车,是12块钱左右,大约40分钟可以到小市,\n那有拼车的出租车到关门山,大约10到15元一个人。 \nC.若十一假期期间,火车站广场每天都有多班次直接到达关门山的旅游班车,坐满即走。\n本溪市客运站到小市最早发车时间为6:40,最晚到18:00,8:30前二十分钟一班次,后面10分钟一班次,大约有40-50公里。\n小市到关门山国家森林公园约20多公里。驾车一小时左右,打车约60元以内。\n总结:火车站坐客车到小市(本溪县),然后拼车到关门山。\n##### 住宿\n本溪位于我国东部,9月20至十月的日落时间在18点之前,大约5:50-50:30。\n如果坐第一班高铁,下午一点到本溪,可以直接坐车去小市,在小市或关门山附近找民宿,会有一些时间逛一逛小市,吃些本地的小吃。\n如果坐第二班高铁,当天可以直接在本溪市吃饭住宿,第二天早上出发去关门山。\n更偏向于第一选择。\n民宿或酒店???\n景区内有绿菌宾馆,需要提前与景区确认与订房\n##### 赏枫\n枫红指树(红叶指数)\n> Ⅰ级\n 代表枫叶变色率为10%至35%之间,处于初红状态\n Ⅱ级\n 代表枫叶变色率为35%至60%之间\n Ⅲ级\n 代表枫叶变色率为60%至95%之间,处于正红状态 ,进入最佳观赏期\n2020年9月27日,本溪林业科学研究所开始上线发布枫红指数,其中 关门山森林公园 待红\n\n**关门山国家森林公园**\n> 关门山国家森林公园位于本溪市东南70公里,占地3517公顷,海拔310米—1234米,森林覆盖率85%。\n 园内植物保存完好,古树名木众多,风景秀丽,是辽宁五十佳景之一,因两座山峰一大一小互为对峙,酷似门户,故称为关门山。\n 关门山共有四大景区,小黄山景区、夹砬子景区、龙门峡景区和月台子景区,共计108处景点。\n 关门山是有名的以枫树为主的国家旅游风景区,景区内有枫树多达120多种,\n 比较著名的有日本红枫,中国红枫,五角枫,三角枫,羽毛枫,青枫,元宝枫,血皮槭,鸡爪槭,地锦槭,秀丽槭,红翅槭,樟叶槭等。\n 一到秋天,漫山遍野的枫叶让整个天关门山都批上了一层红妆。\n\n门票:90元\n开放时间: 8点--16点\n上山的话一般坐观光车。到达终点20元。到达枫王景区10元每位。\n\n如果想拍出好的红枫照,尽量选择阳光灿烂、能见度好的日子。 \n需要注意的是,由于气候情况不同,每年的枫叶变红及凋零的周期也可能变化,\n建议来之前提早关注一下景区官网资讯,如果来晚了就只能看枯枝了。\n\n打卡地:\n枫王\n红叶最早,落叶最晚,红得鲜艳,姿态最飘逸。\n美食: 小市小羊汤 酸汤子 三姐妹烧烤 山野菜 和 笨鸡\n特产: 山野菜 本溪一号肠 冻梨\n景区出口就有大巴车和出租车\n\n小黄山是整个公园的风光精华,比较经典的线路是:\n小黄山入口-木兰谷-五彩湖-枫之海-好汉坡-龙脊岭-通天门-小黄山出口(即入口),\n此线路全程步行加爬山,耗时约4小时,对体力较为考验。\n也可以搭游览车到龙门峡,从龙门峡开始步行,线路为:\n夫妻树-晶帘瀑布-龙门峡口-转心湖-红松林-枫之海-好汉坡-龙脊岭-通天门-迎客峰-小黄山出口-木兰谷-五彩湖,\n在五彩湖搭车出山,全程耗时约5小时。\n\n##### 注意事项:\n枫叶景区往往昼夜温差大,位于山区,防寒保暖非常重要。\n同时备足水和干粮。\n\n##### 备选景点\n老边沟景区,同样也是赏枫的一个好去处,开发配套差一点,更原始。\n![老边沟枫叶](https://raw.githubusercontent.com/student2028/blog_markdown/master/img/fengye1.jpeg)\n\n##### 备选方案\n洛阳老君山?\n\n#### 最美的不是美景,而是身边的美人\n","source":"_posts/秋枫.md","raw":"---\ntitle: 秋枫\ndate: 2021-09-04\ntoc: true #是否显示文章目录\ncategories: \"生活\" #分类\ntags: #标签\n - 旅行\n---\n![关门山枫叶](https://raw.githubusercontent.com/student2028/blog_markdown/master/img/fengye2.jpeg)\n#### 本溪关门山\n2021年秋天,夏夏转给我一个去本溪赏枫的文章,后面她表达了想去山里住两天,同时看看枫叶。\n她之前在枫叶国外派生活过两年,想来对枫叶有别样的情怀。\n我看了知乎、小红书、马峰窝、携程上面的攻略。\n\n##### 交通方式 \n北京到本溪\nG3693(复)->北京朝阳到本溪高铁->8:52-12:56 历时4小时04分\nG3691(复)->北京朝阳到本溪高铁->13:05-17:07 历时4小时02分\n本溪市到国家森林公园\nA.火车站有拼车20一位到小市客运站,然后拼车到关门山\nB.本溪火车站,坐16路公交车,坐到东芬客运站,那有到小市的客车,是12块钱左右,大约40分钟可以到小市,\n那有拼车的出租车到关门山,大约10到15元一个人。 \nC.若十一假期期间,火车站广场每天都有多班次直接到达关门山的旅游班车,坐满即走。\n本溪市客运站到小市最早发车时间为6:40,最晚到18:00,8:30前二十分钟一班次,后面10分钟一班次,大约有40-50公里。\n小市到关门山国家森林公园约20多公里。驾车一小时左右,打车约60元以内。\n总结:火车站坐客车到小市(本溪县),然后拼车到关门山。\n##### 住宿\n本溪位于我国东部,9月20至十月的日落时间在18点之前,大约5:50-50:30。\n如果坐第一班高铁,下午一点到本溪,可以直接坐车去小市,在小市或关门山附近找民宿,会有一些时间逛一逛小市,吃些本地的小吃。\n如果坐第二班高铁,当天可以直接在本溪市吃饭住宿,第二天早上出发去关门山。\n更偏向于第一选择。\n民宿或酒店???\n景区内有绿菌宾馆,需要提前与景区确认与订房\n##### 赏枫\n枫红指树(红叶指数)\n> Ⅰ级\n 代表枫叶变色率为10%至35%之间,处于初红状态\n Ⅱ级\n 代表枫叶变色率为35%至60%之间\n Ⅲ级\n 代表枫叶变色率为60%至95%之间,处于正红状态 ,进入最佳观赏期\n2020年9月27日,本溪林业科学研究所开始上线发布枫红指数,其中 关门山森林公园 待红\n\n**关门山国家森林公园**\n> 关门山国家森林公园位于本溪市东南70公里,占地3517公顷,海拔310米—1234米,森林覆盖率85%。\n 园内植物保存完好,古树名木众多,风景秀丽,是辽宁五十佳景之一,因两座山峰一大一小互为对峙,酷似门户,故称为关门山。\n 关门山共有四大景区,小黄山景区、夹砬子景区、龙门峡景区和月台子景区,共计108处景点。\n 关门山是有名的以枫树为主的国家旅游风景区,景区内有枫树多达120多种,\n 比较著名的有日本红枫,中国红枫,五角枫,三角枫,羽毛枫,青枫,元宝枫,血皮槭,鸡爪槭,地锦槭,秀丽槭,红翅槭,樟叶槭等。\n 一到秋天,漫山遍野的枫叶让整个天关门山都批上了一层红妆。\n\n门票:90元\n开放时间: 8点--16点\n上山的话一般坐观光车。到达终点20元。到达枫王景区10元每位。\n\n如果想拍出好的红枫照,尽量选择阳光灿烂、能见度好的日子。 \n需要注意的是,由于气候情况不同,每年的枫叶变红及凋零的周期也可能变化,\n建议来之前提早关注一下景区官网资讯,如果来晚了就只能看枯枝了。\n\n打卡地:\n枫王\n红叶最早,落叶最晚,红得鲜艳,姿态最飘逸。\n美食: 小市小羊汤 酸汤子 三姐妹烧烤 山野菜 和 笨鸡\n特产: 山野菜 本溪一号肠 冻梨\n景区出口就有大巴车和出租车\n\n小黄山是整个公园的风光精华,比较经典的线路是:\n小黄山入口-木兰谷-五彩湖-枫之海-好汉坡-龙脊岭-通天门-小黄山出口(即入口),\n此线路全程步行加爬山,耗时约4小时,对体力较为考验。\n也可以搭游览车到龙门峡,从龙门峡开始步行,线路为:\n夫妻树-晶帘瀑布-龙门峡口-转心湖-红松林-枫之海-好汉坡-龙脊岭-通天门-迎客峰-小黄山出口-木兰谷-五彩湖,\n在五彩湖搭车出山,全程耗时约5小时。\n\n##### 注意事项:\n枫叶景区往往昼夜温差大,位于山区,防寒保暖非常重要。\n同时备足水和干粮。\n\n##### 备选景点\n老边沟景区,同样也是赏枫的一个好去处,开发配套差一点,更原始。\n![老边沟枫叶](https://raw.githubusercontent.com/student2028/blog_markdown/master/img/fengye1.jpeg)\n\n##### 备选方案\n洛阳老君山?\n\n#### 最美的不是美景,而是身边的美人\n","slug":"秋枫","published":1,"updated":"2022-04-20T09:35:50.864Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cl27q3uvb0019n94ocrpd7v5o","content":"<p><img src=\"https://raw.githubusercontent.com/student2028/blog_markdown/master/img/fengye2.jpeg\" alt=\"关门山枫叶\"></p>\n<h4 id=\"本溪关门山\"><a href=\"#本溪关门山\" class=\"headerlink\" title=\"本溪关门山\"></a>本溪关门山</h4><p>2021年秋天,夏夏转给我一个去本溪赏枫的文章,后面她表达了想去山里住两天,同时看看枫叶。<br>她之前在枫叶国外派生活过两年,想来对枫叶有别样的情怀。<br>我看了知乎、小红书、马峰窝、携程上面的攻略。</p>\n<h5 id=\"交通方式\"><a href=\"#交通方式\" class=\"headerlink\" title=\"交通方式\"></a>交通方式</h5><p>北京到本溪<br>G3693(复)->北京朝阳到本溪高铁->8:52-12:56 历时4小时04分<br>G3691(复)->北京朝阳到本溪高铁->13:05-17:07 历时4小时02分<br>本溪市到国家森林公园<br>A.火车站有拼车20一位到小市客运站,然后拼车到关门山<br>B.本溪火车站,坐16路公交车,坐到东芬客运站,那有到小市的客车,是12块钱左右,大约40分钟可以到小市,<br>那有拼车的出租车到关门山,大约10到15元一个人。<br>C.若十一假期期间,火车站广场每天都有多班次直接到达关门山的旅游班车,坐满即走。<br>本溪市客运站到小市最早发车时间为6:40,最晚到18:00,8:30前二十分钟一班次,后面10分钟一班次,大约有40-50公里。<br>小市到关门山国家森林公园约20多公里。驾车一小时左右,打车约60元以内。<br>总结:火车站坐客车到小市(本溪县),然后拼车到关门山。</p>\n<h5 id=\"住宿\"><a href=\"#住宿\" class=\"headerlink\" title=\"住宿\"></a>住宿</h5><p>本溪位于我国东部,9月20至十月的日落时间在18点之前,大约5:50-50:30。<br>如果坐第一班高铁,下午一点到本溪,可以直接坐车去小市,在小市或关门山附近找民宿,会有一些时间逛一逛小市,吃些本地的小吃。<br>如果坐第二班高铁,当天可以直接在本溪市吃饭住宿,第二天早上出发去关门山。<br>更偏向于第一选择。<br>民宿或酒店???<br>景区内有绿菌宾馆,需要提前与景区确认与订房</p>\n<h5 id=\"赏枫\"><a href=\"#赏枫\" class=\"headerlink\" title=\"赏枫\"></a>赏枫</h5><p>枫红指树(红叶指数)</p>\n<blockquote>\n<p>Ⅰ级<br> 代表枫叶变色率为10%至35%之间,处于初红状态<br> Ⅱ级<br> 代表枫叶变色率为35%至60%之间<br> Ⅲ级<br> 代表枫叶变色率为60%至95%之间,处于正红状态 ,进入最佳观赏期<br>2020年9月27日,本溪林业科学研究所开始上线发布枫红指数,其中 关门山森林公园 待红</p>\n</blockquote>\n<p><strong>关门山国家森林公园</strong></p>\n<blockquote>\n<p>关门山国家森林公园位于本溪市东南70公里,占地3517公顷,海拔310米—1234米,森林覆盖率85%。<br> 园内植物保存完好,古树名木众多,风景秀丽,是辽宁五十佳景之一,因两座山峰一大一小互为对峙,酷似门户,故称为关门山。<br> 关门山共有四大景区,小黄山景区、夹砬子景区、龙门峡景区和月台子景区,共计108处景点。<br> 关门山是有名的以枫树为主的国家旅游风景区,景区内有枫树多达120多种,<br> 比较著名的有日本红枫,中国红枫,五角枫,三角枫,羽毛枫,青枫,元宝枫,血皮槭,鸡爪槭,地锦槭,秀丽槭,红翅槭,樟叶槭等。<br> 一到秋天,漫山遍野的枫叶让整个天关门山都批上了一层红妆。</p>\n</blockquote>\n<p>门票:90元<br>开放时间: 8点–16点<br>上山的话一般坐观光车。到达终点20元。到达枫王景区10元每位。</p>\n<p>如果想拍出好的红枫照,尽量选择阳光灿烂、能见度好的日子。<br>需要注意的是,由于气候情况不同,每年的枫叶变红及凋零的周期也可能变化,<br>建议来之前提早关注一下景区官网资讯,如果来晚了就只能看枯枝了。</p>\n<p>打卡地:<br>枫王<br>红叶最早,落叶最晚,红得鲜艳,姿态最飘逸。<br>美食: 小市小羊汤 酸汤子 三姐妹烧烤 山野菜 和 笨鸡<br>特产: 山野菜 本溪一号肠 冻梨<br>景区出口就有大巴车和出租车</p>\n<p>小黄山是整个公园的风光精华,比较经典的线路是:<br>小黄山入口-木兰谷-五彩湖-枫之海-好汉坡-龙脊岭-通天门-小黄山出口(即入口),<br>此线路全程步行加爬山,耗时约4小时,对体力较为考验。<br>也可以搭游览车到龙门峡,从龙门峡开始步行,线路为:<br>夫妻树-晶帘瀑布-龙门峡口-转心湖-红松林-枫之海-好汉坡-龙脊岭-通天门-迎客峰-小黄山出口-木兰谷-五彩湖,<br>在五彩湖搭车出山,全程耗时约5小时。</p>\n<h5 id=\"注意事项:\"><a href=\"#注意事项:\" class=\"headerlink\" title=\"注意事项:\"></a>注意事项:</h5><p>枫叶景区往往昼夜温差大,位于山区,防寒保暖非常重要。<br>同时备足水和干粮。</p>\n<h5 id=\"备选景点\"><a href=\"#备选景点\" class=\"headerlink\" title=\"备选景点\"></a>备选景点</h5><p>老边沟景区,同样也是赏枫的一个好去处,开发配套差一点,更原始。<br><img src=\"https://raw.githubusercontent.com/student2028/blog_markdown/master/img/fengye1.jpeg\" alt=\"老边沟枫叶\"></p>\n<h5 id=\"备选方案\"><a href=\"#备选方案\" class=\"headerlink\" title=\"备选方案\"></a>备选方案</h5><p>洛阳老君山?</p>\n<h4 id=\"最美的不是美景,而是身边的美人\"><a href=\"#最美的不是美景,而是身边的美人\" class=\"headerlink\" title=\"最美的不是美景,而是身边的美人\"></a>最美的不是美景,而是身边的美人</h4>","site":{"data":{}},"excerpt":"","more":"<p><img src=\"https://raw.githubusercontent.com/student2028/blog_markdown/master/img/fengye2.jpeg\" alt=\"关门山枫叶\"></p>\n<h4 id=\"本溪关门山\"><a href=\"#本溪关门山\" class=\"headerlink\" title=\"本溪关门山\"></a>本溪关门山</h4><p>2021年秋天,夏夏转给我一个去本溪赏枫的文章,后面她表达了想去山里住两天,同时看看枫叶。<br>她之前在枫叶国外派生活过两年,想来对枫叶有别样的情怀。<br>我看了知乎、小红书、马峰窝、携程上面的攻略。</p>\n<h5 id=\"交通方式\"><a href=\"#交通方式\" class=\"headerlink\" title=\"交通方式\"></a>交通方式</h5><p>北京到本溪<br>G3693(复)->北京朝阳到本溪高铁->8:52-12:56 历时4小时04分<br>G3691(复)->北京朝阳到本溪高铁->13:05-17:07 历时4小时02分<br>本溪市到国家森林公园<br>A.火车站有拼车20一位到小市客运站,然后拼车到关门山<br>B.本溪火车站,坐16路公交车,坐到东芬客运站,那有到小市的客车,是12块钱左右,大约40分钟可以到小市,<br>那有拼车的出租车到关门山,大约10到15元一个人。<br>C.若十一假期期间,火车站广场每天都有多班次直接到达关门山的旅游班车,坐满即走。<br>本溪市客运站到小市最早发车时间为6:40,最晚到18:00,8:30前二十分钟一班次,后面10分钟一班次,大约有40-50公里。<br>小市到关门山国家森林公园约20多公里。驾车一小时左右,打车约60元以内。<br>总结:火车站坐客车到小市(本溪县),然后拼车到关门山。</p>\n<h5 id=\"住宿\"><a href=\"#住宿\" class=\"headerlink\" title=\"住宿\"></a>住宿</h5><p>本溪位于我国东部,9月20至十月的日落时间在18点之前,大约5:50-50:30。<br>如果坐第一班高铁,下午一点到本溪,可以直接坐车去小市,在小市或关门山附近找民宿,会有一些时间逛一逛小市,吃些本地的小吃。<br>如果坐第二班高铁,当天可以直接在本溪市吃饭住宿,第二天早上出发去关门山。<br>更偏向于第一选择。<br>民宿或酒店???<br>景区内有绿菌宾馆,需要提前与景区确认与订房</p>\n<h5 id=\"赏枫\"><a href=\"#赏枫\" class=\"headerlink\" title=\"赏枫\"></a>赏枫</h5><p>枫红指树(红叶指数)</p>\n<blockquote>\n<p>Ⅰ级<br> 代表枫叶变色率为10%至35%之间,处于初红状态<br> Ⅱ级<br> 代表枫叶变色率为35%至60%之间<br> Ⅲ级<br> 代表枫叶变色率为60%至95%之间,处于正红状态 ,进入最佳观赏期<br>2020年9月27日,本溪林业科学研究所开始上线发布枫红指数,其中 关门山森林公园 待红</p>\n</blockquote>\n<p><strong>关门山国家森林公园</strong></p>\n<blockquote>\n<p>关门山国家森林公园位于本溪市东南70公里,占地3517公顷,海拔310米—1234米,森林覆盖率85%。<br> 园内植物保存完好,古树名木众多,风景秀丽,是辽宁五十佳景之一,因两座山峰一大一小互为对峙,酷似门户,故称为关门山。<br> 关门山共有四大景区,小黄山景区、夹砬子景区、龙门峡景区和月台子景区,共计108处景点。<br> 关门山是有名的以枫树为主的国家旅游风景区,景区内有枫树多达120多种,<br> 比较著名的有日本红枫,中国红枫,五角枫,三角枫,羽毛枫,青枫,元宝枫,血皮槭,鸡爪槭,地锦槭,秀丽槭,红翅槭,樟叶槭等。<br> 一到秋天,漫山遍野的枫叶让整个天关门山都批上了一层红妆。</p>\n</blockquote>\n<p>门票:90元<br>开放时间: 8点–16点<br>上山的话一般坐观光车。到达终点20元。到达枫王景区10元每位。</p>\n<p>如果想拍出好的红枫照,尽量选择阳光灿烂、能见度好的日子。<br>需要注意的是,由于气候情况不同,每年的枫叶变红及凋零的周期也可能变化,<br>建议来之前提早关注一下景区官网资讯,如果来晚了就只能看枯枝了。</p>\n<p>打卡地:<br>枫王<br>红叶最早,落叶最晚,红得鲜艳,姿态最飘逸。<br>美食: 小市小羊汤 酸汤子 三姐妹烧烤 山野菜 和 笨鸡<br>特产: 山野菜 本溪一号肠 冻梨<br>景区出口就有大巴车和出租车</p>\n<p>小黄山是整个公园的风光精华,比较经典的线路是:<br>小黄山入口-木兰谷-五彩湖-枫之海-好汉坡-龙脊岭-通天门-小黄山出口(即入口),<br>此线路全程步行加爬山,耗时约4小时,对体力较为考验。<br>也可以搭游览车到龙门峡,从龙门峡开始步行,线路为:<br>夫妻树-晶帘瀑布-龙门峡口-转心湖-红松林-枫之海-好汉坡-龙脊岭-通天门-迎客峰-小黄山出口-木兰谷-五彩湖,<br>在五彩湖搭车出山,全程耗时约5小时。</p>\n<h5 id=\"注意事项:\"><a href=\"#注意事项:\" class=\"headerlink\" title=\"注意事项:\"></a>注意事项:</h5><p>枫叶景区往往昼夜温差大,位于山区,防寒保暖非常重要。<br>同时备足水和干粮。</p>\n<h5 id=\"备选景点\"><a href=\"#备选景点\" class=\"headerlink\" title=\"备选景点\"></a>备选景点</h5><p>老边沟景区,同样也是赏枫的一个好去处,开发配套差一点,更原始。<br><img src=\"https://raw.githubusercontent.com/student2028/blog_markdown/master/img/fengye1.jpeg\" alt=\"老边沟枫叶\"></p>\n<h5 id=\"备选方案\"><a href=\"#备选方案\" class=\"headerlink\" title=\"备选方案\"></a>备选方案</h5><p>洛阳老君山?</p>\n<h4 id=\"最美的不是美景,而是身边的美人\"><a href=\"#最美的不是美景,而是身边的美人\" class=\"headerlink\" title=\"最美的不是美景,而是身边的美人\"></a>最美的不是美景,而是身边的美人</h4>"},{"title":"理解Java中的动态代理Part1","date":"2021-07-25T09:19:43.000Z","comments":1,"toc":true,"_content":"> 对Java动态代理的理解,有助于我们理解复杂框架的设计,是提高自身java水平的一个重要的点。\n 像Spring和Hadoop等开源框架中都有使用动态代理。\n##### **代理模式**\n首先说一下代理模式,它有一个subject(接口),一个RealSubject实现类和一个Proxy实现类。\n客户端不会直接访问到RealSubject,只会访问Proxy对象,Proxy对象会有一个RealSubject对象\n的实例作为成员变量,在subject定义的方法中做一些增强功能然后再调用RealSubject中实现的接口中\n的方法。\n##### **为什么需要代理?**\n这是不是多此一举,我们直接new RealSubject,然后调用方法不就可以吗?\n是的,通常就是这么使用的,但是在框架的设计中,通常会有一些统一的统计或监控等工作,譬如记录\n每一个方法执行的开始结束时间,运行时长和方法被调用时传入的参数等信息,你不用在每一个方法实现\n中都加入这一段功能代码,你可以拦截方法的执行,在方法执行前后加一些统一的代码。\n##### **动态代理中的动态是什么意思?**\n静态代理中代理类是在代码中提前写好的。而使用JDK反射包下的Proxy和InvocationHandler来实现的\n代理中,代理类事先并不存在,是在运行过程中根据传入的类加载器和接口动态生成的字节码,然后加载成\n类对象,并构造一个接口的实现类实例对象供客户端使用。\n##### **JAVA中的动态代理怎么使用?**\nJava的反射包下面有两个类,Proxy和InvocationHandler接口。\nInvocationHandler接口中有一个invoke方法,它接收一个方法元数据和方法参数数组,因为事先不知道\n被代理对象的具体类型,所以这里使用反射来操作,要告诉它执行什么方法,参数是什么。\n首先我们做代理的目标是要增强功能,我们把我们要增强的功能写在哪里?就是写在这个invoke方法里面。\n所以我们需要写一个类来实现这个InvocationHandler接口,这个类需要有一个Object对象(如果不使用泛型)\n的成员变量来表示被代理的类实例;提供一种对象的注入方式,譬如set或者构造器都可以。\n然后在invoke方法中把我们的增强逻辑实现,\b然后需要写method.invoke(obj,args);obj就是被代理对象,\nargs就是传入的method方法的参数。\nProxy类是来做什么的呢?\n按理说,有了实现InvocationHandler的实现类就可以使用了,但是这样使用起来太不方便。Proxy可以用来动\n态生成一个代理类,生成的代理类可以转成被代理的接口对象。\n``` java\nProxy.newProxyInstance(RealSubject.class.getClassLoader(),\n RealSubject.class.getInterfaces(),\n handler);\n```\n它根据传入的参数动态生成字节码,并命名为$proxy + num,这个类扩展了Proxy并实现了RealSubject实现的接口。\n同时会有几个静态的static Method成员变量代表接口中的方法,后面供InvocationHandler的invoke方法使用。\nProxy类有一个构造器是接收InvocationHandler作为参数,所以newProxyInstance时传入的handler就会被传入\n新生成的类中使用;同时它还会有RealSubject中要实现的方法,在这个方法中会调用handler的invoke方法。\n\n代码示例如下,你可以下面的代码中体会一下:\n``` java\npackage org.java;\n\nimport sun.misc.ProxyGenerator;\nimport java.io.FileOutputStream;\nimport java.lang.reflect.InvocationHandler;\nimport java.lang.reflect.Method;\nimport java.lang.reflect.Proxy;\n\n\npublic class Test {\n\n public static void main(String[] args) {\n\n Add adder = new Adder();\n CalculateIH handler = new CalculateIH(adder);\n Add adder1 = (Add) Proxy.newProxyInstance(Adder.class.getClassLoader(),\n Adder.class.getInterfaces(),\n handler);\n System.out.println( adder1.add(1,100));\n\n\n Multiplyer m = new Multiplyer();\n CalculateIH handler2 = new CalculateIH(m);\n multiply multiply = (multiply) Proxy.newProxyInstance(Multiplyer.class.getClassLoader(),\n Multiplyer.class.getInterfaces(),\n handler2);\n System.out.println( multiply.multiply(12,12));\n\n byte[] classFile = ProxyGenerator.generateProxyClass(\"$Proxy10\", Adder.class.getInterfaces());\n String path = \"./AdderProxy.class\";\n try(FileOutputStream fos = new FileOutputStream(path)) {\n fos.write(classFile);\n fos.flush();\n System.out.println(\"代理类class文件写入成功\");\n } catch (Exception e) {\n e.printStackTrace();\n System.out.println(\"写文件错误\");\n }\n\n }\n\n}\n\ninterface Add {\n int add(int add1, int add2);\n}\n\nclass Adder implements Add {\n @Override\n public int add(int add1, int add2) {\n return add1 + add2;\n }\n}\n\ninterface multiply {\n int multiply (int m1, int m2);\n}\n\nclass Multiplyer implements multiply {\n\n @Override\n public int multiply(int m1, int m2) {\n return m1 * m2;\n }\n}\n\nclass CalculateIH implements InvocationHandler {\n\n Object obj;\n\n public CalculateIH(Object obj) {\n this.obj = obj;\n }\n\n @Override\n public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {\n\n System.out.println(\"start at : \" + System.nanoTime());\n Object ret = method.invoke(obj, args);\n System.out.println(\"executed result is \" + ret);\n System.out.println(\"end at : \" + System.nanoTime());\n return ret;\n }\n}\n\n```\n以下是对生成的代理类反编译的java代码:\n``` java \nimport java.lang.reflect.InvocationHandler;\nimport java.lang.reflect.Method;\nimport java.lang.reflect.Proxy;\nimport java.lang.reflect.UndeclaredThrowableException;\nimport org.java.Add;\n\npublic final class $Proxy10 extends Proxy implements Add {\n private static Method m1;\n private static Method m2;\n private static Method m3;\n private static Method m0;\n\n public $Proxy10(InvocationHandler var1) throws {\n super(var1);\n }\n\n public final boolean equals(Object var1) throws {\n try {\n return (Boolean)super.h.invoke(this, m1, new Object[]{var1});\n } catch (RuntimeException | Error var3) {\n throw var3;\n } catch (Throwable var4) {\n throw new UndeclaredThrowableException(var4);\n }\n }\n\n public final String toString() throws {\n try {\n return (String)super.h.invoke(this, m2, (Object[])null);\n } catch (RuntimeException | Error var2) {\n throw var2;\n } catch (Throwable var3) {\n throw new UndeclaredThrowableException(var3);\n }\n }\n\n public final int add(int var1, int var2) throws {\n try {\n return (Integer)super.h.invoke(this, m3, new Object[]{var1, var2});\n } catch (RuntimeException | Error var4) {\n throw var4;\n } catch (Throwable var5) {\n throw new UndeclaredThrowableException(var5);\n }\n }\n\n public final int hashCode() throws {\n try {\n return (Integer)super.h.invoke(this, m0, (Object[])null);\n } catch (RuntimeException | Error var2) {\n throw var2;\n } catch (Throwable var3) {\n throw new UndeclaredThrowableException(var3);\n }\n }\n\n static {\n try {\n m1 = Class.forName(\"java.lang.Object\").getMethod(\"equals\", Class.forName(\"java.lang.Object\"));\n m2 = Class.forName(\"java.lang.Object\").getMethod(\"toString\");\n m3 = Class.forName(\"org.java.Add\").getMethod(\"add\", Integer.TYPE, Integer.TYPE);\n m0 = Class.forName(\"java.lang.Object\").getMethod(\"hashCode\");\n } catch (NoSuchMethodException var2) {\n throw new NoSuchMethodError(var2.getMessage());\n } catch (ClassNotFoundException var3) {\n throw new NoClassDefFoundError(var3.getMessage());\n }\n }\n}\n\n```\n\n\n\n","source":"_posts/理解Java中的动态代理Part1.md","raw":"---\ntitle: 理解Java中的动态代理Part1\ndate: 2021-07-25 17:19:43\ncomments: true #是否可评论\ntoc: true #是否显示文章目录\ncategories: \"基础\" #分类\ntags: #标签\n - JAVA\n---\n> 对Java动态代理的理解,有助于我们理解复杂框架的设计,是提高自身java水平的一个重要的点。\n 像Spring和Hadoop等开源框架中都有使用动态代理。\n##### **代理模式**\n首先说一下代理模式,它有一个subject(接口),一个RealSubject实现类和一个Proxy实现类。\n客户端不会直接访问到RealSubject,只会访问Proxy对象,Proxy对象会有一个RealSubject对象\n的实例作为成员变量,在subject定义的方法中做一些增强功能然后再调用RealSubject中实现的接口中\n的方法。\n##### **为什么需要代理?**\n这是不是多此一举,我们直接new RealSubject,然后调用方法不就可以吗?\n是的,通常就是这么使用的,但是在框架的设计中,通常会有一些统一的统计或监控等工作,譬如记录\n每一个方法执行的开始结束时间,运行时长和方法被调用时传入的参数等信息,你不用在每一个方法实现\n中都加入这一段功能代码,你可以拦截方法的执行,在方法执行前后加一些统一的代码。\n##### **动态代理中的动态是什么意思?**\n静态代理中代理类是在代码中提前写好的。而使用JDK反射包下的Proxy和InvocationHandler来实现的\n代理中,代理类事先并不存在,是在运行过程中根据传入的类加载器和接口动态生成的字节码,然后加载成\n类对象,并构造一个接口的实现类实例对象供客户端使用。\n##### **JAVA中的动态代理怎么使用?**\nJava的反射包下面有两个类,Proxy和InvocationHandler接口。\nInvocationHandler接口中有一个invoke方法,它接收一个方法元数据和方法参数数组,因为事先不知道\n被代理对象的具体类型,所以这里使用反射来操作,要告诉它执行什么方法,参数是什么。\n首先我们做代理的目标是要增强功能,我们把我们要增强的功能写在哪里?就是写在这个invoke方法里面。\n所以我们需要写一个类来实现这个InvocationHandler接口,这个类需要有一个Object对象(如果不使用泛型)\n的成员变量来表示被代理的类实例;提供一种对象的注入方式,譬如set或者构造器都可以。\n然后在invoke方法中把我们的增强逻辑实现,\b然后需要写method.invoke(obj,args);obj就是被代理对象,\nargs就是传入的method方法的参数。\nProxy类是来做什么的呢?\n按理说,有了实现InvocationHandler的实现类就可以使用了,但是这样使用起来太不方便。Proxy可以用来动\n态生成一个代理类,生成的代理类可以转成被代理的接口对象。\n``` java\nProxy.newProxyInstance(RealSubject.class.getClassLoader(),\n RealSubject.class.getInterfaces(),\n handler);\n```\n它根据传入的参数动态生成字节码,并命名为$proxy + num,这个类扩展了Proxy并实现了RealSubject实现的接口。\n同时会有几个静态的static Method成员变量代表接口中的方法,后面供InvocationHandler的invoke方法使用。\nProxy类有一个构造器是接收InvocationHandler作为参数,所以newProxyInstance时传入的handler就会被传入\n新生成的类中使用;同时它还会有RealSubject中要实现的方法,在这个方法中会调用handler的invoke方法。\n\n代码示例如下,你可以下面的代码中体会一下:\n``` java\npackage org.java;\n\nimport sun.misc.ProxyGenerator;\nimport java.io.FileOutputStream;\nimport java.lang.reflect.InvocationHandler;\nimport java.lang.reflect.Method;\nimport java.lang.reflect.Proxy;\n\n\npublic class Test {\n\n public static void main(String[] args) {\n\n Add adder = new Adder();\n CalculateIH handler = new CalculateIH(adder);\n Add adder1 = (Add) Proxy.newProxyInstance(Adder.class.getClassLoader(),\n Adder.class.getInterfaces(),\n handler);\n System.out.println( adder1.add(1,100));\n\n\n Multiplyer m = new Multiplyer();\n CalculateIH handler2 = new CalculateIH(m);\n multiply multiply = (multiply) Proxy.newProxyInstance(Multiplyer.class.getClassLoader(),\n Multiplyer.class.getInterfaces(),\n handler2);\n System.out.println( multiply.multiply(12,12));\n\n byte[] classFile = ProxyGenerator.generateProxyClass(\"$Proxy10\", Adder.class.getInterfaces());\n String path = \"./AdderProxy.class\";\n try(FileOutputStream fos = new FileOutputStream(path)) {\n fos.write(classFile);\n fos.flush();\n System.out.println(\"代理类class文件写入成功\");\n } catch (Exception e) {\n e.printStackTrace();\n System.out.println(\"写文件错误\");\n }\n\n }\n\n}\n\ninterface Add {\n int add(int add1, int add2);\n}\n\nclass Adder implements Add {\n @Override\n public int add(int add1, int add2) {\n return add1 + add2;\n }\n}\n\ninterface multiply {\n int multiply (int m1, int m2);\n}\n\nclass Multiplyer implements multiply {\n\n @Override\n public int multiply(int m1, int m2) {\n return m1 * m2;\n }\n}\n\nclass CalculateIH implements InvocationHandler {\n\n Object obj;\n\n public CalculateIH(Object obj) {\n this.obj = obj;\n }\n\n @Override\n public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {\n\n System.out.println(\"start at : \" + System.nanoTime());\n Object ret = method.invoke(obj, args);\n System.out.println(\"executed result is \" + ret);\n System.out.println(\"end at : \" + System.nanoTime());\n return ret;\n }\n}\n\n```\n以下是对生成的代理类反编译的java代码:\n``` java \nimport java.lang.reflect.InvocationHandler;\nimport java.lang.reflect.Method;\nimport java.lang.reflect.Proxy;\nimport java.lang.reflect.UndeclaredThrowableException;\nimport org.java.Add;\n\npublic final class $Proxy10 extends Proxy implements Add {\n private static Method m1;\n private static Method m2;\n private static Method m3;\n private static Method m0;\n\n public $Proxy10(InvocationHandler var1) throws {\n super(var1);\n }\n\n public final boolean equals(Object var1) throws {\n try {\n return (Boolean)super.h.invoke(this, m1, new Object[]{var1});\n } catch (RuntimeException | Error var3) {\n throw var3;\n } catch (Throwable var4) {\n throw new UndeclaredThrowableException(var4);\n }\n }\n\n public final String toString() throws {\n try {\n return (String)super.h.invoke(this, m2, (Object[])null);\n } catch (RuntimeException | Error var2) {\n throw var2;\n } catch (Throwable var3) {\n throw new UndeclaredThrowableException(var3);\n }\n }\n\n public final int add(int var1, int var2) throws {\n try {\n return (Integer)super.h.invoke(this, m3, new Object[]{var1, var2});\n } catch (RuntimeException | Error var4) {\n throw var4;\n } catch (Throwable var5) {\n throw new UndeclaredThrowableException(var5);\n }\n }\n\n public final int hashCode() throws {\n try {\n return (Integer)super.h.invoke(this, m0, (Object[])null);\n } catch (RuntimeException | Error var2) {\n throw var2;\n } catch (Throwable var3) {\n throw new UndeclaredThrowableException(var3);\n }\n }\n\n static {\n try {\n m1 = Class.forName(\"java.lang.Object\").getMethod(\"equals\", Class.forName(\"java.lang.Object\"));\n m2 = Class.forName(\"java.lang.Object\").getMethod(\"toString\");\n m3 = Class.forName(\"org.java.Add\").getMethod(\"add\", Integer.TYPE, Integer.TYPE);\n m0 = Class.forName(\"java.lang.Object\").getMethod(\"hashCode\");\n } catch (NoSuchMethodException var2) {\n throw new NoSuchMethodError(var2.getMessage());\n } catch (ClassNotFoundException var3) {\n throw new NoClassDefFoundError(var3.getMessage());\n }\n }\n}\n\n```\n\n\n\n","slug":"理解Java中的动态代理Part1","published":1,"updated":"2022-04-20T09:35:50.864Z","layout":"post","photos":[],"link":"","_id":"cl27q3uvc001dn94ofer8g8p4","content":"<blockquote>\n<p>对Java动态代理的理解,有助于我们理解复杂框架的设计,是提高自身java水平的一个重要的点。<br> 像Spring和Hadoop等开源框架中都有使用动态代理。</p>\n</blockquote>\n<h5 id=\"代理模式\"><a href=\"#代理模式\" class=\"headerlink\" title=\"代理模式\"></a><strong>代理模式</strong></h5><p>首先说一下代理模式,它有一个subject(接口),一个RealSubject实现类和一个Proxy实现类。<br>客户端不会直接访问到RealSubject,只会访问Proxy对象,Proxy对象会有一个RealSubject对象<br>的实例作为成员变量,在subject定义的方法中做一些增强功能然后再调用RealSubject中实现的接口中<br>的方法。</p>\n<h5 id=\"为什么需要代理\"><a href=\"#为什么需要代理\" class=\"headerlink\" title=\"为什么需要代理?\"></a><strong>为什么需要代理?</strong></h5><p>这是不是多此一举,我们直接new RealSubject,然后调用方法不就可以吗?<br>是的,通常就是这么使用的,但是在框架的设计中,通常会有一些统一的统计或监控等工作,譬如记录<br>每一个方法执行的开始结束时间,运行时长和方法被调用时传入的参数等信息,你不用在每一个方法实现<br>中都加入这一段功能代码,你可以拦截方法的执行,在方法执行前后加一些统一的代码。</p>\n<h5 id=\"动态代理中的动态是什么意思?\"><a href=\"#动态代理中的动态是什么意思?\" class=\"headerlink\" title=\"动态代理中的动态是什么意思?\"></a><strong>动态代理中的动态是什么意思?</strong></h5><p>静态代理中代理类是在代码中提前写好的。而使用JDK反射包下的Proxy和InvocationHandler来实现的<br>代理中,代理类事先并不存在,是在运行过程中根据传入的类加载器和接口动态生成的字节码,然后加载成<br>类对象,并构造一个接口的实现类实例对象供客户端使用。</p>\n<h5 id=\"JAVA中的动态代理怎么使用?\"><a href=\"#JAVA中的动态代理怎么使用?\" class=\"headerlink\" title=\"JAVA中的动态代理怎么使用?\"></a><strong>JAVA中的动态代理怎么使用?</strong></h5><p>Java的反射包下面有两个类,Proxy和InvocationHandler接口。<br>InvocationHandler接口中有一个invoke方法,它接收一个方法元数据和方法参数数组,因为事先不知道<br>被代理对象的具体类型,所以这里使用反射来操作,要告诉它执行什么方法,参数是什么。<br>首先我们做代理的目标是要增强功能,我们把我们要增强的功能写在哪里?就是写在这个invoke方法里面。<br>所以我们需要写一个类来实现这个InvocationHandler接口,这个类需要有一个Object对象(如果不使用泛型)<br>的成员变量来表示被代理的类实例;提供一种对象的注入方式,譬如set或者构造器都可以。<br>然后在invoke方法中把我们的增强逻辑实现,\b然后需要写method.invoke(obj,args);obj就是被代理对象,<br>args就是传入的method方法的参数。<br>Proxy类是来做什么的呢?<br>按理说,有了实现InvocationHandler的实现类就可以使用了,但是这样使用起来太不方便。Proxy可以用来动<br>态生成一个代理类,生成的代理类可以转成被代理的接口对象。</p>\n<figure class=\"highlight java\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">Proxy.newProxyInstance(RealSubject.class.getClassLoader(),</span><br><span class=\"line\"> RealSubject.class.getInterfaces(),</span><br><span class=\"line\"> handler);</span><br></pre></td></tr></table></figure>\n<p>它根据传入的参数动态生成字节码,并命名为$proxy + num,这个类扩展了Proxy并实现了RealSubject实现的接口。<br>同时会有几个静态的static Method成员变量代表接口中的方法,后面供InvocationHandler的invoke方法使用。<br>Proxy类有一个构造器是接收InvocationHandler作为参数,所以newProxyInstance时传入的handler就会被传入<br>新生成的类中使用;同时它还会有RealSubject中要实现的方法,在这个方法中会调用handler的invoke方法。</p>\n<p>代码示例如下,你可以下面的代码中体会一下:</p>\n<figure class=\"highlight java\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br><span class=\"line\">11</span><br><span class=\"line\">12</span><br><span class=\"line\">13</span><br><span class=\"line\">14</span><br><span class=\"line\">15</span><br><span class=\"line\">16</span><br><span class=\"line\">17</span><br><span class=\"line\">18</span><br><span class=\"line\">19</span><br><span class=\"line\">20</span><br><span class=\"line\">21</span><br><span class=\"line\">22</span><br><span class=\"line\">23</span><br><span class=\"line\">24</span><br><span class=\"line\">25</span><br><span class=\"line\">26</span><br><span class=\"line\">27</span><br><span class=\"line\">28</span><br><span class=\"line\">29</span><br><span class=\"line\">30</span><br><span class=\"line\">31</span><br><span class=\"line\">32</span><br><span class=\"line\">33</span><br><span class=\"line\">34</span><br><span class=\"line\">35</span><br><span class=\"line\">36</span><br><span class=\"line\">37</span><br><span class=\"line\">38</span><br><span class=\"line\">39</span><br><span class=\"line\">40</span><br><span class=\"line\">41</span><br><span class=\"line\">42</span><br><span class=\"line\">43</span><br><span class=\"line\">44</span><br><span class=\"line\">45</span><br><span class=\"line\">46</span><br><span class=\"line\">47</span><br><span class=\"line\">48</span><br><span class=\"line\">49</span><br><span class=\"line\">50</span><br><span class=\"line\">51</span><br><span class=\"line\">52</span><br><span class=\"line\">53</span><br><span class=\"line\">54</span><br><span class=\"line\">55</span><br><span class=\"line\">56</span><br><span class=\"line\">57</span><br><span class=\"line\">58</span><br><span class=\"line\">59</span><br><span class=\"line\">60</span><br><span class=\"line\">61</span><br><span class=\"line\">62</span><br><span class=\"line\">63</span><br><span class=\"line\">64</span><br><span class=\"line\">65</span><br><span class=\"line\">66</span><br><span class=\"line\">67</span><br><span class=\"line\">68</span><br><span class=\"line\">69</span><br><span class=\"line\">70</span><br><span class=\"line\">71</span><br><span class=\"line\">72</span><br><span class=\"line\">73</span><br><span class=\"line\">74</span><br><span class=\"line\">75</span><br><span class=\"line\">76</span><br><span class=\"line\">77</span><br><span class=\"line\">78</span><br><span class=\"line\">79</span><br><span class=\"line\">80</span><br><span class=\"line\">81</span><br><span class=\"line\">82</span><br><span class=\"line\">83</span><br><span class=\"line\">84</span><br><span class=\"line\">85</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"keyword\">package</span> org.java;</span><br><span class=\"line\"></span><br><span class=\"line\"><span class=\"keyword\">import</span> sun.misc.ProxyGenerator;</span><br><span class=\"line\"><span class=\"keyword\">import</span> java.io.FileOutputStream;</span><br><span class=\"line\"><span class=\"keyword\">import</span> java.lang.reflect.InvocationHandler;</span><br><span class=\"line\"><span class=\"keyword\">import</span> java.lang.reflect.Method;</span><br><span class=\"line\"><span class=\"keyword\">import</span> java.lang.reflect.Proxy;</span><br><span class=\"line\"></span><br><span class=\"line\"></span><br><span class=\"line\"><span class=\"keyword\">public</span> <span class=\"keyword\">class</span> <span class=\"title class_\">Test</span> {</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"keyword\">public</span> <span class=\"keyword\">static</span> <span class=\"keyword\">void</span> <span class=\"title function_\">main</span><span class=\"params\">(String[] args)</span> {</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"type\">Add</span> <span class=\"variable\">adder</span> <span class=\"operator\">=</span> <span class=\"keyword\">new</span> <span class=\"title class_\">Adder</span>();</span><br><span class=\"line\"> <span class=\"type\">CalculateIH</span> <span class=\"variable\">handler</span> <span class=\"operator\">=</span> <span class=\"keyword\">new</span> <span class=\"title class_\">CalculateIH</span>(adder);</span><br><span class=\"line\"> <span class=\"type\">Add</span> <span class=\"variable\">adder1</span> <span class=\"operator\">=</span> (Add) Proxy.newProxyInstance(Adder.class.getClassLoader(),</span><br><span class=\"line\"> Adder.class.getInterfaces(),</span><br><span class=\"line\"> handler);</span><br><span class=\"line\"> System.out.println( adder1.add(<span class=\"number\">1</span>,<span class=\"number\">100</span>));</span><br><span class=\"line\"></span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"type\">Multiplyer</span> <span class=\"variable\">m</span> <span class=\"operator\">=</span> <span class=\"keyword\">new</span> <span class=\"title class_\">Multiplyer</span>();</span><br><span class=\"line\"> <span class=\"type\">CalculateIH</span> <span class=\"variable\">handler2</span> <span class=\"operator\">=</span> <span class=\"keyword\">new</span> <span class=\"title class_\">CalculateIH</span>(m);</span><br><span class=\"line\"> <span class=\"type\">multiply</span> <span class=\"variable\">multiply</span> <span class=\"operator\">=</span> (multiply) Proxy.newProxyInstance(Multiplyer.class.getClassLoader(),</span><br><span class=\"line\"> Multiplyer.class.getInterfaces(),</span><br><span class=\"line\"> handler2);</span><br><span class=\"line\"> System.out.println( multiply.multiply(<span class=\"number\">12</span>,<span class=\"number\">12</span>));</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"type\">byte</span>[] classFile = ProxyGenerator.generateProxyClass(<span class=\"string\">"$Proxy10"</span>, Adder.class.getInterfaces());</span><br><span class=\"line\"> <span class=\"type\">String</span> <span class=\"variable\">path</span> <span class=\"operator\">=</span> <span class=\"string\">"./AdderProxy.class"</span>;</span><br><span class=\"line\"> <span class=\"keyword\">try</span>(<span class=\"type\">FileOutputStream</span> <span class=\"variable\">fos</span> <span class=\"operator\">=</span> <span class=\"keyword\">new</span> <span class=\"title class_\">FileOutputStream</span>(path)) {</span><br><span class=\"line\"> fos.write(classFile);</span><br><span class=\"line\"> fos.flush();</span><br><span class=\"line\"> System.out.println(<span class=\"string\">"代理类class文件写入成功"</span>);</span><br><span class=\"line\"> } <span class=\"keyword\">catch</span> (Exception e) {</span><br><span class=\"line\"> e.printStackTrace();</span><br><span class=\"line\"> System.out.println(<span class=\"string\">"写文件错误"</span>);</span><br><span class=\"line\"> }</span><br><span class=\"line\"></span><br><span class=\"line\"> }</span><br><span class=\"line\"></span><br><span class=\"line\">}</span><br><span class=\"line\"></span><br><span class=\"line\"><span class=\"keyword\">interface</span> <span class=\"title class_\">Add</span> {</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"title function_\">add</span><span class=\"params\">(<span class=\"type\">int</span> add1, <span class=\"type\">int</span> add2)</span>;</span><br><span class=\"line\">}</span><br><span class=\"line\"></span><br><span class=\"line\"><span class=\"keyword\">class</span> <span class=\"title class_\">Adder</span> <span class=\"keyword\">implements</span> <span class=\"title class_\">Add</span> {</span><br><span class=\"line\"> <span class=\"meta\">@Override</span></span><br><span class=\"line\"> <span class=\"keyword\">public</span> <span class=\"type\">int</span> <span class=\"title function_\">add</span><span class=\"params\">(<span class=\"type\">int</span> add1, <span class=\"type\">int</span> add2)</span> {</span><br><span class=\"line\"> <span class=\"keyword\">return</span> add1 + add2;</span><br><span class=\"line\"> }</span><br><span class=\"line\">}</span><br><span class=\"line\"></span><br><span class=\"line\"><span class=\"keyword\">interface</span> <span class=\"title class_\">multiply</span> {</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"title function_\">multiply</span> <span class=\"params\">(<span class=\"type\">int</span> m1, <span class=\"type\">int</span> m2)</span>;</span><br><span class=\"line\">}</span><br><span class=\"line\"></span><br><span class=\"line\"><span class=\"keyword\">class</span> <span class=\"title class_\">Multiplyer</span> <span class=\"keyword\">implements</span> <span class=\"title class_\">multiply</span> {</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"meta\">@Override</span></span><br><span class=\"line\"> <span class=\"keyword\">public</span> <span class=\"type\">int</span> <span class=\"title function_\">multiply</span><span class=\"params\">(<span class=\"type\">int</span> m1, <span class=\"type\">int</span> m2)</span> {</span><br><span class=\"line\"> <span class=\"keyword\">return</span> m1 * m2;</span><br><span class=\"line\"> }</span><br><span class=\"line\">}</span><br><span class=\"line\"></span><br><span class=\"line\"><span class=\"keyword\">class</span> <span class=\"title class_\">CalculateIH</span> <span class=\"keyword\">implements</span> <span class=\"title class_\">InvocationHandler</span> {</span><br><span class=\"line\"></span><br><span class=\"line\"> Object obj;</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"keyword\">public</span> <span class=\"title function_\">CalculateIH</span><span class=\"params\">(Object obj)</span> {</span><br><span class=\"line\"> <span class=\"built_in\">this</span>.obj = obj;</span><br><span class=\"line\"> }</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"meta\">@Override</span></span><br><span class=\"line\"> <span class=\"keyword\">public</span> Object <span class=\"title function_\">invoke</span><span class=\"params\">(Object proxy, Method method, Object[] args)</span> <span class=\"keyword\">throws</span> Throwable {</span><br><span class=\"line\"></span><br><span class=\"line\"> System.out.println(<span class=\"string\">"start at : "</span> + System.nanoTime());</span><br><span class=\"line\"> <span class=\"type\">Object</span> <span class=\"variable\">ret</span> <span class=\"operator\">=</span> method.invoke(obj, args);</span><br><span class=\"line\"> System.out.println(<span class=\"string\">"executed result is "</span> + ret);</span><br><span class=\"line\"> System.out.println(<span class=\"string\">"end at : "</span> + System.nanoTime());</span><br><span class=\"line\"> <span class=\"keyword\">return</span> ret;</span><br><span class=\"line\"> }</span><br><span class=\"line\">}</span><br><span class=\"line\"></span><br></pre></td></tr></table></figure>\n<p>以下是对生成的代理类反编译的java代码:</p>\n<figure class=\"highlight java\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br><span class=\"line\">11</span><br><span class=\"line\">12</span><br><span class=\"line\">13</span><br><span class=\"line\">14</span><br><span class=\"line\">15</span><br><span class=\"line\">16</span><br><span class=\"line\">17</span><br><span class=\"line\">18</span><br><span class=\"line\">19</span><br><span class=\"line\">20</span><br><span class=\"line\">21</span><br><span class=\"line\">22</span><br><span class=\"line\">23</span><br><span class=\"line\">24</span><br><span class=\"line\">25</span><br><span class=\"line\">26</span><br><span class=\"line\">27</span><br><span class=\"line\">28</span><br><span class=\"line\">29</span><br><span class=\"line\">30</span><br><span class=\"line\">31</span><br><span class=\"line\">32</span><br><span class=\"line\">33</span><br><span class=\"line\">34</span><br><span class=\"line\">35</span><br><span class=\"line\">36</span><br><span class=\"line\">37</span><br><span class=\"line\">38</span><br><span class=\"line\">39</span><br><span class=\"line\">40</span><br><span class=\"line\">41</span><br><span class=\"line\">42</span><br><span class=\"line\">43</span><br><span class=\"line\">44</span><br><span class=\"line\">45</span><br><span class=\"line\">46</span><br><span class=\"line\">47</span><br><span class=\"line\">48</span><br><span class=\"line\">49</span><br><span class=\"line\">50</span><br><span class=\"line\">51</span><br><span class=\"line\">52</span><br><span class=\"line\">53</span><br><span class=\"line\">54</span><br><span class=\"line\">55</span><br><span class=\"line\">56</span><br><span class=\"line\">57</span><br><span class=\"line\">58</span><br><span class=\"line\">59</span><br><span class=\"line\">60</span><br><span class=\"line\">61</span><br><span class=\"line\">62</span><br><span class=\"line\">63</span><br><span class=\"line\">64</span><br><span class=\"line\">65</span><br><span class=\"line\">66</span><br><span class=\"line\">67</span><br><span class=\"line\">68</span><br><span class=\"line\">69</span><br><span class=\"line\">70</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"keyword\">import</span> java.lang.reflect.InvocationHandler;</span><br><span class=\"line\"><span class=\"keyword\">import</span> java.lang.reflect.Method;</span><br><span class=\"line\"><span class=\"keyword\">import</span> java.lang.reflect.Proxy;</span><br><span class=\"line\"><span class=\"keyword\">import</span> java.lang.reflect.UndeclaredThrowableException;</span><br><span class=\"line\"><span class=\"keyword\">import</span> org.java.Add;</span><br><span class=\"line\"></span><br><span class=\"line\"><span class=\"keyword\">public</span> <span class=\"keyword\">final</span> <span class=\"keyword\">class</span> <span class=\"title class_\">$Proxy10</span> <span class=\"keyword\">extends</span> <span class=\"title class_\">Proxy</span> <span class=\"keyword\">implements</span> <span class=\"title class_\">Add</span> {</span><br><span class=\"line\"> <span class=\"keyword\">private</span> <span class=\"keyword\">static</span> Method m1;</span><br><span class=\"line\"> <span class=\"keyword\">private</span> <span class=\"keyword\">static</span> Method m2;</span><br><span class=\"line\"> <span class=\"keyword\">private</span> <span class=\"keyword\">static</span> Method m3;</span><br><span class=\"line\"> <span class=\"keyword\">private</span> <span class=\"keyword\">static</span> Method m0;</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"keyword\">public</span> $Proxy10(InvocationHandler var1) <span class=\"keyword\">throws</span> {</span><br><span class=\"line\"> <span class=\"built_in\">super</span>(var1);</span><br><span class=\"line\"> }</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"keyword\">public</span> <span class=\"keyword\">final</span> <span class=\"type\">boolean</span> <span class=\"title function_\">equals</span><span class=\"params\">(Object var1)</span> <span class=\"keyword\">throws</span> {</span><br><span class=\"line\"> <span class=\"keyword\">try</span> {</span><br><span class=\"line\"> <span class=\"keyword\">return</span> (Boolean)<span class=\"built_in\">super</span>.h.invoke(<span class=\"built_in\">this</span>, m1, <span class=\"keyword\">new</span> <span class=\"title class_\">Object</span>[]{var1});</span><br><span class=\"line\"> } <span class=\"keyword\">catch</span> (RuntimeException | Error var3) {</span><br><span class=\"line\"> <span class=\"keyword\">throw</span> var3;</span><br><span class=\"line\"> } <span class=\"keyword\">catch</span> (Throwable var4) {</span><br><span class=\"line\"> <span class=\"keyword\">throw</span> <span class=\"keyword\">new</span> <span class=\"title class_\">UndeclaredThrowableException</span>(var4);</span><br><span class=\"line\"> }</span><br><span class=\"line\"> }</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"keyword\">public</span> <span class=\"keyword\">final</span> String <span class=\"title function_\">toString</span><span class=\"params\">()</span> <span class=\"keyword\">throws</span> {</span><br><span class=\"line\"> <span class=\"keyword\">try</span> {</span><br><span class=\"line\"> <span class=\"keyword\">return</span> (String)<span class=\"built_in\">super</span>.h.invoke(<span class=\"built_in\">this</span>, m2, (Object[])<span class=\"literal\">null</span>);</span><br><span class=\"line\"> } <span class=\"keyword\">catch</span> (RuntimeException | Error var2) {</span><br><span class=\"line\"> <span class=\"keyword\">throw</span> var2;</span><br><span class=\"line\"> } <span class=\"keyword\">catch</span> (Throwable var3) {</span><br><span class=\"line\"> <span class=\"keyword\">throw</span> <span class=\"keyword\">new</span> <span class=\"title class_\">UndeclaredThrowableException</span>(var3);</span><br><span class=\"line\"> }</span><br><span class=\"line\"> }</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"keyword\">public</span> <span class=\"keyword\">final</span> <span class=\"type\">int</span> <span class=\"title function_\">add</span><span class=\"params\">(<span class=\"type\">int</span> var1, <span class=\"type\">int</span> var2)</span> <span class=\"keyword\">throws</span> {</span><br><span class=\"line\"> <span class=\"keyword\">try</span> {</span><br><span class=\"line\"> <span class=\"keyword\">return</span> (Integer)<span class=\"built_in\">super</span>.h.invoke(<span class=\"built_in\">this</span>, m3, <span class=\"keyword\">new</span> <span class=\"title class_\">Object</span>[]{var1, var2});</span><br><span class=\"line\"> } <span class=\"keyword\">catch</span> (RuntimeException | Error var4) {</span><br><span class=\"line\"> <span class=\"keyword\">throw</span> var4;</span><br><span class=\"line\"> } <span class=\"keyword\">catch</span> (Throwable var5) {</span><br><span class=\"line\"> <span class=\"keyword\">throw</span> <span class=\"keyword\">new</span> <span class=\"title class_\">UndeclaredThrowableException</span>(var5);</span><br><span class=\"line\"> }</span><br><span class=\"line\"> }</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"keyword\">public</span> <span class=\"keyword\">final</span> <span class=\"type\">int</span> <span class=\"title function_\">hashCode</span><span class=\"params\">()</span> <span class=\"keyword\">throws</span> {</span><br><span class=\"line\"> <span class=\"keyword\">try</span> {</span><br><span class=\"line\"> <span class=\"keyword\">return</span> (Integer)<span class=\"built_in\">super</span>.h.invoke(<span class=\"built_in\">this</span>, m0, (Object[])<span class=\"literal\">null</span>);</span><br><span class=\"line\"> } <span class=\"keyword\">catch</span> (RuntimeException | Error var2) {</span><br><span class=\"line\"> <span class=\"keyword\">throw</span> var2;</span><br><span class=\"line\"> } <span class=\"keyword\">catch</span> (Throwable var3) {</span><br><span class=\"line\"> <span class=\"keyword\">throw</span> <span class=\"keyword\">new</span> <span class=\"title class_\">UndeclaredThrowableException</span>(var3);</span><br><span class=\"line\"> }</span><br><span class=\"line\"> }</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"keyword\">static</span> {</span><br><span class=\"line\"> <span class=\"keyword\">try</span> {</span><br><span class=\"line\"> m1 = Class.forName(<span class=\"string\">"java.lang.Object"</span>).getMethod(<span class=\"string\">"equals"</span>, Class.forName(<span class=\"string\">"java.lang.Object"</span>));</span><br><span class=\"line\"> m2 = Class.forName(<span class=\"string\">"java.lang.Object"</span>).getMethod(<span class=\"string\">"toString"</span>);</span><br><span class=\"line\"> m3 = Class.forName(<span class=\"string\">"org.java.Add"</span>).getMethod(<span class=\"string\">"add"</span>, Integer.TYPE, Integer.TYPE);</span><br><span class=\"line\"> m0 = Class.forName(<span class=\"string\">"java.lang.Object"</span>).getMethod(<span class=\"string\">"hashCode"</span>);</span><br><span class=\"line\"> } <span class=\"keyword\">catch</span> (NoSuchMethodException var2) {</span><br><span class=\"line\"> <span class=\"keyword\">throw</span> <span class=\"keyword\">new</span> <span class=\"title class_\">NoSuchMethodError</span>(var2.getMessage());</span><br><span class=\"line\"> } <span class=\"keyword\">catch</span> (ClassNotFoundException var3) {</span><br><span class=\"line\"> <span class=\"keyword\">throw</span> <span class=\"keyword\">new</span> <span class=\"title class_\">NoClassDefFoundError</span>(var3.getMessage());</span><br><span class=\"line\"> }</span><br><span class=\"line\"> }</span><br><span class=\"line\">}</span><br><span class=\"line\"></span><br></pre></td></tr></table></figure>\n\n\n\n","site":{"data":{}},"excerpt":"","more":"<blockquote>\n<p>对Java动态代理的理解,有助于我们理解复杂框架的设计,是提高自身java水平的一个重要的点。<br> 像Spring和Hadoop等开源框架中都有使用动态代理。</p>\n</blockquote>\n<h5 id=\"代理模式\"><a href=\"#代理模式\" class=\"headerlink\" title=\"代理模式\"></a><strong>代理模式</strong></h5><p>首先说一下代理模式,它有一个subject(接口),一个RealSubject实现类和一个Proxy实现类。<br>客户端不会直接访问到RealSubject,只会访问Proxy对象,Proxy对象会有一个RealSubject对象<br>的实例作为成员变量,在subject定义的方法中做一些增强功能然后再调用RealSubject中实现的接口中<br>的方法。</p>\n<h5 id=\"为什么需要代理\"><a href=\"#为什么需要代理\" class=\"headerlink\" title=\"为什么需要代理?\"></a><strong>为什么需要代理?</strong></h5><p>这是不是多此一举,我们直接new RealSubject,然后调用方法不就可以吗?<br>是的,通常就是这么使用的,但是在框架的设计中,通常会有一些统一的统计或监控等工作,譬如记录<br>每一个方法执行的开始结束时间,运行时长和方法被调用时传入的参数等信息,你不用在每一个方法实现<br>中都加入这一段功能代码,你可以拦截方法的执行,在方法执行前后加一些统一的代码。</p>\n<h5 id=\"动态代理中的动态是什么意思?\"><a href=\"#动态代理中的动态是什么意思?\" class=\"headerlink\" title=\"动态代理中的动态是什么意思?\"></a><strong>动态代理中的动态是什么意思?</strong></h5><p>静态代理中代理类是在代码中提前写好的。而使用JDK反射包下的Proxy和InvocationHandler来实现的<br>代理中,代理类事先并不存在,是在运行过程中根据传入的类加载器和接口动态生成的字节码,然后加载成<br>类对象,并构造一个接口的实现类实例对象供客户端使用。</p>\n<h5 id=\"JAVA中的动态代理怎么使用?\"><a href=\"#JAVA中的动态代理怎么使用?\" class=\"headerlink\" title=\"JAVA中的动态代理怎么使用?\"></a><strong>JAVA中的动态代理怎么使用?</strong></h5><p>Java的反射包下面有两个类,Proxy和InvocationHandler接口。<br>InvocationHandler接口中有一个invoke方法,它接收一个方法元数据和方法参数数组,因为事先不知道<br>被代理对象的具体类型,所以这里使用反射来操作,要告诉它执行什么方法,参数是什么。<br>首先我们做代理的目标是要增强功能,我们把我们要增强的功能写在哪里?就是写在这个invoke方法里面。<br>所以我们需要写一个类来实现这个InvocationHandler接口,这个类需要有一个Object对象(如果不使用泛型)<br>的成员变量来表示被代理的类实例;提供一种对象的注入方式,譬如set或者构造器都可以。<br>然后在invoke方法中把我们的增强逻辑实现,\b然后需要写method.invoke(obj,args);obj就是被代理对象,<br>args就是传入的method方法的参数。<br>Proxy类是来做什么的呢?<br>按理说,有了实现InvocationHandler的实现类就可以使用了,但是这样使用起来太不方便。Proxy可以用来动<br>态生成一个代理类,生成的代理类可以转成被代理的接口对象。</p>\n<figure class=\"highlight java\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">Proxy.newProxyInstance(RealSubject.class.getClassLoader(),</span><br><span class=\"line\"> RealSubject.class.getInterfaces(),</span><br><span class=\"line\"> handler);</span><br></pre></td></tr></table></figure>\n<p>它根据传入的参数动态生成字节码,并命名为$proxy + num,这个类扩展了Proxy并实现了RealSubject实现的接口。<br>同时会有几个静态的static Method成员变量代表接口中的方法,后面供InvocationHandler的invoke方法使用。<br>Proxy类有一个构造器是接收InvocationHandler作为参数,所以newProxyInstance时传入的handler就会被传入<br>新生成的类中使用;同时它还会有RealSubject中要实现的方法,在这个方法中会调用handler的invoke方法。</p>\n<p>代码示例如下,你可以下面的代码中体会一下:</p>\n<figure class=\"highlight java\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br><span class=\"line\">11</span><br><span class=\"line\">12</span><br><span class=\"line\">13</span><br><span class=\"line\">14</span><br><span class=\"line\">15</span><br><span class=\"line\">16</span><br><span class=\"line\">17</span><br><span class=\"line\">18</span><br><span class=\"line\">19</span><br><span class=\"line\">20</span><br><span class=\"line\">21</span><br><span class=\"line\">22</span><br><span class=\"line\">23</span><br><span class=\"line\">24</span><br><span class=\"line\">25</span><br><span class=\"line\">26</span><br><span class=\"line\">27</span><br><span class=\"line\">28</span><br><span class=\"line\">29</span><br><span class=\"line\">30</span><br><span class=\"line\">31</span><br><span class=\"line\">32</span><br><span class=\"line\">33</span><br><span class=\"line\">34</span><br><span class=\"line\">35</span><br><span class=\"line\">36</span><br><span class=\"line\">37</span><br><span class=\"line\">38</span><br><span class=\"line\">39</span><br><span class=\"line\">40</span><br><span class=\"line\">41</span><br><span class=\"line\">42</span><br><span class=\"line\">43</span><br><span class=\"line\">44</span><br><span class=\"line\">45</span><br><span class=\"line\">46</span><br><span class=\"line\">47</span><br><span class=\"line\">48</span><br><span class=\"line\">49</span><br><span class=\"line\">50</span><br><span class=\"line\">51</span><br><span class=\"line\">52</span><br><span class=\"line\">53</span><br><span class=\"line\">54</span><br><span class=\"line\">55</span><br><span class=\"line\">56</span><br><span class=\"line\">57</span><br><span class=\"line\">58</span><br><span class=\"line\">59</span><br><span class=\"line\">60</span><br><span class=\"line\">61</span><br><span class=\"line\">62</span><br><span class=\"line\">63</span><br><span class=\"line\">64</span><br><span class=\"line\">65</span><br><span class=\"line\">66</span><br><span class=\"line\">67</span><br><span class=\"line\">68</span><br><span class=\"line\">69</span><br><span class=\"line\">70</span><br><span class=\"line\">71</span><br><span class=\"line\">72</span><br><span class=\"line\">73</span><br><span class=\"line\">74</span><br><span class=\"line\">75</span><br><span class=\"line\">76</span><br><span class=\"line\">77</span><br><span class=\"line\">78</span><br><span class=\"line\">79</span><br><span class=\"line\">80</span><br><span class=\"line\">81</span><br><span class=\"line\">82</span><br><span class=\"line\">83</span><br><span class=\"line\">84</span><br><span class=\"line\">85</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"keyword\">package</span> org.java;</span><br><span class=\"line\"></span><br><span class=\"line\"><span class=\"keyword\">import</span> sun.misc.ProxyGenerator;</span><br><span class=\"line\"><span class=\"keyword\">import</span> java.io.FileOutputStream;</span><br><span class=\"line\"><span class=\"keyword\">import</span> java.lang.reflect.InvocationHandler;</span><br><span class=\"line\"><span class=\"keyword\">import</span> java.lang.reflect.Method;</span><br><span class=\"line\"><span class=\"keyword\">import</span> java.lang.reflect.Proxy;</span><br><span class=\"line\"></span><br><span class=\"line\"></span><br><span class=\"line\"><span class=\"keyword\">public</span> <span class=\"keyword\">class</span> <span class=\"title class_\">Test</span> {</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"keyword\">public</span> <span class=\"keyword\">static</span> <span class=\"keyword\">void</span> <span class=\"title function_\">main</span><span class=\"params\">(String[] args)</span> {</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"type\">Add</span> <span class=\"variable\">adder</span> <span class=\"operator\">=</span> <span class=\"keyword\">new</span> <span class=\"title class_\">Adder</span>();</span><br><span class=\"line\"> <span class=\"type\">CalculateIH</span> <span class=\"variable\">handler</span> <span class=\"operator\">=</span> <span class=\"keyword\">new</span> <span class=\"title class_\">CalculateIH</span>(adder);</span><br><span class=\"line\"> <span class=\"type\">Add</span> <span class=\"variable\">adder1</span> <span class=\"operator\">=</span> (Add) Proxy.newProxyInstance(Adder.class.getClassLoader(),</span><br><span class=\"line\"> Adder.class.getInterfaces(),</span><br><span class=\"line\"> handler);</span><br><span class=\"line\"> System.out.println( adder1.add(<span class=\"number\">1</span>,<span class=\"number\">100</span>));</span><br><span class=\"line\"></span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"type\">Multiplyer</span> <span class=\"variable\">m</span> <span class=\"operator\">=</span> <span class=\"keyword\">new</span> <span class=\"title class_\">Multiplyer</span>();</span><br><span class=\"line\"> <span class=\"type\">CalculateIH</span> <span class=\"variable\">handler2</span> <span class=\"operator\">=</span> <span class=\"keyword\">new</span> <span class=\"title class_\">CalculateIH</span>(m);</span><br><span class=\"line\"> <span class=\"type\">multiply</span> <span class=\"variable\">multiply</span> <span class=\"operator\">=</span> (multiply) Proxy.newProxyInstance(Multiplyer.class.getClassLoader(),</span><br><span class=\"line\"> Multiplyer.class.getInterfaces(),</span><br><span class=\"line\"> handler2);</span><br><span class=\"line\"> System.out.println( multiply.multiply(<span class=\"number\">12</span>,<span class=\"number\">12</span>));</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"type\">byte</span>[] classFile = ProxyGenerator.generateProxyClass(<span class=\"string\">"$Proxy10"</span>, Adder.class.getInterfaces());</span><br><span class=\"line\"> <span class=\"type\">String</span> <span class=\"variable\">path</span> <span class=\"operator\">=</span> <span class=\"string\">"./AdderProxy.class"</span>;</span><br><span class=\"line\"> <span class=\"keyword\">try</span>(<span class=\"type\">FileOutputStream</span> <span class=\"variable\">fos</span> <span class=\"operator\">=</span> <span class=\"keyword\">new</span> <span class=\"title class_\">FileOutputStream</span>(path)) {</span><br><span class=\"line\"> fos.write(classFile);</span><br><span class=\"line\"> fos.flush();</span><br><span class=\"line\"> System.out.println(<span class=\"string\">"代理类class文件写入成功"</span>);</span><br><span class=\"line\"> } <span class=\"keyword\">catch</span> (Exception e) {</span><br><span class=\"line\"> e.printStackTrace();</span><br><span class=\"line\"> System.out.println(<span class=\"string\">"写文件错误"</span>);</span><br><span class=\"line\"> }</span><br><span class=\"line\"></span><br><span class=\"line\"> }</span><br><span class=\"line\"></span><br><span class=\"line\">}</span><br><span class=\"line\"></span><br><span class=\"line\"><span class=\"keyword\">interface</span> <span class=\"title class_\">Add</span> {</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"title function_\">add</span><span class=\"params\">(<span class=\"type\">int</span> add1, <span class=\"type\">int</span> add2)</span>;</span><br><span class=\"line\">}</span><br><span class=\"line\"></span><br><span class=\"line\"><span class=\"keyword\">class</span> <span class=\"title class_\">Adder</span> <span class=\"keyword\">implements</span> <span class=\"title class_\">Add</span> {</span><br><span class=\"line\"> <span class=\"meta\">@Override</span></span><br><span class=\"line\"> <span class=\"keyword\">public</span> <span class=\"type\">int</span> <span class=\"title function_\">add</span><span class=\"params\">(<span class=\"type\">int</span> add1, <span class=\"type\">int</span> add2)</span> {</span><br><span class=\"line\"> <span class=\"keyword\">return</span> add1 + add2;</span><br><span class=\"line\"> }</span><br><span class=\"line\">}</span><br><span class=\"line\"></span><br><span class=\"line\"><span class=\"keyword\">interface</span> <span class=\"title class_\">multiply</span> {</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"title function_\">multiply</span> <span class=\"params\">(<span class=\"type\">int</span> m1, <span class=\"type\">int</span> m2)</span>;</span><br><span class=\"line\">}</span><br><span class=\"line\"></span><br><span class=\"line\"><span class=\"keyword\">class</span> <span class=\"title class_\">Multiplyer</span> <span class=\"keyword\">implements</span> <span class=\"title class_\">multiply</span> {</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"meta\">@Override</span></span><br><span class=\"line\"> <span class=\"keyword\">public</span> <span class=\"type\">int</span> <span class=\"title function_\">multiply</span><span class=\"params\">(<span class=\"type\">int</span> m1, <span class=\"type\">int</span> m2)</span> {</span><br><span class=\"line\"> <span class=\"keyword\">return</span> m1 * m2;</span><br><span class=\"line\"> }</span><br><span class=\"line\">}</span><br><span class=\"line\"></span><br><span class=\"line\"><span class=\"keyword\">class</span> <span class=\"title class_\">CalculateIH</span> <span class=\"keyword\">implements</span> <span class=\"title class_\">InvocationHandler</span> {</span><br><span class=\"line\"></span><br><span class=\"line\"> Object obj;</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"keyword\">public</span> <span class=\"title function_\">CalculateIH</span><span class=\"params\">(Object obj)</span> {</span><br><span class=\"line\"> <span class=\"built_in\">this</span>.obj = obj;</span><br><span class=\"line\"> }</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"meta\">@Override</span></span><br><span class=\"line\"> <span class=\"keyword\">public</span> Object <span class=\"title function_\">invoke</span><span class=\"params\">(Object proxy, Method method, Object[] args)</span> <span class=\"keyword\">throws</span> Throwable {</span><br><span class=\"line\"></span><br><span class=\"line\"> System.out.println(<span class=\"string\">"start at : "</span> + System.nanoTime());</span><br><span class=\"line\"> <span class=\"type\">Object</span> <span class=\"variable\">ret</span> <span class=\"operator\">=</span> method.invoke(obj, args);</span><br><span class=\"line\"> System.out.println(<span class=\"string\">"executed result is "</span> + ret);</span><br><span class=\"line\"> System.out.println(<span class=\"string\">"end at : "</span> + System.nanoTime());</span><br><span class=\"line\"> <span class=\"keyword\">return</span> ret;</span><br><span class=\"line\"> }</span><br><span class=\"line\">}</span><br><span class=\"line\"></span><br></pre></td></tr></table></figure>\n<p>以下是对生成的代理类反编译的java代码:</p>\n<figure class=\"highlight java\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br><span class=\"line\">11</span><br><span class=\"line\">12</span><br><span class=\"line\">13</span><br><span class=\"line\">14</span><br><span class=\"line\">15</span><br><span class=\"line\">16</span><br><span class=\"line\">17</span><br><span class=\"line\">18</span><br><span class=\"line\">19</span><br><span class=\"line\">20</span><br><span class=\"line\">21</span><br><span class=\"line\">22</span><br><span class=\"line\">23</span><br><span class=\"line\">24</span><br><span class=\"line\">25</span><br><span class=\"line\">26</span><br><span class=\"line\">27</span><br><span class=\"line\">28</span><br><span class=\"line\">29</span><br><span class=\"line\">30</span><br><span class=\"line\">31</span><br><span class=\"line\">32</span><br><span class=\"line\">33</span><br><span class=\"line\">34</span><br><span class=\"line\">35</span><br><span class=\"line\">36</span><br><span class=\"line\">37</span><br><span class=\"line\">38</span><br><span class=\"line\">39</span><br><span class=\"line\">40</span><br><span class=\"line\">41</span><br><span class=\"line\">42</span><br><span class=\"line\">43</span><br><span class=\"line\">44</span><br><span class=\"line\">45</span><br><span class=\"line\">46</span><br><span class=\"line\">47</span><br><span class=\"line\">48</span><br><span class=\"line\">49</span><br><span class=\"line\">50</span><br><span class=\"line\">51</span><br><span class=\"line\">52</span><br><span class=\"line\">53</span><br><span class=\"line\">54</span><br><span class=\"line\">55</span><br><span class=\"line\">56</span><br><span class=\"line\">57</span><br><span class=\"line\">58</span><br><span class=\"line\">59</span><br><span class=\"line\">60</span><br><span class=\"line\">61</span><br><span class=\"line\">62</span><br><span class=\"line\">63</span><br><span class=\"line\">64</span><br><span class=\"line\">65</span><br><span class=\"line\">66</span><br><span class=\"line\">67</span><br><span class=\"line\">68</span><br><span class=\"line\">69</span><br><span class=\"line\">70</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"keyword\">import</span> java.lang.reflect.InvocationHandler;</span><br><span class=\"line\"><span class=\"keyword\">import</span> java.lang.reflect.Method;</span><br><span class=\"line\"><span class=\"keyword\">import</span> java.lang.reflect.Proxy;</span><br><span class=\"line\"><span class=\"keyword\">import</span> java.lang.reflect.UndeclaredThrowableException;</span><br><span class=\"line\"><span class=\"keyword\">import</span> org.java.Add;</span><br><span class=\"line\"></span><br><span class=\"line\"><span class=\"keyword\">public</span> <span class=\"keyword\">final</span> <span class=\"keyword\">class</span> <span class=\"title class_\">$Proxy10</span> <span class=\"keyword\">extends</span> <span class=\"title class_\">Proxy</span> <span class=\"keyword\">implements</span> <span class=\"title class_\">Add</span> {</span><br><span class=\"line\"> <span class=\"keyword\">private</span> <span class=\"keyword\">static</span> Method m1;</span><br><span class=\"line\"> <span class=\"keyword\">private</span> <span class=\"keyword\">static</span> Method m2;</span><br><span class=\"line\"> <span class=\"keyword\">private</span> <span class=\"keyword\">static</span> Method m3;</span><br><span class=\"line\"> <span class=\"keyword\">private</span> <span class=\"keyword\">static</span> Method m0;</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"keyword\">public</span> $Proxy10(InvocationHandler var1) <span class=\"keyword\">throws</span> {</span><br><span class=\"line\"> <span class=\"built_in\">super</span>(var1);</span><br><span class=\"line\"> }</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"keyword\">public</span> <span class=\"keyword\">final</span> <span class=\"type\">boolean</span> <span class=\"title function_\">equals</span><span class=\"params\">(Object var1)</span> <span class=\"keyword\">throws</span> {</span><br><span class=\"line\"> <span class=\"keyword\">try</span> {</span><br><span class=\"line\"> <span class=\"keyword\">return</span> (Boolean)<span class=\"built_in\">super</span>.h.invoke(<span class=\"built_in\">this</span>, m1, <span class=\"keyword\">new</span> <span class=\"title class_\">Object</span>[]{var1});</span><br><span class=\"line\"> } <span class=\"keyword\">catch</span> (RuntimeException | Error var3) {</span><br><span class=\"line\"> <span class=\"keyword\">throw</span> var3;</span><br><span class=\"line\"> } <span class=\"keyword\">catch</span> (Throwable var4) {</span><br><span class=\"line\"> <span class=\"keyword\">throw</span> <span class=\"keyword\">new</span> <span class=\"title class_\">UndeclaredThrowableException</span>(var4);</span><br><span class=\"line\"> }</span><br><span class=\"line\"> }</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"keyword\">public</span> <span class=\"keyword\">final</span> String <span class=\"title function_\">toString</span><span class=\"params\">()</span> <span class=\"keyword\">throws</span> {</span><br><span class=\"line\"> <span class=\"keyword\">try</span> {</span><br><span class=\"line\"> <span class=\"keyword\">return</span> (String)<span class=\"built_in\">super</span>.h.invoke(<span class=\"built_in\">this</span>, m2, (Object[])<span class=\"literal\">null</span>);</span><br><span class=\"line\"> } <span class=\"keyword\">catch</span> (RuntimeException | Error var2) {</span><br><span class=\"line\"> <span class=\"keyword\">throw</span> var2;</span><br><span class=\"line\"> } <span class=\"keyword\">catch</span> (Throwable var3) {</span><br><span class=\"line\"> <span class=\"keyword\">throw</span> <span class=\"keyword\">new</span> <span class=\"title class_\">UndeclaredThrowableException</span>(var3);</span><br><span class=\"line\"> }</span><br><span class=\"line\"> }</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"keyword\">public</span> <span class=\"keyword\">final</span> <span class=\"type\">int</span> <span class=\"title function_\">add</span><span class=\"params\">(<span class=\"type\">int</span> var1, <span class=\"type\">int</span> var2)</span> <span class=\"keyword\">throws</span> {</span><br><span class=\"line\"> <span class=\"keyword\">try</span> {</span><br><span class=\"line\"> <span class=\"keyword\">return</span> (Integer)<span class=\"built_in\">super</span>.h.invoke(<span class=\"built_in\">this</span>, m3, <span class=\"keyword\">new</span> <span class=\"title class_\">Object</span>[]{var1, var2});</span><br><span class=\"line\"> } <span class=\"keyword\">catch</span> (RuntimeException | Error var4) {</span><br><span class=\"line\"> <span class=\"keyword\">throw</span> var4;</span><br><span class=\"line\"> } <span class=\"keyword\">catch</span> (Throwable var5) {</span><br><span class=\"line\"> <span class=\"keyword\">throw</span> <span class=\"keyword\">new</span> <span class=\"title class_\">UndeclaredThrowableException</span>(var5);</span><br><span class=\"line\"> }</span><br><span class=\"line\"> }</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"keyword\">public</span> <span class=\"keyword\">final</span> <span class=\"type\">int</span> <span class=\"title function_\">hashCode</span><span class=\"params\">()</span> <span class=\"keyword\">throws</span> {</span><br><span class=\"line\"> <span class=\"keyword\">try</span> {</span><br><span class=\"line\"> <span class=\"keyword\">return</span> (Integer)<span class=\"built_in\">super</span>.h.invoke(<span class=\"built_in\">this</span>, m0, (Object[])<span class=\"literal\">null</span>);</span><br><span class=\"line\"> } <span class=\"keyword\">catch</span> (RuntimeException | Error var2) {</span><br><span class=\"line\"> <span class=\"keyword\">throw</span> var2;</span><br><span class=\"line\"> } <span class=\"keyword\">catch</span> (Throwable var3) {</span><br><span class=\"line\"> <span class=\"keyword\">throw</span> <span class=\"keyword\">new</span> <span class=\"title class_\">UndeclaredThrowableException</span>(var3);</span><br><span class=\"line\"> }</span><br><span class=\"line\"> }</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"keyword\">static</span> {</span><br><span class=\"line\"> <span class=\"keyword\">try</span> {</span><br><span class=\"line\"> m1 = Class.forName(<span class=\"string\">"java.lang.Object"</span>).getMethod(<span class=\"string\">"equals"</span>, Class.forName(<span class=\"string\">"java.lang.Object"</span>));</span><br><span class=\"line\"> m2 = Class.forName(<span class=\"string\">"java.lang.Object"</span>).getMethod(<span class=\"string\">"toString"</span>);</span><br><span class=\"line\"> m3 = Class.forName(<span class=\"string\">"org.java.Add"</span>).getMethod(<span class=\"string\">"add"</span>, Integer.TYPE, Integer.TYPE);</span><br><span class=\"line\"> m0 = Class.forName(<span class=\"string\">"java.lang.Object"</span>).getMethod(<span class=\"string\">"hashCode"</span>);</span><br><span class=\"line\"> } <span class=\"keyword\">catch</span> (NoSuchMethodException var2) {</span><br><span class=\"line\"> <span class=\"keyword\">throw</span> <span class=\"keyword\">new</span> <span class=\"title class_\">NoSuchMethodError</span>(var2.getMessage());</span><br><span class=\"line\"> } <span class=\"keyword\">catch</span> (ClassNotFoundException var3) {</span><br><span class=\"line\"> <span class=\"keyword\">throw</span> <span class=\"keyword\">new</span> <span class=\"title class_\">NoClassDefFoundError</span>(var3.getMessage());</span><br><span class=\"line\"> }</span><br><span class=\"line\"> }</span><br><span class=\"line\">}</span><br><span class=\"line\"></span><br></pre></td></tr></table></figure>\n\n\n\n"},{"title":"经典算法之划分","date":"2021-07-21T00:56:15.000Z","comments":1,"toc":true,"_content":"#### 经典算法之划分\n这个划分可以理解为经典算法快排里面的partition,快排里面的划分,一次操作,可以实现把数组划分为两块,\n一块数据大于pivot,一块数据小于pivot。\n我们对数据操作的时候,经常会有类似的需求,譬如把数组中的满足特定条件的数据移到后面或前面。例如\n1.把数组中的非0元素移到前面,即0元素全部放到尾部,非零元素的位置相对不变。\n2.把数组中的奇数放到前面,偶数放到后面\n这两种场景都算划分,本质上是一样的,满足某种条件之后进行交换。\n所以今天借用快排中的划分,对这两道类似的题进行求解。\n查看下面的java代码,你会发现adjustArray这一部分的代码和前面经典排序中快排的partition算法代码\n基本一样,只是少了后面的交换。\n``` java\nimport java.util.Arrays;\nimport java.util.function.Predicate;\npublic class Test {\n public static void main(String[] args) {\n int[] nums = {1, 3, 2, 5, 4, 7, 6, 9, 8};\n int[] nums2 = {0, 1, 3, 0, 0, 4, 0, 0, 2};\n adjustArray(nums, i -> i % 2 != 0);\n adjustArray(nums2, i -> i != 0);\n System.out.println(Arrays.toString(nums));\n System.out.println(Arrays.toString(nums2));\n }\n\n static void swap(int[] nums, int i, int j) {\n int t = nums[i];\n nums[i] = nums[j];\n nums[j] = t;\n }\n\n static void adjustArray(int[] nums, Predicate<Integer> p) {\n int right = nums.length - 1;\n int i = 0;\n int j = 0;\n while (j <= right) {\n if (p.test(nums[j])) swap(nums, j, i++);\n j++;\n }\n }\n}\n```\n","source":"_posts/经典算法之划分.md","raw":"---\ntitle: 经典算法之划分\ndate: 2021-07-21 08:56:15\ncomments: true #是否可评论\ntoc: true #是否显示文章目录\ncategories: \"算法\" #分类\ntags: #标签\n - JAVA\n - 面试题\n - 算法\n---\n#### 经典算法之划分\n这个划分可以理解为经典算法快排里面的partition,快排里面的划分,一次操作,可以实现把数组划分为两块,\n一块数据大于pivot,一块数据小于pivot。\n我们对数据操作的时候,经常会有类似的需求,譬如把数组中的满足特定条件的数据移到后面或前面。例如\n1.把数组中的非0元素移到前面,即0元素全部放到尾部,非零元素的位置相对不变。\n2.把数组中的奇数放到前面,偶数放到后面\n这两种场景都算划分,本质上是一样的,满足某种条件之后进行交换。\n所以今天借用快排中的划分,对这两道类似的题进行求解。\n查看下面的java代码,你会发现adjustArray这一部分的代码和前面经典排序中快排的partition算法代码\n基本一样,只是少了后面的交换。\n``` java\nimport java.util.Arrays;\nimport java.util.function.Predicate;\npublic class Test {\n public static void main(String[] args) {\n int[] nums = {1, 3, 2, 5, 4, 7, 6, 9, 8};\n int[] nums2 = {0, 1, 3, 0, 0, 4, 0, 0, 2};\n adjustArray(nums, i -> i % 2 != 0);\n adjustArray(nums2, i -> i != 0);\n System.out.println(Arrays.toString(nums));\n System.out.println(Arrays.toString(nums2));\n }\n\n static void swap(int[] nums, int i, int j) {\n int t = nums[i];\n nums[i] = nums[j];\n nums[j] = t;\n }\n\n static void adjustArray(int[] nums, Predicate<Integer> p) {\n int right = nums.length - 1;\n int i = 0;\n int j = 0;\n while (j <= right) {\n if (p.test(nums[j])) swap(nums, j, i++);\n j++;\n }\n }\n}\n```\n","slug":"经典算法之划分","published":1,"updated":"2022-04-20T09:35:50.864Z","layout":"post","photos":[],"link":"","_id":"cl27q3uvd001fn94o0ypr65pm","content":"<h4 id=\"经典算法之划分\"><a href=\"#经典算法之划分\" class=\"headerlink\" title=\"经典算法之划分\"></a>经典算法之划分</h4><p>这个划分可以理解为经典算法快排里面的partition,快排里面的划分,一次操作,可以实现把数组划分为两块,<br>一块数据大于pivot,一块数据小于pivot。<br>我们对数据操作的时候,经常会有类似的需求,譬如把数组中的满足特定条件的数据移到后面或前面。例如<br>1.把数组中的非0元素移到前面,即0元素全部放到尾部,非零元素的位置相对不变。<br>2.把数组中的奇数放到前面,偶数放到后面<br>这两种场景都算划分,本质上是一样的,满足某种条件之后进行交换。<br>所以今天借用快排中的划分,对这两道类似的题进行求解。<br>查看下面的java代码,你会发现adjustArray这一部分的代码和前面经典排序中快排的partition算法代码<br>基本一样,只是少了后面的交换。</p>\n<figure class=\"highlight java\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br><span class=\"line\">11</span><br><span class=\"line\">12</span><br><span class=\"line\">13</span><br><span class=\"line\">14</span><br><span class=\"line\">15</span><br><span class=\"line\">16</span><br><span class=\"line\">17</span><br><span class=\"line\">18</span><br><span class=\"line\">19</span><br><span class=\"line\">20</span><br><span class=\"line\">21</span><br><span class=\"line\">22</span><br><span class=\"line\">23</span><br><span class=\"line\">24</span><br><span class=\"line\">25</span><br><span class=\"line\">26</span><br><span class=\"line\">27</span><br><span class=\"line\">28</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"keyword\">import</span> java.util.Arrays;</span><br><span class=\"line\"><span class=\"keyword\">import</span> java.util.function.Predicate;</span><br><span class=\"line\"><span class=\"keyword\">public</span> <span class=\"keyword\">class</span> <span class=\"title class_\">Test</span> {</span><br><span class=\"line\"> <span class=\"keyword\">public</span> <span class=\"keyword\">static</span> <span class=\"keyword\">void</span> <span class=\"title function_\">main</span><span class=\"params\">(String[] args)</span> {</span><br><span class=\"line\"> <span class=\"type\">int</span>[] nums = {<span class=\"number\">1</span>, <span class=\"number\">3</span>, <span class=\"number\">2</span>, <span class=\"number\">5</span>, <span class=\"number\">4</span>, <span class=\"number\">7</span>, <span class=\"number\">6</span>, <span class=\"number\">9</span>, <span class=\"number\">8</span>};</span><br><span class=\"line\"> <span class=\"type\">int</span>[] nums2 = {<span class=\"number\">0</span>, <span class=\"number\">1</span>, <span class=\"number\">3</span>, <span class=\"number\">0</span>, <span class=\"number\">0</span>, <span class=\"number\">4</span>, <span class=\"number\">0</span>, <span class=\"number\">0</span>, <span class=\"number\">2</span>};</span><br><span class=\"line\"> adjustArray(nums, i -> i % <span class=\"number\">2</span> != <span class=\"number\">0</span>);</span><br><span class=\"line\"> adjustArray(nums2, i -> i != <span class=\"number\">0</span>);</span><br><span class=\"line\"> System.out.println(Arrays.toString(nums));</span><br><span class=\"line\"> System.out.println(Arrays.toString(nums2));</span><br><span class=\"line\"> }</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"keyword\">static</span> <span class=\"keyword\">void</span> <span class=\"title function_\">swap</span><span class=\"params\">(<span class=\"type\">int</span>[] nums, <span class=\"type\">int</span> i, <span class=\"type\">int</span> j)</span> {</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">t</span> <span class=\"operator\">=</span> nums[i];</span><br><span class=\"line\"> nums[i] = nums[j];</span><br><span class=\"line\"> nums[j] = t;</span><br><span class=\"line\"> }</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"keyword\">static</span> <span class=\"keyword\">void</span> <span class=\"title function_\">adjustArray</span><span class=\"params\">(<span class=\"type\">int</span>[] nums, Predicate<Integer> p)</span> {</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">right</span> <span class=\"operator\">=</span> nums.length - <span class=\"number\">1</span>;</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">i</span> <span class=\"operator\">=</span> <span class=\"number\">0</span>;</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">j</span> <span class=\"operator\">=</span> <span class=\"number\">0</span>;</span><br><span class=\"line\"> <span class=\"keyword\">while</span> (j <= right) {</span><br><span class=\"line\"> <span class=\"keyword\">if</span> (p.test(nums[j])) swap(nums, j, i++);</span><br><span class=\"line\"> j++;</span><br><span class=\"line\"> }</span><br><span class=\"line\"> }</span><br><span class=\"line\">}</span><br></pre></td></tr></table></figure>\n","site":{"data":{}},"excerpt":"","more":"<h4 id=\"经典算法之划分\"><a href=\"#经典算法之划分\" class=\"headerlink\" title=\"经典算法之划分\"></a>经典算法之划分</h4><p>这个划分可以理解为经典算法快排里面的partition,快排里面的划分,一次操作,可以实现把数组划分为两块,<br>一块数据大于pivot,一块数据小于pivot。<br>我们对数据操作的时候,经常会有类似的需求,譬如把数组中的满足特定条件的数据移到后面或前面。例如<br>1.把数组中的非0元素移到前面,即0元素全部放到尾部,非零元素的位置相对不变。<br>2.把数组中的奇数放到前面,偶数放到后面<br>这两种场景都算划分,本质上是一样的,满足某种条件之后进行交换。<br>所以今天借用快排中的划分,对这两道类似的题进行求解。<br>查看下面的java代码,你会发现adjustArray这一部分的代码和前面经典排序中快排的partition算法代码<br>基本一样,只是少了后面的交换。</p>\n<figure class=\"highlight java\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br><span class=\"line\">11</span><br><span class=\"line\">12</span><br><span class=\"line\">13</span><br><span class=\"line\">14</span><br><span class=\"line\">15</span><br><span class=\"line\">16</span><br><span class=\"line\">17</span><br><span class=\"line\">18</span><br><span class=\"line\">19</span><br><span class=\"line\">20</span><br><span class=\"line\">21</span><br><span class=\"line\">22</span><br><span class=\"line\">23</span><br><span class=\"line\">24</span><br><span class=\"line\">25</span><br><span class=\"line\">26</span><br><span class=\"line\">27</span><br><span class=\"line\">28</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"keyword\">import</span> java.util.Arrays;</span><br><span class=\"line\"><span class=\"keyword\">import</span> java.util.function.Predicate;</span><br><span class=\"line\"><span class=\"keyword\">public</span> <span class=\"keyword\">class</span> <span class=\"title class_\">Test</span> {</span><br><span class=\"line\"> <span class=\"keyword\">public</span> <span class=\"keyword\">static</span> <span class=\"keyword\">void</span> <span class=\"title function_\">main</span><span class=\"params\">(String[] args)</span> {</span><br><span class=\"line\"> <span class=\"type\">int</span>[] nums = {<span class=\"number\">1</span>, <span class=\"number\">3</span>, <span class=\"number\">2</span>, <span class=\"number\">5</span>, <span class=\"number\">4</span>, <span class=\"number\">7</span>, <span class=\"number\">6</span>, <span class=\"number\">9</span>, <span class=\"number\">8</span>};</span><br><span class=\"line\"> <span class=\"type\">int</span>[] nums2 = {<span class=\"number\">0</span>, <span class=\"number\">1</span>, <span class=\"number\">3</span>, <span class=\"number\">0</span>, <span class=\"number\">0</span>, <span class=\"number\">4</span>, <span class=\"number\">0</span>, <span class=\"number\">0</span>, <span class=\"number\">2</span>};</span><br><span class=\"line\"> adjustArray(nums, i -> i % <span class=\"number\">2</span> != <span class=\"number\">0</span>);</span><br><span class=\"line\"> adjustArray(nums2, i -> i != <span class=\"number\">0</span>);</span><br><span class=\"line\"> System.out.println(Arrays.toString(nums));</span><br><span class=\"line\"> System.out.println(Arrays.toString(nums2));</span><br><span class=\"line\"> }</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"keyword\">static</span> <span class=\"keyword\">void</span> <span class=\"title function_\">swap</span><span class=\"params\">(<span class=\"type\">int</span>[] nums, <span class=\"type\">int</span> i, <span class=\"type\">int</span> j)</span> {</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">t</span> <span class=\"operator\">=</span> nums[i];</span><br><span class=\"line\"> nums[i] = nums[j];</span><br><span class=\"line\"> nums[j] = t;</span><br><span class=\"line\"> }</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"keyword\">static</span> <span class=\"keyword\">void</span> <span class=\"title function_\">adjustArray</span><span class=\"params\">(<span class=\"type\">int</span>[] nums, Predicate<Integer> p)</span> {</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">right</span> <span class=\"operator\">=</span> nums.length - <span class=\"number\">1</span>;</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">i</span> <span class=\"operator\">=</span> <span class=\"number\">0</span>;</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">j</span> <span class=\"operator\">=</span> <span class=\"number\">0</span>;</span><br><span class=\"line\"> <span class=\"keyword\">while</span> (j <= right) {</span><br><span class=\"line\"> <span class=\"keyword\">if</span> (p.test(nums[j])) swap(nums, j, i++);</span><br><span class=\"line\"> j++;</span><br><span class=\"line\"> }</span><br><span class=\"line\"> }</span><br><span class=\"line\">}</span><br></pre></td></tr></table></figure>\n"},{"title":"经典算法之快选求TOPK","date":"2021-07-20T00:53:57.000Z","comments":1,"toc":true,"_content":"#### 快选法求未排序数组中第KTH大的元素\n题目是有一个未排序的数组,求里面第k大的元素,例如\n``` java\n int[] nums = {2, 1, 4, 3, 6, 5, 9, 7, 8};\n```\n这个数组的元素是1-9,未排序。\nk=1时,则返回9\nk=2时,则返回8\nk=9时,则返回1\n可能会想到,首先对这个数组排序一下(从小到大),然后直接返回nums.length - k的元素即可。\n或者从大到小排序,返回数组中位于k-1的元素也是可以的。\n这里我们使用快选法来求解,快选法的原理是,使用快排的框架来求解。\n快排的划分算法,一次划分的结果得到的pivot值,就是位于数组中pivot的位置,是确定的,我们对比\npivot与kth的值(kth=nums.length - k),如果pivot==kth则,我们直接返回nums[pivot]即可。\n如果pivot 小于 kth 则我们下次划分的时候把左边界设为pivot + 1 即可;如果pivoit 大于 kth,\n则我们把右边界设置为 pivot - 1,然后再次调用划分,如果最后找不到我们就返回-1。\n因为前面我在文章中已经练习过快排和二分查找,所以这个时候练习这算法非常合适,比较容易接受。\n这个算法刚好就结合了快排的partition划分和二分查找,可以使用递归和迭代两种实现。\n时间复杂度:平均是O(n) 最坏情况下是O(N平方)\n代码如下:\n``` java\n\n static void swap(int[] nums, int i, int j ){\n int t = nums[i];\n nums[i] = nums[j];\n nums[j] = t;\n }\n\n static int partition(int[] nums, int left, int right) {\n int i = left;\n int j = left;\n while(j <= right) {\n if(nums[j] < nums[left]) swap(nums, j, ++i);\n j++;\n }\n swap(nums, i, left);\n return i;\n }\n\n static int quickSelectKth(int[] nums, int left, int right, int k){\n int kth = nums.length - k;\n while( left <= right) {\n int pivot = partition(nums, left, right);\n if(pivot == kth) return nums[kth];\n if(pivot > kth) right = pivot - 1;\n if(pivot < kth) left = pivot + 1;\n }\n return -1;\n }\n\n static int quickSelectKthR(int[] nums, int left, int right, int k){\n if(left >= right) return -1;\n int kth = nums.length - k;\n int pivot = partition(nums, left ,right);\n if(pivot == kth) return nums[kth];\n if(pivot > kth) return quickSelectKthR(nums, left, pivot -1 , k);\n if(pivot < kth) return quickSelectKthR(nums, pivot + 1, right, k);\n return -1;\n }\n```","source":"_posts/经典算法之快选求TOPK.md","raw":"---\ntitle: 经典算法之快选求TOPK\ndate: 2021-07-20 08:53:57\ncomments: true #是否可评论\ntoc: true #是否显示文章目录\ncategories: \"算法\" #分类\ntags: #标签\n - 面试题\n - 算法\n - JAVA\n---\n#### 快选法求未排序数组中第KTH大的元素\n题目是有一个未排序的数组,求里面第k大的元素,例如\n``` java\n int[] nums = {2, 1, 4, 3, 6, 5, 9, 7, 8};\n```\n这个数组的元素是1-9,未排序。\nk=1时,则返回9\nk=2时,则返回8\nk=9时,则返回1\n可能会想到,首先对这个数组排序一下(从小到大),然后直接返回nums.length - k的元素即可。\n或者从大到小排序,返回数组中位于k-1的元素也是可以的。\n这里我们使用快选法来求解,快选法的原理是,使用快排的框架来求解。\n快排的划分算法,一次划分的结果得到的pivot值,就是位于数组中pivot的位置,是确定的,我们对比\npivot与kth的值(kth=nums.length - k),如果pivot==kth则,我们直接返回nums[pivot]即可。\n如果pivot 小于 kth 则我们下次划分的时候把左边界设为pivot + 1 即可;如果pivoit 大于 kth,\n则我们把右边界设置为 pivot - 1,然后再次调用划分,如果最后找不到我们就返回-1。\n因为前面我在文章中已经练习过快排和二分查找,所以这个时候练习这算法非常合适,比较容易接受。\n这个算法刚好就结合了快排的partition划分和二分查找,可以使用递归和迭代两种实现。\n时间复杂度:平均是O(n) 最坏情况下是O(N平方)\n代码如下:\n``` java\n\n static void swap(int[] nums, int i, int j ){\n int t = nums[i];\n nums[i] = nums[j];\n nums[j] = t;\n }\n\n static int partition(int[] nums, int left, int right) {\n int i = left;\n int j = left;\n while(j <= right) {\n if(nums[j] < nums[left]) swap(nums, j, ++i);\n j++;\n }\n swap(nums, i, left);\n return i;\n }\n\n static int quickSelectKth(int[] nums, int left, int right, int k){\n int kth = nums.length - k;\n while( left <= right) {\n int pivot = partition(nums, left, right);\n if(pivot == kth) return nums[kth];\n if(pivot > kth) right = pivot - 1;\n if(pivot < kth) left = pivot + 1;\n }\n return -1;\n }\n\n static int quickSelectKthR(int[] nums, int left, int right, int k){\n if(left >= right) return -1;\n int kth = nums.length - k;\n int pivot = partition(nums, left ,right);\n if(pivot == kth) return nums[kth];\n if(pivot > kth) return quickSelectKthR(nums, left, pivot -1 , k);\n if(pivot < kth) return quickSelectKthR(nums, pivot + 1, right, k);\n return -1;\n }\n```","slug":"经典算法之快选求TOPK","published":1,"updated":"2022-04-20T09:35:50.864Z","layout":"post","photos":[],"link":"","_id":"cl27q3uvd001in94oboc15iwr","content":"<h4 id=\"快选法求未排序数组中第KTH大的元素\"><a href=\"#快选法求未排序数组中第KTH大的元素\" class=\"headerlink\" title=\"快选法求未排序数组中第KTH大的元素\"></a>快选法求未排序数组中第KTH大的元素</h4><p>题目是有一个未排序的数组,求里面第k大的元素,例如</p>\n<figure class=\"highlight java\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"type\">int</span>[] nums = {<span class=\"number\">2</span>, <span class=\"number\">1</span>, <span class=\"number\">4</span>, <span class=\"number\">3</span>, <span class=\"number\">6</span>, <span class=\"number\">5</span>, <span class=\"number\">9</span>, <span class=\"number\">7</span>, <span class=\"number\">8</span>};</span><br></pre></td></tr></table></figure>\n<p>这个数组的元素是1-9,未排序。<br>k=1时,则返回9<br>k=2时,则返回8<br>k=9时,则返回1<br>可能会想到,首先对这个数组排序一下(从小到大),然后直接返回nums.length - k的元素即可。<br>或者从大到小排序,返回数组中位于k-1的元素也是可以的。<br>这里我们使用快选法来求解,快选法的原理是,使用快排的框架来求解。<br>快排的划分算法,一次划分的结果得到的pivot值,就是位于数组中pivot的位置,是确定的,我们对比<br>pivot与kth的值(kth=nums.length - k),如果pivot==kth则,我们直接返回nums[pivot]即可。<br>如果pivot 小于 kth 则我们下次划分的时候把左边界设为pivot + 1 即可;如果pivoit 大于 kth,<br>则我们把右边界设置为 pivot - 1,然后再次调用划分,如果最后找不到我们就返回-1。<br>因为前面我在文章中已经练习过快排和二分查找,所以这个时候练习这算法非常合适,比较容易接受。<br>这个算法刚好就结合了快排的partition划分和二分查找,可以使用递归和迭代两种实现。<br>时间复杂度:平均是O(n) 最坏情况下是O(N平方)<br>代码如下:</p>\n<figure class=\"highlight java\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br><span class=\"line\">11</span><br><span class=\"line\">12</span><br><span class=\"line\">13</span><br><span class=\"line\">14</span><br><span class=\"line\">15</span><br><span class=\"line\">16</span><br><span class=\"line\">17</span><br><span class=\"line\">18</span><br><span class=\"line\">19</span><br><span class=\"line\">20</span><br><span class=\"line\">21</span><br><span class=\"line\">22</span><br><span class=\"line\">23</span><br><span class=\"line\">24</span><br><span class=\"line\">25</span><br><span class=\"line\">26</span><br><span class=\"line\">27</span><br><span class=\"line\">28</span><br><span class=\"line\">29</span><br><span class=\"line\">30</span><br><span class=\"line\">31</span><br><span class=\"line\">32</span><br><span class=\"line\">33</span><br><span class=\"line\">34</span><br><span class=\"line\">35</span><br><span class=\"line\">36</span><br><span class=\"line\">37</span><br><span class=\"line\">38</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"></span><br><span class=\"line\"><span class=\"keyword\">static</span> <span class=\"keyword\">void</span> <span class=\"title function_\">swap</span><span class=\"params\">(<span class=\"type\">int</span>[] nums, <span class=\"type\">int</span> i, <span class=\"type\">int</span> j )</span>{</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">t</span> <span class=\"operator\">=</span> nums[i];</span><br><span class=\"line\"> nums[i] = nums[j];</span><br><span class=\"line\"> nums[j] = t;</span><br><span class=\"line\">}</span><br><span class=\"line\"></span><br><span class=\"line\"><span class=\"keyword\">static</span> <span class=\"type\">int</span> <span class=\"title function_\">partition</span><span class=\"params\">(<span class=\"type\">int</span>[] nums, <span class=\"type\">int</span> left, <span class=\"type\">int</span> right)</span> {</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">i</span> <span class=\"operator\">=</span> left;</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">j</span> <span class=\"operator\">=</span> left;</span><br><span class=\"line\"> <span class=\"keyword\">while</span>(j <= right) {</span><br><span class=\"line\"> <span class=\"keyword\">if</span>(nums[j] < nums[left]) swap(nums, j, ++i);</span><br><span class=\"line\"> j++;</span><br><span class=\"line\"> }</span><br><span class=\"line\"> swap(nums, i, left);</span><br><span class=\"line\"> <span class=\"keyword\">return</span> i;</span><br><span class=\"line\">}</span><br><span class=\"line\"></span><br><span class=\"line\"><span class=\"keyword\">static</span> <span class=\"type\">int</span> <span class=\"title function_\">quickSelectKth</span><span class=\"params\">(<span class=\"type\">int</span>[] nums, <span class=\"type\">int</span> left, <span class=\"type\">int</span> right, <span class=\"type\">int</span> k)</span>{</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">kth</span> <span class=\"operator\">=</span> nums.length - k;</span><br><span class=\"line\"> <span class=\"keyword\">while</span>( left <= right) {</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">pivot</span> <span class=\"operator\">=</span> partition(nums, left, right);</span><br><span class=\"line\"> <span class=\"keyword\">if</span>(pivot == kth) <span class=\"keyword\">return</span> nums[kth];</span><br><span class=\"line\"> <span class=\"keyword\">if</span>(pivot > kth) right = pivot - <span class=\"number\">1</span>;</span><br><span class=\"line\"> <span class=\"keyword\">if</span>(pivot < kth) left = pivot + <span class=\"number\">1</span>;</span><br><span class=\"line\"> }</span><br><span class=\"line\"> <span class=\"keyword\">return</span> -<span class=\"number\">1</span>;</span><br><span class=\"line\">}</span><br><span class=\"line\"></span><br><span class=\"line\"><span class=\"keyword\">static</span> <span class=\"type\">int</span> <span class=\"title function_\">quickSelectKthR</span><span class=\"params\">(<span class=\"type\">int</span>[] nums, <span class=\"type\">int</span> left, <span class=\"type\">int</span> right, <span class=\"type\">int</span> k)</span>{</span><br><span class=\"line\"> <span class=\"keyword\">if</span>(left >= right) <span class=\"keyword\">return</span> -<span class=\"number\">1</span>;</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">kth</span> <span class=\"operator\">=</span> nums.length - k;</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">pivot</span> <span class=\"operator\">=</span> partition(nums, left ,right);</span><br><span class=\"line\"> <span class=\"keyword\">if</span>(pivot == kth) <span class=\"keyword\">return</span> nums[kth];</span><br><span class=\"line\"> <span class=\"keyword\">if</span>(pivot > kth) <span class=\"keyword\">return</span> quickSelectKthR(nums, left, pivot -<span class=\"number\">1</span> , k);</span><br><span class=\"line\"> <span class=\"keyword\">if</span>(pivot < kth) <span class=\"keyword\">return</span> quickSelectKthR(nums, pivot + <span class=\"number\">1</span>, right, k);</span><br><span class=\"line\"> <span class=\"keyword\">return</span> -<span class=\"number\">1</span>;</span><br><span class=\"line\">}</span><br></pre></td></tr></table></figure>","site":{"data":{}},"excerpt":"","more":"<h4 id=\"快选法求未排序数组中第KTH大的元素\"><a href=\"#快选法求未排序数组中第KTH大的元素\" class=\"headerlink\" title=\"快选法求未排序数组中第KTH大的元素\"></a>快选法求未排序数组中第KTH大的元素</h4><p>题目是有一个未排序的数组,求里面第k大的元素,例如</p>\n<figure class=\"highlight java\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"type\">int</span>[] nums = {<span class=\"number\">2</span>, <span class=\"number\">1</span>, <span class=\"number\">4</span>, <span class=\"number\">3</span>, <span class=\"number\">6</span>, <span class=\"number\">5</span>, <span class=\"number\">9</span>, <span class=\"number\">7</span>, <span class=\"number\">8</span>};</span><br></pre></td></tr></table></figure>\n<p>这个数组的元素是1-9,未排序。<br>k=1时,则返回9<br>k=2时,则返回8<br>k=9时,则返回1<br>可能会想到,首先对这个数组排序一下(从小到大),然后直接返回nums.length - k的元素即可。<br>或者从大到小排序,返回数组中位于k-1的元素也是可以的。<br>这里我们使用快选法来求解,快选法的原理是,使用快排的框架来求解。<br>快排的划分算法,一次划分的结果得到的pivot值,就是位于数组中pivot的位置,是确定的,我们对比<br>pivot与kth的值(kth=nums.length - k),如果pivot==kth则,我们直接返回nums[pivot]即可。<br>如果pivot 小于 kth 则我们下次划分的时候把左边界设为pivot + 1 即可;如果pivoit 大于 kth,<br>则我们把右边界设置为 pivot - 1,然后再次调用划分,如果最后找不到我们就返回-1。<br>因为前面我在文章中已经练习过快排和二分查找,所以这个时候练习这算法非常合适,比较容易接受。<br>这个算法刚好就结合了快排的partition划分和二分查找,可以使用递归和迭代两种实现。<br>时间复杂度:平均是O(n) 最坏情况下是O(N平方)<br>代码如下:</p>\n<figure class=\"highlight java\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br><span class=\"line\">11</span><br><span class=\"line\">12</span><br><span class=\"line\">13</span><br><span class=\"line\">14</span><br><span class=\"line\">15</span><br><span class=\"line\">16</span><br><span class=\"line\">17</span><br><span class=\"line\">18</span><br><span class=\"line\">19</span><br><span class=\"line\">20</span><br><span class=\"line\">21</span><br><span class=\"line\">22</span><br><span class=\"line\">23</span><br><span class=\"line\">24</span><br><span class=\"line\">25</span><br><span class=\"line\">26</span><br><span class=\"line\">27</span><br><span class=\"line\">28</span><br><span class=\"line\">29</span><br><span class=\"line\">30</span><br><span class=\"line\">31</span><br><span class=\"line\">32</span><br><span class=\"line\">33</span><br><span class=\"line\">34</span><br><span class=\"line\">35</span><br><span class=\"line\">36</span><br><span class=\"line\">37</span><br><span class=\"line\">38</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"></span><br><span class=\"line\"><span class=\"keyword\">static</span> <span class=\"keyword\">void</span> <span class=\"title function_\">swap</span><span class=\"params\">(<span class=\"type\">int</span>[] nums, <span class=\"type\">int</span> i, <span class=\"type\">int</span> j )</span>{</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">t</span> <span class=\"operator\">=</span> nums[i];</span><br><span class=\"line\"> nums[i] = nums[j];</span><br><span class=\"line\"> nums[j] = t;</span><br><span class=\"line\">}</span><br><span class=\"line\"></span><br><span class=\"line\"><span class=\"keyword\">static</span> <span class=\"type\">int</span> <span class=\"title function_\">partition</span><span class=\"params\">(<span class=\"type\">int</span>[] nums, <span class=\"type\">int</span> left, <span class=\"type\">int</span> right)</span> {</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">i</span> <span class=\"operator\">=</span> left;</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">j</span> <span class=\"operator\">=</span> left;</span><br><span class=\"line\"> <span class=\"keyword\">while</span>(j <= right) {</span><br><span class=\"line\"> <span class=\"keyword\">if</span>(nums[j] < nums[left]) swap(nums, j, ++i);</span><br><span class=\"line\"> j++;</span><br><span class=\"line\"> }</span><br><span class=\"line\"> swap(nums, i, left);</span><br><span class=\"line\"> <span class=\"keyword\">return</span> i;</span><br><span class=\"line\">}</span><br><span class=\"line\"></span><br><span class=\"line\"><span class=\"keyword\">static</span> <span class=\"type\">int</span> <span class=\"title function_\">quickSelectKth</span><span class=\"params\">(<span class=\"type\">int</span>[] nums, <span class=\"type\">int</span> left, <span class=\"type\">int</span> right, <span class=\"type\">int</span> k)</span>{</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">kth</span> <span class=\"operator\">=</span> nums.length - k;</span><br><span class=\"line\"> <span class=\"keyword\">while</span>( left <= right) {</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">pivot</span> <span class=\"operator\">=</span> partition(nums, left, right);</span><br><span class=\"line\"> <span class=\"keyword\">if</span>(pivot == kth) <span class=\"keyword\">return</span> nums[kth];</span><br><span class=\"line\"> <span class=\"keyword\">if</span>(pivot > kth) right = pivot - <span class=\"number\">1</span>;</span><br><span class=\"line\"> <span class=\"keyword\">if</span>(pivot < kth) left = pivot + <span class=\"number\">1</span>;</span><br><span class=\"line\"> }</span><br><span class=\"line\"> <span class=\"keyword\">return</span> -<span class=\"number\">1</span>;</span><br><span class=\"line\">}</span><br><span class=\"line\"></span><br><span class=\"line\"><span class=\"keyword\">static</span> <span class=\"type\">int</span> <span class=\"title function_\">quickSelectKthR</span><span class=\"params\">(<span class=\"type\">int</span>[] nums, <span class=\"type\">int</span> left, <span class=\"type\">int</span> right, <span class=\"type\">int</span> k)</span>{</span><br><span class=\"line\"> <span class=\"keyword\">if</span>(left >= right) <span class=\"keyword\">return</span> -<span class=\"number\">1</span>;</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">kth</span> <span class=\"operator\">=</span> nums.length - k;</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">pivot</span> <span class=\"operator\">=</span> partition(nums, left ,right);</span><br><span class=\"line\"> <span class=\"keyword\">if</span>(pivot == kth) <span class=\"keyword\">return</span> nums[kth];</span><br><span class=\"line\"> <span class=\"keyword\">if</span>(pivot > kth) <span class=\"keyword\">return</span> quickSelectKthR(nums, left, pivot -<span class=\"number\">1</span> , k);</span><br><span class=\"line\"> <span class=\"keyword\">if</span>(pivot < kth) <span class=\"keyword\">return</span> quickSelectKthR(nums, pivot + <span class=\"number\">1</span>, right, k);</span><br><span class=\"line\"> <span class=\"keyword\">return</span> -<span class=\"number\">1</span>;</span><br><span class=\"line\">}</span><br></pre></td></tr></table></figure>"},{"title":"经典算法之求众数","date":"2021-07-20T23:55:03.000Z","comments":1,"toc":true,"_content":"#### 经典算法之求众数\n**众数**是一组数据中出现最多次数的那个数。\n题目:如果一个数在数组中出现次数超过一半,如何设计一个高效的算法,求出这个数?\n最直接的想法就是使用hashmap,遍历一遍数组,以数字作为key,以出现次数做为value。\n另一种直接的想法就是使用另外一个数组来代替hashmap来实现类似的效果,使用下标来表示\n数字,使用值来表示出现的次数,就是位图排序的思路。\n下面说两种对空间要求没那么高的算法。\n1. 设想如果我们对这个数组排序,则位于N/2位置的数字肯定就是这个超过一半数字的众数,\n所以这个题就可以转成求N/2位置的数字,可以使用求TOPK的算法,即求第K个位置的数字。\n使用快选法来解决即可,只是K固定为N/2.\n2. 使用**摩尔投票法**,这个算法不仅可以用来处理大于一半的众数,还可以求大于任意M分之一情况下\n的众数,譬如大于三分之一,就可以选择两个侯选人。\n它的思路是假设有一个侯选人,就是nums[0],起始自己投了自己一票,遍历其余数字,如果数字相等,\n投票数则加1,否则减1,如果投票数等于0,则换成当前的新数字,投票数置1,一直遍历到结束。\n最后的侯选人就是众数。\n下面是java代码:\n``` java\n\npublic class Test { \n public static void main(String[] args) {\n int[] nums = {1, 2, 3, 2, 2, 4, 2};\n System.out.println(findMajors(nums));\n int[] nums2 = {1, 2, 3, 2, 2, 4, 2, 4, 4, 4, 4, 4, 4};\n System.out.println(findMajors(nums2));\n System.out.println(molVotes(nums));\n System.out.println(molVotes(nums2));\n }\n /**\n * 摩尔投票法\n * 如果超过m分之一 则可以选取m-1个侯选人\n * 例如超过二分之一,肯定只能选一个侯选人\n * 超过三分之一,则可以选二个侯选人\n */\n static int molVotes(int[] nums) {\n int c1 = nums[0];\n int cnt1 = 1;\n for (int i = 1; i < nums.length; i++) {\n if (c1 == nums[i]) cnt1++;\n else cnt1 -- ;\n if(cnt1 == 0){\n c1 = nums[i];\n cnt1 = 1;\n }\n }\n return c1;\n }\n\n static void swap(int[] nums, int i, int j) {\n int t = nums[i];\n nums[i] = nums[j];\n nums[j] = t;\n }\n\n static int partition(int[] nums, int left, int right) {\n int i = left;\n int j = left;\n while (j <= right) {\n if (nums[j] < nums[left]) swap(nums, j, ++i);\n j++;\n }\n swap(nums, i, left);\n return i;\n }\n\n static int findMajors(int[] nums) {\n int left = 0;\n int right = nums.length - 1;\n int k = nums.length >> 1;\n while (left <= right) {\n int pivot = partition(nums, left, right);\n if (pivot == k) return nums[k];\n if (pivot < k) left = pivot + 1;\n if (pivot > k) right = pivot - 1;\n }\n return -1;\n }\n}\n```\n","source":"_posts/经典算法之求众数.md","raw":"---\ntitle: 经典算法之求众数\ndate: 2021-07-21 07:55:03\ncomments: true #是否可评论\ntoc: true #是否显示文章目录\ncategories: \"算法\" #分类\ntags: #标签\n - JAVA\n - 面试题\n - 算法\n---\n#### 经典算法之求众数\n**众数**是一组数据中出现最多次数的那个数。\n题目:如果一个数在数组中出现次数超过一半,如何设计一个高效的算法,求出这个数?\n最直接的想法就是使用hashmap,遍历一遍数组,以数字作为key,以出现次数做为value。\n另一种直接的想法就是使用另外一个数组来代替hashmap来实现类似的效果,使用下标来表示\n数字,使用值来表示出现的次数,就是位图排序的思路。\n下面说两种对空间要求没那么高的算法。\n1. 设想如果我们对这个数组排序,则位于N/2位置的数字肯定就是这个超过一半数字的众数,\n所以这个题就可以转成求N/2位置的数字,可以使用求TOPK的算法,即求第K个位置的数字。\n使用快选法来解决即可,只是K固定为N/2.\n2. 使用**摩尔投票法**,这个算法不仅可以用来处理大于一半的众数,还可以求大于任意M分之一情况下\n的众数,譬如大于三分之一,就可以选择两个侯选人。\n它的思路是假设有一个侯选人,就是nums[0],起始自己投了自己一票,遍历其余数字,如果数字相等,\n投票数则加1,否则减1,如果投票数等于0,则换成当前的新数字,投票数置1,一直遍历到结束。\n最后的侯选人就是众数。\n下面是java代码:\n``` java\n\npublic class Test { \n public static void main(String[] args) {\n int[] nums = {1, 2, 3, 2, 2, 4, 2};\n System.out.println(findMajors(nums));\n int[] nums2 = {1, 2, 3, 2, 2, 4, 2, 4, 4, 4, 4, 4, 4};\n System.out.println(findMajors(nums2));\n System.out.println(molVotes(nums));\n System.out.println(molVotes(nums2));\n }\n /**\n * 摩尔投票法\n * 如果超过m分之一 则可以选取m-1个侯选人\n * 例如超过二分之一,肯定只能选一个侯选人\n * 超过三分之一,则可以选二个侯选人\n */\n static int molVotes(int[] nums) {\n int c1 = nums[0];\n int cnt1 = 1;\n for (int i = 1; i < nums.length; i++) {\n if (c1 == nums[i]) cnt1++;\n else cnt1 -- ;\n if(cnt1 == 0){\n c1 = nums[i];\n cnt1 = 1;\n }\n }\n return c1;\n }\n\n static void swap(int[] nums, int i, int j) {\n int t = nums[i];\n nums[i] = nums[j];\n nums[j] = t;\n }\n\n static int partition(int[] nums, int left, int right) {\n int i = left;\n int j = left;\n while (j <= right) {\n if (nums[j] < nums[left]) swap(nums, j, ++i);\n j++;\n }\n swap(nums, i, left);\n return i;\n }\n\n static int findMajors(int[] nums) {\n int left = 0;\n int right = nums.length - 1;\n int k = nums.length >> 1;\n while (left <= right) {\n int pivot = partition(nums, left, right);\n if (pivot == k) return nums[k];\n if (pivot < k) left = pivot + 1;\n if (pivot > k) right = pivot - 1;\n }\n return -1;\n }\n}\n```\n","slug":"经典算法之求众数","published":1,"updated":"2022-04-20T09:35:50.864Z","layout":"post","photos":[],"link":"","_id":"cl27q3uve001nn94oac344ljn","content":"<h4 id=\"经典算法之求众数\"><a href=\"#经典算法之求众数\" class=\"headerlink\" title=\"经典算法之求众数\"></a>经典算法之求众数</h4><p><strong>众数</strong>是一组数据中出现最多次数的那个数。<br>题目:如果一个数在数组中出现次数超过一半,如何设计一个高效的算法,求出这个数?<br>最直接的想法就是使用hashmap,遍历一遍数组,以数字作为key,以出现次数做为value。<br>另一种直接的想法就是使用另外一个数组来代替hashmap来实现类似的效果,使用下标来表示<br>数字,使用值来表示出现的次数,就是位图排序的思路。<br>下面说两种对空间要求没那么高的算法。</p>\n<ol>\n<li>设想如果我们对这个数组排序,则位于N/2位置的数字肯定就是这个超过一半数字的众数,<br>所以这个题就可以转成求N/2位置的数字,可以使用求TOPK的算法,即求第K个位置的数字。<br>使用快选法来解决即可,只是K固定为N/2.</li>\n<li>使用<strong>摩尔投票法</strong>,这个算法不仅可以用来处理大于一半的众数,还可以求大于任意M分之一情况下<br>的众数,譬如大于三分之一,就可以选择两个侯选人。<br>它的思路是假设有一个侯选人,就是nums[0],起始自己投了自己一票,遍历其余数字,如果数字相等,<br>投票数则加1,否则减1,如果投票数等于0,则换成当前的新数字,投票数置1,一直遍历到结束。<br>最后的侯选人就是众数。<br>下面是java代码:<figure class=\"highlight java\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br><span class=\"line\">11</span><br><span class=\"line\">12</span><br><span class=\"line\">13</span><br><span class=\"line\">14</span><br><span class=\"line\">15</span><br><span class=\"line\">16</span><br><span class=\"line\">17</span><br><span class=\"line\">18</span><br><span class=\"line\">19</span><br><span class=\"line\">20</span><br><span class=\"line\">21</span><br><span class=\"line\">22</span><br><span class=\"line\">23</span><br><span class=\"line\">24</span><br><span class=\"line\">25</span><br><span class=\"line\">26</span><br><span class=\"line\">27</span><br><span class=\"line\">28</span><br><span class=\"line\">29</span><br><span class=\"line\">30</span><br><span class=\"line\">31</span><br><span class=\"line\">32</span><br><span class=\"line\">33</span><br><span class=\"line\">34</span><br><span class=\"line\">35</span><br><span class=\"line\">36</span><br><span class=\"line\">37</span><br><span class=\"line\">38</span><br><span class=\"line\">39</span><br><span class=\"line\">40</span><br><span class=\"line\">41</span><br><span class=\"line\">42</span><br><span class=\"line\">43</span><br><span class=\"line\">44</span><br><span class=\"line\">45</span><br><span class=\"line\">46</span><br><span class=\"line\">47</span><br><span class=\"line\">48</span><br><span class=\"line\">49</span><br><span class=\"line\">50</span><br><span class=\"line\">51</span><br><span class=\"line\">52</span><br><span class=\"line\">53</span><br><span class=\"line\">54</span><br><span class=\"line\">55</span><br><span class=\"line\">56</span><br><span class=\"line\">57</span><br><span class=\"line\">58</span><br><span class=\"line\">59</span><br><span class=\"line\">60</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"></span><br><span class=\"line\"><span class=\"keyword\">public</span> <span class=\"keyword\">class</span> <span class=\"title class_\">Test</span> { </span><br><span class=\"line\"> <span class=\"keyword\">public</span> <span class=\"keyword\">static</span> <span class=\"keyword\">void</span> <span class=\"title function_\">main</span><span class=\"params\">(String[] args)</span> {</span><br><span class=\"line\"> <span class=\"type\">int</span>[] nums = {<span class=\"number\">1</span>, <span class=\"number\">2</span>, <span class=\"number\">3</span>, <span class=\"number\">2</span>, <span class=\"number\">2</span>, <span class=\"number\">4</span>, <span class=\"number\">2</span>};</span><br><span class=\"line\"> System.out.println(findMajors(nums));</span><br><span class=\"line\"> <span class=\"type\">int</span>[] nums2 = {<span class=\"number\">1</span>, <span class=\"number\">2</span>, <span class=\"number\">3</span>, <span class=\"number\">2</span>, <span class=\"number\">2</span>, <span class=\"number\">4</span>, <span class=\"number\">2</span>, <span class=\"number\">4</span>, <span class=\"number\">4</span>, <span class=\"number\">4</span>, <span class=\"number\">4</span>, <span class=\"number\">4</span>, <span class=\"number\">4</span>};</span><br><span class=\"line\"> System.out.println(findMajors(nums2));</span><br><span class=\"line\"> System.out.println(molVotes(nums));</span><br><span class=\"line\"> System.out.println(molVotes(nums2));</span><br><span class=\"line\"> }</span><br><span class=\"line\"> <span class=\"comment\">/**</span></span><br><span class=\"line\"><span class=\"comment\"> * 摩尔投票法</span></span><br><span class=\"line\"><span class=\"comment\"> * 如果超过m分之一 则可以选取m-1个侯选人</span></span><br><span class=\"line\"><span class=\"comment\"> * 例如超过二分之一,肯定只能选一个侯选人</span></span><br><span class=\"line\"><span class=\"comment\"> * 超过三分之一,则可以选二个侯选人</span></span><br><span class=\"line\"><span class=\"comment\"> */</span></span><br><span class=\"line\"> <span class=\"keyword\">static</span> <span class=\"type\">int</span> <span class=\"title function_\">molVotes</span><span class=\"params\">(<span class=\"type\">int</span>[] nums)</span> {</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">c1</span> <span class=\"operator\">=</span> nums[<span class=\"number\">0</span>];</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">cnt1</span> <span class=\"operator\">=</span> <span class=\"number\">1</span>;</span><br><span class=\"line\"> <span class=\"keyword\">for</span> (<span class=\"type\">int</span> <span class=\"variable\">i</span> <span class=\"operator\">=</span> <span class=\"number\">1</span>; i < nums.length; i++) {</span><br><span class=\"line\"> <span class=\"keyword\">if</span> (c1 == nums[i]) cnt1++;</span><br><span class=\"line\"> <span class=\"keyword\">else</span> cnt1 -- ;</span><br><span class=\"line\"> <span class=\"keyword\">if</span>(cnt1 == <span class=\"number\">0</span>){</span><br><span class=\"line\"> c1 = nums[i];</span><br><span class=\"line\"> cnt1 = <span class=\"number\">1</span>;</span><br><span class=\"line\"> }</span><br><span class=\"line\"> }</span><br><span class=\"line\"> <span class=\"keyword\">return</span> c1;</span><br><span class=\"line\"> }</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"keyword\">static</span> <span class=\"keyword\">void</span> <span class=\"title function_\">swap</span><span class=\"params\">(<span class=\"type\">int</span>[] nums, <span class=\"type\">int</span> i, <span class=\"type\">int</span> j)</span> {</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">t</span> <span class=\"operator\">=</span> nums[i];</span><br><span class=\"line\"> nums[i] = nums[j];</span><br><span class=\"line\"> nums[j] = t;</span><br><span class=\"line\"> }</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"keyword\">static</span> <span class=\"type\">int</span> <span class=\"title function_\">partition</span><span class=\"params\">(<span class=\"type\">int</span>[] nums, <span class=\"type\">int</span> left, <span class=\"type\">int</span> right)</span> {</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">i</span> <span class=\"operator\">=</span> left;</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">j</span> <span class=\"operator\">=</span> left;</span><br><span class=\"line\"> <span class=\"keyword\">while</span> (j <= right) {</span><br><span class=\"line\"> <span class=\"keyword\">if</span> (nums[j] < nums[left]) swap(nums, j, ++i);</span><br><span class=\"line\"> j++;</span><br><span class=\"line\"> }</span><br><span class=\"line\"> swap(nums, i, left);</span><br><span class=\"line\"> <span class=\"keyword\">return</span> i;</span><br><span class=\"line\"> }</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"keyword\">static</span> <span class=\"type\">int</span> <span class=\"title function_\">findMajors</span><span class=\"params\">(<span class=\"type\">int</span>[] nums)</span> {</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">left</span> <span class=\"operator\">=</span> <span class=\"number\">0</span>;</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">right</span> <span class=\"operator\">=</span> nums.length - <span class=\"number\">1</span>;</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">k</span> <span class=\"operator\">=</span> nums.length >> <span class=\"number\">1</span>;</span><br><span class=\"line\"> <span class=\"keyword\">while</span> (left <= right) {</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">pivot</span> <span class=\"operator\">=</span> partition(nums, left, right);</span><br><span class=\"line\"> <span class=\"keyword\">if</span> (pivot == k) <span class=\"keyword\">return</span> nums[k];</span><br><span class=\"line\"> <span class=\"keyword\">if</span> (pivot < k) left = pivot + <span class=\"number\">1</span>;</span><br><span class=\"line\"> <span class=\"keyword\">if</span> (pivot > k) right = pivot - <span class=\"number\">1</span>;</span><br><span class=\"line\"> }</span><br><span class=\"line\"> <span class=\"keyword\">return</span> -<span class=\"number\">1</span>;</span><br><span class=\"line\"> }</span><br><span class=\"line\">}</span><br></pre></td></tr></table></figure></li>\n</ol>\n","site":{"data":{}},"excerpt":"","more":"<h4 id=\"经典算法之求众数\"><a href=\"#经典算法之求众数\" class=\"headerlink\" title=\"经典算法之求众数\"></a>经典算法之求众数</h4><p><strong>众数</strong>是一组数据中出现最多次数的那个数。<br>题目:如果一个数在数组中出现次数超过一半,如何设计一个高效的算法,求出这个数?<br>最直接的想法就是使用hashmap,遍历一遍数组,以数字作为key,以出现次数做为value。<br>另一种直接的想法就是使用另外一个数组来代替hashmap来实现类似的效果,使用下标来表示<br>数字,使用值来表示出现的次数,就是位图排序的思路。<br>下面说两种对空间要求没那么高的算法。</p>\n<ol>\n<li>设想如果我们对这个数组排序,则位于N/2位置的数字肯定就是这个超过一半数字的众数,<br>所以这个题就可以转成求N/2位置的数字,可以使用求TOPK的算法,即求第K个位置的数字。<br>使用快选法来解决即可,只是K固定为N/2.</li>\n<li>使用<strong>摩尔投票法</strong>,这个算法不仅可以用来处理大于一半的众数,还可以求大于任意M分之一情况下<br>的众数,譬如大于三分之一,就可以选择两个侯选人。<br>它的思路是假设有一个侯选人,就是nums[0],起始自己投了自己一票,遍历其余数字,如果数字相等,<br>投票数则加1,否则减1,如果投票数等于0,则换成当前的新数字,投票数置1,一直遍历到结束。<br>最后的侯选人就是众数。<br>下面是java代码:<figure class=\"highlight java\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br><span class=\"line\">11</span><br><span class=\"line\">12</span><br><span class=\"line\">13</span><br><span class=\"line\">14</span><br><span class=\"line\">15</span><br><span class=\"line\">16</span><br><span class=\"line\">17</span><br><span class=\"line\">18</span><br><span class=\"line\">19</span><br><span class=\"line\">20</span><br><span class=\"line\">21</span><br><span class=\"line\">22</span><br><span class=\"line\">23</span><br><span class=\"line\">24</span><br><span class=\"line\">25</span><br><span class=\"line\">26</span><br><span class=\"line\">27</span><br><span class=\"line\">28</span><br><span class=\"line\">29</span><br><span class=\"line\">30</span><br><span class=\"line\">31</span><br><span class=\"line\">32</span><br><span class=\"line\">33</span><br><span class=\"line\">34</span><br><span class=\"line\">35</span><br><span class=\"line\">36</span><br><span class=\"line\">37</span><br><span class=\"line\">38</span><br><span class=\"line\">39</span><br><span class=\"line\">40</span><br><span class=\"line\">41</span><br><span class=\"line\">42</span><br><span class=\"line\">43</span><br><span class=\"line\">44</span><br><span class=\"line\">45</span><br><span class=\"line\">46</span><br><span class=\"line\">47</span><br><span class=\"line\">48</span><br><span class=\"line\">49</span><br><span class=\"line\">50</span><br><span class=\"line\">51</span><br><span class=\"line\">52</span><br><span class=\"line\">53</span><br><span class=\"line\">54</span><br><span class=\"line\">55</span><br><span class=\"line\">56</span><br><span class=\"line\">57</span><br><span class=\"line\">58</span><br><span class=\"line\">59</span><br><span class=\"line\">60</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"></span><br><span class=\"line\"><span class=\"keyword\">public</span> <span class=\"keyword\">class</span> <span class=\"title class_\">Test</span> { </span><br><span class=\"line\"> <span class=\"keyword\">public</span> <span class=\"keyword\">static</span> <span class=\"keyword\">void</span> <span class=\"title function_\">main</span><span class=\"params\">(String[] args)</span> {</span><br><span class=\"line\"> <span class=\"type\">int</span>[] nums = {<span class=\"number\">1</span>, <span class=\"number\">2</span>, <span class=\"number\">3</span>, <span class=\"number\">2</span>, <span class=\"number\">2</span>, <span class=\"number\">4</span>, <span class=\"number\">2</span>};</span><br><span class=\"line\"> System.out.println(findMajors(nums));</span><br><span class=\"line\"> <span class=\"type\">int</span>[] nums2 = {<span class=\"number\">1</span>, <span class=\"number\">2</span>, <span class=\"number\">3</span>, <span class=\"number\">2</span>, <span class=\"number\">2</span>, <span class=\"number\">4</span>, <span class=\"number\">2</span>, <span class=\"number\">4</span>, <span class=\"number\">4</span>, <span class=\"number\">4</span>, <span class=\"number\">4</span>, <span class=\"number\">4</span>, <span class=\"number\">4</span>};</span><br><span class=\"line\"> System.out.println(findMajors(nums2));</span><br><span class=\"line\"> System.out.println(molVotes(nums));</span><br><span class=\"line\"> System.out.println(molVotes(nums2));</span><br><span class=\"line\"> }</span><br><span class=\"line\"> <span class=\"comment\">/**</span></span><br><span class=\"line\"><span class=\"comment\"> * 摩尔投票法</span></span><br><span class=\"line\"><span class=\"comment\"> * 如果超过m分之一 则可以选取m-1个侯选人</span></span><br><span class=\"line\"><span class=\"comment\"> * 例如超过二分之一,肯定只能选一个侯选人</span></span><br><span class=\"line\"><span class=\"comment\"> * 超过三分之一,则可以选二个侯选人</span></span><br><span class=\"line\"><span class=\"comment\"> */</span></span><br><span class=\"line\"> <span class=\"keyword\">static</span> <span class=\"type\">int</span> <span class=\"title function_\">molVotes</span><span class=\"params\">(<span class=\"type\">int</span>[] nums)</span> {</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">c1</span> <span class=\"operator\">=</span> nums[<span class=\"number\">0</span>];</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">cnt1</span> <span class=\"operator\">=</span> <span class=\"number\">1</span>;</span><br><span class=\"line\"> <span class=\"keyword\">for</span> (<span class=\"type\">int</span> <span class=\"variable\">i</span> <span class=\"operator\">=</span> <span class=\"number\">1</span>; i < nums.length; i++) {</span><br><span class=\"line\"> <span class=\"keyword\">if</span> (c1 == nums[i]) cnt1++;</span><br><span class=\"line\"> <span class=\"keyword\">else</span> cnt1 -- ;</span><br><span class=\"line\"> <span class=\"keyword\">if</span>(cnt1 == <span class=\"number\">0</span>){</span><br><span class=\"line\"> c1 = nums[i];</span><br><span class=\"line\"> cnt1 = <span class=\"number\">1</span>;</span><br><span class=\"line\"> }</span><br><span class=\"line\"> }</span><br><span class=\"line\"> <span class=\"keyword\">return</span> c1;</span><br><span class=\"line\"> }</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"keyword\">static</span> <span class=\"keyword\">void</span> <span class=\"title function_\">swap</span><span class=\"params\">(<span class=\"type\">int</span>[] nums, <span class=\"type\">int</span> i, <span class=\"type\">int</span> j)</span> {</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">t</span> <span class=\"operator\">=</span> nums[i];</span><br><span class=\"line\"> nums[i] = nums[j];</span><br><span class=\"line\"> nums[j] = t;</span><br><span class=\"line\"> }</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"keyword\">static</span> <span class=\"type\">int</span> <span class=\"title function_\">partition</span><span class=\"params\">(<span class=\"type\">int</span>[] nums, <span class=\"type\">int</span> left, <span class=\"type\">int</span> right)</span> {</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">i</span> <span class=\"operator\">=</span> left;</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">j</span> <span class=\"operator\">=</span> left;</span><br><span class=\"line\"> <span class=\"keyword\">while</span> (j <= right) {</span><br><span class=\"line\"> <span class=\"keyword\">if</span> (nums[j] < nums[left]) swap(nums, j, ++i);</span><br><span class=\"line\"> j++;</span><br><span class=\"line\"> }</span><br><span class=\"line\"> swap(nums, i, left);</span><br><span class=\"line\"> <span class=\"keyword\">return</span> i;</span><br><span class=\"line\"> }</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"keyword\">static</span> <span class=\"type\">int</span> <span class=\"title function_\">findMajors</span><span class=\"params\">(<span class=\"type\">int</span>[] nums)</span> {</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">left</span> <span class=\"operator\">=</span> <span class=\"number\">0</span>;</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">right</span> <span class=\"operator\">=</span> nums.length - <span class=\"number\">1</span>;</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">k</span> <span class=\"operator\">=</span> nums.length >> <span class=\"number\">1</span>;</span><br><span class=\"line\"> <span class=\"keyword\">while</span> (left <= right) {</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">pivot</span> <span class=\"operator\">=</span> partition(nums, left, right);</span><br><span class=\"line\"> <span class=\"keyword\">if</span> (pivot == k) <span class=\"keyword\">return</span> nums[k];</span><br><span class=\"line\"> <span class=\"keyword\">if</span> (pivot < k) left = pivot + <span class=\"number\">1</span>;</span><br><span class=\"line\"> <span class=\"keyword\">if</span> (pivot > k) right = pivot - <span class=\"number\">1</span>;</span><br><span class=\"line\"> }</span><br><span class=\"line\"> <span class=\"keyword\">return</span> -<span class=\"number\">1</span>;</span><br><span class=\"line\"> }</span><br><span class=\"line\">}</span><br></pre></td></tr></table></figure></li>\n</ol>\n"},{"title":"链表环检算法","date":"2021-07-20T10:51:39.000Z","comments":1,"toc":true,"_content":"#### 链表环检算法\n1. 检查链表中是否存在环路\n2. 检查环路的长度\n3. 找到环路的开始节点\n\n\n##### **问题1** 判断链表中是否存在环路?\n通常使用快慢指针的方式,一个fast指针,一次走两步,\n一个slow指针,一次走一路,如果最后快指针很快指向了末尾,判断fast是否等于slow\n,相等即存在环路,否则不存在环路。\n\n**疑问** 会不会存在fast越过slow的情况?\n不会。为什么,假设在环路中,slow走了i,\nfast走了i+1,刚好越过了slow的情况发生了,则在上一路,slow在i-1,而fast在i+1-2=i-1,\n则在上一步它们就相遇了,矛盾,假设不成立。\n\n##### **问题2** 如何计算环路的长度?\n当slow=fast的时候,我们再让slow走一圈,再次碰到fast指针,\n中间记录长度,可以算出环路的长度。\n\n##### **问题3** 如何找到环路的入口节点?\n假设从head到入口节点之间有k个节点,则slow走了k个节点到入口节点的时候,fast刚好走了2k个\n节点,它距离slow的距离为loop_size - (k mod loop_size),\n当slow再走i个节点,即fast再走2i个节点,这个时候,fast遇到slow节点。\n这个时候,让fast指向head,当他们再次相遇的时候,就是开始的节点?为什么?\nloop_size - k + i = 2i \nloop_size = k + i\n现在slow距离入口处走了i,则再走k就到了环路的入口处。\n而head距离环路入口刚好是k,所以它们同时走,再次相遇的时候,肯定是在环路入口处。\n有了上面的分析,就可以写代码了。\n``` java\npublic class Test {\n\n static class ListNode {\n int val;\n public ListNode next;\n public int getVal() {\n return val;\n }\n public ListNode(int val, ListNode next) {\n this.val = val;\n this.next = next;\n }\n public ListNode(int val) {\n this.val = val;\n this.next = null;\n }\n }\n\n public static void main(String[] args) {\n ListNode n1 = new ListNode(1);\n ListNode n2 = new ListNode(2);\n ListNode n3 = new ListNode(3);\n ListNode n4 = new ListNode(4);\n ListNode n5 = new ListNode(5);\n ListNode n6 = new ListNode(6);\n ListNode n7 = new ListNode(7);\n\n n1.next = n2;\n n2.next = n3;\n n3.next = n4;\n n4.next = n5;\n n5.next = n6;\n n6.next = n7;\n\n System.out.println(checkCycle(n1));\n n7.next = n3;\n System.out.println(checkCycle(n1));\n\n ListNode entryPoint = findCycleEntrypoint(n1);\n System.out.println(entryPoint.getVal());\n\n }\n\n static boolean checkCycle(ListNode head) {\n if (head == null || head.next == null) return false;\n ListNode fast = head;\n ListNode slow = head;\n do {\n slow = slow.next;\n fast = fast.next;\n if (fast.next != null) fast = fast.next;\n } while (slow != fast && fast.next != null);\n return fast == slow;\n }\n\n //假设已经判定是有环路,不用再次判断\n static ListNode findCycleEntrypoint(ListNode head) {\n ListNode fast = head;\n ListNode slow = head;\n do {\n slow = slow.next;\n fast = fast.next;\n if (fast.next != null) fast = fast.next;\n } while (slow != fast);\n // then slow was catched by fast\n int i = 0;\n do {\n slow = slow.next;\n i++;\n } while (slow != fast);\n System.out.println(\"loop size is \" + i);\n slow = head;\n while (fast != slow) {\n slow = slow.next;\n fast = fast.next;\n }\n return slow;\n }\n}\n\n```\n","source":"_posts/链表环检算法.md","raw":"---\ntitle: 链表环检算法\ndate: 2021-07-20 18:51:39\ncomments: true #是否可评论\ntoc: true #是否显示文章目录\ncategories: \"算法\" #分类\ntags: #标签\n - JAVA\n - 面试题\n - 算法\n---\n#### 链表环检算法\n1. 检查链表中是否存在环路\n2. 检查环路的长度\n3. 找到环路的开始节点\n\n\n##### **问题1** 判断链表中是否存在环路?\n通常使用快慢指针的方式,一个fast指针,一次走两步,\n一个slow指针,一次走一路,如果最后快指针很快指向了末尾,判断fast是否等于slow\n,相等即存在环路,否则不存在环路。\n\n**疑问** 会不会存在fast越过slow的情况?\n不会。为什么,假设在环路中,slow走了i,\nfast走了i+1,刚好越过了slow的情况发生了,则在上一路,slow在i-1,而fast在i+1-2=i-1,\n则在上一步它们就相遇了,矛盾,假设不成立。\n\n##### **问题2** 如何计算环路的长度?\n当slow=fast的时候,我们再让slow走一圈,再次碰到fast指针,\n中间记录长度,可以算出环路的长度。\n\n##### **问题3** 如何找到环路的入口节点?\n假设从head到入口节点之间有k个节点,则slow走了k个节点到入口节点的时候,fast刚好走了2k个\n节点,它距离slow的距离为loop_size - (k mod loop_size),\n当slow再走i个节点,即fast再走2i个节点,这个时候,fast遇到slow节点。\n这个时候,让fast指向head,当他们再次相遇的时候,就是开始的节点?为什么?\nloop_size - k + i = 2i \nloop_size = k + i\n现在slow距离入口处走了i,则再走k就到了环路的入口处。\n而head距离环路入口刚好是k,所以它们同时走,再次相遇的时候,肯定是在环路入口处。\n有了上面的分析,就可以写代码了。\n``` java\npublic class Test {\n\n static class ListNode {\n int val;\n public ListNode next;\n public int getVal() {\n return val;\n }\n public ListNode(int val, ListNode next) {\n this.val = val;\n this.next = next;\n }\n public ListNode(int val) {\n this.val = val;\n this.next = null;\n }\n }\n\n public static void main(String[] args) {\n ListNode n1 = new ListNode(1);\n ListNode n2 = new ListNode(2);\n ListNode n3 = new ListNode(3);\n ListNode n4 = new ListNode(4);\n ListNode n5 = new ListNode(5);\n ListNode n6 = new ListNode(6);\n ListNode n7 = new ListNode(7);\n\n n1.next = n2;\n n2.next = n3;\n n3.next = n4;\n n4.next = n5;\n n5.next = n6;\n n6.next = n7;\n\n System.out.println(checkCycle(n1));\n n7.next = n3;\n System.out.println(checkCycle(n1));\n\n ListNode entryPoint = findCycleEntrypoint(n1);\n System.out.println(entryPoint.getVal());\n\n }\n\n static boolean checkCycle(ListNode head) {\n if (head == null || head.next == null) return false;\n ListNode fast = head;\n ListNode slow = head;\n do {\n slow = slow.next;\n fast = fast.next;\n if (fast.next != null) fast = fast.next;\n } while (slow != fast && fast.next != null);\n return fast == slow;\n }\n\n //假设已经判定是有环路,不用再次判断\n static ListNode findCycleEntrypoint(ListNode head) {\n ListNode fast = head;\n ListNode slow = head;\n do {\n slow = slow.next;\n fast = fast.next;\n if (fast.next != null) fast = fast.next;\n } while (slow != fast);\n // then slow was catched by fast\n int i = 0;\n do {\n slow = slow.next;\n i++;\n } while (slow != fast);\n System.out.println(\"loop size is \" + i);\n slow = head;\n while (fast != slow) {\n slow = slow.next;\n fast = fast.next;\n }\n return slow;\n }\n}\n\n```\n","slug":"链表环检算法","published":1,"updated":"2022-04-20T09:35:50.864Z","layout":"post","photos":[],"link":"","_id":"cl27q3uvf001on94o3q6y1mq6","content":"<h4 id=\"链表环检算法\"><a href=\"#链表环检算法\" class=\"headerlink\" title=\"链表环检算法\"></a>链表环检算法</h4><ol>\n<li>检查链表中是否存在环路</li>\n<li>检查环路的长度</li>\n<li>找到环路的开始节点</li>\n</ol>\n<h5 id=\"问题1-判断链表中是否存在环路\"><a href=\"#问题1-判断链表中是否存在环路\" class=\"headerlink\" title=\"问题1 判断链表中是否存在环路?\"></a><strong>问题1</strong> 判断链表中是否存在环路?</h5><p>通常使用快慢指针的方式,一个fast指针,一次走两步,<br>一个slow指针,一次走一路,如果最后快指针很快指向了末尾,判断fast是否等于slow<br>,相等即存在环路,否则不存在环路。</p>\n<p><strong>疑问</strong> 会不会存在fast越过slow的情况?<br>不会。为什么,假设在环路中,slow走了i,<br>fast走了i+1,刚好越过了slow的情况发生了,则在上一路,slow在i-1,而fast在i+1-2=i-1,<br>则在上一步它们就相遇了,矛盾,假设不成立。</p>\n<h5 id=\"问题2-如何计算环路的长度\"><a href=\"#问题2-如何计算环路的长度\" class=\"headerlink\" title=\"问题2 如何计算环路的长度?\"></a><strong>问题2</strong> 如何计算环路的长度?</h5><p>当slow=fast的时候,我们再让slow走一圈,再次碰到fast指针,<br>中间记录长度,可以算出环路的长度。</p>\n<h5 id=\"问题3-如何找到环路的入口节点?\"><a href=\"#问题3-如何找到环路的入口节点?\" class=\"headerlink\" title=\"问题3 如何找到环路的入口节点?\"></a><strong>问题3</strong> 如何找到环路的入口节点?</h5><p>假设从head到入口节点之间有k个节点,则slow走了k个节点到入口节点的时候,fast刚好走了2k个<br>节点,它距离slow的距离为loop_size - (k mod loop_size),<br>当slow再走i个节点,即fast再走2i个节点,这个时候,fast遇到slow节点。<br>这个时候,让fast指向head,当他们再次相遇的时候,就是开始的节点?为什么?<br>loop_size - k + i = 2i<br>loop_size = k + i<br>现在slow距离入口处走了i,则再走k就到了环路的入口处。<br>而head距离环路入口刚好是k,所以它们同时走,再次相遇的时候,肯定是在环路入口处。<br>有了上面的分析,就可以写代码了。</p>\n<figure class=\"highlight java\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br><span class=\"line\">11</span><br><span class=\"line\">12</span><br><span class=\"line\">13</span><br><span class=\"line\">14</span><br><span class=\"line\">15</span><br><span class=\"line\">16</span><br><span class=\"line\">17</span><br><span class=\"line\">18</span><br><span class=\"line\">19</span><br><span class=\"line\">20</span><br><span class=\"line\">21</span><br><span class=\"line\">22</span><br><span class=\"line\">23</span><br><span class=\"line\">24</span><br><span class=\"line\">25</span><br><span class=\"line\">26</span><br><span class=\"line\">27</span><br><span class=\"line\">28</span><br><span class=\"line\">29</span><br><span class=\"line\">30</span><br><span class=\"line\">31</span><br><span class=\"line\">32</span><br><span class=\"line\">33</span><br><span class=\"line\">34</span><br><span class=\"line\">35</span><br><span class=\"line\">36</span><br><span class=\"line\">37</span><br><span class=\"line\">38</span><br><span class=\"line\">39</span><br><span class=\"line\">40</span><br><span class=\"line\">41</span><br><span class=\"line\">42</span><br><span class=\"line\">43</span><br><span class=\"line\">44</span><br><span class=\"line\">45</span><br><span class=\"line\">46</span><br><span class=\"line\">47</span><br><span class=\"line\">48</span><br><span class=\"line\">49</span><br><span class=\"line\">50</span><br><span class=\"line\">51</span><br><span class=\"line\">52</span><br><span class=\"line\">53</span><br><span class=\"line\">54</span><br><span class=\"line\">55</span><br><span class=\"line\">56</span><br><span class=\"line\">57</span><br><span class=\"line\">58</span><br><span class=\"line\">59</span><br><span class=\"line\">60</span><br><span class=\"line\">61</span><br><span class=\"line\">62</span><br><span class=\"line\">63</span><br><span class=\"line\">64</span><br><span class=\"line\">65</span><br><span class=\"line\">66</span><br><span class=\"line\">67</span><br><span class=\"line\">68</span><br><span class=\"line\">69</span><br><span class=\"line\">70</span><br><span class=\"line\">71</span><br><span class=\"line\">72</span><br><span class=\"line\">73</span><br><span class=\"line\">74</span><br><span class=\"line\">75</span><br><span class=\"line\">76</span><br><span class=\"line\">77</span><br><span class=\"line\">78</span><br><span class=\"line\">79</span><br><span class=\"line\">80</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"keyword\">public</span> <span class=\"keyword\">class</span> <span class=\"title class_\">Test</span> {</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"keyword\">static</span> <span class=\"keyword\">class</span> <span class=\"title class_\">ListNode</span> {</span><br><span class=\"line\"> <span class=\"type\">int</span> val;</span><br><span class=\"line\"> <span class=\"keyword\">public</span> ListNode next;</span><br><span class=\"line\"> <span class=\"keyword\">public</span> <span class=\"type\">int</span> <span class=\"title function_\">getVal</span><span class=\"params\">()</span> {</span><br><span class=\"line\"> <span class=\"keyword\">return</span> val;</span><br><span class=\"line\"> }</span><br><span class=\"line\"> <span class=\"keyword\">public</span> <span class=\"title function_\">ListNode</span><span class=\"params\">(<span class=\"type\">int</span> val, ListNode next)</span> {</span><br><span class=\"line\"> <span class=\"built_in\">this</span>.val = val;</span><br><span class=\"line\"> <span class=\"built_in\">this</span>.next = next;</span><br><span class=\"line\"> }</span><br><span class=\"line\"> <span class=\"keyword\">public</span> <span class=\"title function_\">ListNode</span><span class=\"params\">(<span class=\"type\">int</span> val)</span> {</span><br><span class=\"line\"> <span class=\"built_in\">this</span>.val = val;</span><br><span class=\"line\"> <span class=\"built_in\">this</span>.next = <span class=\"literal\">null</span>;</span><br><span class=\"line\"> }</span><br><span class=\"line\"> }</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"keyword\">public</span> <span class=\"keyword\">static</span> <span class=\"keyword\">void</span> <span class=\"title function_\">main</span><span class=\"params\">(String[] args)</span> {</span><br><span class=\"line\"> <span class=\"type\">ListNode</span> <span class=\"variable\">n1</span> <span class=\"operator\">=</span> <span class=\"keyword\">new</span> <span class=\"title class_\">ListNode</span>(<span class=\"number\">1</span>);</span><br><span class=\"line\"> <span class=\"type\">ListNode</span> <span class=\"variable\">n2</span> <span class=\"operator\">=</span> <span class=\"keyword\">new</span> <span class=\"title class_\">ListNode</span>(<span class=\"number\">2</span>);</span><br><span class=\"line\"> <span class=\"type\">ListNode</span> <span class=\"variable\">n3</span> <span class=\"operator\">=</span> <span class=\"keyword\">new</span> <span class=\"title class_\">ListNode</span>(<span class=\"number\">3</span>);</span><br><span class=\"line\"> <span class=\"type\">ListNode</span> <span class=\"variable\">n4</span> <span class=\"operator\">=</span> <span class=\"keyword\">new</span> <span class=\"title class_\">ListNode</span>(<span class=\"number\">4</span>);</span><br><span class=\"line\"> <span class=\"type\">ListNode</span> <span class=\"variable\">n5</span> <span class=\"operator\">=</span> <span class=\"keyword\">new</span> <span class=\"title class_\">ListNode</span>(<span class=\"number\">5</span>);</span><br><span class=\"line\"> <span class=\"type\">ListNode</span> <span class=\"variable\">n6</span> <span class=\"operator\">=</span> <span class=\"keyword\">new</span> <span class=\"title class_\">ListNode</span>(<span class=\"number\">6</span>);</span><br><span class=\"line\"> <span class=\"type\">ListNode</span> <span class=\"variable\">n7</span> <span class=\"operator\">=</span> <span class=\"keyword\">new</span> <span class=\"title class_\">ListNode</span>(<span class=\"number\">7</span>);</span><br><span class=\"line\"></span><br><span class=\"line\"> n1.next = n2;</span><br><span class=\"line\"> n2.next = n3;</span><br><span class=\"line\"> n3.next = n4;</span><br><span class=\"line\"> n4.next = n5;</span><br><span class=\"line\"> n5.next = n6;</span><br><span class=\"line\"> n6.next = n7;</span><br><span class=\"line\"></span><br><span class=\"line\"> System.out.println(checkCycle(n1));</span><br><span class=\"line\"> n7.next = n3;</span><br><span class=\"line\"> System.out.println(checkCycle(n1));</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"type\">ListNode</span> <span class=\"variable\">entryPoint</span> <span class=\"operator\">=</span> findCycleEntrypoint(n1);</span><br><span class=\"line\"> System.out.println(entryPoint.getVal());</span><br><span class=\"line\"></span><br><span class=\"line\"> }</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"keyword\">static</span> <span class=\"type\">boolean</span> <span class=\"title function_\">checkCycle</span><span class=\"params\">(ListNode head)</span> {</span><br><span class=\"line\"> <span class=\"keyword\">if</span> (head == <span class=\"literal\">null</span> || head.next == <span class=\"literal\">null</span>) <span class=\"keyword\">return</span> <span class=\"literal\">false</span>;</span><br><span class=\"line\"> <span class=\"type\">ListNode</span> <span class=\"variable\">fast</span> <span class=\"operator\">=</span> head;</span><br><span class=\"line\"> <span class=\"type\">ListNode</span> <span class=\"variable\">slow</span> <span class=\"operator\">=</span> head;</span><br><span class=\"line\"> <span class=\"keyword\">do</span> {</span><br><span class=\"line\"> slow = slow.next;</span><br><span class=\"line\"> fast = fast.next;</span><br><span class=\"line\"> <span class=\"keyword\">if</span> (fast.next != <span class=\"literal\">null</span>) fast = fast.next;</span><br><span class=\"line\"> } <span class=\"keyword\">while</span> (slow != fast && fast.next != <span class=\"literal\">null</span>);</span><br><span class=\"line\"> <span class=\"type\">return</span> <span class=\"variable\">fast</span> <span class=\"operator\">=</span>= slow;</span><br><span class=\"line\"> }</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"comment\">//假设已经判定是有环路,不用再次判断</span></span><br><span class=\"line\"> <span class=\"keyword\">static</span> ListNode <span class=\"title function_\">findCycleEntrypoint</span><span class=\"params\">(ListNode head)</span> {</span><br><span class=\"line\"> <span class=\"type\">ListNode</span> <span class=\"variable\">fast</span> <span class=\"operator\">=</span> head;</span><br><span class=\"line\"> <span class=\"type\">ListNode</span> <span class=\"variable\">slow</span> <span class=\"operator\">=</span> head;</span><br><span class=\"line\"> <span class=\"keyword\">do</span> {</span><br><span class=\"line\"> slow = slow.next;</span><br><span class=\"line\"> fast = fast.next;</span><br><span class=\"line\"> <span class=\"keyword\">if</span> (fast.next != <span class=\"literal\">null</span>) fast = fast.next;</span><br><span class=\"line\"> } <span class=\"keyword\">while</span> (slow != fast);</span><br><span class=\"line\"> <span class=\"comment\">// then slow was catched by fast</span></span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">i</span> <span class=\"operator\">=</span> <span class=\"number\">0</span>;</span><br><span class=\"line\"> <span class=\"keyword\">do</span> {</span><br><span class=\"line\"> slow = slow.next;</span><br><span class=\"line\"> i++;</span><br><span class=\"line\"> } <span class=\"keyword\">while</span> (slow != fast);</span><br><span class=\"line\"> System.out.println(<span class=\"string\">"loop size is "</span> + i);</span><br><span class=\"line\"> slow = head;</span><br><span class=\"line\"> <span class=\"keyword\">while</span> (fast != slow) {</span><br><span class=\"line\"> slow = slow.next;</span><br><span class=\"line\"> fast = fast.next;</span><br><span class=\"line\"> }</span><br><span class=\"line\"> <span class=\"keyword\">return</span> slow;</span><br><span class=\"line\"> }</span><br><span class=\"line\">}</span><br><span class=\"line\"></span><br></pre></td></tr></table></figure>\n","site":{"data":{}},"excerpt":"","more":"<h4 id=\"链表环检算法\"><a href=\"#链表环检算法\" class=\"headerlink\" title=\"链表环检算法\"></a>链表环检算法</h4><ol>\n<li>检查链表中是否存在环路</li>\n<li>检查环路的长度</li>\n<li>找到环路的开始节点</li>\n</ol>\n<h5 id=\"问题1-判断链表中是否存在环路\"><a href=\"#问题1-判断链表中是否存在环路\" class=\"headerlink\" title=\"问题1 判断链表中是否存在环路?\"></a><strong>问题1</strong> 判断链表中是否存在环路?</h5><p>通常使用快慢指针的方式,一个fast指针,一次走两步,<br>一个slow指针,一次走一路,如果最后快指针很快指向了末尾,判断fast是否等于slow<br>,相等即存在环路,否则不存在环路。</p>\n<p><strong>疑问</strong> 会不会存在fast越过slow的情况?<br>不会。为什么,假设在环路中,slow走了i,<br>fast走了i+1,刚好越过了slow的情况发生了,则在上一路,slow在i-1,而fast在i+1-2=i-1,<br>则在上一步它们就相遇了,矛盾,假设不成立。</p>\n<h5 id=\"问题2-如何计算环路的长度\"><a href=\"#问题2-如何计算环路的长度\" class=\"headerlink\" title=\"问题2 如何计算环路的长度?\"></a><strong>问题2</strong> 如何计算环路的长度?</h5><p>当slow=fast的时候,我们再让slow走一圈,再次碰到fast指针,<br>中间记录长度,可以算出环路的长度。</p>\n<h5 id=\"问题3-如何找到环路的入口节点?\"><a href=\"#问题3-如何找到环路的入口节点?\" class=\"headerlink\" title=\"问题3 如何找到环路的入口节点?\"></a><strong>问题3</strong> 如何找到环路的入口节点?</h5><p>假设从head到入口节点之间有k个节点,则slow走了k个节点到入口节点的时候,fast刚好走了2k个<br>节点,它距离slow的距离为loop_size - (k mod loop_size),<br>当slow再走i个节点,即fast再走2i个节点,这个时候,fast遇到slow节点。<br>这个时候,让fast指向head,当他们再次相遇的时候,就是开始的节点?为什么?<br>loop_size - k + i = 2i<br>loop_size = k + i<br>现在slow距离入口处走了i,则再走k就到了环路的入口处。<br>而head距离环路入口刚好是k,所以它们同时走,再次相遇的时候,肯定是在环路入口处。<br>有了上面的分析,就可以写代码了。</p>\n<figure class=\"highlight java\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br><span class=\"line\">11</span><br><span class=\"line\">12</span><br><span class=\"line\">13</span><br><span class=\"line\">14</span><br><span class=\"line\">15</span><br><span class=\"line\">16</span><br><span class=\"line\">17</span><br><span class=\"line\">18</span><br><span class=\"line\">19</span><br><span class=\"line\">20</span><br><span class=\"line\">21</span><br><span class=\"line\">22</span><br><span class=\"line\">23</span><br><span class=\"line\">24</span><br><span class=\"line\">25</span><br><span class=\"line\">26</span><br><span class=\"line\">27</span><br><span class=\"line\">28</span><br><span class=\"line\">29</span><br><span class=\"line\">30</span><br><span class=\"line\">31</span><br><span class=\"line\">32</span><br><span class=\"line\">33</span><br><span class=\"line\">34</span><br><span class=\"line\">35</span><br><span class=\"line\">36</span><br><span class=\"line\">37</span><br><span class=\"line\">38</span><br><span class=\"line\">39</span><br><span class=\"line\">40</span><br><span class=\"line\">41</span><br><span class=\"line\">42</span><br><span class=\"line\">43</span><br><span class=\"line\">44</span><br><span class=\"line\">45</span><br><span class=\"line\">46</span><br><span class=\"line\">47</span><br><span class=\"line\">48</span><br><span class=\"line\">49</span><br><span class=\"line\">50</span><br><span class=\"line\">51</span><br><span class=\"line\">52</span><br><span class=\"line\">53</span><br><span class=\"line\">54</span><br><span class=\"line\">55</span><br><span class=\"line\">56</span><br><span class=\"line\">57</span><br><span class=\"line\">58</span><br><span class=\"line\">59</span><br><span class=\"line\">60</span><br><span class=\"line\">61</span><br><span class=\"line\">62</span><br><span class=\"line\">63</span><br><span class=\"line\">64</span><br><span class=\"line\">65</span><br><span class=\"line\">66</span><br><span class=\"line\">67</span><br><span class=\"line\">68</span><br><span class=\"line\">69</span><br><span class=\"line\">70</span><br><span class=\"line\">71</span><br><span class=\"line\">72</span><br><span class=\"line\">73</span><br><span class=\"line\">74</span><br><span class=\"line\">75</span><br><span class=\"line\">76</span><br><span class=\"line\">77</span><br><span class=\"line\">78</span><br><span class=\"line\">79</span><br><span class=\"line\">80</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"keyword\">public</span> <span class=\"keyword\">class</span> <span class=\"title class_\">Test</span> {</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"keyword\">static</span> <span class=\"keyword\">class</span> <span class=\"title class_\">ListNode</span> {</span><br><span class=\"line\"> <span class=\"type\">int</span> val;</span><br><span class=\"line\"> <span class=\"keyword\">public</span> ListNode next;</span><br><span class=\"line\"> <span class=\"keyword\">public</span> <span class=\"type\">int</span> <span class=\"title function_\">getVal</span><span class=\"params\">()</span> {</span><br><span class=\"line\"> <span class=\"keyword\">return</span> val;</span><br><span class=\"line\"> }</span><br><span class=\"line\"> <span class=\"keyword\">public</span> <span class=\"title function_\">ListNode</span><span class=\"params\">(<span class=\"type\">int</span> val, ListNode next)</span> {</span><br><span class=\"line\"> <span class=\"built_in\">this</span>.val = val;</span><br><span class=\"line\"> <span class=\"built_in\">this</span>.next = next;</span><br><span class=\"line\"> }</span><br><span class=\"line\"> <span class=\"keyword\">public</span> <span class=\"title function_\">ListNode</span><span class=\"params\">(<span class=\"type\">int</span> val)</span> {</span><br><span class=\"line\"> <span class=\"built_in\">this</span>.val = val;</span><br><span class=\"line\"> <span class=\"built_in\">this</span>.next = <span class=\"literal\">null</span>;</span><br><span class=\"line\"> }</span><br><span class=\"line\"> }</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"keyword\">public</span> <span class=\"keyword\">static</span> <span class=\"keyword\">void</span> <span class=\"title function_\">main</span><span class=\"params\">(String[] args)</span> {</span><br><span class=\"line\"> <span class=\"type\">ListNode</span> <span class=\"variable\">n1</span> <span class=\"operator\">=</span> <span class=\"keyword\">new</span> <span class=\"title class_\">ListNode</span>(<span class=\"number\">1</span>);</span><br><span class=\"line\"> <span class=\"type\">ListNode</span> <span class=\"variable\">n2</span> <span class=\"operator\">=</span> <span class=\"keyword\">new</span> <span class=\"title class_\">ListNode</span>(<span class=\"number\">2</span>);</span><br><span class=\"line\"> <span class=\"type\">ListNode</span> <span class=\"variable\">n3</span> <span class=\"operator\">=</span> <span class=\"keyword\">new</span> <span class=\"title class_\">ListNode</span>(<span class=\"number\">3</span>);</span><br><span class=\"line\"> <span class=\"type\">ListNode</span> <span class=\"variable\">n4</span> <span class=\"operator\">=</span> <span class=\"keyword\">new</span> <span class=\"title class_\">ListNode</span>(<span class=\"number\">4</span>);</span><br><span class=\"line\"> <span class=\"type\">ListNode</span> <span class=\"variable\">n5</span> <span class=\"operator\">=</span> <span class=\"keyword\">new</span> <span class=\"title class_\">ListNode</span>(<span class=\"number\">5</span>);</span><br><span class=\"line\"> <span class=\"type\">ListNode</span> <span class=\"variable\">n6</span> <span class=\"operator\">=</span> <span class=\"keyword\">new</span> <span class=\"title class_\">ListNode</span>(<span class=\"number\">6</span>);</span><br><span class=\"line\"> <span class=\"type\">ListNode</span> <span class=\"variable\">n7</span> <span class=\"operator\">=</span> <span class=\"keyword\">new</span> <span class=\"title class_\">ListNode</span>(<span class=\"number\">7</span>);</span><br><span class=\"line\"></span><br><span class=\"line\"> n1.next = n2;</span><br><span class=\"line\"> n2.next = n3;</span><br><span class=\"line\"> n3.next = n4;</span><br><span class=\"line\"> n4.next = n5;</span><br><span class=\"line\"> n5.next = n6;</span><br><span class=\"line\"> n6.next = n7;</span><br><span class=\"line\"></span><br><span class=\"line\"> System.out.println(checkCycle(n1));</span><br><span class=\"line\"> n7.next = n3;</span><br><span class=\"line\"> System.out.println(checkCycle(n1));</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"type\">ListNode</span> <span class=\"variable\">entryPoint</span> <span class=\"operator\">=</span> findCycleEntrypoint(n1);</span><br><span class=\"line\"> System.out.println(entryPoint.getVal());</span><br><span class=\"line\"></span><br><span class=\"line\"> }</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"keyword\">static</span> <span class=\"type\">boolean</span> <span class=\"title function_\">checkCycle</span><span class=\"params\">(ListNode head)</span> {</span><br><span class=\"line\"> <span class=\"keyword\">if</span> (head == <span class=\"literal\">null</span> || head.next == <span class=\"literal\">null</span>) <span class=\"keyword\">return</span> <span class=\"literal\">false</span>;</span><br><span class=\"line\"> <span class=\"type\">ListNode</span> <span class=\"variable\">fast</span> <span class=\"operator\">=</span> head;</span><br><span class=\"line\"> <span class=\"type\">ListNode</span> <span class=\"variable\">slow</span> <span class=\"operator\">=</span> head;</span><br><span class=\"line\"> <span class=\"keyword\">do</span> {</span><br><span class=\"line\"> slow = slow.next;</span><br><span class=\"line\"> fast = fast.next;</span><br><span class=\"line\"> <span class=\"keyword\">if</span> (fast.next != <span class=\"literal\">null</span>) fast = fast.next;</span><br><span class=\"line\"> } <span class=\"keyword\">while</span> (slow != fast && fast.next != <span class=\"literal\">null</span>);</span><br><span class=\"line\"> <span class=\"type\">return</span> <span class=\"variable\">fast</span> <span class=\"operator\">=</span>= slow;</span><br><span class=\"line\"> }</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"comment\">//假设已经判定是有环路,不用再次判断</span></span><br><span class=\"line\"> <span class=\"keyword\">static</span> ListNode <span class=\"title function_\">findCycleEntrypoint</span><span class=\"params\">(ListNode head)</span> {</span><br><span class=\"line\"> <span class=\"type\">ListNode</span> <span class=\"variable\">fast</span> <span class=\"operator\">=</span> head;</span><br><span class=\"line\"> <span class=\"type\">ListNode</span> <span class=\"variable\">slow</span> <span class=\"operator\">=</span> head;</span><br><span class=\"line\"> <span class=\"keyword\">do</span> {</span><br><span class=\"line\"> slow = slow.next;</span><br><span class=\"line\"> fast = fast.next;</span><br><span class=\"line\"> <span class=\"keyword\">if</span> (fast.next != <span class=\"literal\">null</span>) fast = fast.next;</span><br><span class=\"line\"> } <span class=\"keyword\">while</span> (slow != fast);</span><br><span class=\"line\"> <span class=\"comment\">// then slow was catched by fast</span></span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">i</span> <span class=\"operator\">=</span> <span class=\"number\">0</span>;</span><br><span class=\"line\"> <span class=\"keyword\">do</span> {</span><br><span class=\"line\"> slow = slow.next;</span><br><span class=\"line\"> i++;</span><br><span class=\"line\"> } <span class=\"keyword\">while</span> (slow != fast);</span><br><span class=\"line\"> System.out.println(<span class=\"string\">"loop size is "</span> + i);</span><br><span class=\"line\"> slow = head;</span><br><span class=\"line\"> <span class=\"keyword\">while</span> (fast != slow) {</span><br><span class=\"line\"> slow = slow.next;</span><br><span class=\"line\"> fast = fast.next;</span><br><span class=\"line\"> }</span><br><span class=\"line\"> <span class=\"keyword\">return</span> slow;</span><br><span class=\"line\"> }</span><br><span class=\"line\">}</span><br><span class=\"line\"></span><br></pre></td></tr></table></figure>\n"},{"title":"链表相关的算法","date":"2021-07-22T16:00:00.000Z","comments":1,"toc":true,"_content":"#### 有关链表的常见算法题\n因为之前专门有一篇写的链表的环测,所以这里就不再提链表环测算法了。\n1. 合并两个有序链表\n2. 检测两个链表是否相交,相交的话,找到两个链表的第一个公共节点\n3. 链表排序(快排和归并)\n4. 删除链表中的某个节点\n5. 反转链表\n6. 链表中倒数第k个节点\n7. 链表表示的加法\n8. 链表回文判断\n\n使用链表的时候,要使用临时的节点变量指向链表头节点,然后让这个临时节点\n向前走,不要直接操作head,这样会影响链表的后续使用,这一点需要特别注意。\n在求链表的长度,取中间节点的时候都会用到。\n##### 获取中间节点\n如何获取中间节点,可以使用快慢指针,快指针一次走两步,慢指针一次走一步,\n当快指针走到末尾的时候,slow所处的位置就是中间位置,返回slow即可。\n这个方法会在链表的归并排序中使用到。\n##### 合并有序链表\n合并有序链表是链表归并排序的另一个方法,可以使用递归和迭代两种方法实现。\n需要使用一个技巧就是使用两个临时节点,这两个节点都初始化一下,这样后面\n使用的时候不用判断空值。ListNode head = new ListNode();\nListNode temp = head;然后使用temp.next 去接收l1,l2中小的节点。\nhead代表的头,temp不断去接收l1,l2中小的节点,然后同时都往后移动,\n最后返回 head.next即可。\n##### 检测两个链表是否相交\n两个链表相交的话,两个链表中有一个节点是同一个节点,不是值相等,而是引用相同。\n则相交处后面的都是一样的,所以只要判断它们末尾的节点引用是否相同即可。\n如何获取相交处的节点呢?可以采用对齐的思路。\n先求出两个链表的长度差,然后让指向长链表的指针先走差值个节点,这样就对齐了,\n然后先个指针一起往后移,当他们两个引用相同的时候,就是第一个相交的节点了。\n##### 链表的排序\n快排:也是分为两个小方法,一个是划分partition,代码与使用数组的几乎一样,\n注意swap方法是交换节点的值,而不是交换节点。另一个方法是quickSort,这两\n个方法都接受两个参数,一个是指向链表头节点的head,一个是指向链表尾节点的tail,\n所以快排还需要先遍历一遍获取尾节点。quickSort使用递归方法,需要注意的是这里\n判断退出的条件是head==tail,这是链表的特点决定的。\n归并:前面提到的getMid,合并两个有序链表就是归并需要使用的两个方法。归并排序\n也是递归方法,需要注意的一点是,在获取到中间节点后,链表被划分为两段,这里是\n这么做的ListNode mid = getMid(head); ListNode right = mid.next;\nmid.next = null;这里需要注意right指向了中间后面的链表部分,mid.next=null\n断开了中间节点与后面的产联,这样才划分了两条链表。\n##### 删除链表中的某个节点\n如果只传你一个参数就是待删除的节点,因为单链表没有指向前置节点的指针,所以可以变\n通地让下一个节点的值赋给当前节点,然后移除下一个节点的思路来实现。\n##### 反转链表 \n递归方法:可以把链表认为有两部分,一部分是head,另一部分是head.next,反转链表,\n我们需要做的就是head.next.next = head,即让后面的节点的next指向head,然后\nhead.next = null,然后head的下一个节点置为null,这样就完成了。\n递推方法:使用三个临时节点,cur节点指向head,一直往后移,一直到链表末尾,还有\n一个pre节点初始化为NULL,cur往后移的时候,cur的next指向pre,然后cur,pre一\n起往后移,当cur成为null的时候,pre当好就是最后一个节点,返回pre即可。\n##### 求链表倒数第k个节点\n可以使用双指针的方法,快指针先走k个节点,然后快指针和慢指针一起往后走,当快指\n针走到链表末尾时,慢指针就是倒数第k个节点。\n##### 链表表示的加法\n链表表示的加法,链有顺序表示的数字和逆序表示的数字两种。逆序表示的简单些,直接从\n链表头部相加即可,如果不是,则需要先反转一下链表,因为加法都是从个数加起。\n##### 链表回文判断\n链表回文可以使用getMid方法获取到中间节点,把链表划分成前后两段,\n然后再使用上面的reverse链表,这样对比前后两个链表节点值是否相同即可。\n需要注意的是对于链表长度是奇数的时候,中间节点不参与比较。\n``` java\npackage org.example.basic;\n\npublic class ListQA {\n static class ListNode {\n public int val;\n public ListNode next;\n public ListNode(int val, ListNode next) {\n this.val = val;\n this.next = next;\n }\n public ListNode(int val) {\n this.val = val;\n this.next = null;\n }\n }\n\n public static void main(String[] args) {\n\n String str1 = \"12345\";\n ListNode l1 = chars2List(str1.toCharArray());\n ListNode l2 = chars2List(\"87654999\".toCharArray());\n printList(l1);\n printList(l2);\n //需要先反转链表之后计算\n ListNode r1 = reverse(l1);\n ListNode r2 = reverseList(l2);\n printList(r1);\n printList(r2);\n\n ListNode sum = listAdd(r1, r2);\n printList(sum);\n\n }\n\n static void printList(ListNode head) {\n ListNode t = head;\n while (t != null) {\n System.out.print(t.next == null ? t.val : t.val + \",\");\n t = t.next;\n }\n System.out.println();\n }\n\n static ListNode listAdd(ListNode add1, ListNode add2) {\n ListNode ret = new ListNode(0);\n ListNode temp = ret;\n ListNode l1 = add1;\n ListNode l2 = add2;\n int sum = 0;\n int carry = 0;\n while (l1 != null || l2 != null) {\n sum = (l1 == null ? 0 : l1.val) + (l2 == null ? 0 : l2.val) + carry;\n carry = sum > 10 ? 1 : 0;\n sum = sum % 10;\n temp.next = new ListNode(sum);\n temp = temp.next;\n l2 = l2 == null ? null : l2.next;\n l1 = l1 == null ? null : l1.next;\n// if(l1==null && l2!=null) {temp.next = l2; break;}\n// if(l2==null && l1!=null) {temp.next = l1; break;}\n }\n return ret.next;\n }\n\n static ListNode chars2List(char[] chars) {\n ListNode head = new ListNode(0);\n ListNode temp = head;\n int N = chars.length;\n for (int i = 0; i < N; i++) {\n temp.next = new ListNode(chars[i] - '0');\n temp = temp.next;\n }\n return head.next;\n }\n\n static ListNode getMid(ListNode head) {\n if (head == null || head.next == null) return head;\n ListNode fast = head;\n ListNode slow = head;\n while (fast.next != null && fast.next.next != null) {\n fast = fast.next.next;\n slow = slow.next;\n }\n return slow;\n }\n\n //合并两个有序链表\n static ListNode mergeList(ListNode left, ListNode right) {\n ListNode head = new ListNode(0);\n ListNode temp = head;\n ListNode l1 = left;\n ListNode l2 = right;\n while (l1 != null && l2 != null) {\n if (l1.val < l2.val) {\n temp.next = l1;\n l1 = l1.next;\n } else {\n temp.next = l2;\n l2 = l2.next;\n }\n temp = temp.next;\n }\n temp.next = l1 == null ? l2 : l1;\n return head.next;\n }\n\n static ListNode mergeSort(ListNode head) {\n if (head == null || head.next == null) return head;\n ListNode mid = getMid(head);\n ListNode right = mid.next;\n mid.next = null;\n return mergeList(mergeSort(head), mergeSort(right));\n }\n\n\n //revert List\n static ListNode reverse(ListNode head) {\n ListNode pre = null;\n ListNode cur = head;\n while (cur != null) {\n ListNode next = cur.next;\n cur.next = pre;\n pre = cur;\n cur = next;\n }\n return pre;\n }\n\n static ListNode reverseList(ListNode head) {\n if (head == null || head.next == null) return head;\n ListNode last = reverseList(head.next);\n head.next.next = head;\n head.next = null;\n return last;\n }\n\n static void swap(ListNode a, ListNode b) {\n int t = a.val;\n a.val = b.val;\n b.val = t;\n }\n\n static ListNode partition(ListNode head, ListNode tail) {\n ListNode left = head;\n ListNode right = head.next;\n while (right != tail.next) {\n if (right.val < head.val) {\n swap(right, left.next);\n left = left.next;\n }\n right = right.next;\n }\n swap(head, left);\n return left;\n }\n\n static void quickSort(ListNode head, ListNode tail) {\n if (head == tail) return;\n ListNode pivot = partition(head, tail);\n quickSort(head, pivot);\n quickSort(pivot.next, tail);\n }\n\n\n static boolean isCrossList(ListNode l1, ListNode l2) {\n if (l1 == null && l2 != null) return false;\n if (l2 == null && l1 != null) return false;\n ListNode tail1 = l1;\n ListNode tail2 = l2;\n while (tail1.next != null) tail1 = tail1.next;\n while (tail2.next != null) tail2 = tail2.next;\n return tail1 == tail2;\n }\n\n static ListNode getLast(ListNode head) {\n ListNode temp = head;\n while (temp.next != null) temp = temp.next;\n return temp;\n }\n\n static int getLen(ListNode head) {\n int len = 0;\n ListNode temp = head;\n while (temp != null) {\n len++;\n temp = temp.next;\n }\n return len;\n }\n\n static ListNode getCrossEntrypoint(ListNode l1, ListNode l2) {\n boolean isCross = isCrossList(l1, l2);\n if (!isCross) return new ListNode(Integer.MIN_VALUE);\n int len1 = getLen(l1);\n int len2 = getLen(l2);\n int interval = Math.abs(len1 - len2);\n ListNode t1 = l1;\n ListNode t2 = l2;\n while (interval > 0) {\n if (len1 > len2) {\n t1 = t1.next;\n }\n if (len1 < len2) {\n t2 = t2.next;\n }\n interval--;\n }\n //对齐之后\n while (t1 != t2) {\n t1 = t1.next;\n t2 = t2.next;\n }\n return t1;\n }\n}\n\n```","source":"_posts/链表相关的算法.md","raw":"\n---\ntitle: 链表相关的算法\ndate: 2021-07-23\ncomments: true #是否可评论\ntoc: true #是否显示文章目录\ncategories: \"算法\" #分类\ntags: #标签\n - JAVA\n - 面试题\n - 算法\n---\n#### 有关链表的常见算法题\n因为之前专门有一篇写的链表的环测,所以这里就不再提链表环测算法了。\n1. 合并两个有序链表\n2. 检测两个链表是否相交,相交的话,找到两个链表的第一个公共节点\n3. 链表排序(快排和归并)\n4. 删除链表中的某个节点\n5. 反转链表\n6. 链表中倒数第k个节点\n7. 链表表示的加法\n8. 链表回文判断\n\n使用链表的时候,要使用临时的节点变量指向链表头节点,然后让这个临时节点\n向前走,不要直接操作head,这样会影响链表的后续使用,这一点需要特别注意。\n在求链表的长度,取中间节点的时候都会用到。\n##### 获取中间节点\n如何获取中间节点,可以使用快慢指针,快指针一次走两步,慢指针一次走一步,\n当快指针走到末尾的时候,slow所处的位置就是中间位置,返回slow即可。\n这个方法会在链表的归并排序中使用到。\n##### 合并有序链表\n合并有序链表是链表归并排序的另一个方法,可以使用递归和迭代两种方法实现。\n需要使用一个技巧就是使用两个临时节点,这两个节点都初始化一下,这样后面\n使用的时候不用判断空值。ListNode head = new ListNode();\nListNode temp = head;然后使用temp.next 去接收l1,l2中小的节点。\nhead代表的头,temp不断去接收l1,l2中小的节点,然后同时都往后移动,\n最后返回 head.next即可。\n##### 检测两个链表是否相交\n两个链表相交的话,两个链表中有一个节点是同一个节点,不是值相等,而是引用相同。\n则相交处后面的都是一样的,所以只要判断它们末尾的节点引用是否相同即可。\n如何获取相交处的节点呢?可以采用对齐的思路。\n先求出两个链表的长度差,然后让指向长链表的指针先走差值个节点,这样就对齐了,\n然后先个指针一起往后移,当他们两个引用相同的时候,就是第一个相交的节点了。\n##### 链表的排序\n快排:也是分为两个小方法,一个是划分partition,代码与使用数组的几乎一样,\n注意swap方法是交换节点的值,而不是交换节点。另一个方法是quickSort,这两\n个方法都接受两个参数,一个是指向链表头节点的head,一个是指向链表尾节点的tail,\n所以快排还需要先遍历一遍获取尾节点。quickSort使用递归方法,需要注意的是这里\n判断退出的条件是head==tail,这是链表的特点决定的。\n归并:前面提到的getMid,合并两个有序链表就是归并需要使用的两个方法。归并排序\n也是递归方法,需要注意的一点是,在获取到中间节点后,链表被划分为两段,这里是\n这么做的ListNode mid = getMid(head); ListNode right = mid.next;\nmid.next = null;这里需要注意right指向了中间后面的链表部分,mid.next=null\n断开了中间节点与后面的产联,这样才划分了两条链表。\n##### 删除链表中的某个节点\n如果只传你一个参数就是待删除的节点,因为单链表没有指向前置节点的指针,所以可以变\n通地让下一个节点的值赋给当前节点,然后移除下一个节点的思路来实现。\n##### 反转链表 \n递归方法:可以把链表认为有两部分,一部分是head,另一部分是head.next,反转链表,\n我们需要做的就是head.next.next = head,即让后面的节点的next指向head,然后\nhead.next = null,然后head的下一个节点置为null,这样就完成了。\n递推方法:使用三个临时节点,cur节点指向head,一直往后移,一直到链表末尾,还有\n一个pre节点初始化为NULL,cur往后移的时候,cur的next指向pre,然后cur,pre一\n起往后移,当cur成为null的时候,pre当好就是最后一个节点,返回pre即可。\n##### 求链表倒数第k个节点\n可以使用双指针的方法,快指针先走k个节点,然后快指针和慢指针一起往后走,当快指\n针走到链表末尾时,慢指针就是倒数第k个节点。\n##### 链表表示的加法\n链表表示的加法,链有顺序表示的数字和逆序表示的数字两种。逆序表示的简单些,直接从\n链表头部相加即可,如果不是,则需要先反转一下链表,因为加法都是从个数加起。\n##### 链表回文判断\n链表回文可以使用getMid方法获取到中间节点,把链表划分成前后两段,\n然后再使用上面的reverse链表,这样对比前后两个链表节点值是否相同即可。\n需要注意的是对于链表长度是奇数的时候,中间节点不参与比较。\n``` java\npackage org.example.basic;\n\npublic class ListQA {\n static class ListNode {\n public int val;\n public ListNode next;\n public ListNode(int val, ListNode next) {\n this.val = val;\n this.next = next;\n }\n public ListNode(int val) {\n this.val = val;\n this.next = null;\n }\n }\n\n public static void main(String[] args) {\n\n String str1 = \"12345\";\n ListNode l1 = chars2List(str1.toCharArray());\n ListNode l2 = chars2List(\"87654999\".toCharArray());\n printList(l1);\n printList(l2);\n //需要先反转链表之后计算\n ListNode r1 = reverse(l1);\n ListNode r2 = reverseList(l2);\n printList(r1);\n printList(r2);\n\n ListNode sum = listAdd(r1, r2);\n printList(sum);\n\n }\n\n static void printList(ListNode head) {\n ListNode t = head;\n while (t != null) {\n System.out.print(t.next == null ? t.val : t.val + \",\");\n t = t.next;\n }\n System.out.println();\n }\n\n static ListNode listAdd(ListNode add1, ListNode add2) {\n ListNode ret = new ListNode(0);\n ListNode temp = ret;\n ListNode l1 = add1;\n ListNode l2 = add2;\n int sum = 0;\n int carry = 0;\n while (l1 != null || l2 != null) {\n sum = (l1 == null ? 0 : l1.val) + (l2 == null ? 0 : l2.val) + carry;\n carry = sum > 10 ? 1 : 0;\n sum = sum % 10;\n temp.next = new ListNode(sum);\n temp = temp.next;\n l2 = l2 == null ? null : l2.next;\n l1 = l1 == null ? null : l1.next;\n// if(l1==null && l2!=null) {temp.next = l2; break;}\n// if(l2==null && l1!=null) {temp.next = l1; break;}\n }\n return ret.next;\n }\n\n static ListNode chars2List(char[] chars) {\n ListNode head = new ListNode(0);\n ListNode temp = head;\n int N = chars.length;\n for (int i = 0; i < N; i++) {\n temp.next = new ListNode(chars[i] - '0');\n temp = temp.next;\n }\n return head.next;\n }\n\n static ListNode getMid(ListNode head) {\n if (head == null || head.next == null) return head;\n ListNode fast = head;\n ListNode slow = head;\n while (fast.next != null && fast.next.next != null) {\n fast = fast.next.next;\n slow = slow.next;\n }\n return slow;\n }\n\n //合并两个有序链表\n static ListNode mergeList(ListNode left, ListNode right) {\n ListNode head = new ListNode(0);\n ListNode temp = head;\n ListNode l1 = left;\n ListNode l2 = right;\n while (l1 != null && l2 != null) {\n if (l1.val < l2.val) {\n temp.next = l1;\n l1 = l1.next;\n } else {\n temp.next = l2;\n l2 = l2.next;\n }\n temp = temp.next;\n }\n temp.next = l1 == null ? l2 : l1;\n return head.next;\n }\n\n static ListNode mergeSort(ListNode head) {\n if (head == null || head.next == null) return head;\n ListNode mid = getMid(head);\n ListNode right = mid.next;\n mid.next = null;\n return mergeList(mergeSort(head), mergeSort(right));\n }\n\n\n //revert List\n static ListNode reverse(ListNode head) {\n ListNode pre = null;\n ListNode cur = head;\n while (cur != null) {\n ListNode next = cur.next;\n cur.next = pre;\n pre = cur;\n cur = next;\n }\n return pre;\n }\n\n static ListNode reverseList(ListNode head) {\n if (head == null || head.next == null) return head;\n ListNode last = reverseList(head.next);\n head.next.next = head;\n head.next = null;\n return last;\n }\n\n static void swap(ListNode a, ListNode b) {\n int t = a.val;\n a.val = b.val;\n b.val = t;\n }\n\n static ListNode partition(ListNode head, ListNode tail) {\n ListNode left = head;\n ListNode right = head.next;\n while (right != tail.next) {\n if (right.val < head.val) {\n swap(right, left.next);\n left = left.next;\n }\n right = right.next;\n }\n swap(head, left);\n return left;\n }\n\n static void quickSort(ListNode head, ListNode tail) {\n if (head == tail) return;\n ListNode pivot = partition(head, tail);\n quickSort(head, pivot);\n quickSort(pivot.next, tail);\n }\n\n\n static boolean isCrossList(ListNode l1, ListNode l2) {\n if (l1 == null && l2 != null) return false;\n if (l2 == null && l1 != null) return false;\n ListNode tail1 = l1;\n ListNode tail2 = l2;\n while (tail1.next != null) tail1 = tail1.next;\n while (tail2.next != null) tail2 = tail2.next;\n return tail1 == tail2;\n }\n\n static ListNode getLast(ListNode head) {\n ListNode temp = head;\n while (temp.next != null) temp = temp.next;\n return temp;\n }\n\n static int getLen(ListNode head) {\n int len = 0;\n ListNode temp = head;\n while (temp != null) {\n len++;\n temp = temp.next;\n }\n return len;\n }\n\n static ListNode getCrossEntrypoint(ListNode l1, ListNode l2) {\n boolean isCross = isCrossList(l1, l2);\n if (!isCross) return new ListNode(Integer.MIN_VALUE);\n int len1 = getLen(l1);\n int len2 = getLen(l2);\n int interval = Math.abs(len1 - len2);\n ListNode t1 = l1;\n ListNode t2 = l2;\n while (interval > 0) {\n if (len1 > len2) {\n t1 = t1.next;\n }\n if (len1 < len2) {\n t2 = t2.next;\n }\n interval--;\n }\n //对齐之后\n while (t1 != t2) {\n t1 = t1.next;\n t2 = t2.next;\n }\n return t1;\n }\n}\n\n```","slug":"链表相关的算法","published":1,"updated":"2022-04-20T09:35:50.865Z","layout":"post","photos":[],"link":"","_id":"cl27q3uvh001tn94ohgkohpdk","content":"<h4 id=\"有关链表的常见算法题\"><a href=\"#有关链表的常见算法题\" class=\"headerlink\" title=\"有关链表的常见算法题\"></a>有关链表的常见算法题</h4><p>因为之前专门有一篇写的链表的环测,所以这里就不再提链表环测算法了。</p>\n<ol>\n<li>合并两个有序链表</li>\n<li>检测两个链表是否相交,相交的话,找到两个链表的第一个公共节点</li>\n<li>链表排序(快排和归并)</li>\n<li>删除链表中的某个节点</li>\n<li>反转链表</li>\n<li>链表中倒数第k个节点</li>\n<li>链表表示的加法</li>\n<li>链表回文判断</li>\n</ol>\n<p>使用链表的时候,要使用临时的节点变量指向链表头节点,然后让这个临时节点<br>向前走,不要直接操作head,这样会影响链表的后续使用,这一点需要特别注意。<br>在求链表的长度,取中间节点的时候都会用到。</p>\n<h5 id=\"获取中间节点\"><a href=\"#获取中间节点\" class=\"headerlink\" title=\"获取中间节点\"></a>获取中间节点</h5><p>如何获取中间节点,可以使用快慢指针,快指针一次走两步,慢指针一次走一步,<br>当快指针走到末尾的时候,slow所处的位置就是中间位置,返回slow即可。<br>这个方法会在链表的归并排序中使用到。</p>\n<h5 id=\"合并有序链表\"><a href=\"#合并有序链表\" class=\"headerlink\" title=\"合并有序链表\"></a>合并有序链表</h5><p>合并有序链表是链表归并排序的另一个方法,可以使用递归和迭代两种方法实现。<br>需要使用一个技巧就是使用两个临时节点,这两个节点都初始化一下,这样后面<br>使用的时候不用判断空值。ListNode head = new ListNode();<br>ListNode temp = head;然后使用temp.next 去接收l1,l2中小的节点。<br>head代表的头,temp不断去接收l1,l2中小的节点,然后同时都往后移动,<br>最后返回 head.next即可。</p>\n<h5 id=\"检测两个链表是否相交\"><a href=\"#检测两个链表是否相交\" class=\"headerlink\" title=\"检测两个链表是否相交\"></a>检测两个链表是否相交</h5><p>两个链表相交的话,两个链表中有一个节点是同一个节点,不是值相等,而是引用相同。<br>则相交处后面的都是一样的,所以只要判断它们末尾的节点引用是否相同即可。<br>如何获取相交处的节点呢?可以采用对齐的思路。<br>先求出两个链表的长度差,然后让指向长链表的指针先走差值个节点,这样就对齐了,<br>然后先个指针一起往后移,当他们两个引用相同的时候,就是第一个相交的节点了。</p>\n<h5 id=\"链表的排序\"><a href=\"#链表的排序\" class=\"headerlink\" title=\"链表的排序\"></a>链表的排序</h5><p>快排:也是分为两个小方法,一个是划分partition,代码与使用数组的几乎一样,<br>注意swap方法是交换节点的值,而不是交换节点。另一个方法是quickSort,这两<br>个方法都接受两个参数,一个是指向链表头节点的head,一个是指向链表尾节点的tail,<br>所以快排还需要先遍历一遍获取尾节点。quickSort使用递归方法,需要注意的是这里<br>判断退出的条件是head==tail,这是链表的特点决定的。<br>归并:前面提到的getMid,合并两个有序链表就是归并需要使用的两个方法。归并排序<br>也是递归方法,需要注意的一点是,在获取到中间节点后,链表被划分为两段,这里是<br>这么做的ListNode mid = getMid(head); ListNode right = mid.next;<br>mid.next = null;这里需要注意right指向了中间后面的链表部分,mid.next=null<br>断开了中间节点与后面的产联,这样才划分了两条链表。</p>\n<h5 id=\"删除链表中的某个节点\"><a href=\"#删除链表中的某个节点\" class=\"headerlink\" title=\"删除链表中的某个节点\"></a>删除链表中的某个节点</h5><p>如果只传你一个参数就是待删除的节点,因为单链表没有指向前置节点的指针,所以可以变<br>通地让下一个节点的值赋给当前节点,然后移除下一个节点的思路来实现。</p>\n<h5 id=\"反转链表\"><a href=\"#反转链表\" class=\"headerlink\" title=\"反转链表\"></a>反转链表</h5><p>递归方法:可以把链表认为有两部分,一部分是head,另一部分是head.next,反转链表,<br>我们需要做的就是head.next.next = head,即让后面的节点的next指向head,然后<br>head.next = null,然后head的下一个节点置为null,这样就完成了。<br>递推方法:使用三个临时节点,cur节点指向head,一直往后移,一直到链表末尾,还有<br>一个pre节点初始化为NULL,cur往后移的时候,cur的next指向pre,然后cur,pre一<br>起往后移,当cur成为null的时候,pre当好就是最后一个节点,返回pre即可。</p>\n<h5 id=\"求链表倒数第k个节点\"><a href=\"#求链表倒数第k个节点\" class=\"headerlink\" title=\"求链表倒数第k个节点\"></a>求链表倒数第k个节点</h5><p>可以使用双指针的方法,快指针先走k个节点,然后快指针和慢指针一起往后走,当快指<br>针走到链表末尾时,慢指针就是倒数第k个节点。</p>\n<h5 id=\"链表表示的加法\"><a href=\"#链表表示的加法\" class=\"headerlink\" title=\"链表表示的加法\"></a>链表表示的加法</h5><p>链表表示的加法,链有顺序表示的数字和逆序表示的数字两种。逆序表示的简单些,直接从<br>链表头部相加即可,如果不是,则需要先反转一下链表,因为加法都是从个数加起。</p>\n<h5 id=\"链表回文判断\"><a href=\"#链表回文判断\" class=\"headerlink\" title=\"链表回文判断\"></a>链表回文判断</h5><p>链表回文可以使用getMid方法获取到中间节点,把链表划分成前后两段,<br>然后再使用上面的reverse链表,这样对比前后两个链表节点值是否相同即可。<br>需要注意的是对于链表长度是奇数的时候,中间节点不参与比较。</p>\n<figure class=\"highlight java\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br><span class=\"line\">11</span><br><span class=\"line\">12</span><br><span class=\"line\">13</span><br><span class=\"line\">14</span><br><span class=\"line\">15</span><br><span class=\"line\">16</span><br><span class=\"line\">17</span><br><span class=\"line\">18</span><br><span class=\"line\">19</span><br><span class=\"line\">20</span><br><span class=\"line\">21</span><br><span class=\"line\">22</span><br><span class=\"line\">23</span><br><span class=\"line\">24</span><br><span class=\"line\">25</span><br><span class=\"line\">26</span><br><span class=\"line\">27</span><br><span class=\"line\">28</span><br><span class=\"line\">29</span><br><span class=\"line\">30</span><br><span class=\"line\">31</span><br><span class=\"line\">32</span><br><span class=\"line\">33</span><br><span class=\"line\">34</span><br><span class=\"line\">35</span><br><span class=\"line\">36</span><br><span class=\"line\">37</span><br><span class=\"line\">38</span><br><span class=\"line\">39</span><br><span class=\"line\">40</span><br><span class=\"line\">41</span><br><span class=\"line\">42</span><br><span class=\"line\">43</span><br><span class=\"line\">44</span><br><span class=\"line\">45</span><br><span class=\"line\">46</span><br><span class=\"line\">47</span><br><span class=\"line\">48</span><br><span class=\"line\">49</span><br><span class=\"line\">50</span><br><span class=\"line\">51</span><br><span class=\"line\">52</span><br><span class=\"line\">53</span><br><span class=\"line\">54</span><br><span class=\"line\">55</span><br><span class=\"line\">56</span><br><span class=\"line\">57</span><br><span class=\"line\">58</span><br><span class=\"line\">59</span><br><span class=\"line\">60</span><br><span class=\"line\">61</span><br><span class=\"line\">62</span><br><span class=\"line\">63</span><br><span class=\"line\">64</span><br><span class=\"line\">65</span><br><span class=\"line\">66</span><br><span class=\"line\">67</span><br><span class=\"line\">68</span><br><span class=\"line\">69</span><br><span class=\"line\">70</span><br><span class=\"line\">71</span><br><span class=\"line\">72</span><br><span class=\"line\">73</span><br><span class=\"line\">74</span><br><span class=\"line\">75</span><br><span class=\"line\">76</span><br><span class=\"line\">77</span><br><span class=\"line\">78</span><br><span class=\"line\">79</span><br><span class=\"line\">80</span><br><span class=\"line\">81</span><br><span class=\"line\">82</span><br><span class=\"line\">83</span><br><span class=\"line\">84</span><br><span class=\"line\">85</span><br><span class=\"line\">86</span><br><span class=\"line\">87</span><br><span class=\"line\">88</span><br><span class=\"line\">89</span><br><span class=\"line\">90</span><br><span class=\"line\">91</span><br><span class=\"line\">92</span><br><span class=\"line\">93</span><br><span class=\"line\">94</span><br><span class=\"line\">95</span><br><span class=\"line\">96</span><br><span class=\"line\">97</span><br><span class=\"line\">98</span><br><span class=\"line\">99</span><br><span class=\"line\">100</span><br><span class=\"line\">101</span><br><span class=\"line\">102</span><br><span class=\"line\">103</span><br><span class=\"line\">104</span><br><span class=\"line\">105</span><br><span class=\"line\">106</span><br><span class=\"line\">107</span><br><span class=\"line\">108</span><br><span class=\"line\">109</span><br><span class=\"line\">110</span><br><span class=\"line\">111</span><br><span class=\"line\">112</span><br><span class=\"line\">113</span><br><span class=\"line\">114</span><br><span class=\"line\">115</span><br><span class=\"line\">116</span><br><span class=\"line\">117</span><br><span class=\"line\">118</span><br><span class=\"line\">119</span><br><span class=\"line\">120</span><br><span class=\"line\">121</span><br><span class=\"line\">122</span><br><span class=\"line\">123</span><br><span class=\"line\">124</span><br><span class=\"line\">125</span><br><span class=\"line\">126</span><br><span class=\"line\">127</span><br><span class=\"line\">128</span><br><span class=\"line\">129</span><br><span class=\"line\">130</span><br><span class=\"line\">131</span><br><span class=\"line\">132</span><br><span class=\"line\">133</span><br><span class=\"line\">134</span><br><span class=\"line\">135</span><br><span class=\"line\">136</span><br><span class=\"line\">137</span><br><span class=\"line\">138</span><br><span class=\"line\">139</span><br><span class=\"line\">140</span><br><span class=\"line\">141</span><br><span class=\"line\">142</span><br><span class=\"line\">143</span><br><span class=\"line\">144</span><br><span class=\"line\">145</span><br><span class=\"line\">146</span><br><span class=\"line\">147</span><br><span class=\"line\">148</span><br><span class=\"line\">149</span><br><span class=\"line\">150</span><br><span class=\"line\">151</span><br><span class=\"line\">152</span><br><span class=\"line\">153</span><br><span class=\"line\">154</span><br><span class=\"line\">155</span><br><span class=\"line\">156</span><br><span class=\"line\">157</span><br><span class=\"line\">158</span><br><span class=\"line\">159</span><br><span class=\"line\">160</span><br><span class=\"line\">161</span><br><span class=\"line\">162</span><br><span class=\"line\">163</span><br><span class=\"line\">164</span><br><span class=\"line\">165</span><br><span class=\"line\">166</span><br><span class=\"line\">167</span><br><span class=\"line\">168</span><br><span class=\"line\">169</span><br><span class=\"line\">170</span><br><span class=\"line\">171</span><br><span class=\"line\">172</span><br><span class=\"line\">173</span><br><span class=\"line\">174</span><br><span class=\"line\">175</span><br><span class=\"line\">176</span><br><span class=\"line\">177</span><br><span class=\"line\">178</span><br><span class=\"line\">179</span><br><span class=\"line\">180</span><br><span class=\"line\">181</span><br><span class=\"line\">182</span><br><span class=\"line\">183</span><br><span class=\"line\">184</span><br><span class=\"line\">185</span><br><span class=\"line\">186</span><br><span class=\"line\">187</span><br><span class=\"line\">188</span><br><span class=\"line\">189</span><br><span class=\"line\">190</span><br><span class=\"line\">191</span><br><span class=\"line\">192</span><br><span class=\"line\">193</span><br><span class=\"line\">194</span><br><span class=\"line\">195</span><br><span class=\"line\">196</span><br><span class=\"line\">197</span><br><span class=\"line\">198</span><br><span class=\"line\">199</span><br><span class=\"line\">200</span><br><span class=\"line\">201</span><br><span class=\"line\">202</span><br><span class=\"line\">203</span><br><span class=\"line\">204</span><br><span class=\"line\">205</span><br><span class=\"line\">206</span><br><span class=\"line\">207</span><br><span class=\"line\">208</span><br><span class=\"line\">209</span><br><span class=\"line\">210</span><br><span class=\"line\">211</span><br><span class=\"line\">212</span><br><span class=\"line\">213</span><br><span class=\"line\">214</span><br><span class=\"line\">215</span><br><span class=\"line\">216</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"keyword\">package</span> org.example.basic;</span><br><span class=\"line\"></span><br><span class=\"line\"><span class=\"keyword\">public</span> <span class=\"keyword\">class</span> <span class=\"title class_\">ListQA</span> {</span><br><span class=\"line\"> <span class=\"keyword\">static</span> <span class=\"keyword\">class</span> <span class=\"title class_\">ListNode</span> {</span><br><span class=\"line\"> <span class=\"keyword\">public</span> <span class=\"type\">int</span> val;</span><br><span class=\"line\"> <span class=\"keyword\">public</span> ListNode next;</span><br><span class=\"line\"> <span class=\"keyword\">public</span> <span class=\"title function_\">ListNode</span><span class=\"params\">(<span class=\"type\">int</span> val, ListNode next)</span> {</span><br><span class=\"line\"> <span class=\"built_in\">this</span>.val = val;</span><br><span class=\"line\"> <span class=\"built_in\">this</span>.next = next;</span><br><span class=\"line\"> }</span><br><span class=\"line\"> <span class=\"keyword\">public</span> <span class=\"title function_\">ListNode</span><span class=\"params\">(<span class=\"type\">int</span> val)</span> {</span><br><span class=\"line\"> <span class=\"built_in\">this</span>.val = val;</span><br><span class=\"line\"> <span class=\"built_in\">this</span>.next = <span class=\"literal\">null</span>;</span><br><span class=\"line\"> }</span><br><span class=\"line\"> }</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"keyword\">public</span> <span class=\"keyword\">static</span> <span class=\"keyword\">void</span> <span class=\"title function_\">main</span><span class=\"params\">(String[] args)</span> {</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"type\">String</span> <span class=\"variable\">str1</span> <span class=\"operator\">=</span> <span class=\"string\">"12345"</span>;</span><br><span class=\"line\"> <span class=\"type\">ListNode</span> <span class=\"variable\">l1</span> <span class=\"operator\">=</span> chars2List(str1.toCharArray());</span><br><span class=\"line\"> <span class=\"type\">ListNode</span> <span class=\"variable\">l2</span> <span class=\"operator\">=</span> chars2List(<span class=\"string\">"87654999"</span>.toCharArray());</span><br><span class=\"line\"> printList(l1);</span><br><span class=\"line\"> printList(l2);</span><br><span class=\"line\"> <span class=\"comment\">//需要先反转链表之后计算</span></span><br><span class=\"line\"> <span class=\"type\">ListNode</span> <span class=\"variable\">r1</span> <span class=\"operator\">=</span> reverse(l1);</span><br><span class=\"line\"> <span class=\"type\">ListNode</span> <span class=\"variable\">r2</span> <span class=\"operator\">=</span> reverseList(l2);</span><br><span class=\"line\"> printList(r1);</span><br><span class=\"line\"> printList(r2);</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"type\">ListNode</span> <span class=\"variable\">sum</span> <span class=\"operator\">=</span> listAdd(r1, r2);</span><br><span class=\"line\"> printList(sum);</span><br><span class=\"line\"></span><br><span class=\"line\"> }</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"keyword\">static</span> <span class=\"keyword\">void</span> <span class=\"title function_\">printList</span><span class=\"params\">(ListNode head)</span> {</span><br><span class=\"line\"> <span class=\"type\">ListNode</span> <span class=\"variable\">t</span> <span class=\"operator\">=</span> head;</span><br><span class=\"line\"> <span class=\"keyword\">while</span> (t != <span class=\"literal\">null</span>) {</span><br><span class=\"line\"> System.out.print(t.next == <span class=\"literal\">null</span> ? t.val : t.val + <span class=\"string\">","</span>);</span><br><span class=\"line\"> t = t.next;</span><br><span class=\"line\"> }</span><br><span class=\"line\"> System.out.println();</span><br><span class=\"line\"> }</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"keyword\">static</span> ListNode <span class=\"title function_\">listAdd</span><span class=\"params\">(ListNode add1, ListNode add2)</span> {</span><br><span class=\"line\"> <span class=\"type\">ListNode</span> <span class=\"variable\">ret</span> <span class=\"operator\">=</span> <span class=\"keyword\">new</span> <span class=\"title class_\">ListNode</span>(<span class=\"number\">0</span>);</span><br><span class=\"line\"> <span class=\"type\">ListNode</span> <span class=\"variable\">temp</span> <span class=\"operator\">=</span> ret;</span><br><span class=\"line\"> <span class=\"type\">ListNode</span> <span class=\"variable\">l1</span> <span class=\"operator\">=</span> add1;</span><br><span class=\"line\"> <span class=\"type\">ListNode</span> <span class=\"variable\">l2</span> <span class=\"operator\">=</span> add2;</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">sum</span> <span class=\"operator\">=</span> <span class=\"number\">0</span>;</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">carry</span> <span class=\"operator\">=</span> <span class=\"number\">0</span>;</span><br><span class=\"line\"> <span class=\"keyword\">while</span> (l1 != <span class=\"literal\">null</span> || l2 != <span class=\"literal\">null</span>) {</span><br><span class=\"line\"> sum = (l1 == <span class=\"literal\">null</span> ? <span class=\"number\">0</span> : l1.val) + (l2 == <span class=\"literal\">null</span> ? <span class=\"number\">0</span> : l2.val) + carry;</span><br><span class=\"line\"> carry = sum > <span class=\"number\">10</span> ? <span class=\"number\">1</span> : <span class=\"number\">0</span>;</span><br><span class=\"line\"> sum = sum % <span class=\"number\">10</span>;</span><br><span class=\"line\"> temp.next = <span class=\"keyword\">new</span> <span class=\"title class_\">ListNode</span>(sum);</span><br><span class=\"line\"> temp = temp.next;</span><br><span class=\"line\"> l2 = l2 == <span class=\"literal\">null</span> ? <span class=\"literal\">null</span> : l2.next;</span><br><span class=\"line\"> l1 = l1 == <span class=\"literal\">null</span> ? <span class=\"literal\">null</span> : l1.next;</span><br><span class=\"line\"><span class=\"comment\">// if(l1==null && l2!=null) {temp.next = l2; break;}</span></span><br><span class=\"line\"><span class=\"comment\">// if(l2==null && l1!=null) {temp.next = l1; break;}</span></span><br><span class=\"line\"> }</span><br><span class=\"line\"> <span class=\"keyword\">return</span> ret.next;</span><br><span class=\"line\"> }</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"keyword\">static</span> ListNode <span class=\"title function_\">chars2List</span><span class=\"params\">(<span class=\"type\">char</span>[] chars)</span> {</span><br><span class=\"line\"> <span class=\"type\">ListNode</span> <span class=\"variable\">head</span> <span class=\"operator\">=</span> <span class=\"keyword\">new</span> <span class=\"title class_\">ListNode</span>(<span class=\"number\">0</span>);</span><br><span class=\"line\"> <span class=\"type\">ListNode</span> <span class=\"variable\">temp</span> <span class=\"operator\">=</span> head;</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">N</span> <span class=\"operator\">=</span> chars.length;</span><br><span class=\"line\"> <span class=\"keyword\">for</span> (<span class=\"type\">int</span> <span class=\"variable\">i</span> <span class=\"operator\">=</span> <span class=\"number\">0</span>; i < N; i++) {</span><br><span class=\"line\"> temp.next = <span class=\"keyword\">new</span> <span class=\"title class_\">ListNode</span>(chars[i] - <span class=\"string\">'0'</span>);</span><br><span class=\"line\"> temp = temp.next;</span><br><span class=\"line\"> }</span><br><span class=\"line\"> <span class=\"keyword\">return</span> head.next;</span><br><span class=\"line\"> }</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"keyword\">static</span> ListNode <span class=\"title function_\">getMid</span><span class=\"params\">(ListNode head)</span> {</span><br><span class=\"line\"> <span class=\"keyword\">if</span> (head == <span class=\"literal\">null</span> || head.next == <span class=\"literal\">null</span>) <span class=\"keyword\">return</span> head;</span><br><span class=\"line\"> <span class=\"type\">ListNode</span> <span class=\"variable\">fast</span> <span class=\"operator\">=</span> head;</span><br><span class=\"line\"> <span class=\"type\">ListNode</span> <span class=\"variable\">slow</span> <span class=\"operator\">=</span> head;</span><br><span class=\"line\"> <span class=\"keyword\">while</span> (fast.next != <span class=\"literal\">null</span> && fast.next.next != <span class=\"literal\">null</span>) {</span><br><span class=\"line\"> fast = fast.next.next;</span><br><span class=\"line\"> slow = slow.next;</span><br><span class=\"line\"> }</span><br><span class=\"line\"> <span class=\"keyword\">return</span> slow;</span><br><span class=\"line\"> }</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"comment\">//合并两个有序链表</span></span><br><span class=\"line\"> <span class=\"keyword\">static</span> ListNode <span class=\"title function_\">mergeList</span><span class=\"params\">(ListNode left, ListNode right)</span> {</span><br><span class=\"line\"> <span class=\"type\">ListNode</span> <span class=\"variable\">head</span> <span class=\"operator\">=</span> <span class=\"keyword\">new</span> <span class=\"title class_\">ListNode</span>(<span class=\"number\">0</span>);</span><br><span class=\"line\"> <span class=\"type\">ListNode</span> <span class=\"variable\">temp</span> <span class=\"operator\">=</span> head;</span><br><span class=\"line\"> <span class=\"type\">ListNode</span> <span class=\"variable\">l1</span> <span class=\"operator\">=</span> left;</span><br><span class=\"line\"> <span class=\"type\">ListNode</span> <span class=\"variable\">l2</span> <span class=\"operator\">=</span> right;</span><br><span class=\"line\"> <span class=\"keyword\">while</span> (l1 != <span class=\"literal\">null</span> && l2 != <span class=\"literal\">null</span>) {</span><br><span class=\"line\"> <span class=\"keyword\">if</span> (l1.val < l2.val) {</span><br><span class=\"line\"> temp.next = l1;</span><br><span class=\"line\"> l1 = l1.next;</span><br><span class=\"line\"> } <span class=\"keyword\">else</span> {</span><br><span class=\"line\"> temp.next = l2;</span><br><span class=\"line\"> l2 = l2.next;</span><br><span class=\"line\"> }</span><br><span class=\"line\"> temp = temp.next;</span><br><span class=\"line\"> }</span><br><span class=\"line\"> temp.next = l1 == <span class=\"literal\">null</span> ? l2 : l1;</span><br><span class=\"line\"> <span class=\"keyword\">return</span> head.next;</span><br><span class=\"line\"> }</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"keyword\">static</span> ListNode <span class=\"title function_\">mergeSort</span><span class=\"params\">(ListNode head)</span> {</span><br><span class=\"line\"> <span class=\"keyword\">if</span> (head == <span class=\"literal\">null</span> || head.next == <span class=\"literal\">null</span>) <span class=\"keyword\">return</span> head;</span><br><span class=\"line\"> <span class=\"type\">ListNode</span> <span class=\"variable\">mid</span> <span class=\"operator\">=</span> getMid(head);</span><br><span class=\"line\"> <span class=\"type\">ListNode</span> <span class=\"variable\">right</span> <span class=\"operator\">=</span> mid.next;</span><br><span class=\"line\"> mid.next = <span class=\"literal\">null</span>;</span><br><span class=\"line\"> <span class=\"keyword\">return</span> mergeList(mergeSort(head), mergeSort(right));</span><br><span class=\"line\"> }</span><br><span class=\"line\"></span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"comment\">//revert List</span></span><br><span class=\"line\"> <span class=\"keyword\">static</span> ListNode <span class=\"title function_\">reverse</span><span class=\"params\">(ListNode head)</span> {</span><br><span class=\"line\"> <span class=\"type\">ListNode</span> <span class=\"variable\">pre</span> <span class=\"operator\">=</span> <span class=\"literal\">null</span>;</span><br><span class=\"line\"> <span class=\"type\">ListNode</span> <span class=\"variable\">cur</span> <span class=\"operator\">=</span> head;</span><br><span class=\"line\"> <span class=\"keyword\">while</span> (cur != <span class=\"literal\">null</span>) {</span><br><span class=\"line\"> <span class=\"type\">ListNode</span> <span class=\"variable\">next</span> <span class=\"operator\">=</span> cur.next;</span><br><span class=\"line\"> cur.next = pre;</span><br><span class=\"line\"> pre = cur;</span><br><span class=\"line\"> cur = next;</span><br><span class=\"line\"> }</span><br><span class=\"line\"> <span class=\"keyword\">return</span> pre;</span><br><span class=\"line\"> }</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"keyword\">static</span> ListNode <span class=\"title function_\">reverseList</span><span class=\"params\">(ListNode head)</span> {</span><br><span class=\"line\"> <span class=\"keyword\">if</span> (head == <span class=\"literal\">null</span> || head.next == <span class=\"literal\">null</span>) <span class=\"keyword\">return</span> head;</span><br><span class=\"line\"> <span class=\"type\">ListNode</span> <span class=\"variable\">last</span> <span class=\"operator\">=</span> reverseList(head.next);</span><br><span class=\"line\"> head.next.next = head;</span><br><span class=\"line\"> head.next = <span class=\"literal\">null</span>;</span><br><span class=\"line\"> <span class=\"keyword\">return</span> last;</span><br><span class=\"line\"> }</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"keyword\">static</span> <span class=\"keyword\">void</span> <span class=\"title function_\">swap</span><span class=\"params\">(ListNode a, ListNode b)</span> {</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">t</span> <span class=\"operator\">=</span> a.val;</span><br><span class=\"line\"> a.val = b.val;</span><br><span class=\"line\"> b.val = t;</span><br><span class=\"line\"> }</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"keyword\">static</span> ListNode <span class=\"title function_\">partition</span><span class=\"params\">(ListNode head, ListNode tail)</span> {</span><br><span class=\"line\"> <span class=\"type\">ListNode</span> <span class=\"variable\">left</span> <span class=\"operator\">=</span> head;</span><br><span class=\"line\"> <span class=\"type\">ListNode</span> <span class=\"variable\">right</span> <span class=\"operator\">=</span> head.next;</span><br><span class=\"line\"> <span class=\"keyword\">while</span> (right != tail.next) {</span><br><span class=\"line\"> <span class=\"keyword\">if</span> (right.val < head.val) {</span><br><span class=\"line\"> swap(right, left.next);</span><br><span class=\"line\"> left = left.next;</span><br><span class=\"line\"> }</span><br><span class=\"line\"> right = right.next;</span><br><span class=\"line\"> }</span><br><span class=\"line\"> swap(head, left);</span><br><span class=\"line\"> <span class=\"keyword\">return</span> left;</span><br><span class=\"line\"> }</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"keyword\">static</span> <span class=\"keyword\">void</span> <span class=\"title function_\">quickSort</span><span class=\"params\">(ListNode head, ListNode tail)</span> {</span><br><span class=\"line\"> <span class=\"keyword\">if</span> (head == tail) <span class=\"keyword\">return</span>;</span><br><span class=\"line\"> <span class=\"type\">ListNode</span> <span class=\"variable\">pivot</span> <span class=\"operator\">=</span> partition(head, tail);</span><br><span class=\"line\"> quickSort(head, pivot);</span><br><span class=\"line\"> quickSort(pivot.next, tail);</span><br><span class=\"line\"> }</span><br><span class=\"line\"></span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"keyword\">static</span> <span class=\"type\">boolean</span> <span class=\"title function_\">isCrossList</span><span class=\"params\">(ListNode l1, ListNode l2)</span> {</span><br><span class=\"line\"> <span class=\"keyword\">if</span> (l1 == <span class=\"literal\">null</span> && l2 != <span class=\"literal\">null</span>) <span class=\"keyword\">return</span> <span class=\"literal\">false</span>;</span><br><span class=\"line\"> <span class=\"keyword\">if</span> (l2 == <span class=\"literal\">null</span> && l1 != <span class=\"literal\">null</span>) <span class=\"keyword\">return</span> <span class=\"literal\">false</span>;</span><br><span class=\"line\"> <span class=\"type\">ListNode</span> <span class=\"variable\">tail1</span> <span class=\"operator\">=</span> l1;</span><br><span class=\"line\"> <span class=\"type\">ListNode</span> <span class=\"variable\">tail2</span> <span class=\"operator\">=</span> l2;</span><br><span class=\"line\"> <span class=\"keyword\">while</span> (tail1.next != <span class=\"literal\">null</span>) tail1 = tail1.next;</span><br><span class=\"line\"> <span class=\"keyword\">while</span> (tail2.next != <span class=\"literal\">null</span>) tail2 = tail2.next;</span><br><span class=\"line\"> <span class=\"type\">return</span> <span class=\"variable\">tail1</span> <span class=\"operator\">=</span>= tail2;</span><br><span class=\"line\"> }</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"keyword\">static</span> ListNode <span class=\"title function_\">getLast</span><span class=\"params\">(ListNode head)</span> {</span><br><span class=\"line\"> <span class=\"type\">ListNode</span> <span class=\"variable\">temp</span> <span class=\"operator\">=</span> head;</span><br><span class=\"line\"> <span class=\"keyword\">while</span> (temp.next != <span class=\"literal\">null</span>) temp = temp.next;</span><br><span class=\"line\"> <span class=\"keyword\">return</span> temp;</span><br><span class=\"line\"> }</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"keyword\">static</span> <span class=\"type\">int</span> <span class=\"title function_\">getLen</span><span class=\"params\">(ListNode head)</span> {</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">len</span> <span class=\"operator\">=</span> <span class=\"number\">0</span>;</span><br><span class=\"line\"> <span class=\"type\">ListNode</span> <span class=\"variable\">temp</span> <span class=\"operator\">=</span> head;</span><br><span class=\"line\"> <span class=\"keyword\">while</span> (temp != <span class=\"literal\">null</span>) {</span><br><span class=\"line\"> len++;</span><br><span class=\"line\"> temp = temp.next;</span><br><span class=\"line\"> }</span><br><span class=\"line\"> <span class=\"keyword\">return</span> len;</span><br><span class=\"line\"> }</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"keyword\">static</span> ListNode <span class=\"title function_\">getCrossEntrypoint</span><span class=\"params\">(ListNode l1, ListNode l2)</span> {</span><br><span class=\"line\"> <span class=\"type\">boolean</span> <span class=\"variable\">isCross</span> <span class=\"operator\">=</span> isCrossList(l1, l2);</span><br><span class=\"line\"> <span class=\"keyword\">if</span> (!isCross) <span class=\"keyword\">return</span> <span class=\"keyword\">new</span> <span class=\"title class_\">ListNode</span>(Integer.MIN_VALUE);</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">len1</span> <span class=\"operator\">=</span> getLen(l1);</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">len2</span> <span class=\"operator\">=</span> getLen(l2);</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">interval</span> <span class=\"operator\">=</span> Math.abs(len1 - len2);</span><br><span class=\"line\"> <span class=\"type\">ListNode</span> <span class=\"variable\">t1</span> <span class=\"operator\">=</span> l1;</span><br><span class=\"line\"> <span class=\"type\">ListNode</span> <span class=\"variable\">t2</span> <span class=\"operator\">=</span> l2;</span><br><span class=\"line\"> <span class=\"keyword\">while</span> (interval > <span class=\"number\">0</span>) {</span><br><span class=\"line\"> <span class=\"keyword\">if</span> (len1 > len2) {</span><br><span class=\"line\"> t1 = t1.next;</span><br><span class=\"line\"> }</span><br><span class=\"line\"> <span class=\"keyword\">if</span> (len1 < len2) {</span><br><span class=\"line\"> t2 = t2.next;</span><br><span class=\"line\"> }</span><br><span class=\"line\"> interval--;</span><br><span class=\"line\"> }</span><br><span class=\"line\"> <span class=\"comment\">//对齐之后</span></span><br><span class=\"line\"> <span class=\"keyword\">while</span> (t1 != t2) {</span><br><span class=\"line\"> t1 = t1.next;</span><br><span class=\"line\"> t2 = t2.next;</span><br><span class=\"line\"> }</span><br><span class=\"line\"> <span class=\"keyword\">return</span> t1;</span><br><span class=\"line\"> }</span><br><span class=\"line\">}</span><br><span class=\"line\"></span><br></pre></td></tr></table></figure>","site":{"data":{}},"excerpt":"","more":"<h4 id=\"有关链表的常见算法题\"><a href=\"#有关链表的常见算法题\" class=\"headerlink\" title=\"有关链表的常见算法题\"></a>有关链表的常见算法题</h4><p>因为之前专门有一篇写的链表的环测,所以这里就不再提链表环测算法了。</p>\n<ol>\n<li>合并两个有序链表</li>\n<li>检测两个链表是否相交,相交的话,找到两个链表的第一个公共节点</li>\n<li>链表排序(快排和归并)</li>\n<li>删除链表中的某个节点</li>\n<li>反转链表</li>\n<li>链表中倒数第k个节点</li>\n<li>链表表示的加法</li>\n<li>链表回文判断</li>\n</ol>\n<p>使用链表的时候,要使用临时的节点变量指向链表头节点,然后让这个临时节点<br>向前走,不要直接操作head,这样会影响链表的后续使用,这一点需要特别注意。<br>在求链表的长度,取中间节点的时候都会用到。</p>\n<h5 id=\"获取中间节点\"><a href=\"#获取中间节点\" class=\"headerlink\" title=\"获取中间节点\"></a>获取中间节点</h5><p>如何获取中间节点,可以使用快慢指针,快指针一次走两步,慢指针一次走一步,<br>当快指针走到末尾的时候,slow所处的位置就是中间位置,返回slow即可。<br>这个方法会在链表的归并排序中使用到。</p>\n<h5 id=\"合并有序链表\"><a href=\"#合并有序链表\" class=\"headerlink\" title=\"合并有序链表\"></a>合并有序链表</h5><p>合并有序链表是链表归并排序的另一个方法,可以使用递归和迭代两种方法实现。<br>需要使用一个技巧就是使用两个临时节点,这两个节点都初始化一下,这样后面<br>使用的时候不用判断空值。ListNode head = new ListNode();<br>ListNode temp = head;然后使用temp.next 去接收l1,l2中小的节点。<br>head代表的头,temp不断去接收l1,l2中小的节点,然后同时都往后移动,<br>最后返回 head.next即可。</p>\n<h5 id=\"检测两个链表是否相交\"><a href=\"#检测两个链表是否相交\" class=\"headerlink\" title=\"检测两个链表是否相交\"></a>检测两个链表是否相交</h5><p>两个链表相交的话,两个链表中有一个节点是同一个节点,不是值相等,而是引用相同。<br>则相交处后面的都是一样的,所以只要判断它们末尾的节点引用是否相同即可。<br>如何获取相交处的节点呢?可以采用对齐的思路。<br>先求出两个链表的长度差,然后让指向长链表的指针先走差值个节点,这样就对齐了,<br>然后先个指针一起往后移,当他们两个引用相同的时候,就是第一个相交的节点了。</p>\n<h5 id=\"链表的排序\"><a href=\"#链表的排序\" class=\"headerlink\" title=\"链表的排序\"></a>链表的排序</h5><p>快排:也是分为两个小方法,一个是划分partition,代码与使用数组的几乎一样,<br>注意swap方法是交换节点的值,而不是交换节点。另一个方法是quickSort,这两<br>个方法都接受两个参数,一个是指向链表头节点的head,一个是指向链表尾节点的tail,<br>所以快排还需要先遍历一遍获取尾节点。quickSort使用递归方法,需要注意的是这里<br>判断退出的条件是head==tail,这是链表的特点决定的。<br>归并:前面提到的getMid,合并两个有序链表就是归并需要使用的两个方法。归并排序<br>也是递归方法,需要注意的一点是,在获取到中间节点后,链表被划分为两段,这里是<br>这么做的ListNode mid = getMid(head); ListNode right = mid.next;<br>mid.next = null;这里需要注意right指向了中间后面的链表部分,mid.next=null<br>断开了中间节点与后面的产联,这样才划分了两条链表。</p>\n<h5 id=\"删除链表中的某个节点\"><a href=\"#删除链表中的某个节点\" class=\"headerlink\" title=\"删除链表中的某个节点\"></a>删除链表中的某个节点</h5><p>如果只传你一个参数就是待删除的节点,因为单链表没有指向前置节点的指针,所以可以变<br>通地让下一个节点的值赋给当前节点,然后移除下一个节点的思路来实现。</p>\n<h5 id=\"反转链表\"><a href=\"#反转链表\" class=\"headerlink\" title=\"反转链表\"></a>反转链表</h5><p>递归方法:可以把链表认为有两部分,一部分是head,另一部分是head.next,反转链表,<br>我们需要做的就是head.next.next = head,即让后面的节点的next指向head,然后<br>head.next = null,然后head的下一个节点置为null,这样就完成了。<br>递推方法:使用三个临时节点,cur节点指向head,一直往后移,一直到链表末尾,还有<br>一个pre节点初始化为NULL,cur往后移的时候,cur的next指向pre,然后cur,pre一<br>起往后移,当cur成为null的时候,pre当好就是最后一个节点,返回pre即可。</p>\n<h5 id=\"求链表倒数第k个节点\"><a href=\"#求链表倒数第k个节点\" class=\"headerlink\" title=\"求链表倒数第k个节点\"></a>求链表倒数第k个节点</h5><p>可以使用双指针的方法,快指针先走k个节点,然后快指针和慢指针一起往后走,当快指<br>针走到链表末尾时,慢指针就是倒数第k个节点。</p>\n<h5 id=\"链表表示的加法\"><a href=\"#链表表示的加法\" class=\"headerlink\" title=\"链表表示的加法\"></a>链表表示的加法</h5><p>链表表示的加法,链有顺序表示的数字和逆序表示的数字两种。逆序表示的简单些,直接从<br>链表头部相加即可,如果不是,则需要先反转一下链表,因为加法都是从个数加起。</p>\n<h5 id=\"链表回文判断\"><a href=\"#链表回文判断\" class=\"headerlink\" title=\"链表回文判断\"></a>链表回文判断</h5><p>链表回文可以使用getMid方法获取到中间节点,把链表划分成前后两段,<br>然后再使用上面的reverse链表,这样对比前后两个链表节点值是否相同即可。<br>需要注意的是对于链表长度是奇数的时候,中间节点不参与比较。</p>\n<figure class=\"highlight java\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br><span class=\"line\">11</span><br><span class=\"line\">12</span><br><span class=\"line\">13</span><br><span class=\"line\">14</span><br><span class=\"line\">15</span><br><span class=\"line\">16</span><br><span class=\"line\">17</span><br><span class=\"line\">18</span><br><span class=\"line\">19</span><br><span class=\"line\">20</span><br><span class=\"line\">21</span><br><span class=\"line\">22</span><br><span class=\"line\">23</span><br><span class=\"line\">24</span><br><span class=\"line\">25</span><br><span class=\"line\">26</span><br><span class=\"line\">27</span><br><span class=\"line\">28</span><br><span class=\"line\">29</span><br><span class=\"line\">30</span><br><span class=\"line\">31</span><br><span class=\"line\">32</span><br><span class=\"line\">33</span><br><span class=\"line\">34</span><br><span class=\"line\">35</span><br><span class=\"line\">36</span><br><span class=\"line\">37</span><br><span class=\"line\">38</span><br><span class=\"line\">39</span><br><span class=\"line\">40</span><br><span class=\"line\">41</span><br><span class=\"line\">42</span><br><span class=\"line\">43</span><br><span class=\"line\">44</span><br><span class=\"line\">45</span><br><span class=\"line\">46</span><br><span class=\"line\">47</span><br><span class=\"line\">48</span><br><span class=\"line\">49</span><br><span class=\"line\">50</span><br><span class=\"line\">51</span><br><span class=\"line\">52</span><br><span class=\"line\">53</span><br><span class=\"line\">54</span><br><span class=\"line\">55</span><br><span class=\"line\">56</span><br><span class=\"line\">57</span><br><span class=\"line\">58</span><br><span class=\"line\">59</span><br><span class=\"line\">60</span><br><span class=\"line\">61</span><br><span class=\"line\">62</span><br><span class=\"line\">63</span><br><span class=\"line\">64</span><br><span class=\"line\">65</span><br><span class=\"line\">66</span><br><span class=\"line\">67</span><br><span class=\"line\">68</span><br><span class=\"line\">69</span><br><span class=\"line\">70</span><br><span class=\"line\">71</span><br><span class=\"line\">72</span><br><span class=\"line\">73</span><br><span class=\"line\">74</span><br><span class=\"line\">75</span><br><span class=\"line\">76</span><br><span class=\"line\">77</span><br><span class=\"line\">78</span><br><span class=\"line\">79</span><br><span class=\"line\">80</span><br><span class=\"line\">81</span><br><span class=\"line\">82</span><br><span class=\"line\">83</span><br><span class=\"line\">84</span><br><span class=\"line\">85</span><br><span class=\"line\">86</span><br><span class=\"line\">87</span><br><span class=\"line\">88</span><br><span class=\"line\">89</span><br><span class=\"line\">90</span><br><span class=\"line\">91</span><br><span class=\"line\">92</span><br><span class=\"line\">93</span><br><span class=\"line\">94</span><br><span class=\"line\">95</span><br><span class=\"line\">96</span><br><span class=\"line\">97</span><br><span class=\"line\">98</span><br><span class=\"line\">99</span><br><span class=\"line\">100</span><br><span class=\"line\">101</span><br><span class=\"line\">102</span><br><span class=\"line\">103</span><br><span class=\"line\">104</span><br><span class=\"line\">105</span><br><span class=\"line\">106</span><br><span class=\"line\">107</span><br><span class=\"line\">108</span><br><span class=\"line\">109</span><br><span class=\"line\">110</span><br><span class=\"line\">111</span><br><span class=\"line\">112</span><br><span class=\"line\">113</span><br><span class=\"line\">114</span><br><span class=\"line\">115</span><br><span class=\"line\">116</span><br><span class=\"line\">117</span><br><span class=\"line\">118</span><br><span class=\"line\">119</span><br><span class=\"line\">120</span><br><span class=\"line\">121</span><br><span class=\"line\">122</span><br><span class=\"line\">123</span><br><span class=\"line\">124</span><br><span class=\"line\">125</span><br><span class=\"line\">126</span><br><span class=\"line\">127</span><br><span class=\"line\">128</span><br><span class=\"line\">129</span><br><span class=\"line\">130</span><br><span class=\"line\">131</span><br><span class=\"line\">132</span><br><span class=\"line\">133</span><br><span class=\"line\">134</span><br><span class=\"line\">135</span><br><span class=\"line\">136</span><br><span class=\"line\">137</span><br><span class=\"line\">138</span><br><span class=\"line\">139</span><br><span class=\"line\">140</span><br><span class=\"line\">141</span><br><span class=\"line\">142</span><br><span class=\"line\">143</span><br><span class=\"line\">144</span><br><span class=\"line\">145</span><br><span class=\"line\">146</span><br><span class=\"line\">147</span><br><span class=\"line\">148</span><br><span class=\"line\">149</span><br><span class=\"line\">150</span><br><span class=\"line\">151</span><br><span class=\"line\">152</span><br><span class=\"line\">153</span><br><span class=\"line\">154</span><br><span class=\"line\">155</span><br><span class=\"line\">156</span><br><span class=\"line\">157</span><br><span class=\"line\">158</span><br><span class=\"line\">159</span><br><span class=\"line\">160</span><br><span class=\"line\">161</span><br><span class=\"line\">162</span><br><span class=\"line\">163</span><br><span class=\"line\">164</span><br><span class=\"line\">165</span><br><span class=\"line\">166</span><br><span class=\"line\">167</span><br><span class=\"line\">168</span><br><span class=\"line\">169</span><br><span class=\"line\">170</span><br><span class=\"line\">171</span><br><span class=\"line\">172</span><br><span class=\"line\">173</span><br><span class=\"line\">174</span><br><span class=\"line\">175</span><br><span class=\"line\">176</span><br><span class=\"line\">177</span><br><span class=\"line\">178</span><br><span class=\"line\">179</span><br><span class=\"line\">180</span><br><span class=\"line\">181</span><br><span class=\"line\">182</span><br><span class=\"line\">183</span><br><span class=\"line\">184</span><br><span class=\"line\">185</span><br><span class=\"line\">186</span><br><span class=\"line\">187</span><br><span class=\"line\">188</span><br><span class=\"line\">189</span><br><span class=\"line\">190</span><br><span class=\"line\">191</span><br><span class=\"line\">192</span><br><span class=\"line\">193</span><br><span class=\"line\">194</span><br><span class=\"line\">195</span><br><span class=\"line\">196</span><br><span class=\"line\">197</span><br><span class=\"line\">198</span><br><span class=\"line\">199</span><br><span class=\"line\">200</span><br><span class=\"line\">201</span><br><span class=\"line\">202</span><br><span class=\"line\">203</span><br><span class=\"line\">204</span><br><span class=\"line\">205</span><br><span class=\"line\">206</span><br><span class=\"line\">207</span><br><span class=\"line\">208</span><br><span class=\"line\">209</span><br><span class=\"line\">210</span><br><span class=\"line\">211</span><br><span class=\"line\">212</span><br><span class=\"line\">213</span><br><span class=\"line\">214</span><br><span class=\"line\">215</span><br><span class=\"line\">216</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"keyword\">package</span> org.example.basic;</span><br><span class=\"line\"></span><br><span class=\"line\"><span class=\"keyword\">public</span> <span class=\"keyword\">class</span> <span class=\"title class_\">ListQA</span> {</span><br><span class=\"line\"> <span class=\"keyword\">static</span> <span class=\"keyword\">class</span> <span class=\"title class_\">ListNode</span> {</span><br><span class=\"line\"> <span class=\"keyword\">public</span> <span class=\"type\">int</span> val;</span><br><span class=\"line\"> <span class=\"keyword\">public</span> ListNode next;</span><br><span class=\"line\"> <span class=\"keyword\">public</span> <span class=\"title function_\">ListNode</span><span class=\"params\">(<span class=\"type\">int</span> val, ListNode next)</span> {</span><br><span class=\"line\"> <span class=\"built_in\">this</span>.val = val;</span><br><span class=\"line\"> <span class=\"built_in\">this</span>.next = next;</span><br><span class=\"line\"> }</span><br><span class=\"line\"> <span class=\"keyword\">public</span> <span class=\"title function_\">ListNode</span><span class=\"params\">(<span class=\"type\">int</span> val)</span> {</span><br><span class=\"line\"> <span class=\"built_in\">this</span>.val = val;</span><br><span class=\"line\"> <span class=\"built_in\">this</span>.next = <span class=\"literal\">null</span>;</span><br><span class=\"line\"> }</span><br><span class=\"line\"> }</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"keyword\">public</span> <span class=\"keyword\">static</span> <span class=\"keyword\">void</span> <span class=\"title function_\">main</span><span class=\"params\">(String[] args)</span> {</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"type\">String</span> <span class=\"variable\">str1</span> <span class=\"operator\">=</span> <span class=\"string\">"12345"</span>;</span><br><span class=\"line\"> <span class=\"type\">ListNode</span> <span class=\"variable\">l1</span> <span class=\"operator\">=</span> chars2List(str1.toCharArray());</span><br><span class=\"line\"> <span class=\"type\">ListNode</span> <span class=\"variable\">l2</span> <span class=\"operator\">=</span> chars2List(<span class=\"string\">"87654999"</span>.toCharArray());</span><br><span class=\"line\"> printList(l1);</span><br><span class=\"line\"> printList(l2);</span><br><span class=\"line\"> <span class=\"comment\">//需要先反转链表之后计算</span></span><br><span class=\"line\"> <span class=\"type\">ListNode</span> <span class=\"variable\">r1</span> <span class=\"operator\">=</span> reverse(l1);</span><br><span class=\"line\"> <span class=\"type\">ListNode</span> <span class=\"variable\">r2</span> <span class=\"operator\">=</span> reverseList(l2);</span><br><span class=\"line\"> printList(r1);</span><br><span class=\"line\"> printList(r2);</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"type\">ListNode</span> <span class=\"variable\">sum</span> <span class=\"operator\">=</span> listAdd(r1, r2);</span><br><span class=\"line\"> printList(sum);</span><br><span class=\"line\"></span><br><span class=\"line\"> }</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"keyword\">static</span> <span class=\"keyword\">void</span> <span class=\"title function_\">printList</span><span class=\"params\">(ListNode head)</span> {</span><br><span class=\"line\"> <span class=\"type\">ListNode</span> <span class=\"variable\">t</span> <span class=\"operator\">=</span> head;</span><br><span class=\"line\"> <span class=\"keyword\">while</span> (t != <span class=\"literal\">null</span>) {</span><br><span class=\"line\"> System.out.print(t.next == <span class=\"literal\">null</span> ? t.val : t.val + <span class=\"string\">","</span>);</span><br><span class=\"line\"> t = t.next;</span><br><span class=\"line\"> }</span><br><span class=\"line\"> System.out.println();</span><br><span class=\"line\"> }</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"keyword\">static</span> ListNode <span class=\"title function_\">listAdd</span><span class=\"params\">(ListNode add1, ListNode add2)</span> {</span><br><span class=\"line\"> <span class=\"type\">ListNode</span> <span class=\"variable\">ret</span> <span class=\"operator\">=</span> <span class=\"keyword\">new</span> <span class=\"title class_\">ListNode</span>(<span class=\"number\">0</span>);</span><br><span class=\"line\"> <span class=\"type\">ListNode</span> <span class=\"variable\">temp</span> <span class=\"operator\">=</span> ret;</span><br><span class=\"line\"> <span class=\"type\">ListNode</span> <span class=\"variable\">l1</span> <span class=\"operator\">=</span> add1;</span><br><span class=\"line\"> <span class=\"type\">ListNode</span> <span class=\"variable\">l2</span> <span class=\"operator\">=</span> add2;</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">sum</span> <span class=\"operator\">=</span> <span class=\"number\">0</span>;</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">carry</span> <span class=\"operator\">=</span> <span class=\"number\">0</span>;</span><br><span class=\"line\"> <span class=\"keyword\">while</span> (l1 != <span class=\"literal\">null</span> || l2 != <span class=\"literal\">null</span>) {</span><br><span class=\"line\"> sum = (l1 == <span class=\"literal\">null</span> ? <span class=\"number\">0</span> : l1.val) + (l2 == <span class=\"literal\">null</span> ? <span class=\"number\">0</span> : l2.val) + carry;</span><br><span class=\"line\"> carry = sum > <span class=\"number\">10</span> ? <span class=\"number\">1</span> : <span class=\"number\">0</span>;</span><br><span class=\"line\"> sum = sum % <span class=\"number\">10</span>;</span><br><span class=\"line\"> temp.next = <span class=\"keyword\">new</span> <span class=\"title class_\">ListNode</span>(sum);</span><br><span class=\"line\"> temp = temp.next;</span><br><span class=\"line\"> l2 = l2 == <span class=\"literal\">null</span> ? <span class=\"literal\">null</span> : l2.next;</span><br><span class=\"line\"> l1 = l1 == <span class=\"literal\">null</span> ? <span class=\"literal\">null</span> : l1.next;</span><br><span class=\"line\"><span class=\"comment\">// if(l1==null && l2!=null) {temp.next = l2; break;}</span></span><br><span class=\"line\"><span class=\"comment\">// if(l2==null && l1!=null) {temp.next = l1; break;}</span></span><br><span class=\"line\"> }</span><br><span class=\"line\"> <span class=\"keyword\">return</span> ret.next;</span><br><span class=\"line\"> }</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"keyword\">static</span> ListNode <span class=\"title function_\">chars2List</span><span class=\"params\">(<span class=\"type\">char</span>[] chars)</span> {</span><br><span class=\"line\"> <span class=\"type\">ListNode</span> <span class=\"variable\">head</span> <span class=\"operator\">=</span> <span class=\"keyword\">new</span> <span class=\"title class_\">ListNode</span>(<span class=\"number\">0</span>);</span><br><span class=\"line\"> <span class=\"type\">ListNode</span> <span class=\"variable\">temp</span> <span class=\"operator\">=</span> head;</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">N</span> <span class=\"operator\">=</span> chars.length;</span><br><span class=\"line\"> <span class=\"keyword\">for</span> (<span class=\"type\">int</span> <span class=\"variable\">i</span> <span class=\"operator\">=</span> <span class=\"number\">0</span>; i < N; i++) {</span><br><span class=\"line\"> temp.next = <span class=\"keyword\">new</span> <span class=\"title class_\">ListNode</span>(chars[i] - <span class=\"string\">'0'</span>);</span><br><span class=\"line\"> temp = temp.next;</span><br><span class=\"line\"> }</span><br><span class=\"line\"> <span class=\"keyword\">return</span> head.next;</span><br><span class=\"line\"> }</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"keyword\">static</span> ListNode <span class=\"title function_\">getMid</span><span class=\"params\">(ListNode head)</span> {</span><br><span class=\"line\"> <span class=\"keyword\">if</span> (head == <span class=\"literal\">null</span> || head.next == <span class=\"literal\">null</span>) <span class=\"keyword\">return</span> head;</span><br><span class=\"line\"> <span class=\"type\">ListNode</span> <span class=\"variable\">fast</span> <span class=\"operator\">=</span> head;</span><br><span class=\"line\"> <span class=\"type\">ListNode</span> <span class=\"variable\">slow</span> <span class=\"operator\">=</span> head;</span><br><span class=\"line\"> <span class=\"keyword\">while</span> (fast.next != <span class=\"literal\">null</span> && fast.next.next != <span class=\"literal\">null</span>) {</span><br><span class=\"line\"> fast = fast.next.next;</span><br><span class=\"line\"> slow = slow.next;</span><br><span class=\"line\"> }</span><br><span class=\"line\"> <span class=\"keyword\">return</span> slow;</span><br><span class=\"line\"> }</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"comment\">//合并两个有序链表</span></span><br><span class=\"line\"> <span class=\"keyword\">static</span> ListNode <span class=\"title function_\">mergeList</span><span class=\"params\">(ListNode left, ListNode right)</span> {</span><br><span class=\"line\"> <span class=\"type\">ListNode</span> <span class=\"variable\">head</span> <span class=\"operator\">=</span> <span class=\"keyword\">new</span> <span class=\"title class_\">ListNode</span>(<span class=\"number\">0</span>);</span><br><span class=\"line\"> <span class=\"type\">ListNode</span> <span class=\"variable\">temp</span> <span class=\"operator\">=</span> head;</span><br><span class=\"line\"> <span class=\"type\">ListNode</span> <span class=\"variable\">l1</span> <span class=\"operator\">=</span> left;</span><br><span class=\"line\"> <span class=\"type\">ListNode</span> <span class=\"variable\">l2</span> <span class=\"operator\">=</span> right;</span><br><span class=\"line\"> <span class=\"keyword\">while</span> (l1 != <span class=\"literal\">null</span> && l2 != <span class=\"literal\">null</span>) {</span><br><span class=\"line\"> <span class=\"keyword\">if</span> (l1.val < l2.val) {</span><br><span class=\"line\"> temp.next = l1;</span><br><span class=\"line\"> l1 = l1.next;</span><br><span class=\"line\"> } <span class=\"keyword\">else</span> {</span><br><span class=\"line\"> temp.next = l2;</span><br><span class=\"line\"> l2 = l2.next;</span><br><span class=\"line\"> }</span><br><span class=\"line\"> temp = temp.next;</span><br><span class=\"line\"> }</span><br><span class=\"line\"> temp.next = l1 == <span class=\"literal\">null</span> ? l2 : l1;</span><br><span class=\"line\"> <span class=\"keyword\">return</span> head.next;</span><br><span class=\"line\"> }</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"keyword\">static</span> ListNode <span class=\"title function_\">mergeSort</span><span class=\"params\">(ListNode head)</span> {</span><br><span class=\"line\"> <span class=\"keyword\">if</span> (head == <span class=\"literal\">null</span> || head.next == <span class=\"literal\">null</span>) <span class=\"keyword\">return</span> head;</span><br><span class=\"line\"> <span class=\"type\">ListNode</span> <span class=\"variable\">mid</span> <span class=\"operator\">=</span> getMid(head);</span><br><span class=\"line\"> <span class=\"type\">ListNode</span> <span class=\"variable\">right</span> <span class=\"operator\">=</span> mid.next;</span><br><span class=\"line\"> mid.next = <span class=\"literal\">null</span>;</span><br><span class=\"line\"> <span class=\"keyword\">return</span> mergeList(mergeSort(head), mergeSort(right));</span><br><span class=\"line\"> }</span><br><span class=\"line\"></span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"comment\">//revert List</span></span><br><span class=\"line\"> <span class=\"keyword\">static</span> ListNode <span class=\"title function_\">reverse</span><span class=\"params\">(ListNode head)</span> {</span><br><span class=\"line\"> <span class=\"type\">ListNode</span> <span class=\"variable\">pre</span> <span class=\"operator\">=</span> <span class=\"literal\">null</span>;</span><br><span class=\"line\"> <span class=\"type\">ListNode</span> <span class=\"variable\">cur</span> <span class=\"operator\">=</span> head;</span><br><span class=\"line\"> <span class=\"keyword\">while</span> (cur != <span class=\"literal\">null</span>) {</span><br><span class=\"line\"> <span class=\"type\">ListNode</span> <span class=\"variable\">next</span> <span class=\"operator\">=</span> cur.next;</span><br><span class=\"line\"> cur.next = pre;</span><br><span class=\"line\"> pre = cur;</span><br><span class=\"line\"> cur = next;</span><br><span class=\"line\"> }</span><br><span class=\"line\"> <span class=\"keyword\">return</span> pre;</span><br><span class=\"line\"> }</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"keyword\">static</span> ListNode <span class=\"title function_\">reverseList</span><span class=\"params\">(ListNode head)</span> {</span><br><span class=\"line\"> <span class=\"keyword\">if</span> (head == <span class=\"literal\">null</span> || head.next == <span class=\"literal\">null</span>) <span class=\"keyword\">return</span> head;</span><br><span class=\"line\"> <span class=\"type\">ListNode</span> <span class=\"variable\">last</span> <span class=\"operator\">=</span> reverseList(head.next);</span><br><span class=\"line\"> head.next.next = head;</span><br><span class=\"line\"> head.next = <span class=\"literal\">null</span>;</span><br><span class=\"line\"> <span class=\"keyword\">return</span> last;</span><br><span class=\"line\"> }</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"keyword\">static</span> <span class=\"keyword\">void</span> <span class=\"title function_\">swap</span><span class=\"params\">(ListNode a, ListNode b)</span> {</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">t</span> <span class=\"operator\">=</span> a.val;</span><br><span class=\"line\"> a.val = b.val;</span><br><span class=\"line\"> b.val = t;</span><br><span class=\"line\"> }</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"keyword\">static</span> ListNode <span class=\"title function_\">partition</span><span class=\"params\">(ListNode head, ListNode tail)</span> {</span><br><span class=\"line\"> <span class=\"type\">ListNode</span> <span class=\"variable\">left</span> <span class=\"operator\">=</span> head;</span><br><span class=\"line\"> <span class=\"type\">ListNode</span> <span class=\"variable\">right</span> <span class=\"operator\">=</span> head.next;</span><br><span class=\"line\"> <span class=\"keyword\">while</span> (right != tail.next) {</span><br><span class=\"line\"> <span class=\"keyword\">if</span> (right.val < head.val) {</span><br><span class=\"line\"> swap(right, left.next);</span><br><span class=\"line\"> left = left.next;</span><br><span class=\"line\"> }</span><br><span class=\"line\"> right = right.next;</span><br><span class=\"line\"> }</span><br><span class=\"line\"> swap(head, left);</span><br><span class=\"line\"> <span class=\"keyword\">return</span> left;</span><br><span class=\"line\"> }</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"keyword\">static</span> <span class=\"keyword\">void</span> <span class=\"title function_\">quickSort</span><span class=\"params\">(ListNode head, ListNode tail)</span> {</span><br><span class=\"line\"> <span class=\"keyword\">if</span> (head == tail) <span class=\"keyword\">return</span>;</span><br><span class=\"line\"> <span class=\"type\">ListNode</span> <span class=\"variable\">pivot</span> <span class=\"operator\">=</span> partition(head, tail);</span><br><span class=\"line\"> quickSort(head, pivot);</span><br><span class=\"line\"> quickSort(pivot.next, tail);</span><br><span class=\"line\"> }</span><br><span class=\"line\"></span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"keyword\">static</span> <span class=\"type\">boolean</span> <span class=\"title function_\">isCrossList</span><span class=\"params\">(ListNode l1, ListNode l2)</span> {</span><br><span class=\"line\"> <span class=\"keyword\">if</span> (l1 == <span class=\"literal\">null</span> && l2 != <span class=\"literal\">null</span>) <span class=\"keyword\">return</span> <span class=\"literal\">false</span>;</span><br><span class=\"line\"> <span class=\"keyword\">if</span> (l2 == <span class=\"literal\">null</span> && l1 != <span class=\"literal\">null</span>) <span class=\"keyword\">return</span> <span class=\"literal\">false</span>;</span><br><span class=\"line\"> <span class=\"type\">ListNode</span> <span class=\"variable\">tail1</span> <span class=\"operator\">=</span> l1;</span><br><span class=\"line\"> <span class=\"type\">ListNode</span> <span class=\"variable\">tail2</span> <span class=\"operator\">=</span> l2;</span><br><span class=\"line\"> <span class=\"keyword\">while</span> (tail1.next != <span class=\"literal\">null</span>) tail1 = tail1.next;</span><br><span class=\"line\"> <span class=\"keyword\">while</span> (tail2.next != <span class=\"literal\">null</span>) tail2 = tail2.next;</span><br><span class=\"line\"> <span class=\"type\">return</span> <span class=\"variable\">tail1</span> <span class=\"operator\">=</span>= tail2;</span><br><span class=\"line\"> }</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"keyword\">static</span> ListNode <span class=\"title function_\">getLast</span><span class=\"params\">(ListNode head)</span> {</span><br><span class=\"line\"> <span class=\"type\">ListNode</span> <span class=\"variable\">temp</span> <span class=\"operator\">=</span> head;</span><br><span class=\"line\"> <span class=\"keyword\">while</span> (temp.next != <span class=\"literal\">null</span>) temp = temp.next;</span><br><span class=\"line\"> <span class=\"keyword\">return</span> temp;</span><br><span class=\"line\"> }</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"keyword\">static</span> <span class=\"type\">int</span> <span class=\"title function_\">getLen</span><span class=\"params\">(ListNode head)</span> {</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">len</span> <span class=\"operator\">=</span> <span class=\"number\">0</span>;</span><br><span class=\"line\"> <span class=\"type\">ListNode</span> <span class=\"variable\">temp</span> <span class=\"operator\">=</span> head;</span><br><span class=\"line\"> <span class=\"keyword\">while</span> (temp != <span class=\"literal\">null</span>) {</span><br><span class=\"line\"> len++;</span><br><span class=\"line\"> temp = temp.next;</span><br><span class=\"line\"> }</span><br><span class=\"line\"> <span class=\"keyword\">return</span> len;</span><br><span class=\"line\"> }</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"keyword\">static</span> ListNode <span class=\"title function_\">getCrossEntrypoint</span><span class=\"params\">(ListNode l1, ListNode l2)</span> {</span><br><span class=\"line\"> <span class=\"type\">boolean</span> <span class=\"variable\">isCross</span> <span class=\"operator\">=</span> isCrossList(l1, l2);</span><br><span class=\"line\"> <span class=\"keyword\">if</span> (!isCross) <span class=\"keyword\">return</span> <span class=\"keyword\">new</span> <span class=\"title class_\">ListNode</span>(Integer.MIN_VALUE);</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">len1</span> <span class=\"operator\">=</span> getLen(l1);</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">len2</span> <span class=\"operator\">=</span> getLen(l2);</span><br><span class=\"line\"> <span class=\"type\">int</span> <span class=\"variable\">interval</span> <span class=\"operator\">=</span> Math.abs(len1 - len2);</span><br><span class=\"line\"> <span class=\"type\">ListNode</span> <span class=\"variable\">t1</span> <span class=\"operator\">=</span> l1;</span><br><span class=\"line\"> <span class=\"type\">ListNode</span> <span class=\"variable\">t2</span> <span class=\"operator\">=</span> l2;</span><br><span class=\"line\"> <span class=\"keyword\">while</span> (interval > <span class=\"number\">0</span>) {</span><br><span class=\"line\"> <span class=\"keyword\">if</span> (len1 > len2) {</span><br><span class=\"line\"> t1 = t1.next;</span><br><span class=\"line\"> }</span><br><span class=\"line\"> <span class=\"keyword\">if</span> (len1 < len2) {</span><br><span class=\"line\"> t2 = t2.next;</span><br><span class=\"line\"> }</span><br><span class=\"line\"> interval--;</span><br><span class=\"line\"> }</span><br><span class=\"line\"> <span class=\"comment\">//对齐之后</span></span><br><span class=\"line\"> <span class=\"keyword\">while</span> (t1 != t2) {</span><br><span class=\"line\"> t1 = t1.next;</span><br><span class=\"line\"> t2 = t2.next;</span><br><span class=\"line\"> }</span><br><span class=\"line\"> <span class=\"keyword\">return</span> t1;</span><br><span class=\"line\"> }</span><br><span class=\"line\">}</span><br><span class=\"line\"></span><br></pre></td></tr></table></figure>"},{"title":"稻盛的活法","date":"2021-09-20T09:46:10.000Z","toc":true,"_content":"\n\n#### 热爱导致成功\n你想获得事业的成功和人生的幸福吗?\n你必须热爱自己的工作。\n如果你热爱自己的工作,你就会全身心地投入,如果你全身心地投入,\n就会产生良好的工作结果,这种工作态度和结果不但获得周围人的肯定,\n而且会让你内心满足和产生自信,而这又会成为动力,激励你更努力地\n投入工作,这样的良性循环,是成功的条件。\n\n冬天越寒冷,樱花就开得越烂漫,人也一样,不体验痛苦和烦恼,就难有\n大的发展,就不会抓住真正的幸福。\n\n我看青山多妩媚,料青山看我应如是。\n\n稻盛和夫说,他原本也像随处可见的小青年一样,兴趣多变,不善于将心思\n集中在一件事情上。\n为什么他又做到了50年这么漫长的时间里一心一意专注于工作呢?\n因为他付出了努力,让自己喜欢上了迷恋上了工作。\n他一开始逼迫自己,埋头到工作中去,他努力地说服自己。\n当时他不具备与新型陶瓷相关的基础知识,就去大学图书馆找资料,\n没有复印机,他就翻阅期刊把重要内容记到笔记本上,总之,一切都从获取\n最基础的知识着手的。\n然后,他根据这些资料信息设计实验,根据实验结果,再去寻求新的理论解释,\n然后再做实验,不断反复,在这个过程中,不知从何时起,他深深地为新型陶瓷\n的魅力所吸引。\n这样的研究,恐怕大学里也不会有吧,或许全世界也只有我一个人在钻研。这么一想,\n枯燥的研究也变得熠熠生辉起来了。\n天职不是偶然碰上的,而是由自己亲自制造出来的。\n\n要想一个充实的人生,你只有两种选择:一种是“从事自己喜欢的工作”,另一种是\n”让自己喜欢上工作“。一个人能够碰上自己喜欢的工作概率,恐怕不足万分之一。\n无论如何,必须得喜欢上自己的工作。\n\n#### 心想事成\n心想事成,心里想事真能成。\n废寝忘食,朝思暮想,念念不忘,反复思考,如果你真的做到了整日里只想这一件事,\n这样的愿望就渐渐渗透到潜意识当中。\n\n\n#### 专注目标\n自京瓷创立以来,稻只用心建立一年的年度经营计划,一年的计划大致能看清,不至于\n太离谱。\n做年度计划,就要细化到每个月甚至每一天的具体目标,然后千方百计努力达成。\n今天一天努力干吧,以今天一天的勤奋就一定能看清明天,这个月努力干吧,以这个月\n的勤奋就能看清下个月,今年一年努力干吧,以今年一年的勤奋就能看清明年。\n\n在设定目标时,要设定超过自己能力之上(20%)的指标,这是我的主张。\n苦难不会没完没了,幸福也不会永远持续,得意不忘形,失意不消沉,\n每天勤奋努力工作,这比什么都重要。\n\n工作要想做到完美,就必须要注意细节。\n事物的本质在于细节,美好的事物产生于注重细节的认真态度。\n","source":"_posts/稻盛的活法.md","raw":"---\ntitle: 稻盛的活法\ndate: 2021-09-20 17:46:10\ntoc: true #是否显示文章目录\ncategories: \"学习\" #分类\ntags: #标签\n - 生活\n---\n\n\n#### 热爱导致成功\n你想获得事业的成功和人生的幸福吗?\n你必须热爱自己的工作。\n如果你热爱自己的工作,你就会全身心地投入,如果你全身心地投入,\n就会产生良好的工作结果,这种工作态度和结果不但获得周围人的肯定,\n而且会让你内心满足和产生自信,而这又会成为动力,激励你更努力地\n投入工作,这样的良性循环,是成功的条件。\n\n冬天越寒冷,樱花就开得越烂漫,人也一样,不体验痛苦和烦恼,就难有\n大的发展,就不会抓住真正的幸福。\n\n我看青山多妩媚,料青山看我应如是。\n\n稻盛和夫说,他原本也像随处可见的小青年一样,兴趣多变,不善于将心思\n集中在一件事情上。\n为什么他又做到了50年这么漫长的时间里一心一意专注于工作呢?\n因为他付出了努力,让自己喜欢上了迷恋上了工作。\n他一开始逼迫自己,埋头到工作中去,他努力地说服自己。\n当时他不具备与新型陶瓷相关的基础知识,就去大学图书馆找资料,\n没有复印机,他就翻阅期刊把重要内容记到笔记本上,总之,一切都从获取\n最基础的知识着手的。\n然后,他根据这些资料信息设计实验,根据实验结果,再去寻求新的理论解释,\n然后再做实验,不断反复,在这个过程中,不知从何时起,他深深地为新型陶瓷\n的魅力所吸引。\n这样的研究,恐怕大学里也不会有吧,或许全世界也只有我一个人在钻研。这么一想,\n枯燥的研究也变得熠熠生辉起来了。\n天职不是偶然碰上的,而是由自己亲自制造出来的。\n\n要想一个充实的人生,你只有两种选择:一种是“从事自己喜欢的工作”,另一种是\n”让自己喜欢上工作“。一个人能够碰上自己喜欢的工作概率,恐怕不足万分之一。\n无论如何,必须得喜欢上自己的工作。\n\n#### 心想事成\n心想事成,心里想事真能成。\n废寝忘食,朝思暮想,念念不忘,反复思考,如果你真的做到了整日里只想这一件事,\n这样的愿望就渐渐渗透到潜意识当中。\n\n\n#### 专注目标\n自京瓷创立以来,稻只用心建立一年的年度经营计划,一年的计划大致能看清,不至于\n太离谱。\n做年度计划,就要细化到每个月甚至每一天的具体目标,然后千方百计努力达成。\n今天一天努力干吧,以今天一天的勤奋就一定能看清明天,这个月努力干吧,以这个月\n的勤奋就能看清下个月,今年一年努力干吧,以今年一年的勤奋就能看清明年。\n\n在设定目标时,要设定超过自己能力之上(20%)的指标,这是我的主张。\n苦难不会没完没了,幸福也不会永远持续,得意不忘形,失意不消沉,\n每天勤奋努力工作,这比什么都重要。\n\n工作要想做到完美,就必须要注意细节。\n事物的本质在于细节,美好的事物产生于注重细节的认真态度。\n","slug":"稻盛的活法","published":1,"updated":"2022-04-20T09:35:50.864Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cl27q3uvt003kn94oekpjbe4t","content":"<h4 id=\"热爱导致成功\"><a href=\"#热爱导致成功\" class=\"headerlink\" title=\"热爱导致成功\"></a>热爱导致成功</h4><p>你想获得事业的成功和人生的幸福吗?<br>你必须热爱自己的工作。<br>如果你热爱自己的工作,你就会全身心地投入,如果你全身心地投入,<br>就会产生良好的工作结果,这种工作态度和结果不但获得周围人的肯定,<br>而且会让你内心满足和产生自信,而这又会成为动力,激励你更努力地<br>投入工作,这样的良性循环,是成功的条件。</p>\n<p>冬天越寒冷,樱花就开得越烂漫,人也一样,不体验痛苦和烦恼,就难有<br>大的发展,就不会抓住真正的幸福。</p>\n<p>我看青山多妩媚,料青山看我应如是。</p>\n<p>稻盛和夫说,他原本也像随处可见的小青年一样,兴趣多变,不善于将心思<br>集中在一件事情上。<br>为什么他又做到了50年这么漫长的时间里一心一意专注于工作呢?<br>因为他付出了努力,让自己喜欢上了迷恋上了工作。<br>他一开始逼迫自己,埋头到工作中去,他努力地说服自己。<br>当时他不具备与新型陶瓷相关的基础知识,就去大学图书馆找资料,<br>没有复印机,他就翻阅期刊把重要内容记到笔记本上,总之,一切都从获取<br>最基础的知识着手的。<br>然后,他根据这些资料信息设计实验,根据实验结果,再去寻求新的理论解释,<br>然后再做实验,不断反复,在这个过程中,不知从何时起,他深深地为新型陶瓷<br>的魅力所吸引。<br>这样的研究,恐怕大学里也不会有吧,或许全世界也只有我一个人在钻研。这么一想,<br>枯燥的研究也变得熠熠生辉起来了。<br>天职不是偶然碰上的,而是由自己亲自制造出来的。</p>\n<p>要想一个充实的人生,你只有两种选择:一种是“从事自己喜欢的工作”,另一种是<br>”让自己喜欢上工作“。一个人能够碰上自己喜欢的工作概率,恐怕不足万分之一。<br>无论如何,必须得喜欢上自己的工作。</p>\n<h4 id=\"心想事成\"><a href=\"#心想事成\" class=\"headerlink\" title=\"心想事成\"></a>心想事成</h4><p>心想事成,心里想事真能成。<br>废寝忘食,朝思暮想,念念不忘,反复思考,如果你真的做到了整日里只想这一件事,<br>这样的愿望就渐渐渗透到潜意识当中。</p>\n<h4 id=\"专注目标\"><a href=\"#专注目标\" class=\"headerlink\" title=\"专注目标\"></a>专注目标</h4><p>自京瓷创立以来,稻只用心建立一年的年度经营计划,一年的计划大致能看清,不至于<br>太离谱。<br>做年度计划,就要细化到每个月甚至每一天的具体目标,然后千方百计努力达成。<br>今天一天努力干吧,以今天一天的勤奋就一定能看清明天,这个月努力干吧,以这个月<br>的勤奋就能看清下个月,今年一年努力干吧,以今年一年的勤奋就能看清明年。</p>\n<p>在设定目标时,要设定超过自己能力之上(20%)的指标,这是我的主张。<br>苦难不会没完没了,幸福也不会永远持续,得意不忘形,失意不消沉,<br>每天勤奋努力工作,这比什么都重要。</p>\n<p>工作要想做到完美,就必须要注意细节。<br>事物的本质在于细节,美好的事物产生于注重细节的认真态度。</p>\n","site":{"data":{}},"excerpt":"","more":"<h4 id=\"热爱导致成功\"><a href=\"#热爱导致成功\" class=\"headerlink\" title=\"热爱导致成功\"></a>热爱导致成功</h4><p>你想获得事业的成功和人生的幸福吗?<br>你必须热爱自己的工作。<br>如果你热爱自己的工作,你就会全身心地投入,如果你全身心地投入,<br>就会产生良好的工作结果,这种工作态度和结果不但获得周围人的肯定,<br>而且会让你内心满足和产生自信,而这又会成为动力,激励你更努力地<br>投入工作,这样的良性循环,是成功的条件。</p>\n<p>冬天越寒冷,樱花就开得越烂漫,人也一样,不体验痛苦和烦恼,就难有<br>大的发展,就不会抓住真正的幸福。</p>\n<p>我看青山多妩媚,料青山看我应如是。</p>\n<p>稻盛和夫说,他原本也像随处可见的小青年一样,兴趣多变,不善于将心思<br>集中在一件事情上。<br>为什么他又做到了50年这么漫长的时间里一心一意专注于工作呢?<br>因为他付出了努力,让自己喜欢上了迷恋上了工作。<br>他一开始逼迫自己,埋头到工作中去,他努力地说服自己。<br>当时他不具备与新型陶瓷相关的基础知识,就去大学图书馆找资料,<br>没有复印机,他就翻阅期刊把重要内容记到笔记本上,总之,一切都从获取<br>最基础的知识着手的。<br>然后,他根据这些资料信息设计实验,根据实验结果,再去寻求新的理论解释,<br>然后再做实验,不断反复,在这个过程中,不知从何时起,他深深地为新型陶瓷<br>的魅力所吸引。<br>这样的研究,恐怕大学里也不会有吧,或许全世界也只有我一个人在钻研。这么一想,<br>枯燥的研究也变得熠熠生辉起来了。<br>天职不是偶然碰上的,而是由自己亲自制造出来的。</p>\n<p>要想一个充实的人生,你只有两种选择:一种是“从事自己喜欢的工作”,另一种是<br>”让自己喜欢上工作“。一个人能够碰上自己喜欢的工作概率,恐怕不足万分之一。<br>无论如何,必须得喜欢上自己的工作。</p>\n<h4 id=\"心想事成\"><a href=\"#心想事成\" class=\"headerlink\" title=\"心想事成\"></a>心想事成</h4><p>心想事成,心里想事真能成。<br>废寝忘食,朝思暮想,念念不忘,反复思考,如果你真的做到了整日里只想这一件事,<br>这样的愿望就渐渐渗透到潜意识当中。</p>\n<h4 id=\"专注目标\"><a href=\"#专注目标\" class=\"headerlink\" title=\"专注目标\"></a>专注目标</h4><p>自京瓷创立以来,稻只用心建立一年的年度经营计划,一年的计划大致能看清,不至于<br>太离谱。<br>做年度计划,就要细化到每个月甚至每一天的具体目标,然后千方百计努力达成。<br>今天一天努力干吧,以今天一天的勤奋就一定能看清明天,这个月努力干吧,以这个月<br>的勤奋就能看清下个月,今年一年努力干吧,以今年一年的勤奋就能看清明年。</p>\n<p>在设定目标时,要设定超过自己能力之上(20%)的指标,这是我的主张。<br>苦难不会没完没了,幸福也不会永远持续,得意不忘形,失意不消沉,<br>每天勤奋努力工作,这比什么都重要。</p>\n<p>工作要想做到完美,就必须要注意细节。<br>事物的本质在于细节,美好的事物产生于注重细节的认真态度。</p>\n"},{"title":"hive metastore on minio","date":"2022-04-22T16:00:00.000Z","toc":true,"_content":"### hive metastore install step by step\n目标是安装好hive metastore stand alone的服务。\n本文还使用了s3存储,使用的是minio,把warehouse.dir配置到了对象存储上。\n#### 安装mysql\n因为存储要使用到mysql,所以直接使用了flink cdc 的一个demo,这个demo中有mysql,pg,es,kibana.\nhttps://ververica.github.io/flink-cdc-connectors/master/content/quickstart/mysql-postgres-tutorial.html\n```yaml\nversion: '2.1'\nservices:\n postgres:\n image: debezium/example-postgres:1.1\n ports:\n - \"5432:5432\"\n environment:\n - POSTGRES_DB=postgres\n - POSTGRES_USER=postgres\n - POSTGRES_PASSWORD=postgres\n mysql:\n image: debezium/example-mysql:1.1\n ports:\n - \"3306:3306\"\n environment:\n - MYSQL_ROOT_PASSWORD=123456\n - MYSQL_USER=mysqluser\n - MYSQL_PASSWORD=mysqlpw\n elasticsearch:\n image: elastic/elasticsearch:7.6.0\n environment:\n - cluster.name=docker-cluster\n - bootstrap.memory_lock=true\n - \"ES_JAVA_OPTS=-Xms512m -Xmx512m\"\n - discovery.type=single-node\n ports:\n - \"9200:9200\"\n - \"9300:9300\"\n ulimits:\n memlock:\n soft: -1\n hard: -1\n nofile:\n soft: 65536\n hard: 65536\n kibana:\n image: elastic/kibana:7.6.0\n ports:\n - \"5601:5601\"\n```\n以上内容存储为docker-compose.yml即可,如果不需要pg,es,kibana可以去掉.\ndocker compose up -d 即可启动mysql.\n#### mysql 添加用户hive\n```bash\ndocker-compose exec mysql mysql -uroot -p123456\ngrant all privileges on *.* to hive@'%' identified by 'hive';\nflush privileges ;\n```\n#### 下载安装 hive metastore\n```bash\ncd ~/app\nwget https://repo1.maven.org/maven2/org/apache/hive/hive-standalone-metastore/3.1.2/hive-standalone-metastore-3.1.2-bin.tar.gz\ntar -xvf hive-standalone-metastore-3.1.2-bin.tar.gz\nrm -f hive-standalone-metastore-3.1.2-bin.tar.gz\nmv apache-hive-metastore-3.1.2-bin metastore\ncd metastore/lib\nwget https://repo1.maven.org/maven2/mysql/mysql-connector-java/5.1.49/mysql-connector-java-5.1.49.jar \n####metastore 依赖hadoop \ncd ~/app/\nwget https://dlcdn.apache.org/hadoop/common/hadoop-3.3.1/hadoop-3.3.1.tar.gz \ntar -xvf hadoop-3.3.1.tar.gz\nrm hadoop-3.3.1.tar.gz\nln -s ./hadoop-3.3.1 hadoop\n```\n#### 更新hive metastore的配置\n metastore-site.xml, mysql 连接信息\n```xml\n<property>\n <name>javax.jdo.option.ConnectionDriverName</name>\n <value>com.mysql.jdbc.Driver</value>\n</property>\n<property>\n <name>javax.jdo.option.ConnectionURL</name>\n <value>jdbc:mysql://localhost:3306/hive?createDatabaseIfNotExist=true&useSSL=false</value>\n</property>\n<property>\n <name>javax.jdo.option.ConnectionUserName</name>\n <value>hive</value>\n</property>\n<property>\n <name>javax.jdo.option.ConnectionPassword</name>\n <value>hive</value>\n</property>\n\n<property>\n <name>hive.metastore.warehouse.dir</name>\n <value>s3a://hive/warehouse</value>\n</property>\n\n <property>\n <name>fs.defaultFS</name>\n <value>s3a://</value>\n </property>\n <property>\n <name>fs.s3a.access.key</name>\n <value>minio</value>\n </property>\n <property>\n <name>fs.s3a.secret.key</name>\n <value>minio123</value>\n </property>\n <property>\n <name>fs.s3a.connection.ssl.enabled</name>\n <value>false</value>\n </property>\n <property>\n <name>fs.s3a.path.style.access</name>\n <value>true</value>\n </property>\n <property>\n <name>fs.s3a.endpoint</name>\n <value>http://localhost:9000</value>\n </property>\n <property>\n <name>fs.s3a.impl</name>\n <value>org.apache.hadoop.fs.s3a.S3AFileSystem</value>\n </property>\n\n```\n#### 初始化元数据 \n```bash\nrm -f ~/app/metastore/lib/guava-19.0.jar\nbin/schematool -dbType mysql -initSchema\n```\n> Exception in thread \"main\" java.lang.NoSuchMethodError: 'void com.google.common.base.Preconditions.checkArgument(boolean, java.lang.String, java.lang.Object)'\n因为guava jar包的冲突,需要提前处理一下。\n \n然后检查mysql数据库,发现有一个新建的hive db,里面有多张相关的表。\n\n#### minio的安装使用\nminio是一个兼容s3的开源存储服务,非常适合在本地测试对象存储。\n```yml\nversion: '3'\nservices:\n minio:\n image: minio/minio\n hostname: minio\n ports:\n - 9000:9000 #api 端口\n - 9001:9001 #控制台端口\n environment:\n - MINIO_ROOT_USER=minio\n - MINIO_ROOT_PASSWORD=minio123\n volumes:\n - data-1:/data\n command: server --console-address \":9001\" /data\nvolumes:\n data-1:%\n```\n存储为docker-compose.yml\ndocker compose up -d即可启动minio服务。\n可以在浏览器中localhost:9001使用前面配置的用户名密码登录即可查看。\n#### 开启服务进程\n```bash\ncp conf/metastore-log4j2.properties conf/log4j2.properties\nbin/start-metastore\n```\n> MetaException(message:java.lang.ClassNotFoundException: Class org.apache.hadoop.fs.s3a.S3AFileSystem not found)\n因为配置了\n<name>hive.metastore.warehouse.dir</name><value>s3a://hive/warehouse</value>\n复制aws相关的jar包到lib/目录下。\ncd ~/app/hadoop\nfind . -iname \"*aws*.jar\" |xargs -I _ cp _ ~/app/metastore/lib/\n接着重启服务进程遇到如下错误:\nCaused by: MetaException(message:Got exception: org.apache.hadoop.fs.s3a.AWSClientIOException initializing on s3a://hive/warehouse: com.amazonaws.SdkClientException: Unable to find a region via the region provider chain. Must provide an explicit region in the builder or setup environment to supply a region.: Unable to find a region via the region provider chain. Must provide an explicit region in the builder or setup environment to supply a region.)\n把minio相关的访问信息配置到metastore-site.xml 或单独配置一个core-site.xml到conf下都可以。\n\n下章节使用spark-sql or spark-shell来使用hive metastore。\n\n\n","source":"_posts/hive-metastore-on-minio.md","raw":"---\ntitle: hive metastore on minio\ndate: 2022-04-23\ntoc: true #是否显示文章目录\ncategories: \"Hive\" #分类\ntags: #标签\n - 大数据\n - Hive\n---\n### hive metastore install step by step\n目标是安装好hive metastore stand alone的服务。\n本文还使用了s3存储,使用的是minio,把warehouse.dir配置到了对象存储上。\n#### 安装mysql\n因为存储要使用到mysql,所以直接使用了flink cdc 的一个demo,这个demo中有mysql,pg,es,kibana.\nhttps://ververica.github.io/flink-cdc-connectors/master/content/quickstart/mysql-postgres-tutorial.html\n```yaml\nversion: '2.1'\nservices:\n postgres:\n image: debezium/example-postgres:1.1\n ports:\n - \"5432:5432\"\n environment:\n - POSTGRES_DB=postgres\n - POSTGRES_USER=postgres\n - POSTGRES_PASSWORD=postgres\n mysql:\n image: debezium/example-mysql:1.1\n ports:\n - \"3306:3306\"\n environment:\n - MYSQL_ROOT_PASSWORD=123456\n - MYSQL_USER=mysqluser\n - MYSQL_PASSWORD=mysqlpw\n elasticsearch:\n image: elastic/elasticsearch:7.6.0\n environment:\n - cluster.name=docker-cluster\n - bootstrap.memory_lock=true\n - \"ES_JAVA_OPTS=-Xms512m -Xmx512m\"\n - discovery.type=single-node\n ports:\n - \"9200:9200\"\n - \"9300:9300\"\n ulimits:\n memlock:\n soft: -1\n hard: -1\n nofile:\n soft: 65536\n hard: 65536\n kibana:\n image: elastic/kibana:7.6.0\n ports:\n - \"5601:5601\"\n```\n以上内容存储为docker-compose.yml即可,如果不需要pg,es,kibana可以去掉.\ndocker compose up -d 即可启动mysql.\n#### mysql 添加用户hive\n```bash\ndocker-compose exec mysql mysql -uroot -p123456\ngrant all privileges on *.* to hive@'%' identified by 'hive';\nflush privileges ;\n```\n#### 下载安装 hive metastore\n```bash\ncd ~/app\nwget https://repo1.maven.org/maven2/org/apache/hive/hive-standalone-metastore/3.1.2/hive-standalone-metastore-3.1.2-bin.tar.gz\ntar -xvf hive-standalone-metastore-3.1.2-bin.tar.gz\nrm -f hive-standalone-metastore-3.1.2-bin.tar.gz\nmv apache-hive-metastore-3.1.2-bin metastore\ncd metastore/lib\nwget https://repo1.maven.org/maven2/mysql/mysql-connector-java/5.1.49/mysql-connector-java-5.1.49.jar \n####metastore 依赖hadoop \ncd ~/app/\nwget https://dlcdn.apache.org/hadoop/common/hadoop-3.3.1/hadoop-3.3.1.tar.gz \ntar -xvf hadoop-3.3.1.tar.gz\nrm hadoop-3.3.1.tar.gz\nln -s ./hadoop-3.3.1 hadoop\n```\n#### 更新hive metastore的配置\n metastore-site.xml, mysql 连接信息\n```xml\n<property>\n <name>javax.jdo.option.ConnectionDriverName</name>\n <value>com.mysql.jdbc.Driver</value>\n</property>\n<property>\n <name>javax.jdo.option.ConnectionURL</name>\n <value>jdbc:mysql://localhost:3306/hive?createDatabaseIfNotExist=true&useSSL=false</value>\n</property>\n<property>\n <name>javax.jdo.option.ConnectionUserName</name>\n <value>hive</value>\n</property>\n<property>\n <name>javax.jdo.option.ConnectionPassword</name>\n <value>hive</value>\n</property>\n\n<property>\n <name>hive.metastore.warehouse.dir</name>\n <value>s3a://hive/warehouse</value>\n</property>\n\n <property>\n <name>fs.defaultFS</name>\n <value>s3a://</value>\n </property>\n <property>\n <name>fs.s3a.access.key</name>\n <value>minio</value>\n </property>\n <property>\n <name>fs.s3a.secret.key</name>\n <value>minio123</value>\n </property>\n <property>\n <name>fs.s3a.connection.ssl.enabled</name>\n <value>false</value>\n </property>\n <property>\n <name>fs.s3a.path.style.access</name>\n <value>true</value>\n </property>\n <property>\n <name>fs.s3a.endpoint</name>\n <value>http://localhost:9000</value>\n </property>\n <property>\n <name>fs.s3a.impl</name>\n <value>org.apache.hadoop.fs.s3a.S3AFileSystem</value>\n </property>\n\n```\n#### 初始化元数据 \n```bash\nrm -f ~/app/metastore/lib/guava-19.0.jar\nbin/schematool -dbType mysql -initSchema\n```\n> Exception in thread \"main\" java.lang.NoSuchMethodError: 'void com.google.common.base.Preconditions.checkArgument(boolean, java.lang.String, java.lang.Object)'\n因为guava jar包的冲突,需要提前处理一下。\n \n然后检查mysql数据库,发现有一个新建的hive db,里面有多张相关的表。\n\n#### minio的安装使用\nminio是一个兼容s3的开源存储服务,非常适合在本地测试对象存储。\n```yml\nversion: '3'\nservices:\n minio:\n image: minio/minio\n hostname: minio\n ports:\n - 9000:9000 #api 端口\n - 9001:9001 #控制台端口\n environment:\n - MINIO_ROOT_USER=minio\n - MINIO_ROOT_PASSWORD=minio123\n volumes:\n - data-1:/data\n command: server --console-address \":9001\" /data\nvolumes:\n data-1:%\n```\n存储为docker-compose.yml\ndocker compose up -d即可启动minio服务。\n可以在浏览器中localhost:9001使用前面配置的用户名密码登录即可查看。\n#### 开启服务进程\n```bash\ncp conf/metastore-log4j2.properties conf/log4j2.properties\nbin/start-metastore\n```\n> MetaException(message:java.lang.ClassNotFoundException: Class org.apache.hadoop.fs.s3a.S3AFileSystem not found)\n因为配置了\n<name>hive.metastore.warehouse.dir</name><value>s3a://hive/warehouse</value>\n复制aws相关的jar包到lib/目录下。\ncd ~/app/hadoop\nfind . -iname \"*aws*.jar\" |xargs -I _ cp _ ~/app/metastore/lib/\n接着重启服务进程遇到如下错误:\nCaused by: MetaException(message:Got exception: org.apache.hadoop.fs.s3a.AWSClientIOException initializing on s3a://hive/warehouse: com.amazonaws.SdkClientException: Unable to find a region via the region provider chain. Must provide an explicit region in the builder or setup environment to supply a region.: Unable to find a region via the region provider chain. Must provide an explicit region in the builder or setup environment to supply a region.)\n把minio相关的访问信息配置到metastore-site.xml 或单独配置一个core-site.xml到conf下都可以。\n\n下章节使用spark-sql or spark-shell来使用hive metastore。\n\n\n","slug":"hive-metastore-on-minio","published":1,"updated":"2022-04-23T05:28:30.426Z","_id":"cl2bf01vq0000r74ocw0q794k","comments":1,"layout":"post","photos":[],"link":"","content":"<h3 id=\"hive-metastore-install-step-by-step\"><a href=\"#hive-metastore-install-step-by-step\" class=\"headerlink\" title=\"hive metastore install step by step\"></a>hive metastore install step by step</h3><p>目标是安装好hive metastore stand alone的服务。<br>本文还使用了s3存储,使用的是minio,把warehouse.dir配置到了对象存储上。</p>\n<h4 id=\"安装mysql\"><a href=\"#安装mysql\" class=\"headerlink\" title=\"安装mysql\"></a>安装mysql</h4><p>因为存储要使用到mysql,所以直接使用了flink cdc 的一个demo,这个demo中有mysql,pg,es,kibana.<br><a href=\"https://ververica.github.io/flink-cdc-connectors/master/content/quickstart/mysql-postgres-tutorial.html\">https://ververica.github.io/flink-cdc-connectors/master/content/quickstart/mysql-postgres-tutorial.html</a></p>\n<figure class=\"highlight yaml\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br><span class=\"line\">11</span><br><span class=\"line\">12</span><br><span class=\"line\">13</span><br><span class=\"line\">14</span><br><span class=\"line\">15</span><br><span class=\"line\">16</span><br><span class=\"line\">17</span><br><span class=\"line\">18</span><br><span class=\"line\">19</span><br><span class=\"line\">20</span><br><span class=\"line\">21</span><br><span class=\"line\">22</span><br><span class=\"line\">23</span><br><span class=\"line\">24</span><br><span class=\"line\">25</span><br><span class=\"line\">26</span><br><span class=\"line\">27</span><br><span class=\"line\">28</span><br><span class=\"line\">29</span><br><span class=\"line\">30</span><br><span class=\"line\">31</span><br><span class=\"line\">32</span><br><span class=\"line\">33</span><br><span class=\"line\">34</span><br><span class=\"line\">35</span><br><span class=\"line\">36</span><br><span class=\"line\">37</span><br><span class=\"line\">38</span><br><span class=\"line\">39</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"attr\">version:</span> <span class=\"string\">'2.1'</span></span><br><span class=\"line\"><span class=\"attr\">services:</span></span><br><span class=\"line\"> <span class=\"attr\">postgres:</span></span><br><span class=\"line\"> <span class=\"attr\">image:</span> <span class=\"string\">debezium/example-postgres:1.1</span></span><br><span class=\"line\"> <span class=\"attr\">ports:</span></span><br><span class=\"line\"> <span class=\"bullet\">-</span> <span class=\"string\">"5432:5432"</span></span><br><span class=\"line\"> <span class=\"attr\">environment:</span></span><br><span class=\"line\"> <span class=\"bullet\">-</span> <span class=\"string\">POSTGRES_DB=postgres</span></span><br><span class=\"line\"> <span class=\"bullet\">-</span> <span class=\"string\">POSTGRES_USER=postgres</span></span><br><span class=\"line\"> <span class=\"bullet\">-</span> <span class=\"string\">POSTGRES_PASSWORD=postgres</span></span><br><span class=\"line\"> <span class=\"attr\">mysql:</span></span><br><span class=\"line\"> <span class=\"attr\">image:</span> <span class=\"string\">debezium/example-mysql:1.1</span></span><br><span class=\"line\"> <span class=\"attr\">ports:</span></span><br><span class=\"line\"> <span class=\"bullet\">-</span> <span class=\"string\">"3306:3306"</span></span><br><span class=\"line\"> <span class=\"attr\">environment:</span></span><br><span class=\"line\"> <span class=\"bullet\">-</span> <span class=\"string\">MYSQL_ROOT_PASSWORD=123456</span></span><br><span class=\"line\"> <span class=\"bullet\">-</span> <span class=\"string\">MYSQL_USER=mysqluser</span></span><br><span class=\"line\"> <span class=\"bullet\">-</span> <span class=\"string\">MYSQL_PASSWORD=mysqlpw</span></span><br><span class=\"line\"> <span class=\"attr\">elasticsearch:</span></span><br><span class=\"line\"> <span class=\"attr\">image:</span> <span class=\"string\">elastic/elasticsearch:7.6.0</span></span><br><span class=\"line\"> <span class=\"attr\">environment:</span></span><br><span class=\"line\"> <span class=\"bullet\">-</span> <span class=\"string\">cluster.name=docker-cluster</span></span><br><span class=\"line\"> <span class=\"bullet\">-</span> <span class=\"string\">bootstrap.memory_lock=true</span></span><br><span class=\"line\"> <span class=\"bullet\">-</span> <span class=\"string\">"ES_JAVA_OPTS=-Xms512m -Xmx512m"</span></span><br><span class=\"line\"> <span class=\"bullet\">-</span> <span class=\"string\">discovery.type=single-node</span></span><br><span class=\"line\"> <span class=\"attr\">ports:</span></span><br><span class=\"line\"> <span class=\"bullet\">-</span> <span class=\"string\">"9200:9200"</span></span><br><span class=\"line\"> <span class=\"bullet\">-</span> <span class=\"string\">"9300:9300"</span></span><br><span class=\"line\"> <span class=\"attr\">ulimits:</span></span><br><span class=\"line\"> <span class=\"attr\">memlock:</span></span><br><span class=\"line\"> <span class=\"attr\">soft:</span> <span class=\"number\">-1</span></span><br><span class=\"line\"> <span class=\"attr\">hard:</span> <span class=\"number\">-1</span></span><br><span class=\"line\"> <span class=\"attr\">nofile:</span></span><br><span class=\"line\"> <span class=\"attr\">soft:</span> <span class=\"number\">65536</span></span><br><span class=\"line\"> <span class=\"attr\">hard:</span> <span class=\"number\">65536</span></span><br><span class=\"line\"> <span class=\"attr\">kibana:</span></span><br><span class=\"line\"> <span class=\"attr\">image:</span> <span class=\"string\">elastic/kibana:7.6.0</span></span><br><span class=\"line\"> <span class=\"attr\">ports:</span></span><br><span class=\"line\"> <span class=\"bullet\">-</span> <span class=\"string\">"5601:5601"</span></span><br></pre></td></tr></table></figure>\n<p>以上内容存储为docker-compose.yml即可,如果不需要pg,es,kibana可以去掉.<br>docker compose up -d 即可启动mysql.</p>\n<h4 id=\"mysql-添加用户hive\"><a href=\"#mysql-添加用户hive\" class=\"headerlink\" title=\"mysql 添加用户hive\"></a>mysql 添加用户hive</h4><figure class=\"highlight bash\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">docker-compose <span class=\"built_in\">exec</span> mysql mysql -uroot -p123456</span><br><span class=\"line\">grant all privileges on *.* to hive@<span class=\"string\">'%'</span> identified by <span class=\"string\">'hive'</span>;</span><br><span class=\"line\">flush privileges ;</span><br></pre></td></tr></table></figure>\n<h4 id=\"下载安装-hive-metastore\"><a href=\"#下载安装-hive-metastore\" class=\"headerlink\" title=\"下载安装 hive metastore\"></a>下载安装 hive metastore</h4><figure class=\"highlight bash\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br><span class=\"line\">11</span><br><span class=\"line\">12</span><br><span class=\"line\">13</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"built_in\">cd</span> ~/app</span><br><span class=\"line\">wget https://repo1.maven.org/maven2/org/apache/hive/hive-standalone-metastore/3.1.2/hive-standalone-metastore-3.1.2-bin.tar.gz</span><br><span class=\"line\">tar -xvf hive-standalone-metastore-3.1.2-bin.tar.gz</span><br><span class=\"line\"><span class=\"built_in\">rm</span> -f hive-standalone-metastore-3.1.2-bin.tar.gz</span><br><span class=\"line\"><span class=\"built_in\">mv</span> apache-hive-metastore-3.1.2-bin metastore</span><br><span class=\"line\"><span class=\"built_in\">cd</span> metastore/lib</span><br><span class=\"line\">wget https://repo1.maven.org/maven2/mysql/mysql-connector-java/5.1.49/mysql-connector-java-5.1.49.jar </span><br><span class=\"line\"><span class=\"comment\">####metastore 依赖hadoop </span></span><br><span class=\"line\"><span class=\"built_in\">cd</span> ~/app/</span><br><span class=\"line\">wget https://dlcdn.apache.org/hadoop/common/hadoop-3.3.1/hadoop-3.3.1.tar.gz </span><br><span class=\"line\">tar -xvf hadoop-3.3.1.tar.gz</span><br><span class=\"line\"><span class=\"built_in\">rm</span> hadoop-3.3.1.tar.gz</span><br><span class=\"line\"><span class=\"built_in\">ln</span> -s ./hadoop-3.3.1 hadoop</span><br></pre></td></tr></table></figure>\n<h4 id=\"更新hive-metastore的配置\"><a href=\"#更新hive-metastore的配置\" class=\"headerlink\" title=\"更新hive metastore的配置\"></a>更新hive metastore的配置</h4><p> metastore-site.xml, mysql 连接信息</p>\n<figure class=\"highlight xml\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br><span class=\"line\">11</span><br><span class=\"line\">12</span><br><span class=\"line\">13</span><br><span class=\"line\">14</span><br><span class=\"line\">15</span><br><span class=\"line\">16</span><br><span class=\"line\">17</span><br><span class=\"line\">18</span><br><span class=\"line\">19</span><br><span class=\"line\">20</span><br><span class=\"line\">21</span><br><span class=\"line\">22</span><br><span class=\"line\">23</span><br><span class=\"line\">24</span><br><span class=\"line\">25</span><br><span class=\"line\">26</span><br><span class=\"line\">27</span><br><span class=\"line\">28</span><br><span class=\"line\">29</span><br><span class=\"line\">30</span><br><span class=\"line\">31</span><br><span class=\"line\">32</span><br><span class=\"line\">33</span><br><span class=\"line\">34</span><br><span class=\"line\">35</span><br><span class=\"line\">36</span><br><span class=\"line\">37</span><br><span class=\"line\">38</span><br><span class=\"line\">39</span><br><span class=\"line\">40</span><br><span class=\"line\">41</span><br><span class=\"line\">42</span><br><span class=\"line\">43</span><br><span class=\"line\">44</span><br><span class=\"line\">45</span><br><span class=\"line\">46</span><br><span class=\"line\">47</span><br><span class=\"line\">48</span><br><span class=\"line\">49</span><br><span class=\"line\">50</span><br><span class=\"line\">51</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"tag\"><<span class=\"name\">property</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">name</span>></span>javax.jdo.option.ConnectionDriverName<span class=\"tag\"></<span class=\"name\">name</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">value</span>></span>com.mysql.jdbc.Driver<span class=\"tag\"></<span class=\"name\">value</span>></span></span><br><span class=\"line\"><span class=\"tag\"></<span class=\"name\">property</span>></span></span><br><span class=\"line\"><span class=\"tag\"><<span class=\"name\">property</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">name</span>></span>javax.jdo.option.ConnectionURL<span class=\"tag\"></<span class=\"name\">name</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">value</span>></span>jdbc:mysql://localhost:3306/hive?createDatabaseIfNotExist=true<span class=\"symbol\">&amp;</span>useSSL=false<span class=\"tag\"></<span class=\"name\">value</span>></span></span><br><span class=\"line\"><span class=\"tag\"></<span class=\"name\">property</span>></span></span><br><span class=\"line\"><span class=\"tag\"><<span class=\"name\">property</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">name</span>></span>javax.jdo.option.ConnectionUserName<span class=\"tag\"></<span class=\"name\">name</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">value</span>></span>hive<span class=\"tag\"></<span class=\"name\">value</span>></span></span><br><span class=\"line\"><span class=\"tag\"></<span class=\"name\">property</span>></span></span><br><span class=\"line\"><span class=\"tag\"><<span class=\"name\">property</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">name</span>></span>javax.jdo.option.ConnectionPassword<span class=\"tag\"></<span class=\"name\">name</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">value</span>></span>hive<span class=\"tag\"></<span class=\"name\">value</span>></span></span><br><span class=\"line\"><span class=\"tag\"></<span class=\"name\">property</span>></span></span><br><span class=\"line\"></span><br><span class=\"line\"><span class=\"tag\"><<span class=\"name\">property</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">name</span>></span>hive.metastore.warehouse.dir<span class=\"tag\"></<span class=\"name\">name</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">value</span>></span>s3a://hive/warehouse<span class=\"tag\"></<span class=\"name\">value</span>></span></span><br><span class=\"line\"><span class=\"tag\"></<span class=\"name\">property</span>></span></span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">property</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">name</span>></span>fs.defaultFS<span class=\"tag\"></<span class=\"name\">name</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">value</span>></span>s3a://<span class=\"tag\"></<span class=\"name\">value</span>></span></span><br><span class=\"line\"> <span class=\"tag\"></<span class=\"name\">property</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">property</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">name</span>></span>fs.s3a.access.key<span class=\"tag\"></<span class=\"name\">name</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">value</span>></span>minio<span class=\"tag\"></<span class=\"name\">value</span>></span></span><br><span class=\"line\"> <span class=\"tag\"></<span class=\"name\">property</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">property</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">name</span>></span>fs.s3a.secret.key<span class=\"tag\"></<span class=\"name\">name</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">value</span>></span>minio123<span class=\"tag\"></<span class=\"name\">value</span>></span></span><br><span class=\"line\"> <span class=\"tag\"></<span class=\"name\">property</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">property</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">name</span>></span>fs.s3a.connection.ssl.enabled<span class=\"tag\"></<span class=\"name\">name</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">value</span>></span>false<span class=\"tag\"></<span class=\"name\">value</span>></span></span><br><span class=\"line\"> <span class=\"tag\"></<span class=\"name\">property</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">property</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">name</span>></span>fs.s3a.path.style.access<span class=\"tag\"></<span class=\"name\">name</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">value</span>></span>true<span class=\"tag\"></<span class=\"name\">value</span>></span></span><br><span class=\"line\"> <span class=\"tag\"></<span class=\"name\">property</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">property</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">name</span>></span>fs.s3a.endpoint<span class=\"tag\"></<span class=\"name\">name</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">value</span>></span>http://localhost:9000<span class=\"tag\"></<span class=\"name\">value</span>></span></span><br><span class=\"line\"> <span class=\"tag\"></<span class=\"name\">property</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">property</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">name</span>></span>fs.s3a.impl<span class=\"tag\"></<span class=\"name\">name</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">value</span>></span>org.apache.hadoop.fs.s3a.S3AFileSystem<span class=\"tag\"></<span class=\"name\">value</span>></span></span><br><span class=\"line\"> <span class=\"tag\"></<span class=\"name\">property</span>></span></span><br><span class=\"line\"></span><br></pre></td></tr></table></figure>\n<h4 id=\"初始化元数据\"><a href=\"#初始化元数据\" class=\"headerlink\" title=\"初始化元数据\"></a>初始化元数据</h4><figure class=\"highlight bash\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"built_in\">rm</span> -f ~/app/metastore/lib/guava-19.0.jar</span><br><span class=\"line\">bin/schematool -dbType mysql -initSchema</span><br></pre></td></tr></table></figure>\n<blockquote>\n<p>Exception in thread “main” java.lang.NoSuchMethodError: ‘void com.google.common.base.Preconditions.checkArgument(boolean, java.lang.String, java.lang.Object)’<br>因为guava jar包的冲突,需要提前处理一下。</p>\n</blockquote>\n<p>然后检查mysql数据库,发现有一个新建的hive db,里面有多张相关的表。</p>\n<h4 id=\"minio的安装使用\"><a href=\"#minio的安装使用\" class=\"headerlink\" title=\"minio的安装使用\"></a>minio的安装使用</h4><p>minio是一个兼容s3的开源存储服务,非常适合在本地测试对象存储。</p>\n<figure class=\"highlight yml\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br><span class=\"line\">11</span><br><span class=\"line\">12</span><br><span class=\"line\">13</span><br><span class=\"line\">14</span><br><span class=\"line\">15</span><br><span class=\"line\">16</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"attr\">version:</span> <span class=\"string\">'3'</span></span><br><span class=\"line\"><span class=\"attr\">services:</span></span><br><span class=\"line\"> <span class=\"attr\">minio:</span></span><br><span class=\"line\"> <span class=\"attr\">image:</span> <span class=\"string\">minio/minio</span></span><br><span class=\"line\"> <span class=\"attr\">hostname:</span> <span class=\"string\">minio</span></span><br><span class=\"line\"> <span class=\"attr\">ports:</span></span><br><span class=\"line\"> <span class=\"bullet\">-</span> <span class=\"number\">9000</span><span class=\"string\">:9000</span> <span class=\"comment\">#api 端口</span></span><br><span class=\"line\"> <span class=\"bullet\">-</span> <span class=\"number\">9001</span><span class=\"string\">:9001</span> <span class=\"comment\">#控制台端口</span></span><br><span class=\"line\"> <span class=\"attr\">environment:</span></span><br><span class=\"line\"> <span class=\"bullet\">-</span> <span class=\"string\">MINIO_ROOT_USER=minio</span></span><br><span class=\"line\"> <span class=\"bullet\">-</span> <span class=\"string\">MINIO_ROOT_PASSWORD=minio123</span></span><br><span class=\"line\"> <span class=\"attr\">volumes:</span></span><br><span class=\"line\"> <span class=\"bullet\">-</span> <span class=\"string\">data-1:/data</span></span><br><span class=\"line\"> <span class=\"attr\">command:</span> <span class=\"string\">server</span> <span class=\"string\">--console-address</span> <span class=\"string\">":9001"</span> <span class=\"string\">/data</span></span><br><span class=\"line\"><span class=\"attr\">volumes:</span></span><br><span class=\"line\"> <span class=\"string\">data-1:%</span></span><br></pre></td></tr></table></figure>\n<p>存储为docker-compose.yml<br>docker compose up -d即可启动minio服务。<br>可以在浏览器中localhost:9001使用前面配置的用户名密码登录即可查看。</p>\n<h4 id=\"开启服务进程\"><a href=\"#开启服务进程\" class=\"headerlink\" title=\"开启服务进程\"></a>开启服务进程</h4><figure class=\"highlight bash\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"built_in\">cp</span> conf/metastore-log4j2.properties conf/log4j2.properties</span><br><span class=\"line\">bin/start-metastore</span><br></pre></td></tr></table></figure>\n<blockquote>\n<p>MetaException(message:java.lang.ClassNotFoundException: Class org.apache.hadoop.fs.s3a.S3AFileSystem not found)<br>因为配置了<br><name>hive.metastore.warehouse.dir</name><value>s3a://hive/warehouse</value><br>复制aws相关的jar包到lib/目录下。<br>cd ~/app/hadoop<br>find . -iname “<em>aws</em>.jar” |xargs -I _ cp _ ~/app/metastore/lib/<br>接着重启服务进程遇到如下错误:<br>Caused by: MetaException(message:Got exception: org.apache.hadoop.fs.s3a.AWSClientIOException initializing on s3a://hive/warehouse: com.amazonaws.SdkClientException: Unable to find a region via the region provider chain. Must provide an explicit region in the builder or setup environment to supply a region.: Unable to find a region via the region provider chain. Must provide an explicit region in the builder or setup environment to supply a region.)<br>把minio相关的访问信息配置到metastore-site.xml 或单独配置一个core-site.xml到conf下都可以。</p>\n</blockquote>\n<p>下章节使用spark-sql or spark-shell来使用hive metastore。</p>\n","site":{"data":{}},"excerpt":"","more":"<h3 id=\"hive-metastore-install-step-by-step\"><a href=\"#hive-metastore-install-step-by-step\" class=\"headerlink\" title=\"hive metastore install step by step\"></a>hive metastore install step by step</h3><p>目标是安装好hive metastore stand alone的服务。<br>本文还使用了s3存储,使用的是minio,把warehouse.dir配置到了对象存储上。</p>\n<h4 id=\"安装mysql\"><a href=\"#安装mysql\" class=\"headerlink\" title=\"安装mysql\"></a>安装mysql</h4><p>因为存储要使用到mysql,所以直接使用了flink cdc 的一个demo,这个demo中有mysql,pg,es,kibana.<br><a href=\"https://ververica.github.io/flink-cdc-connectors/master/content/quickstart/mysql-postgres-tutorial.html\">https://ververica.github.io/flink-cdc-connectors/master/content/quickstart/mysql-postgres-tutorial.html</a></p>\n<figure class=\"highlight yaml\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br><span class=\"line\">11</span><br><span class=\"line\">12</span><br><span class=\"line\">13</span><br><span class=\"line\">14</span><br><span class=\"line\">15</span><br><span class=\"line\">16</span><br><span class=\"line\">17</span><br><span class=\"line\">18</span><br><span class=\"line\">19</span><br><span class=\"line\">20</span><br><span class=\"line\">21</span><br><span class=\"line\">22</span><br><span class=\"line\">23</span><br><span class=\"line\">24</span><br><span class=\"line\">25</span><br><span class=\"line\">26</span><br><span class=\"line\">27</span><br><span class=\"line\">28</span><br><span class=\"line\">29</span><br><span class=\"line\">30</span><br><span class=\"line\">31</span><br><span class=\"line\">32</span><br><span class=\"line\">33</span><br><span class=\"line\">34</span><br><span class=\"line\">35</span><br><span class=\"line\">36</span><br><span class=\"line\">37</span><br><span class=\"line\">38</span><br><span class=\"line\">39</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"attr\">version:</span> <span class=\"string\">'2.1'</span></span><br><span class=\"line\"><span class=\"attr\">services:</span></span><br><span class=\"line\"> <span class=\"attr\">postgres:</span></span><br><span class=\"line\"> <span class=\"attr\">image:</span> <span class=\"string\">debezium/example-postgres:1.1</span></span><br><span class=\"line\"> <span class=\"attr\">ports:</span></span><br><span class=\"line\"> <span class=\"bullet\">-</span> <span class=\"string\">"5432:5432"</span></span><br><span class=\"line\"> <span class=\"attr\">environment:</span></span><br><span class=\"line\"> <span class=\"bullet\">-</span> <span class=\"string\">POSTGRES_DB=postgres</span></span><br><span class=\"line\"> <span class=\"bullet\">-</span> <span class=\"string\">POSTGRES_USER=postgres</span></span><br><span class=\"line\"> <span class=\"bullet\">-</span> <span class=\"string\">POSTGRES_PASSWORD=postgres</span></span><br><span class=\"line\"> <span class=\"attr\">mysql:</span></span><br><span class=\"line\"> <span class=\"attr\">image:</span> <span class=\"string\">debezium/example-mysql:1.1</span></span><br><span class=\"line\"> <span class=\"attr\">ports:</span></span><br><span class=\"line\"> <span class=\"bullet\">-</span> <span class=\"string\">"3306:3306"</span></span><br><span class=\"line\"> <span class=\"attr\">environment:</span></span><br><span class=\"line\"> <span class=\"bullet\">-</span> <span class=\"string\">MYSQL_ROOT_PASSWORD=123456</span></span><br><span class=\"line\"> <span class=\"bullet\">-</span> <span class=\"string\">MYSQL_USER=mysqluser</span></span><br><span class=\"line\"> <span class=\"bullet\">-</span> <span class=\"string\">MYSQL_PASSWORD=mysqlpw</span></span><br><span class=\"line\"> <span class=\"attr\">elasticsearch:</span></span><br><span class=\"line\"> <span class=\"attr\">image:</span> <span class=\"string\">elastic/elasticsearch:7.6.0</span></span><br><span class=\"line\"> <span class=\"attr\">environment:</span></span><br><span class=\"line\"> <span class=\"bullet\">-</span> <span class=\"string\">cluster.name=docker-cluster</span></span><br><span class=\"line\"> <span class=\"bullet\">-</span> <span class=\"string\">bootstrap.memory_lock=true</span></span><br><span class=\"line\"> <span class=\"bullet\">-</span> <span class=\"string\">"ES_JAVA_OPTS=-Xms512m -Xmx512m"</span></span><br><span class=\"line\"> <span class=\"bullet\">-</span> <span class=\"string\">discovery.type=single-node</span></span><br><span class=\"line\"> <span class=\"attr\">ports:</span></span><br><span class=\"line\"> <span class=\"bullet\">-</span> <span class=\"string\">"9200:9200"</span></span><br><span class=\"line\"> <span class=\"bullet\">-</span> <span class=\"string\">"9300:9300"</span></span><br><span class=\"line\"> <span class=\"attr\">ulimits:</span></span><br><span class=\"line\"> <span class=\"attr\">memlock:</span></span><br><span class=\"line\"> <span class=\"attr\">soft:</span> <span class=\"number\">-1</span></span><br><span class=\"line\"> <span class=\"attr\">hard:</span> <span class=\"number\">-1</span></span><br><span class=\"line\"> <span class=\"attr\">nofile:</span></span><br><span class=\"line\"> <span class=\"attr\">soft:</span> <span class=\"number\">65536</span></span><br><span class=\"line\"> <span class=\"attr\">hard:</span> <span class=\"number\">65536</span></span><br><span class=\"line\"> <span class=\"attr\">kibana:</span></span><br><span class=\"line\"> <span class=\"attr\">image:</span> <span class=\"string\">elastic/kibana:7.6.0</span></span><br><span class=\"line\"> <span class=\"attr\">ports:</span></span><br><span class=\"line\"> <span class=\"bullet\">-</span> <span class=\"string\">"5601:5601"</span></span><br></pre></td></tr></table></figure>\n<p>以上内容存储为docker-compose.yml即可,如果不需要pg,es,kibana可以去掉.<br>docker compose up -d 即可启动mysql.</p>\n<h4 id=\"mysql-添加用户hive\"><a href=\"#mysql-添加用户hive\" class=\"headerlink\" title=\"mysql 添加用户hive\"></a>mysql 添加用户hive</h4><figure class=\"highlight bash\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">docker-compose <span class=\"built_in\">exec</span> mysql mysql -uroot -p123456</span><br><span class=\"line\">grant all privileges on *.* to hive@<span class=\"string\">'%'</span> identified by <span class=\"string\">'hive'</span>;</span><br><span class=\"line\">flush privileges ;</span><br></pre></td></tr></table></figure>\n<h4 id=\"下载安装-hive-metastore\"><a href=\"#下载安装-hive-metastore\" class=\"headerlink\" title=\"下载安装 hive metastore\"></a>下载安装 hive metastore</h4><figure class=\"highlight bash\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br><span class=\"line\">11</span><br><span class=\"line\">12</span><br><span class=\"line\">13</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"built_in\">cd</span> ~/app</span><br><span class=\"line\">wget https://repo1.maven.org/maven2/org/apache/hive/hive-standalone-metastore/3.1.2/hive-standalone-metastore-3.1.2-bin.tar.gz</span><br><span class=\"line\">tar -xvf hive-standalone-metastore-3.1.2-bin.tar.gz</span><br><span class=\"line\"><span class=\"built_in\">rm</span> -f hive-standalone-metastore-3.1.2-bin.tar.gz</span><br><span class=\"line\"><span class=\"built_in\">mv</span> apache-hive-metastore-3.1.2-bin metastore</span><br><span class=\"line\"><span class=\"built_in\">cd</span> metastore/lib</span><br><span class=\"line\">wget https://repo1.maven.org/maven2/mysql/mysql-connector-java/5.1.49/mysql-connector-java-5.1.49.jar </span><br><span class=\"line\"><span class=\"comment\">####metastore 依赖hadoop </span></span><br><span class=\"line\"><span class=\"built_in\">cd</span> ~/app/</span><br><span class=\"line\">wget https://dlcdn.apache.org/hadoop/common/hadoop-3.3.1/hadoop-3.3.1.tar.gz </span><br><span class=\"line\">tar -xvf hadoop-3.3.1.tar.gz</span><br><span class=\"line\"><span class=\"built_in\">rm</span> hadoop-3.3.1.tar.gz</span><br><span class=\"line\"><span class=\"built_in\">ln</span> -s ./hadoop-3.3.1 hadoop</span><br></pre></td></tr></table></figure>\n<h4 id=\"更新hive-metastore的配置\"><a href=\"#更新hive-metastore的配置\" class=\"headerlink\" title=\"更新hive metastore的配置\"></a>更新hive metastore的配置</h4><p> metastore-site.xml, mysql 连接信息</p>\n<figure class=\"highlight xml\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br><span class=\"line\">11</span><br><span class=\"line\">12</span><br><span class=\"line\">13</span><br><span class=\"line\">14</span><br><span class=\"line\">15</span><br><span class=\"line\">16</span><br><span class=\"line\">17</span><br><span class=\"line\">18</span><br><span class=\"line\">19</span><br><span class=\"line\">20</span><br><span class=\"line\">21</span><br><span class=\"line\">22</span><br><span class=\"line\">23</span><br><span class=\"line\">24</span><br><span class=\"line\">25</span><br><span class=\"line\">26</span><br><span class=\"line\">27</span><br><span class=\"line\">28</span><br><span class=\"line\">29</span><br><span class=\"line\">30</span><br><span class=\"line\">31</span><br><span class=\"line\">32</span><br><span class=\"line\">33</span><br><span class=\"line\">34</span><br><span class=\"line\">35</span><br><span class=\"line\">36</span><br><span class=\"line\">37</span><br><span class=\"line\">38</span><br><span class=\"line\">39</span><br><span class=\"line\">40</span><br><span class=\"line\">41</span><br><span class=\"line\">42</span><br><span class=\"line\">43</span><br><span class=\"line\">44</span><br><span class=\"line\">45</span><br><span class=\"line\">46</span><br><span class=\"line\">47</span><br><span class=\"line\">48</span><br><span class=\"line\">49</span><br><span class=\"line\">50</span><br><span class=\"line\">51</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"tag\"><<span class=\"name\">property</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">name</span>></span>javax.jdo.option.ConnectionDriverName<span class=\"tag\"></<span class=\"name\">name</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">value</span>></span>com.mysql.jdbc.Driver<span class=\"tag\"></<span class=\"name\">value</span>></span></span><br><span class=\"line\"><span class=\"tag\"></<span class=\"name\">property</span>></span></span><br><span class=\"line\"><span class=\"tag\"><<span class=\"name\">property</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">name</span>></span>javax.jdo.option.ConnectionURL<span class=\"tag\"></<span class=\"name\">name</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">value</span>></span>jdbc:mysql://localhost:3306/hive?createDatabaseIfNotExist=true<span class=\"symbol\">&amp;</span>useSSL=false<span class=\"tag\"></<span class=\"name\">value</span>></span></span><br><span class=\"line\"><span class=\"tag\"></<span class=\"name\">property</span>></span></span><br><span class=\"line\"><span class=\"tag\"><<span class=\"name\">property</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">name</span>></span>javax.jdo.option.ConnectionUserName<span class=\"tag\"></<span class=\"name\">name</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">value</span>></span>hive<span class=\"tag\"></<span class=\"name\">value</span>></span></span><br><span class=\"line\"><span class=\"tag\"></<span class=\"name\">property</span>></span></span><br><span class=\"line\"><span class=\"tag\"><<span class=\"name\">property</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">name</span>></span>javax.jdo.option.ConnectionPassword<span class=\"tag\"></<span class=\"name\">name</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">value</span>></span>hive<span class=\"tag\"></<span class=\"name\">value</span>></span></span><br><span class=\"line\"><span class=\"tag\"></<span class=\"name\">property</span>></span></span><br><span class=\"line\"></span><br><span class=\"line\"><span class=\"tag\"><<span class=\"name\">property</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">name</span>></span>hive.metastore.warehouse.dir<span class=\"tag\"></<span class=\"name\">name</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">value</span>></span>s3a://hive/warehouse<span class=\"tag\"></<span class=\"name\">value</span>></span></span><br><span class=\"line\"><span class=\"tag\"></<span class=\"name\">property</span>></span></span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">property</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">name</span>></span>fs.defaultFS<span class=\"tag\"></<span class=\"name\">name</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">value</span>></span>s3a://<span class=\"tag\"></<span class=\"name\">value</span>></span></span><br><span class=\"line\"> <span class=\"tag\"></<span class=\"name\">property</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">property</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">name</span>></span>fs.s3a.access.key<span class=\"tag\"></<span class=\"name\">name</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">value</span>></span>minio<span class=\"tag\"></<span class=\"name\">value</span>></span></span><br><span class=\"line\"> <span class=\"tag\"></<span class=\"name\">property</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">property</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">name</span>></span>fs.s3a.secret.key<span class=\"tag\"></<span class=\"name\">name</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">value</span>></span>minio123<span class=\"tag\"></<span class=\"name\">value</span>></span></span><br><span class=\"line\"> <span class=\"tag\"></<span class=\"name\">property</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">property</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">name</span>></span>fs.s3a.connection.ssl.enabled<span class=\"tag\"></<span class=\"name\">name</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">value</span>></span>false<span class=\"tag\"></<span class=\"name\">value</span>></span></span><br><span class=\"line\"> <span class=\"tag\"></<span class=\"name\">property</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">property</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">name</span>></span>fs.s3a.path.style.access<span class=\"tag\"></<span class=\"name\">name</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">value</span>></span>true<span class=\"tag\"></<span class=\"name\">value</span>></span></span><br><span class=\"line\"> <span class=\"tag\"></<span class=\"name\">property</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">property</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">name</span>></span>fs.s3a.endpoint<span class=\"tag\"></<span class=\"name\">name</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">value</span>></span>http://localhost:9000<span class=\"tag\"></<span class=\"name\">value</span>></span></span><br><span class=\"line\"> <span class=\"tag\"></<span class=\"name\">property</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">property</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">name</span>></span>fs.s3a.impl<span class=\"tag\"></<span class=\"name\">name</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">value</span>></span>org.apache.hadoop.fs.s3a.S3AFileSystem<span class=\"tag\"></<span class=\"name\">value</span>></span></span><br><span class=\"line\"> <span class=\"tag\"></<span class=\"name\">property</span>></span></span><br><span class=\"line\"></span><br></pre></td></tr></table></figure>\n<h4 id=\"初始化元数据\"><a href=\"#初始化元数据\" class=\"headerlink\" title=\"初始化元数据\"></a>初始化元数据</h4><figure class=\"highlight bash\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"built_in\">rm</span> -f ~/app/metastore/lib/guava-19.0.jar</span><br><span class=\"line\">bin/schematool -dbType mysql -initSchema</span><br></pre></td></tr></table></figure>\n<blockquote>\n<p>Exception in thread “main” java.lang.NoSuchMethodError: ‘void com.google.common.base.Preconditions.checkArgument(boolean, java.lang.String, java.lang.Object)’<br>因为guava jar包的冲突,需要提前处理一下。</p>\n</blockquote>\n<p>然后检查mysql数据库,发现有一个新建的hive db,里面有多张相关的表。</p>\n<h4 id=\"minio的安装使用\"><a href=\"#minio的安装使用\" class=\"headerlink\" title=\"minio的安装使用\"></a>minio的安装使用</h4><p>minio是一个兼容s3的开源存储服务,非常适合在本地测试对象存储。</p>\n<figure class=\"highlight yml\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br><span class=\"line\">11</span><br><span class=\"line\">12</span><br><span class=\"line\">13</span><br><span class=\"line\">14</span><br><span class=\"line\">15</span><br><span class=\"line\">16</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"attr\">version:</span> <span class=\"string\">'3'</span></span><br><span class=\"line\"><span class=\"attr\">services:</span></span><br><span class=\"line\"> <span class=\"attr\">minio:</span></span><br><span class=\"line\"> <span class=\"attr\">image:</span> <span class=\"string\">minio/minio</span></span><br><span class=\"line\"> <span class=\"attr\">hostname:</span> <span class=\"string\">minio</span></span><br><span class=\"line\"> <span class=\"attr\">ports:</span></span><br><span class=\"line\"> <span class=\"bullet\">-</span> <span class=\"number\">9000</span><span class=\"string\">:9000</span> <span class=\"comment\">#api 端口</span></span><br><span class=\"line\"> <span class=\"bullet\">-</span> <span class=\"number\">9001</span><span class=\"string\">:9001</span> <span class=\"comment\">#控制台端口</span></span><br><span class=\"line\"> <span class=\"attr\">environment:</span></span><br><span class=\"line\"> <span class=\"bullet\">-</span> <span class=\"string\">MINIO_ROOT_USER=minio</span></span><br><span class=\"line\"> <span class=\"bullet\">-</span> <span class=\"string\">MINIO_ROOT_PASSWORD=minio123</span></span><br><span class=\"line\"> <span class=\"attr\">volumes:</span></span><br><span class=\"line\"> <span class=\"bullet\">-</span> <span class=\"string\">data-1:/data</span></span><br><span class=\"line\"> <span class=\"attr\">command:</span> <span class=\"string\">server</span> <span class=\"string\">--console-address</span> <span class=\"string\">":9001"</span> <span class=\"string\">/data</span></span><br><span class=\"line\"><span class=\"attr\">volumes:</span></span><br><span class=\"line\"> <span class=\"string\">data-1:%</span></span><br></pre></td></tr></table></figure>\n<p>存储为docker-compose.yml<br>docker compose up -d即可启动minio服务。<br>可以在浏览器中localhost:9001使用前面配置的用户名密码登录即可查看。</p>\n<h4 id=\"开启服务进程\"><a href=\"#开启服务进程\" class=\"headerlink\" title=\"开启服务进程\"></a>开启服务进程</h4><figure class=\"highlight bash\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"built_in\">cp</span> conf/metastore-log4j2.properties conf/log4j2.properties</span><br><span class=\"line\">bin/start-metastore</span><br></pre></td></tr></table></figure>\n<blockquote>\n<p>MetaException(message:java.lang.ClassNotFoundException: Class org.apache.hadoop.fs.s3a.S3AFileSystem not found)<br>因为配置了<br><name>hive.metastore.warehouse.dir</name><value>s3a://hive/warehouse</value><br>复制aws相关的jar包到lib/目录下。<br>cd ~/app/hadoop<br>find . -iname “<em>aws</em>.jar” |xargs -I _ cp _ ~/app/metastore/lib/<br>接着重启服务进程遇到如下错误:<br>Caused by: MetaException(message:Got exception: org.apache.hadoop.fs.s3a.AWSClientIOException initializing on s3a://hive/warehouse: com.amazonaws.SdkClientException: Unable to find a region via the region provider chain. Must provide an explicit region in the builder or setup environment to supply a region.: Unable to find a region via the region provider chain. Must provide an explicit region in the builder or setup environment to supply a region.)<br>把minio相关的访问信息配置到metastore-site.xml 或单独配置一个core-site.xml到conf下都可以。</p>\n</blockquote>\n<p>下章节使用spark-sql or spark-shell来使用hive metastore。</p>\n"},{"title":"spark s3 测试","date":"2022-04-22T16:00:00.000Z","toc":true,"_content":"spark的本地安装与测试\n环境:mac m1 \n安装当前最新版的spark3.2.1并进行简单的测试,整合hive metastore。\nhive metastore的安装请参考前面一文章即可。\n#### 下载安装jdk8\nhttps://www.oracle.com/java/technologies/downloads/#java8-mac\nuser: hnyaoxh@126.com\npwd: xxxxxx\n下载后一步一步安装即可。\n因为之前安装了jdk18,所以现在有两个jdk版本。需要在.zshrc中指定JAVA_HOME\n或者在spark相关的sh文件中指定也可以。\n```bash\nexport PATH=\"/Users/student2028/app/spark/bin:$PATH\"\nJAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.8.0_331.jdk/Contents/Home\nPATH=$JAVA_HOME/bin:$PATH:.\nCLASSPATH=$JAVA_HOME/lib/tools.jar:$JAVA_HOME/lib/dt.jar:.\nexport JAVA_HOME\nexport PATH\nexport CLASSPATH\n```\n#### 下载安装spark\n如果下载without hadoop版本因为缺少各种jar包,所以需要自己补充进来。\n同样使用prebuilt hadoop的版本,一定要两者兼容,否则也会报相关错误。\n当前最新版本spark3.2兼容 hadoop3.3.1。如果版本不兼容,常报错误有\n> Exception in thread \"main\" java.lang.NoSuchFieldError: JAVA_9\n```bash\nwget https://dlcdn.apache.org/spark/spark-3.2.1/spark-3.2.1-bin-hadoop3.2.tgz\ntar xvzf spark-3.2.1-bin-hadoop3.2.tgz\nrm spark-3.2.1-bin-hadoop3.2.tgz\nln -s ./spark-3.2.1-bin-hadoop3.2 spark\n##copy aws related jars to lib 注意此处的hadoop 需要是3.3版本\ncd ~/app/hadoop\nfind . -iname \"*aws*.jar\" |xargs -I _ cp _ ~/app/spark/jars\n```\n#### 测试读写s3的数据\n```bash\nspark-shell \\\n--conf spark.hadoop.fs.s3a.bucket.all.committer.magic.enabled=true \\\n--conf spark.hadoop.fs.s3a.access.key=minio \\\n--conf spark.hadoop.fs.s3a.secret.key=minio123 \\\n--conf spark.hadoop.fs.s3a.endpoint=localhost:9000 \\\n--conf spark.hadoop.fs.s3a.path.style.access=true \\\n--conf spark.hadoop.fs.s3a.connection.ssl.enabled=false \\\n--conf spark.hadoop.fs.s3a.impl=org.apache.hadoop.fs.s3a.S3AFileSystem \\\n--conf spark.hadoop.fs.s3a.bucket.all.committer.magic.enabled=true \n\nval df=Seq((1,\"stu1\",\"M\",33),(2,\"stu2\",\"F\",30)).toDF(\"id\",\"name\",\"gender\",\"age\")\ndf.write.format(\"json\").mode(\"overwrite\").save(\"s3a://test/student\");\n\n```\n> WARN AbstractS3ACommitterFactory: Using standard FileOutputCommitter to commit work. This is slow and potentially unsafe.\n spark3.2开始提供了更好用的提交协议 ,请参考:\n https://spot.io/blog/improve-apache-spark-performance-with-the-s3-magic-committer/\n\n> java.lang.ClassNotFoundException: org.apache.spark.internal.io.cloud.PathOutputCommitProtocol\n\n解决方案:\n```bash\n1. cd ~/app/spark/jars/\n wget https://repo1.maven.org/maven2/org/apache/spark/spark-hadoop-cloud_2.12/3.2.1/spark-hadoop-cloud_2.12-3.2.1.jar\n\n2. spark-shell --packages org.apache.spark:spark-hadoop-cloud_2.12:3.2.1\n\n```\n> Class org.apache.hadoop.fs.s3a.auth.IAMInstanceCredentialsProvider not found\n这个问题的原因是spark预置的hadoop版本和hadoop-aws配置的版本不一致导致的,先看一下spark项目依赖的hadoop版本,\n修正hadoop-aws的版本即可,一个方法是在maven项目里面,先去掉hadoop-aws依赖,\n先看一下External Libraries 中搜一下hadoop即可以发现之前依赖的hadoop版本。\n\n#### 测试delta lake \n```bash\ncd ~/app/spark/jars\nwget https://repo1.maven.org/maven2/io/delta/delta-core_2.12/1.2.0/delta-core_2.12-1.2.0.jar\nwget https://repo1.maven.org/maven2/io/delta/delta-storage/1.2.0/delta-storage-1.2.0.jar\n\nspark-shell \\\n--conf \"spark.sql.extensions=io.delta.sql.DeltaSparkSessionExtension\" \\\n--conf \"spark.sql.catalog.spark_catalog=org.apache.spark.sql.delta.catalog.DeltaCatalog\" \\\n--conf spark.hadoop.fs.s3a.bucket.all.committer.magic.enabled=true \\\n--conf spark.hadoop.fs.s3a.access.key=minio \\\n--conf spark.hadoop.fs.s3a.secret.key=minio123 \\\n--conf spark.hadoop.fs.s3a.endpoint=localhost:9000 \\\n--conf spark.hadoop.fs.s3a.path.style.access=true \\\n--conf spark.hadoop.fs.s3a.connection.ssl.enabled=false \\\n--conf spark.hadoop.fs.s3a.impl=org.apache.hadoop.fs.s3a.S3AFileSystem \\\n--conf spark.hadoop.fs.s3a.bucket.all.committer.magic.enabled=true \n\nspark.range(5, 10).write.format(\"delta\").mode(\"overwrite\").save(\"s3a://test/delta1\")\nspark.read.format(\"delta\").load(\"s3a://test/delta1\").show(20)\n配合hivemetastore 创建表\nspark-sql \\\n--conf \"spark.sql.warehouse.dir=s3a://hive/warehouse\" \\\n--conf \"spark.sql.extensions=io.delta.sql.DeltaSparkSessionExtension\" \\\n--conf \"spark.sql.catalog.spark_catalog=org.apache.spark.sql.delta.catalog.DeltaCatalog\" \\\n--conf spark.hadoop.fs.s3a.access.key=minio \\\n--conf spark.hadoop.fs.s3a.secret.key=minio123 \\\n--conf spark.hadoop.fs.s3a.endpoint=localhost:9000 \\\n--conf spark.hadoop.fs.s3a.path.style.access=true \\\n--conf spark.hadoop.fs.s3a.connection.ssl.enabled=false \\\n--conf spark.hadoop.fs.s3a.impl=org.apache.hadoop.fs.s3a.S3AFileSystem \\\n--conf spark.hadoop.fs.s3a.bucket.all.committer.magic.enabled=true \\\n--conf \"spark.hadoop.hive.metastore.uris=thrift://localhost:9083\" \n```\n```sql\ncreate database if not exists test;\nuse test;\ndrop table test2;\ncreate table test2 (id int, name string) using delta;\ninsert into test2 values(1,'1');\ninsert into test2 values(2,'22');\nselect * from test2;\n```\n\n#### spark java project demo\n```java\n\nimport org.apache.spark.SparkConf;\nimport org.apache.spark.sql.Dataset;\nimport org.apache.spark.sql.Row;\nimport org.apache.spark.sql.SparkSession;\nimport org.apache.spark.sql.functions;\nimport java.util.Random;\npublic class TestDelta {\n public static void main(String[] args) {\n\n SparkConf sparkConf = new SparkConf();\n sparkConf.set(\"spark.sql.warehouse.dir\",\"s3a://hive/warehouse\");\n sparkConf.set(\"spark.hadoop.fs.s3a.bucket.all.committer.magic.enabled\",\"true\");\n sparkConf.set(\"spark.sql.catalogImplementation\",\"hive\");//enabled hive support\n sparkConf.set(\"spark.sql.extensions\",\"io.delta.sql.DeltaSparkSessionExtension\");\n sparkConf.set(\"spark.sql.catalog.spark_catalog\",\"org.apache.spark.sql.delta.catalog.DeltaCatalog\");\n sparkConf.set(\"spark.hadoop.hive.metastore.uris\",\"thrift://localhost:9083\");\n sparkConf.set(\"spark.hadoop.hive.metastore.warehouse.dir\",\"s3a://hive/warehouse\");\n\n sparkConf.set(\"fs.s3a.access.key\", \"minio\");\n sparkConf.set(\"fs.s3a.secret.key\", \"minio123\");\n sparkConf.set(\"fs.s3a.endpoint\",\"localhost:9000\");\n sparkConf.set(\"fs.s3a.path.style.access\", \"true\");\n sparkConf.set(\"fs.s3a.connection.ssl.enabled\", \"false\");\n sparkConf.set(\"fs.s3a.impl\", \"org.apache.hadoop.fs.s3a.S3AFileSystem\");\n\n SparkSession spark = SparkSession.builder().config(sparkConf).master(\"local\").getOrCreate();\n// spark.sparkContext().setLogLevel(\"warn\");\n// spark.sql(\"create database test\");\n// spark.sql(\"use test\");\n// spark.sql(\"drop table if exists test2\");\n// spark.sql(\"create table if not exists test2(id int,name string)\");\n// spark.sql(\"insert into test2 values(1,'1')\");\n spark.sql(\"use test\");\n spark.sql(\"show tables\").show();\n spark.sql(\"select * from test.test2\").show();\n spark.stop();\n }\n\n}\n\n```\n```xml\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n <modelVersion>4.0.0</modelVersion>\n\n <groupId>org.examples</groupId>\n <artifactId>sparktest</artifactId>\n <version>1.0-SNAPSHOT</version>\n\n <properties>\n <maven.compiler.source>8</maven.compiler.source>\n <maven.compiler.target>8</maven.compiler.target>\n </properties>\n\n <dependencies>\n <dependency>\n <groupId>io.delta</groupId>\n <artifactId>delta-core_2.12</artifactId>\n <version>1.2.0</version>\n </dependency>\n\n <dependency>\n <groupId>org.apache.spark</groupId>\n <artifactId>spark-hadoop-cloud_2.12</artifactId>\n <version>3.2.1</version>\n </dependency>\n\n <dependency>\n <groupId>org.apache.hadoop</groupId>\n <artifactId>hadoop-aws</artifactId>\n <version>3.3.1</version>\n </dependency>\n\n <dependency>\n <groupId>org.apache.spark</groupId>\n <artifactId>spark-hive_2.12</artifactId>\n <version>3.2.1</version>\n </dependency>\n\n <dependency>\n <groupId>org.apache.spark</groupId>\n <artifactId>spark-sql_2.12</artifactId>\n <version>3.2.1</version>\n </dependency>\n </dependencies>\n</project>\n```","source":"_posts/spark-minio.md","raw":"---\ntitle: spark s3 测试\ndate: 2022-04-23\ntoc: true #是否显示文章目录\ncategories: \"Spark\" #分类\ntags: #标签\n - 大数据\n - Spark\n---\nspark的本地安装与测试\n环境:mac m1 \n安装当前最新版的spark3.2.1并进行简单的测试,整合hive metastore。\nhive metastore的安装请参考前面一文章即可。\n#### 下载安装jdk8\nhttps://www.oracle.com/java/technologies/downloads/#java8-mac\nuser: hnyaoxh@126.com\npwd: xxxxxx\n下载后一步一步安装即可。\n因为之前安装了jdk18,所以现在有两个jdk版本。需要在.zshrc中指定JAVA_HOME\n或者在spark相关的sh文件中指定也可以。\n```bash\nexport PATH=\"/Users/student2028/app/spark/bin:$PATH\"\nJAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.8.0_331.jdk/Contents/Home\nPATH=$JAVA_HOME/bin:$PATH:.\nCLASSPATH=$JAVA_HOME/lib/tools.jar:$JAVA_HOME/lib/dt.jar:.\nexport JAVA_HOME\nexport PATH\nexport CLASSPATH\n```\n#### 下载安装spark\n如果下载without hadoop版本因为缺少各种jar包,所以需要自己补充进来。\n同样使用prebuilt hadoop的版本,一定要两者兼容,否则也会报相关错误。\n当前最新版本spark3.2兼容 hadoop3.3.1。如果版本不兼容,常报错误有\n> Exception in thread \"main\" java.lang.NoSuchFieldError: JAVA_9\n```bash\nwget https://dlcdn.apache.org/spark/spark-3.2.1/spark-3.2.1-bin-hadoop3.2.tgz\ntar xvzf spark-3.2.1-bin-hadoop3.2.tgz\nrm spark-3.2.1-bin-hadoop3.2.tgz\nln -s ./spark-3.2.1-bin-hadoop3.2 spark\n##copy aws related jars to lib 注意此处的hadoop 需要是3.3版本\ncd ~/app/hadoop\nfind . -iname \"*aws*.jar\" |xargs -I _ cp _ ~/app/spark/jars\n```\n#### 测试读写s3的数据\n```bash\nspark-shell \\\n--conf spark.hadoop.fs.s3a.bucket.all.committer.magic.enabled=true \\\n--conf spark.hadoop.fs.s3a.access.key=minio \\\n--conf spark.hadoop.fs.s3a.secret.key=minio123 \\\n--conf spark.hadoop.fs.s3a.endpoint=localhost:9000 \\\n--conf spark.hadoop.fs.s3a.path.style.access=true \\\n--conf spark.hadoop.fs.s3a.connection.ssl.enabled=false \\\n--conf spark.hadoop.fs.s3a.impl=org.apache.hadoop.fs.s3a.S3AFileSystem \\\n--conf spark.hadoop.fs.s3a.bucket.all.committer.magic.enabled=true \n\nval df=Seq((1,\"stu1\",\"M\",33),(2,\"stu2\",\"F\",30)).toDF(\"id\",\"name\",\"gender\",\"age\")\ndf.write.format(\"json\").mode(\"overwrite\").save(\"s3a://test/student\");\n\n```\n> WARN AbstractS3ACommitterFactory: Using standard FileOutputCommitter to commit work. This is slow and potentially unsafe.\n spark3.2开始提供了更好用的提交协议 ,请参考:\n https://spot.io/blog/improve-apache-spark-performance-with-the-s3-magic-committer/\n\n> java.lang.ClassNotFoundException: org.apache.spark.internal.io.cloud.PathOutputCommitProtocol\n\n解决方案:\n```bash\n1. cd ~/app/spark/jars/\n wget https://repo1.maven.org/maven2/org/apache/spark/spark-hadoop-cloud_2.12/3.2.1/spark-hadoop-cloud_2.12-3.2.1.jar\n\n2. spark-shell --packages org.apache.spark:spark-hadoop-cloud_2.12:3.2.1\n\n```\n> Class org.apache.hadoop.fs.s3a.auth.IAMInstanceCredentialsProvider not found\n这个问题的原因是spark预置的hadoop版本和hadoop-aws配置的版本不一致导致的,先看一下spark项目依赖的hadoop版本,\n修正hadoop-aws的版本即可,一个方法是在maven项目里面,先去掉hadoop-aws依赖,\n先看一下External Libraries 中搜一下hadoop即可以发现之前依赖的hadoop版本。\n\n#### 测试delta lake \n```bash\ncd ~/app/spark/jars\nwget https://repo1.maven.org/maven2/io/delta/delta-core_2.12/1.2.0/delta-core_2.12-1.2.0.jar\nwget https://repo1.maven.org/maven2/io/delta/delta-storage/1.2.0/delta-storage-1.2.0.jar\n\nspark-shell \\\n--conf \"spark.sql.extensions=io.delta.sql.DeltaSparkSessionExtension\" \\\n--conf \"spark.sql.catalog.spark_catalog=org.apache.spark.sql.delta.catalog.DeltaCatalog\" \\\n--conf spark.hadoop.fs.s3a.bucket.all.committer.magic.enabled=true \\\n--conf spark.hadoop.fs.s3a.access.key=minio \\\n--conf spark.hadoop.fs.s3a.secret.key=minio123 \\\n--conf spark.hadoop.fs.s3a.endpoint=localhost:9000 \\\n--conf spark.hadoop.fs.s3a.path.style.access=true \\\n--conf spark.hadoop.fs.s3a.connection.ssl.enabled=false \\\n--conf spark.hadoop.fs.s3a.impl=org.apache.hadoop.fs.s3a.S3AFileSystem \\\n--conf spark.hadoop.fs.s3a.bucket.all.committer.magic.enabled=true \n\nspark.range(5, 10).write.format(\"delta\").mode(\"overwrite\").save(\"s3a://test/delta1\")\nspark.read.format(\"delta\").load(\"s3a://test/delta1\").show(20)\n配合hivemetastore 创建表\nspark-sql \\\n--conf \"spark.sql.warehouse.dir=s3a://hive/warehouse\" \\\n--conf \"spark.sql.extensions=io.delta.sql.DeltaSparkSessionExtension\" \\\n--conf \"spark.sql.catalog.spark_catalog=org.apache.spark.sql.delta.catalog.DeltaCatalog\" \\\n--conf spark.hadoop.fs.s3a.access.key=minio \\\n--conf spark.hadoop.fs.s3a.secret.key=minio123 \\\n--conf spark.hadoop.fs.s3a.endpoint=localhost:9000 \\\n--conf spark.hadoop.fs.s3a.path.style.access=true \\\n--conf spark.hadoop.fs.s3a.connection.ssl.enabled=false \\\n--conf spark.hadoop.fs.s3a.impl=org.apache.hadoop.fs.s3a.S3AFileSystem \\\n--conf spark.hadoop.fs.s3a.bucket.all.committer.magic.enabled=true \\\n--conf \"spark.hadoop.hive.metastore.uris=thrift://localhost:9083\" \n```\n```sql\ncreate database if not exists test;\nuse test;\ndrop table test2;\ncreate table test2 (id int, name string) using delta;\ninsert into test2 values(1,'1');\ninsert into test2 values(2,'22');\nselect * from test2;\n```\n\n#### spark java project demo\n```java\n\nimport org.apache.spark.SparkConf;\nimport org.apache.spark.sql.Dataset;\nimport org.apache.spark.sql.Row;\nimport org.apache.spark.sql.SparkSession;\nimport org.apache.spark.sql.functions;\nimport java.util.Random;\npublic class TestDelta {\n public static void main(String[] args) {\n\n SparkConf sparkConf = new SparkConf();\n sparkConf.set(\"spark.sql.warehouse.dir\",\"s3a://hive/warehouse\");\n sparkConf.set(\"spark.hadoop.fs.s3a.bucket.all.committer.magic.enabled\",\"true\");\n sparkConf.set(\"spark.sql.catalogImplementation\",\"hive\");//enabled hive support\n sparkConf.set(\"spark.sql.extensions\",\"io.delta.sql.DeltaSparkSessionExtension\");\n sparkConf.set(\"spark.sql.catalog.spark_catalog\",\"org.apache.spark.sql.delta.catalog.DeltaCatalog\");\n sparkConf.set(\"spark.hadoop.hive.metastore.uris\",\"thrift://localhost:9083\");\n sparkConf.set(\"spark.hadoop.hive.metastore.warehouse.dir\",\"s3a://hive/warehouse\");\n\n sparkConf.set(\"fs.s3a.access.key\", \"minio\");\n sparkConf.set(\"fs.s3a.secret.key\", \"minio123\");\n sparkConf.set(\"fs.s3a.endpoint\",\"localhost:9000\");\n sparkConf.set(\"fs.s3a.path.style.access\", \"true\");\n sparkConf.set(\"fs.s3a.connection.ssl.enabled\", \"false\");\n sparkConf.set(\"fs.s3a.impl\", \"org.apache.hadoop.fs.s3a.S3AFileSystem\");\n\n SparkSession spark = SparkSession.builder().config(sparkConf).master(\"local\").getOrCreate();\n// spark.sparkContext().setLogLevel(\"warn\");\n// spark.sql(\"create database test\");\n// spark.sql(\"use test\");\n// spark.sql(\"drop table if exists test2\");\n// spark.sql(\"create table if not exists test2(id int,name string)\");\n// spark.sql(\"insert into test2 values(1,'1')\");\n spark.sql(\"use test\");\n spark.sql(\"show tables\").show();\n spark.sql(\"select * from test.test2\").show();\n spark.stop();\n }\n\n}\n\n```\n```xml\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n <modelVersion>4.0.0</modelVersion>\n\n <groupId>org.examples</groupId>\n <artifactId>sparktest</artifactId>\n <version>1.0-SNAPSHOT</version>\n\n <properties>\n <maven.compiler.source>8</maven.compiler.source>\n <maven.compiler.target>8</maven.compiler.target>\n </properties>\n\n <dependencies>\n <dependency>\n <groupId>io.delta</groupId>\n <artifactId>delta-core_2.12</artifactId>\n <version>1.2.0</version>\n </dependency>\n\n <dependency>\n <groupId>org.apache.spark</groupId>\n <artifactId>spark-hadoop-cloud_2.12</artifactId>\n <version>3.2.1</version>\n </dependency>\n\n <dependency>\n <groupId>org.apache.hadoop</groupId>\n <artifactId>hadoop-aws</artifactId>\n <version>3.3.1</version>\n </dependency>\n\n <dependency>\n <groupId>org.apache.spark</groupId>\n <artifactId>spark-hive_2.12</artifactId>\n <version>3.2.1</version>\n </dependency>\n\n <dependency>\n <groupId>org.apache.spark</groupId>\n <artifactId>spark-sql_2.12</artifactId>\n <version>3.2.1</version>\n </dependency>\n </dependencies>\n</project>\n```","slug":"spark-minio","published":1,"updated":"2022-04-23T05:28:30.430Z","_id":"cl2bf01vv0001r74o013l5fgg","comments":1,"layout":"post","photos":[],"link":"","content":"<p>spark的本地安装与测试<br>环境:mac m1<br>安装当前最新版的spark3.2.1并进行简单的测试,整合hive metastore。<br>hive metastore的安装请参考前面一文章即可。</p>\n<h4 id=\"下载安装jdk8\"><a href=\"#下载安装jdk8\" class=\"headerlink\" title=\"下载安装jdk8\"></a>下载安装jdk8</h4><p><a href=\"https://www.oracle.com/java/technologies/downloads/#java8-mac\">https://www.oracle.com/java/technologies/downloads/#java8-mac</a><br>user: <a href=\"mailto:hnyaoxh@126.com\">hnyaoxh@126.com</a><br>pwd: xxxxxx<br>下载后一步一步安装即可。<br>因为之前安装了jdk18,所以现在有两个jdk版本。需要在.zshrc中指定JAVA_HOME<br>或者在spark相关的sh文件中指定也可以。</p>\n<figure class=\"highlight bash\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"built_in\">export</span> PATH=<span class=\"string\">"/Users/student2028/app/spark/bin:<span class=\"variable\">$PATH</span>"</span></span><br><span class=\"line\">JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.8.0_331.jdk/Contents/Home</span><br><span class=\"line\">PATH=<span class=\"variable\">$JAVA_HOME</span>/bin:<span class=\"variable\">$PATH</span>:.</span><br><span class=\"line\">CLASSPATH=<span class=\"variable\">$JAVA_HOME</span>/lib/tools.jar:<span class=\"variable\">$JAVA_HOME</span>/lib/dt.jar:.</span><br><span class=\"line\"><span class=\"built_in\">export</span> JAVA_HOME</span><br><span class=\"line\"><span class=\"built_in\">export</span> PATH</span><br><span class=\"line\"><span class=\"built_in\">export</span> CLASSPATH</span><br></pre></td></tr></table></figure>\n<h4 id=\"下载安装spark\"><a href=\"#下载安装spark\" class=\"headerlink\" title=\"下载安装spark\"></a>下载安装spark</h4><p>如果下载without hadoop版本因为缺少各种jar包,所以需要自己补充进来。<br>同样使用prebuilt hadoop的版本,一定要两者兼容,否则也会报相关错误。<br>当前最新版本spark3.2兼容 hadoop3.3.1。如果版本不兼容,常报错误有</p>\n<blockquote>\n<p>Exception in thread “main” java.lang.NoSuchFieldError: JAVA_9</p>\n</blockquote>\n<figure class=\"highlight bash\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">wget https://dlcdn.apache.org/spark/spark-3.2.1/spark-3.2.1-bin-hadoop3.2.tgz</span><br><span class=\"line\">tar xvzf spark-3.2.1-bin-hadoop3.2.tgz</span><br><span class=\"line\"><span class=\"built_in\">rm</span> spark-3.2.1-bin-hadoop3.2.tgz</span><br><span class=\"line\"><span class=\"built_in\">ln</span> -s ./spark-3.2.1-bin-hadoop3.2 spark</span><br><span class=\"line\"><span class=\"comment\">##copy aws related jars to lib 注意此处的hadoop 需要是3.3版本</span></span><br><span class=\"line\"><span class=\"built_in\">cd</span> ~/app/hadoop</span><br><span class=\"line\">find . -iname <span class=\"string\">"*aws*.jar"</span> |xargs -I _ <span class=\"built_in\">cp</span> _ ~/app/spark/jars</span><br></pre></td></tr></table></figure>\n<h4 id=\"测试读写s3的数据\"><a href=\"#测试读写s3的数据\" class=\"headerlink\" title=\"测试读写s3的数据\"></a>测试读写s3的数据</h4><figure class=\"highlight bash\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br><span class=\"line\">11</span><br><span class=\"line\">12</span><br><span class=\"line\">13</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">spark-shell \\</span><br><span class=\"line\">--conf spark.hadoop.fs.s3a.bucket.all.committer.magic.enabled=<span class=\"literal\">true</span> \\</span><br><span class=\"line\">--conf spark.hadoop.fs.s3a.access.key=minio \\</span><br><span class=\"line\">--conf spark.hadoop.fs.s3a.secret.key=minio123 \\</span><br><span class=\"line\">--conf spark.hadoop.fs.s3a.endpoint=localhost:9000 \\</span><br><span class=\"line\">--conf spark.hadoop.fs.s3a.path.style.access=<span class=\"literal\">true</span> \\</span><br><span class=\"line\">--conf spark.hadoop.fs.s3a.connection.ssl.enabled=<span class=\"literal\">false</span> \\</span><br><span class=\"line\">--conf spark.hadoop.fs.s3a.impl=org.apache.hadoop.fs.s3a.S3AFileSystem \\</span><br><span class=\"line\">--conf spark.hadoop.fs.s3a.bucket.all.committer.magic.enabled=<span class=\"literal\">true</span> </span><br><span class=\"line\"></span><br><span class=\"line\">val <span class=\"built_in\">df</span>=Seq((1,<span class=\"string\">"stu1"</span>,<span class=\"string\">"M"</span>,33),(2,<span class=\"string\">"stu2"</span>,<span class=\"string\">"F"</span>,30)).toDF(<span class=\"string\">"id"</span>,<span class=\"string\">"name"</span>,<span class=\"string\">"gender"</span>,<span class=\"string\">"age"</span>)</span><br><span class=\"line\">df.write.format(<span class=\"string\">"json"</span>).mode(<span class=\"string\">"overwrite"</span>).save(<span class=\"string\">"s3a://test/student"</span>);</span><br><span class=\"line\"></span><br></pre></td></tr></table></figure>\n<blockquote>\n<p>WARN AbstractS3ACommitterFactory: Using standard FileOutputCommitter to commit work. This is slow and potentially unsafe.<br> spark3.2开始提供了更好用的提交协议 ,请参考:<br> <a href=\"https://spot.io/blog/improve-apache-spark-performance-with-the-s3-magic-committer/\">https://spot.io/blog/improve-apache-spark-performance-with-the-s3-magic-committer/</a></p>\n</blockquote>\n<blockquote>\n<p>java.lang.ClassNotFoundException: org.apache.spark.internal.io.cloud.PathOutputCommitProtocol</p>\n</blockquote>\n<p>解决方案:</p>\n<figure class=\"highlight bash\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">1. <span class=\"built_in\">cd</span> ~/app/spark/jars/</span><br><span class=\"line\"> wget https://repo1.maven.org/maven2/org/apache/spark/spark-hadoop-cloud_2.12/3.2.1/spark-hadoop-cloud_2.12-3.2.1.jar</span><br><span class=\"line\"></span><br><span class=\"line\">2. spark-shell --packages org.apache.spark:spark-hadoop-cloud_2.12:3.2.1</span><br><span class=\"line\"></span><br></pre></td></tr></table></figure>\n<blockquote>\n<p>Class org.apache.hadoop.fs.s3a.auth.IAMInstanceCredentialsProvider not found<br>这个问题的原因是spark预置的hadoop版本和hadoop-aws配置的版本不一致导致的,先看一下spark项目依赖的hadoop版本,<br>修正hadoop-aws的版本即可,一个方法是在maven项目里面,先去掉hadoop-aws依赖,<br>先看一下External Libraries 中搜一下hadoop即可以发现之前依赖的hadoop版本。</p>\n</blockquote>\n<h4 id=\"测试delta-lake\"><a href=\"#测试delta-lake\" class=\"headerlink\" title=\"测试delta lake\"></a>测试delta lake</h4><figure class=\"highlight bash\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br><span class=\"line\">11</span><br><span class=\"line\">12</span><br><span class=\"line\">13</span><br><span class=\"line\">14</span><br><span class=\"line\">15</span><br><span class=\"line\">16</span><br><span class=\"line\">17</span><br><span class=\"line\">18</span><br><span class=\"line\">19</span><br><span class=\"line\">20</span><br><span class=\"line\">21</span><br><span class=\"line\">22</span><br><span class=\"line\">23</span><br><span class=\"line\">24</span><br><span class=\"line\">25</span><br><span class=\"line\">26</span><br><span class=\"line\">27</span><br><span class=\"line\">28</span><br><span class=\"line\">29</span><br><span class=\"line\">30</span><br><span class=\"line\">31</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"built_in\">cd</span> ~/app/spark/jars</span><br><span class=\"line\">wget https://repo1.maven.org/maven2/io/delta/delta-core_2.12/1.2.0/delta-core_2.12-1.2.0.jar</span><br><span class=\"line\">wget https://repo1.maven.org/maven2/io/delta/delta-storage/1.2.0/delta-storage-1.2.0.jar</span><br><span class=\"line\"></span><br><span class=\"line\">spark-shell \\</span><br><span class=\"line\">--conf <span class=\"string\">"spark.sql.extensions=io.delta.sql.DeltaSparkSessionExtension"</span> \\</span><br><span class=\"line\">--conf <span class=\"string\">"spark.sql.catalog.spark_catalog=org.apache.spark.sql.delta.catalog.DeltaCatalog"</span> \\</span><br><span class=\"line\">--conf spark.hadoop.fs.s3a.bucket.all.committer.magic.enabled=<span class=\"literal\">true</span> \\</span><br><span class=\"line\">--conf spark.hadoop.fs.s3a.access.key=minio \\</span><br><span class=\"line\">--conf spark.hadoop.fs.s3a.secret.key=minio123 \\</span><br><span class=\"line\">--conf spark.hadoop.fs.s3a.endpoint=localhost:9000 \\</span><br><span class=\"line\">--conf spark.hadoop.fs.s3a.path.style.access=<span class=\"literal\">true</span> \\</span><br><span class=\"line\">--conf spark.hadoop.fs.s3a.connection.ssl.enabled=<span class=\"literal\">false</span> \\</span><br><span class=\"line\">--conf spark.hadoop.fs.s3a.impl=org.apache.hadoop.fs.s3a.S3AFileSystem \\</span><br><span class=\"line\">--conf spark.hadoop.fs.s3a.bucket.all.committer.magic.enabled=<span class=\"literal\">true</span> </span><br><span class=\"line\"></span><br><span class=\"line\">spark.range(5, 10).write.format(<span class=\"string\">"delta"</span>).mode(<span class=\"string\">"overwrite"</span>).save(<span class=\"string\">"s3a://test/delta1"</span>)</span><br><span class=\"line\">spark.read.format(<span class=\"string\">"delta"</span>).load(<span class=\"string\">"s3a://test/delta1"</span>).show(20)</span><br><span class=\"line\">配合hivemetastore 创建表</span><br><span class=\"line\">spark-sql \\</span><br><span class=\"line\">--conf <span class=\"string\">"spark.sql.warehouse.dir=s3a://hive/warehouse"</span> \\</span><br><span class=\"line\">--conf <span class=\"string\">"spark.sql.extensions=io.delta.sql.DeltaSparkSessionExtension"</span> \\</span><br><span class=\"line\">--conf <span class=\"string\">"spark.sql.catalog.spark_catalog=org.apache.spark.sql.delta.catalog.DeltaCatalog"</span> \\</span><br><span class=\"line\">--conf spark.hadoop.fs.s3a.access.key=minio \\</span><br><span class=\"line\">--conf spark.hadoop.fs.s3a.secret.key=minio123 \\</span><br><span class=\"line\">--conf spark.hadoop.fs.s3a.endpoint=localhost:9000 \\</span><br><span class=\"line\">--conf spark.hadoop.fs.s3a.path.style.access=<span class=\"literal\">true</span> \\</span><br><span class=\"line\">--conf spark.hadoop.fs.s3a.connection.ssl.enabled=<span class=\"literal\">false</span> \\</span><br><span class=\"line\">--conf spark.hadoop.fs.s3a.impl=org.apache.hadoop.fs.s3a.S3AFileSystem \\</span><br><span class=\"line\">--conf spark.hadoop.fs.s3a.bucket.all.committer.magic.enabled=<span class=\"literal\">true</span> \\</span><br><span class=\"line\">--conf <span class=\"string\">"spark.hadoop.hive.metastore.uris=thrift://localhost:9083"</span> </span><br></pre></td></tr></table></figure>\n<figure class=\"highlight sql\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"keyword\">create</span> database if <span class=\"keyword\">not</span> <span class=\"keyword\">exists</span> test;</span><br><span class=\"line\">use test;</span><br><span class=\"line\"><span class=\"keyword\">drop</span> <span class=\"keyword\">table</span> test2;</span><br><span class=\"line\"><span class=\"keyword\">create</span> <span class=\"keyword\">table</span> test2 (id <span class=\"type\">int</span>, name string) <span class=\"keyword\">using</span> delta;</span><br><span class=\"line\"><span class=\"keyword\">insert</span> <span class=\"keyword\">into</span> test2 <span class=\"keyword\">values</span>(<span class=\"number\">1</span>,<span class=\"string\">'1'</span>);</span><br><span class=\"line\"><span class=\"keyword\">insert</span> <span class=\"keyword\">into</span> test2 <span class=\"keyword\">values</span>(<span class=\"number\">2</span>,<span class=\"string\">'22'</span>);</span><br><span class=\"line\"><span class=\"keyword\">select</span> <span class=\"operator\">*</span> <span class=\"keyword\">from</span> test2;</span><br></pre></td></tr></table></figure>\n\n<h4 id=\"spark-java-project-demo\"><a href=\"#spark-java-project-demo\" class=\"headerlink\" title=\"spark java project demo\"></a>spark java project demo</h4><figure class=\"highlight java\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br><span class=\"line\">11</span><br><span class=\"line\">12</span><br><span class=\"line\">13</span><br><span class=\"line\">14</span><br><span class=\"line\">15</span><br><span class=\"line\">16</span><br><span class=\"line\">17</span><br><span class=\"line\">18</span><br><span class=\"line\">19</span><br><span class=\"line\">20</span><br><span class=\"line\">21</span><br><span class=\"line\">22</span><br><span class=\"line\">23</span><br><span class=\"line\">24</span><br><span class=\"line\">25</span><br><span class=\"line\">26</span><br><span class=\"line\">27</span><br><span class=\"line\">28</span><br><span class=\"line\">29</span><br><span class=\"line\">30</span><br><span class=\"line\">31</span><br><span class=\"line\">32</span><br><span class=\"line\">33</span><br><span class=\"line\">34</span><br><span class=\"line\">35</span><br><span class=\"line\">36</span><br><span class=\"line\">37</span><br><span class=\"line\">38</span><br><span class=\"line\">39</span><br><span class=\"line\">40</span><br><span class=\"line\">41</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"></span><br><span class=\"line\"><span class=\"keyword\">import</span> org.apache.spark.SparkConf;</span><br><span class=\"line\"><span class=\"keyword\">import</span> org.apache.spark.sql.Dataset;</span><br><span class=\"line\"><span class=\"keyword\">import</span> org.apache.spark.sql.Row;</span><br><span class=\"line\"><span class=\"keyword\">import</span> org.apache.spark.sql.SparkSession;</span><br><span class=\"line\"><span class=\"keyword\">import</span> org.apache.spark.sql.functions;</span><br><span class=\"line\"><span class=\"keyword\">import</span> java.util.Random;</span><br><span class=\"line\"><span class=\"keyword\">public</span> <span class=\"keyword\">class</span> <span class=\"title class_\">TestDelta</span> {</span><br><span class=\"line\"> <span class=\"keyword\">public</span> <span class=\"keyword\">static</span> <span class=\"keyword\">void</span> <span class=\"title function_\">main</span><span class=\"params\">(String[] args)</span> {</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"type\">SparkConf</span> <span class=\"variable\">sparkConf</span> <span class=\"operator\">=</span> <span class=\"keyword\">new</span> <span class=\"title class_\">SparkConf</span>();</span><br><span class=\"line\"> sparkConf.set(<span class=\"string\">"spark.sql.warehouse.dir"</span>,<span class=\"string\">"s3a://hive/warehouse"</span>);</span><br><span class=\"line\"> sparkConf.set(<span class=\"string\">"spark.hadoop.fs.s3a.bucket.all.committer.magic.enabled"</span>,<span class=\"string\">"true"</span>);</span><br><span class=\"line\"> sparkConf.set(<span class=\"string\">"spark.sql.catalogImplementation"</span>,<span class=\"string\">"hive"</span>);<span class=\"comment\">//enabled hive support</span></span><br><span class=\"line\"> sparkConf.set(<span class=\"string\">"spark.sql.extensions"</span>,<span class=\"string\">"io.delta.sql.DeltaSparkSessionExtension"</span>);</span><br><span class=\"line\"> sparkConf.set(<span class=\"string\">"spark.sql.catalog.spark_catalog"</span>,<span class=\"string\">"org.apache.spark.sql.delta.catalog.DeltaCatalog"</span>);</span><br><span class=\"line\"> sparkConf.set(<span class=\"string\">"spark.hadoop.hive.metastore.uris"</span>,<span class=\"string\">"thrift://localhost:9083"</span>);</span><br><span class=\"line\"> sparkConf.set(<span class=\"string\">"spark.hadoop.hive.metastore.warehouse.dir"</span>,<span class=\"string\">"s3a://hive/warehouse"</span>);</span><br><span class=\"line\"></span><br><span class=\"line\"> sparkConf.set(<span class=\"string\">"fs.s3a.access.key"</span>, <span class=\"string\">"minio"</span>);</span><br><span class=\"line\"> sparkConf.set(<span class=\"string\">"fs.s3a.secret.key"</span>, <span class=\"string\">"minio123"</span>);</span><br><span class=\"line\"> sparkConf.set(<span class=\"string\">"fs.s3a.endpoint"</span>,<span class=\"string\">"localhost:9000"</span>);</span><br><span class=\"line\"> sparkConf.set(<span class=\"string\">"fs.s3a.path.style.access"</span>, <span class=\"string\">"true"</span>);</span><br><span class=\"line\"> sparkConf.set(<span class=\"string\">"fs.s3a.connection.ssl.enabled"</span>, <span class=\"string\">"false"</span>);</span><br><span class=\"line\"> sparkConf.set(<span class=\"string\">"fs.s3a.impl"</span>, <span class=\"string\">"org.apache.hadoop.fs.s3a.S3AFileSystem"</span>);</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"type\">SparkSession</span> <span class=\"variable\">spark</span> <span class=\"operator\">=</span> SparkSession.builder().config(sparkConf).master(<span class=\"string\">"local"</span>).getOrCreate();</span><br><span class=\"line\"><span class=\"comment\">// spark.sparkContext().setLogLevel("warn");</span></span><br><span class=\"line\"><span class=\"comment\">// spark.sql("create database test");</span></span><br><span class=\"line\"><span class=\"comment\">// spark.sql("use test");</span></span><br><span class=\"line\"><span class=\"comment\">// spark.sql("drop table if exists test2");</span></span><br><span class=\"line\"><span class=\"comment\">// spark.sql("create table if not exists test2(id int,name string)");</span></span><br><span class=\"line\"><span class=\"comment\">// spark.sql("insert into test2 values(1,'1')");</span></span><br><span class=\"line\"> spark.sql(<span class=\"string\">"use test"</span>);</span><br><span class=\"line\"> spark.sql(<span class=\"string\">"show tables"</span>).show();</span><br><span class=\"line\"> spark.sql(<span class=\"string\">"select * from test.test2"</span>).show();</span><br><span class=\"line\"> spark.stop();</span><br><span class=\"line\"> }</span><br><span class=\"line\"></span><br><span class=\"line\">}</span><br><span class=\"line\"></span><br></pre></td></tr></table></figure>\n<figure class=\"highlight xml\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br><span class=\"line\">11</span><br><span class=\"line\">12</span><br><span class=\"line\">13</span><br><span class=\"line\">14</span><br><span class=\"line\">15</span><br><span class=\"line\">16</span><br><span class=\"line\">17</span><br><span class=\"line\">18</span><br><span class=\"line\">19</span><br><span class=\"line\">20</span><br><span class=\"line\">21</span><br><span class=\"line\">22</span><br><span class=\"line\">23</span><br><span class=\"line\">24</span><br><span class=\"line\">25</span><br><span class=\"line\">26</span><br><span class=\"line\">27</span><br><span class=\"line\">28</span><br><span class=\"line\">29</span><br><span class=\"line\">30</span><br><span class=\"line\">31</span><br><span class=\"line\">32</span><br><span class=\"line\">33</span><br><span class=\"line\">34</span><br><span class=\"line\">35</span><br><span class=\"line\">36</span><br><span class=\"line\">37</span><br><span class=\"line\">38</span><br><span class=\"line\">39</span><br><span class=\"line\">40</span><br><span class=\"line\">41</span><br><span class=\"line\">42</span><br><span class=\"line\">43</span><br><span class=\"line\">44</span><br><span class=\"line\">45</span><br><span class=\"line\">46</span><br><span class=\"line\">47</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"meta\"><?xml version=<span class=\"string\">"1.0"</span> encoding=<span class=\"string\">"UTF-8"</span>?></span></span><br><span class=\"line\"><span class=\"tag\"><<span class=\"name\">project</span> <span class=\"attr\">xmlns</span>=<span class=\"string\">"http://maven.apache.org/POM/4.0.0"</span></span></span><br><span class=\"line\"><span class=\"tag\"> <span class=\"attr\">xmlns:xsi</span>=<span class=\"string\">"http://www.w3.org/2001/XMLSchema-instance"</span></span></span><br><span class=\"line\"><span class=\"tag\"> <span class=\"attr\">xsi:schemaLocation</span>=<span class=\"string\">"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">modelVersion</span>></span>4.0.0<span class=\"tag\"></<span class=\"name\">modelVersion</span>></span></span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">groupId</span>></span>org.examples<span class=\"tag\"></<span class=\"name\">groupId</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">artifactId</span>></span>sparktest<span class=\"tag\"></<span class=\"name\">artifactId</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">version</span>></span>1.0-SNAPSHOT<span class=\"tag\"></<span class=\"name\">version</span>></span></span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">properties</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">maven.compiler.source</span>></span>8<span class=\"tag\"></<span class=\"name\">maven.compiler.source</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">maven.compiler.target</span>></span>8<span class=\"tag\"></<span class=\"name\">maven.compiler.target</span>></span></span><br><span class=\"line\"> <span class=\"tag\"></<span class=\"name\">properties</span>></span></span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">dependencies</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">dependency</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">groupId</span>></span>io.delta<span class=\"tag\"></<span class=\"name\">groupId</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">artifactId</span>></span>delta-core_2.12<span class=\"tag\"></<span class=\"name\">artifactId</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">version</span>></span>1.2.0<span class=\"tag\"></<span class=\"name\">version</span>></span></span><br><span class=\"line\"> <span class=\"tag\"></<span class=\"name\">dependency</span>></span></span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">dependency</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">groupId</span>></span>org.apache.spark<span class=\"tag\"></<span class=\"name\">groupId</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">artifactId</span>></span>spark-hadoop-cloud_2.12<span class=\"tag\"></<span class=\"name\">artifactId</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">version</span>></span>3.2.1<span class=\"tag\"></<span class=\"name\">version</span>></span></span><br><span class=\"line\"> <span class=\"tag\"></<span class=\"name\">dependency</span>></span></span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">dependency</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">groupId</span>></span>org.apache.hadoop<span class=\"tag\"></<span class=\"name\">groupId</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">artifactId</span>></span>hadoop-aws<span class=\"tag\"></<span class=\"name\">artifactId</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">version</span>></span>3.3.1<span class=\"tag\"></<span class=\"name\">version</span>></span></span><br><span class=\"line\"> <span class=\"tag\"></<span class=\"name\">dependency</span>></span></span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">dependency</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">groupId</span>></span>org.apache.spark<span class=\"tag\"></<span class=\"name\">groupId</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">artifactId</span>></span>spark-hive_2.12<span class=\"tag\"></<span class=\"name\">artifactId</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">version</span>></span>3.2.1<span class=\"tag\"></<span class=\"name\">version</span>></span></span><br><span class=\"line\"> <span class=\"tag\"></<span class=\"name\">dependency</span>></span></span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">dependency</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">groupId</span>></span>org.apache.spark<span class=\"tag\"></<span class=\"name\">groupId</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">artifactId</span>></span>spark-sql_2.12<span class=\"tag\"></<span class=\"name\">artifactId</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">version</span>></span>3.2.1<span class=\"tag\"></<span class=\"name\">version</span>></span></span><br><span class=\"line\"> <span class=\"tag\"></<span class=\"name\">dependency</span>></span></span><br><span class=\"line\"> <span class=\"tag\"></<span class=\"name\">dependencies</span>></span></span><br><span class=\"line\"><span class=\"tag\"></<span class=\"name\">project</span>></span></span><br></pre></td></tr></table></figure>","site":{"data":{}},"excerpt":"","more":"<p>spark的本地安装与测试<br>环境:mac m1<br>安装当前最新版的spark3.2.1并进行简单的测试,整合hive metastore。<br>hive metastore的安装请参考前面一文章即可。</p>\n<h4 id=\"下载安装jdk8\"><a href=\"#下载安装jdk8\" class=\"headerlink\" title=\"下载安装jdk8\"></a>下载安装jdk8</h4><p><a href=\"https://www.oracle.com/java/technologies/downloads/#java8-mac\">https://www.oracle.com/java/technologies/downloads/#java8-mac</a><br>user: <a href=\"mailto:hnyaoxh@126.com\">hnyaoxh@126.com</a><br>pwd: xxxxxx<br>下载后一步一步安装即可。<br>因为之前安装了jdk18,所以现在有两个jdk版本。需要在.zshrc中指定JAVA_HOME<br>或者在spark相关的sh文件中指定也可以。</p>\n<figure class=\"highlight bash\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"built_in\">export</span> PATH=<span class=\"string\">"/Users/student2028/app/spark/bin:<span class=\"variable\">$PATH</span>"</span></span><br><span class=\"line\">JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.8.0_331.jdk/Contents/Home</span><br><span class=\"line\">PATH=<span class=\"variable\">$JAVA_HOME</span>/bin:<span class=\"variable\">$PATH</span>:.</span><br><span class=\"line\">CLASSPATH=<span class=\"variable\">$JAVA_HOME</span>/lib/tools.jar:<span class=\"variable\">$JAVA_HOME</span>/lib/dt.jar:.</span><br><span class=\"line\"><span class=\"built_in\">export</span> JAVA_HOME</span><br><span class=\"line\"><span class=\"built_in\">export</span> PATH</span><br><span class=\"line\"><span class=\"built_in\">export</span> CLASSPATH</span><br></pre></td></tr></table></figure>\n<h4 id=\"下载安装spark\"><a href=\"#下载安装spark\" class=\"headerlink\" title=\"下载安装spark\"></a>下载安装spark</h4><p>如果下载without hadoop版本因为缺少各种jar包,所以需要自己补充进来。<br>同样使用prebuilt hadoop的版本,一定要两者兼容,否则也会报相关错误。<br>当前最新版本spark3.2兼容 hadoop3.3.1。如果版本不兼容,常报错误有</p>\n<blockquote>\n<p>Exception in thread “main” java.lang.NoSuchFieldError: JAVA_9</p>\n</blockquote>\n<figure class=\"highlight bash\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">wget https://dlcdn.apache.org/spark/spark-3.2.1/spark-3.2.1-bin-hadoop3.2.tgz</span><br><span class=\"line\">tar xvzf spark-3.2.1-bin-hadoop3.2.tgz</span><br><span class=\"line\"><span class=\"built_in\">rm</span> spark-3.2.1-bin-hadoop3.2.tgz</span><br><span class=\"line\"><span class=\"built_in\">ln</span> -s ./spark-3.2.1-bin-hadoop3.2 spark</span><br><span class=\"line\"><span class=\"comment\">##copy aws related jars to lib 注意此处的hadoop 需要是3.3版本</span></span><br><span class=\"line\"><span class=\"built_in\">cd</span> ~/app/hadoop</span><br><span class=\"line\">find . -iname <span class=\"string\">"*aws*.jar"</span> |xargs -I _ <span class=\"built_in\">cp</span> _ ~/app/spark/jars</span><br></pre></td></tr></table></figure>\n<h4 id=\"测试读写s3的数据\"><a href=\"#测试读写s3的数据\" class=\"headerlink\" title=\"测试读写s3的数据\"></a>测试读写s3的数据</h4><figure class=\"highlight bash\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br><span class=\"line\">11</span><br><span class=\"line\">12</span><br><span class=\"line\">13</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">spark-shell \\</span><br><span class=\"line\">--conf spark.hadoop.fs.s3a.bucket.all.committer.magic.enabled=<span class=\"literal\">true</span> \\</span><br><span class=\"line\">--conf spark.hadoop.fs.s3a.access.key=minio \\</span><br><span class=\"line\">--conf spark.hadoop.fs.s3a.secret.key=minio123 \\</span><br><span class=\"line\">--conf spark.hadoop.fs.s3a.endpoint=localhost:9000 \\</span><br><span class=\"line\">--conf spark.hadoop.fs.s3a.path.style.access=<span class=\"literal\">true</span> \\</span><br><span class=\"line\">--conf spark.hadoop.fs.s3a.connection.ssl.enabled=<span class=\"literal\">false</span> \\</span><br><span class=\"line\">--conf spark.hadoop.fs.s3a.impl=org.apache.hadoop.fs.s3a.S3AFileSystem \\</span><br><span class=\"line\">--conf spark.hadoop.fs.s3a.bucket.all.committer.magic.enabled=<span class=\"literal\">true</span> </span><br><span class=\"line\"></span><br><span class=\"line\">val <span class=\"built_in\">df</span>=Seq((1,<span class=\"string\">"stu1"</span>,<span class=\"string\">"M"</span>,33),(2,<span class=\"string\">"stu2"</span>,<span class=\"string\">"F"</span>,30)).toDF(<span class=\"string\">"id"</span>,<span class=\"string\">"name"</span>,<span class=\"string\">"gender"</span>,<span class=\"string\">"age"</span>)</span><br><span class=\"line\">df.write.format(<span class=\"string\">"json"</span>).mode(<span class=\"string\">"overwrite"</span>).save(<span class=\"string\">"s3a://test/student"</span>);</span><br><span class=\"line\"></span><br></pre></td></tr></table></figure>\n<blockquote>\n<p>WARN AbstractS3ACommitterFactory: Using standard FileOutputCommitter to commit work. This is slow and potentially unsafe.<br> spark3.2开始提供了更好用的提交协议 ,请参考:<br> <a href=\"https://spot.io/blog/improve-apache-spark-performance-with-the-s3-magic-committer/\">https://spot.io/blog/improve-apache-spark-performance-with-the-s3-magic-committer/</a></p>\n</blockquote>\n<blockquote>\n<p>java.lang.ClassNotFoundException: org.apache.spark.internal.io.cloud.PathOutputCommitProtocol</p>\n</blockquote>\n<p>解决方案:</p>\n<figure class=\"highlight bash\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">1. <span class=\"built_in\">cd</span> ~/app/spark/jars/</span><br><span class=\"line\"> wget https://repo1.maven.org/maven2/org/apache/spark/spark-hadoop-cloud_2.12/3.2.1/spark-hadoop-cloud_2.12-3.2.1.jar</span><br><span class=\"line\"></span><br><span class=\"line\">2. spark-shell --packages org.apache.spark:spark-hadoop-cloud_2.12:3.2.1</span><br><span class=\"line\"></span><br></pre></td></tr></table></figure>\n<blockquote>\n<p>Class org.apache.hadoop.fs.s3a.auth.IAMInstanceCredentialsProvider not found<br>这个问题的原因是spark预置的hadoop版本和hadoop-aws配置的版本不一致导致的,先看一下spark项目依赖的hadoop版本,<br>修正hadoop-aws的版本即可,一个方法是在maven项目里面,先去掉hadoop-aws依赖,<br>先看一下External Libraries 中搜一下hadoop即可以发现之前依赖的hadoop版本。</p>\n</blockquote>\n<h4 id=\"测试delta-lake\"><a href=\"#测试delta-lake\" class=\"headerlink\" title=\"测试delta lake\"></a>测试delta lake</h4><figure class=\"highlight bash\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br><span class=\"line\">11</span><br><span class=\"line\">12</span><br><span class=\"line\">13</span><br><span class=\"line\">14</span><br><span class=\"line\">15</span><br><span class=\"line\">16</span><br><span class=\"line\">17</span><br><span class=\"line\">18</span><br><span class=\"line\">19</span><br><span class=\"line\">20</span><br><span class=\"line\">21</span><br><span class=\"line\">22</span><br><span class=\"line\">23</span><br><span class=\"line\">24</span><br><span class=\"line\">25</span><br><span class=\"line\">26</span><br><span class=\"line\">27</span><br><span class=\"line\">28</span><br><span class=\"line\">29</span><br><span class=\"line\">30</span><br><span class=\"line\">31</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"built_in\">cd</span> ~/app/spark/jars</span><br><span class=\"line\">wget https://repo1.maven.org/maven2/io/delta/delta-core_2.12/1.2.0/delta-core_2.12-1.2.0.jar</span><br><span class=\"line\">wget https://repo1.maven.org/maven2/io/delta/delta-storage/1.2.0/delta-storage-1.2.0.jar</span><br><span class=\"line\"></span><br><span class=\"line\">spark-shell \\</span><br><span class=\"line\">--conf <span class=\"string\">"spark.sql.extensions=io.delta.sql.DeltaSparkSessionExtension"</span> \\</span><br><span class=\"line\">--conf <span class=\"string\">"spark.sql.catalog.spark_catalog=org.apache.spark.sql.delta.catalog.DeltaCatalog"</span> \\</span><br><span class=\"line\">--conf spark.hadoop.fs.s3a.bucket.all.committer.magic.enabled=<span class=\"literal\">true</span> \\</span><br><span class=\"line\">--conf spark.hadoop.fs.s3a.access.key=minio \\</span><br><span class=\"line\">--conf spark.hadoop.fs.s3a.secret.key=minio123 \\</span><br><span class=\"line\">--conf spark.hadoop.fs.s3a.endpoint=localhost:9000 \\</span><br><span class=\"line\">--conf spark.hadoop.fs.s3a.path.style.access=<span class=\"literal\">true</span> \\</span><br><span class=\"line\">--conf spark.hadoop.fs.s3a.connection.ssl.enabled=<span class=\"literal\">false</span> \\</span><br><span class=\"line\">--conf spark.hadoop.fs.s3a.impl=org.apache.hadoop.fs.s3a.S3AFileSystem \\</span><br><span class=\"line\">--conf spark.hadoop.fs.s3a.bucket.all.committer.magic.enabled=<span class=\"literal\">true</span> </span><br><span class=\"line\"></span><br><span class=\"line\">spark.range(5, 10).write.format(<span class=\"string\">"delta"</span>).mode(<span class=\"string\">"overwrite"</span>).save(<span class=\"string\">"s3a://test/delta1"</span>)</span><br><span class=\"line\">spark.read.format(<span class=\"string\">"delta"</span>).load(<span class=\"string\">"s3a://test/delta1"</span>).show(20)</span><br><span class=\"line\">配合hivemetastore 创建表</span><br><span class=\"line\">spark-sql \\</span><br><span class=\"line\">--conf <span class=\"string\">"spark.sql.warehouse.dir=s3a://hive/warehouse"</span> \\</span><br><span class=\"line\">--conf <span class=\"string\">"spark.sql.extensions=io.delta.sql.DeltaSparkSessionExtension"</span> \\</span><br><span class=\"line\">--conf <span class=\"string\">"spark.sql.catalog.spark_catalog=org.apache.spark.sql.delta.catalog.DeltaCatalog"</span> \\</span><br><span class=\"line\">--conf spark.hadoop.fs.s3a.access.key=minio \\</span><br><span class=\"line\">--conf spark.hadoop.fs.s3a.secret.key=minio123 \\</span><br><span class=\"line\">--conf spark.hadoop.fs.s3a.endpoint=localhost:9000 \\</span><br><span class=\"line\">--conf spark.hadoop.fs.s3a.path.style.access=<span class=\"literal\">true</span> \\</span><br><span class=\"line\">--conf spark.hadoop.fs.s3a.connection.ssl.enabled=<span class=\"literal\">false</span> \\</span><br><span class=\"line\">--conf spark.hadoop.fs.s3a.impl=org.apache.hadoop.fs.s3a.S3AFileSystem \\</span><br><span class=\"line\">--conf spark.hadoop.fs.s3a.bucket.all.committer.magic.enabled=<span class=\"literal\">true</span> \\</span><br><span class=\"line\">--conf <span class=\"string\">"spark.hadoop.hive.metastore.uris=thrift://localhost:9083"</span> </span><br></pre></td></tr></table></figure>\n<figure class=\"highlight sql\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"keyword\">create</span> database if <span class=\"keyword\">not</span> <span class=\"keyword\">exists</span> test;</span><br><span class=\"line\">use test;</span><br><span class=\"line\"><span class=\"keyword\">drop</span> <span class=\"keyword\">table</span> test2;</span><br><span class=\"line\"><span class=\"keyword\">create</span> <span class=\"keyword\">table</span> test2 (id <span class=\"type\">int</span>, name string) <span class=\"keyword\">using</span> delta;</span><br><span class=\"line\"><span class=\"keyword\">insert</span> <span class=\"keyword\">into</span> test2 <span class=\"keyword\">values</span>(<span class=\"number\">1</span>,<span class=\"string\">'1'</span>);</span><br><span class=\"line\"><span class=\"keyword\">insert</span> <span class=\"keyword\">into</span> test2 <span class=\"keyword\">values</span>(<span class=\"number\">2</span>,<span class=\"string\">'22'</span>);</span><br><span class=\"line\"><span class=\"keyword\">select</span> <span class=\"operator\">*</span> <span class=\"keyword\">from</span> test2;</span><br></pre></td></tr></table></figure>\n\n<h4 id=\"spark-java-project-demo\"><a href=\"#spark-java-project-demo\" class=\"headerlink\" title=\"spark java project demo\"></a>spark java project demo</h4><figure class=\"highlight java\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br><span class=\"line\">11</span><br><span class=\"line\">12</span><br><span class=\"line\">13</span><br><span class=\"line\">14</span><br><span class=\"line\">15</span><br><span class=\"line\">16</span><br><span class=\"line\">17</span><br><span class=\"line\">18</span><br><span class=\"line\">19</span><br><span class=\"line\">20</span><br><span class=\"line\">21</span><br><span class=\"line\">22</span><br><span class=\"line\">23</span><br><span class=\"line\">24</span><br><span class=\"line\">25</span><br><span class=\"line\">26</span><br><span class=\"line\">27</span><br><span class=\"line\">28</span><br><span class=\"line\">29</span><br><span class=\"line\">30</span><br><span class=\"line\">31</span><br><span class=\"line\">32</span><br><span class=\"line\">33</span><br><span class=\"line\">34</span><br><span class=\"line\">35</span><br><span class=\"line\">36</span><br><span class=\"line\">37</span><br><span class=\"line\">38</span><br><span class=\"line\">39</span><br><span class=\"line\">40</span><br><span class=\"line\">41</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"></span><br><span class=\"line\"><span class=\"keyword\">import</span> org.apache.spark.SparkConf;</span><br><span class=\"line\"><span class=\"keyword\">import</span> org.apache.spark.sql.Dataset;</span><br><span class=\"line\"><span class=\"keyword\">import</span> org.apache.spark.sql.Row;</span><br><span class=\"line\"><span class=\"keyword\">import</span> org.apache.spark.sql.SparkSession;</span><br><span class=\"line\"><span class=\"keyword\">import</span> org.apache.spark.sql.functions;</span><br><span class=\"line\"><span class=\"keyword\">import</span> java.util.Random;</span><br><span class=\"line\"><span class=\"keyword\">public</span> <span class=\"keyword\">class</span> <span class=\"title class_\">TestDelta</span> {</span><br><span class=\"line\"> <span class=\"keyword\">public</span> <span class=\"keyword\">static</span> <span class=\"keyword\">void</span> <span class=\"title function_\">main</span><span class=\"params\">(String[] args)</span> {</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"type\">SparkConf</span> <span class=\"variable\">sparkConf</span> <span class=\"operator\">=</span> <span class=\"keyword\">new</span> <span class=\"title class_\">SparkConf</span>();</span><br><span class=\"line\"> sparkConf.set(<span class=\"string\">"spark.sql.warehouse.dir"</span>,<span class=\"string\">"s3a://hive/warehouse"</span>);</span><br><span class=\"line\"> sparkConf.set(<span class=\"string\">"spark.hadoop.fs.s3a.bucket.all.committer.magic.enabled"</span>,<span class=\"string\">"true"</span>);</span><br><span class=\"line\"> sparkConf.set(<span class=\"string\">"spark.sql.catalogImplementation"</span>,<span class=\"string\">"hive"</span>);<span class=\"comment\">//enabled hive support</span></span><br><span class=\"line\"> sparkConf.set(<span class=\"string\">"spark.sql.extensions"</span>,<span class=\"string\">"io.delta.sql.DeltaSparkSessionExtension"</span>);</span><br><span class=\"line\"> sparkConf.set(<span class=\"string\">"spark.sql.catalog.spark_catalog"</span>,<span class=\"string\">"org.apache.spark.sql.delta.catalog.DeltaCatalog"</span>);</span><br><span class=\"line\"> sparkConf.set(<span class=\"string\">"spark.hadoop.hive.metastore.uris"</span>,<span class=\"string\">"thrift://localhost:9083"</span>);</span><br><span class=\"line\"> sparkConf.set(<span class=\"string\">"spark.hadoop.hive.metastore.warehouse.dir"</span>,<span class=\"string\">"s3a://hive/warehouse"</span>);</span><br><span class=\"line\"></span><br><span class=\"line\"> sparkConf.set(<span class=\"string\">"fs.s3a.access.key"</span>, <span class=\"string\">"minio"</span>);</span><br><span class=\"line\"> sparkConf.set(<span class=\"string\">"fs.s3a.secret.key"</span>, <span class=\"string\">"minio123"</span>);</span><br><span class=\"line\"> sparkConf.set(<span class=\"string\">"fs.s3a.endpoint"</span>,<span class=\"string\">"localhost:9000"</span>);</span><br><span class=\"line\"> sparkConf.set(<span class=\"string\">"fs.s3a.path.style.access"</span>, <span class=\"string\">"true"</span>);</span><br><span class=\"line\"> sparkConf.set(<span class=\"string\">"fs.s3a.connection.ssl.enabled"</span>, <span class=\"string\">"false"</span>);</span><br><span class=\"line\"> sparkConf.set(<span class=\"string\">"fs.s3a.impl"</span>, <span class=\"string\">"org.apache.hadoop.fs.s3a.S3AFileSystem"</span>);</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"type\">SparkSession</span> <span class=\"variable\">spark</span> <span class=\"operator\">=</span> SparkSession.builder().config(sparkConf).master(<span class=\"string\">"local"</span>).getOrCreate();</span><br><span class=\"line\"><span class=\"comment\">// spark.sparkContext().setLogLevel("warn");</span></span><br><span class=\"line\"><span class=\"comment\">// spark.sql("create database test");</span></span><br><span class=\"line\"><span class=\"comment\">// spark.sql("use test");</span></span><br><span class=\"line\"><span class=\"comment\">// spark.sql("drop table if exists test2");</span></span><br><span class=\"line\"><span class=\"comment\">// spark.sql("create table if not exists test2(id int,name string)");</span></span><br><span class=\"line\"><span class=\"comment\">// spark.sql("insert into test2 values(1,'1')");</span></span><br><span class=\"line\"> spark.sql(<span class=\"string\">"use test"</span>);</span><br><span class=\"line\"> spark.sql(<span class=\"string\">"show tables"</span>).show();</span><br><span class=\"line\"> spark.sql(<span class=\"string\">"select * from test.test2"</span>).show();</span><br><span class=\"line\"> spark.stop();</span><br><span class=\"line\"> }</span><br><span class=\"line\"></span><br><span class=\"line\">}</span><br><span class=\"line\"></span><br></pre></td></tr></table></figure>\n<figure class=\"highlight xml\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br><span class=\"line\">11</span><br><span class=\"line\">12</span><br><span class=\"line\">13</span><br><span class=\"line\">14</span><br><span class=\"line\">15</span><br><span class=\"line\">16</span><br><span class=\"line\">17</span><br><span class=\"line\">18</span><br><span class=\"line\">19</span><br><span class=\"line\">20</span><br><span class=\"line\">21</span><br><span class=\"line\">22</span><br><span class=\"line\">23</span><br><span class=\"line\">24</span><br><span class=\"line\">25</span><br><span class=\"line\">26</span><br><span class=\"line\">27</span><br><span class=\"line\">28</span><br><span class=\"line\">29</span><br><span class=\"line\">30</span><br><span class=\"line\">31</span><br><span class=\"line\">32</span><br><span class=\"line\">33</span><br><span class=\"line\">34</span><br><span class=\"line\">35</span><br><span class=\"line\">36</span><br><span class=\"line\">37</span><br><span class=\"line\">38</span><br><span class=\"line\">39</span><br><span class=\"line\">40</span><br><span class=\"line\">41</span><br><span class=\"line\">42</span><br><span class=\"line\">43</span><br><span class=\"line\">44</span><br><span class=\"line\">45</span><br><span class=\"line\">46</span><br><span class=\"line\">47</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"meta\"><?xml version=<span class=\"string\">"1.0"</span> encoding=<span class=\"string\">"UTF-8"</span>?></span></span><br><span class=\"line\"><span class=\"tag\"><<span class=\"name\">project</span> <span class=\"attr\">xmlns</span>=<span class=\"string\">"http://maven.apache.org/POM/4.0.0"</span></span></span><br><span class=\"line\"><span class=\"tag\"> <span class=\"attr\">xmlns:xsi</span>=<span class=\"string\">"http://www.w3.org/2001/XMLSchema-instance"</span></span></span><br><span class=\"line\"><span class=\"tag\"> <span class=\"attr\">xsi:schemaLocation</span>=<span class=\"string\">"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">modelVersion</span>></span>4.0.0<span class=\"tag\"></<span class=\"name\">modelVersion</span>></span></span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">groupId</span>></span>org.examples<span class=\"tag\"></<span class=\"name\">groupId</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">artifactId</span>></span>sparktest<span class=\"tag\"></<span class=\"name\">artifactId</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">version</span>></span>1.0-SNAPSHOT<span class=\"tag\"></<span class=\"name\">version</span>></span></span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">properties</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">maven.compiler.source</span>></span>8<span class=\"tag\"></<span class=\"name\">maven.compiler.source</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">maven.compiler.target</span>></span>8<span class=\"tag\"></<span class=\"name\">maven.compiler.target</span>></span></span><br><span class=\"line\"> <span class=\"tag\"></<span class=\"name\">properties</span>></span></span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">dependencies</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">dependency</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">groupId</span>></span>io.delta<span class=\"tag\"></<span class=\"name\">groupId</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">artifactId</span>></span>delta-core_2.12<span class=\"tag\"></<span class=\"name\">artifactId</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">version</span>></span>1.2.0<span class=\"tag\"></<span class=\"name\">version</span>></span></span><br><span class=\"line\"> <span class=\"tag\"></<span class=\"name\">dependency</span>></span></span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">dependency</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">groupId</span>></span>org.apache.spark<span class=\"tag\"></<span class=\"name\">groupId</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">artifactId</span>></span>spark-hadoop-cloud_2.12<span class=\"tag\"></<span class=\"name\">artifactId</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">version</span>></span>3.2.1<span class=\"tag\"></<span class=\"name\">version</span>></span></span><br><span class=\"line\"> <span class=\"tag\"></<span class=\"name\">dependency</span>></span></span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">dependency</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">groupId</span>></span>org.apache.hadoop<span class=\"tag\"></<span class=\"name\">groupId</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">artifactId</span>></span>hadoop-aws<span class=\"tag\"></<span class=\"name\">artifactId</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">version</span>></span>3.3.1<span class=\"tag\"></<span class=\"name\">version</span>></span></span><br><span class=\"line\"> <span class=\"tag\"></<span class=\"name\">dependency</span>></span></span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">dependency</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">groupId</span>></span>org.apache.spark<span class=\"tag\"></<span class=\"name\">groupId</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">artifactId</span>></span>spark-hive_2.12<span class=\"tag\"></<span class=\"name\">artifactId</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">version</span>></span>3.2.1<span class=\"tag\"></<span class=\"name\">version</span>></span></span><br><span class=\"line\"> <span class=\"tag\"></<span class=\"name\">dependency</span>></span></span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">dependency</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">groupId</span>></span>org.apache.spark<span class=\"tag\"></<span class=\"name\">groupId</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">artifactId</span>></span>spark-sql_2.12<span class=\"tag\"></<span class=\"name\">artifactId</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">version</span>></span>3.2.1<span class=\"tag\"></<span class=\"name\">version</span>></span></span><br><span class=\"line\"> <span class=\"tag\"></<span class=\"name\">dependency</span>></span></span><br><span class=\"line\"> <span class=\"tag\"></<span class=\"name\">dependencies</span>></span></span><br><span class=\"line\"><span class=\"tag\"></<span class=\"name\">project</span>></span></span><br></pre></td></tr></table></figure>"}],"PostAsset":[],"PostCategory":[{"post_id":"cl27q3uuy0006n94odzu85sbo","category_id":"cl27q3uuw0003n94oagqk7zob","_id":"cl27q3uv2000dn94oggs3gdwn"},{"post_id":"cl27q3uur0001n94obrda5ot9","category_id":"cl27q3uuw0003n94oagqk7zob","_id":"cl27q3uv4000in94ogvbb8sh5"},{"post_id":"cl27q3uv0000bn94o1wfu11qd","category_id":"cl27q3uuw0003n94oagqk7zob","_id":"cl27q3uv5000kn94o9w2g4rpi"},{"post_id":"cl27q3uuu0002n94o8acn1so7","category_id":"cl27q3uuw0003n94oagqk7zob","_id":"cl27q3uv6000on94o5r8m9iqc"},{"post_id":"cl27q3uux0005n94ochtcf2k5","category_id":"cl27q3uuw0003n94oagqk7zob","_id":"cl27q3uv7000qn94of1ie4qky"},{"post_id":"cl27q3uuy0007n94o5wthbwbf","category_id":"cl27q3uv5000ln94of9qs8r19","_id":"cl27q3uv9000xn94og7ipfl6u"},{"post_id":"cl27q3uv8000tn94o78y392lt","category_id":"cl27q3uv7000rn94o92ih2tge","_id":"cl27q3uva0013n94oa8hihqh2"},{"post_id":"cl27q3uv2000cn94o63964j8h","category_id":"cl27q3uv7000rn94o92ih2tge","_id":"cl27q3uvb0018n94o8ihwh7a9"},{"post_id":"cl27q3uv3000hn94ohmkj81sp","category_id":"cl27q3uv7000rn94o92ih2tge","_id":"cl27q3uvb001an94o0dzbdbxm"},{"post_id":"cl27q3uv4000jn94ohkt19a72","category_id":"cl27q3uva0015n94ogfc67m5q","_id":"cl27q3uvd001hn94ogz3bdrxd"},{"post_id":"cl27q3uvb0019n94ocrpd7v5o","category_id":"cl27q3uva0015n94ogfc67m5q","_id":"cl27q3uve001mn94obetc0gde"},{"post_id":"cl27q3uv6000nn94oetlv0mtg","category_id":"cl27q3uvc001bn94o9bpw8ro1","_id":"cl27q3uvf001pn94oe0br2i94"},{"post_id":"cl27q3uve001nn94oac344ljn","category_id":"cl27q3uve001jn94o3o3q778e","_id":"cl27q3uvi001vn94o7kwt73e7"},{"post_id":"cl27q3uv6000pn94o9myi0mhc","category_id":"cl27q3uve001jn94o3o3q778e","_id":"cl27q3uvi001yn94o5a4s5tm3"},{"post_id":"cl27q3uvf001on94o3q6y1mq6","category_id":"cl27q3uve001jn94o3o3q778e","_id":"cl27q3uvi0020n94o5xseavut"},{"post_id":"cl27q3uvh001tn94ohgkohpdk","category_id":"cl27q3uve001jn94o3o3q778e","_id":"cl27q3uvj0023n94obwi738hj"},{"post_id":"cl27q3uv9000vn94ofoehhccc","category_id":"cl27q3uva0015n94ogfc67m5q","_id":"cl27q3uvj0025n94ofvjp2lfh"},{"post_id":"cl27q3uv90010n94o9gvjftdr","category_id":"cl27q3uva0015n94ogfc67m5q","_id":"cl27q3uvj0028n94o1asl8al8"},{"post_id":"cl27q3uva0012n94ogt7mggyr","category_id":"cl27q3uve001jn94o3o3q778e","_id":"cl27q3uvl002bn94o088zf23x"},{"post_id":"cl27q3uvb0017n94o6z6v7div","category_id":"cl27q3uve001jn94o3o3q778e","_id":"cl27q3uvl002en94o94fb0qmu"},{"post_id":"cl27q3uvc001dn94ofer8g8p4","category_id":"cl27q3uvj002an94oars8g1td","_id":"cl27q3uvm002jn94o890y2rig"},{"post_id":"cl27q3uvd001fn94o0ypr65pm","category_id":"cl27q3uve001jn94o3o3q778e","_id":"cl27q3uvm002nn94o1hfcfy10"},{"post_id":"cl27q3uvd001in94oboc15iwr","category_id":"cl27q3uve001jn94o3o3q778e","_id":"cl27q3uvn002qn94obiei22zs"},{"post_id":"cl27q3uvt003kn94oekpjbe4t","category_id":"cl27q3uv7000rn94o92ih2tge","_id":"cl27q3uvw003mn94o3i9mbp6r"},{"post_id":"cl2bf01vq0000r74ocw0q794k","category_id":"cl2bf01vx0002r74oc8uu0tcv","_id":"cl2bf01w40009r74o5sk2717n"},{"post_id":"cl2bf01vv0001r74o013l5fgg","category_id":"cl2bf01w40006r74of8x15gwb","_id":"cl2bf01w4000ar74o0akf4aqb"}],"PostTag":[{"post_id":"cl27q3uur0001n94obrda5ot9","tag_id":"cl27q3uux0004n94obk3f37sl","_id":"cl27q3uv0000an94o5z3w2ceo"},{"post_id":"cl27q3uuu0002n94o8acn1so7","tag_id":"cl27q3uux0004n94obk3f37sl","_id":"cl27q3uv3000gn94o8k45b69r"},{"post_id":"cl27q3uux0005n94ochtcf2k5","tag_id":"cl27q3uux0004n94obk3f37sl","_id":"cl27q3uv8000un94og7zb6kvw"},{"post_id":"cl27q3uux0005n94ochtcf2k5","tag_id":"cl27q3uv6000mn94o5li9gebn","_id":"cl27q3uv9000wn94o2p7c3ly9"},{"post_id":"cl27q3uuy0006n94odzu85sbo","tag_id":"cl27q3uux0004n94obk3f37sl","_id":"cl27q3uva0011n94o2sj22rti"},{"post_id":"cl27q3uuy0006n94odzu85sbo","tag_id":"cl27q3uv6000mn94o5li9gebn","_id":"cl27q3uva0014n94oabui7sqy"},{"post_id":"cl27q3uuy0007n94o5wthbwbf","tag_id":"cl27q3uv9000yn94o7udwfs3n","_id":"cl27q3uvc001en94ogrk203i3"},{"post_id":"cl27q3uuy0007n94o5wthbwbf","tag_id":"cl27q3uvb0016n94o5100hdrl","_id":"cl27q3uvd001gn94o0g4j59vo"},{"post_id":"cl27q3uvc001dn94ofer8g8p4","tag_id":"cl27q3uux0004n94obk3f37sl","_id":"cl27q3uve001ln94o6mhxgfzh"},{"post_id":"cl27q3uv0000bn94o1wfu11qd","tag_id":"cl27q3uvc001cn94o6caa9l6o","_id":"cl27q3uvh001sn94oczcia8rq"},{"post_id":"cl27q3uv0000bn94o1wfu11qd","tag_id":"cl27q3uv6000mn94o5li9gebn","_id":"cl27q3uvh001un94o601q7004"},{"post_id":"cl27q3uv2000cn94o63964j8h","tag_id":"cl27q3uvf001qn94ogwa7d1g2","_id":"cl27q3uvi001zn94ofto9gng2"},{"post_id":"cl27q3uv3000hn94ohmkj81sp","tag_id":"cl27q3uvf001qn94ogwa7d1g2","_id":"cl27q3uvj0024n94o3l8a1549"},{"post_id":"cl27q3uv4000jn94ohkt19a72","tag_id":"cl27q3uvi0022n94oewfw1c4c","_id":"cl27q3uvj0029n94o2ik335w7"},{"post_id":"cl27q3uv6000nn94oetlv0mtg","tag_id":"cl27q3uvj0027n94oaycd0vv9","_id":"cl27q3uvl002dn94od06ubzkr"},{"post_id":"cl27q3uv6000pn94o9myi0mhc","tag_id":"cl27q3uux0004n94obk3f37sl","_id":"cl27q3uvm002hn94oftdb35qy"},{"post_id":"cl27q3uv6000pn94o9myi0mhc","tag_id":"cl27q3uvl002cn94ob41fchew","_id":"cl27q3uvm002kn94odu633f6c"},{"post_id":"cl27q3uv8000tn94o78y392lt","tag_id":"cl27q3uvf001qn94ogwa7d1g2","_id":"cl27q3uvm002mn94o96r5ahuo"},{"post_id":"cl27q3uv9000vn94ofoehhccc","tag_id":"cl27q3uvm002ln94oa64t3b2h","_id":"cl27q3uvn002pn94oa0r0cynx"},{"post_id":"cl27q3uv90010n94o9gvjftdr","tag_id":"cl27q3uvn002on94oejqr768d","_id":"cl27q3uvn002sn94o0p5c1x6s"},{"post_id":"cl27q3uva0012n94ogt7mggyr","tag_id":"cl27q3uux0004n94obk3f37sl","_id":"cl27q3uvn002un94oc8grdxs7"},{"post_id":"cl27q3uva0012n94ogt7mggyr","tag_id":"cl27q3uvl002cn94ob41fchew","_id":"cl27q3uvn002vn94o4qkl1g9e"},{"post_id":"cl27q3uvb0017n94o6z6v7div","tag_id":"cl27q3uux0004n94obk3f37sl","_id":"cl27q3uvo002xn94o6mexe1yf"},{"post_id":"cl27q3uvb0017n94o6z6v7div","tag_id":"cl27q3uvl002cn94ob41fchew","_id":"cl27q3uvo002yn94oevtbeyy6"},{"post_id":"cl27q3uvb0019n94ocrpd7v5o","tag_id":"cl27q3uvn002wn94o7bg04nfk","_id":"cl27q3uvo0030n94o00c80th1"},{"post_id":"cl27q3uvd001fn94o0ypr65pm","tag_id":"cl27q3uux0004n94obk3f37sl","_id":"cl27q3uvo0032n94ofl7p7z2k"},{"post_id":"cl27q3uvd001fn94o0ypr65pm","tag_id":"cl27q3uv6000mn94o5li9gebn","_id":"cl27q3uvo0033n94o0ibx4zsp"},{"post_id":"cl27q3uvd001fn94o0ypr65pm","tag_id":"cl27q3uvl002cn94ob41fchew","_id":"cl27q3uvo0035n94obaia22ww"},{"post_id":"cl27q3uvd001in94oboc15iwr","tag_id":"cl27q3uv6000mn94o5li9gebn","_id":"cl27q3uvo0036n94o5vhchtxz"},{"post_id":"cl27q3uvd001in94oboc15iwr","tag_id":"cl27q3uvl002cn94ob41fchew","_id":"cl27q3uvp0038n94o3efif8gi"},{"post_id":"cl27q3uvd001in94oboc15iwr","tag_id":"cl27q3uux0004n94obk3f37sl","_id":"cl27q3uvp0039n94o5ocv2hk7"},{"post_id":"cl27q3uve001nn94oac344ljn","tag_id":"cl27q3uux0004n94obk3f37sl","_id":"cl27q3uvp003bn94obh4r3fsq"},{"post_id":"cl27q3uve001nn94oac344ljn","tag_id":"cl27q3uv6000mn94o5li9gebn","_id":"cl27q3uvp003cn94o9f9wctj8"},{"post_id":"cl27q3uve001nn94oac344ljn","tag_id":"cl27q3uvl002cn94ob41fchew","_id":"cl27q3uvp003dn94o0b5b0tgm"},{"post_id":"cl27q3uvf001on94o3q6y1mq6","tag_id":"cl27q3uux0004n94obk3f37sl","_id":"cl27q3uvp003en94ogg2a2uz9"},{"post_id":"cl27q3uvf001on94o3q6y1mq6","tag_id":"cl27q3uv6000mn94o5li9gebn","_id":"cl27q3uvp003fn94ohd53b07k"},{"post_id":"cl27q3uvf001on94o3q6y1mq6","tag_id":"cl27q3uvl002cn94ob41fchew","_id":"cl27q3uvp003gn94obd7qc89a"},{"post_id":"cl27q3uvh001tn94ohgkohpdk","tag_id":"cl27q3uux0004n94obk3f37sl","_id":"cl27q3uvp003hn94oexnh09yl"},{"post_id":"cl27q3uvh001tn94ohgkohpdk","tag_id":"cl27q3uv6000mn94o5li9gebn","_id":"cl27q3uvp003in94ocb1r8dei"},{"post_id":"cl27q3uvh001tn94ohgkohpdk","tag_id":"cl27q3uvl002cn94ob41fchew","_id":"cl27q3uvq003jn94o7ci13oz8"},{"post_id":"cl27q3uvt003kn94oekpjbe4t","tag_id":"cl27q3uvn002on94oejqr768d","_id":"cl27q3uvw003ln94o0nhw3j56"},{"post_id":"cl2bf01vv0001r74o013l5fgg","tag_id":"cl27q3uvb0016n94o5100hdrl","_id":"cl2bf01w40004r74oedo09ucb"},{"post_id":"cl2bf01vv0001r74o013l5fgg","tag_id":"cl27q3uvc001cn94o6caa9l6o","_id":"cl2bf01w40005r74obllgdlkd"},{"post_id":"cl2bf01vq0000r74ocw0q794k","tag_id":"cl27q3uvb0016n94o5100hdrl","_id":"cl2bf01w40007r74oaqs12ug6"},{"post_id":"cl2bf01vq0000r74ocw0q794k","tag_id":"cl2bf01w20003r74o4jod1ggt","_id":"cl2bf01w40008r74o6w9eafk5"}],"Tag":[{"name":"JAVA","_id":"cl27q3uux0004n94obk3f37sl"},{"name":"面试题","_id":"cl27q3uv6000mn94o5li9gebn"},{"name":"Hbase","_id":"cl27q3uv9000yn94o7udwfs3n"},{"name":"大数据","_id":"cl27q3uvb0016n94o5100hdrl"},{"name":"Spark","_id":"cl27q3uvc001cn94o6caa9l6o"},{"name":"Scala","_id":"cl27q3uvf001qn94ogwa7d1g2"},{"name":"博客","_id":"cl27q3uvi0022n94oewfw1c4c"},{"name":"JS","_id":"cl27q3uvj0027n94oaycd0vv9"},{"name":"算法","_id":"cl27q3uvl002cn94ob41fchew"},{"name":"语录","_id":"cl27q3uvm002ln94oa64t3b2h"},{"name":"生活","_id":"cl27q3uvn002on94oejqr768d"},{"name":"旅行","_id":"cl27q3uvn002wn94o7bg04nfk"},{"name":"Hive","_id":"cl2bf01w20003r74o4jod1ggt"}]}}