Java集合框架

集合

集合:就是用来 存放数据 的一个 容器

Java提供的集合类

  • 长度可以改变
  • 能存储任意的对象
  • 长度是随着你元素的个数增加而增加

🐤数组和集合的区别

  1. 数组能存 基本数据类型,和 引用数据类型
  2. 集合当中只能存放 引用数据类型,直接放基本数据类型,也会自动帮你装箱(把基本数据类型转成对象)集合当中只能存放 对象 也就是引用数据类型
  3. 数组的长度是固定的,定义好就不能再去增长,在程序跑起来的时候是不能修改的,还没有跑起来是可以修改的,相当于还在定义
  4. 集合长度是可以改变的,根据元素的个数增长而增加,在程序跑起来了,元素突然增加的集合的长度也会改变,而数组就不能
  • 什么时候使用数组,什么时候使用集合
  • 如果元素个数是 固定 的,推荐使用 数组
  • 如果元素个数 不是固定 的,推荐使用 集合

集合继承结构图

  • Collection 是一个接口,真正使用的是它的 实现类

集合通用方法

添加元素

  • 创建 Student.java
/**
 * @author BNTang
 **/
public class Student {
    public String name;
    public int age;
}
  • 测试类代码如下:
/**
 * @author BNTang
 **/
public class Demo {
    public static void main(String[] args) {
        Collection c = new ArrayList();

        boolean b1 = c.add("abc");

        // 会自动装箱(把基本数据类型转成对象)
        boolean b2 = c.add(10);
        boolean b3 = c.add(true);

        Student s = new Student();
        s.name = "BNTang";
        s.age = 23;
        boolean b4 = c.add(s);

        System.out.println(c);
    }
}

删除元素

  • 测试代码如下:
/**
 * @author BNTang
 **/
public class Demo {
    public static void main(String[] args) {
        Collection c2 = new ArrayList();
        c2.add("a");
        c2.add("b");
        c2.add("c");
        
        System.out.println(c2);
        
        // 可以从集合当中移除指定元素
        c2.remove("a");
        System.out.println(c2);
    }
}

判断一个集合是否为空

  • 测试代码如下:
/**
 * @author BNTang
 **/
public class Demo {
    public static void main(String[] args) {
        Collection c2 = new ArrayList();
        c2.add("a");
        c2.add("b");
        c2.add("c");

        // 判断一个集合是否为空,为空返回 true,不为空返回 false
        System.out.println(c2.isEmpty());
    }
}

获取集合当中的长度

  • 测试代码如下:
/**
 * @author BNTang
 **/
public class Demo {
    public static void main(String[] args) {
        Collection c2 = new ArrayList();
        c2.add("a");
        c2.add("b");
        c2.add("c");

        // 获取集合当中的长度(也就是有几个元素)
        System.out.println(c2.size());
    }
}

清空集合当中所有的内容

  • 测试代码如下:
/**
 * @author BNTang
 **/
public class Demo {
    public static void main(String[] args) {
        Collection c2 = new ArrayList();
        c2.add("a");
        c2.add("b");
        c2.add("c");

        c2.clear();
        System.out.println(c2);
        System.out.println(c2.size());
    }
}

把c2当中的所有元素合并到c1当中

  • 测试代码如下:
/**
 * @author BNTang
 **/
public class Demo {
    public static void main(String[] args) {
        Collection c1 = new ArrayList();
        c1.add("a");
        c1.add("b");
        c1.add("c");
        c1.add("d");

        Collection c2 = new ArrayList();
        c2.add("a");
        c2.add("b");
        c2.add("c");
        c2.add("d");
        c2.add("e");

        System.out.println(c1);
        c1.addAll(c2);
        System.out.println(c1);
    }
}

删除c1两个集合中的交集元素

  • 例如 c1 调用删除的方式 c2 作为参数传入, c2 当中的元素在 c1 当中存在了,就会删除该元素
  • 测试代码如下,结果自行运行:
/**
 * @author BNTang
 **/
public class Demo {
    public static void main(String[] args) {
        Collection c1 = new ArrayList();
        c1.add("a");
        c1.add("b");
        c1.add("c");
        c1.add("d");

        Collection c2 = new ArrayList();
        c2.add("a");
        c2.add("b");
        c2.add("e");

        c1.removeAll(c2);
        System.out.println(c1);
    }
}

判断调用的集合是否包含(全部包含)传入集合

  • 意思是判断传入的集合当中所有的值是否和当前集合中的内容匹配
  • 注意是传入的集当中的值全部进行判断,其中一个在当前集合中不包含就为 false,全部存在就为 true
  • 代码如下:
/**
 * @author BNTang
 **/
public class Demo {
    public static void main(String[] args) {
        Collection c1 = new ArrayList();
        c1.add("a");
        c1.add("b");
        c1.add("c");
        c1.add("d");

        Collection c2 = new ArrayList();
        c2.add("a");
        c2.add("b");
        c2.add("e");

        boolean res = c1.containsAll(c2);
        System.out.println(res);
    }
}

取交集把交集的结果赋值给调用者

  • 代码如下:
/**
 * @author BNTang
 **/
public class Demo {
    public static void main(String[] args) {
        Collection c1 = new ArrayList();
        c1.add("a");
        c1.add("b");
        c1.add("c");
        c1.add("d");

        Collection c2 = new ArrayList();
        c2.add("a");
        c2.add("b");

        boolean b = c1.retainAll(c2);
        System.out.println(c1);
        System.out.println(b);
    }
}

集合的遍历

集合遍历

  • 代码如下:
/**
 * @author BNTang
 **/
public class Demo {
    public static void main(String[] args) {
        Collection c1 = new ArrayList();
        c1.add("a");
        c1.add("b");
        c1.add("c");
        c1.add("d");

        // 会出现类型转换异常
        // c1.add(10);

        Object[] arr = c1.toArray();
        for (Object o : arr) {
            System.out.println(o);
        }
    }
}

迭代器遍历

  • 代码如下:
/**
 * @author BNTang
 **/
public class Demo {
    public static void main(String[] args) {
        Collection arrayList = new ArrayList();
        arrayList.add("a");
        arrayList.add(1);
        arrayList.add("c");

        Iterator iterator = arrayList.iterator();
        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }
    }
}

迭代器源码

  • 有一个 iterator 方法返回了一个 Iterator 类型的结果,内部又定义了一个内部类实现了 Iterator
  • 由于不同的集合数据结构都不一样所以都有自己不一样的实现
  • 首先定义 3 个变量分别记录不同的结果,当前角标,和最后一个元素两个
  • 每次调用 next 方法都会把当前角标赋值一份给 i,如果 i 大于了当前集合的个数,则会抛出异常,没有元素异常
  • 不大于则会取出当前集合元素用一个变量接收,在判断一下当前 i,是否大于当前变量接收的角标如果大于的话则会抛出一个 并发修改异常,如果不大于,等于则会进行取元素,会把当前 i 赋值给 lastRet 变量
public Iterator<E> iterator() {
    return new Itr();
}

/**
 * An optimized version of AbstractList.Itr
 */
private class Itr implements Iterator<E> {
    int cursor;       // index of next element to return
    int lastRet = -1; // index of last element returned; -1 if no such
    int expectedModCount = modCount;

    // prevent creating a synthetic constructor
    Itr() {}

    public boolean hasNext() {
        return cursor != size;
    }

    @SuppressWarnings("unchecked")
    public E next() {
        checkForComodification();
        int i = cursor;
        if (i >= size)
            throw new NoSuchElementException();
        Object[] elementData = ArrayList.this.elementData;
        if (i >= elementData.length)
            throw new ConcurrentModificationException();
        cursor = i + 1;
        return (E) elementData[lastRet = i];
    }

    public void remove() {
        if (lastRet < 0)
            throw new IllegalStateException();
        checkForComodification();

        try {
            ArrayList.this.remove(lastRet);
            cursor = lastRet;
            lastRet = -1;
            expectedModCount = modCount;
        } catch (IndexOutOfBoundsException ex) {
            throw new ConcurrentModificationException();
        }
    }
}
  • 并发修改异常,这个问题是调用了集合自身的 remove 方法,导致了底层在判断的时候数据不一致抛出的,自己看一下集合 remove 源码就知道为啥会出现这个问题了
public boolean remove(Object o) {
    final Object[] es = elementData;
    final int size = this.size;
    int i = 0;
    found: {
        if (o == null) {
            for (; i < size; i++)
                if (es[i] == null)
                    break found;
        } else {
            for (; i < size; i++)
                if (o.equals(es[i]))
                    break found;
        }
        return false;
    }
    fastRemove(es, i);
    return true;
}

private void fastRemove(Object[] es, int i) {
    modCount++;
    final int newSize;
    if ((newSize = size - 1) > i)
        System.arraycopy(es, i + 1, es, i, newSize - i);
    es[size = newSize] = null;
}
  • 所以在移除元素的时候尽可能的调用迭代器的 remove 方法,迭代器的 remove 方法,查看源码就知道为什么了
public void remove() {
    if (lastRet < 0)
        throw new IllegalStateException();
    checkForComodification();

    try {
        ArrayList.this.remove(lastRet);
        cursor = lastRet;
        lastRet = -1;
        expectedModCount = modCount;
    } catch (IndexOutOfBoundsException ex) {
        throw new ConcurrentModificationException();
    }
}

List集合

ArrayList

添加元素

  • 代码如下:
/**
 * @author BNTang
 **/
public class Demo {
    public static void main(String[] args) throws Exception {
        List<String> person = new ArrayList<>();
        person.add("jackie");   //索引为0
        person.add("peter");    //索引为1
        person.add("annie");    //索引为2
        person.add("martin");   //索引为3
        person.add("BNTang");   //索引为4

        System.out.println(person);
    }
}

获取元素

  • 代码如下:
/**
 * @author BNTang
 **/
public class Demo {
    public static void main(String[] args) throws Exception {
        List<String> person = new ArrayList<>();
        person.add("jackie");   //索引为0
        person.add("peter");    //索引为1
        person.add("annie");    //索引为2
        person.add("martin");   //索引为3
        person.add("BNTang");   //索引为4

        System.out.println(person.get(4));
    }
}

删除元素

  • 代码如下:
/**
 * @author BNTang
 **/
public class Demo {
    public static void main(String[] args) throws Exception {
        List<String> person = new ArrayList<>();
        person.add("jackie");   //索引为0
        person.add("peter");    //索引为1
        person.add("annie");    //索引为2
        person.add("martin");   //索引为3
        person.add("BNTang");   //索引为4

        person.remove(0);
        System.out.println(person);
    }
}

遍历集合元素

  • 上面我写了一种方式
  • 这种是传统的 for 循环的方式根据长度遍历
/**
 * @author BNTang
 **/
public class Demo {
    public static void main(String[] args) throws Exception {
        List<String> person = new ArrayList<>();
        person.add("jackie");   //索引为0
        person.add("peter");    //索引为1
        person.add("annie");    //索引为2
        person.add("martin");   //索引为3
        person.add("BNTang");    //索引为4

        for (int i = 0; i < person.size(); i++) {
            System.out.println(person.get(i));
        }
    }
}

迭代器遍历

  • 代码如下:
/**
 * @author BNTang
 **/
public class Demo {
    public static void main(String[] args) throws Exception {
        List list = new ArrayList();
        list.add("jackie");   //索引为0
        list.add("peter");    //索引为1
        list.add("annie");    //索引为2
        list.add("martin");   //索引为3
        list.add("BNTang");   //索引为4

        Iterator it = list.iterator();
        while (it.hasNext()) {
            System.out.println(it.next());
        }
    }
}

🐤并发修改异常

🐸并发修改异常原因

🦄解决并发修改异常

ListIterator迭代器

ArrayList数据结构

去除List集合中重复的元素

如果比较的是自定义对象需要自己覆盖 equals 方法,他默认比较的是 内存地址,而我们想要达到对象里面的属性值比较的话需要自己覆盖 equals 当然可以使用编辑器生成例如:intellij IDEA

LinkedList

遍历元素

特有方法

LinkedList数据结构

Vector

泛型

  • List 中添加元素存在的问题
  • 往集合当中存储元素,可以存任何类型元素
  • 使用某个类型当中特有的方法,必须得要转回来才可以进行使用
/**
 * @author BNTang
 **/
public class Demo {
    public static void main(String[] args) throws Exception {
        List list = new ArrayList();
        list.add(1);
        list.add("abc");

        Object obj = list.get(1);

        String str = (String) obj;
        str.substring(0, 1);
    }
}
  • 没有泛型存在的问题

  • 什么是泛型:广泛通用的类型
  • 一开始还不确定是什么类型,在使用的时候,才能确定是什么类型
  • 在开始定义类的时候,留一个插口
  • 在创建对象的时候,再去插入对应的类型

🐤泛型注意点

  1. 泛型前后类型必须得要保持一致
  2. 从 Java7 开始,后面的类型可以不写:new ArrayList<>(); 菱形语法
  3. 泛型是没有继承的
  4. 泛型其实是一个 语法糖(本质还是 Object,内部其实还是要做强转)
  • 没有使用泛型的 List

泛型类

  • 在类上面定义的泛型,在创建对象的时候,要指明泛型的类型
  • 泛型类当中定义的泛型只能用在普通方法上面
  • 不能使用在静态方法上面
  • 静态方法是直接使用类名调用,泛型是在创建对象的时候,才去指定类型,所以就不能使用了

泛型方法

  • 就是在方法上面添加了泛型
  • 单独对一个方法上面声明泛型,方法当中定义的泛型

自定义泛型方法

通配符

  • 不知道使用什么类型来接收的时候,可以使用 ? 来表示未知
  • 通配符只是用来做接收使用
  • 不能做添加操作

泛型上限

  • 泛型的上限:用来限定元素的类型必须得是指定类型(Number)的子类<或者是指定类型或者是指定类的子类>

泛型下限

  • 用来限定元素的类型必须得是指定类型(Number)的父类(或者是指定的类)

泛型擦除

  • 把一个有泛型集合赋值给了一个没有泛型的集合最后就没有泛型了 → 也就是泛型擦除(把泛型给去掉了)

数组与集合的转换

把数组转成集合

引用数据类型的数组转集合

集合转数组

  • list3 是上图中的集合连着的,阅读的时候需要注意一下

Set

  • Set 当中存的元素是 无序 的,里面没有 重复 的元素

HashSet

  • HashSet 自定义对象的比较方法,需要重写 hashcodeequals,他底层其实是用 hashCode 来比较的

  • 获取1到20之间随机数
  • 自己可以抽出一个方法来
  • 传的数字是几就生成几个不重复的随机数

LinkedHashSet

  • LinkedHashSet 它是 HashSet 的子类
  • 它底层是使用 链表 实现,是 Set 集合当中唯一的一个保证元素是怎么存怎么取的
  • HashSet 能够保证元素的唯一

TreeSet

Map

  • 映射关系
  • A集合、B集合可能是(ArrayList LinkedList Vector HashSet LinkedHashSet TreeSet)这其中的一种集合类型
  • A集合当中的每一元素,都可以在B集合当中找到一个唯一的一个值与之对应
  • A集合当中的元素不能是重复的(Set)
  • A集合当中的每一个元素称它是一个 key(键)
  • B集合当中的每一个元素称它是一个 value(值)

添加功能

删除功能

获取长度

遍历Map

/**
 * @author BNTang
 **/
public class Demo {
    public static void main(String[] args) throws Exception {
        Map<String, Integer> map = new HashMap<>();
        map.put("张三", 20);
        map.put("李四", 21);
        map.put("王五", 22);

        Set<String> allKeys = map.keySet();

        Iterator<String> it = allKeys.iterator();
        while (it.hasNext()) {
            String key = it.next();
            Object val = map.get(key);
            System.out.println(key + "=" + val);
        }

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

        for (String key : map.keySet()) {
            System.out.println(key + "=" + map.get(key));
        }
    }
}

/**
 * @author BNTang
 **/
public class Demo {
    public static void main(String[] args) throws Exception {
        Map<String, Integer> map = new HashMap<>();
        map.put("张三", 20);
        map.put("李四", 21);
        map.put("王五", 22);

        Set<Map.Entry<String, Integer>> entrySet = map.entrySet();

        Iterator<Map.Entry<String, Integer>> it = entrySet.iterator();
        while (it.hasNext()) {
            Map.Entry<String, Integer> en = it.next();
            String key = en.getKey();
            Integer value = en.getValue();
            System.out.println(key + " = " + value);
        }

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

        for (Map.Entry<String, Integer> entry : entrySet) {
            System.out.println(entry.getKey() + "=" + entry.getValue());
        }
    }
}

TreeMap

  • 如果是自定义对象,想要按照自己的方式进行排序,你就可以参考我上面介绍的 TreeSet 的方式进行实现一个接口,然后覆盖里面的 compareTo 方法

HashTable

LinkedHashMap

  • 使用 HashMap 它的 key 是没有顺序的
  • LinkedHashMap 添加的元素是有顺序(你怎么放的,打印时就是什么顺序的)

/**
 * @author BNTang
 **/
public class Demo {
    public static void main(String[] args) throws Exception {
        Map<String, Integer> map = new HashMap<>();
        map.put("张三", 20);
        map.put("李四", 21);
        map.put("王五", 22);
        System.out.println(map);

        HashMap<String, Integer> hm = new HashMap<>();
        hm.put("张三", 20);
        hm.put("李四", 20);
        hm.put("王五", 20);
        System.out.println(hm);

        LinkedHashMap<String, Integer> hm2 = new LinkedHashMap<>();
        hm2.put("张三", 20);
        hm2.put("李四", 20);
        hm2.put("王五", 20);
        System.out.println(hm2);
    }
}

异常

  • Java 程序在运行过程当中出现的 错误

异常继承体系

  • Throwable

  • 🐸Throwable继承体系

异常处理方式

  1. 自己处理,然后继续运行
  2. 自己没有办法处理,交给 JVM 来处理

JVM 有一个默认的异常处理机制处理异常,并将该异常的名称,异常信息,异常出现的位置给打印在控制台上,同时停止程序的运行

10/0;
违背了算法运算法则,抛出异常
抛出的异常是一个对象
New ArithmeticException(“/by zero”),把这个异常对象返回  
此时想把异常对象赋值给 a
a 是一个 int 类型不能接收,main方法解决不了
交给 JVM 来处理,JVM 就将该异常在控制台打印程序终止

自己处理异常

  • 基本格式
  • try...catch...finally
try{
    可能出现异常的代码 
}catch(异常类型){

}finally{
   处理完异常最后做的处理
}
  • try:用来检测可能出现异常的代码
  • catch:用来捕获出现的异常
  • finally:一般用来释放资源

10/0;会创建一个异常对象
New ArithmeticException(“/by zero”)
赋值给a,接收不了,此时就把异常对象传给catch当中的参数a
能够接收,就会执行catch当中的内容,程序也没有终止
  • 只有在 try 中的代码出了问题的时候才会执行 catch 当中的内容
  • 出问题时 catch 处理完毕之后,后续的代码继续执行
  • try…catch 可以同时处理多个异常
  • try 当中可能会出现不同的异常类型此时可以使用多个 catch 来进行处理

  • Exception e 不能写在最上面,写在上面会报错,因为下面的永远不会执行到
  • 因为 Exception 是所有异常的父类,所以就不会再一一往下进行判断了

  • 🐤JDK7处理多个异常

常用方法

  • 获取异常信息

  • 获取异常的类名和异常信息

  • 获取异常类名和异常信息,及异常出现的位置

throws方式处理异常

  • 🐤抛出运行时异常

  • 抛出运行时异常,不需要处理这个抛出的异常
  • 🐸抛出编译时异常

  • 抛出了一个编译时异常,必须得要有人处理如果不处理,必须继续往上抛,抛给方法调用者

  • 此时由于 setAge 内部抛出了一个异常,在调用该方法时必须得要处理抛出的异常
  • 处理异常

  • 可以选择继续往上抛

throw与throws的区别

  • throw
    • 用在方法体内,跟的是异常对象名称,也就是异常对象实例
    • 只能抛出一个异常对象
    • 表示抛出异常
  • throws
    • 用在方法声明后面,跟的是异常类名
    • 可以跟多个异常类名,中间使用逗号隔开
    • 表示抛出异常,由方法调用者来处理

编译时异常与运行时异常

  • 运行时异常:所有的 RuntimeException 类及其子类称为运行时异常,在编译时不会报错,在运行过程中程序会被终止运行
  • 编译时异常:程序员必须显示的处理了这个异常,否则程序就会发生错误无法通过编译,就跑不起来
  • 编译时异常发生的情况

在编译时就必须处理这个异常
这个代码是读取一个文件,在读取时,它也不确定你这个文件是否存在
如果不存在,你要怎么处理,所以在编译时就要确定好
如果文件不存在该怎么样处理
posted @ 2020-08-25 22:13  BNTang  阅读(202)  评论(0编辑  收藏  举报