day17-18 集合Collection接口和实现类

集合

1. Collection接口

Collection接口的实现子类可以存放多个元素,每个元素可以是Object

有些Collection接口的实现类,可以存放重复的元素,有些不可以

有些Collection接口的实现类,是有序的(List),有些不是有序的(Set)

Collection接口没有直接的实现子类,是通过它的子接口Set 和 List来实现的

2.List接口

有序集合(也称为序列 )。 该界面的用户可以精确控制列表中每个元素的插入位置。 用户可以通过整数索引(列表中的位置)访问元素,并搜索列表中的元素。

2.1 ArrayList

可调整大小的数组的实现List接口。 实现所有可选列表操作,并允许所有元素,包括null 。 除了实现List 接口之外,该类还提供了一些方法来操纵内部使用的存储列表的数组的大小。 (这个类是大致相当于Vector,不同之处在于它是不同步的)。

2.1.1 常用方法

常用方法:

add(Object obj) 添加元素在列表末尾

add(int index,Object obj) 指定位置添加元素

remove(int index) 根据下标删除元素

remove(Object obj) 根据对象删除元素

set(int index,Object obj) 根据下标修改元素

get(int index) 根据下标获取元素

size() 获取集合元素个数(长度)

clear() 清空集合元素

isEmpty()判断集合是否为空

contains(Object obj) 是否包含某个元素

toArray() 转换为数组

package com.atguigu.test1;

import java.util.ArrayList;

/**
 * Collection
 *      List
 *          ArrayList
 * 常用方法:
 *  add(Object obj) 添加元素再列表末尾
 *  add(int index, Object obj) 指定位置添加错误
 *  remove(int index) 根据下标删除元素
 *  remove(Object obj) 根据对象删除元素
 *  set(int index, Object obj) 根据下标修改元素
 *  get(int index) 根据下标获取元素
 *
 *  size() 获取集合元素个数(长度)
 *  clear() 清空集合元素
 *  isEmpty() 判断集合是否为空
 *  contains(Object obj) 是否包含某个元素
 *  toArray() 转化为数组
 *
 *  泛型:可以用于统一集合中的数据类型
 * */
public class TestArrayList1 {
    public static void main(String[] args) {

        ArrayList list = new ArrayList();
        // add(Object o) 只能传对象类型,不能传基本数据类型
        list.add(1);  // 这里是自动装箱成包装类了
        list.add(2.5);
        list.add("abc");
        list.add("a");
        list.add(false);

        System.out.println(list);  // [1, 2.5, abc, a, false]
        Object[] objs = list.toArray();  // 集合转成数组

        for (int i = 0;i < objs.length;i++){
            System.out.print(objs[i] + "\t");  // 1	2.5	abc	a	false
        }
        System.out.println();
        System.out.println("----------------------");

        ArrayList<Integer> list1 = new ArrayList<>();

//        list1.add("123");  // 报错 泛型规定了只能传入Integer类型
        list1.add(1);
        list1.add(123);
        list1.add(444);
        list1.add(666);
        list1.add(243);

        // contains判断是否包含传入的元素,包含返回true,否则返回false
        System.out.println("list1是否包含:" + list1.contains(123));  // list1是否包含:true

        // size() 返回集合的元素个数,类型为int
        System.out.println("list1集合长度为:" + list1.size());  // list1集合长度为:5

        // remove(index)  根据传入的索引删除元素,并返回该元素的值
        System.out.println("删除的元素为:" + list1.remove(0));  // 删除的元素为:1

        // set(int index, E element)  根据传入的索引更改为第2个参数的值,并返回修改之前的元素值
        System.out.println("修改之前的元素为:" + list1.set(0, 999));  // 修改之前的元素为:123

        System.out.println("list1集合长度为:" + list1.size());  // list1集合长度为:4

        // add(int index, E element) 根据传入的索引插入元素 元素值为第2个参数
        list1.add(1, 888);

        // get(index) 根据索引获取对应的值
        System.out.println("下标为1的元素是:" + list1.get(1));  // 下标为1的元素是:888

        // isEmpty() 判断集合是否为空,没有元素返回true 有元素则返回false
        System.out.println("list1集合是否为空:" + list1.isEmpty());  // list1集合是否为空:false

        // clear() 清空集合中所有元素
        list1.clear();

        System.out.println("调用clear方法之后,list1集合是否为空:" + list1.isEmpty());  // 调用clear方法之后,list1集合是否为空:true

        System.out.println("调用clear方法之后,list1集合长度为:" + list1.size());  // 调用clear方法之后,list1集合长度为:0
    }
}

2.1.2 遍历

ArrayList遍历方式 3种

1.普通for循环遍历

2.迭代器遍历

3.增强for循环(forEach)遍历

package com.atguigu.test2;

import java.util.ArrayList;
import java.util.Iterator;

/**
 * ArrayList遍历方式 3种
 *  1.普通for循环遍历
 *  2.迭代器遍历
 *  3.增强for循环(forEach)遍历
 * */
public class TestArrayList1 {
    public static void main(String[] args) {

        ArrayList<String> list = new ArrayList<>();

        list.add("hello");
        list.add("hi");
        list.add("abc");
        list.add("123123");
        list.add("234i2u");

        // 方式1:普通for循环遍历
        for (int i = 0;i < list.size();i++){
            System.out.print(list.get(i) + "\t");  // hello	hi	abc	123123	234i2u
        }
        System.out.println();

        System.out.println("=================================");


        // 方式2:迭代器遍历
        Iterator<String> it = list.iterator();
        while (it.hasNext()){
            System.out.print(it.next() + "\t");  // hello	hi	abc	123123	234i2u
        }

        System.out.println();
        System.out.println("=================================");

        // 方式3:增强for循环 是JDK1.5新增的内容 底层实现依然是迭代器
        for (String str : list){
            System.out.print(str + "\t");  // hello	hi	abc	123123	234i2u
        }

    }
}

使用for循环和迭代器清空集合元素

package com.atguigu.test2;

import java.util.ArrayList;
import java.util.Iterator;

public class TestArrayList2 {
    public static void main(String[] args) {

        ArrayList<String> list = new ArrayList<>();

        list.add("hello");
        list.add("hi");
        list.add("ada");
        list.add("qwe");
        list.add("!@#l2i3");

        // 不使用clear方法 清空集合中的元素, 使用for循环倒序方式清除元素
//        for (int i = list.size() - 1;i >= 0;i--){
//            list.remove(i);
//        }

        // 使用迭代器遍历方式删除,无需索引
        Iterator<String> it = list.iterator();
        while (it.hasNext()){
            it.next();
            it.remove();
        }

        for (String str : list){
            System.out.print(str + "\t");
        }

    }
}

2.1.3 集合特点

集合特点:有序(插入顺序),有下标,允许null元素,线程不安全

增删改查效率:

增加需要扩容的情况,删除需要移动元素位置的情况,效率低,慢,

查询快,修改快

当我们使用无参构造创建一个ArrayList实例,底层维护一个空数组

当我们第一次添加元素,elementData数组指向一个长度为10的数组,数组长度修改为10

后续添加第11个元素的时候,将数组扩容为原数组长度1.5倍

删除元素地层水会用System.arraycopy()方法移动元素,再将最后一个元素赋值为null

clear()方法 循环遍历集合 将集合中的每个元素都赋值为null size元素个数修改为0

2.2 Vector

Vector类实现了可扩展的对象数组。像数组一样,它包含可以使用整数索引访问的组件。但是, Vector的大小可以根据需要增长或缩小,以适应在创建Vector`之后添加和删除项目。

每个向量尝试通过维护capacitycapacityIncrement优化存储capacityIncrementcapacity总是至少与矢量大小一样大; 通常较大,因为当向量中添加组分时,向量的存储空间大小capacityIncrement 。 应用程序可以在插入大量组件之前增加向量的容量; 这减少了增量重新分配的数量。

Vector类 提供与ArrayList相同的API

Vector 线程安全的 JDK1.0 无参构造初始化长度为10的数组 扩容2倍

ArrayList 线程不安全 JDK1.2 无参构造初始化空数组 扩容1.5倍

package com.atguigu.test4;

import java.util.Iterator;
import java.util.Vector;

/**
 * Vector类 提供与ArrayList相同的API
 *
 * Vector        线程安全的  JDK1.0  无参构造初始化长度为10的数组     扩容2倍
 * ArrayList    线程不安全   JDK1.2  无参构造初始化空数组            扩容1.5倍
 * */
public class TestVector1 {
    public static void main(String[] args) {


        Vector<Character> v1 = new Vector<>();
//        Vector v2 = new Vector();

//        v2.add(123);  // 未设置泛型的话,默认是Object类型,也就是可以传任意类型的参数
        v1.add('a');  // 设置泛型为Character,只能添加Character类型的参数
        v1.add('b');
        v1.add('c');
        v1.add('d');
        v1.add('e');

        // 删除索引为0的元素,并返回元素的值
        System.out.println(v1.remove(0));  // a

        // 删除对象为'b'的元素,注意需要转换为包装类,不然会报错 自动提升为整数类型,导致下标越界
//        System.out.println(v1.remove('b'));  // 报错 这里b自动转成98了,导致下标越界
        System.out.println(v1.remove(new Character('b')));  // true

        // 修改索引为0的元素,值改为'C' 并返回元素修改之前的值
        System.out.println(v1.set(0, 'C'));  // c

        // 判断集合是否为空,为空则返回false,不为空则返回false
        System.out.println(v1.isEmpty());  // false

        // 清空集合中所有元素
        v1.clear();

        // 获取集合中元素个数
        System.out.println(v1.size());  // 0
        System.out.println("------------");


        // 遍历方式 3种
        Vector<Character> v2 = new Vector<>();
        v2.add('a');
        v2.add('b');
        v2.add('c');
        v2.add('d');
        v2.add('e');

        // 方式1:普通for循环
        for (int i = 0;i < v2.size();i++){
            System.out.print(v2.get(i) + "\t");  // a	b	c	d	e
        }
        System.out.println();
        System.out.println("========================");


        // 方式2:迭代器遍历
        Iterator<Character> it = v2.iterator();
        while (it.hasNext()){
            System.out.print(it.next() + "\t");  // a	b	c	d	e
        }
        System.out.println();
        System.out.println("========================");


        // 方式3:增强for循环
        for (Character ch: v2){
            System.out.print(ch + "\t");  // a	b	c	d	e
        }


    }
}

2.3 LinkedList

双链表实现了ListDeque接口。实现所有可选列表操作,并允许所有元素(包括null )。

所有的操作都能像双向列表一样预期。 索引到列表中的操作将从开始或结束遍历列表,以更接近指定的索引为准。

请注意,此实现不同步。**如果多个线程同时访问链接列表,并且至少有一个线程在结构上修改列表,则必须在外部进行同步。(结构修改是添加或删除一个或多个元素的任何操作;仅设置元素的值不是结构修改。)这通常通过在自然封装列表的对象上进行同步来实现。如果没有这样的对象存在,列表应该使用Collections.synchronizedList方法“包装”。这最好在创建时完成,以防止意外的不同步访问列表:

2.3.1 常用方法

contains(Object obj) 是否包含指定元素

add()添加元素

addFirst() 添加在第一个位置

addLast() 添加在末尾

remove() 删除第一个元素

remove(int index) 根据下标删除元素

removeFirst()删除第一个元素

removeLast() 删除最后一个元素

get(int index) 根据下标获取元素

getFirst() 获取第一个元素

getLast() 获取最后一个元素

set(int index,Object obj) 修改元素

size() 获取集合元素个数

clear() 清空集合

isEmpty() 判断集合是否为空

toArray() 将集合转换为数组

package com.atguigu.test5;

import java.util.LinkedList;

/**
 * LinkedList 常用方法
 *
 * contains(Object obj) 是否包含指定元素
 *
 * add() 添加元素
 * addFirst() 添加在第一个位置
 * addLast()  添加在最后一个位置
 * remove() 删除第一个元素
 * remove(int index) 根据下标删除元素
 * removeFirst() 删除第一个元素
 * removeLast() 删除最后一个元素
 *
 * get(int index) 根据下标获取元素
 * getFirst() 获取第一个元素
 * getLast() 获取最后一个元素
 *
 *
 * */
public class TestLinkedList1 {
    public static void main(String[] args) {
        LinkedList<String> list = new LinkedList<>();

        list.add("abc");  // 3
        list.addFirst("hello");  // 1
        list.addLast("666");  // 0
        list.add(2, "world");  // 2

        System.out.println("删除第一个元素:其内容为:" + list.removeFirst());  // 删除第一个元素:其内容为:hello
        System.out.println("删除内容为abc的元素:" + list.remove("abc"));  // 删除内容为abc的元素:true

        // 将索引为0的元素值修改为JAVA,并返回修改前的元素值
        System.out.println("修改前的元素值为:" + list.set(0, "JAVA"));  // 修改前的元素值为:world


        System.out.println("获取第一个元素:" + list.get(0));  // 获取第一个元素:JAVA
        System.out.println("获取第一个元素:" + list.getFirst());  // 获取第一个元素:JAVA
        System.out.println("获取最后一个元素:" + list.getLast());  // 获取最后一个元素:666

        // 判断是否包含传入的元素,包含返回true,不包含则返回false
        System.out.println("是否包含内容为PYTHON的元素:" + list.contains("PYTHON"));  // 是否包含内容为PYTHON的元素:false
        System.out.println("是否包含内容为JAVA的元素:" + list.contains("JAVA"));  // 是否包含内容为JAVA的元素:true

        System.out.println("集合长度为:" + list.size());  // 集合长度为:2

        System.out.println(list);  // [JAVA, 666]
        Object[] objects = list.toArray();  // 集合转换为数组

        for (Object obj : list){
            System.out.print(obj + "\t");  // JAVA	666
        }

        System.out.println();

        list.clear();  // 清空集合中所有元素

        // 判断集合是否为空 为空返回true 否则返回false
        System.out.println("集合是否为空:" + list.isEmpty());  // 集合是否为空:true


    }
}
2.3.2 遍历

LinkedList集合遍历 三种方式

1.普通for循环

2.迭代器

3.增强for循环

package com.atguigu.test5;

import java.util.Iterator;
import java.util.LinkedList;

/**
 * LinkedList集合遍历 三种方式
 * 1.普通for循环
 * 2.迭代器
 * 3.增强for循环
 */
public class TestLinkedList2 {
    public static void main(String[] args) {
        LinkedList<String> list = new LinkedList<>();

        // 设置泛型为String类型,只能添加String类型的元素
        list.add("abc");
        list.add("hello");
        list.add("666");
        list.add("world");

        // 方式1:普通for循环
        for (int i = 0;i < list.size();i++){
            System.out.print(list.get(i) + "\t");  // abc	hello	666	world
        }
        System.out.println();
        System.out.println("======================");

        // 方式2:迭代器遍历
        Iterator<String> it = list.iterator();
        while (it.hasNext()){
            System.out.print(it.next() + "\t");  // abc	hello	666	world
        }
        System.out.println();
        System.out.println("======================");

        // 方式3:增强for循环
        for (String str : list){
            System.out.print(str + "\t");  // abc	hello	666	world
        }
    }
}

2.3.3 集合特点

LinkedList集合特点

有序 有下标 允许重复 允许为null 线程不安全 不需要扩容 没有初始大小

增删改查效率:

查询、修改慢,因为根据下标查找某个元素,必须先找到与之相邻的元素 所以效率低 而修改也是先查询

删除、添加快,因为不需要扩容,也不需要移动元素

LinkedList底层数据结构为双向链表,每一个元素称之为一个Node,每个Node包含三部分,上一个元素的引用

下一个元素的引用,当前元素本身

get(int index)优化:根据传入的下标做判断 如果小于总长度中间值 从前往后查找

如果大于等于集合长度中间值 那么从后往前查找

package com.atguigu.test6;

import java.util.Iterator;
import java.util.LinkedList;

/**
 * LinkedList集合特点
 * 有序 有下标 允许重复 允许为null 线程不安全 不需要扩容 没有初始大小
 * 增删改查效率:
 *      查询、修改慢,因为根据下标查找某个元素,必须先找到与之相邻的元素 所以效率低 而修改也是需要先查询
 *      删除、添加快,因为不需要扩容,也不需要移动元素
 *
 * LinkedList底层数据结构为双向链表,每一个元素称之为一个Node,每个Node包含三部分,上一个元素的引用,下一个元素的引用,以及当前元素本身
 *
 * get(int index)优化:根据传入的下标做判断 如果小于总长度中间值 从前往后查找 如果大于集合长度中间值 那么从后往前查找
 * */
public class TestLinkedList1 {
    public static void main(String[] args) {
        LinkedList<Integer> list = new LinkedList<>();

        for (int i = 0;i < 300000;i++){
            list.add(i);
        }

        // 测试普通for循环耗时 42672毫秒
//        long startTime = System.currentTimeMillis();  // 开始时间
//        for (int i = 0;i < list.size();i++){
//            System.out.println(list.get(i));
//        }
//        long endTime = System.currentTimeMillis();  // 结束时间
//        System.out.println("普通for循环遍历耗时:" + (endTime - startTime) + "毫秒");  // 普通for循环遍历耗时:42672毫秒


        // 测试迭代器遍历耗时 1065毫秒
//        Iterator<Integer> it = list.iterator();
//        long startTime = System.currentTimeMillis();  // 开始时间
//        while (it.hasNext()){
//            System.out.println(it.next());
//        }
//        long endTime = System.currentTimeMillis();  // 结束时间
//        System.out.println("迭代器遍历耗时:" + (endTime - startTime) + "毫秒");  // 迭代器遍历耗时:1065毫秒


        // 测试增强for循环遍历耗时 849毫秒
        long startTime = System.currentTimeMillis();  // 开始时间
        for (Integer i : list){
            System.out.println(i);
        }
        long endTime = System.currentTimeMillis();  // 结束时间
        System.out.println("增强for循环遍历耗时:" + (endTime - startTime) + "毫秒");  // 增强for循环遍历耗时:849毫秒
    }
}

3.Map接口

将键映射到值的对象。地图不能包含重复的键;每个键可以映射到最多一个值。

这个接口取代了Dictionary类,它是一个完全抽象的类而不是接口。

3.1 HashMap

基于哈希表的实现的Map接口。 此实现提供了所有可选的地图操作,并允许null的值和null键。 ( HashMap类大致相当于Hashtable ,除了它是不同步的,并允许null)。这个类不能保证地图的顺序; 特别是,它不能保证订单在一段时间内保持不变。

3.1.1 常用方法

Map

HashMap :允许键和值为null 但是键不能重复 如果重复添加 将覆盖

常用方法:

clear() 清空集合

containsKey(Object key) 是否包含某一个键

containsValue(Object value) 是否包含某一个值

entrySet() 获取所有的键值对的组合

keySet() 获取所有的键

values() 获取所有的值

put(Object key ,Object value) 添加元素

remove(Object key) 根据键删除元素

get(Object key ) 根据键查找值

replace(Object key,Object value) 根据键修改对应的值

size() 集合长度

isEmpty() 判断集合是否为空

package com.atguigu.test7;

import java.util.HashMap;

/**
 * Map
 *      hashMap:允许键和值为null 但是键不能重复 如果重复添加 将覆盖
 * 常用方法:
 *      clear() 清空集合
 *      containsKey() 是否包含某一个值
 *      containsValue() 是否包含某一个值
 *      entrySet() 获取所有的键值对的组合
 *      KeySet() 获取所有的键
 *      values() 获取所有的值
 *      put(Object key, Object value) 添加元素
 *      remove(Object key) 根据键删除元素
 *      get(Object key) 根据键查找值
 *      replace(Object key, Object value) 根据键修改对应的值
 *      size() 集合长度
 *      isEmpty() 判断集合是否为空
 * */
public class TestHashMap1 {
    public static void main(String[] args) {
        HashMap<String, String> map = new HashMap<>();

        map.put("CN", "中国");
        map.put("US", "美国");
        map.put("RU", "俄国");
        map.put("JP", "日本");
        map.put("KR", "韩国");

        // 根据键查找并返回对应的值,如果键不存在则返回null
        System.out.println("根据键CN获取对应的值:" + map.get("CN"));  // 根据键CN获取对应的值:中国
        System.out.println("根据键CN获取对应的值:" + map.get("HELLO"));  // 根据键CN获取对应的值:null

        // 新增键值对,如果键已存在,将替换对应的值
        map.put("CN", "中华人民共和国");

        // 根据键获取对应的值
        System.out.println("根据键CN获取对应的值:" + map.get("CN"));  // 根据键CN获取对应的值:中华人民共和国

        // 获取集合中存在的键值对的个数
        System.out.println("集合长度为:" + map.size());  // 集合长度为:5

        // 根据键删除并返回对应的值 如果键不存在则返回null
        System.out.println("根据键KR删除元素:" + map.remove("KR"));  // 根据键KR删除元素:韩国
        System.out.println("根据键KR删除元素:" + map.remove("JAVA"));  // 根据键KR删除元素:null

        // 将传入的值根据键替换原来对应的值,并返回修改之前的值
        System.out.println("根据键JP修改之前的内容:" + map.replace("JP", "鬼子"));  // 根据键JP修改之前的内容:日本

        // 根据键获取对应的值
        System.out.println("根据键JP获取对应的值:" + map.get("JP"));  // 根据键JP获取对应的值:鬼子

        // 判断集合中的键是否包含此字符, 如果包含返回true,否则返回false
        System.out.println("集合中的键是否包含'CN':" + map.containsKey("CN"));  // 集合中的键是否包含'CN':true
        System.out.println("集合中的键是否包含'QW':" + map.containsKey("QW"));  // 集合中的键是否包含'QW':false

        // 判断集合中的值是否包含此字符, 如果包含返回true,否则返回false
        System.out.println("集合中的值是否包含'美国':" + map.containsValue("美国"));  // 集合中的值是否包含'美国':true
        System.out.println("集合中的值是否包含'英国':" + map.containsValue("英国"));  // 集合中的值是否包含'英国':false

        // 清空集合中所有的元素
        map.clear();

        // 判断集合是否为空,如果为空返回true,否则返回false
        System.out.println("清空之后集合是否为空:" + map.isEmpty());  // 清空之后集合是否为空:true

    }
}
3.1.2 遍历

HashMap遍历方式6种

1.获取所有的键 再根据键获取值

2.获取所有的值

3.获取所有的键值对的组合

4.获取所有的键的迭代器

5.获取所有的值的迭代器

6.获取所有的键值对组合的迭代器

package com.atguigu.test7;

import java.util.*;

/**
 * HashMap 6种遍历方式
 *  1.获取所有的键 再根据键获取值
 *  2.获取所有的值
 *  3.获取所有的键值对的组合
 *  4.获取所有的键的迭代器
 *  5.获取所有的值的迭代器
 *  6.获取所有的键值对的迭代器
 * */
public class TestHashMap2 {
    public static void main(String[] args) {
        HashMap<String, String> map = new HashMap<>();

        map.put("CN", "中国");
        map.put("US", "美国");
        map.put("RU", "俄国");
        map.put("JP", "日本");
        map.put("KR", "韩国");

        // 方式1:获取所有的键 再根据键获取值
        Set<String> keys = map.keySet();
        for (String key : keys){
            System.out.println(key + "===>" + map.get(key));
        }
        System.out.println("-----------------------");


        // 方式2:获取所有的值
        Collection<String> values = map.values();
        for (String val : values){
            System.out.println(val);
        }
        System.out.println("-----------------------");

        // 方式3:获取所有的键值对
        Set<Map.Entry<String, String>> entries = map.entrySet();
        for (Map.Entry<String , String> entry : entries){
            System.out.println(entry.getKey() + "===>" + entry.getValue());
        }
        System.out.println("-----------------------");

        // 方式4:获取所有的键的迭代器
        Iterator<String> it1 = map.keySet().iterator();
        while (it1.hasNext()){
            // 运行报错 因为键和值各调用了一次next(),导致键和值的关系映射不正确
//            System.out.println(it1.next() + "===>" + map.get(it1.next()));

            // 正确用法 将next()赋值给变量,再使用
            String next = it1.next();
            System.out.println(next + "===>" + map.get(next));
        }
        System.out.println("-----------------------");

        // 方式5:获取所有的值的迭代器
        Iterator<String> it2 = map.values().iterator();
        while (it2.hasNext()){
            System.out.println(it2.next());
        }
        System.out.println("-----------------------");

        // 方式6:获取所有的键值对的迭代器
        Iterator<Map.Entry<String, String>> it3 = map.entrySet().iterator();
        while (it3.hasNext()){
            Map.Entry<String, String> next = it3.next();
            System.out.println(next.getKey() + "===>" + next.getValue());
        }
    }

}

3.1.3 集合特点

HashMap 无序 没有下标 键不能重复 值可以重复 初始数组长度16 扩容2倍 线程不安全

回顾我们之前所学过的数据结构:

ArrayList 数组 因为空间是连续的 有下标 所以 查询 修改快

LinkedList 双向链表 因为空间不连续 有下标 所以 查询 修改 慢

HashMap数据结构和集合特点:

JDK 1.7 数组 + 单向链表

JDK 1.8 数组 + 单向链表 + 红黑树

HashMap中一个元素称之为一个Entry 每个元素包含四部分 key value hash next

默认初始容量:16 表示数组的长度为16

默认负载因子:0.75F 表示数组的使用率达到75% 就扩容2倍

树化阈值:8

最小树化数组长度:64

表示数组的长度大于64 并且 链表的长度大于8 将链表转换为红黑树

取消树化阈值:6 当链表的长度小于6 红黑树转换为单向链表

HashMap添加元素过程:

首先根据键计算出来的hash值 对数组的长度 进行与&运算

得到具体的数值存储在对应的数组下标位置

如果当前位置为null 表示没有内容 直接存放

如果当前位置不为null 表示有内容

进行hash值 和 == 以及 equals比较 key值 如果相同 则直接覆盖

如果不同 再进行判断 是树节点 还是 链表节点 分别以不同的方式添加元素

链表转换为红黑树的概率的非常低的 这里设计到统计学中的 泊松分布

package com.atguigu.test1;

import java.util.HashMap;

/**
 * 回顾我们之前所学过的数据结构:
 * ArrayList    数组      因为空间是连续的    有下标  所以查询、修改快
 * LinkedList   双向链表  因为空间不连续      有下标  所以查询、修改慢
 *
 * HashMap数据结构和集合特点:
 *  JDK 1.7 数组 + 单向链表
 *  JDK 1.8 数组 + 单向链表 + 红黑树
 *
 *  HashMap种一个元素称之为一个Entry 每个元素包含四部分    key value hash next
 *
 *  默认初始容量:16 表示数组的长度为16
 *  默认负载因子:0.75F 表示数组的使用率达到75% 就扩容2倍
 *  树华阈值:8
 *  最小化数组长度:64
 *  表示数组的长度大于64 并且链表的长度大于8 将链表转换为红黑树
 *
 *  取消树华阈值:6 当链表的长度小于6 红黑树转换为单向链表
 *
 * HashMap添加元素过程:
 *  首先根据键计算出来的hash值 对数组的长度 进行&运算
 *  得到具体的数值存储在对应的数值下标位置
 *  如果当前位置为null 表示没有内容 直接存放
 *  如果当前位置不为null 表示有内容
 *  进行hash值 和 == 以及 equals比较 key值 如果相同 则直接覆盖
 *  如果不同 再进行判断 是树节点 还是 链表节点 分别以不同的方式添加元素
 *
 *  链表转换为红黑树的概率是非常低的 这里涉及到统计学中的泊松分布
 * */

class TestHashMap1 {
    public static void main(String[] args) {
        HashMap<Student, String> map = new HashMap<>();
        map.put(null, null);  // 添加的元素可以为null
    }
}

3.2 Hashtable

Hashtable和HashMap的区别?

Hashtable 线程安全 初始数组长度11 不允许null的键 和 null的值 扩容2倍 + 1

HashMap 线程不安全 初始数组长度16 允许null的键 和 null的值 扩容2倍

package com.atguigu.test2;

import java.util.Collection;
import java.util.Hashtable;
import java.util.Map;
import java.util.Set;

/**
 * Hashtable和HashMap的区别?
 * Hashtable 线程安全   初始数组长度11 不允许null的键 和 null的值 扩容2倍 + 1
 * HashMap   线程不安全 初始数组长度16 允许null的键 和 null的值   扩容2倍
 * */
public class TestHashtable1 {
    public static void main(String[] args) {
//        Hashtable table = new Hashtable();

//        table.put(12, "aw");  // 不设置泛型的话,默认使用object类型,put方法可以传任意类型的参数

        Hashtable<String, Integer> table = new Hashtable<>();

        table.put("A", 100);
        table.put("B", 101);
        table.put("C", 102);
        table.put("D", 103);
        table.put("E", 104);
        table.put("F", 105);

        String str = new String("A");
        // 无论传入的参数是new出来的还是直接赋值的都可以查找到,因为比对的方式是==或equals,String的equals重写过
        // 根据键删除对应的元素,返回对应的值
        System.out.println(table.remove(str));  // 100

        // 返回集合中元素的数量
        System.out.println(table.size());  // 5

        // 根据键替换值,并返回被替换的值
        System.out.println(table.replace("B", 999));  // 101

        // 根据键查找值,查找到返回对应的值,未找到返回null
        System.out.println(table.get("B"));  // 999
        System.out.println(table.get("QW"));  // null

        // 查看集合是否包含此键,包含返回true,否则返回false
        System.out.println(table.containsKey("B"));  // true
        System.out.println(table.containsKey("QW"));  // false

        // 查看集合中是否包含此值,包含返回true,否则返回false
        System.out.println(table.containsValue(999));  // true
        System.out.println(table.containsValue(888));  // false

        // 遍历方式与HashMap相同 6种方式
        // 显示集合中所有的键,再根据键获取值
        Set<String> keys = table.keySet();
        for (String key : keys){
            System.out.print(key + "===>" + table.get(key) + "\t");  //  F===>105	E===>104	D===>103	C===>102	B===>999
        }
        System.out.println();
        System.out.println("--------------------------");

        // 显示集合中所有的值
        Collection<Integer> values = table.values();
        for (Integer val : values){
            System.out.print(val + "\t");  // 105	104	103	102	999
        }
        System.out.println();
        System.out.println("--------------------------");

        // 显示集合中所有键值对
        Set<Map.Entry<String, Integer>> entries = table.entrySet();
        for (Map.Entry<String, Integer> entry : entries){
            System.out.print(entry.getKey() + "===>" + entry.getValue() + "\t");  // F===>105	E===>104	D===>103	C===>102	B===>999
        }

    }
}

3.3 TreeMap

TreeMap : 有序的Map集合 顺序为键比较的顺序 升序

如果使用自定义的数据类型来作为TreeMap的键 此类必须实现Comparable接口 重写 compareTo方法

制定比较规则

package com.atguigu.test2;

import java.util.Iterator;
import java.util.Map;
import java.util.TreeMap;

/**
 * TreeMap:有序的Map集合 顺序为键比较的顺序 升序
 * 如果使用自定义的数据类型作为TreeMap的键 此类必须实现Comparable接口 重写 compareTo方法  制定比较规则
 * */
public class TestTreeMap {
    public static void main(String[] args) {
        TreeMap<Integer, String> map = new TreeMap<>();

        map.put(122, "hello1");
        map.put(895, "hello2");
        map.put(741, "hello3");
        map.put(258, "hello4");
        map.put(369, "hello5");

        Iterator<Map.Entry<Integer, String>> it = map.entrySet().iterator();
        while (it.hasNext()){
            Map.Entry<Integer, String> next = it.next();
            // 按照键比较的顺序进行排序 升序
            System.out.print(next + "\t");  // 122=hello1	258=hello4	369=hello5	741=hello3	895=hello2
        }
        System.out.println();
        System.out.println("--------------------------------------");

        TreeMap<Student, String> stuMap = new TreeMap<>();

        Student stu1 = new Student("赵四", 17);
        Student stu2 = new Student("刘能", 15);
        Student stu3 = new Student("大拿", 22);
        Student stu4 = new Student("小宝", 18);

        // Student必须实现Comparable接口的compareTo方法才能进行添加,不然会报错
        // 根据重写的规则进行比较排序
        stuMap.put(stu1, "a1");
        stuMap.put(stu2, "a2");
        stuMap.put(stu3, "a3");
        stuMap.put(stu4, "a4");

        Iterator<Map.Entry<Student, String>> it1 = stuMap.entrySet().iterator();


        while (it1.hasNext()){
            Map.Entry<Student, String> next = it1.next();
            // Student{name='刘能', age=15}=a2	Student{name='赵四', age=17}=a1	Student{name='小宝', age=18}=a4	Student{name='大拿', age=22}=a3
            System.out.print(next + "\t");
        }
    }

}

package com.atguigu.test2;

public class Student implements Comparable<Student>{
    private String name;
    private int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }


    @Override
    public int compareTo(Student stu) {
        if (this.getAge() > stu.getAge()){
            return 1;
        }else if(this.getAge() < stu.getAge()){
            return -1;
        }
        return 0;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

3.4 LinkedHashMap

LinkedHashMap 基于链表的HashMap 有序的Map集合 顺序为插入顺序

package com.atguigu.test2;

import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * LinkedHashMap 基于链表的HashMap 有序的map集合 顺序为插入顺序
 * */
public class TestLinkedHashMap {
    public static void main(String[] args) {

        LinkedHashMap<String, Integer> map = new LinkedHashMap<>();

        map.put("abc", 895);
        map.put("hello", 451);
        map.put("world", 741);
        map.put("java", 852);
        map.put("mysql", 120);

        Iterator<Map.Entry<String, Integer>> it = map.entrySet().iterator();
        while (it.hasNext()){
            Map.Entry<String, Integer> next = it.next();
            // abc===>895	hello===>451	world===>741	java===>852	mysql===>120
            System.out.print(next.getKey() + "===>" + next.getValue() + "\t");
        }
    }
}

3.4 Properties

Properties类是单独用于存储键和值都是String类型的Map集合

不要使用从Hashtable继承而来是 put或者putAll方法 因为这两个方法可以添加非String的键 和 值

应该使用本类提供 setProperty() 来添加元素

list(OutputStream out) : 指定打印流对象将此Properties对象打印

load(InputStream in) :指定读取流将流中的数据加载到Properties对象中

package com.atguigu.test3;

import java.util.Properties;

/**
 * Properties类是单独用于存储键和值都是String类型的Map集合
 * 不要使用从Hashtable继承而来的 put或者putAll方法 因为这两个方法可以添加非String的键和值
 * 应该使用本类提供 setProperty() 来添加元素
 *
 * list(OutputStream out): 指定打印流对象将此Properties对象打印
 * load(InputStream in): 指定读取流将流中的数据加载到Properties对象中
 * */
public class TestProperties {
    public static void main(String[] args) {

        Properties p1 = System.getProperties();

        p1.list(System.out);
        System.out.println("-----------------");

        Properties p2 = new Properties();

        p2.put(100, "abc");  // put方法可以添加非String的键和值 不推荐使用

//        p2.setProperty("a", 666);  // 报错 要求传String类型,这里传的是Integer类型
        p2.setProperty("a", "666");

        // 继承的get方法可以获取非String的键 不推荐使用
        System.out.println(p2.get(100));  // abc

        // 推荐使用getProperty()
        System.out.println(p2.getProperty("a"));  // 666
    }
}

4.Set接口

4.1 HashSet

HashSet常用方法

add()

remove()

contains()

clear()

isEmpty()

size()

iterator()

HashSet底层维护的是一个HashMap 即:HashSet中的元素将存放在HashMap中键的位置

所以回顾HashMap中键的特点 不能重复 没有顺序 允许为null

HashSet去重复的依据是什么,HashMap中键去重复的依据?

两个对象equals比较为true 并且hashCode相同 则认为是重复的元素

package com.atguigu.test4;

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

/**
 * HashSet常用方法
 * add()
 * remove()
 * contains()
 * clear()
 * isEmpty()
 * size()
 * iterator()
 *
 * HashSet底层维护的是一个HashMap 即:HashSet中的元素将存放在HashMap中键的位置
 * 所以回顾HashMap中键的特点 不能重复 没有顺序 允许为null
 *
 * HashSet去重复的依据是什么,HashMap中键去重复的依据?
 *  两个对象equals比较为true,并且hashCode相同 则认为是重复的元素
 * */
public class TestHashSet {

    public static void main(String[] args) {

        HashSet<String> set = new HashSet<>();

        System.out.println(set.add("abc"));  // true
        System.out.println(set.add("abc"));  // 因为set中已经存放了abc,所以添加失败

        set.add("123");
        set.add("666");
        set.add("hello");
        set.add("world");

        // 根据传入的元素进行删除 删除成功 返回true 否则返回false
        System.out.println(set.remove("abc"));  // true

        // 查看集合中是否包含此元素 包含返回true 否则返回false
        System.out.println(set.contains("hello"));  // true
        System.out.println(set.contains("python"));  // false

        // 查看集合中元素的数量
        System.out.println(set.size());  // 4

        // 查看集合是否为空 是返回true 否则返回false
        System.out.println(set.isEmpty());  // false

        System.out.println("--------------------");


        // 因为HashSet没有下标 没有顺序 所以不能单独查询某个元素 只能遍历
        Iterator<String> it = set.iterator();
        while (it.hasNext()){
            System.out.print(it.next() + "\t");  // 123	world	666	hello
        }
        System.out.println();
        System.out.println("--------------------");

        for (String str : set){
            System.out.print(str + "\t");  // 123	world	666	hello
        }
    }
}

package com.atguigu.test4;

import java.util.Objects;

public class Person {
    private String name;
    private String idCard;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getIdCard() {
        return idCard;
    }

    public void setIdCard(String idCard) {
        this.idCard = idCard;
    }

    public Person() {
    }

    public Person(String name, String idCard) {
        this.name = name;
        this.idCard = idCard;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return Objects.equals(name, person.name) &&
                Objects.equals(idCard, person.idCard);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, idCard);
    }
}

package com.atguigu.test4;

import java.util.HashSet;

public class TestPerson {
    public static void main(String[] args) {

        HashSet<Person> perSet = new HashSet<>();

        Person p1 = new Person("赵四", "123091784203974197412421343");
        Person p2 = new Person("赵四", "123091784203974197412421343");

        // 需要重写equals和hashCode才能去重成功,不然不能去重
        System.out.println(perSet.add(p1));  // true
        System.out.println(perSet.add(p2));  // 去重成功 显示false
        System.out.println(perSet.add(new Person("赵四", "123091784203974197412421343")));  // 去重成功 显示false


        System.out.println(perSet.size());  // 集合中元素数量显示为1 表示去重成功

    }
}

4.2 TreeSet

TreeSet : 底层实现是 TreeMap 有序是Set集合 顺序为比较顺序

package com.atguigu.test5;

import java.util.Iterator;
import java.util.TreeSet;

/**
 * TreeSet:底层实现是TreeMap 有序是Set集合 顺序为比较顺序
 * */
public class TestTreeSet {
    public static void main(String[] args) {

        TreeSet<Integer> set = new TreeSet<>();
        set.add(132);
        set.add(354);
        set.add(234);
        set.add(456);
        set.add(923);
        set.add(45);

        // 删除集合中的元素 删除成功返回true 删除失败返回false
        System.out.println(set.remove(123));  // false
        System.out.println(set.remove(132));  // true

        // 查看集合中是否包含此元素
        System.out.println(set.contains(132));  // false

        System.out.println(set.size());  // 5

        System.out.println(set.isEmpty());  // false

        System.out.println("-------------------");

        for (Integer i : set){
            System.out.print(i + "\t");  // 45	234	354	456	923
        }
        System.out.println();
        System.out.println("-------------------");

        Iterator<Integer> it = set.iterator();
        while (it.hasNext()){
            System.out.print(it.next() + "\t");  // 45	234	354	456	923
        }
    }
}

package com.atguigu.test5;

import java.util.TreeSet;

public class Student implements Comparable<Student>{
    private String name;
    private int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    public static void main(String[] args) {
        TreeSet<Student> set = new TreeSet<>();

        Student stu1 = new Student("赵四1", 55);
        Student stu2 = new Student("赵四2", 66);
        Student stu3 = new Student("赵四3", 77);

        set.add(stu1);
        set.add(stu2);
        set.add(stu3);

        for (Student stu: set){
            System.out.println(stu);
        }
    }


    @Override
    public int compareTo(Student o) {
        return this.getAge() > o.getAge() ? 1 : (this.getAge() < o.getAge() ? -1 : 0);
    }
}

4.3 LinkedHashSet

LinkedHashSet 有序的Set集合 顺序为添加顺序 底层由LinkedHashMap维护

package com.atguigu.test5;

import java.util.Iterator;
import java.util.LinkedHashSet;

/**
 * LinkedMashSet 有序的Set集合 顺序为添加顺序 底层由LinkedHashMap维护
 * */
public class TestLinkedHashSet {
    public static void main(String[] args) {

        LinkedHashSet<String> set = new LinkedHashSet<>();

        set.add("abc");
        set.add("hi");
        set.add("hello");
        set.add("999");
        set.add("xyz");

        // 根据传入的参数删除集合中对应的元素
        System.out.println(set.remove("abc"));  // true

        // 查看集合中是否包含此元素
        System.out.println(set.contains("abc"));  // false

        // 查看集合是否为空 为空返回true 否则返回false
        System.out.println(set.isEmpty());  // false

        Iterator<String> it = set.iterator();
        while (it.hasNext()){
            System.out.print(it.next() + "\t");  // hi	hello	999	xyz
        }
        System.out.println();
        System.out.println("----------------------");

        for (String str : set){
            System.out.print(str + "\t");  // hi	hello	999	xyz
        }
    }
}

5. 泛型

泛型:可以统一数据类型

泛型适用位置:

类、接口、方法、形参、返回值

泛型通常使用的字母:可以用任何字母表示

E Element 元素

K Key 键

V Value 值

T Type 类型

P Parameter 参数

R Return 返回值

package com.atguigu.test7;

import java.util.*;

/**
 * 泛型:可以统一数据类型
 *
 * 泛型使用位置:
 *  类、接口、方法、形参、返回值
 *
 *  泛型通常使用的字母:可以用任何字母表示
 *  E   Element     元素
 *  K   Key         键
 *  V   Value       值
 *  T   Type        类型
 *  P   Parameter   参数
 *  R   Return      返回值
 * */

public class Test1 {
    public static void main(String[] args) {
        // 传入Integer的泛型
        A<Integer> a1 = new A<>();
        a1.m1(100);  // 100
        System.out.println(a1.m2());  // null

        // 传入String类型的泛型
        A<String > a2 = new A<>();
        a2.m1("abc");  // abc
        System.out.println(a2.m2());  // null

    }
}

// 表示A类的泛型为type
class A<T>{
    // 传入指定类型的参数
    public void m1(T t){
        System.out.println(t);
    }

    // 返回指定type类型的返回值
    public T m2(){
        return null;
    }

    // 表示返回值类型和参数要保持一致
    public static <E>List<E> m3(E e){
        List<E> list = new ArrayList<>();
        list.add(e);
        return list;
    }
}

// B接口中指定使用Parameter参数 和 return返回的泛型
interface B<P, R>{
    R m1(P p);  // 接口中使用的都是抽象方法 ===> public abstract R m1(P p);  指定返回R类型的返回值,传入P类型的参数
}

// B1实现类指定使用String和Integer泛型
class B1 implements B<String, Integer>{

    @Override
    public Integer m1(String s) {
        return null;
    }
}


// B2实现类指定使用Double和Character泛型
class B2 implements B<Double, Character>{
    @Override
    public Character m1(Double aDouble) {
        return null;
    }
}

// B3实现类不使用泛型 默认形参类型和返回值类型为Object类型
class B3 implements B{
    @Override
    public Object m1(Object o) {
        return null;
    }
}


class Pet{}

class Dog extends Pet{}

class Cat extends Pet{}


class TestPet{
    // 要求m1方法传入List接口或实现类存放Dog类型的参数
    public static void m1(List<Dog> list){

    }

    // 要求m2方法传入List接口或实现类存放Dog类型或Dog父类的参数
    public static void m2(List<? super Dog> list){

    }

    // 要求m3方法传入Set接口或实现类存放Pet类型或Pet子类的参数
    public static void m3(Set<? extends Pet> set){

    }

    public static void main(String[] args) {
        m1(new ArrayList<Dog>());  // 传入ArrayList实现类和泛型为Dog的类型

        m2(new Vector<Pet>());  // 传入Vector实现类和泛型为Pet的类型
        m2(new LinkedList<Object>());  // 传入LinkedList和泛型为Object的类型

        m3(new HashSet<Pet>());  // 传入HashSet实现类和泛型为Pet的类型
        m3(new TreeSet<Dog>());  // 传入TreeSet实现类和泛型为Dog的类型
        m3(new LinkedHashSet<Cat>());  // 传入LinkedHashSet实现类和泛型为Cat的类型
    }


}

6. Collections工具类

Collections 集合工具类 提供有用于操作集合的常用方法

binarySearch 二分查找某个元素的位置

fill 填充集合元素

max 查找集合中最大元素

min 查找集合中最小元素

swap 交换指定位置元素

sort 集合排序

package com.atguigu.test8;

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

/**
 * Collections 集合工具类 提供有用于操作集合的常用方法'
 *      binarySearch 二分查找某个元素的位置
 *      fill 填充集合元素
 *      max 查找集合中最大元素
 *      min 查找集合中最小元素
 *      swap 交换指定位置元素
 *      sort 集合排序
 *      replaceAll 替换集合中的元素
 *      reverse 反转集合中的元素
 * */
public class TestCollections {
    public static void main(String[] args) {

        List<Integer> list = new ArrayList<>();

        list.add(200);
        list.add(243);
        list.add(435);
        list.add(132);
        list.add(2348);
        list.add(32);

        // sort() 从小到大排序
        Collections.sort(list);
        for (Integer i : list){
            System.out.print(i + "\t");  // 32	132	200	243	435	2348
        }
        System.out.println();

        // binarySearch() 查找元素所在的下标,如果没找到返回负数
        System.out.println("888所在的下标为:" + Collections.binarySearch(list, 888));  // 888所在的下标为:-6
        System.out.println("132所在的下标为:" + Collections.binarySearch(list, 132));  // 132所在的下标为:1

        System.out.println("list1中最大元素为:" + Collections.max(list));  // list1中最大元素为:2348
        System.out.println("list1中最小元素为:" + Collections.min(list));  // list1中最小元素为:32

        System.out.println("------------------------------");

        // swap() 交换两个位置的索引
        Collections.swap(list, 1, 2);
        for (Integer i : list){
            System.out.print(i + "\t");  // 32	200	132	243	435	2348
        }
        System.out.println();
        System.out.println("------------------------------");

        //  reverse() 反转列表中的元素
        Collections.reverse(list);
        for (Integer i : list){
            System.out.print(i + "\t");  // 2348	435	243	132	200	32
        }
        System.out.println();
        System.out.println("------------------------------");

        // replaceAll 将列表中一个指定值的所有出现替换为另一个。
        Collections.replaceAll(list, 200, 222);
        for (Integer i : list){
            System.out.print(i + "\t");  // 2348	435	243	132	222	32
        }
        System.out.println();
        System.out.println("------------------------------");

        // fill 用指定的元素代替指定列表的所有元素。
        Collections.fill(list, 300);
        for (Integer i : list){
            System.out.print(i + "\t");  // 300	300	300	300	300	300
        }

    }

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值