`

Java集合API

阅读更多
1. ArrayList
(1) 当List<String> list = new ArrayList<String> ();
其构造器执行:
this.elementData = new Object[10];
即创建一个大小为10的Object类型的数组。

(2) add(E): 用传入的对象填充数组的下一个位置。
若数组长度不够用了怎么办呢?
1) minCapacity = 数组中已有元素的数目 + 1;
2) 用minCapacity与数组的大小比较(注:数组中元素的数目不一定等于数组的大小)
若 minCapacity > 数组的大小(oldCapacity)
         则 newCapacity = oldCapacity * 1.5 + 1;
              若newCapacity仍 < minCapacity
                    则newCapacity = minCapacity;

然后用newCapacity作为新数组的长度。
用elementData = Arrays.copyOf(elementData, newCapacity);来生成新的数组的长度。

(3) add(int, E): 将元素插入到指定下标的位置上
首先要确保要插入指定的index下标必须 < 数组的长度
然后将元素放到指定的index位置,将index之后的元素向右移动一位。

(4) remove(E): 使用ArrayList的fastRemove方法删除在遍历中所有为E的对象。
fastRemove的实现为:
将index之后的对象往前移一位,并将数组的最后一个元素的位置置为null.

(5) remove(int): 删除指定位置的对象
remove(int)相比remove(E)少了对象位置的查找,所以性能会好些。

(6) iterator(): 该方法由ArrayList的父类AbstractList实现,每次调用iterator方法时,都会创建一个新的AbstractList内部类Iter类的实例。
1)当调用hasNext()时,会比较当前指向的数组位置是否等于数组中所有元素的数目,相等则返回false.
2)当调用next()时,则通过index调用get(index+1)来获取元素。

(7) contains(E): 判断E在ArrayList中是否存在
若E为null, 则用==判断
若E不为null, 则用equals判断

ArrayList总结:
1) ArrayList是基于数组方式实现的,但可以无限的扩容。
2)在ArrayList中插入元素时,可能要扩容;但在删除元素时,并不会减小数组的容量,如果希望缩小容量,则需要调用ArrayList的trimToSize().
3)ArrayList是非线程安全的。

2. LinkedList
LinkedList是基于双向链表实现的,即集合中的每一个元素,都知道其前一个元素和后一个元素的位置。
在LinkedList中,以一个内部类Entry来代表集合中的元素。
元素的值赋值给Entry内部类的element属性,Entry的next属性指向元素的后一个元素;Entry的previous属性指向元素的前一个元素。
基于这样的机制,可以快速实现集合中元素的移动。
(1) LinkedList():
在创建LinkedList对象的实例时,先创建一个element属性为null, next属性为null, previous属性为null的Entry对象,并赋值给LinkedList的全局变量header: header.next = header.previous = header;
即在执行构造器时,将header的next, header的previous都指向header,以形成双向链表所需的闭环。

(2) add(E)
当向LinkedList中增加元素时:
1)建一个Entry对象
2)将此Entry对象的next指向header
3)将此Entry对象的previous指向header的previous
4)将新元素的previous对象的next指针指向新元素
5)将新元素的next对象的next对象的previous指针指向新元素
以此完成双向链表共4个指针的修改。

LinkedList的add方法,不用想ArrayList,要考虑数组扩容以及复制的问题,但它每增加一个元素,就要增加一个Entry对象,并修改它相邻的两个元素的属性

(3) remove(E)
LinkedList删除元素时,要遍历整个LinkedList,匹配过程与ArrayList一致,但找到元素后,删除元素的过程比ArrayList快多了:只需要修改双向指针,不用移动元素。

(4) get(int)
LinkedList存于双向链表中,所以不像数组有下标,get过程比ArrayList麻烦:
若index < LinkedList长度的一半
    则从头查找index位置的元素
否则
    则从尾向前查找index位置的元素

(5) iterator()
也由其父类AbstractList实现。
但由于LinkedList是双向链表,所以也可以调用hasPrevious()与previous()向前遍历。

(6) contains(E)
遍历所有元素,过程与ArrayList一致
对null用==,对非null用equals

LinkedList总结:
1)LinkedList是基于链表实现的。
2)LinkedList在插入元素时,要创建一个Entry对象,并切换相应元素的前后引用;在查找元素时,要遍历整个链表;在删除元素时,找到要删除的元素,然后将此元素从链表上删除。
3)LinkedList是非线程安全的

3. Vector
Vector与ArrayList一样,也是基于Object数组实现的

(1) add(E)
Vector的add方法加入了Synchronized关键字,所以是线程安全的
与ArrayList不同的是:扩容方法不同。
capacityIncrement为Vector扩容时自动增加的大小, 有创建Vector时有构造器参数传入,默认为0
若capacityIncrement > 0
     则newCapacity = oldCapacity + capacityIncrement;
若capacityIncrement <= 0
     则capacityIncrement = oldCapacity * 2;

(2) Vector的其他方法都与ArrayList一致,唯一不同的是加入了Synchronized关键字,变成了线程安全。

4. Stack
Stack继承自Vector, 在其基础上实现了栈的后进先出(LIFO): 主要增加了push(), pop(), peek()三个方法。
(1) push()
向Stack中增加元素,具体过程同Vector的add(E)

(2) peek()
先计算出数组的大小,然后获取数组的最后一个元素。

(3) pop()
先用peek()获取到数组中最后一个元素,然后从数组中删除这个元素

Stack总结:Stack是基于Vector的实现,遵循LIFO原则。

5. HashSet
HashSet是对Set接口的实现。
Set接口与List接口的区别:Set是无序不可重复;List是有序可重复。

(1) HashSet()
新建一个HashMap对象

(2) add(E)
调用HashMap的put(Object, Object)方法来完成:将需要增加的元素作为map中的key, value则传入一个已经创建好的对象:private static final Object PRESENT = new Object();

(3) remove(E)
调用HashMap的remove(E)

(4) contains(E)
调用HashMap的containsKey(E)

(5) iterator()
调用Hashmap的内部类keySet的iterator()方法

HashSet总结:
(1) HashSet是基于HashMap实现的,无容量限制。
(2) Hashset是非线程安全的

6. TreeSet
TreeSet基于TreeMap实现。
TreeSet与HashSet的主要区别在于,TreeSet支持排序。

TreeSet与TreeMap的关系类似于HashSet与HashMap的关系

TreeSet与HashSet一样,也是完全基于Map来完成的,并且同样不支持get(int)来获取指定元素的位置。TreeSet还提供了一些排序方面的支持,
例如:我的代码中的“TreeSet排序”中,可以加入实现了Comparable接口类的实例作为TreeSet的元素,这样即使加入的顺序是混乱的,TreeSet会根据compareTo方法定义来自动做排序。打印结果为:
age:20,, age:21,, age:22,,

TreeSet总结:
(1) TreeSet基于TreeMap实现,支持排序
(2) TreeSet是非线程安全的

7. HashMap(重中之重)
(1) HashMap的数据结构
HashMap的数据结构是“数组+链表”。即HashMap的底层就是一个数组,而数组中的每个元素又是一个链表。

当新建一个HashMap的时候,会初始化一个Entry类型的数组:
transient Entry[] table;

static class Entry<K,V> implements Map.Entry<K,V> {
    final K key;
    V value;
    Entry<K,V> next;
    final int hash;
    ……
}
Entry对象就是数组的元素,每个Entry对象就是一个Key-Value对,它持有指向链表下一个对象的引用next.

(2) HashMap的存,取,删除实现
1)put(Object key, Object value): 向HashMap加入key-value, 若key已经存在,则用newValue替换掉oldValue, 并返回oldValue.

put的源代码如下:
public V put(K key, V value) {
    // HashMap允许存放null键和null值。
    // 当key为null时,调用putForNullKey方法,将value放置在数组第一个位置。
    if (key == null)
        return putForNullKey(value);
    // 根据key的hashCode重新计算hash值。
    int hash = hash(key.hashCode());
    // 搜索指定hash值所对应table中的索引(table为HashMap Entry[]对象数组的实例)。
    int i = indexFor(hash, table.length);
    // 如果 i 索引处的 Entry 不为 null,通过循环不断遍历 e 元素的下一个元素。
    for (Entry<K,V> e = table[i]; e != null; e = e.next) {
        Object k;
        if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
            //如果在HashMap中找到了与要插入的key相同的元素,说明要用新的value替换掉旧的value.
            V oldValue = e.value;
            e.value = value;
            e.recordAccess(this);
            return oldValue;
        }
    }
    //如果执行到此处,说明要插入的key在HashMap中无这个key
    // 如果i索引处的Entry为null,表明此处还没有Entry。
    // modCount记录HashMap中修改结构的次数
    modCount++;
    // 将key、value添加到i索引处。
    addEntry(hash, key, value, i);
    return null;
}

当执行put的时候,先根据key的hashcode重新计算hash值,然后根据hash值获取这个元素在数组中的位置(即下标),如果数组该位置已经有其他元素了,那么在这个位置上的元素将以链表的形式存放。新加入的放在链表的表头,最先加入的放在链表的表尾。如果数组上该位置没有元素,则直接将新的元素放在该位置上。

2)V get(Object key):在HashMap中获取指定key的value.
源代码如下:
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;
}
从HashMap中get元素时,先计算key的hashcode, 然后根据hashcode找到数组中对应位置的某一元素,再通过key的equals方法在对应位置的链表中找到需要的元素。

3)remove(Object key):删除指定的key的元素,返回值为删掉的元素。
remove过程与get类似:只是在找到指定的key之后,如果数组上的元素等于key,则将数组上的该元素置为其next元素的值;如数组上的元素不等于key, 则对链表遍历,一直到找到匹配的key或链表的结尾。

(3) HashMap的扩容
当HashMap中元素越来越多的时候,hash冲突的几率也就越来越高,因为数组的长度是固定的。为了提高查询的效率,就需要对HashMap的数组进行扩容,扩容时最消耗性能的点就出现了:原数组中的数据必须重新计算其在新数组中的位置,并放进去,这就是HashMap的resize.

那么,什么时候需要对HashMap进行扩容呢?
当HashMap中元素的个数超过“数组的大小*loadFactor”的时候,就会进行数组扩容,loadFactor默认值为0.75, 数组的默认大小是16.那么当HashMap中的元素大小超过16 * 0.75 =12的时候(这个值就是HashMap源码中的threshold值,也叫临界值),就把数组大小扩大为16 * 2 = 32,即扩大一倍,然后重新计算每个元素在数组中的位置,而这时一个非常消耗性能的操作。
所以我们如果已经预知HashMap中元素的格式,那么就预设元素的个数,这样能够有效的提高HashMap的性能。

HashMap包含如下几个构造器:
1)HashMap(): 构建一个初始大小为16的,负载因子为0.75的HashMap
2)HashMap(int initialCapacity): 构建一个初始大小为initialCapacity,负载因子为0.75的HashMap
3)HashMap(int initialCapacity, float loadFactor): 构建一个初始大小为initialCapacity,负载因子为loadFactor的HashMap
注:
initialCapacity:HashMap的最大容量,即为底层数组的长度
loadFactor:负载因子:散列表的实际元素数目(n)/散列表的容量(m).

HashMap总结:
1)HashMap与HashTable的区别在于HashMap是非同步的,即非线程安全的。HashMap的key和value都允许为null, 而HashTable不允许。
2) HashMap的key都是保存在Set中,因此HashMap中的对象是无序的,如果要保重Map中的对象按顺序排列,最好使用TreeMap.

这样遍历HashMap:
public void pringHashMap (HashMap hashMap)
{
    if (hashMap == null) {
        return;
    }
    Iterator iter = hashMap.entrySet().iterator();
    while (iter.hasNext) {
        Map.Entry entry = (Map.Entry)iter.next();
        System.out.println("key = " + entry.getKey());
        System.out.println("value = " + entry.getValue());
    }
}


8. TreeMap
TreeMap是支持排序的Map实现,其实现方式与HashMap完全不同。
(1) Put(Object key, Object value):
当调用put时,先判断root属性是否为Null,如是,则创建一个新的Entry对象,并赋值给root属性,如不是,则首先判断是否传入了指定的Comparable实现,
    如已传入,则基于红黑树的方式遍历,基于comparable来比较key应放在当前节点的左边还是右边,如找到相等的key, 则直接替换其value, 并返回oldValue.
    如没有传入, 则判断key是否为Null,是则抛出NPE.

综上,TreeMap是典型的红黑树的实现。

(2) get(Object)
TreeMap的查找过程就是典型的红黑树的查找过程,从根对象开始往下比较,一直找到相等的key, 并返回其value。
与put的处理方式相同,如果未传入Comparable的实现,当传入的Object为null时,则直接抛出NPE.

TreeMap是红黑树的实现
TreeMap是非线程安全的。

9. 如何选择集合类
(1) 先根据功能选择要用List, Set, Map.
List适用于允许重复元素的单个对象的集合
Set适用于不允许重复元素的单个对象的集合
Map适用于key-value结构的集合

(2) 在选择好List, Set, Map后,就要选择相应的实现类了。
ArrayList:通过位置读取元素
LinkedList:要对头尾操作,及插入到指定位置
Vector:线程安全的ArrayList
Stack:线程安全的后进先出
HashSet: 元素无序且不允许重复
TreeSet: 要排序的无重复元素
HashMap:绝大部分的key-value
TreeMap:要排序存放的key-value.
  • 大小: 13.5 KB
分享到:
评论
发表评论

文章已被作者锁定,不允许评论。

相关推荐

    java集合API

    ### Java集合API详解 #### 一、引言 在Java编程中,集合API是一个至关重要的组成部分,对于希望深入了解Java语言并提升编程技能的开发者来说,掌握这一API的重要性不言而喻。本文旨在深入剖析Java集合API的核心...

    Java集合API用例学习

    Java集合API是Java编程语言中不可或缺的一部分,它为开发者提供了数据结构和对象组织的强大工具。在Java中,集合API包括接口、类以及它们之间的关系,主要用于存储和操作对象。理解和熟练掌握这些API对于任何Java...

    Java的api集合

    2. **集合框架**:Java集合框架是Java API中的重要部分,提供了`ArrayList`、`LinkedList`、`HashSet`、`HashMap`等数据结构,以及`List`、`Set`、`Map`等接口。它们允许我们高效地存储、检索和操作对象。 3. **IO...

    JAVA_API1.6文档(中文)

    本文档是 Java 2 Platform Standard Edition 6.0 的 API 规范。 请参见: 描述 Java 2 Platform 软件包 java.applet 提供创建 applet 所必需的类和 applet 用来与其 applet 上下文通信的类。 java.awt 包含...

    关于 Java Collections API 您不知道的 5 件事,第 2 部分

    通过深入理解这些知识点,开发者不仅可以写出更高效、更安全的代码,还能在面试或者实际工作中展现出对Java集合API的深度掌握。在阅读和分析提供的代码示例`5things3-code`后,将会有更具体的实践理解。例如,代码...

    JAVA8API-官方文档下载-中文版

    **JAVA8 API 中文官方文档概述** JAVA8 API 是Java开发者的重要参考资料,它详细阐述了Java 8平台的核心类库,包括各种接口、类、枚举和注解等。这份中文版的官方文档使得国内开发者能够更加方便地理解和使用Java 8...

    Java1.8API中文手册

    一、Java集合框架 在Java 1.8中,集合框架得到了进一步优化。HashMap和HashSet的性能得到提升,特别是插入和查找操作。LinkedHashMap和LinkedHashSet保持了元素的插入顺序,方便遍历。另外,引入了TreeMap和TreeSet...

    Java程序员集合框架面试题-java集合框架面试题.docx

    Java集合API是一系列接口和类的集合,它们定义了处理各种数据结构(如列表、集合、映射)的标准方法。主要接口包括`Collection`、`List`、`Set`、`Map`等,其中`Collection`是所有集合接口的根接口。这些接口和实现...

    java8 API 中文版

    Java 8 API是Java开发的重要参考资料,它包含了Java 8版本的所有类库、接口和方法的详细说明。这个中文版的API文档对于中国开发者来说,无疑提供了极大的便利,因为中文语言的理解更为直观,有助于提高开发效率。...

    java1.7API中文版

    Java 1.7 API中文版是Java开发人员的重要参考资料,它包含了Java 7版本的所有公共类、接口、方法和常量的详细说明。这个API文档是开发者理解和使用Java平台标准版(Java SE)7功能的关键工具。以下是Java 1.7 API中...

    Java8API文档(官方离线版)

    Stream API是Java 8的另一个重大改进,它为处理集合数据提供了一种声明式方式。`java.util.stream`包包含了一系列操作,如map、filter、reduce等,可以对集合进行高效的并行或串行处理。Stream API特别适合大数据量...

    java8 API 文档

    Java 8 API 文档是Java开发人员的重要参考资料,它详细阐述了Java 8及更高版本中的各种类库、接口和方法。这个文档包含了Java SE(标准版)8的所有公共API,是理解和使用新特性的关键工具。让我们深入探讨Java 8 API...

    java1.8-api

    3. **Stream API**:Java 1.8引入了Stream API,提供了一种新的数据处理方式,可以对集合进行过滤、映射和聚合操作,支持串行和并行处理,大大提高了代码的可读性和性能。 4. **Optional类**:这个类用于表示可能为...

    Java1.6api

    Java 1.6 API是Java开发工具包(Java Development Kit)的一个重要组成部分,它包含了所有Java编程语言在1.6版本中的核心类库、接口、枚举和注解的详细文档。这个API文档以中文版的形式提供,方便了中文开发者理解和...

    java1.8api中文版

    1. **核心类库**:Java 1.8 API的核心类库包括了基础数据类型、集合框架、IO流、网络编程、多线程、反射等多个方面。例如,`java.lang`包中的`String`、`Integer`、`Object`等类是所有Java程序的基础;`java.util`包...

    java8 API文档

    Java 8 API文档是Java开发人员的重要参考资料,它包含了Java Development Kit (JDK) 8的所有公共类、接口和方法的详细说明。这份文档不仅是学习Java 8新特性的宝典,也是日常开发中查阅API功能和用法的必备工具。 ...

    java api大集合

    3. **集合框架**:`java.util`包含了许多容器类,如`ArrayList`、`HashMap`、`LinkedList`等,以及`Collections`工具类,用于对数据进行管理和操作。 4. **多线程**:`java.lang.Thread`和`java.util.concurrent`包...

    java1.8-api-中文.zip

    Java 1.8 API中文版是一个非常重要的学习资源,它为开发者提供了详细的Java 1.8版本的标准类库文档,方便中国用户理解并使用Java语言进行开发。这个压缩包包含了两个文件:`Java8中文API.CHM`和`8cm.jpg`。前者是CHM...

    Java开发API集合

    Java开发API集合是一个重要的资源,尤其对于Java开发者来说,它整合了Java标准版(Java SE)和Java Web应用开发所需的API。这个集合旨在提供全面的参考文档,帮助开发者理解和使用各种Java类库,提高开发效率。 ...

    Java8 API 文档.CHM

    4. **Stream API**:Java 8的Stream API提供了对集合数据的并行和串行操作,如过滤、映射、减少、查找和匹配。`Stream`支持创建、中间操作(map, filter)和终端操作(collect, forEach),极大地提高了代码的可读性...

Global site tag (gtag.js) - Google Analytics