先看下面这段代码
c = a;
a = b;
b = c;
刚学编程那会儿,完全不知道这段代码到底在干嘛?!现在大家应该都能看懂这段代码的意义,就是实现了a和b值的互换,c作为一个过渡变量而存在。用人的思维去理解这段代码就是:第一步,将a的值给到c。此时c就等于是a的一个备份。因为a有了备份,就可以对a进行操作,所以有第二步,将b的值给到a。此时a的值已经是b。我们完成了互换的一半。因为b的值已经给到a了,所以我们可以对b进行操作,所以有第三步,将c的值给到b。因为c的值就是a,所以此时b的值就是a。我们完成了a和b值得互换。
原本挺简单的代码,让我解释了这么多,似乎有点画蛇添足。如果你真的觉得简单,咱们再接着看。
下面是很经典的冒泡排序
for(i=0;i<numbers.length-1;i++)
{
for(j=0;j<numbers.length-1-i;j++)
{
if(numbers[j]>numbers[j+1])
{
int temp=numbers[j];
numbers[j]=numbers[j+1];
numbers[j+1]=temp;
}
}
}
啊哈,好像也没什么难度。就是实现了数组numbers的j和j+1位置的互换。再接着看。
下面是JDK 1.7 HashMap transfer方法的一段代码
for (Entry<K,V> e : table) {
while(null != e) {
Entry<K,V> next = e.next;
if (rehash) {
e.hash = null == e.key ? 0 : hash(e.key);
}
int i = indexFor(e.hash, newCapacity);
e.next = newTable[i];
newTable[i] = e;
e = next;
}
}
这段代码到底干了啥?!简单来说,就是将HashMap旧数组中的元素移动到新数组中。table是个Entry数组,e代表一个Entry,Entry又是个列表。当然这不是我们关注的重点,我们此时值关注代码3,8,9,10行。这个似乎跟我们上面的有点不一样了。不一样的地方:一是变成了四个变量,包括e,e.next,next和newTable[i];二是变量之间是有关联的;三也是最重要的不同,此时的变量存储的不再是简单的数值,而是地址引用。最后一点有什么不同呢?在这一点上就是我们不能再将等于简单理解成是赋值(当然其实还是赋值,不过赋值的是地址引用),而是应该将等于认为是引用的变更。接着我们试着去理解这段代码的意义。
当我们执行第3行代码,我们的意思是将e的下一个节点赋值给next。此时next就是e.next的备份。我们就可以对e.next做一些操作了。看第8行,我们果然将newTable[i]赋值给e.next。newTable[i]代表新数组中索引为i的Entry链表的首节点。真正的含义就是e.next的指向变成了Entry链表的首节点所指向的地址。当是第一次while循环的时候,首节点为null,也就是说将e.next指向null。之后再看,我们会知道e会变成新数组Entry链表的尾结点,它的next节点应该指向null;当是大于第一次while循环的时候,首节点为当前e节点的前一个节点。这句话有点绕,其实就是e和e.next的值进行了互换,或者说让e指向了e.next,同时让e.next指向了e。接着看第9行,我们将e赋值给newTable[i],或者说让首节点指向了e,形成了首节点->e,e.next->原首节点。这个也就是注明的头插法(1.8的时候已经弃用了),因为我们总是将新节点放在Entry链表的头结点。第11号,就是将next赋值给e,作为下次循环的开始。
是不是觉得有点难度了,我们再接着看一个。
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
出自AQS的shouldParkAfterFailedAcquire。