集合

集合就是java中存放数据的一个容器

数组:

  • 长度固定,不能再去添加元素

集合类

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

数组和集合的区别

  • 数组能存基本数据类型和引用类型
  • 集合当中只能存放引用数据类型,直接放基本数据,也会自动装箱(把基本数据类型转换成对象),集合当中只能存放对象
  • 数组的长度是固定的,不能再去增长,集合长度是可变的,根据元素的增长而增加

什么时候使用数组,什么时候使用集合类

  • 如果元素个数是固定的,推荐使用数组,否则推荐使用集合类

Collection

Collection接口是 (java.util.Collection)是Java集合类的顶级接口之一,整个集合框架就围绕一组标准接口而设计.

Collection下的集合常用的分为俩种,有序的List集合(接口),无序的set集合(接口)

list下有三种实现类Arraylist,LinkedList,Vector

set下有俩种 实现类hashSet和TreeSet

boolean add(E e)确保此collection包含指定的元素

boolean addAll(Collection<? extends E> c)将指定集合中的所有元素添加到此集合

void clear()从此集合中删除所有元素

boolean contains(Object o)如果此集合包含指定的元素返回true)

boolean containsAll(Object o)如果此集合包含指定的元素,则返回true

boolean rquals(Object o)将指定的对象与此集合进行比较已获得相等性

int hashCode()返回此集合的哈希码值

beeloean isEmpty()如果此集合不包含元素,则返回true

default Stream<E> parallelStream()返回可能的

boolean remove(Object o)从该集合中删除指定元素的单个实例

boolean removeAll(Collection<?>c)删除集合中包含有的所有此集合的元素

default boolean removeIf(Predicate<? super E>filter)删除满足给定谓词的此集合的所有元素

boolean retainAll(Collection<?> c)保留此集合中包含在指定集合中的元素

int size()返回集合中的元素数

default Spliterator<E> spliterator()创建一个Spliterator在这个集合中的元素

default Stream<E> stream()返回此集合作为源顺序Stream

Object[]toArray(T[]a)返回包含此集合中所有元素的数组

<T> T[] toarray(T[] a)返回包含此集合中所有元素的数组,返回数组的运行类型是制定数组的运行时类型

List

Collenction

下文用ArrayList来讲解Collenction的内容

list可以添加重复元素,set不能添加,list是一个有序的集合

arrayList添加元素

package com.company;


import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;

public class Main {

    public static void main(String[] args) {
        //多台写法运行时候表现出来是子类的特征
        //集合当中存的都是对象
        Collection c=new ArrayList();
        //开发当中list不会接受add方法的返回参数,是给set方法使用的
        // 因为set方法不能添加重复元素,如果添加了重复元素就会返回false
        //list会一直返回true
        boolean abc = c.add("abc");
        c.add(10);//自动装箱(把基本数据类型转换成对象)
        c.add(true);//编译成字节码的时候,会编译成 c.add(Boolean.valueOf(10))
        System.out.println(c);//ArrayList当中的爷爷类AbstractCollection中覆盖了tostring方法
    }

}

List删除元素

package com.company;

import java.util.ArrayList;
import java.util.Collection;
public class Main {

    public static void main(String[] args) {
        Collection c2=new ArrayList();
        c2.add("a");
        c2.add("b");
        c2.add("a");
        c2.add("a");
        c2.add("c");
        //可以从集合移除指定的元素
        c2.remove("b");//成功返回true,失败返回false
        System.out.println(c2);//[a, a, a, c]
        //清空集合当中所有元素
        c2.clear();
        System.out.println(c2);//[]
        //判断集合是否为空
        System.out.println(c2.isEmpty());//true
    }

}

集合的遍历

package com.company;


import java.util.ArrayList;
import java.util.Collection;

public class Main {

    public static void main(String[] args) {
        Collection c1=new ArrayList();
        c1.add("a");
        c1.add("b");
        c1.add("a");
        c1.add("a");

        Object[] objects = c1.toArray();
        for (int i=0;i<objects.length;i++){
            System.out.println(objects[i]);
        }

    }

}

遇到对象的时候,需要将集合toArray()方法会自动将对象升级为Object类型,用到对象中的方法时,需要强制类型转换降级才可以

集合的常用方法

package com.company;
import java.util.ArrayList;
import java.util.Collection;

public class Main {
    public static void main(String[] args) {
        Collection c1=new ArrayList();
        c1.add("a");
        c1.add("b");
        c1.add("a");
        c1.add("a");
        c1.add("e");

        Collection c2=new ArrayList();
        c2.add("a");
        c2.add("b");
        c2.add("c");
        c2.add("d");
        //把c2中的所有元素添加到c1当中
//        c1.addAll(c2);
//        System.out.println(c1);//[a, b, a, a, e, a, b, c, d]
        //去除c1中c2有的元素
//        c1.removeAll(c2);
//        System.out.println(c1);//[e] c1与c2的差集

//        boolean b = c1.containsAll(c2);//全判断c1是不是包含了c2的全部元素
//        System.out.println(b);//

        c1.retainAll(c2);//取交集,把交集结果赋值给调用者,如果改变了返回true,没有改变返回false
        System.out.println(c1);

    }

}

迭代器

在每一个实现类中都定义了自己的迭代方法

package com.company;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class Main {
    public static void main(String[] args) {
        Collection c=new ArrayList();
        c.add("a");
        c.add("b");
        c.add("a");
        c.add("a");
        //迭代器遍历
        //这里是实现Iterator接口的类,在ArrayList当中定义
        Iterator it=c.iterator();//放到Iterator内容中会自动类型提升
        //获取得迭代器当中的内容
//        Object obj = it.next();//把当前游标的内容取出来,当前游标向后走一位
//        System.out.println(obj);//a
//        obj = it.next();//把当前游标的内容取出来,当前游标向后走一位
//        System.out.println(obj);//b
//        boolean res = it.hasNext();//有下一个元素返回true,没有返回false
//        System.out.println(res);//true
        while(it.hasNext()){//底层有一个角标,如何角标和长度相等怎返回false
            //使用对象中的方法的时候同样需要向下转型
            System.out.println(it.next());//abaa
        }
    }
}

List独有的方法

package com.company;
import java.util.ArrayList;
import java.util.List;

public class Main {

    public static void main(String[] args) {
        //list有角标
        List list=new ArrayList();
        list.add("a");
        list.add("b");
        list.add("c");
        //根据角标来添加元素
        //角标一定要<=size,大于size就会报错,带角标的添加只能在`list当中才能使用
        list.add(1,"myxq");
        System.out.println(list);//[a, myxq, b, c]
        //获取指定角标的元素
        System.out.println(list.get(0));//a
        for(int i=0;i<list.size();i++){
            //根据角标遍历元素
            System.out.println(list.get(i));//a myxq b c
        }

    }

}

并发修改异常

在获取迭代器时,回合集合进行关联,保持李阿扁数据一致,内部会有一个modCount和ModCount,默认是相等的,modCount:集合记录修改次数,expectedModCount迭代器当中记录集合修改的次数,在我们取元素的时候,都会先做一个判断,如果不相等就会抛出一个并发修改异常

在迭代集合的时候不允许修改集合结构,会报错(增加和删除都不行)

java.util.ConcurrentModificationException

每一次调用next方法会调用比较modCount和expectedModCount,如果不相等则抛出错误

modCount:集合修改的次数

expectedModCount:迭代器当中集合修改的次数

迭代器当中的remove会自动把俩个修改的次数给统一

迭代器listIterator

在list中有自己的迭代器

Modifier and TypeMethod and Description
voidadd(E e) 将指定的元素插入列表(可选操作)。
booleanhasNext() 返回 true如果遍历正向列表,列表迭代器有多个元素。
booleanhasPrevious() 返回 true如果遍历反向列表,列表迭代器有多个元素。
Enext() 返回列表中的下一个元素,并且前进光标位置。
intnextIndex() 返回随后调用 next()返回的元素的索引。
Eprevious() 返回列表中的上一个元素,并向后移动光标位置。
intpreviousIndex() 返回由后续调用 previous()返回的元素的索引。
voidremove() 从列表中删除由 next()previous()返回的最后一个元素(可选操作)。
voidset(E e)指定的元素替换由 next()previous()返回的最后一个元素(可选操作)。

利用list的迭代器listIterator在指定元素后面追加固定的元素

倒着打印元素

package com.company;
import java.util.ArrayList;
import java.util.ListIterator;

public class Main {

    public static void main(String[] args) {
        ArrayList list=new ArrayList();
        list.add("1");
        list.add("2");
        list.add("3");
        //list当中特有 的迭代器
        ListIterator it = list.listIterator();
        while(it.hasNext()){
            String str=(String)it.next();
            if(str.equals("2")){
                //这里也做过同步
                //set方法不需要同步,因为没有修改元素
                it.add("23333");//[1, 2, 23333, 3]
            }
        }
        System.out.println(list);
        //因为使用的是同一个迭代器,所以现在角标在末尾
        while (it.hasPrevious()){
            System.out.println("游标为"+it.previousIndex());//游标逐渐减小
            //倒着打印整个集合,如果注释之后就会死循环
            //这是由于角标没有向前移动的关系
            System.out.println(it.previous());
        }
    }

}

ArrayList底层实现是数组

数组初始化就不会改变,当数组的长度不够的时候,把原来的数组赋值出来,再创建一个新数组容量增加50%,把复制的数组放到新数组当中,原来的数组扔掉

添加元素

把对应的值通过一个值覆盖后一个值的方式,依次往后移动,把要插入的值覆盖到指定的位置(相当于把其他值全部后移)

删除元素

把要删除的后位置的一个值,赋值到要删除的位置,后续依次进行复制覆盖,把最后一个值变为null

排序

排序可以直接通过equals重写,来让自定义返回值,contanins比较其实调用的是equals方法,如果没有覆盖不这个方法会直接去调用Object中的方法,Object中的equals默认比较的是地址

package com.company;

import java.util.ArrayList;
import java.util.ListIterator;

public class Main {

    public static void main(String[] args) {
        ArrayList list=new ArrayList();
        list.add("1");
        list.add("2");
        list.add("2");
        list.add("3");
        ArrayList singleEle = getSingleEle(list);
        System.out.println(singleEle);

    }
    //去重的函数
    public static ArrayList getSingleEle(ArrayList list){

        ArrayList newlist=new ArrayList();
        ListIterator it = list.listIterator();
        while(it.hasNext()){
            Object obj = it.next();
            if (!newlist.contains(obj)){
                newlist.add(obj);
            }

        }
        return newlist;

    }
}

LinkedList

底层是一个链表的结构

链表是一种物理存储单元上非连续、非顺序的存储结构数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。 相比于线性表顺序结构,操作复杂。由于不必须按顺序存储,链表在插入的时候可以达到O(1)的复杂度,比另一种线性表顺序表快得多,但是查找一个节点或者访问特定编号的节点则需要O(n)的时间,而线性表和顺序表相应的时间复杂度分别是O(logn)和O(1)。

优点:链表的插入和删除比较快,只需要切换前后的指向地址

缺点:查询和修改比较的快

LinkedList特有方法

package com.company;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.ListIterator;

public class Main {

    public static void main(String[] args) {
        LinkedList list = new LinkedList<>();
        list.add("c");
        list.add("c");
        ListIterator it=list.listIterator();
        //特有
        //往第一个位置添加元素
        list.addFirst("one");
        //在最后一个位置添元素
        list.addLast("last");
        //移除第一个元素
        list.removeFirst();
        //移除集合中最后一个元素
        list.removeLast();

        //也可以根据角标获取元素
        //内部实现是通过一个个遍历节点来实现的,所以linked查询是比较慢的
        System.out.println(list.get(0));//c

    }
}

Vector

jdk1.2之后为实现list接口,1.2之前才不是list集合里的

内部实现也是数组

vector没有ArrayList使用的平凡

vector会加锁,使用起来更加的安全

package com.company;

import java.util.*;

public class Main {

    public static void main(String[] args) {

        Vector vc=new Vector();
        vc.add("a");
        vc.add("b");

        //除了使用ListIterator之外还能使用自己本身的迭代器
        Enumeration e=vc.elements();
        while (e.hasMoreElements()){
            System.out.println(e.nextElement());
        }

    }
}

集合与数组互相转换

封装过之后,可以用面向对象的方式去操作数组(除了不能添加和删除元素之外,集合当中的其他东西都可以使用)

package com.company;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class Main {
    public static void main(String[] args) {

        int []arr={10,20,30};
        //把数组转换成集合
        //基本数据类型
        List list= Arrays.asList(arr);
//        list.add(10);//数组转集合不能添加元素
        Object o = list.get(0);//只能get第0位,也就是数组本身
        System.out.println(o);//输出的是地址,转成集合时,是把基本数据类型的数组,当做是一个对象
        System.out.println(list.size());//1
        System.out.println("--------------------------------------------");
        //引用数据类型的数组采取转成集合
        //引用数据类型
        Integer[]arr2={10,20,30};
        List<Integer> list2 = Arrays.asList(arr2);
        //可以使用get,不能添加和删除
        list2.get(1);
        System.out.println(list2);//[10, 20, 30]


        //集合转数组
        List<String>list3=new ArrayList<>();
        list3.add("a");
        list3.add("b");
        //集合数组
        Object[] objects=list3.toArray();
        //指定开辟空间大小,如果空间小于size会自动创建和size一样的空间大小
        //如果大于size,多余的空间为null
        Object[] strArr = list3.toArray(new String[0]);
        System.out.println(Arrays.toString(strArr));


    }
}

底层的实现

集合嵌套集合

package com.company;


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

class Person{
    String name;
    Person(String name){
        this.name=name;
    }
}
public class Main {
    public static void main(String[]args){
        Person per1=new Person("zs");
        Person per2=new Person("ls");
        ArrayList<Person> c1 = new ArrayList<>();
        c1.add(per1);
        c1.add(per2);

        Person per3=new Person("zs");
        Person per4=new Person("ls");
        ArrayList<Person> c2 = new ArrayList<>();
        c2.add(per3);
        c2.add(per4);

        //学科(集合当中存放集合)
        List<List<Person>> x=new ArrayList<>();
        x.add(c1);
        x.add(c2);
        //把所有班级当中的学生姓名打印出来
        for (List<Person>g:x){
            //取出每一个班级
            for (Person per:g){
                System.out.println(per.name);//循环嵌套输出嵌套的集合
                

            }
        }


    }
}

Set

set当中的元素是无序的,Collection中的方法在Set中都适用

package com.company;


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


public class Main {
    public static void main(String[]args){
      //set当中的元素是无序的
        //set当中已经覆盖了toString方法
        //set当中存在的元素都是无序的
        HashSet<String> hs = new HashSet<>();
        boolean res = hs.add("a");
        boolean res2=hs.add("a");
        hs.add("c");
        hs.add("1");
        System.out.println(res);//true
        System.out.println(res2);//false
        Iterator<String> iterator = hs.iterator();
        while(iterator.hasNext()){
            System.out.println(iterator.next());
        }
        //能够使用迭代器的都能使用foreach循环
        System.out.println("-------");
        for(String str:hs){
            System.out.println(str);//a 1 c
        }


    }
}

自定义对象去重

package com.company;


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

class Person{
    String name;
    int age;
    Person(String name,int age){
        this.name=name;
        this.age=age;
    }

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

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return age == person.age &&
                Objects.equals(name, person.name);
    }
    //在set集合当中要覆盖hashCode
    //7之后的新特性,要比较的任意个数的变量,就跨可以返回根据字段对象生成的hash值
    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
}

public class Main {
    public static void main(String[]args){
        //想要set当中自定义对象去重
        //覆盖equals方法
        //覆盖hashcode方法
        Set<Person> s=new HashSet<>();
        s.add(new Person("张三",20));
        s.add(new Person("张三",20));
        s.add(new Person("李四",21));
        s.add(new Person("李四",21));
        System.out.println(s);//
    }
}

HashSet

package com.company;


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

class Cat{
    String name;
    Cat(String name){
        this.name=name;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Cat)) return false;
        Cat cat = (Cat) o;
        return Objects.equals(name, cat.name);
    }

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

    @Override
    public String toString() {
        return "Cat{" +
                "name='" + name + '\'' +
                '}';
    }
}
public class Main {
    public static void main(String[]args){
        Cat c1=new Cat("mm");
        Cat c2=new Cat("mm");

        Set s=new HashSet();
        //添加对象时,会调用hashCode方法
        //hashCode不相同
        s.add(c1);
        s.add(c2);
        System.out.println(s);


    }
}

去重原理

每一个对象都会有一个hashcode,调用的时候会返回一个int值,hashcode,就是和内存地址对应的编号,每个对象的hash值都不一样,可以重写这个方法指定编号,在添加对象的时候,会调用对象的hashcode方法

比较的时候会先比较equals,如果c2兑现共和集合当中的hashCode不相同就会调用equals,当hashcode相同的时候回调用equals。

测试:生成1-20的随机数

package com.company;

import java.util.HashSet;
import java.util.Random;

public class Main {
    public static void main(String[]args){
        //在从1-20之间的随机数,获取10,不允许有重复

        //使用 Random来生成随机数
        Random r=new Random();
        //创建存放生成结果的集合,HashSet
        HashSet<Integer> hs = new HashSet<>();
        //当结果大于HashSet.size大于10的时候,就不放,否则一直生成往里面放
        while (hs.size()<10){
            //生成1.20之间的随机数
            int res=r.nextInt(20)+1;
            //添加到集合中
            hs.add(res);
        }
        System.out.println(hs);
    }
}

LinkHashSet

LinkHashSet是hashset的子类,底层是使用链表实现的,是Set集合当中唯一的一个保证元素是怎么存怎么取的,相比于hashset性能要低一点

package com.company;
import java.util.LinkedHashSet;
public class Main {
    public static void main(String[]args){
        LinkedHashSet<String>set=new LinkedHashSet<>();
        set.add("a");
        set.add("1");
        set.add("c");
        set.add("e");
        System.out.println(set);//[a, 1, c, e]
    }
}

生成随机数

package com.company;

import java.util.HashSet;
import java.util.Random;

public class Main {
    public static void main(String[]args){
        //在从1-20之间的随机数,获取10,不允许有重复

        //使用 Random来生成随机数
        Random r=new Random();
        //创建存放生成结果的集合,HashSet
        HashSet<Integer> hs = new HashSet<>();
        //当结果大于HashSet.size大于10的时候,就不放,否则一直生成往里面放
        while (hs.size()<10){
            //生成1.20之间的随机数
            int res=r.nextInt(20)+1;
            //添加到集合中
            hs.add(res);
        }
        System.out.println(hs);
    }
}

TreeSet

TreeSet它也不是按照添加的顺序展示的

TreeSet用来对元素进行排序,里面1也是可以保证元素的唯一

数字从小到打排序,字符串按照字母顺序依次比较,汉字是按照unicode码表来进行排序的

基本使用

package com.company;

import java.util.TreeSet;

public class Main {
    public static void main(String[]args){
        //TreeSet它也不是按照添加的顺序展示的
        //TreeSet用来对元素进行排序,里面1也是可以保证元素的唯一
        TreeSet<Integer> set=new TreeSet<Integer>();
        set.add(10);
        set.add(2);
        set.add(2);//添加不进去,会返回false
        set.add(9);
        set.add(6);
        System.out.println(set);


    }
}

TreeSer当中存放的必须得是同一类型,自定义的对象不能直接添加到TreeSet当中,想要添加到Three当中必须要实现Comparable覆盖compareTo方法,String,Integer中都实现了这个接口

package com.company;

import java.util.TreeSet;

class Person implements Comparable<Person> {
    String name;
    int age;
    Person(String name,int age){
        this.name=name;
        this.age=age;
    }

    @Override
    //如果返回0只添加第一个元素,
    //如果是一个正数,都能添加到集合当中,顺序是怎么添加的怎么显示
    //如果是一个负数,都能添加到集合当中,
    public int compareTo(Person o) {
        return 0;
    }

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

public class Main {
    public static void main(String[]args){
        TreeSet<Person>set=new TreeSet<>();
        set.add(new Person("张三",20));
        set.add(new Person("李四",21));
        System.out.println(set);//[Person{name='张三', age=20}]

    }
}

TreeSet自定义排序实现
treeset底层是红黑树

package com.company;

import java.util.TreeSet;

class Person implements Comparable<Person> {
    String name;
    int age;
    Person(String name,int age){
        this.name=name;
        this.age=age;
    }

    @Override
    //如果返回0只添加第一个元素,
    //如果是一个正数,都能添加到集合当中,顺序是怎么添加的怎么显示
    //如果是一个负数,都能添加到集合当中,
    public int compareTo(Person o) {//这里传进来的元素是当前比较的元素
        //如果比当前的小就会返回正数,并且添加到后方,大就会返回负数添加到前方
        //输出的时候会从根节点开始查找是否有左边的下级元素,一直找到地依次向上输出
        return this.age-o.age;
        /*
        //这样可以实现先对比年龄再对比姓名
        int num=this.age-obj.age;
        if(num==0){
            //String中已经覆盖了compare方法
            return this.name.compareTo(obj.name);
        }else{
            return num;
        }
         */
    }

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

public class Main {
    public static void main(String[]args){
        TreeSet<Person>set=new TreeSet<>();
        set.add(new Person("张三",20));
        set.add(new Person("李四",21));
        set.add(new Person("王五",22));
        set.add(new Person("赵六",21));
        //[Person{name='张三', age=20}, Person{name='李四', age=21}, Person{name='王五', age=22}]
        System.out.println(set);

    }
}

比较器

package com.company;

import java.util.Comparator;
import java.util.TreeSet;


public class Main {
    public static void main(String[] args) {
        //比较器
        //默认情况下,会调用对象的compareTo进行比较
//        如果传入了比较器,就会使用传入的比较器进行比较
        //不穿参数使用的默认是字符串中的CompareTo
        TreeSet<String>set=new TreeSet<>();
        set.add("aaaaaa");
        set.add("z");
        set.add("wc");
        set.add("myx");
        set.add("cba");
        System.out.println(set);//[aaaaaa, cba, myx, wc, z]
        //使用比较器进行比较
        //实现一个类来去实现这个接口
        //覆盖它里面的方法
        System.out.println("--------------------------");
        TreeSet<String>set2=new TreeSet<>(new CompareLength());//传入比较器的话,就不会调用字符串中的compare进行比较
        set2.add("aaaaaa");
        set2.add("z");
        set2.add("wc");
        set2.add("myx");
        set2.add("cba");
        System.out.println(set2);//
    }
}
class CompareLength implements Comparator<String>{

    @Override
    //1第一个参数代表当前正在添加的对象
    //2代表集合当中的对象
    public int compare(String o1, String o2) {
        int length = o1.length() - o2.length();
        return length==0?o1.compareTo(o2):length;
    }
}
Last modification:April 22, 2022
如果觉得我的文章对你有用,请随意赞赏