日期: 2021 年 9 月 24 日

IOS -声音播放AudioServices

在 iPhone 应用或者是游戏的开发过程中,对声音的支持是必不可少的。在我做过的几个应用中,每个都涉及到音效,所以在这里做个简单的归纳,很多都是引用自《iPhone Application Programming Guide》 (需要有 Apple ID 才能打开链接),加了一些实际使用的经验。

iPhone OS 主要提供以下了几种播放音频的方法:

System Sound Services
AVAudioPlayer 类
Audio Queue Services
OpenAL

关于System Sound Services

System Sound Services 是*底层也是*简单的声音播放服务,调用 AudioServicesPlaySystemSound 这个方法就可以播放一些简单的音频文件,使用此方法只适合播放一些很小的提示或者警告音,因为它有很多限制:

■ 声音长度要小于 30 秒

■ In linear PCM 或者 IMA4 (IMA/ADPCM) 格式的

■ 打包成 .caf, .aif, 或者 .wav 的文件

■ 不能控制播放的进度

■ 调用方法后立即播放声音

■ 没有循环播放和立体声控制
另外,它还可以调用系统的震动功能,方法也很简单。具体的代码可以参考官方的示例 SysSound,但是官方的示例只有一些简单的用法,从文档中我们发现可以通过 AudioServicesAddSystemSoundCompletion 方法为音频播放添加 CallBack 函数,有了 CallBack 函数我们可以解决不少问题,比如可以克服 System Sound Services 本身不支持循环播放的问题。以下代码可以实现一个在程序中循环播放的背景音乐:

static void completionCallback (SystemSoundID mySSID) {
// Play again after sound play completion
AudioServicesPlaySystemSound(mySSID);
}
– (void ) playSound {
// Get the main bundle for the app
CFBundleRef mainBundle;
SystemSoundID soundFileObject;
mainBundle = CFBundleGetMainBundle ();

// Get the URL to the sound file to play
CFURLRef soundFileURLRef = CFBundleCopyResourceURL (
mainBundle,
CFSTR (“background” ),
CFSTR (“wav” ),
NULL
);
// Create a system sound object representing the sound file
AudioServicesCreateSystemSoundID (
soundFileURLRef,
&soundFileObject
);
// Add sound completion callback
AudioServicesAddSystemSoundCompletion (soundFileObject, NULL , NULL ,
completionCallback,
(void *) self );
// Play the audio
AudioServicesPlaySystemSound(soundFileObject);

}

另外一段:

#include <AudioToolbox/AudioToolbox.h>
#include <CoreFoundation/CoreFoundation.h>
// Define a callback to be called when the sound is finished
// playing. Useful when you need to free memory after playing.
static void MyCompletionCallback (
SystemSoundID mySSID,
void * myURLRef
) {
AudioServicesDisposeSystemSoundID (mySSID);
CFRelease (myURLRef); //CFURLCreateWithFileSystemPath()创建的需释放

CFRunLoopStop (CFRunLoopGetCurrent());
}
int main (int argc, const char * argv[]) {
// Set up the pieces needed to play a sound.
SystemSoundID mySSID;
CFURLRef myURLRef;
myURLRef = CFURLCreateWithFileSystemPath (
kCFAllocatorDefault,
CFSTR (“http://www.cnblogs.com/ComedyHorns.aif”),
kCFURLPOSIXPathStyle,
FALSE
);
// create a system sound ID to represent the sound file
OSStatus error = AudioServicesCreateSystemSoundID (myURLRef,
&mySSID);
// Register the sound completion callback.
// Again, useful when you need to free memory after playing.
AudioServicesAddSystemSoundCompletion (
mySSID,
NULL,
NULL,
MyCompletionCallback,
(void *) myURLRef
);
// Play the sound file.
AudioServicesPlaySystemSound (mySSID);
// Invoke a run loop on the current thread to keep the application
// running long enough for the sound to play; the sound completion
// callback later stops this run loop.
CFRunLoopRun ();
return 0;
}

除了对简单音频的限制外,你对于音频播放的方式也基本无法控制。一旦音频播放就会立即开始,而且是当前电话使用者设置的音量播放。你将无法循环播放声音,也无法控制立体声效果。不过你还是可以设置一个回调函数,在音频播放结束时被调用,这样你就可以对音频对象做清理工作,以及通知你的程序播放结束。

GitHub入门之二 参与一个项目编写

GitHub入门之二 参与一个项目编写

接上文:大多数时候我们也需要把别人的代码进行整合和修改,而不是简单的修改,这时就需要对一个项目进行修改。

注意,本系列文章主要说明在github网站上的操作,更多高级操作请使用git控制台

 

一、fork一个项目

 

%title插图%num

 

 

打开一个项目,点击界面右上角的的“fork”,就会在你自己的项目库里创建一个代码的拷贝。

这份代码的主人就是你,你可以对代码进行任何修改。

进入自己的主页后看到有了一份项目的拷贝,而且和原来的项目一模一样。

%title插图%num

 

画线部分体现了这个项目的源头,底部是项目的文件。接下来就可以对这个项目进行一系列的操作。

 

二、修改项目

git重要的功能就协作开发。当我们觉得代码有必要进行修改的时候,可以修改,并把更改提交给代码原始的主人,这样就完成了一次代码的协作。

点击我们项目中的某一个文件。可以看到这个文件的内容

%title插图%num

其中上面的 contributor是这个文件的贡献者,他们为这个文件的编写做出了贡献。

如果你也想做贡献,就点击EDIT,进入编辑状态

%title插图%num

 

添加一些文字

 

%title插图%num

 

然后把我们的更改进行提交(commit),提交时要填写这次修改的内容,让其它人清楚修改了什么内容。填写完毕后点击commit changes,这时代码进行了更新,contributor里也看到了我的ID~!

%title插图%num

三、提交修改申请

 

注意:刚才我们所做的内容都是在 自己的项目下进行的,举个例子,是在 icyfox-bupt/bitcoin 下进行的, 原始的项目 bitcoin/bitcoin 并没有发生变化。毕竟如果这样就可以修改代码的话也太不安全了!

如果想把我们的代码同步到原项目中,需要向原项目主提交申请:

%title插图%num

 

回到项目首页,点击Pull Request,新建一个PR(Pull Request),在接下来的页面中会显示这次提交对代码所做的改动。

 

%title插图%num

 

确认无误后点击上方的Create Pull Request,然后进行代码说明,再点击Send Pull Request,完成这次PR的申请。

 

这样在原项目的Pull Request列表里,就会出现我们的这一条。

 

%title插图%num

 

原项目的作者可以判断我们的代码是否有用,来决定是否合并。当然由于我们的代码完全是无效的,所以一定会进入close中。

 

以上是在github网站上对项目进行git操作的一些步骤,注意这些操作都是网页操作,如果要正经的使用,还是要使用git bash这样的控制台。

 

GitHub入门之一:使用github下载项目

GitHub入门之一:使用github下载项目

git作为目前比较流行的版本控制系统,被各个互联网公司广泛使用着。目前国外的网站有GitHub,国内的有CSDN和OSCHINA的git。

使用git可以很方便地进行多人协作和版本控制。作为一个入门小白,我先从我的角度来和大家一起看看如何使用github,我注重操作,不注重原理。

如果想深入学习git,建议看类似git入门这样的文档

 

这篇讲如何使用其它人的代码,*初,github被我当作一个下载开源库的好地方。当我做android需要一个actionbar、chart、menu的时候,git上总是能找到。这里不乏有世界上*好的程序员们在工作。为了让我们的开发更快,需要使用他们做好的项目

 

1、打开github,*上方会有一个搜索框,在里面输入我们可能用到的库,比如chart

%title插图%num

 

2、会搜索出github中所有公开的chart项目:

%title插图%num

 

3、由于我是做安卓的,在这里我选择JAVA

%title插图%num

我们看到这里给出了让我们满意的筛选,而且有472个项目之多,选择*个:

 

4、Android-charts

打开Android-Charts后我们看到了一个项目的界面:主界面是项目的名称和代码树,右侧是项目的一些属性(话题、提交次数、人数等等),下方是作者对项目的介绍:

%title插图%num

 

在代码结构的区域,可以通过点击文件直接查看当前的文件内容。

项目介绍区域,作者会对项目的功能,使用方法进行介绍。

如果想直接下载这个项目的压缩包,点击右下角的DownLoad ZIP;

如果想直接把项目导入到Eclipse里(因为我做android嘛),打开Eclipse,选择File–>Import–>Projects from git –> URI ,输入HTTPS网址–next

%title插图%num

选择一个分支:如Master — > next –>选择保存目录,eclipse会自动帮我们下载内容

%title插图%num

*后我们把项目导入就大功告成了!~

 

这样的好处是会有git的地址,如果作者某天更新了程序,我们可以很方便地更新我们的本地git库。

 

Gradle学习系列之一——Gradle快速入门

Gradle学习系列之一——Gradle快速入门

这是一个关于Gradle的学习系列,其中包含以下文章:

    1. Gradle快速入门
    2. 创建Task的多种方法
    3. 读懂Gradle语法
    4. 增量式构建
    5. 自定义Property
    6. 使用java Plugin
    7. 依赖管理
    8. 构建多个Project
    9. 自定义Task类型
    10. 自定义Plugin

 

%title插图%num

 

 

和Maven一样,Gradle只是提供了构建项目的一个框架,真正起作用的是Plugin。Gradle在默认情况下为我们提供了许多常用的Plugin,其中包括有构建Java项目的Plugin,还有War,Ear等。与Maven不同的是,Gradle不提供内建的项目生命周期管理,只是java Plugin向Project中添加了许多Task,这些Task依次执行,为我们营造了一种如同Maven般项目构建周期。更多有关Maven的知识,读者可以访问Maven官网,或者可以参考笔者写的Maven学习系列文章。

 

现在我们都在谈领域驱动设计,Gradle本身的领域对象主要有Project和Task。Project为Task提供了执行上下文,所有的Plugin要么向Project中添加用于配置的Property,要么向Project中添加不同的Task。一个Task表示一个逻辑上较为独立的执行过程,比如编译Java源代码,拷贝文件,打包Jar文件,甚至可以是执行一个系统命令或者调用Ant。另外,一个Task可以读取和设置Project的Property以完成特定的操作。

让我们来看一个*简单的Task,创建一个build.gradle文件,内容如下:

task helloWorld << {
   println "Hello World!"
}

 

这里的“<<”表示向helloWorld中加入执行代码——其实就是groovy代码。Gradle向我们提供了一整套DSL,所以在很多时候我们写的代码似乎已经脱离了groovy,但是在底层依然是执行的groovy。比如上面的task关键字,其实就是一个groovy中的方法,而大括号之间的内容则表示传递给task()方法的一个闭包。除了“<<”之外,我们还很多种方式可以定义一个Task,我们将在本系列后续的文章中讲到。

在与build.gradle相同的目录下执行:

gradle helloWorld

 

命令行输出如下:

复制代码
复制代码
:helloWorld
Hello World!

BUILD SUCCESSFUL

Total time: 2.544 secs
复制代码
复制代码

 

在默认情况下,Gradle将当前目录下的build.gradle文件作为项目的构建文件。在上面的例子中,我们创建了一个名为helloWorld的Task,在执行gradle命令时,我们指定执行这个helloWorld Task。这里的helloWorld是一个DefaultTask类型的对象,这也是定义一个Task时的默认类型,当然我们也可以显式地声明Task的类型,甚至可以自定义一个Task类型(我们将在本系列的后续文章中讲到)。

比如,我们可以定义一个用于文件拷贝的Task:

task copyFile(type: Copy) {
   from 'xml'
   into 'destination'
}

 

以上copyFile将xml文件夹中的所有内容拷贝到destination文件夹中。这里的两个文件夹都是相对于当前Project而言的,即build.gradle文件所在的目录。

Task之间可以存在依赖关系,比如taskA依赖于taskB,那么在执行taskA时,Gradle会先执行taskB,然后再执行taskA。声明Task依赖关系的一种方式是在定义一个Task的时候:

task taskA(dependsOn: taskB) {
   //do something
}

 

Gradle在默认情况下为我们提供了几个常用的Task,比如查看Project的Properties、显示当前Project中定义的所有Task等。可以通过一下命令查看Project中所有的Task:

gradle tasks

 

输出如下:

复制代码
复制代码
:tasks

------------------------------------------------------------
All tasks runnable from root project
------------------------------------------------------------

Build Setup tasks
-----------------
setupBuild - Initializes a new Gradle build. [incubating]
wrapper - Generates Gradle wrapper files. [incubating]

Help tasks
----------
dependencies - Displays all dependencies declared in root project 'gradle-blog'.
dependencyInsight - Displays the insight into a specific dependency in root project 'gradle-blog'.
help - Displays a help message
projects - Displays the sub-projects of root project 'gradle-blog'.
properties - Displays the properties of root project 'gradle-blog'.
tasks - Displays the tasks runnable from root project 'gradle-blog'.

Other tasks
-----------
copyFile
helloWorld

To see all tasks and more detail, run with --all.

BUILD SUCCESSFUL

Total time: 2.845 secs
复制代码
复制代码

 

可以看到,除了我们自己定义的copyFile和helloWorld之外,Gradle还默认为我们提供了dependencies、projects和properties等Task。dependencies用于显示Project的依赖信息,projects用于显示所有Project,包括根Project和子Project,而properties则用于显示一个Project所包含的所有Property。

在默认情况下,Gradle已经为Project添加了很多Property,我们可以调用以下命令进行查看:

gradle properties

 

输出如下:

复制代码
复制代码
:properties

------------------------------------------------------------
Root project
------------------------------------------------------------

allprojects: [root project 'gradle-blog']
ant: org.gradle.api.internal.project.DefaultAntBuilder@1342097

buildDir: /home/davenkin/Desktop/gradle-blog/build
buildFile: /home/davenkin/Desktop/gradle-blog/build.gradle
...
configurations: []
convention: org.gradle.api.internal.plugins.DefaultConvention@11492ed
copyFile: task ':copyFile'
...
ext: org.gradle.api.internal.plugins.DefaultExtraPropertiesExtension@1b5d53a
extensions: org.gradle.api.internal.plugins.DefaultConvention@11492ed
...
helloWorld: task ':helloWorld'
...
plugins: [org.gradle.api.plugins.HelpTasksPlugin@7359f7]
project: root project 'gradle-blog'
...
properties: {...}
repositories: []

tasks: [task ':copyFile', task ':helloWorld']
version: unspecified

BUILD SUCCESSFUL

Total time: 2.667 secs
复制代码
复制代码

 

在以上Property中,allprojects表示所有的Project,这里只包含一个根Project,在多项目构建中,它将包含多个Project;buildDir表示构建结果的输出目录;我们自己定义的helloWorld和copyFile也成为了Project中的Property。另外,Project还包括用于执行Ant命令的DefaultAntBuilder(Property名为ant)和Project的描述属性description。

 

【Java集合学习】HashMap源码之“拉链法”散列冲突的解决

【Java集合学习】HashMap源码之“拉链法”散列冲突的解决

1.HashMap的概念

HashMap 是一个散列表,它存储的内容是键值对(key-value)映射。

HashMap 继承于AbstractMap,实现了Map、Cloneable、java.io.Serializable接口。
HashMap 的实现不是同步的,这意味着它是线程不安全的。它的key、value都可以为null。此外,HashMap中的映射是无序的。

%title插图%num

  本文重点是介绍HashMap中的“拉链法”解决散列冲突。如果想了解其他方面的知识可参考http://www.cnblogs.com/skywang12345/p/3310835.html。

下面先介绍一下散列表及散列冲突的基本概念。

 

2.Hash表的概念

我们知道,有一种数据结构能够快速地查找所需的对象(O(1)时间复杂度),这就是散列表(hash table)。散列码(hash code)是由对象的实例产生的一个整数。

更准确的说,具有不同的数据域的对象将产生不同的散列码。如下图(这里顺带提一下散列表的概念,即哈希表的概念):%title插图%num

设所有可能出现的关键字集合记为U(简称全集)。实际发生(即实际存储)的关键字集合记为K(|K|比|U|小得多)。
散列方法是使用函数h将U映射到表T[0..m-1]的下标上(m=O(|U|))。这样以U中关键字为自变量,以h为函数的运算结果就是相应结点的存储地址。从而达到在O(1)时间内就可完成查找。
其中:
① h:U→{0,1,2,…,m-1} ,通常称h为散列函数(Hash Function)。散列函数h的作用是压缩待处理的下标范围,使待处理的|U|个值减少到m个值,从而降低空间开销。
② T为散列表(Hash Table)。
③ h(Ki)(Ki∈U)是关键字为Ki结点存储地址(亦称散列值或散列地址)。
④ 将结点按其关键字的散列地址存储到散列表中的过程称为散列(Hashing)

3.散列冲突

  两个不同的关键字,由于散列函数值相同,因而被映射到同一表位置上。该现象称为冲突(Collision)或碰撞。发生冲突的两个关键字称为该散列函数的同义词(Synonym)。

上图中的k2≠k5,但h(k2)=h(k5),故k2和K5所在的结点的存储地址相同。

有了冲突,那自然要尽可能地去同时还需要确定解决冲突的方法,使发生冲突的同义词能够存储到表中。

HashMap中采用的“拉链法”就是一种冲突解决的方式(hash函数的设计才是冲突避免,但不是一种完全的冲突解决方法),如下图所示为“拉链法”结构。

%title插图%num

但是HashMap中的节点是Map.Entry类型的,而不是简单的value,如下图所示,左边是一个Node<K,V>[] table数组(在jdk6中是Entry<K,V>数组),Node是Map.Entry的实现类。

%title插图%num

那么它是如何解决冲突的呢?即key值不同的两个或多个Map.Entry<K,V>可能会插在同一个桶下面,但是当查找到某个特定的hash值的时候,下面挂了很多个<K,V>映射,怎么确定哪个是我要找的那个<K,V>呢?这就是HashMap底层结构的一个亮点,在它的Entry中不仅仅只是插入value的,他是插入整个Entry 的,里面包含key和value的,所以能识别同一个hash值下的不同Map.Entry,想要了解更多,建议查看源码。

HashMap实现原理分析

HashMap实现原理分析

1. HashMap的数据结构

数据结构中有数组和链表来实现对数据的存储,但这两者基本上是两个*端。

      数组

数组存储区间是连续的,占用内存严重,故空间复杂的很大。但数组的二分查找时间复杂度小,为O(1);数组的特点是:寻址容易,插入和删除困难;

链表

链表存储区间离散,占用内存比较宽松,故空间复杂度很小,但时间复杂度很大,达O(N)。链表的特点是:寻址困难,插入和删除容易。

哈希表

那么我们能不能综合两者的特性,做出一种寻址容易,插入删除也容易的数据结构?答案是肯定的,这就是我们要提起的哈希表。哈希表((Hash table)既满足了数据的查找方便,同时不占用太多的内容空间,使用也十分方便。

哈希表有多种不同的实现方法,我接下来解释的是*常用的一种方法—— 拉链法,我们可以理解为“链表的数组” ,如图:

%title插图%num

 

%title插图%num

 

从上图我们可以发现哈希表是由数组+链表组成的,一个长度为16的数组中,每个元素存储的是一个链表的头结点。那么这些元素是按照什么样的规则存储到数组中呢。一般情况是通过hash(key)%len获得,也就是元素的key的哈希值对数组长度取模得到。比如上述哈希表中,12%16=12,28%16=12,108%16=12,140%16=12。所以12、28、108以及140都存储在数组下标为12的位置。

HashMap其实也是一个线性的数组实现的,所以可以理解为其存储数据的容器就是一个线性数组。这可能让我们很不解,一个线性的数组怎么实现按键值对来存取数据呢?这里HashMap有做一些处理。

首先HashMap里面实现一个静态内部类Entry,其重要的属性有 key , value, next,从属性key,value我们就能很明显的看出来Entry就是HashMap键值对实现的一个基础bean,我们上面说到HashMap的基础就是一个线性数组,这个数组就是Entry[],Map里面的内容都保存在Entry[]里面。

    /**
     * The table, resized as necessary. Length MUST Always be a power of two.
     */

transient Entry[] table;

2. HashMap的存取实现

既然是线性数组,为什么能随机存取?这里HashMap用了一个小算法,大致是这样实现:

// 存储时:
int hash = key.hashCode(); // 这个hashCode方法这里不详述,只要理解每个key的hash是一个固定的int值
int index = hash % Entry[].length;
Entry[index] = value;

// 取值时:
int hash = key.hashCode();
int index = hash % Entry[].length;
return Entry[index];

1)put

疑问:如果两个key通过hash%Entry[].length得到的index相同,会不会有覆盖的危险?

这里HashMap里面用到链式数据结构的一个概念。上面我们提到过Entry类里面有一个next属性,作用是指向下一个Entry。打个比方, *个键值对A进来,通过计算其key的hash得到的index=0,记做:Entry[0] = A。一会后又进来一个键值对B,通过计算其index也等于0,现在怎么办?HashMap会这样做:B.next = A,Entry[0] = B,如果又进来C,index也等于0,那么C.next = B,Entry[0] = C;这样我们发现index=0的地方其实存取了A,B,C三个键值对,他们通过next这个属性链接在一起。所以疑问不用担心。也就是说数组中存储的是*后插入的元素。到这里为止,HashMap的大致实现,我们应该已经清楚了。

 public V put(K key, V value) {
        if (key == null)
            return putForNullKey(value); //null总是放在数组的*个链表中
        int hash = hash(key.hashCode());
        int i = indexFor(hash, table.length);
        //遍历链表
        for (Entry<K,V> e = table[i]; e != null; e = e.next) {
            Object k;
            //如果key在链表中已存在,则替换为新value
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }
        modCount++;
        addEntry(hash, key, value, i);
        return null;

}

 

void addEntry(int hash, K key, V value, int bucketIndex) {
    Entry<K,V> e = table[bucketIndex];
    table[bucketIndex] = new Entry<K,V>(hash, key, value, e); //参数e, 是Entry.next
    //如果size超过threshold,则扩充table大小。再散列
    if (size++ >= threshold)
            resize(2 * table.length);
}

当然HashMap里面也包含一些优化方面的实现,这里也说一下。比如:Entry[]的长度一定后,随着map里面数据的越来越长,这样同一个index的链就会很长,会不会影响性能?HashMap里面设置一个因子,随着map的size越来越大,Entry[]会以一定的规则加长长度。

2)get

 public V get(Object key) {
        if (key == null)
            return getForNullKey();
        int hash = hash(key.hashCode());
        //先定位到数组元素,再遍历该元素处的链表
        for (Entry<K,V> e = table[indexFor(hash, table.length)];
             e != null;
             e = e.next) {
            Object k;
            if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
                return e.value;
        }
        return null;
}

 

3)null key的存取

null key总是存放在Entry[]数组的*个元素。

   private V putForNullKey(V value) {
        for (Entry<K,V> e = table[0]; e != null; e = e.next) {
            if (e.key == null) {
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }
        modCount++;
        addEntry(0, null, value, 0);
        return null;
    }
    private V getForNullKey() {
        for (Entry<K,V> e = table[0]; e != null; e = e.next) {
            if (e.key == null)
                return e.value;
        }
        return null;

}

4)确定数组index:hashcode % table.length取模

HashMap存取时,都需要计算当前key应该对应Entry[]数组哪个元素,即计算数组下标;算法如下:

   /**
     * Returns index for hash code h.
     */
    static int indexFor(int h, int length) {
        return h & (length-1);
    }
按位取并,作用上相当于取模mod或者取余%。
这意味着数组下标相同,并不表示hashCode相同。

5)table初始大小

  public HashMap(int initialCapacity, float loadFactor) {
        …..         // Find a power of 2 >= initialCapacity
        int capacity = 1;
        while (capacity < initialCapacity)
            capacity <<= 1;
        this.loadFactor = loadFactor;
        threshold = (int)(capacity * loadFactor);
        table = new Entry[capacity];
        init();
    }

注意table初始大小并不是构造函数中的initialCapacity!!

而是 >= initialCapacity的2的n次幂!!!!

————为什么这么设计呢?——

3. 解决hash冲突的办法

  1. 开放定址法(线性探测再散列,二次探测再散列,伪随机探测再散列)
  2. 再哈希法
  3. 链地址法
  4. 建立一个公共溢出区

Java中hashmap的解决办法就是采用的链地址法。

 

4. 再散列rehash过程

当哈希表的容量超过默认容量时,必须调整table的大小。当容量已经达到*大可能值时,那么该方法就将容量调整到Integer.MAX_VALUE返回,这时,需要创建一张新表,将原表的映射到新表中。

   /**
     * Rehashes the contents of this map into a new array with a
     * larger capacity.  This method is called automatically when the
     * number of keys in this map reaches its threshold.
     *
     * If current capacity is MAXIMUM_CAPACITY, this method does not
     * resize the map, but sets threshold to Integer.MAX_VALUE.
     * This has the effect of preventing future calls.
     *
     * @param newCapacity the new capacity, MUST be a power of two;
     *        must be greater than current capacity unless current
     *        capacity is MAXIMUM_CAPACITY (in which case value
     *        is irrelevant).
     */
    void resize(int newCapacity) {
        Entry[] oldTable = table;
        int oldCapacity = oldTable.length;
        if (oldCapacity == MAXIMUM_CAPACITY) {
            threshold = Integer.MAX_VALUE;
            return;
        }
        Entry[] newTable = new Entry[newCapacity];
        transfer(newTable);
        table = newTable;
        threshold = (int)(newCapacity * loadFactor);

}

 

    /**
     * Transfers all entries from current table to newTable.
     */
    void transfer(Entry[] newTable) {
        Entry[] src = table;
        int newCapacity = newTable.length;
        for (int j = 0; j < src.length; j++) {
            Entry<K,V> e = src[j];
            if (e != null) {
                src[j] = null;
                do {
                    Entry<K,V> next = e.next;
                    //重新计算index
                    int i = indexFor(e.hash, newCapacity);
                    e.next = newTable[i];
                    newTable[i] = e;
                    e = next;
                } while (e != null);
            }
        }

}

友情链接: SITEMAP | 旋风加速器官网 | 旋风软件中心 | textarea | 黑洞加速器 | jiaohess | 老王加速器 | 烧饼哥加速器 | 小蓝鸟 | tiktok加速器 | 旋风加速度器 | 旋风加速 | quickq加速器 | 飞驰加速器 | 飞鸟加速器 | 狗急加速器 | hammer加速器 | trafficace | 原子加速器 | 葫芦加速器 | 麦旋风 | 油管加速器 | anycastly | INS加速器 | INS加速器免费版 | 免费vqn加速外网 | 旋风加速器 | 快橙加速器 | 啊哈加速器 | 迷雾通 | 优途加速器 | 海外播 | 坚果加速器 | 海外vqn加速 | 蘑菇加速器 | 毛豆加速器 | 接码平台 | 接码S | 西柚加速器 | 快柠檬加速器 | 黑洞加速 | falemon | 快橙加速器 | anycast加速器 | ibaidu | moneytreeblog | 坚果加速器 | 派币加速器 | 飞鸟加速器 | 毛豆APP | PIKPAK | 安卓vqn免费 | 一元机场加速器 | 一元机场 | 老王加速器 | 黑洞加速器 | 白石山 | 小牛加速器 | 黑洞加速 | 迷雾通官网 | 迷雾通 | 迷雾通加速器 | 十大免费加速神器 | 猎豹加速器 | 蚂蚁加速器 | 坚果加速器 | 黑洞加速 | 银河加速器 | 猎豹加速器 | 海鸥加速器 | 芒果加速器 | 小牛加速器 | 极光加速器 | 黑洞加速 | movabletype中文网 | 猎豹加速器官网 | 烧饼哥加速器官网 | 旋风加速器度器 | 哔咔漫画 | PicACG | 雷霆加速