集合类存放于 Java.util 包中,主要有 3 种:set(集)、list(列表包含 Queue)和 map(映射)。


集合的体系结构

集合的特点:提供一种存储空间可变的存储模型,存储的数据容量可以随时发生改变

集合类的体系图:

单列模式是最后常见的设计模式之一,主要用途是保证在整个程序中,指定的对象只创建一次,用到的始终都是同一个对象;

java 列表包含 java的列表和集合_List


1. Collection:Collection 是集合 List、Set、Queue 的最基本的接口。


2. Iterator:迭代器,可以通过迭代器遍历集合中的数据


3. Map:是映射表的基础接口


Collection集合

   是单例集合的顶层接口,它表示一组对象,这些对象也称为Collection的元素

   JDK 不提供此接口的任何直接实现,它提供更具体的子接口(如Set和List)实现

Collections类常用方法

方法名

说明

public static void sort(List<T> list)

将指定的列表按升序排序

public static void reverse(List<?> list)

反转指定列表中元素的顺序

public static void shuffle(List<?> list)

使用默认的随机源随机排列指定的列表

方法名

说明

boolean add(E e)

添加元素

boolean remove(Object o)

从集合中移除指定的元素

void clear()

清空集合中的元素

boolean contains(Object o)

判断集合中是否存在指定的元素

boolean isEmpty()

判断集合是否为空

int size()

集合的长度,也就是集合中元素的个数

boolean hasNext()

如果迭代具有更多元素,则返回true 。 (换句话说,如果true将返回一个元素而不是抛出异常,则返回true ) 结果 true如果迭代有更多的元素

Collection集合的遍历

public class IteratorDemo {
    public static void main(String[] args) {
        //创建集合对象
        Collection<String> c = new ArrayList<>();
        //添加元素
        c.add("hello");
        c.add("world");
        c.add("java");
        c.add("javaee");
        //Iterator<E> iterator():返回此集合中元素的迭代器,通过集合的iterator()方法得到
        Iterator<String> it = c.iterator();
        //用while循环改进元素的判断和获取
        while (it.hasNext()) {
            String s = it.next();
            System.out.println(s);
        }
    }
}

List集合

特点:有索引,可以存储重复元素,元素存取有序

List子类集合:List 一共三个实现类: 分别是 ArrayList、Vector 和 LinkedList。

ArrayList:排列有序,可重复,底层数据结构是数组,查询快,增删慢,线程不安全,效率高

当容量不够时,ArrayList是当前容量*1.5+1

ArrayList 是最常用的 List 实现类,内部是通过数组实现的,它允许对元素进行快速随机访问。数组的缺点是每个元素之间不能有间隔,当数组大小不满足时需要增加存储能力,就要将已经有数组的数据复制到新的存储空间中。当从 ArrayList 的中间位置插入或者删除元素时,需要对数组进行复制、移动、代价比较高。因此,它适合随机查找和遍历,不适合插入和删除。

Vector:排列有序,可重复,底层数据结构是数组,查询快,增删慢,线程安全,效率低

当容量不够时,Vector默认扩容一倍容量

Vector 与 ArrayList 一样,也是通过数组实现的,不同的是它支持线程的同步,即某一时刻只有一个线程能够写 Vector,避免多线程同时写而引起的不一致性,但实现同步需要很高的花费,因此,访问它比访问 ArrayList 慢。

LinkedList:排列有序,可重复,底层数据结构是链表,查询慢,增删快,线程不安全,效率高

LinkedList 是用链表结构存储数据的,很适合数据的动态插入和删除,随机访问和遍历速度比较慢。另外,他还提供了 List 接口中没有定义的方法,专门用于操作表头和表尾元素,可以当作堆栈、队列和双向队列使用。

List集合特有的方法

方法名

描述

void add(int index,E element)

在此集合中的指定位置插入指定的元素

E remove(int index)

删除指定索引处的元素,返回被删除的元素

E set(int index,E element)

修改指定索引处的元素,返回被修改的元素

E get(int index)

返回指定索引处的元素

List Iterator介绍

  • 通过List集合的listIterator()方法得到,所以说它是List集合特有的迭代器
  • 用于允许程序员沿任一方向遍历的列表迭代器,在迭代期间修改列表,并获取列表中迭代器的当前位
public class ListIteratorDemo {
    public static void main(String[] args) {
        //创建集合对象
        List<String> list = new ArrayList<String>();

        //添加元素
        list.add("hello");
        list.add("world");
        list.add("java");

        //获取列表迭代器
        ListIterator<String> lit = list.listIterator();
        while (lit.hasNext()) {
            String s = lit.next();
            if(s.equals("world")) {
                lit.add("javaee");
            }
        }

        System.out.println(list);

    }
}

增强for循环

格式:

for(元素数据类型 变量名 : 数组/集合对象名) {
    循环体;
}

Set集合

Set 注重独一无二的性质,该体系集合用于存储无序(存入和取出的顺序不一定相同)元素,值不能重复。对象的相等性本质是对象 hashCode 值(java 是依据对象的内存地址计算出的此序号)判断的,如果想要让两个不同的对象视为相等的,就必须覆盖 Object 的 hashCode 方法和 equals 方法。

特点:元素存取无序,没有索引,不能存储重复元素

哈希值:

  • 什么是哈希值
    是JDK根据对象的地址或字符串或数字计算出来的一串int类型的数值
  • 如何获取哈希值
    Object类中有一个方法:hashCode()用于获取哈希值

Set子类集合的特点:

HashSet:1,底层是哈希表结构,哈希表底层依赖2个方法hashcode,equals。2,元素存取无序 存取速度快,内部是HashMap。3.没有索引 4.不能存储重复元素。5.排列无序,不可重复

哈希表存放的是哈希值。HashSet 存储元素的顺序并不是按照存入时的顺序(和 List 显然不同) 而是按照哈希值来存的所以取数据也是按照哈希值取得。元素的哈希值是通过元素的hashcode 方法来获取的, HashSet 首先判断两个元素的哈希值,如果哈希值一样,接着会比较equals 方法 如果 equls 结果为 true ,HashSet 就视为同一个元素。如果 equals 为 false 就不是同一个元素。

哈希值相同 equals 为 false 的元素是怎么存储呢,就是在同样的哈希值下顺延(可以认为哈希值相同的元素放在一个哈希桶中)。也就是哈希一样的存一列。如图 1 表示 hashCode 值不相同的情况;图 2 表示 hashCode 值相同,但 equals 不相同的情况。

HashSet集合保证元素唯一性的原理

1.根据对象的哈希值计算存储位置

如果当前位置没有元素则直接存入

如果当前位置有元素存在,则进入第二步

2.当前的元素和已经存在的元素比较哈希值

如果哈希值不同,则将当前元素进行存储

如果哈希值相同,则进入第三步

3.通过equals()方法比较两个元素的内容

如果内容不相同,则将当前元素进行存储

如果内容相同,则不存储当前元素

为什么存储字符串的时候,字符串内容相同的只存储了一个呢 ?

通过查看add方法的源码,我们知道这个方法底层依赖2个方法:hashCode()和equals();

步骤: 首先比较哈希值,如果相同继续走,比较地址值或者equals();如果不同就直接添加到集合中

按照方法的步骤来说:

首先看hashCode值是否相同,

   相同:继续走equals方法

            返回true:说明元素重复,就不添加

            返回false: 说明元素不重复,就添加到集合

   不同:就直接吧元素添加到集合

如果类没有重写这2个方法就默认使用Object()

TreeSet:

特点:排列无序,不可重复    底层使用二叉树     实现排序存储

底层数据结构是红黑树(是一个自平衡的二叉树)

保证元素排序的方式:

自然排序:

让元素所属的类实现Compareable接口

比较器排序:

让集合构造方法接受Comparator的实现类对象

TreeSet(二叉树)

  1. TreeSet()是使用二叉树的原理对新 add()的对象按照指定的顺序排序(升序、降序),每增加一个对象都会进行排序,将对象插入的二叉树指定的位置。
  2. Integer 和 String 对象都可以进行默认的 TreeSet 排序,而自定义类的对象是不可以的,自己定义的类必须实现 Comparable 接口,并且覆写相应的 compareTo()函数,才可以正常使用。
  3. 在覆写 compare()函数时,要返回相应的值才能使 TreeSet 按照一定的规则来排序
  4. 比较此对象与指定对象的顺序。如果该对象小于、等于或大于指定对象,则分别返回负整数、零或正整数。

LinkHashSet(HashSet+LinkedHashMap)

对于 LinkedHashSet 而言,它继承与 HashSet、又基于 LinkedHashMap 来实现的。LinkedHashSet 底层使用 LinkedHashMap 来保存所有元素,它继承与 HashSet,其所有的方法操作上又与 HashSet 相同,因此 LinkedHashSet 的实现上非常简单,只提供了四个构造方法,并通过传递一个标识参数,调用父类的构造器,底层构造一个 LinkedHashMap 来实现,在相关操作上与父类 HashSet 的操作相同,直接调用父类 HashSet 的方法即可。

Map集合

概述:interface Map<K,V>  K:键的类型;V:值的类型

特点:

  • 键值对映射关系
  • 一个键对应一个值
  • 键不能重复,值可以重复
  • 元素存取无序
  • Map集合的数据结构值针对键有效,跟值无关

Map集合和Collection集合的区别

  • Map集合存储元素是成对出现的,Map集合的键是唯一的,值是可重复的。可以理解为夫妻对
  • Collection 集合存储元素是单独出现的,Collection的儿子Set是唯一的,List是可重复的。可理解为:光棍

Map集合的基本功能

功能有 添加 删除 判断 获取 长度功能

方法名

说明

V put(K key,V value)

添加元素

V remove(Object key)

根据键删除键值对元素

void clear()

移除所有的键值对元素

boolean containsKey(Object key)

判断集合是否包含指定的键

boolean containsValue(Object value)

判断集合是否包含指定的值

boolean isEmpty()

判断集合是否为空

V get(Object key)

根据建获取值

Set<k> keySet()

获取集合中所有键的集合

Collection<V> values()

获取集合中所有值的集合

int size()

集合的长度,也就是集合中键值对的个数

map集合获取功能

  • 方法介绍

方法名

说明

V get(Object key) V是返回值

根据键获取值 V是返回值类型,返回的是泛型V

Set<K> keySet() Set<k>是返回值类型

获取所有键的集合 Set<String> keySet = map.keySet();

Collection<V> values()

获取所有值的集合 Collection<V>是返回值类型

Set<Map.Entry<K,V>> entrySet()

获取所有键值对对象的集合 Set<Map.Entry<K,V>>是返回值类型

  • 示例代码
public class MapDemo03 {
    public static void main(String[] args) {
        //创建集合对象
        Map<String, String> map = new HashMap<String, String>();

        //添加元素
        map.put("张无忌", "赵敏");
        map.put("郭靖", "黄蓉");
        map.put("杨过", "小龙女");

        //V get(Object key):根据键获取值
        System.out.println(map.get("张无忌"));
        System.out.println(map.g+et("张三丰"));

        //Set<K> keySet():获取所有键的集合
        Set<String> keySet = map.keySet();
        for(String key : keySet) {//遍历Set
           System.out.println(key);
       }

        //Collection<V> values():获取所有值的集合
        Collection<String> values = map.values();
        for(String value : values) {
            System.out.println(value);
        }
    }
}

Map集合的遍历(方式1)

  • 键找值
  • 获取所有键的集合
  • 遍历键的集合,得到每一个键
  • 根据键到集合中去找值
  • 键值对对象找键和值
  • 获取所有的键值对对象的集合
  • 遍历键值对对象的集合,获取每一个键值对对象
  • 根据键值对对象去获取键和值
  • 遍历思路
    我们刚才存储的元素都是成对出现的,所以我们把Map看成是一个夫妻对的集合
  • 把所有的丈夫给集中起来
  • 遍历丈夫的集合,获取到每一个丈夫
  • 根据丈夫去找对应的妻子
  • 步骤分析
  • 获取所有键的集合。用keySet()方法实现
  • 遍历键的集合,获取到每一个键。用增强for实现
  • 根据键去找值。用get(Object key)方法实现

Map集合的遍历(方式2)

遍历思路

  • 我们刚才存储的元素都是成对出现的,所以我们把Map看成是一个夫妻对的集合
  • 获取所有结婚证的集合
  • 遍历结婚证的集合,得到每一个结婚证
  • 根据结婚证获取丈夫和妻子

步骤分析

  • 获取所有键值对对象的集合
  • Set<Map.Entry<K,V>> entrySet():获取所有键值对对象的集合
  • 遍历键值对对象的集合,得到每一个键值对对象
  • 用增强for实现,得到每一个Map.Entry
  • 根据键值对对象获取键和值
  • 用getKey()得到键
  • 用getValue()得到值

Map集合的案例

  1. HashMap集合练习之键是String值是Student

案例需求

创建一个HashMap集合,键是学号(String),值是学生对象(Student)。存储三个键值对元素,并遍历

代码实现

//学生类
public class Student {
    private String name;
    private int age;
    }
//测试类
/*
    需求:
        创建一个HashMap集合,键是学号(String),值是学生对象(Student)。存储三个键值对元素,并遍历
    思路:
        1:定义学生类
        2:创建HashMap集合对象
        3:创建学生对象
        4:把学生添加到集合
        5:遍历集合
            方式1:键找值
            方式2:键值对对象找键和值
 */
public class HashMapDemo {
    public static void main(String[] args) {
        //创建HashMap集合对象
        HashMap<String, Student> hm = new HashMap<String, Student>();

        //创建学生对象
        Student s1 = new Student("林青霞", 30);
        Student s2 = new Student("张曼玉", 35);
        Student s3 = new Student("王祖贤", 33);

        //把学生添加到集合
        hm.put("itheima001", s1);
        hm.put("itheima002", s2);
        hm.put("itheima003", s3);

        //方式1:键找值
        Set<String> keySet = hm.keySet();
        for (String key : keySet) {
            Student value = hm.get(key);
            System.out.println(key + "," + value.getName() + "," + value.getAge());
        }
        System.out.println("--------");

        //方式2:键值对对象找键和值
        Set<Map.Entry<String, Student>> entrySet = hm.entrySet();
        for (Map.Entry<String, Student> me : entrySet) {
            String key = me.getKey();
            Student value = me.getValue();
            System.out.println(key + "," + value.getName() + "," + value.getAge());
        }
    }
}
  1. HashMap集合练习之键是Student值是String

案例需求

  • 创建一个HashMap集合,键是学生对象(Student),值是居住地 (String)。存储多个元素,并遍历。
  • 要求保证键的唯一性:如果学生对象的成员变量值相同,我们就认为是同一个对象

代码实现

//学生类
public class Student {
    private String name;
    private int age;
    }
//测试类
public class HashMapDemo {
    public static void main(String[] args) {
        //创建HashMap集合对象
        HashMap<Student, String> hm = new HashMap<Student, String>();

        //创建学生对象
        Student s1 = new Student("林青霞", 30);
        Student s2 = new Student("张曼玉", 35);
        Student s3 = new Student("王祖贤", 33);
        Student s4 = new Student("王祖贤", 33);

        //把学生添加到集合
        hm.put(s1, "西安");
        hm.put(s2, "武汉");
        hm.put(s3, "郑州");
        hm.put(s4, "北京");

        //遍历集合
        Set<Student> keySet = hm.keySet();
        for (Student key : keySet) {
            String value = hm.get(key);
            System.out.println(key.getName() + "," + key.getAge() + "," + value);
        }
    }
}
  1. 集合嵌套之ArrayList嵌套HashMap

案例需求

  • 创建一个ArrayList集合,存储三个元素,每一个元素都是HashMap
  • 每一个HashMap的键和值都是String,并遍历。

代码实现

public class ArrayListIncludeHashMapDemo {
    public static void main(String[] args) {
        //创建ArrayList集合
        ArrayList<HashMap<String, String>> array = new ArrayList<HashMap<String, String>>();

        //创建HashMap集合,并添加键值对元素
        HashMap<String, String> hm1 = new HashMap<String, String>();
        hm1.put("孙策", "大乔");
        hm1.put("周瑜", "小乔");
        //把HashMap作为元素添加到ArrayList集合
        array.add(hm1);

        HashMap<String, String> hm2 = new HashMap<String, String>();
        hm2.put("郭靖", "黄蓉");
        hm2.put("杨过", "小龙女");
        //把HashMap作为元素添加到ArrayList集合
        array.add(hm2);

        HashMap<String, String> hm3 = new HashMap<String, String>();
        hm3.put("令狐冲", "任盈盈");
        hm3.put("林平之", "岳灵珊");
        //把HashMap作为元素添加到ArrayList集合
        array.add(hm3);

        //遍历ArrayList集合
        for (HashMap<String, String> hm : array) {
            Set<String> keySet = hm.keySet();
            for (String key : keySet) {
                String value = hm.get(key);
                System.out.println(key + "," + value);
            }
        }
    }
}
  1. 集合嵌套之HashMap嵌套ArrayList

案例需求

  • 创建一个HashMap集合,存储三个键值对元素,每一个键值对元素的键是String,值是ArrayList
  • 每一个ArrayList的元素是String,并遍历。

代码实现

public class HashMapIncludeArrayListDemo {
    public static void main(String[] args) {
        //创建HashMap集合
        HashMap<String, ArrayList<String>> hm = new HashMap<String, ArrayList<String>>();

        //创建ArrayList集合,并添加元素
        ArrayList<String> sgyy = new ArrayList<String>();
        sgyy.add("诸葛亮");
        sgyy.add("赵云");
        //把ArrayList作为元素添加到HashMap集合
        hm.put("三国演义",sgyy);

        ArrayList<String> xyj = new ArrayList<String>();
        xyj.add("唐僧");
        xyj.add("孙悟空");
        //把ArrayList作为元素添加到HashMap集合
        hm.put("西游记",xyj);

        ArrayList<String> shz = new ArrayList<String>();
        shz.add("武松");
        shz.add("鲁智深");
        //把ArrayList作为元素添加到HashMap集合
        hm.put("水浒传",shz);

        //遍历HashMap集合
        Set<String> keySet = hm.keySet();
        for(String key : keySet) {
            System.out.println(key);
            ArrayList<String> value = hm.get(key);
            for(String s : value) {
                System.out.println("\t" + s);
            }
        }
    }
}
  1. 统计字符串中每个字符出现的次数‘

案例需求

  • 键盘录入一个字符串,要求统计字符串中每个字符串出现的次数。
  • 举例:键盘录入“aababcabcdabcde” 在控制台输出:“a(5)b(4)c(3)d(2)e(1)”

代码实现

public class HashMapDemo {
    public static void main(String[] args) {
        //键盘录入一个字符串
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入一个字符串:");
        String line = sc.nextLine();

        //创建HashMap集合,键是Character,值是Integer
//        HashMap<Character, Integer> hm = new HashMap<Character, Integer>();
        TreeMap<Character, Integer> hm = new TreeMap<Character, Integer>();

        //遍历字符串,得到每一个字符
        for (int i = 0; i < line.length(); i++) {
            char key = line.charAt(i);

            //拿得到的每一个字符作为键到HashMap集合中去找对应的值,看其返回值
            Integer value = hm.get(key);

            if (value == null) {
                //如果返回值是null:说明该字符在HashMap集合中不存在,就把该字符作为键,1作为值存储
                hm.put(key,1);
            } else {
                //如果返回值不是null:说明该字符在HashMap集合中存在,把该值加1,然后重新存储该字符和对应的值
                value++;
                hm.put(key,value);
            }
        }

        //遍历HashMap集合,得到键和值,按照要求进行拼接
        StringBuilder sb = new StringBuilder();
        Set<Character> keySet = hm.keySet();
        for(Character key : keySet) {
            Integer value = hm.get(key);
            sb.append(key).append("(").append(value).append(")");
        }

        String result = sb.toString();

        //输出结果
        System.out.println(result);
    }
}

Map集合子类

HashMap

  • 键不可重复,值可重复
  • 底层是哈希表,线程不安全
  • 允许key值为null,value也可以为null
  • HashMap:是基于哈希表的Map接口实现
  • 哈希表的作用是用来保证键的唯一性的
  • HashMap<String,String> 键:String 值: String
    HashMap 根据键的 hashCode 值存储数据,大多数情况下可以直接定位到它的值,因而具有很快的访问速度,但遍历顺序却是不确定的。 HashMap 最多只允许一条记录的键为 null,允许多条记录的值为 null。HashMap 非线程安全,即任一时刻可以有多个线程同时写 HashMap,可能会导致数据的不一致。如果需要满足线程安全,可以用 Collections 的 synchronizedMap 方法使HashMap 具有线程安全的能力,或者使用 ConcurrentHashMap。我们用下面这张图来介绍HashMap 的结构。

        大方向上,HashMap 里面是一个数组,然后数组中每个元素是一个单向链表。上图中,每个绿色的实体是嵌套类 Entry 的         实例,Entry 包含四个属性:key, value, hash 值和用于单向链表的 next。

         1.capacity:当前数组容量,始终保持 2^n,可以扩容,扩容后数组大小为当前的 2 倍。

          2.loadFactor:负载因子,默认为 0.75。

 

java 列表包含 java的列表和集合_键值对_02

Java8 对 HashMap 进行了一些修改,最大的不同就是利用了红黑树,所以其由 数组+链表+红黑树 组成。

根据 Java7 HashMap 的介绍,我们知道,查找的时候,根据 hash 值我们能够快速定位到数组的具体下标,但是之后的话,需要顺着链表一个个比较下去才能找到我们需要的,时间复杂度取决于链表的长度,为 O(n)。为了降低这部分的开销,在 Java8 中,当链表中的元素超过了 8 个以后,会将链表转换为红黑树,在这些位置进行查找的时候可以降低时间复杂度为 O(logN)。

Hashtable

  • 键不可重复值可重复
  • 底层是哈希表
  • 线程安全
  • key和value不允许为null

Hashtable 是遗留类,很多映射的常用功能与 HashMap 类似,不同的是它承自 Dictionary 类,并且是线程安全的,任一时间只有一个线程能写 Hashtable,并发性不如 ConcurrentHashMap,因为 ConcurrentHashMap 引入了分段锁。Hashtable 不建议在新代码中使用,不需要线程安全的场合可以用 HashMap 替换,需要线程安全的场合可以用 ConcurrentHashMap 替换。

LinkedHashMap

  • LinkedHashMap:是Map接口的哈希表和链表列表的实现,具有可预知的迭代顺序

有哈希表保证键的唯一性

Hashtable和HashMap的区别?

Hashtable :线程安全,效率低,不允许null键和null值

HashMap:线程不安全,效率低,允许null键和null值

3.4.4. TreeMap(可排序)

TreeMap 实现 SortedMap 接口,能够把它保存的记录根据键排序,默认是按键值的升序排序,

也可以指定排序的比较器,当用 Iterator 遍历 TreeMap 时,得到的记录是排过序的。

如果使用排序的映射,建议使用 TreeMap。

在使用 TreeMap 时,key 必须实现 Comparable 接口或者在构造 TreeMap 传入自定义的

Comparator,否则会在运行时抛出 java.lang.ClassCastException 类型的异常。

参考:https://www.ibm.com/developerworks/cn/java/j-lo-tree/index.html

3.4.5. LinkHashMap(记录插入顺序)

LinkedHashMap 是 HashMap 的一个子类,保存了记录的插入顺序,在用 Iterator 遍历

LinkedHashMap 时,先得到的记录肯定是先插入的,也可以在构造时带参数,按照访问次序排序。

参考 1:http://www.importnew.com/28263.html

参考 2:http://www.importnew.com/20386.html#comment-648123