《沃顿商学院最受欢迎的谈判课》读书笔记(03-04章)

《沃顿商学院最受欢迎的谈判课》读书笔记(03-04章)

承接上一篇:《沃顿商学院最受欢迎的谈判课》读书笔记(01-02章)

03 观念和沟通

缩小认知差距
从现在起,当你与某人发生冲突的时候,问问自己下列问题:(1)我的看法是什么?(2) 对方的看法是什么? (3)是否存在观点不一致的情况? (4)如果是,原因是什么?
在生活中,你有时候也许已经以一种特定的、随意的方式问过自己上述问题。现在,你 应该让这样的问题成为你众多谈判技巧中一个特殊的、重要的技巧。这意味着你要理解双方 所持有的偏见,努力让对方明确表达出他们的观点,然后再来解释你的观点。
如 果你首先询问对方的观点是什么,这表明你很重视他们,对方会因此更有兴趣倾听你想说的话。
在这个世界上,大多数人对事实和理性都不感兴趣。在谈判中,我们应该这样开 始:对方准备好要听我说话了吗?要想知道答案,你必须了解对方脑海中的画面:他们的观 点和感受、对你的看法以及对世界的看法。如果不这样做,你就无从下手,就如同在黑暗中 摸索前行。
解释你的观点是你应该做的最后一件事。首先,了解一下对方的观点。了解对方观点的一个好办法是提问。在谈判中,提问要远比陈述更有力量。
陈述会让你受制于自己所说的话,无法帮你获取任何信息,而且还会授人以柄,让你成 为众矢之的。换句话说,提问不会让你受到限制,通常还会帮你获取信息,如果你愿意还能 授你以柄。提问可以让对方将注意力放在他们自己身上。
在谈判中,几乎你所说的一切都应该以提问的方式表现出来。这有助于你搞清楚对方是 否真的打算把自己的意图告诉你。
提问还能给对方提供一个更好的机会去参与交流。
交流隔阂及其消除之道
下面是有效沟通的基本原则:

  1. 始终保持沟通;
  2. 倾听并提问;
  3. 尊重而不是责 怪对方;
  4. 经常总结;
  5. 进行角色互换;
  6. 平心静气;
  7. 明确目标;
  8. 在不损害 双方关系的前提下坚持自己的立场;
  9. 寻找不起眼的小信号;
  10. 就知觉差异进行讨论;
  11. 了解对方作出承诺的方式;
  12. 作决定之前进行协商;
  13. 专注于自己力所能及之 事;
  14. 避免争论谁是谁非。

第一要务:必须与对方沟通
讲话是实力的象征,不讲话是示弱的表现。
角色互换
人们往往无法表达自己的感情。你的任务就是找出隐藏在人们言论背后的真正想法。如 何才能做到这一点呢?可通过如下方式:努力找出有关对方的更多信息、将自己置于对方的位置、努力看清对方脑海中的画面。
保持沉着冷静
最出色的 谈判者都是头脑冷静的,会继续从对方那里获取信息。
如果有人对你说“我恨你”,你要问他为什么。尽量多问对方喜欢什么样的竞争对手,少询问对方对自己的看法。如果对方威胁你,问他们为什么如此生 气。既要看到对方在表达自己情感方面所做的努力尽管这种努力往往是无效的并对其作出 回应,还要发现对方的言外之意并作出回应。即使对方只向你透露一点点信息,你也可以获 取能用来说服他们的宝贵信息。
注意各种信号
只要认真倾听、仔细观察,对方就会以无数种不同的方式,明显的或不明显的,给出能 够说服他们的办法。
争论对错于谈判 毫无意义
你无法告诉任何人任何事,除非对方愿意聆听。

04 面对强硬的谈判对手

利用准则
有关准则的了不起的一点是,其过程是清澈透明的,不是人为操纵的。当你在利用准则 的时候,你完全可以向对方坦言相告。
你要牢记的另外一点是:利用准则可能会对他人造成伤害。
采取循序渐进的策略
你应该将谈判划分成一个个比较小的步骤,每一步都要停靠并确认一下。每一个 停靠点相互之间的距离不能太远。通过这些渐进式步骤,你可以将对方带到很远的地方。你 要一次一小步地带领对方从熟悉的内容逐渐到不熟悉的内容。
本质上,在每一种情况下,这样做都是在为说服对方迈出下一步打基础。如果对方问你 的目标是什么,告诉他们你正在努力确认他们的准则,以便找到在目前的状况下有可能采取 的方案。如果对方问你更多问题,披露一些能让你更接近目标的信息。“这种情况下怎样做 才有可能呢”要比“我想让你给我便宜20%”更有效,因为后面这种问法要求对方迈的步子太大。
只有后退足够远的距离,对方才会既无法拒绝你所提出的要求,又不会产生被你利用的 感觉。从对方脑海中的各种画面开始。准则的含义即是对方脑海中的一幅画面。在谈判中大 多数人后退的距离远远不够。要从对方所熟悉的内容开始,然后再一步一步地缓缓前进。
我所说的后退到足够远的距离是什么意思呢?例如,这些问题:“您想达成一项协议 吗? ”“您想赚钱吗? ”“您想让客户满意吗? ”这为谈判提供了一个停靠点。如果对方一开始 说他们希望达成一项协议,但之后却开始提出蛮横无理的要求。你可以问对方,这样的要求 如何与他们希望达成协议的说法相符合。
在谈判中,应该一步一步地带领对方从熟悉的内容到不熟悉的内容。情况越复杂,迈的步子要越小,所需步骤要越多。
描述
谈判最重要的并不是合理性,而是对方的感受和看法。这 也是为什么准确描述(呈现信息的方式)如此重要的原因。
记住,你要站在对方一边,你只是在帮助对方以一种不同的 方式来看待问题。
不一定非要接受对方的准则或对方的表达描述。运用合理得体的表达方式的一个重要部 分是“重新构架”。从对方所用的措辞开始,找到一种不同的方式将其重新描述出来,让对方 获得更深刻的认识你的目标也会更有希望实现。
在最难学的技巧之中,运用合理得体的表达方式和采取渐进式策略是其中两种。大多数 人都想勇往直前,认为将事情分解成一个个小步骤很困难。此外,要找出合适的表达方式需 要很长时间,很多人没有这个耐心。然而,在谈判中,清晰准确的表达方式可以立即帮你达 成有利于你的协议。
制定准则
即使一个简单的会议,也需要一个议程。如果在会议开始前已经制定了一个议程,会议 开始时需要再检查一遍,以免事情有变。我们大家都知道,会议是多么容易跑题。在会议开 始阶段,如果没有一个大家一致同意的议程,这就像是坐上一辆不知开往何方的汽车,很难 指引你到达目的地。
在谈判中,要从简单的事情开始。这会给双方带来一种成就感。例如,“下次会议是什 么时候? ”即使前5个议题都只是铺垫,也并不意味着它们无足轻重。实现任何目标都会让双 方对会议倍增好感并变得更加合作。
如果不知道对方的准则是什么,该怎么办呢?那就问。在工作中,问对方用以决定加薪 和发放奖金的准则是什么。如果他们不告诉你,可以礼貌地说,你无法满足他们的要求,除 非你知道他们对你的确切要求是什么。
直接指出对方的不当行为
对付强硬的谈判者需要技巧。并非所有的谈判者都与人为善。愿意与人为善,但如果情况不允许,我也不会让自己或 你陷入无保护的境地。
在指出对方行为不当时有一个关键这也是所有谈判技巧中最有力的谈判技巧之一,即 在指出对方行为不当的时候,绝对不要让自己成为问题的焦点。如果这样做,你就失去了筹 码,因为此时的你也变得不理智。
当面指出对方行为不当的同时不要使自己成为问题的焦点,这一技巧威力巨大,因为这 是利用对方的所有力量来对抗他们所有焦点都聚集在他们身上。
在公司或人际关系的背景下,必须谨慎选择指出对方行为不当的方式,这往往需要机智。
如果对方违反了他们自己的准则,千万要沉住气,这是关键。只有改变态度才能如你所 愿。例如,每次有人企图欺骗我的时候,我就告诉我的团队不要心烦意乱。
收起你的争强好胜心
要完成基本要求并集中注意力:我的目标是什么,我应该使用什么样的准则,对 方的需求是什么,我能让对方与我同仇敌忾吗,我能与对方建立起良好的关系吗,对方的决策者是谁等。
在谈判之前,无疑要制定策略并作好准备。然后集中注意力实施自己的策略,要沉着冷 静。如果发现一个问题,稍事休息,重新检查策略,进行必要的调整。之后,重新回到谈判 当中,再次实施新的策略。这是一个极其有效的过程,既适用于最佳运动团队,也适用于最 出色的谈判者们。
 \

《沃顿商学院最受欢迎的谈判课》读书笔记(01-02章)

《沃顿商学院最受欢迎的谈判课》读书笔记(01-02章)

一直觉得自己性子太直,读读这本《沃顿商学院最受欢迎的谈判课》,看大家评论貌似有人觉得这书一般,类似厚黑学的东西,不过我觉得尚可,可以学习不同的东西,毕竟总是看技术类书籍偶尔还是会觉得有些单调,学习点新东西。这里做做摘要,记录一下。

01 换种思路

准备充分,哪怕只有5s的准备时间。
在谈判中,人几乎是决定一切的因素。
世界是非理性的。
当对方意识到 你在意他们的感受时,他们就会更愿意倾听,也就更容易被打动、被说服。
谈判目标
目标是你要努力去实现的东西。不要努力去建立人际关系,除非这种关系能让你更接近自己的目标。不要去管他人的利益、需求、情感或其他东西,除非它们能让你更接近自己的目标。不要给予或收集信息,除非这回让你更接近目标。
在任何时期,不仅要实现自己的目标,同时还要帮助所有相关的人实现他们的目标。
每个人都能从谈判训练中受益
大多数谈判者不知道如何制订或实现自己的谈判目标。 他们不懂得如何倾听,也不明白如何发现其他人脑海中的想法。这些谈判者大都具有对抗性 和防守性的特点,而且其谈判态度不正确。
坚持不懈
当对方对你说不行的时候,问一问为什么不行。
你必须找出更深层次的动机
这不是一个理想状态下的谈判过程,你也不应该指望谈判过程会始终处于理想状态。但是,如果你能坚持运用那些谈判策略和技巧,你的生活将会以很多你无法预知的方式变得更加美好。

02 人几乎决定一切

记住,在整个谈判中,你是最无足轻重的一个人。最重要的人是对方,其次是对谈判者极为重要的第三方。如果不接受这个观点,你就无法说服别人相信你所说的任何一句话。
对方脑海中的想法
如果你和对方在讨论议题的过程中不顺利,那么就立刻 停下来!回过头去谈一谈人的问题,然后将所有问题搞定。不要失去理智地一味继续下去, 否则你将无法达成协议。或者即使你达成了协议,这项协议也不会长久维持下去。
人际交流
无论在事业上还是个人生活当中,如果知道哪些人真正相同,哪些人真正不同,这将带 来多么有利的竞争优势啊!仅靠一些外部特征或表面上的社会交际关系是无法获得这种竞争优势的。而一旦拥有了这种竞争优势,你就能和任何团队的成员建立关系,成功达成交易。 对那些识人不明、技巧不足的谈判者而言,这正是容易令他们失手的地方。
第三方
很少有人会忽视对自己很重要的第三方的意见。当你需要对某人施加影响,而又自认为 没有足够的影响力的时候,想一想对对方而言很重要的人还有谁,以及更容易受你影响的人又是谁。
尊重对方
了解对方脑海中的想法是谈判中的一个重要主题,我会反复谈及这个问题。要想说服对 方,了解对方脑海中的想法是你所能做的最重要的事。只有努力去了解对方脑海中的想法, 才有可能转变对方的思想。
你要把日常生活中的所有遭遇都当成谈判来 加以思考,并不断实践,直到你能迅速将注意力集中到对方身上。换句话说,必须让对方真 正觉得这个谈判关注的是他们以及他们的需求和看法,而不是你的需求和看法。
进行人际沟通意味着你必须将注意力放在对方身上,而不仅放在自己身上,你要让他们 主动与你交谈。
摸清对方的实力并予以肯定
关键在于不要过于反应激烈,即使对方心情不好。通常,人们之所以抨击你只是因为他 们在别处受挫。不要把这归结为你的原因。告诉他们,你很抱歉他们这一天过得不愉快。这 样做会令你大有所获。这样做需要很强的自律能力,但获得的回报却是值得的。
让你的同事、 上司以及员工给你提供解决问题的建议,并让他们知道你也许不会采纳所有的建议。无论如 何你会因此而获得更多。
信任
信任的主要组成部分是诚实对人要坦率。信任并不意味着双方意见一致或总是一团和 气。不过,信任意味着彼此相信对方。正如我在前面提到的,你的可信度是你所拥有的最有 力的谈判工具。
这并不意味着你必须将一切毫无保留地告知对方。
缺乏信任的谈判
我们需要的是一个承诺
下面这个清单中的内容一定要牢记于心:

  • 如果对方获得的信息比你多很多,你就处于劣势。要采取渐进的方式,不要轻易作出 承诺,直到获得更多信息或更多信任。
  • 收集大量有关对方的信息(“请正当调查”)。问他们有关细节问题。看看是否所有的 信息都相互匹配。对每一件事进行检查和测试。请可靠的第三方提供帮助。
  • 对方逃避你的问题或转移话题吗?对方越躲躲闪闪,他们掩盖真相的可能性就越大。
  • 如果与诚实相比,欺骗能让对方更有利可图,那就改变刺激物。例如,就对方在一段 时期内的表现(价值)给予对方一定报酬。
  • 除非有明确的保护措施否则不要向对方提供自己的资产(发明创造、时间、房屋建筑)。
  • 确保任何协议的真实性。告诉对方:“只要你所说的是真话,我就会放心,你也不用 付出任何代价。”如果对方犹豫退缩,一定要警惕。
  • 在协议中将违反协议的后果予以说明。
  • 亲自与对方会面,不要藏头露尾。在一些文化当中,谈判各方必须亲自交谈,这样他 们可以观察彼此。
  • 如果对方对某些内容有所保留,你对此感到不满,那就问对方:“还有什么我应该知 道的吗? ”

相信自己的直觉。对方紧张吗?看上去内疚吗?殷勤过度了吗?目光看向别处吗(除非 是文化上的原因)?长时间沉默吗?在拒绝作出承诺吗?这些并非对方不诚实的真凭实据。 但直觉提出的这些问题却能让你走得更慢,思考得更多,步子迈得更稳。
争取更多还意味着不要紧盯着一些蝇头小利,一定要努力按照这些准则去做,否则悔之 晚矣。
失去和重获信任
即使已经失去对方的信任,也有可能重新赢回来。当然,这并非易事,也不能保证一定 成功。如果从“第二次机会”这个角度来重建信任,你的要求就有可能得到对方的回应。这个 过程是循序渐进的。“你必须礼貌客气、向对方表示歉意并承诺会做得更好。\

idea中maven项目自动重置Java compiler的JDK版本为1.5的问题

idea中maven项目自动重置Java compiler的JDK版本为1.5的问题

这几天给开发机重装系统后遇到的一个问题,maven什么的都是最新的版本(maven 3.5.2, jdk 1.8),然而用idea打开项目后,总是注解报错,然后追根溯源发现是目前版本的maven在构建时默认面向jdk 1.5……参见这里:
http://maven.apache.org/plugins/maven-compiler-plugin/index.html

Also note that at present the default source setting is 1.5 and the default target setting is 1.5, independently of the JDK you run Maven with. If you want to change these defaults, you should set source and target as described in Setting the -source and -target of the Java Compiler.


这导致即便在idea中设置project structure中project/modules的jdk版本1.8, idea的compiler版本设置为1.8,重启后还是会报错(除了注解报错,编译项目时也会报错说java compiler版本有问题的错误信息,这个当时没截图……)。彻底解决这个问题需要制定maven编译时的版本,具体做法maven官网已给出:
http://maven.apache.org/plugins/maven-compiler-plugin/examples/set-compiler-source-and-target.html


[…]

<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>

[…]

或者


[…]

[…]


org.apache.maven.plugins
maven-compiler-plugin
3.7.0

1.8
1.8



[…]

[…]

【源码分析】java sdk篇–Java Collection Framework

【源码分析】java sdk篇–Java Collection Framework

本文就是想学习、简单总结一下JDK里的JCF,并且制定一下自己后续阅读源码的范围——先学习一下JCF中重要的一些类,后续学习Java其他一些东西,给自己定个路线图,O(∩_∩)O~
本文参考了多篇文章,如有引用但没标注的地方,请及时联系我,这文章本意是想记录一下,相当于读书笔记。

一、概述

JCF,即Java Collection Framework,是JDK中已经封装好容器类,类结构如下:

网上常见的是这张图,但其实少了一个Queue,看下面这张图更清楚一些:[](http://www.prayerlaputa.com/wp-content/uploads/2017/02/JCF详细图2.gif)\

 
看着很复杂,其实按照我们日常用到的类,可以按照如下分类(仅列出常见的具体实现类,上图那些抽象类就不一一讨论了):
1、Collection

  • List
    • ArrayList
    • LinkedList
  • Set
    • HashSet
    • TreeSet
    • LinkedHashSet
  • Queue
    • ArrayDeque
    • LinkedList
    • 以及Deque(如ArrayQueue/LinkedList/…)

2、Map

  • HashMap/TreeMap/Hashtable/…

二、具体实现

以下分别讨论一下:

1、List

List接口扩展自Collection,它可以定义一个允许重复的有序集合,从List接口中的方法来看,List接口主要是增加了面向位置的操作,允许在指定位置上操作元素,同时增加了一个能够双向遍历线性表的新列表迭代器ListIterator。AbstractList类提供了List接口的部分实现,AbstractSequentialList扩展自AbstractList,主要是提供对链表的支持。”常用实现ArrayList/LinkedList就没必要讨论了,网上很多,实际编程过程中时刻谨记一个数组实现一个链表实现。

2、Set

Set接口扩展自Collection,它与List的不同之处在于,规定Set的实例不包含重复的元素。在一个规则集内,一定不存在两个相等的元素。AbstractSet是一个实现Set接口的抽象类,Set接口有三个具体实现类,分别是散列集HashSet、链式散列集LinkedHashSet和树形集TreeSet。
HashSet在创建对象时可以指定集合初始容量、负载因子(load factor),当元素个数超过容量*负载因子时集合将自动扩展容量。HashSet内部实际是用了一个key是传入值、val值为一个固定值的HashMap来实现。

LinkedHashSet是用一个链表实现来扩展HashSet类,它支持对规则集内的元素排序。HashSet中的元素是没有被排序的,而LinkedHashSet中的元素可以按照它们插入规则集的顺序提取。

TreeSet扩展自AbstractSet,并实现了NavigableSet,AbstractSet扩展自AbstractCollection,树形集是一个有序的Set,其底层是一颗树,这样就能从Set里面提取一个有序序列了。在实例化TreeSet时,我们可以给TreeSet指定一个比较器Comparator来指定树形集中的元素顺序。树形集中提供了很多便捷的方法。

3、Queue

队列是一种先进先出的数据结构,元素在队列末尾添加,在队列头部删除。Queue接口扩展自Collection,并提供插入、提取、检验等操作。方法offer表示向队列添加一个元素,poll()与remove()方法都是移除队列头部的元素,两者的区别在于如果队列为空,那么poll()返回的是null,而remove()会抛出一个异常。方法element()与peek()主要是获取头部元素,不删除。
接口Deque,是一个扩展自Queue的双端队列,它支持在两端插入和删除元素,因为LinkedList类实现了Deque接口,所以通常我们可以使用LinkedList来创建一个队列。
PriorityQueue类实现了一个优先队列,优先队列中元素被赋予优先级,拥有高优先级的先被删除。

4、Map

Map,图,是一种存储键值对映射的容器类,在Map中键可以是任意类型的对象,但不能有重复的键,每个键都对应一个值,真正存储在图中的是键值构成的条目。
HashMap是基于哈希表的Map接口的非同步实现,继承自AbstractMap,AbstractMap是部分实现Map接口的抽象类。

在之前的版本中,HashMap采用数组+链表实现,即使用链表处理冲突,同一hash值的链表都存储在一个链表里。但是当链表中的元素较多,即hash值相等的元素较多时,通过key值依次查找的效率较低。而JDK1.8中,HashMap采用数组+链表+红黑树实现,当链表长度超过阈值(8)时,将链表转换为红黑树,这样大大减少了查找时间。 也就是说JDK 1.8开始用数组+链表+红黑树是实现。具体分析参考此文章:Java – 集合框架完全解析  以及本博客后续文章。
在HashMap中要找到某个元素,需要根据key的hash值来求得对应数组中的位置。对于任意给定的对象,只要它的hashCode()返回值相同,那么程序调用hash(int h)方法所计算得到的hash码值总是相同的。我们首先想到的就是把hash值对数组长度取模运算,这样一来,元素的分布相对来说是比较均匀的。但是,“模”运算的消耗还是比较大的,在HashMap中,(n – 1) & hash用于计算对象应该保存在table数组的哪个索引处。HashMap底层数组的长度总是2的n次方,当数组长度为2的n次幂的时候,(n – 1) & hash 算得的index相同的几率较小,数据在数组上分布就比较均匀,也就是说碰撞的几率小,相对的,查询的时候就不用遍历某个位置上的链表,这样查询效率也就较高了。
TreeMap基于红黑树数据结构的实现,键值可以使用Comparable或Comparator接口来排序。TreeMap继承自AbstractMap,同时实现了接口NavigableMap,而接口NavigableMap则继承自SortedMap。SortedMap是Map的子接口,使用它可以确保图中的条目是排好序的。
在实际使用中,如果更新Map时不需要保持图中元素的顺序,就使用HashMap,如果需要保持Map中元素的插入顺序或者访问顺序,就使用LinkedHashMap,如果需要使Map按照键值排序,就使用TreeMap。

5、其他

1.Vector

前面我们已经提到,Java设计者们在对之前的容器类进行重新设计时保留了一些数据结构,其中就有Vector。用法上,Vector与ArrayList基本一致,不同之处在于Vector使用了关键字synchronized将访问和修改向量的方法都变成同步的了,所以对于不需要同步的应用程序来说,类ArrayList比类Vector更高效。

Vector非常类似ArrayList,但是Vector是同步的。由Vector创建的Iterator,虽然和ArrayList创建的 Iterator是同一接口,但是,因为Vector是同步的,当一个Iterator被创建而且正在被使用,另一个线程改变了Vector的状态(例如,添加或删除了一些元素),这时调用Iterator的方法时将抛出ConcurrentModificationException,因此必须捕获该异常。通过使用capacity和ensurecapacity操作以及capacityIncrement域可以优化存储操作,这个前面讲过,(Vector的Iterator和listIterator方法翻译的迭代器支持fail-fast机制,因此如果在使用迭代器的过程中有其他线程修改了map,那么将抛出ConcurrentModificationException,这就是所谓fail-fast策略。官方对此的说明是  java.util 包中的集合类都返回 fail-fast迭代器,这意味着它们假设线程在集合内容中进行迭代时,集合不会更改它的内容。如果 fail-fast迭代器检测到在迭代过程中进行了更改操作,那么它会抛出 ConcurrentModificationException,这是不可控异常。)

2.Stack

Stack,栈类,是Java2之前引入的,继承自类Vector。

3.HashTable

HashTable和前面介绍的HashMap很类似,它也是一个散列表,存储的内容是键值对映射,不同之处在于,HashTable是继承自Dictionary的,HashTable中的函数都是同步的,这意味着它也是线程安全的,另外,HashTable中key和value都不可以为null。
上面的三个集合类都是在Java2之前推出的容器类,可以看到,尽管在使用中效率比较低,但是它们都是线程安全的。下面介绍两个特殊的集合类。

4.ConcurrentHashMap

Concurrent,并发,从名字就可以看出来ConcurrentHashMap是HashMap的线程安全版。同HashMap相比,ConcurrentHashMap不仅保证了访问的线程安全性,而且在效率上与HashTable相比,也有较大的提高。关于ConcurrentHashMap的设计,我将会在下一篇关于并发编程的博客中介绍,敬请关注。

5.CopyOnWriteArrayList

CopyOnWriteArrayList,是一个线程安全的List接口的实现,它使用了ReentrantLock锁来保证在并发情况下提供高性能的并发读取。

另外,Collections类中有大量与容器类相关的工具方法可以使用,比如查找元素、找最值、逆序、shuffle等。

三、线程同步

上述提到的容器类除了Vector/Stack/HashTable这些上古遗留代码,以及ConcurrentHashMap/CopyOnWrite容器这些专门针对并发情况的容器,基本都是非同步的(unsynchronized),如需多线程访问需要自己加上同步相关代码。
在java.util.concurrent包中定义的CopyOnWriteArrayList提供了线程安全的Arraylist,但是当进行add和set等变化操作时它是通过为底层数组创建新的副本实现的,所以比较耗费资源。可以参考 JAVA中的COPYONWRITE容器

参考

Java – 集合框架完全解析
java的集合框架最全详解(图)\

2016年Prayer的年度总结

2016年Prayer的年度总结

虽然现在已经是2017年1月底,但是按照中国人的传统,果然还是过了阴历年才算过年,所以,这篇总结现在写也无不为过吧,目前除夕,在家里也没啥我能做的,反正闲着也是闲着,写写博客
2016年应该说是很忙碌的一年,也是比较辛苦的一年。

工作

从年初开始基本每周都至少有3天在加班,一直加到9月份,这对于在某国企软开上班的我们来讲有些不可思议,尤其是看到其他部门的同事经常没事儿干的时候难免觉得很忧桑。没办法,谁让我们组搞移动客户端、是目前的主要渠道呢,前几天看数据,用户峰值已经达到了8kw,想想也是蛮有成就感的一件事。
我这一年工作主要还是客户端开发,间或有一些服务端开发。客户端开发涉及如下内容:

  • 各种控件开发,这个就很零碎了,做的太多,也没必要一一列举出来
  • 多个sdk的集成,比如小米/华为推送服务,umeng的微信分享sdk之类的
  • 离线服务调试,这个挺有用,是通过lazy的方式,动态更新客户端内的各种资源,省去了不断更新客户端的麻烦(互联网发新版客户端貌似随意的多,我们这里发个版本要准备好多东西,除非是出了大事、要紧急发新版修复bug)

相比2015年,工作能力上android开发能力提升了不少,2015年多数情况下其实只是充当助手的角色,写一些边边角角的东西,16年因为同事当爹、一下子好多事情压到了我头上,承担主力开发,压力略大,不过确实学习到了很多,不光是对于项目组内所用的android框架的理解有所提升,与其他同事沟通、配合的能力、与厂商的协调能力都有所提升(跟外面的厂商沟通真是一件麻烦的事情啊.

生活

2016年生活上作为单身狗继续宅着,本来想坚持的每天跑步的健身计划,因为年初时膝盖受伤,不得不停下来,各种求医问药,发现在帝都看病真难,不是你有钱(虽然我也没啥钱,T_T)就可以的,因为你不知道哪个大夫说的是对的,某几个部队医院(比如丰台的30X医院,不说具体名字了)的大夫看完我膝盖的核磁片之后简单诊断了一下就说建议尽快手术,手术可不敢随便做啊,我跑过好几家之后终于明白这肯定是吓唬我的,膝盖伤没那么严重,只是需要静养一段时间。修养了整整一年了吧,目前没完全好,但基本不影响平时生活。
锻炼一定要讲究方式方法!!!
锻炼一定要讲究方式方法!!!
锻炼一定要讲究方式方法!!!
重要的事情说3遍!我就是跑步时没注意,总是每天跑步、还在跑步结束冲刺几百米、下山小跑着下来给伤到的。大家一定要注意。

技术成长

技术上除了android涉猎的范围更广,在业余时间各种充电,先后学习了:机器学习、hadoop,目前正在深入学习hadoop中。机器学习是看邹博邹老师的课,讲的非常细致,推荐一下,虽然没什么实践的机会,但至少明白目前这么火的机器学习、深度学习究竟是用什么来实现的,不过各种算法实在有点多,比如SVM、XGBoost、决策树、随机决策森林、logistic回归……说实话,不动手做项目真的很难深入理解。
hadoop目前还在学习中,研究生时学过一些,目前是深入学习一下,毕竟我对这块还是很有兴趣的。
开始看java sdk的源码,目前已经看了一部分有关JCF(java collection framework)的代码,写了一些学习笔记,加深了对java的认识,感觉很好,而且看源码很有意思,能发现不少以前自己没留意过的地方,让自己印象更深刻,比如以前一直记不住为啥OutputStream类对象在写操作的最后要加一个flush,其实看源码就很容易记住了——继承OutputStream的类一般在进行写入(write)操作时用到一个缓冲区,缓冲区满了才执行一次写入操作,如果最后一次调用write方法、内部缓冲区没有满,那这时就必须在最后调用一个flush将缓冲区的内容写出来。
leetcode一直有断断续续的刷,算法这东西果然是不断练才能体会出一些东西来——虽然做算法题有时候觉得还是蛮有意思的(注意,仅仅是有时候!!算法这东西水实在太深),程序猿这个种群真命苦,要学的东西永远都学不完……不过另一个角度看,永远有新东西出来,这才有意思啊

17年希望做到如下:

  • 机器学习/深度学习复习,尝试做一下照片风格化(网上有很多现成的例子可以参考)
  • hadoop:知识体系建立起来,做1~2个项目练手,学习spark编程
  • java sdk源码分析:继续分析JCF,以及concurrent包 nio包内的代码,学习更多底层知识
  • 重温JVM相关知识
  • 学习java并发相关知识

最后,祝大家新年快乐!\

android获取相册图片以及获取拍照照片

android获取相册图片以及获取拍照照片

之前工作时遇到的问题,趁着春节工作压力不大,总结一下。
声明:本文部分代码取自Android开发之获取相册照片和获取拍照照片 并做了修改
1、从相册或是文件管理器获取
在android中从本地获取图片(不管是相册或是文件系统),调用如下Intent即可:

1
2
3
4
5
public static void getImageFromAlbum(Activity activty, int requestCode) {
Intent intent = new Intent(Intent.ACTION_PICK);
intent.setType("image/*");// 相片类型
activty.startActivityForResult(intent, requestCode);
}

调用该Intent后,将显示相册、文件系统图标,用户选择后将返回,我们在onActivityReSult中可以获取用户选中图片的Uri

1
2
3
4
5
6
7
8
public void onActivityResult(int requestCode, int resultCode, Intent data){
if(Activity.RESULT_OK == resultCode){
if( ALBUM_IMAGE_REQ_CODE == requestCode){
//do something to the album image
Uri imageUri = data.getData();
}
}
}

2、调用系统拍照功能获取照片
同样需要以Intent的方式实现,下面的getImageFromCamera方法在调用Intent时如果不设置MediaStore.EXTRA_OUTPUT,那么在onActivityResult中通过Intent获取的图片只是一个缩略图,并不是清晰图片,要想拿到清晰照片必须将其先保存到SD卡上,通过设置MediaStore.EXTRA_OUTPUT、传入一个SD卡路径的Uri对象即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
       //storageUri必须是一个SD卡地址,以便保存清晰照片
protected void getImageFromCamera(Activity activity, int requestCode, Uri storageUri) {
String state = Environment.getExternalStorageState();
if (state.equals(Environment.MEDIA_MOUNTED)) {
Intent getImageByCamera = new Intent("android.media.action.IMAGE_CAPTURE");
getImageByCamera.putExtra(MediaStore.EXTRA_OUTPUT, storageUri);
activity.startActivityForResult(getImageByCamera, requestCode);
} else {
Toast.makeText(activity.getApplicationContext(), "请确认已经插入SD卡", Toast.LENGTH_LONG).show();
}
}
//辅助方法,用于生成一个SD卡路径的Uri对象
private static Uri getImageStorageUri(String sdSaveDir, String imageName){
Uri uri = null;
String sdState = Environment.getExternalStorageState();
if(sdState.equals(Environment.MEDIA_MOUNTED)){
String imagePath = Environment.getExternalStorageDirectory().getPath()+"/"+sdSaveDir;
File file = new File(imagePath);
if(!file.exists()){
file.mkdir();
}
imagePath = imagePath + "/"+imageName;
uri = Uri.fromFile(new File(imagePath));
} else {
//TODO throw exception or do something
}
return uri;
}

参考资料:
Android开发之获取相册照片和获取拍照照片
Android开发之获取相册照片和获取拍照照片二
Android从相册获取图片
Android从相册中获取图片以及路径
Android Camera开发系列(上)——Camera的基本调用与实现拍照功能以及获取拍照图片加载大图片

【整理】android裁剪图片代码实现

【整理】android裁剪图片代码实现

android系统中已经存在一个用于裁剪图片的控件,基本参数如下(取自:Android中com.android.camera.action.CROP(图片裁剪)所有属性):
附加选项 数据类型 描述
crop String 发送裁剪信号
aspectX int X方向上的比例
aspectY int Y方向上的比例
outputX int 裁剪区的宽
outputY int 裁剪区的高
scale boolean 是否保留比例
return-data boolean 是否将数据保留在Bitmap中返回
data Parcelable 相应的Bitmap数据
circleCrop String 圆形裁剪区域?
MediaStore.EXTRA_OUTPUT (“output”) URI 将URI指向相应的file:///…,
以下3个属性比较重要:data、MediaStore.EXTRA_OUTPUT以及return-data。
data和MediaStore.EXTRA_OUTPUT都是可选的传入数据选项,你可以选择设置data为Bitmap,或者将相应的数据与URI关联起来,你也可以选择是否返回数据(return-data: true)。
为什么还有不用返回数据的选项?如果对URI足够了解的话,应该知道URI与File相似,你所有的操作如裁剪将数据都保存在了URI中,你已经持有了相应的URI,也就无需多此一举,再返回Bitmap了。
前面已经说到,可以设置data为Bitmap,但是这种操作的限制在于,你的Bitmap不能太大。因此,我们前进的思路似乎明确了:截大图用URI,小图用Bitmap。
用法如下:

1
2
3
4
5
6
7
8
9
10
11
intent.setType("image/*"); //这个参数是确定要选择的内容为图片,
intent.putExtra("crop", "true"); //设置了参数,就会调用裁剪,如果不设置,就会跳过裁剪的过程。
intent.putExtra("aspectX", 1); //这个是裁剪时候的 裁剪框的 X 方向的比例。
intent.putExtra("aspectY",1); //同上Y 方向的比例. (注意: aspectX, aspectY ,两个值都需要为 整数,如果有一个为浮点数,就会导致比例失效。)
//设置aspectX 与 aspectY 后,裁剪框会按照所指定的比例出现,放大缩小都不会更改。如果不指定,那么 裁剪框就可以随意调整了。
intent.putExtra("outputX", 100); //返回数据的时候的 X 像素大小。
intent.putExtra("outputY", 100); //返回的时候 Y 的像素大小。
//以上两个值,设置之后会按照两个值生成一个Bitmap, 两个值就是这个bitmap的横向和纵向的像素值,如果裁剪的图像和这个像素值不符合,那么空白部分以黑色填充。
intent.putExtra("noFaceDetection", true); // 是否去除面部检测, 如果你需要特定的比例去裁剪图片,那么这个一定要去掉,因为它会破坏掉特定的比例。
intent.putExtra("return-data", true); //是否要返回值。
startActivityForResult(intent, 1);

有关该控件的用法还可以参考这些文章:
Android调用系统拍照裁剪图片模糊的解决方法
Android 拍照或从相册取图片并裁剪
[Android] 拍照、截图、保存并显示在ImageView控件中
Android 拍照或选择图片并剪裁
Android之图片选择与裁剪
但用这个控件有一个问题,那就是如果系统经过定制(比如我国众多的ROM,如MIUI/Flyme/EMUI),那么无法控制该控件在各种机型上的显示效果,笔者在EMUI上获得的截图框是圆形的(circleCrop参数基本无效,当然,也可能是我参数设置不合适,没深入探究),在MIUI上是矩形。一般情况下,我们都希望控件显示效果是一致的,因此只能自己动手实现了。幸好github上有现成代码,不用重复造轮子,直接拿来用就好,以下推荐我使用过的几个截图控件:
https://github.com/jdamcd/android-crop 代码比较简单,目前star数很高(3281),作者说是修改自从AOSP代码,简单的裁剪图片肯定是够用了(我就是用的这个)
https://github.com/ArthurHub/Android-Image-Cropper 效果不错,可以旋转图片,并集成了其他多种功能,star数也很高(1897),不过我看代码略多,跑了下代码就没细看,不过github wiki上给出了比较详细的使用说明
https://github.com/IsseiAoki/SimpleCropView\
https://github.com/lvillani/android-cropimage\
https://github.com/biokys/cropimage\
https://github.com/edmodo/cropper\
Android图片裁剪实现(EnjoyCrop) https://github.com/Zhouztashin/EnjoyCrop\
Android实现图片的裁剪(不调用系统功能)
以及这些工程:https://android-arsenal.com/tag/45 ,都可以参考一下:

【源码分析】java sdk篇–TreeSet

【源码分析】java sdk篇–TreeSet

今天刷leetcode时需要用到TreeSet,简单看了下源代码,在此做下学习笔记:

概念

TreeSet与TreeMap类似,能够创建有序的集合。

基本操作

TreeSet的实现基于TreeMap(TreeMap源码比较复杂,参考这篇文章:Java提高篇(二七)—–TreeMap ),内部使用

1
private transient NavigableMap<E,Object> m;

这个NavigableMap保存数据,而实际上这个m就是一个TreeMap对象,而在进行add(E)操作时,实际上是进行了如下操作:

1
m.put(E, dummyObject);

由于基于TreeMap,TreeSet的add/remove/contains等基本操作的时间复杂度同样为log(n)。

同步

与TreeMap一样,TreeSet的基本操作并没有做线程安全处理,因此需要多线程访问同一个TreeSet对象、并且至少有一个线程需要修改该对象时,需要手动加上同步语句,比如使用synchronized关键字,除此之外还可以在创建TreeSet对象时使用Collections.synchronizedSortedSet方法来保证同步安全:

1
SortedSet s = Collections.synchronizedSortedSet(new TreeSet(...));

此外,TreeSet所返回的iterator是fail-fast的,源码中解释如下:

The iterators returned by this class’s iterator method are fail-fast: if the set is modified at any time after the iterator is created, in any way except through the iterator’s own remove method, the iterator will throw a ConcurrentModificationException. Thus, in the face of concurrent modification, the iterator fails quickly and cleanly, rather than risking arbitrary, non-deterministic behavior at an undetermined time in the future.
Note that the fail-fast behavior of an iterator cannot be guaranteed as it is, generally speaking, impossible to make any hard guarantees in the presence of unsynchronized concurrent modification. Fail-fast iterators throw ConcurrentModificationException on a best-effort basis. Therefore, it would be wrong to write a program that depended on this exception for its correctness: the fail-fast behavior of iterators should be used only to detect bugs.

有关fail-fast,可以参考这篇文章:Java提高篇(三四)—–fail-fast机制
有关TreeSet更详细的介绍,可以参考这篇文章:Java 集合系列17之 TreeSet详细介绍(源码解析)和使用示例

【整理】使用ant和jenkins自动化编译android项目(windows环境)

【整理】使用ant和jenkins自动化编译android项目(windows环境)

平时工作经常需要编译不同的android测试包,对这种重复劳动着实无感,趁着这几天工作不是很忙,尝试部署了jenkins来自动编译,下面记录一下过程(以下主要讨论在windows环境下的操作)。

第一步

使用ant编译android项目。如果是使用eclipse开发android,应该对ADT打包速度颇为无语,使用ant的速度要快很多。第一步要做的就是保证ant能够正常build项目,这样后面jenkins就可以启动一个ant编译任务进行android打包。ant编译android项目需要做如下工作:

  • 配置android环境变量
  • 配置ant环境变量

配置这两个变量是为了方便后续操作,配置好之后的步骤如下:

  • 生成build脚本
  • 使用ant debug或是ant release生成apk文件,测试是否正常。

详细说明可以参考这两篇文章:Ant自动编译打包&发布 android项目  Ant自动编译打包android项目

第二步

配置jenkins。jenkins安装很简单,从官网下了war包之后,直接命令行中使用

1
java -jar jenkins.war

即可启动,然后访问ip:8080即可看到jenkins站点。如果8080端口被占用,在启动时输入端口号即可,即:

1
java -jar jenkins.war --httpPort=新的端口号

然后新建一个Freestyle Project,逐步配置即可(每个配置项后面的问号都有详细说明,如果还不懂可以去看官网文档),可以参考这篇文章:用jenkins搭建android自动打包环境。需要注意的是“Build”(中文版的话应该是叫“构建”)选项的配置,用jenkins搭建android自动打包环境 这篇文章没细说,截图也不是正确的,我们需要在”build”选项下的“targets”选择中输入“clean release”或是“clean debug”,按照需求可能还需要添加build参数,如下图所示(截图来自:Android 自动build + jenkins配置 )

配置完后即可按照用jenkins搭建android自动打包环境  所说的步骤进行构建。
另外,还可以使用jenkins插件在build出新的apk之后进行装机测试,可以参考这篇文章:(jenkins)hudson平台搭建android项目持续化集成以及基于NativeDriver的UI自动化测试环境
参考:
Ant自动编译打包&发布 android项目
Ant自动编译打包android项目
用jenkins搭建android自动打包环境
Android 自动build + jenkins配置
(jenkins)hudson平台搭建android项目持续化集成以及基于NativeDriver的UI自动化测试环境

【源码分析】java sdk篇–CopyOnWrite容器

【源码分析】java sdk篇–CopyOnWrite容器

工作中遇到CopyOnWriteArrayList这个类,略好奇,简单学习了一下,这里做个笔记。

一、概念

CopyOnWrite容器是在JDK 1.5 的java.uti.concurrent包中出现的,目前只有两个类

  • CopyOnWriteArrayList
  • CopyOnWriteSet


这些容器类与平时用到的ArrayList/Set的区别之处在于:

CopyOnWrite容器即写时复制的容器。通俗的理解是当我们往一个容器添加元素的时候,不直接往当前容器添加,而是先将当前容器进行Copy,复制出一个新的容器,然后新的容器里添加元素,添加完元素之后,再将原容器的引用指向新的容器。这样做的好处是我们可以对CopyOnWrite容器进行并发的读,而不需要加锁,因为当前容器不会添加任何元素。所以CopyOnWrite容器也是一种读写分离的思想,读和写不同的容器。

二、源码分析

与ArrayList类似,CopyOnWriteArrayList内部同样是基于数组实现,如下:

1
2
/** The array, accessed only via getArray/setArray. */
private transient volatile Object[] array;

array数组被transient关键字修饰,保证该数组不能使用Serializable接口自动序列化;被volatile修饰,保证了多线程访问时的可见性,即保证每次读取的是最新的值,但是没保证对变量的操作的原子性(有关volatile关键的分析,参见:Java并发编程:volatile关键字解析)。与ArrayList相比,CopyOnWriteArrayList多出了volatile关键字。
而CopyOnWriteArrayList区别于ArrayList的主要地方在于add/set/remove等涉及到写数据的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
public E set(int index, E element) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
E oldValue = get(elements, index);
if (oldValue != element) {
int len = elements.length;
Object[] newElements = Arrays.copyOf(elements, len);
newElements[index] = element;
setArray(newElements);
} else {
// Not quite a no-op; ensures volatile write semantics
setArray(elements);
}
return oldValue;
} finally {
lock.unlock();
}
}
public boolean add(E e) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
Object[] newElements = Arrays.copyOf(elements, len + 1);
newElements[len] = e;
setArray(newElements);
return true;
} finally {
lock.unlock();
}
}
public E remove(int index) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
E oldValue = get(elements, index);
int numMoved = len - index - 1;
if (numMoved == 0)
setArray(Arrays.copyOf(elements, len - 1));
else {
Object[] newElements = new Object[len - 1];
System.arraycopy(elements, 0, newElements, 0, index);
System.arraycopy(elements, index + 1, newElements, index,
numMoved);
setArray(newElements);
}
return oldValue;
} finally {
lock.unlock();
}
}

ArrayList在add/set/remove时,只需要考虑内部的数组就可以,不用考虑并发。而CopyOnWriteArrayList则采用可重入锁,避免多线程写操作,并且写的时候是重新拷贝一个新的数组,等拷贝结束、将修改引用后再释放锁。因此,CopyOnWriteArrayList若保存对象较大、占用内存较多时可能造成JVM频繁进行gc。另外,CopyOnWriteArrayList只保证了数据的最终一致性,并没有保证数据的实时一致性。

三、场景应用常见

CopyOnWrite并发容器用于读多写少的并发场景。比如白名单,黑名单,商品类目的访问和更新场景

四、参考

JAVA中的COPYONWRITE容器
CopyOnWriteArrayList源码解析