一、集合框架概述

1.引入集合框架

申明一个50长度的数组来存储数据的缺陷:

  1. 数组长度固定不变,不能很好地适应元素数量动态变化的情况。若要存储大于50个元素,则数组长度不足;若只存储20长度的数据,则造成内存空间浪费。
  2. 虽然可通过数组名.length获取数组的长度,却无法直接获取数组中真实存储的狗狗个数。
  3. 数组采用在内存中分配连续空间的存储方式,根据下标可以快速获取对应的信息,但是根据信息查找的时候效率低下,需要多次比较。在进行频繁插入、删除操作时同样效率低下。

2.Java集合框架包含的内容

Java集合框架为我们提供了一套性能优良、使用方便的接口和类,它们都位于java.util包中。

集合框架包括三大块内容:对外接口、接口实现和对集合运算的算法

Java集合框架简图:

开源java 数据统计 java数据统计框架_System

  • 接口:表示集合的抽象数据类型,如:Collection, List, Set, Map, Iterator
  • 实现:集合框架中接口的具体实现,如:ArrayList, LinkedList, HashMap, TreeMap, HashSet, TreeSet
  • 算法:在实现了的接口对象身上完成某种有用的计算方法,例如:查询、排序等。Java 提供了进行集合操作的工具类Collections (注意不是Collection),为我们提供了对集合进行排序等多种算法实现。使用时查看 JDK 帮助文档。

上图中可以看出Java 集合框架中的两大类接口:Collection 和 MapCollection又有两个子接口:List 和 Set。通常说Java集合框架共有三大类接口:List, Set 和 Map。共同点:都是集合接口,都可以用来存储很多对象。区别如下:

  • Collection接口存储一组不唯一(允许重复)、无序的对象。
  • Set接口继承Collection接口,存储一组唯一(不允许重复)、无序的对象。
  • List接口继承Collection接口,存储一组不唯一(允许重复)、有序(以元素插入的次序来放置元素,不会重新排列)的对象。
  • Map接口存储一组成对的键-值对象,提供key-value映射。key不要求有序,不允许重复。value同样不要求有序,但允许重复。
  • Iterator接口是负责定义访问和遍历元素的接口。

List可以理解为数组,元素内容可以重复并且有序。
Set可以理解为数学中的集合,数据不重复并且无序。

开源java 数据统计 java数据统计框架_Java_02

Map可以理解为数学中的集合,只是其中每个元素都由key-value两个对象组成。

开源java 数据统计 java数据统计框架_List_03

二、List接口

ArrayList 对数组进行了封装,实现了长度可变的数组。ArrayList 存储数据的方式和数组相同,都是在内存中分配连续的空间,如下图。它的优点在于遍历元素和随机访问元素的效率比较高。

开源java 数据统计 java数据统计框架_开源java 数据统计_04

LinkedList 采用链表存储方式,如下图所示,优点在于插入、删除元素时效率比较高。他提供了额外的addFirst(), addLast(), removeFirst(), removeLast()等方法,这些方法使得 LinkedList 可被用作堆栈(stack)或者队列(queue)。

开源java 数据统计 java数据统计框架_java_05

1.ArrayList集合类

ArrayList 接口中定义的各种常用方法如下表:

方法名称

说明

boolean add(Object o)

在列表末尾顺序添加元素,起始索引位置从0开始

void add(int index, Object o)

在指定的索引位置添加元素,原索引位置及其后面的元素依次后移

注意:index∈[0, list.size()]

int size()

返回列表中的元素个数

Object get(int index)

返回指定索引位置处的元素

boolean contains(Object o)

判断列表中是否存在指定元素

boolean remove(Object o)

从列表中删除元素

Object remove(int index)

从列表中删除指定位置元素

注意:index∈[0, list.size() - 1]

示例如下:

import java.util.ArrayList;
import java.util.List;

/**
 * @author 天道酬勤
 */
public class ArrayListDemo {
    public static void main(String[] args) {
        // 创建4个学生对象,Student类的属性:int age, String stuName
        Student s1 = new Student("s1", 18);
        Student s2 = new Student("s2", 17);
        Student s3 = new Student("s3", 16);
        Student s4 = new Student("s4", 15);
        // 创建ArrayList集合对象并把4个对象放入其中
        List<Student> students = new ArrayList<>();
        students.add(s1);
        students.add(s2);
        students.add(s3);
        // 添加s4到下标为2的指定位置
        students.add(3, s4);
        // 输出集合中Student对象数量
        System.out.println("集合中共有" + 
            students.size() + "个Student对象");
        // 通过遍历集合显示各条信息
        System.out.println("初始Student信息:");
        for (int i = 0; i < students.size(); i++) {
            Student s = students.get(i);
            System.out.println(s.getStuName() + 
                "\t" + s.getAge());
        }
        // 删除下标为0的Student对象(第一条信息)
        students.remove(0);
        // 删除指定的Student对象
        students.remove(s4);
        // 显示删除后的列表信息
        System.out.println("删除后集合中共有" + 
            students.size() + "个Student对象");
        for (int i = 0; i < students.size(); i++) {
            Student s = students.get(i);
            System.out.println(s.getStuName() + 
                "\t" + s.getAge());
        }
        // 判断集合中是否包含指定的信息
        if (students.contains(s3)) {
            System.out.println("s3 is in ArrayList");
        } else {
            System.out.println("s3 is not in ArrayList");
        }

    }
}

运行结果如下图所示:

开源java 数据统计 java数据统计框架_List_06

3.LinkedList集合类

LinkedList 除了上表中列出的各种方法外还包括一些特殊的方法,如下表所示:

方法名称

说明

void addFirst(Object o)

在列表的首部添加元素

void addLast(Object o)

在列表的末尾添加元素

Object getFirst()

返回列表中的第一个元素

Object getLast()

返回列表中的最后一个元素

Object removeFirst()

删除并返回列表中的第一个元素

Object removeLast()

删除并返回列表中的最后一个元素

示例代码如下:

import java.util.LinkedList;

/**
 * @author 天道酬勤
 */
public class LinkedListDemo {
    public static void main(String[] args) {
        // 创建4个学生对象,Student类的属性:int age, String stuName
        Student s1 = new Student("s1", 18);
        Student s2 = new Student("s2", 17);
        Student s3 = new Student("s3", 16);
        Student s4 = new Student("s4", 15);
        // 创建LinkedList集合对象并把多个Student对象放入其中
        LinkedList<Student> students = new LinkedList<>();
        students.add(s1);
        students.add(s2);
        students.addFirst(s3);
        students.addLast(s4);
        // 查看第一个Student对象的名字
        System.out.println("第一个对象的stuName:" + 
            students.getFirst().getStuName());
        // 查看最后一个Student对象的名字
        System.out.println("最后一个对象的stuName:" + 
            students.getLast().getStuName());
        // 删除第一个Student对象和最后一个Student对象
        students.removeFirst();
        students.removeLast();
        // 显示删除后集合中的信息
        System.out.println("删除后的LinkedList信息");
        for (int i = 0; i < students.size(); i++) {
            System.out.println(students.get(i).getStuName() + 
                "\t" + students.get(i).getAge());
        }
    }
}

三、Set接口

Set 接口可以存储一组唯一、无序的对象。

常用实现类有HashSet

1.HashSet类动态存储数据

在数据量大的情况下,要查找某个数据,LinkedList 和 ArrayList 效率低下,前者由于数据结构决定效率底下,后者由于不知道下标,需要遍历一遍才可以。这时候我们需要HashSet。它实现了Set 接口,是使用 Set 集合的最常用的一个实现类。
HashSet特点如下:

  • 集合内的元素是无序排列的
  • HashSet 类是非线程安全的
  • 允许集合元素值为NULL

下表列出了 HashSet 类的常用方法:

方法

说明

boolean add(Object o)

如果此 Set 中尚未包含指定元素,则添加指定元素

void clear()

从此 Set 中移除所有元素

int size()

返回此 Set 中元素的数量(Set 的容量)

boolean isEmpty()

如果此 Set 不包含任何元素,则返回true

boolean contains(Object o)

如果此 Set 包含指定元素,则返回true

boolean remove(Object o)

如果指定元素存在于此 Set 中,则将其移除

下面代码演示使用HashSet类常用方法存储并操作信息,并遍历集合,代码如下所示:

import java.util.HashSet;
import java.util.Set;

public class HashSetDemo {
    public static void main(String[] args) {
        Set<String> hs = new HashSet<>();
        for (int i = 0; i < 100; i++) {
            hs.add(Integer.toString(i));
        }
        for (String s : hs) {
            System.out.println(s);
        }
    }
}

该结果的部分运行结果如下所示(所以Set一组无序唯一的数据):

开源java 数据统计 java数据统计框架_Java_07

如果添加多次相同数据,则实际只添加了一次该数据:

import java.util.HashSet;
import java.util.Set;

public class HashSetDemo {
    public static void main(String[] args) {
        Set<String> hs = new HashSet<>();
        hs.add("1");
        hs.add("1");
        for (String s : hs) {
            System.out.println(s);
        }
    }
}

运行结果如下所示:

开源java 数据统计 java数据统计框架_System_08

注意
HashSet 不能使用(或者不推荐) 简单 for 循环遍历。
但是可以使用 Iterator 接口遍历,示例如下:

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

public class HashSetDemo {
    public static void main(String[] args) {
        Set<String> hs = new HashSet<>();
        for (int i = 0; i < 100; i++) {
            hs.add(Integer.toString(i));
        }
        Iterator<String> it = hs.iterator();
        while (it.hasNext()) {
            System.out.println(it.next());
        }
    }
}

运行结果与上面相同,无序唯一,在这里不演示了。

四、Map接口

Map 接口存储一组成对的键-值对象,提供key(键)到value (值)的映射。Map 中的 key 不要求有序,不允许重复。value 同样不要求有序,但允许重复。

最常用的Map 实现类是HashMap,它的存储方式是哈希表。哈希表也称为散列表,是根据关键码值(key value)而直接进行访问的数据结构。也就是说,他通过把关键码映射到表中一个位置来进行访问记录,以加快查找速度。存放记录的数组称为散列表。使用这种方式存储数据的优点是查询指定元素效率高

1.HashMap集合类

建立key-value对象,实现映射关系。如:CN->中华人民共和国,根据 “CN” 可以查找到 “中华人民共和国”,通过删除键可实现对应值的删除,存储和修改。

HashMap常用方法如下:

方法名称

说明

Object put(Object key, Object value)

以”键-值对”的方式进行存储。

注意:键必须是唯一的,值可以重复。如果试图添加重复的键,那么最后加入的”键-值对”将替换原先的”键-值对”。

Object get(Object key)

根据键返回和关联的值,若不存在指定的键,则返回NULL

Object remove(Object key)

删除指定的键映射的”键-值对”

int size()

返回元素个数

Set keySet()

返回键的集合

Collection values()

返回值的集合

boolean containsKey(Object key)

若存在指定的键映射的”键-值对”,则返回true

boolean isEmpty()

若不存在键-值映射关系,则返回true

void clear()

从此映射中移除所有映射关系

代码示例如下所示:

import java.util.HashMap;
import java.util.Map;

public class HashMapDemo {
    public static void main(String[] args) {
        // 使用HashMap存储多组国家英文简称和中文全称的"键-值对"
        Map<String, String> hmap = new HashMap<>();
        hmap.put("CN", "中华人民共和国");
        hmap.put("US", "美利坚合众国");
        hmap.put("RU", "俄罗斯联邦");
        hmap.put("FR", "法兰西共和国");
        // 显示"CN"对应国家的中文名称
        String country = hmap.get("CN");
        System.out.println(country);
        // 显示集合中元素的个数
        System.out.println("Map中共有:" + hmap.size() + "组数据");
        // 两次判断Map中是否存在"FR"值
        System.out.println("Map中包含FR的key吗?" 
            + hmap.containsKey("FR"));
        hmap.remove("FR");
        System.out.println("Map中包含FR的key吗?" 
            + hmap.containsKey("FR"));
        // 分别显示键集,值集和键-值对集
        System.out.println(hmap.keySet());
        System.out.println(hmap.values());
        System.out.println(hmap);
        // 清空HashMap并判断
        hmap.clear();
        if (hmap.isEmpty()) {
            System.out.println("已经清空HashMap中的数据!");
        }
    }
}

运行结果如下图所示:

开源java 数据统计 java数据统计框架_java_09

五、迭代器Iterator

所有集合接口都没有提供相应的遍历方法,而是把遍历交给迭代器 Iterator 完成。 Iterator 为集合而生,专门实现集合的遍历。它隐藏了各种集合实现类的内部细节,提供了遍历集合的统一编程接口。

Collection 接口的 Iterator()方法返回一个 Iterator , 然后通过 Iterator 接口的两个方法即可方便的实现遍历。

  • boolean hasNext():判断是否存在另一个可访问的元素。
  • Object next():返回要访问的下一个元素。

1.使用Iterator遍历List集合类

用 Iterator 接口来遍历 List 中的元素(ArrayList 和 LinkedList):

import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

public class IteratorList {
    public static void main(String[] args) {
        // ArrayList演示
        List<String> ls = new ArrayList<>();
        ls.add("1");
        ls.add("2");
        ls.add("3");
        ls.add("4");
        System.out.println("ArrayList中的所有数据:");
        Iterator<String> it = ls.iterator();
        while (it.hasNext()) {
            System.out.println(it.next());
        }

        // LinkedList演示
        List<String> linkl = new LinkedList<>();
        linkl.add("7");
        linkl.add("8");
        linkl.add("9");
        linkl.add("10");
        System.out.println("\n");
        System.out.println("LinkedList中所有数据:");
        Iterator<String> linkl_it = linkl.iterator();
        while (linkl_it.hasNext()) {
            System.out.println(linkl_it.next());
        }
    }
}

运行结果如下:

开源java 数据统计 java数据统计框架_Java_10

2.使用Iterator遍历Map集合类

代码如下:

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

public class IteratorMap {
    public static void main(String[] args) {
        // HashMap
        Map<String, String> map = new HashMap<>();
        map.put("1", "1");
        map.put("2", "2");
        map.put("3", "3");
        map.put("4", "4");
        Set<String> keys = map.keySet();
        Iterator<String> it = keys.iterator();
        while (it.hasNext()) {
            String key = it.next();
            System.out.println(map.get(key));
        }
    }
}

运行结果如下:

开源java 数据统计 java数据统计框架_开源java 数据统计_11

3.使用Foreach循环遍历Map集合

代码如下:

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class ForEachMap {
    public static void main(String[] args) {
        // HashMap
        Map<String, String> map = new HashMap<>();
        map.put("1", "1");
        map.put("2", "2");
        map.put("3", "3");
        map.put("4", "4");
        Set<String> keys = map.keySet();
        for (String key : keys) {
            System.out.println(map.get(key));
        }
    }
}

运行结果如下:

开源java 数据统计 java数据统计框架_开源java 数据统计_12

3.使用Foreach循环遍历List集合

代码如下:

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

public class ForEachList {
    public static void main(String[] args) {
        List<String> arrayList = new ArrayList<>();
        arrayList.add("1");
        arrayList.add("2");
        arrayList.add("3");
        arrayList.add("4");
        System.out.println("遍历arrayList结果:");
        for (String s : arrayList) {
            System.out.println(s);
        }
        List<String> linkedList = new LinkedList<>();
        linkedList.add("11");
        linkedList.add("22");
        linkedList.add("33");
        linkedList.add("44");
        System.out.println("遍历linkedList结果:");
        for (String s : linkedList) {
            System.out.println(s);
        }
    }
}

运行结果如下:

开源java 数据统计 java数据统计框架_System_13

六、泛型集合

Collection 的 add(Object obj)方法的参数就是 Object 类型,无论把什么对象放入 Collection 及其子接口或实现类中,都认为只是 Object 类型,在通过 get(int index)方法取出集合中元素时必须进行强制类型转换,不仅繁琐且容易出现ClassCastException异常。Map 中使用put(Object key, Object value)get(Object key)方法存取对象时、使用 Iterator 的next()方法获取元素时也存在同样的问题。

JDK 1.5 中通过引用泛型(Generic)有效的解决了这个问题。在 JDK 1.5 中已经改写了集合框架中的所有接口和类,增加了对泛型的支持。

使用泛型集合在创建集合对象时指定集合中元素的类型,从集合中取出元素时无需进行类型强制转换,而且如果把非指定类型对象放入集合,会出现编译错误。

该页全部代码均使用了泛型集合,样式为:

//从左到右List为集合类型,
//String为List中存放的元素的引用数据类型或者对象
//(不可使用基本数据类型)
//右边的String可以不写
List<String> listName = new ArrayList<String>();
//Map同理,右边的<String, String>中的两个String要么都写,要么一个写
Map<String, String> mapName = new HashMap<String, String>();