(一)算法伦理的研究
1.算法内涵界定。算法源于数学,但现代算法又远远不止于传统数学的计算范畴。算法多被理解为是计算机用于解决问题的程序或步骤,是现代人工智能系统的运行支柱。《计算主义:一种新的世界观》(李建会等,2012)中将算法定义为能行的方法,在外界的常识性理解中所谓算法就是能感受到的一套运算规则,这个规则的特点在于运算时间的有限性、计算步骤的有穷性、输入结果的确切性,它是机械步骤或能行可算计程序。该定义点明了算法应具备的两个基本属性—或侍李—有限性与有穷性。《用计算的观点看世界》(郦全民,2016)则从信息传播的角度解读算法,认为算法实质上是信息处理方法。
2.算法伦理研究
伦理关乎道德价值真理及其判断。存在于自然界、社会中的人,其行为应遵循一定的伦理道德规范。伦理的效应要导向善。伦理道德关注对个体存在的尊重、个体的自由、公平正义以及组织团体的延续与发展等问题。在一定程度上可以说,当今的人类社会已经不能脱离智能算法系统而运行了。
算法无时无处不在对世界产生影响,因而算法也会必然的触碰到伦理道德。和鸿鹏(2017)已指出,算法系统在人类社会生活中的广泛应用,会陷入诸多如人类面临且无法回避的伦理两难选择困境之中。而当算法与伦理发生关联时,学界一般认为会引出职业伦理和技术伦理两种伦理问题。
职业伦理主要与算法系统的开发者有关,指开发者是带有个性价值观、伦理道德观去研发算法系统的行为体,因而算法系统一开始便会掺杂着设计人主观性的伦理道德观。设计者出于何种目的开发某算法系统、面对不同问题设计者持有的伦理道德态度,这些衫迟都会在算法系统的运行中得到体现。
技术伦理是算法系统在一定意义上可称之为一种科学技术,这种技术自身及其运作结果都会负载着伦理价值。其实在一些情况下,职业伦理与技术伦理之间并没有很明确的界别,关于这一点,刘则渊跟王国豫已做过论述。
本文将主要从技术伦理的角度对算法关涉伦理这一问题尝试做深入研究。
(二)网络新闻传播的算法伦理研究
算法与技术的融合不断英语于网络新闻传播领域中,从数据新闻到机器写作,从算法推送到舆情到分析,国内新闻传媒领域的机器新闻和相关研究逐渐发展,金兼斌在《机器新闻写作:一场正在发生的革命》(2014),作者较早的将眼光聚焦于基于算法的新闻内容生产和编辑。认为在自动化新闻生产大发展的前提下,诸如新闻生产或分发中劳动密集型的基础性工作与环节都将被技术取代。张超、钟新在《从比特到人工智能:数字新闻生产的算法转向》(2017)认为算法正在从比特形式走向人工智能阶段,这种转向使得数字新闻与传统新闻的边界进一步明晰,促使数字新闻生产也产生了变革。胡万鹏在《智能算法推荐的伦理风险及防范策略》中总结了从算法推送方面:针对新闻的价值观所受到的负面影响;以及新闻的公共性、客观性和真实性受到的削弱进行分析;从受众方面:将具体对信息茧房现象以及受众的知情权和被遗忘权展开探讨;从社会影响方面,则针对社会群体、社会公共领域和社会文化所受到的消极影响展开论述。
根据以上文献的梳理可以看出,国内目前对网络新闻传播的算法伦理研究主要集中在新闻业态算法伦理失范的相关问题,因为与其他失范问题相比,这是比较容易发现的。但目前关于网络新闻传播的算法伦理的国内研究还存在不足:国内算谈棚法伦理和网络新闻传播算法伦理的研究还是在起步阶段,比较成熟的系统性研究还未出现;关于算法开发人员和平台的责任机制的研究都比较薄弱,总上所述,算法推送新闻的伦理问题研究是有必要继续加强的。
2.新闻推荐算法的兴起、发展与原理
2.1新闻推荐算法的兴起
随着计算机技术的信息处理的维度越来越高,信息处理的能力不断提升,算法技术可以从大数据中筛选出用户最关心最感兴趣的信息,改变了原有的新闻信息传播方式,重塑了新的媒介生态和传播格局。
但反过来看,在人人都能生产信息的背景下,信息的生产、传播和反馈的速度都是呈几何倍数增长,用户面对的信息越来越多。由于设备的局限性和信息海量,用户无法集中注意力看自己感兴趣的内容,也无法及时抓取对自己有用的信息,于是出现了“注意力经济”。美国经济学家迈克尔·戈德海伯(1997)认为,当今社会是一个信息极大丰富甚至泛滥的社会,而互联网的出现,加快了这一进程,信息非但不是稀缺资源,相反是过剩的。相对于过剩的信息,只有一种资源是稀缺的,那就是人们的注意力。换句话说,信息不能够一味追求量,还要有价值,价值就在于用户对信息的注意力,谁获得了用户的注意力就可以有市场的发展空间,通过“贩卖”用户的注意力能够使新媒体聚合平台获得利润,维持发展。再加上现在生活节奏越来越快,人们对信息获取的量和效率要求提高,不想把时间浪费在自己不感兴趣的信息,从而用户获取信息的“个性化”特征变得明显起来。
基于此背景下,算法推送新闻的传播机制应运而生,用户不需要特意搜索自己需要的信息,而是海量的信息会自行“找到”用户,为用户节省搜索时间之余,又能做到真正为用户提供有用的信息。
2.2新闻推荐算法的发展现状
算法推荐是依据用户数据为用户推荐特定领域的信息,根据受众使用反馈不断修正并完善推荐方案。目前主要有两类新闻机构使用算法推送,其一是新型的互联网新闻聚合类平台,国内主要是以今日头条和一点资讯等算法类平台为代表,在我国新闻客户端市场上拥有极高的占有率。张一鸣创建今日头条是依靠大数据和算法为用户推荐信息,提供连接人与信息的服务,算法会以关键词等元素判断用户的兴趣爱好,从全网抓取内容实现个性化推荐。国外则是以Facebook、Instagram等平台为代表,这些APP都是通过算法挖掘用户的数据,以用户个性化需求为导向对用户进行新闻推送。另一种则是专业新闻生产的传统媒体,为积极应对新闻市场的竞争和提高技术水平而转型到新闻全媒体平台,如国内的“人民日报”等,国外利用算法推送向用户推送新闻的传统媒体则有美国的美联社、华盛顿邮报和英国的BBC等,他们利用算法监督受众的数量还有阅读行为,使他们的新闻报道能够更加受受众的喜欢,增加用户的粘性。
2.2新闻推荐算法的原理
2.2.1新闻推荐算法的基本要素
算法推送有三个基本要素,分别是用户、内容和算法。用户是算法推送系统的服务对象,对用户的理解和认知越是透彻,内容分法的准确性和有效性就越准确。内容是算法推送系统的基本生产资料,对多种形式内通的分析、组织、储存和分发都需要科学的手段与方法。算法是算法推送技术上的支持,也是最核心的。系统中大量用户与海量的信息是无法自行匹配的,需要推送算法把用户和内容连接起来,在用户和内容之间发挥桥梁作用,高效把合适的内容推荐给合适的用户。
2.2.2新闻推荐算法的基本原理
算法推送的出现需要具备两个条件:足够的信息源和精确的算法框架。其中,算法的内容生产源与信息分发最终效果密切相关:是否有足够多的信息可供抓取与信息是否有足够的品质令用户满意都将对信息的传播效果产生影响。与此同时,分发环节也在向前追溯,改变着整个传播的生态。目前,国内新闻传播领域所使用的算法推送主要有三大类——协同过滤推送、基于内容推送和关联规则推送。
协同过滤推送分为基于用户的协同过滤和基于模型的协同过滤。前者主要考虑的是用户和用户之间的相似度,只要找出相似用户喜欢的新闻文章类别,并预测目标用户对该文章的喜欢程度,就可以将其他文章推荐给用户;后者和前者是类似的,区别在此时转向找到文章和文章之间的相似度,只有找到了目标用户对某类文章的喜爱程度,那么我们就可以对相似度高的类似文章进行预测,将喜爱程度相当的相似文章推荐给用户。因此,前者利用用户历史数据在整个用户数据库中寻找相似的推送文章进行推荐,后者通过用户历史数据构造预测模型,再通过模型进行预测并推送。
基于内容的推送即根据用户历史进行文本信息特征抽取、过滤,生成模型,向用户推荐与历史项目内容相似的信息。它的优点之一就是解决了协同过滤中数据稀少时无法准确判断分发的问题。但如果长期只根据用户历史数据推荐信息,会造成过度个性化,容易形成“信息茧房”。
关联规则推送就是基于用户历史数据挖掘用户数据背后的关联,以分析用户的潜在需求,向用户推荐其可能感兴趣的信息。基于该算法的信息推荐流程主要分为两个步骤,第一步是根据当前用户阅读过的感兴趣的内容,通过规则推导出用户还没有阅读过的可能感兴趣的内容;第二是根据规则的重要程度,对内容排序并展现给用户。关联规则推送的效果依赖规则的数量和质量,但随着规则数量的增多,对系统的要求也会提高。
2.2.3算法推送的实现流程
在信息过载的时代,同一个新闻选题有很多同质化的报道,因此分发前需要对新闻内容进行消重,消重后的新闻内容便等待推送,此时的推送有三个类别:启动推送、扩大推送和限制推送。
3.“今日头条”新闻推荐算法分析
“今日头条”是国内一款资讯类的媒体聚合平台,每天有超过1.2亿人使用。从“你关心的,才是头条!”到如今的“信息创造价值!”,产品slogan的变化也意味着今日头条正逐渐摆脱以往单一、粗暴的流量思维,而开始注重人与信息的连接,在促进信息高效、精准传播的同时注重正确的价值引导。
在2018年初,“今日头条”的资深算法架构师曹欢欢博士在一场分享交流会上公开了其算法运行原理。在他的叙述中,非常详细地介绍了“今日头条”的算法推荐系统概述以及算法推荐系统的操作原理。
3.1.1-1曹欢欢博士的今日头条算法建模
上图用数学形式化的方法去描述“今日头条”的算法推送,实际上就是一个能够得出用户对内容满意程度的函数:即y为用户对内容的满意度,Xi,Xc,Xu分别是今日头条公开的算法推送的三个维度:Xi是用户,包括用户的性别、年龄、职业和兴趣标签,还有其他算法模型刻画的隐形用户偏好等;Xc是环境,这也是移动互联网时代新闻推送的特点,由于用户随时随地在不停移动,移动终端也在移动,用户在不同的工作场合、旅行等场景信息推送偏好也会不同;Xu是内容,今日头条本身就是信息聚合类平台,平台上涵盖各种不同形式的内容。本章将以该函数为基础,逐一分析今日头条的推荐算法。
3.1推荐维度之一:内容分析
内容分析原指第二次世界大战期间,传播学家拉斯韦尔等研究学家组织了“战士通讯研究”的工作,以德国公开出版的战时报纸为分析研究对象,弄清报纸内容本质性的事实和趋势,揭示隐含的隐性情报内容,获取了许多军情机密情报并且对事态发展作出情报预测。在“今日头条”中,内容分析则是对文章、视频内容提取关键要素,通过对文本、视频标题关键字进行语义识别,给内容进行分类。“今日头条”的推送系统是典型的层次化文本分类算法,来帮助每篇新闻找到合适的分类,比如:第一大分类是政治、科技、财经、娱乐、体育等,体育类可以下分篮球、足球、网球等,足球又可以下分中国足球和国际足球,中国足球最后下分为甲、中超、国家队等。这一步是对文章进行对这个工作主要目的是对文章进行分类,方便以后对客户推荐。
想要内容分析实现效果,则需要海量的内容信息给算法系统提供有效的筛选和分类。“今日头条”既然是依赖于算法推送新闻,那它背后的数据库必然是强大的,“网页蜘蛛”和“头条号”就是支撑今日头条平台消息来源的重要渠道,其消息来源极其丰富,何时何地有何新鲜事,都能高效率抓取信息。
第一个消息来源的渠道是“网页蜘蛛”,“网页蜘蛛”又叫网页爬虫,头条使用的就是搜索引擎爬虫叫“Bytespider”。它能按照一定的规则,自动爬行抓取互联网的信息或脚本,就像蜘蛛通过蛛网进行捕食,当发现新的信息资源,蜘蛛会立刻出动抓取信息内容并将其收入自己的数据库中。和微信的垂直搜索不同,Bytespider是能够抓取全网内容的全新搜索引擎,因此“今日头条”的搜索引擎功能很全面,搜索的资源很广,资源包容性极高。
Bytespider信息抓取的基本流程如下:首先是网页抓取。Bytespider顺着网页中的超链接,从这个网站爬到另一个网站,通过超链接分析连续访问抓取更多网页。被抓取的网页被称之为网页快照。由于互联网中超链接的应用很普遍,理论上,从一定范围的网页出发,就能搜集到绝大多数的网页。第二步是处理网页。搜索引擎抓到网页后,还要做大量的预处理工作,才能提供检索服务。其中,最重要的就是提取关键词,建立索引库和索引。其他还包括消除重复网页、判断网页类型、分析超链接、计算网页的重要度、丰富度等。第三步提供检索服务。用户输入关键词进行检索,搜索引擎从索引数据库中找到匹配该关键词的网页,为了用户便于判断,除了网页标题和URL外,还会提供一段来自网页的摘要以及其他信息。
3.2推荐维度之二:用户分析
用户分析通过提取用户的有效数据,如用户经常浏览的文字类型、经常搜索的关键字、注册时登记信息的内容等,算法系统可以将每个用户的浏览记录、浏览时间、留言、评论和转发等行为进行关键字提取,最终形成用户画像,以便之后对用户进行文章和视频的精准推送。举个例子,给喜欢阅读“体育”的用户标上“体育”标签;给喜欢“娱乐”的用户标上“娱乐”的标签,这一步的作用是给用户的兴趣进行建模,包括用户对文章和视频的全局热度、分类热度,主题热度,以及关键词热度等。热度信息在大的推荐系统能够解决新闻冷启动问题,帮助新闻实现推送。
用户分析还具有协同特征,它可以在部分程度上帮助解决所谓算法越推越窄的问题。协同特征也就是“联想式”的推送方法,并非只考虑用户已有历史,而是通过用户行为分析不同用户间相似性,比如点击相似、兴趣分类相似、主题相似、兴趣词相似,甚至向量相似,从而扩展模型的探索能力。根据用户之间计算数据的相似程度,把用户细化分类成为不同的目标群体,再向目标群体集中的推送其感兴趣的新闻内容
内容分析和用户分析是相辅相成的,如果没有分析的文本标签,无法得到用户兴趣标签,没有用户的兴趣标签就无法给用户定位实现精准推送。
3.3推荐维度之三:环境分析
环境分析就是根据文章的时效性和接近性推送给相应的用户,比如获取用户当前所在位置是否在旅游区,这个可以通过获取用户的实时位置来实现。还会不断与用户之前经常出现的所在地进行对比等方式确认当前状态,分析出用户是在常住地区还是在旅行。这时若系统检测到用户正在泰山及周边游玩,则可能会相应推送泰山的相关文章、周边的交通新闻和天气信息等等。
通过上面三个推荐维度可以作为数据基础,分析当前用户处于什么环境,结合用户画像以及文章的内容分类来推荐,尽量做到推送的内容都是用户所感兴趣的。算法系统还会通过内容分类、分析抽取,把文本相似度高的文章,包括新闻主题、内容相似的文章进行消重,解决推送重复的问题,进一步对目标用户进行精确且不重复的内容推荐。最后过滤质量低俗色情的内容,以免造成平台会有负面倾向。
3.4“今日头条”新闻推荐算法的价值取向
3.4.1“用户为上”
“今日头条”的算法推送是站在用户的立场上的,以满足用户个性化和推送的精准性,“今日头条”也重新衡量了新闻价值标准:以用户为上,用户对新闻内容和阅读方式的满意度便是平台推送新闻的价值宗旨。传统媒体时代,只有报纸和电视,有什么受众就得看什么,而如今“今日头条”根据用户兴趣去进行推送。算法推送平台用户范围广,很多用户热衷关注负面,也有许多用户都有窥视欲和好奇心,喜欢无聊八卦和无聊新闻,而且在好奇心作用下用户都有从众心理。这使得生产者过度去迎合受众,只要是用户喜欢看就可以发表在“今日头条”上。
3.4.2“算法主导”
“今日头条”更注重技术分发,生产者是用户,受众者也是用户,这样一来内容监管和分发就很困难。算法推送机制根据用户爱好进行推送,这样生产的内容快、也无疑会加速内容配送效率。在算法推送模型中,用户点击频率、阅读时间、点赞评论以及转发在算法时代都是可以进行量化的目标。在这样情况下生产的内容,想要获得较大点击率和推送率,需要标题才能吸引用户,因为用户在平台一眼能看到的就是标题和配图。标题和配图决定用户是否会打开你的内容,这导致许多内容生产者在编辑新闻标题时陷入标题党的怪圈,还有导致低俗内容的呈现,以制造冲突制造悬念贴标签等方式引用户点击,意图把自己的文章做成爆文。对于海量的信息内容,即使今日头条数据和智能推荐做的再好,目前来说也难以抵挡海量的垃圾信息。
4.算法推送新闻引发的伦理问题
在如今网络时代的传播思维中,“用户为上”、“算法主导”的新闻价值取向已经在算法聚合类平台成为了普遍,算法推送技术作为吸引用户的手段,搭建起一个充满诱导的媒介环境,以此增加用户对平台的粘性。算法推送技术在获取信息、传播速度等方面与以往相比有着跨时代的进步,但与此同时,由于算法推送技术的加入,衍生出新的伦理问题,并且日渐复杂化。
4.1算法推送引发的伦理问题
4.1.1算法推送过于机械化,没有思考能力
单向的算法推荐对用户来说经常会带来内容杂乱无章、信息量过大、信息价值低等问题。从逻辑讲,算法只是从关键字的检索匹配来完成统计推荐,但对新闻报道或文学作品具有艺术性、专业性的内容来说,是不能保证推送的质量的。算法方面,目前主要基于匹配检索与统计,大部分都是个人关注的信息类型和标签,难以达到较好的推送效果。一千个人眼里有一千个哈姆雷特,但是计算机只有只有一个。算法技术过于注重机械化的统计,只根据关键词来推荐用户,对我们中国具有博大精深的中国文字文化底蕴,推荐算法是远远不够的。整个新闻客户端显得像是一个菜市场,没有态度、没有风格,阅读感受单一化,呈现了碎片化的特点。新闻不只是让用户能够了解身边发生的新鲜事,还有宣传正面思想和传播正能量的作用,新闻应该还要给人们带来新的思考。让机器做出正确判断很简单,但是让机器综合心理学、社会学、乃至某细分领域内的规则做出判断还要正确地引导受众则很难,正如现在算法技术还不能完成一篇富有人文性、文学性和批判性的深度报道,它止步在了碎片式的、表层的传播范畴。
4.1.2容易引起“信息茧房”效应
“信息茧房”这一概念是凯斯.桑斯坦在《信息乌托邦》一书中提出的。意指受众在过度的信息自我选择之中,这样会降低接触外界其他信息的可能,从而将自己的生活桎梏于蚕茧一般的“蚕房”中的现象。人们的信息领域会习惯性被自己的兴趣引导,信息窄化带来了受众对信息接收的单一性,这种单一性的可能会使受众陷入循环,加重受众信息同质化。
4.1.3算法推送的“伪中立性”
客观和全面是新闻伦理的基本要求,新闻从业者必须从可好信息源来获取真实的信息,以客观的态度反应现实。我们惯常认为,互联网技术服务商是技术中立者,不需要承担约束大众媒体的社会责任,然而当信息把关人又新闻编辑转变为算法工程师,传统的媒介伦理似乎已经失效。算法具有商业倾向性,“中立性”是算法平台用以逃避媒体责任的理由,给大众媒介造成传播乱象,如此一来更像是一场算法平台“肆意妄为又不想负责”的诡辩。
算法平台的信息源是经过选择和过滤的,“头条号”的内容占“今日头条”整个信息系统的绝大部分,然而在“人人都可以做新闻人”的时代,头条号平台是一个开放的网络媒介环境,存在大量的偏见和错误的认知。无论是“今日头条”平台设立的算法规则,还是其他爬虫的抓取的关键词,算法系统的信息源很多是具有目的性的、有偏见和非客观的信息,所以信息源不能直接作用于用户。因此,筛选算法系统的信息源与传统的人工编辑相比较,范围极广且很难把关,若算法被恶意利用,那么使整个传播系统将会被轻易控制。
4.1.4算法推送里的“议程设置”
原议程设置功能揭示的重要内涵是:“受众对新闻的看法虽然被大众媒体议程设置功能所主导,但其更深刻的是议程设置给大众媒体新闻带来放大与延伸,从而使受众对新闻选择做出能动性修正,让受众在满足需求和媒介依赖中逐渐培养出的潜在认同感”。
推送算法技术在互联网平台的运用,使原来传统媒体主导的议程设置过程发生了变化,伴随着传播权的转移、公众参与度的提高和信息量剧增等原因导致议程设置功逐渐能减弱。过往传统新闻的内容是由编辑有选择地进行报道后再呈现在受众面前的,而个性化新闻推送是用户自己来选择看哪一方面的内容,而这一环节中,天然的技术赋权将传播权从传统媒体下放至平台的用户,使得受众和社会的连接无需依赖传统媒介,新闻媒体作为把关人的作用和议程设置功能都在减弱。
4.2算法新闻治理缺陷下的算法权利异化
算法作为人工智能的基石之一,是“一种有限、确定、有效并适合用计算机程序来实现的解决问题的方法,是计算机科学的基础”。近年来,伴随人工智能深度学习算法取得的重大突破和大数据时代的到来,人工智能的应用场景不断拓展,人工智能时代正逐渐从想象成为现实。借助于海量的大数据和具备强大计算能力的硬件设备,拥有深度学习算法的人工智能机器可以通过自主学习和强化训练来不断提升自身的能力,解决很多人类难以有效应对的治理难题。伴随人工能算法在国家和社会治理中重要性的日渐凸显,国家和社会对于算法的依赖也逐渐加深,一种新型的权力形态——算法权力也随之出现。
可以把算法权利分为四种:数据主权、算法设计权、研发的资本权和算法控制权。由于前三种权利都是单向的、算法开发者赋予算法的权利,是属于算法开发者的,与算法分发平台呈现的效果没有直接的影响,所以本文将着重论述算法控制权。
算法控制权是双向的,用户是算法技术数据行为的提供者,同时又是被算法技术控制的受害者。例如我们看到“今日头条”会通过推送算法来监管用户的发布和浏览行为,同时平台会通过算法决策系统来实现内容的发布去引导用户。算法控制权当然是一种天然技术赋予的权利,但算法控制权是在用户提供数据行为的情况下才得以实现的,因此算法控制权既存在内容生产权,同时有要尊重和保护算法相对人的义务。
正因为如此,算法技术被认为是一种双刃剑,一方面算法能够做出精准的行为预测,可以为管理者提供非常好的循环干预机制;对于公共行为主体来说,可以通过对大数据的应用来解决社会治理问题,对于私人主体来说可以借助数据来提供个性化和定制化的服务;另一方面,算法技术存在着诸如利益和风险不对称等问题,而且由于算法技术发展的超前性,新科技的创造者具备不对称的信息和技术优势,能够按照自身利益的需求来塑造在平台上的算法推送逻辑和社会系统,这带来了监管的不确定性。人们要通过集体行为去承担社会责任,通过这样的方式规制算法权利,可以让我们能够对算法分发系统的意义和价值得到更深刻的思考。
㈡ 如何系统地自学 python
是否非常想学好 Python,一方面被琐事纠缠,一直没能动手,另一方面,担心学习成本太高,心里默默敲着退堂鼓?
幸运的是,Python 是一门初学者友好的编程语言,想要完全掌握它,你不必花上太多的时间和精力。
Python 的设计哲学之一就是简单易学,体现在两个方面:
语法简洁明了:相对 Ruby 和 Perl,它的语法特性不多不少,大多数都很简单直接,不玩儿玄学。
切入点很多:Python 可以让你可以做很多事情,科学计算和数据分析、爬虫、Web 网站、游戏、命令行实用工具等等等等,总有一个是你感兴趣并且愿意投入时间的。
废话不多说,学会一门语言的捷径只有一个: Getting Started
¶ 起步阶段
任何一种编程语言都包含两个部分:硬知识和软知识,起步阶段的主要任务是掌握硬知识。
硬知识
“硬知识”指的是编程语言的语法、算法和数据结构、编程范式等,例如:变量和类型、循环语句、分支、函数、类。这部分知识也是具有普适性的,看上去是掌握了一种语法,实际是建立了一种思维。例如:让一个 Java 程序员去学习 Python,他可以很快的将 Java 中的学到的面向对象的知识 map 到 Python 中来,因此能够快速掌握 Python 中面向对象的特性。
如果你是刚开始学习编程的新手,一本可靠的语法书是非常重要的。它看上去可能非常枯燥乏味,但对于建立稳固的编程思维是必不可少。
下面列出了一些适合初学者入门的教学材料:
廖雪峰的 Python 教程 Python 中文教程的翘楚,专为刚刚步入程序世界的小白打造。
笨方法学 Python 这本书在讲解 Python 的语法成分时,还附带大量可实践的例子,非常适合快速起步。
The Hitchhiker’s Guide to Python! 这本指南着重于 Python 的最佳实践,不管你是 Python 专家还是新手,都能获得极大的帮助。
Python 的哲学:
学习也是一样,虽然推荐了多种学习资料,但实际学习的时候,最好只选择其中的一个,坚持看完。
必要的时候,可能需要阅读讲解数据结构和算法的书,这些知识对于理解和使用 Python 中的对象模型有着很大的帮助。
软知识
“软知识”则是特定语言环境下的语法技巧、类库的使用、IDE的选择等等。这一部分,即使完全不了解不会使用,也不会妨碍你去编程,只不过写出的程序,看上去显得“傻”了些。
对这些知识的学习,取决于你尝试解决的问题的领域和深度。对初学者而言,起步阶段极易走火,或者在选择 Python 版本时徘徊不决,一会儿看 2.7 一会儿又转到 3.0,或者徜徉在类库的大海中无法自拔,Scrapy,Numpy,Django 什么都要试试,或者参与编辑器圣战、大括号缩进探究、操作系统辩论赛等无意义活动,或者整天跪舔语法糖,老想着怎么一行代码把所有的事情做完,或者去构想圣洁的性能安全通用性健壮性全部满分的解决方案。
很多“大牛”都会告诫初学者,用这个用那个,少走弯路,这样反而把初学者推向了真正的弯路。
还不如告诉初学者,学习本来就是个需要你去走弯路出 Bug,只能脚踏实地,没有奇迹只有狗屎的过程。
选择一个方向先走下去,哪怕脏丑差,走不动了再看看有没有更好的解决途径。
自己走了弯路,你才知道这么做的好处,才能理解为什么人们可以手写状态机去匹配却偏要发明正则表达式,为什么面向过程可以解决却偏要面向对象,为什么我可以操纵每一根指针却偏要自动管理内存,为什么我可以嵌套回调却偏要用 Promise...
更重要的是,你会明白,高层次的解决方法都是对低层次的封装,并不是任何情况下都是最有效最合适的。
技术涌进就像波浪一样,那些陈旧的封存已久的技术,消退了迟早还会涌回的。就像现在移动端应用、手游和 HTML5 的火热,某些方面不正在重演过去 PC 的那些历史么?
因此,不要担心自己走错路误了终身,坚持并保持进步才是正道。
起步阶段的核心任务是掌握硬知识,软知识做适当了解,有了稳固的根,粗壮的枝干,才能长出浓密的叶子,结出甜美的果实。
¶ 发展阶段
完成了基础知识的学习,必定会感到一阵空虚,怀疑这些语法知识是不是真的有用。
没错,你的怀疑是非常正确的。要让 Python 发挥出它的价值,当然不能停留在语法层面。
发展阶段的核心任务,就是“跳出 Python,拥抱世界”。
在你面前会有多个分支:科学计算和数据分析、爬虫、Web 网站、游戏、命令行实用工具等等等等,这些都不是仅仅知道 Python 语法就能解决的问题。
拿爬虫举例,如果你对计算机网络,HTTP 协议,HTML,文本编码,JSON 一无所知,你能做好这部分的工作么?而你在起步阶段的基础知识也同样重要,如果你连循环递归怎么写都还要查文档,连 BFS 都不知道怎么实现,这就像工匠做石凳每次起锤都要思考锤子怎么使用一样,非常低效。
在这个阶段,不可避免要接触大量类库,阅读大量书籍的。
类库方面
“Awesome Python 项目”:vinta/awesome-python · GitHub
这里列出了你在尝试解决各种实际问题时,Python 社区已有的工具型类库,如下图所示:
vinta/awesome-python
你可以按照实际需求,寻找你需要的类库。
至于相关类库如何使用,必须掌握的技能便是阅读文档。由于开源社区大多数文档都是英文写成的,所以,英语不好的同学,需要恶补下。
书籍方面
这里我只列出一些我觉得比较有一些帮助的书籍,详细的请看豆瓣的书评:
科学和数据分析:
❖“集体智慧编程”:集体智慧编程 (豆瓣)
❖“数学之美”:数学之美 (豆瓣)
❖“统计学习方法”:统计学习方法 (豆瓣)
❖“Pattern Recognition And Machine Learning”:Pattern Recognition And Machine Learning (豆瓣)
❖“数据科学实战”:数据科学实战 (豆瓣)
❖“数据检索导论”:信息检索导论 (豆瓣)
爬虫:
❖“HTTP 权威指南”:HTTP权威指南 (豆瓣)
Web 网站:
❖“HTML & CSS 设计与构建网站”:HTML & CSS设计与构建网站 (豆瓣)
...
列到这里已经不需要继续了。
聪明的你一定会发现上面的大部分书籍,并不是讲 Python 的书,而更多的是专业知识。
事实上,这里所谓“跳出 Python,拥抱世界”,其实是发现 Python 和专业知识相结合,能够解决很多实际问题。这个阶段能走到什么程度,更多的取决于自己的专业知识。
¶ 深入阶段
这个阶段的你,对 Python 几乎了如指掌,那么你一定知道 Python 是用 C 语言实现的。
可是 Python 对象的“动态特征”是怎么用相对底层,连自动内存管理都没有的C语言实现的呢?这时候就不能停留在表面了,勇敢的拆开 Python 的黑盒子,深入到语言的内部,去看它的历史,读它的源码,才能真正理解它的设计思路。
这里推荐一本书:
“Python 源码剖析”:Python源码剖析 (豆瓣)
这本书把 Python 源码中最核心的部分,给出了详细的阐释,不过阅读此书需要对 C 语言内存模型和指针有着很好的理解。
另外,Python 本身是一门杂糅多种范式的动态语言,也就是说,相对于 C 的过程式、 Haskell 等的函数式、Java 基于类的面向对象而言,它都不够纯粹。换而言之,编程语言的“道学”,在 Python 中只能有限的体悟。学习某种编程范式时,从那些面向这种范式更加纯粹的语言出发,才能有更深刻的理解,也能了解到 Python 语言的根源。
这里推荐一门公开课
“编程范式”:斯坦福大学公开课:编程范式
讲师高屋建瓴,从各种编程范式的代表语言出发,给出了每种编程范式最核心的思想。
值得一提的是,这门课程对C语言有非常深入的讲解,例如C语言的范型和内存管理。这些知识,对阅读 Python 源码也有大有帮助。
Python 的许多最佳实践都隐藏在那些众所周知的框架和类库中,例如 Django、Tornado 等等。在它们的源代码中淘金,也是个不错的选择。
¶ 最后的话
每个人学编程的道路都是不一样的,其实大都殊途同归,没有迷路的人只有不能坚持的人!
希望想学 Python 想学编程的同学,不要犹豫了,看完这篇文章,
Just Getting Started !!!
㈢ java中CRC算法是个什么东东
CRC算法实现有2种方法,一、查表法,二、直接计算,查表法的计算速度相对来说比较快,本人介绍的方法是直接计算法,用了2种方法实现,都是面向对象进行算法的封装。
package com.wms.serial;
/**
* @author lino
* @version 2006/08/25
*/
public class CRC16{
public int value;
public CRC16()
{
value = 0;
}
/** update CRC with byte b */
public void update(byte aByte)
{
int a, b;
a = (int) aByte;
for (int count = 7; count >=0; count--) {
a = a << 1;
b = (a >>>8) & 1;
if ((value & 0x8000) != 0) {
value = ((value << 1) + b) ^ 0x1021;
} else {
value = (value << 1) + b;
}
}
value = value & 0xffff;
return;
}
/** reset CRC value to 0 */
public void reset()
{
value = 0;
}
public int getValue()
{
return value;
}
public static void main(String[] args) {
CRC16 crc16 = new CRC16();
byte[] b = new byte[]{
//(byte) 0xF0,(byte)0xF0,(byte)0xF0,(byte)0x72
(byte) 0x2C,(byte)0x00,(byte)0xFF,(byte)0xFE
,(byte) 0xFE,(byte)0x04,(byte)0x00,(byte)0x00
,(byte) 0x00,(byte)0x00
};
for (int k = 0; k < b.length; k++)
{
crc16.update(b[k]);
}
System.out.println(Integer.toHexString(crc16.getValue()));
System.out.println(Integer.toHexString(b.length));
}
}
package com.wms.serial;
public class CRC162 {
public static final void main(String[] args){
CRC162 crc16 = new CRC162();
byte[] b = new byte[]{
//(byte) 0xF0,(byte)0xF0,(byte)0xF0,(byte)0x72
(byte) 0x2C,(byte)0x00,(byte)0xFF,(byte)0xFE
,(byte) 0xFE,(byte)0x04,(byte)0x00,(byte)0x00
,(byte) 0x00,(byte)0x00
};
System.out.println(Integer.toHexString(crc16.encode(b)));
//再把这个2f49替换成b数组的最后两个字节的数组,生成一个新的数组b2
byte[] b2 = new byte[]{
//(byte) 0xF0,(byte)0xF0,(byte)0xF0,(byte)0x72
(byte) 0x2C,(byte)0x00,(byte)0xFF,(byte)0xFE
,(byte) 0xFE,(byte)0x04,(byte)0x00,(byte)0x00
,(byte) 0x2f,(byte)0x49
};
System.out.println(Integer.toHexString(crc16.encode(b2))); //算出来是 0
//你可以自已构造一些byte进行加解密试试
}
public short encode(byte[] b){
short CRC_x = 0;
int pp = 65536; // 1<<16;
int pp2 = 69665; // (1<<16) + (1<<12) + (1<<5) + 1
for(int i=0;i<b.length;i++){
for(int j=0;j<8;j++){
CRC_x = (short)((CRC_x<<1) + (((b[i]<<j)&0x80)>>7));
if((CRC_x/pp) == 1){
CRC_x=(short)(CRC_x^pp2);
}
}
}
return CRC_x;
}
}
㈣ JAVA加密解密要用到的JAR包
一般java加密解密都需要jar包的,不同的加解密方式对应
不同的加解密包,一般加解密方式有这么几种。
资料链接:http://www.doc88.com/p-19252566394.html
㈤ Java代码优化有哪些常用的方法
1、 尽量指定类的final修饰符 带有final修饰符的类是不可派生的。
在Java核心API中,有许多应用final的例子,例如java.lang.String。为String类指定final防止了人们覆盖length()方法。另外,如果指定一个类为final,则该类所有的方法都是final。Java编译器会寻找机会内联(inline)所有的final方法(这和具体的编译器实现有关)。此举能够使性能平均提高50% 。
2、 尽量重用对象。
特别是String 对象的使用中,出现字符串连接情况时应用StringBuffer 代替。由于系统不仅要花时间生成对象,以后可能还需花时间对这些对象进行垃圾回收和处理。因此,生成过多的对象将会给程序的性能带来很大的影响。
3、 尽量使用局部变量,调用方法时传递的参数以及在调用中创建的临时变量都保存在栈(Stack)中,速度较快。
其他变量,如静态变量、实例变量等,都在堆(Heap)中创建,速度较慢。另外,依赖于具体的编译器/JVM,局部变量还可能得到进一步优化。请参见《尽可能使用堆栈变量》。
4、 不要重复初始化变量
默认情况下,调用类的构造函数时, Java会把变量初始化成确定的值:所有的对象被设置成null,整数变量(byte、short、int、long)设置成0,float和double变量设置成0.0,逻辑值设置成false。当一个类从另一个类派生时,这一点尤其应该注意,因为用new关键词创建一个对象时,构造函数链中的所有构造函数都会被自动调用。
5、 在JAVA + ORACLE 的应用系统开发中,java中内嵌的SQL语句尽量使用大写的形式,以减轻ORACLE解析器的解析负担。
6、 Java 编程过程中,进行数据库连接、I/O流操作时务必小心,在使用完毕后,即使关闭以释放资源。
因为对这些大对象的操作会造成系统大的开销,稍有不慎,会导致严重的后果。
7、 由于JVM的有其自身的GC机制,不需要程序开发者的过多考虑,从一定程度上减轻了开发者负担,但同时也遗漏了隐患,过分的创建对象会消耗系统的大量内存,严重时会导致内存泄露,因此,保证过期对象的及时回收具有重要意义。
JVM回收垃圾的条件是:对象不在被引用;然而,JVM的GC并非十分的机智,即使对象满足了垃圾回收的条件也不一定会被立即回收。所以,建议我们在对象使用完毕,应手动置成null。
8、 在使用同步机制时,应尽量使用方法同步代替代码块同步。
9、 尽量减少对变量的重复计算
例如:for(int i = 0;i < list.size; i ++) {
…
}
应替换为:
for(int i = 0,int len = list.size();i < len; i ++){
…
}
10、尽量采用lazy loading 的策略,即在需要的时候才开始创建。
例如: String str = “aaa”;
if(i == 1) {
list.add(str);
}
应替换为:
if(i == 1) {
String str = “aaa”;
list.add(str);
}
11、慎用异常
异常对性能不利。抛出异常首先要创建一个新的对象。Throwable接口的构造函数调用名为fillInStackTrace()的本地(Native)方法,fillInStackTrace()方法检查堆栈,收集调用跟踪信息。只要有异常被抛出,VM就必须调整调用堆栈,因为在处理过程中创建了一个新的对象。 异常只能用于错误处理,不应该用来控制程序流程。
12、不要在循环中使用:
Try {
} catch() {
}
应把其放置在最外层。
13、StringBuffer 的使用:
StringBuffer表示了可变的、可写的字符串。
有三个构造方法 :
StringBuffer (); //默认分配16个字符的空间
StringBuffer (int size); //分配size个字符的空间
StringBuffer (String str); //分配16个字符+str.length()个字符空间
你可以通过StringBuffer的构造函数来设定它的初始化容量,这样可以明显地提升性能。
这里提到的构造函数是StringBuffer(int length),length参数表示当前的StringBuffer能保持的字符数量。你也可以使用ensureCapacity(int minimumcapacity)方法在StringBuffer对象创建之后设置它的容量。首先我们看看StringBuffer的缺省行为,然后再找出一条更好的提升性能的途径。
StringBuffer在内部维护一个字符数组,当你使用缺省的构造函数来创建StringBuffer对象的时候,因为没有设置初始化字符长度,StringBuffer的容量被初始化为16个字符,也就是说缺省容量就是16个字符。当StringBuffer达到最大容量的时候,它会将自身容量增加到当前的2倍再加2,也就是(2*旧值+2)。如果你使用缺省值,初始化之后接着往里面追加字符,在你追加到第16个字符的时候它会将容量增加到34(2*16+2),当追加到34个字符的时候就会将容量增加到70(2*34+2)。无论何事只要StringBuffer到达它的最大容量它就不得不创建一个新的字符数组然后重新将旧字符和新字符都拷贝一遍――这也太昂贵了点。所以总是给StringBuffer设置一个合理的初始化容量值是错不了的,这样会带来立竿见影的性能增益。StringBuffer初始化过程的调整的作用由此可见一斑。所以,使用一个合适的容量值来初始化StringBuffer永远都是一个最佳的建议。
14、合理的使用Java类 java.util.Vector。
简单地说,一个Vector就是一个java.lang.Object实例的数组。Vector与数组相似,它的元素可以通过整数形式的索引访问。但是,Vector类型的对象在创建之后,对象的大小能够根据元素的增加或者删除而扩展、缩小。请考虑下面这个向Vector加入元素的例子:
Object bj = new Object();
Vector v = new Vector(100000);
for(int I=0;
I<100000; I++) { v.add(0,obj); }
除非有绝对充足的理由要求每次都把新元素插入到Vector的前面,否则上面的代码对性能不利。在默认构造函数中,Vector的初始存储能力是10个元素,如果新元素加入时存储能力不足,则以后存储能力每次加倍。Vector类就对象StringBuffer类一样,每次扩展存储能力时,所有现有的元素都要复制到新的存储空间之中。下面的代码片段要比前面的例子快几个数量级:
Object bj = new Object();
Vector v = new Vector(100000);
for(int I=0; I<100000; I++) { v.add(obj); }
同样的规则也适用于Vector类的remove()方法。由于Vector中各个元素之间不能含有“空隙”,删除除最后一个元素之外的任意其他元素都导致被删除元素之后的元素向前移动。也就是说,从Vector删除最后一个元素要比删除第一个元素“开销”低好几倍。
假设要从前面的Vector删除所有元素,我们可以使用这种代码:
for(int I=0; I<100000; I++)
{
v.remove(0);
}
但是,与下面的代码相比,前面的代码要慢几个数量级:
for(int I=0; I<100000; I++)
{
v.remove(v.size()-1);
}
从Vector类型的对象v删除所有元素的最好方法是:
v.removeAllElements();
假设Vector类型的对象v包含字符串“Hello”。考虑下面的代码,它要从这个Vector中删除“Hello”字符串:
String s = "Hello";
int i = v.indexOf(s);
if(I != -1) v.remove(s);
这些代码看起来没什么错误,但它同样对性能不利。在这段代码中,indexOf()方法对v进行顺序搜索寻找字符串“Hello”,remove(s)方法也要进行同样的顺序搜索。改进之后的版本是:
String s = "Hello";
int i = v.indexOf(s);
if(I != -1) v.remove(i);
这个版本中我们直接在remove()方法中给出待删除元素的精确索引位置,从而避免了第二次搜索。一个更好的版本是:
String s = "Hello"; v.remove(s);
最后,我们再来看一个有关Vector类的代码片段:
for(int I=0; I++;I < v.length)
如果v包含100,000个元素,这个代码片段将调用v.size()方法100,000次。虽然size方法是一个简单的方法,但它仍旧需要一次方法调用的开销,至少JVM需要为它配置以及清除堆栈环境。在这里,for循环内部的代码不会以任何方式修改Vector类型对象v的大小,因此上面的代码最好改写成下面这种形式:
int size = v.size(); for(int I=0; I++;I<size)
虽然这是一个简单的改动,但它仍旧赢得了性能。毕竟,每一个CPU周期都是宝贵的。
15、当复制大量数据时,使用System.array()命令。
int[] src={1,3,5,6,7,8};
int[] dest = new int[6];
System.array(src, 0, dest, 0, 6);
src:源数组; srcPos:源数组要复制的起始位置;
dest:目的数组; destPos:目的数组放置的起始位置;
length:复制的长度.
注意:src and dest都必须是同类型或者可以进行转换类型的数组.
16、代码重构:增强代码的可读性。
public class ShopCart {
private List carts ;
…
public void add (Object item) {
if(carts == null) {
carts = new ArrayList();
}
crts.add(item);
}
public void remove(Object item) {
if(carts. contains(item)) {
carts.remove(item);
}
}
public List getCarts() {
//返回只读列表
return Collections.unmodifiableList(carts);
}
//不推荐这种方式
//this.getCarts().add(item);
}
17、不用new关键词创建类的实例
用new关键词创建类的实例时,构造函数链中的所有构造函数都会被自动调用。但如果一个对象实现了Cloneable接口,我们可以调用它的clone()方法。clone()方法不会调用任何类构造函数。
在使用设计模式(Design Pattern)的场合,如果用Factory模式创建对象,则改用clone()方法创建新的对象实例非常简单。例如,下面是Factory模式的一个典型实现:
public static Credit getNewCredit() {
return new Credit();
}
改进后的代码使用clone()方法,如下所示:
private static Credit BaseCredit = new Credit();
public static Credit getNewCredit() {
return (Credit) BaseCredit.clone();
}
上面的思路对于数组处理同样很有用。
18、乘法和除法
考虑下面的代码:
for (val = 0; val < 100000; val +=5) {
alterX = val * 8; myResult = val * 2;
}
用移位操作替代乘法操作可以极大地提高性能。下面是修改后的代码:
for (val = 0; val < 100000; val += 5) {
alterX = val << 3; myResult = val << 1;
}
修改后的代码不再做乘以8的操作,而是改用等价的左移3位操作,每左移1位相当于乘以2。相应地,右移1位操作相当于除以2。值得一提的是,虽然移位操作速度快,但可能使代码比较难于理解,所以最好加上一些注释。
19、在JSP页面中关闭无用的会话。
一个常见的误解是以为session在有客户端访问时就被创建,然而事实是直到某server端程序调用HttpServletRequest.getSession(true)这样的语句时才被创建,注意如果JSP没有显示的使用 <> 关闭session,则JSP文件在编译成Servlet时将会自动加上这样一条语句HttpSession session = HttpServletRequest.getSession(true);这也是JSP中隐含的session对象的来历。由于session会消耗内存资源,因此,如果不打算使用session,应该在所有的JSP中关闭它。
对于那些无需跟踪会话状态的页面,关闭自动创建的会话可以节省一些资源。使用如下page指令:<%@ page session="false"%>
20、JDBC与I/O
如果应用程序需要访问一个规模很大的数据集,则应当考虑使用块提取方式。默认情况下,JDBC每次提取32行数据。举例来说,假设我们要遍历一个5000行的记录集,JDBC必须调用数据库157次才能提取到全部数据。如果把块大小改成512,则调用数据库的次数将减少到10次。
21、Servlet与内存使用
许多开发者随意地把大量信息保存到用户会话之中。一些时候,保存在会话中的对象没有及时地被垃圾回收机制回收。从性能上看,典型的症状是用户感到系统周期性地变慢,却又不能把原因归于任何一个具体的组件。如果监视JVM的堆空间,它的表现是内存占用不正常地大起大落。
解决这类内存问题主要有二种办法。第一种办法是,在所有作用范围为会话的Bean中实现HttpSessionBindingListener接口。这样,只要实现valueUnbound()方法,就可以显式地释放Bean使用的资源。
另外一种办法就是尽快地把会话作废。大多数应用服务器都有设置会话作废间隔时间的选项。另外,也可以用编程的方式调用会话的setMaxInactiveInterval()方法,该方法用来设定在作废会话之前,Servlet容器允许的客户请求的最大间隔时间,以秒计。
22、使用缓冲标记
一些应用服务器加入了面向JSP的缓冲标记功能。例如,BEA的WebLogic Server从6.0版本开始支持这个功能,Open Symphony工程也同样支持这个功能。JSP缓冲标记既能够缓冲页面片断,也能够缓冲整个页面。当JSP页面执行时,如果目标片断已经在缓冲之中,则生成该片断的代码就不用再执行。页面级缓冲捕获对指定URL的请求,并缓冲整个结果页面。对于购物篮、目录以及门户网站的主页来说,这个功能极其有用。对于这类应用,页面级缓冲能够保存页面执行的结果,供后继请求使用。
23、选择合适的引用机制
在典型的JSP应用系统中,页头、页脚部分往往被抽取出来,然后根据需要引入页头、页脚。当前,在JSP页面中引入外部资源的方法主要有两种:include指令,以及include动作。
include指令:例如<%@ include file="right.html" %>。该指令在编译时引入指定的资源。在编译之前,带有include指令的页面和指定的资源被合并成一个文件。被引用的外部资源在编译时就确定,比运行时才确定资源更高效。
include动作:例如<jsp:include page="right.jsp" />。该动作引入指定页面执行后生成的结果。由于它在运行时完成,因此对输出结果的控制更加灵活。但时,只有当被引用的内容频繁地改变时,或者在对主页面的请求没有出现之前,被引用的页面无法确定时,使用include动作才合算。
24、及时清除不再需要的会话
为了清除不再活动的会话,许多应用服务器都有默认的会话超时时间,一般为30分钟。当应用服务器需要保存更多会话时,如果内存容量不足,操作系统会把部分内存数据转移到磁盘,应用服务器也可能根据“最近最频繁使用”(Most Recently Used)算法把部分不活跃的会话转储到磁盘,甚至可能抛出“内存不足”异常。在大规模系统中,串行化会话的代价是很昂贵的。当会话不再需要时,应当及时调用HttpSession.invalidate()方法清除会话。HttpSession.invalidate()方法通常可以在应用的退出页面调用。
25、不要将数组声明为:public static final 。
26、HashMap的遍历效率讨论
经常遇到对HashMap中的key和value值对的遍历操作,有如下两种方法:
Map<String, String[]> paraMap = new HashMap<String, String[]>();
//第一个循环
Set<String> appFieldDefIds = paraMap.keySet();
for (String appFieldDefId : appFieldDefIds) {
String[] values = paraMap.get(appFieldDefId);
......
}
//第二个循环
for(Entry<String, String[]> entry : paraMap.entrySet()){
String appFieldDefId = entry.getKey();
String[] values = entry.getValue();
.......
}
第一种实现明显的效率不如第二种实现。
分析如下 Set<String> appFieldDefIds = paraMap.keySet(); 是先从HashMap中取得keySet
代码如下:
public Set<K> keySet() {
Set<K> ks = keySet;
return (ks != null ? ks : (keySet = new KeySet()));
}
private class KeySet extends AbstractSet<K> {
public Iterator<K> iterator() {
return newKeyIterator();
}
public int size() {
return size;
}
public boolean contains(Object o) {
return containsKey(o);
}
public boolean remove(Object o) {
return HashMap.this.removeEntryForKey(o) != null;
}
public void clear() {
HashMap.this.clear();
}
}
其实就是返回一个私有类KeySet, 它是从AbstractSet继承而来,实现了Set接口。
再来看看for/in循环的语法
for(declaration : expression)
statement
在执行阶段被翻译成如下各式
for(Iterator<E> #i = (expression).iterator(); #i.hashNext();){
declaration = #i.next();
statement
}
因此在第一个for语句for (String appFieldDefId : appFieldDefIds) 中调用了HashMap.keySet().iterator()
而这个方法调用了newKeyIterator()
Iterator<K> newKeyIterator() {
return new KeyIterator();
}
private class KeyIterator extends HashIterator<K> {
public K next() {
return nextEntry().getKey();
}
}
所以在for中还是调用了
在第二个循环for(Entry<String, String[]> entry : paraMap.entrySet())中使用的Iterator是如下的一个内部
类
private class EntryIterator extends HashIterator<Map.Entry<K,V>> {
public Map.Entry<K,V> next() {
return nextEntry();
}
}
此时第一个循环得到key,第二个循环得到HashMap的Entry效率就是从循环里面体现出来的第二个循环此致可以直接取key和value值而第一个循环还是得再利用HashMap的get(Object key)来取value值现在看看HashMap的get(Object key)方法
public V get(Object key) {
Object k = maskNull(key);
int hash = hash(k);
int i = indexFor(hash, table.length); //Entry[] table
Entry<K,V> e = table;
while (true) {
if (e == null)
return null;
if (e.hash == hash && eq(k, e.key))
return e.value;
e = e.next;
}
}
其实就是再次利用Hash值取出相应的Entry做比较得到结果,所以使用第一中循环相当于两次进入HashMap的Entry
中而第二个循环取得Entry的值之后直接取key和value,效率比第一个循环高。其实按照Map的概念来看也应该是用第二个循环好一点,它本来就是key和value的值对,将key和value分开操作在这里不是个好选择。
27、array(数组) 和 ArryList的使用
array([]):最高效;但是其容量固定且无法动态改变;
ArrayList:容量可动态增长;但牺牲效率;
基于效率和类型检验,应尽可能使用array,无法确定数组大小时才使用ArrayList!
ArrayList是Array的复杂版本
ArrayList内部封装了一个Object类型的数组,从一般的意义来说,它和数组没有本质的差别,甚至于ArrayList的许多方法,如Index、IndexOf、Contains、Sort等都是在内部数组的基础上直接调用Array的对应方法。
ArrayList存入对象时,抛弃类型信息,所有对象屏蔽为Object,编译时不检查类型,但是运行时会报错。
注:jdk5中加入了对泛型的支持,已经可以在使用ArrayList时进行类型检查。
从这一点上看来,ArrayList与数组的区别主要就是由于动态增容的效率问题了
28、尽量使用HashMap 和ArrayList ,除非必要,否则不推荐使用HashTable和Vector ,后者由于使用同步机制,而导致了性能的开销。
29、StringBuffer 和StringBuilder的区别:
java.lang.StringBuffer线程安全的可变字符序列。一个类似于 String 的字符串缓冲区,但不能修改。
StringBuilder。与该类相比,通常应该优先使用 java.lang.StringBuilder类,因为它支持所有相同的操作,但由于它不执行同步,所以速度更快。为了获得更好的性能,在构造 StirngBuffer 或 StirngBuilder 时应尽可能指定它的容量。当然,如果你操作的字符串长度不超过 16 个字符就不用了。 相同情况下使用 StirngBuilder 相比使用 StringBuffer 仅能获得 10%-15% 左右的性能提升,但却要冒多线程不安全的风险。而在现实的模块化编程中,负责某一模块的程序员不一定能清晰地判断该模块是否会放入多线程的环境中运行,因此:除非你能确定你的系统的瓶颈是在 StringBuffer 上,并且确定你的模块不会运行在多线程模式下,否则还是用 StringBuffer 吧。
30、尽量避免使用split
除非是必须的,否则应该避免使用split,split由于支持正则表达式,所以效率比较低,如果是频繁的几十,几百万的调用将会耗费大量资源,如果确实需要频繁的调用split,可以考虑使用apache的 StringUtils.split(string,char),频繁split的可以缓存结果。
其他补充:
1、及时清除不再使用的对象,设为null
2、尽可能使用final,static等关键字
3、尽可能使用buffered对象
如何优化代码使JAVA源文件及编译后CLASS文件更小
1 尽量使用继承,继承的方法越多,你要写的代码量也就越少
2 打开JAVA编译器的优化选项: javac -O 这个选项将删除掉CLASS文件中的行号,并能把
一些private, static,final的小段方法申明为inline方法调用
3 把公用的代码提取出来
4 不要初始化很大的数组,尽管初始化一个数组在JAVA代码中只是一行的代码量,但
编译后的代码是一行代码插入一个数组的元素,所以如果你有大量的数据需要存在数组
中的话,可以先把这些数据放在String中,然后在运行期把字符串解析到数组中
5 日期类型的对象会占用很大的空间,如果你要存储大量的日期对象,可以考虑把它存储为
long型,然后在使用的时候转换为Date类型
6 类名,方法名和变量名尽量使用简短的名字,可以考虑使用Hashjava, Jobe, Obfuscate and Jshrink等工具自动完成这个工作
7 将static final类型的变量定义到Interface中去
8 算术运算 能用左移/右移的运算就不要用*和/运算,相同的运算不要运算多次
2. 不要两次初始化变量
Java通过调用独特的类构造器默认地初始化变量为一个已知的值。所有的对象被设置成null,integers (byte, short, int, long)被设置成0,float和double设置成0.0,Boolean变量设置成false。这对那些扩展自其它类的类尤其重要,这跟使用一个新的关键词创建一个对象时所有一连串的构造器被自动调用一样。
3. 在任何可能的地方让类为Final
标记为final的类不能被扩展。在《核心Java API》中有大量这个技术的例子,诸如java.lang.String。将String类标记为final阻止了开发者创建他们自己实现的长度方法。
更深入点说,如果类是final的,所有类的方法也是final的。Java编译器可能会内联所有的方法(这依赖于编译器的实现)。在我的测试里,我已经看到性能平均增加了50%。
9. 异常在需要抛出的地方抛出,try catch能整合就整合
try {
some.method1(); // Difficult for javac
} catch( method1Exception e ) { // and the JVM runtime
// Handle exception 1 // to optimize this
} // code
try {
some.method2();
} catch( method2Exception e ) {
// Handle exception 2
}
try {
some.method3();
} catch( method3Exception e ) {
// Handle exception 3
}
已下代码 更容易被编译器优化
try {
some.method1(); // Easier to optimize
some.method2();
some.method3();
} catch( method1Exception e ) {
// Handle exception 1
} catch( method2Exception e ) {
// Handle exception 2
} catch( method3Exception e ) {
// Handle exception 3
}
10. For循环的优化
Replace…
for( int i = 0; i < collection.size(); i++ ) {
...
}
with…
for( int i = 0, n = collection.size(); i < n; i++ ) {
...
}
5、 在JAVA + ORACLE 的应用系统开发中,java中内嵌的SQL语句尽量使用大写的形式,以减轻ORACLE解析器的解析负担。
10、尽量采用lazy loading 的策略,即在需要的时候才开始创建。
例如: String str = “aaa”;
if(i == 1) {
list.add(str);
}
应替换为:
if(i == 1) {
String str = “aaa”;
list.add(str);
}
12、不要在循环中使用:
Try {
} catch() {
}
应把其放置在最外层