早在 Java 2 中之前,Java 就提供了特设类。比如:Dictionary, Vector, Stack, 和 Properties 这些类用来存储和操作对象组。 虽然这些类都非常有用,但是它们缺少一个核心的,统一的主题。由于这个原因,使用 Vector 类的方式和使用 Properties 类的方式有着很大不同。 集合框架被设计成要满足以下几个目标。

  • 该框架必须是高性能的。基本集合(动态数组,链表,树,哈希表)的实现也必须是高效的。
  • 该框架允许不同类型的集合,以类似的方式工作,具有高度的互操作性。
  • 对一个集合的扩展和适应必须是简单的。

为此,整个集合框架就围绕一组标准接口而设计。你可以直接使用这些接口的标准实现,诸如: LinkedList, HashSet, 和 TreeSet 等,除此之外你也可以通过这些接口实现自己的集合

Java 集合框架 - 图1
从上面的集合框架图可以看到,Java 集合框架主要包括两种类型的容器,
一种是集合(Collection),存储一个元素集合,Collection 接口又有 3 种子类型,List、Set 和 Queue,再下面是一些抽象类,最后是具体实现类,常用的有 ArrayListLinkedListHashSet、LinkedHashSet、HashMap、LinkedHashMap 等等。
另一种是图(Map),存储键/值对映射。
集合框架是一个用来代表和操纵集合的统一架构。所有的集合框架都包含如下内容:

  • 接口:是代表集合的抽象数据类型。例如 Collection、List、Set、Map 等。之所以定义多个接口,是为了以不同的方式操作集合对象
  • 实现(类):是集合接口的具体实现。从本质上讲,它们是可重复使用的数据结构,例如:ArrayList、LinkedList、HashSet、HashMap。
  • 算法:是实现集合接口的对象里的方法执行的一些有用的计算,例如:搜索和排序。这些算法被称为多态,那是因为相同的方法可以在相似的接口上有着不同的实现。

除了集合,该框架也定义了几个 Map 接口和类。Map 里存储的是键/值对。尽管 Map 不是集合,但是它们完全整合在集合中

1848928251,3742053768.jpg
Java 集合框架 - 图3
Java 集合框架提供了一套性能优良,使用方便的接口和类,java集合框架位于java.util包中, 所以当使用集合框架的时候需要进行导包

集合接口

集合框架定义了一些接口。本节提供了每个接口的概述:

序号 接口描述
Collection Collection 是最基本的集合接口,一个 Collection 代表一组 Object,即 Collection 的元素, Java不提供直接继承自Collection的类,只提供继承于的子接口(如List和set)。
Collection 接口存储一组不唯一,无序的对象。
List List接口是一个有序的 Collection,使用此接口能够精确的控制每个元素插入的位置,能够通过索引(元素在List中位置,类似于数组的下标)来访问List中的元素,第一个元素的索引为 0,而且允许有相同的元素。
List 接口存储一组不唯一,有序(插入顺序)的对象。
Set Set 具有与 Collection 完全一样的接口,只是行为上不同,Set 不保存重复的元素。
Set 接口存储一组唯一,无序的对象。
SortedSet 继承于Set保存有序的集合。
Map Map 接口存储一组键值对象,提供key(键)到value(值)的映射。
Map.Entry 描述在一个Map中的一个元素(键/值对)。是一个 Map 的内部接口。
SortedMap 继承于 Map,使 Key 保持在升序排列。
Enumeration 这是一个传统的接口和定义的方法,通过它可以枚举(一次获得一个)对象集合中的元素。这个传统接口已被迭代器取代。

集合实现类(集合类)

Java提供了一套实现了Collection接口的标准集合类。其中一些是具体类,这些类可以直接拿来使用,而另外一些是抽象类,提供了接口的部分实现。
标准集合类汇总于下表:

类描述
AbstractCollection 实现了大部分的集合接口
AbstractList 继承于AbstractCollection 并且实现了大部分List接口。
AbstractSequentialList 继承于 AbstractList ,提供了对数据元素的链式访问而不是随机访问。
ArrayList 该类也是实现了List的接口,实现了可变大小的数组,随机访问和遍历元素时,提供更好的性能。该类也是非同步的,在多线程的情况下不要使用。ArrayList 增长当前长度的50%,插入删除效率低。
LinkedList 该类实现了List接口,允许有null(空)元素。主要用于创建链表数据结构,该类没有同步方法,如果多个线程同时访问一个List,则必须自己实现访问同步,解决方法就是在创建List时候构造一个同步的List。例如:
List list=Collections.synchronizedList(newLinkedList(…));
LinkedList 查找效率低。
AbstractSet 继承于AbstractCollection 并且实现了大部分Set接口。
HashSet 该类实现了Set接口,不允许出现重复元素,不保证集合中元素的顺序,允许包含值为null的元素,但最多只能一个。
LinkedHashSet 具有可预知迭代顺序的 Set 接口的哈希表和链接列表实现。
TreeSet 该类实现了Set接口,可以实现排序等功能。
AbstractMap 实现了大部分的Map接口。
HashMap HashMap 是一个散列表,它存储的内容是键值对(key-value)映射。
该类实现了Map接口,根据键的HashCode值存储数据,具有很快的访问速度,最多允许一条记录的键为null,不支持线程同步。
TreeMap 继承了AbstractMap,并且使用一颗树。
WeakHashMap 继承AbstractMap类,使用弱密钥的哈希表。
LinkedHashMap java.util.LinkedHashMap集合 extends HashMap集合,使用元素的自然顺序对元素进行排序
- 底层是哈希表+链表(保证迭代的顺序)
- 是一个有序的集合,存储元素和取出元素的顺序是一致的
IdentityHashMap 继承AbstractMap类,比较文档时使用引用相等。

在前面的教程中已经讨论通过java.util包中定义的类,如下所示:

类描述
Vector 该类和ArrayList非常相似,但是该类是同步的,可以用在多线程的情况,该类允许设置默认的增长长度,默认扩容方式为原来的2倍。
Stack 栈是Vector的一个子类,它实现了一个标准的后进先出的栈。
Dictionary Dictionary 类是一个抽象类,用来存储键/值对,作用和Map类相似。
Hashtable Hashtable 是 Dictionary(字典) 类的子类,位于 java.util 包中。
底层也是一个哈希表,是一个线程安全的集合,是单线程集合,速度慢
Hashtable和Vector集合一样,在jdk1.2版本之后被更先进的集合(HashMap,ArrayList)取代了
Hashtable的子类Properties依然活跃在历史舞台
不能存储null,键,null值
Properties Properties 继承于 Hashtable,表示一个持久的属性集,属性列表中每个键及其对应值都是一个字符串。
Properties集合是一个唯一和IO流相结合的集合
BitSet 一个Bitset类创建一种特殊类型的数组来保存位值。BitSet中数组大小会随需要增加。

Hashtable集合

  1. public class Demo02Hashtable {
  2. public static void main(String[] args) {
  3. HashMap<String,String> map = new HashMap<>();
  4. map.put(null,"a");
  5. map.put("b",null);
  6. map.put(null,null);
  7. System.out.println(map);//{null=null, b=null}
  8. Hashtable<String,String> table = new Hashtable<>();
  9. //table.put(null,"a"); //NullPointerException
  10. //table.put("b",null); //NullPointerException
  11. table.put(null,null); //NullPointerException
  12. }
  13. }

Collection常用方法

Collection是所有单列集合的父接口,因此在Collection中定义了单列集合(List和Set)通用的一些方法

方法 说明
public boolean add(E e) 把给定的对象添加到当前集合中
public boolean contains(E e) 判断当前集合中是否包含给定的对象
public boolean isEmpty() 判断当前集合是否为空
public int size() 返回集合中元素的个数
public String remove(int index)
public boolean remove(Object o)
把给定的对象在当前集合中删除
public void clear() 清空集合中所有的元素
public Object[] toArray() 把集合中的元素,存储到数组中

Set和List的区别

List Set
List 接口实例存储的是 有序可重复 的元素 Set接口实例存储的是 无序不重复 的数据
和数组类似,可以动态增长,根据实际存储的数据的长度自动增长List的长度。查找元素效率高,插入删除效率低,因为会引起其他元素位置改变
<实现类有ArrayList,LinkedList,Vector>
删除和插入效率高,插入和删除不会引起元素位置改变
<实现类有HashSet,TreeSet>
是一个带有索引的集合,通过索引就可以精确的操作集合中的元素(与数组的索引是一个道理) Set集合取出元素的方式可以采用:迭代器、增强for
集合中可以有重复的元素,通过元素的equals方法,来比较是否为重复的元素

Map

现实生活中,我们常会看到这样的一种集合:IP地址与主机名,身份证号与个人,系统用户名与系统用户对象等,这种一一对应的关系,就叫做映射
Collection中的集合称为单列集合,Map中的集合称为双列集合
Java提供了专门的集合类用来存放这种对象关系的对象,即java.util.Map接口

  • Map集合是一个双列集合,一个元素包含两个值(一个key,一个value)
  • Map集合中的元素,key和value的数据类型可以相同,也可以不同
  • Map集合中的元素,key是不允许重复的,value是可以重复的
  • Map集合中的元素,key和value是一一对应 | 方法 | 说明 | | —- | —- | | public V put(K key, V value) | 把指定的键与指定的值添加到Map集合中 | | public V get(Object key) | 根据指定的键,在Map集合中获取对应的值 | | boolean containsKey(Object key) | 判断集合中是否包含指定的键 | | public V remove(Object key) | 把指定的键 所对应的键值对元素 在Map集合中删除,返回被删除元素的值 | | public Set keySet() | 获取Map集合中所有的键,存储到Set集合中 | | public Set> entrySet() | 获取到Map集合中所有的键值对对象的集合(Set集合) |

集合算法

集合框架定义了几种算法,可用于集合和映射。这些算法被定义为集合类的静态方法。
在尝试比较不兼容的类型时,一些方法能够抛出 ClassCastException异常。当试图修改一个不可修改的集合时,抛出UnsupportedOperationException异常。
集合定义三个静态的变量:EMPTY_SET,EMPTY_LIST,EMPTY_MAP的。这些变量都不可改变。

序号 算法描述
Collection Algorithms 这里是一个列表中的所有算法实现。

如何使用迭代器

通常情况下,你会希望遍历一个集合中的元素。例如,显示集合中的每个元素。
一般遍历数组都是采用for循环或者增强for,这两个方法也可以用在集合框架,但是还有一种方法是采用迭代器遍历集合框架,它是一个对象,实现了Iterator 接口或 ListIterator接口。
迭代器,使你能够通过循环来得到或删除集合的元素。ListIterator 继承了 Iterator,以允许双向遍历列表和修改元素。

迭代器 迭代器方法描述
使用 Java Iterator 这里通过实例列出 Iterator 和 ListIterator 接口提供的所有方法。

如何使用比较器

TreeSet和TreeMap的按照排序顺序来存储元素. 然而,这是通过比较器来精确定义按照什么样的排序顺序。
这个接口可以让我们以不同的方式来排序一个集合

比较器 比较器方法描述
Java Comparator 这里通过实例列出Comparator接口提供的所有方法

遍历

遍历 List

  1. import java.util.*;
  2. public class ListDemo {
  3. public static void main(final String[] args) {
  4. final List<String> list = new ArrayList<String>(); // 查找快,增删慢
  5. // final List<String> list = new LinkedList<String>(); // 查找慢,增删快
  6. list.add("Hello");
  7. list.add("World");
  8. // 第一种遍历,把链表变为数组相关的内容进行遍历
  9. final String[] strArray = new String[list.size()];
  10. list.toArray(strArray);
  11. for (int i = 0; i < strArray.length; i++) // 这里也可以改写为 for(String str:strArray) 这种形式
  12. {
  13. System.out.println(strArray[i]);
  14. }
  15. // 第二种,使用for根据列表长度遍历
  16. for (int i = 0; i < list.size(); i++) {
  17. System.out.println(list.get(i));
  18. }
  19. // 第三种遍历方法使用 For-Each 遍历 List
  20. for (final String str : list) {
  21. System.out.println(str);
  22. }
  23. // 第四种遍历 使用迭代器进行相关遍历
  24. final Iterator<String> ite = list.iterator();
  25. while (ite.hasNext())// 判断下一个元素之后有值
  26. {
  27. System.out.println(ite.next());
  28. }
  29. }
  30. }

遍历 Map

import java.util.*;
import java.util.LinkedHashMap;

public class MapDemo {
  public static void main(String[] args) {
    Map<String, String> map = new HashMap<String, String>();    //无序、查询快
    // Map<String, String> map = new LinkedHashMap<String, String>(); //有序、查询相对慢点
    map.put("1", "value1");
    map.put("2", "value2");
    map.put("3", "value3");

    // 第一种:普遍使用,二次取值
    System.out.println("通过Map.keySet遍历key和value:");
    for (String key : map.keySet()) {
      System.out.println("key= " + key + " and value= " + map.get(key));
    }

    // 第二种
    System.out.println("通过Map.values()遍历所有的value,但不能遍历key");
    for (String v : map.values()) {
      System.out.println("value= " + v);
    }

    // 第三种:推荐,尤其是容量大时
    System.out.println("通过Map.entrySet遍历key和value");
    for (Map.Entry<String, String> entry : map.entrySet()) {
      System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue());
    }

    // 第四种
    System.out.println("通过Map.entrySet使用iterator遍历key和value:");
    Iterator<Map.Entry<String, String>> it = map.entrySet().iterator();
    while (it.hasNext()) {
      Map.Entry<String, String> entry = it.next();
      System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue());
    }

  }
}

JDK9对集合添加的优化

我们在代码中创建一个集合(例如,List 或 Set ),并直接用一些元素填充它。 实例化集合,几个 add方法 调用,使得代码重复
Java 9,添加了几种集合工厂方法,更方便创建少量元素的集合、map实例。新的List、Set、Map的静态工厂方法可 以更方便地创建集合的不可变实例

// List接口,Set接口,Map接口:里边增加了一个静态的方法of,可以给集合一次性添加多个元素
static <E> List<E> of(E... elements)
public class HelloJDK9 {  
    public static void main(String[] args) {  
        Set<String> str1=Set.of("a","b","c");  
        //str1.add("c");这里编译的时候不会错,但是执行的时候会报错,因为是不可变的集合  
        System.out.println(str1);  

        Map<String,Integer> str2=Map.of("a",1,"b",2);  
        System.out.println(str2);  

        List<String> str3=List.of("a","b");  
        System.out.println(str3);  
    }  
}

使用前提: 当集合中存储的元素的个数已经确定了,不在改变时使用
⚠️注意:

  1. of() 只适用于List接口,Set接口,Map接口,不适用于接口的实现类,其父类接口和子类实现并没有这类方法,比如 HashSet,ArrayList等待
  2. of() 的返回值是一个不能改变的集合,集合不能再使用add,put方法添加元素,会抛出异常
  3. Set接口和Map接口在调用of()的时候,不能有重复的元素,否则会抛出异常
  4. of() 只是Map,List,Set这三个接口的静态方法,返回的集合是不可变的

总结

Java集合框架为程序员提供了预先包装的数据结构和算法来操纵他们。
集合是一个对象,可容纳其他对象的引用。集合接口声明对每一种类型的集合可以执行的操作。
集合框架的类和接口均在java.util包中。
任何对象加入集合类后,自动转变为Object类型,所以在取出的时候,需要进行强制类型转换。