集合概念

定义:集合是对象的容器,定义了对多个对象进行操作的常用方法,可实现数组的功能。

对比数组的区别

  • 数组的长度是固定的,集合的长度不固定
  • 数组可以存放基本类型和引用类型,集合只能存储引用类型

位置:Java.until.*

Collection

Colletion

List(有序的,有下标,元素可重复)

  • ArrayList
  • LinkedList
  • Vector

Set(无序的,无下标,元素不能重复)

  • HashSet
  • SortedSet(Interface)
  • TreeSet(在SortedSet下)

Collection接口

特点:代表一组任意类型的对象,无序,无下标,不能重复。

方法

Collection接口应用

为什么不用普通的for而用增强型for

for遍历是需要下标的,collection又没有下标,只能使用增强for

/**
 * Collection接口的使用
 * 1、添加元素
 * 2、删除元素
 * 3、遍历元素
 * 4、判断
 */
public class Demo01 {
    public static void main(String[] args) {

        //创建集合
        Collection collection = new ArrayList();

        //1、添加元素
        collection.add("苹果");
        collection.add("香蕉");
        collection.add("葡萄");
        System.out.println("元素的个数" + collection.size());
        System.out.println(collection);
        System.out.println("===========================");

        //2、删除元素
//        collection.remove("苹果");
//        collection.clear();
        System.out.println("元素的个数" + collection.size());
        System.out.println("===========================");

        //3、遍历元素
        //3.1增强型for循环
        for (Object object:collection) {
            System.out.println(object);
        }
        System.out.println("===========================");
        //3.2使用迭代器(是专门用来迭代集合的一种方法)
        //迭代器下面有hasNext()、next()、remove()这三种方法
        //hasNext()是否有下一个元素
        //next()获取元素
        //remove()删除一个元素
        Iterator iterator = collection.iterator();
        while (iterator.hasNext()){
           // 强制转换类型
            String ob = (String) iterator.next();
            System.out.print(ob);
            //不能使用collection的其他方法用来改变集合
            //否则会出现并发修改异常Exception in thread "main" java.util.ConcurrentModificationException
//            collection.remove("苹果");
            iterator.remove();
        }
        System.out.println("元素的个数" + collection.size());
        System.out.println("===========================");

        //4、判断
        System.out.println(collection.contains("西瓜"));
        System.out.println(collection.isEmpty());
    }
}
/**
 * Collection:保存学生信息
 */
public class Demo02 {

    public static void main(String[] args) {
        Collection collection = new ArrayList();
        Student s1 =  new Student("Lee",11);
        Student s2 =  new Student("zhang",12);
        Student s3 =  new Student("wang",13);

        //1、增加元素
        collection.add(s1);
        collection.add(s2);
        collection.add(s3);
        System.out.println("元素的个数"+collection.size());
        System.out.println(collection.toString());

        //2、删除元素
//        collection.remove(s1);
        ==//思考?如何使用new的方式删除==
        使用new Interger()
        //删除只是删除地址,数据依然存在
//        collection.clear();
//        collection.remove(new Student("Lee",11));
        System.out.println("元素的个数"+collection.size());

        //3、遍历
        //3.1 增强for
        for (Object ob:collection) {
            Student s =(Student) ob;
            System.out.println(s.toString());
            
        }
        System.out.println("===========================");
        //3.2 迭代器
        Iterator it = collection.iterator();
        while (it.hasNext()){
            Student s = (Student)it.next();
            System.out.println(s.toString());
        }

        //4、判断
        System.out.println(collection.contains(s1));
        //思考?new对象如何才能为true
        System.out.println(collection.contains(new Student("Lee",11)));
        System.out.println(collection.isEmpty());
    }

}

List接口

特点:有序、有小标、元素可以重复

方法:API文档可查

/**
 * Collection:保存学生信息
 */
public class Demo02 {

    public static void main(String[] args) {
        Collection collection = new ArrayList();
        Student s1 =  new Student("Lee",11);
        Student s2 =  new Student("zhang",12);
        Student s3 =  new Student("wang",13);

        //1、增加元素
        collection.add(s1);
        collection.add(s2);
        collection.add(s3);
        System.out.println("元素的个数"+collection.size());
        System.out.println(collection.toString());

        //2、删除元素
//        collection.remove(s1);
        ==//思考?如何使用new的方式删除==
        使用new Interger()
        //删除只是删除地址,数据依然存在
//        collection.clear();
//        collection.remove(new Student("Lee",11));
        System.out.println("元素的个数"+collection.size());

        //3、遍历
        //3.1 增强for
        for (Object ob:collection) {
            Student s =(Student) ob;
            System.out.println(s.toString());
            
        }
        System.out.println("===========================");
        //3.2 迭代器
        Iterator it = collection.iterator();
        while (it.hasNext()){
            Student s = (Student)it.next();
            System.out.println(s.toString());
        }

        //4、判断
        System.out.println(collection.contains(s1));
        //思考?new对象如何才能为true
        System.out.println(collection.contains(new Student("Lee",11)));
        System.out.println(collection.isEmpty());
    }

}
public class Demo02 {
    public static void main(String[] args) {
        //创建集合对象
        List list = new ArrayList();
        //增加元素
        list.add(10);
        list.add(20);
        list.add(30);
        list.add(40);
        list.add(50);
        System.out.println("元素的个数:"+list.size());
        System.out.println(list.toString());
        System.out.println("=========================");

        //删除元素
//        list.remove(0);
        //能够通过new 删除,是由于整数缓冲的原因
//        list.remove(new Integer(20));
        System.out.println("元素的个数:"+list.size());
        System.out.println(list.toString());
        System.out.println("=========================");

        //补充方法subList,是含头不含尾
        List list1 = list.subList(0,3);
        System.out.println("元素的个数:"+list1.size());
        System.out.println(list1.toString());
        System.out.println("=========================");
    }
}

List实现类

ArrayList【重点】

数组结构实现,查询快,增删慢

JDK1.2版本出现,运行效率高,但是线程不安全

Vector

数组结构实现,查询快,增删慢

JDK1.0版本出现,运行效率低,但是线程安全

LinkedList:

链表结构实现,增删快,查询慢

ArrayList使用

/**
 * ArrayList的使用
 * 存储结构:数组,查找速度快,增删慢
 *
 */
public class Demo05 {
    public static void main(String[] args) {
        //创建集合
        ArrayList arrayList = new ArrayList<>();


        //1、添加元素
        Student s1 = new Student("A",97);
        Student s2 = new Student("B",98);
        Student s3 = new Student("C",99);
        arrayList.add(s1);
        arrayList.add(s2);
        arrayList.add(s3);
        //打印元素的长度和内容
        System.out.println("元素的长度:"+arrayList.size());
        System.out.println(arrayList.toString());

        //2、删除元素
//        arrayList.remove("20");
        //在重构完equals后,不用写对象,就可以删除
//        arrayList.remove(new Student("A",97));
//        System.out.println("元素的长度:"+arrayList.size());

        //3、遍历元素【重点】
        //3-1 使用迭代器
        System.out.println("---使用迭代器---");
        Iterator it = arrayList.iterator();
        while (it.hasNext()){
            Student s = (Student)it.next();
            System.out.println(s.toString());
        }
        //3-2 列表迭代器
        ListIterator lit = arrayList.listIterator();
        System.out.println("--使用列表迭代器--");
        while (lit.hasNext()){
            Student s = (Student) lit.next();
            System.out.println(s.toString());
        }

        System.out.println("--使用列表迭代器逆序--");
        while (lit.hasPrevious()){
            Student s = (Student) lit.previous();
            System.out.println(s.toString());
        }

        //4、判断
        System.out.println(arrayList.contains(s1));
        System.out.println(arrayList.contains(new Student("A",97)));
        System.out.println(arrayList.isEmpty());

        //5、查找
        System.out.println("---查找---");
        System.out.println(arrayList.indexOf(s1));
        System.out.println(arrayList.indexOf(new Student("A",97)));
    }
}
public class Student {
    private String name;
    private int age;

    public Student(String name, int age) {
        this.name = name;
        this.age = 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;
    }

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

    //重构equals方法重构equals方法重构equals方法重构equals方法重构equals方法重构equals方法
    @Override
    public boolean equals(Object obj) {
        //1、判断是不是同一个对象
        if (this==obj){
            return true;
        }
        //2、判断是否为空
        if (obj==null){
            return false;
        }
        //3、判断是不是Student类型
        if (obj instanceof Student){
            Student s = (Student) obj;
            if (this.name.equals(s.getName())&&this.age==s.getAge()){
                return true;
            }
        }
        //不满足条件返回false
        return false;
    }
}

ArrayList源码分析

ArrayList

  • 底层数据结构是数组,查询快,增删慢。
  • 线程不安全,效率高。
DEFAULT_CAPACITY = 10; 
//默认容量 
//注意:如果没有向集合中添加任何元素时,容量0,添加一个后,容量为10 
//每次扩容是原来的1.5倍 
elementData存放元素的数组 
size 实际元素个数

源码展示

//空数组
private static final int DEFAULT_CAPACITY = 10;
//空数组
private static final Object[] EMPTY_ELEMENTDATA = {};
//用来存放元素的数组
transient Object[] elementData; // non-private to simplify nested class access
//实际元素个数
private int size;

默认容量是10,指的是添加元素后,未添加则为0

重点add方法

public boolean add(E e) {
    //增长修改个数
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    elementData[size++] = e;
    return true;
}

//不知道为什么跟教程的源码不一样啊,但是也没差
    private static int calculateCapacity(Object[] elementData, int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }

    private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }

    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

    private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

两个知识点拓展

Arrays.copyOf(T[] original,int newLength ):拷贝数组,其内部调用了System.arrayCopy()方法,从下标0开始,如果超过原数组长度,会用null进行填充。

Arrays.copyOf()使用样例

Integer[] arr={1,2,3,4,5,6};
        for (int i = 0; i < arr.length; i++) {
            System.out.println(arr[i]);
        }
        //拷贝数组arr1到长度为10的新数组中,
        // 如果新数组长度大于arr1的长度,超过的部分用null填充
        //如果新数组长度小于arr1的长度,只拷贝前几个数据
        Integer[] arr2 = Arrays.copyOf(arr, 3);//arr2是{1,2,3}
        Integer[] arr3 = Arrays.copyOf(arr, 10);//arr3是{1,2,3,4,5,6,null,null,null,null}
        for (int i = 0; i < arr2.length; i++) {
            System.out.println(arr2[i]);
        }
         for (int i = 0; i < arr3.length; i++) {
            System.out.println(arr3[i]);
        }

Vector的使用

/**
 * Vector的使用
 */
public class Demo06 {
    public static void main(String[] args) {
        Vector vector = new Vector<>();

        // 增
        Student s1 = new Student("A",97);
        Student s2 = new Student("B",98);
        Student s3 = new Student("C",99);
        vector.add(s1);
        vector.add(s2);
        vector.add(s3);

        // 删
//        vector.remove(s1);
//        vector.remove(new Student("A",97));
//        vector.clear();

        // 遍历-枚举遍历
        Enumeration en = vector.elements();
        while (en.hasMoreElements()){
            String o = (String) en.nextElement();
            System.out.println(o);
        }


        // 判断
        vector.contains(s1);
        vector.contains(vector.isEmpty());

        //还有很多其他方法,可以在JDK文档中查询   
    }

LinkedList的使用

特点:链表结构、增删快、查询慢

/**
 *LinkedList的使用
 */
public class Demp07 {
    public static void main(String[] args) {
        LinkedList linkedList = new LinkedList();
        Student s1 = new Student("A",97);
        Student s2 = new Student("B",98);
        Student s3 = new Student("C",99);

        //增
        linkedList.add(s1);
        linkedList.add(s2);
        linkedList.add(s3);
        System.out.println("长度:"+linkedList.size());
        System.out.println(linkedList.toString());

        //删
//        linkedList.remove(s1);
//        linkedList.remove(new Student("C",99));
//        linkedList.clear();

        //遍历
        for (int i = 0; i < linkedList.size(); i++) {
            System.out.println(linkedList.get(i));
        }

        for (Object ob:linkedList) {
            Student s = (Student)ob;
            System.out.println(s.toString());
        }

        Iterator iterator = linkedList.listIterator();
        while (iterator.hasNext()){
            Student student = (Student) iterator.next();
            System.out.println(student.toString());
        }

        ListIterator listIterator = linkedList.listIterator();
        while (listIterator.hasNext()){
            Student student2 = (Student) listIterator.next();
            System.out.println(student2.toString());
        }

        //判断
        System.out.println(linkedList.contains(s1));
        System.out.println(linkedList.isEmpty());
        System.out.println(linkedList.indexOf(s1));

    }
}

LinkedList的源码分析

偷个懒,贴个链接,写的比我理解的好多了

LinkedList和ArrayList的区别

ArrayList

  • 数组结构
  • 查询快
  • 增删慢

LinkedList

  • 链表结构
  • 查询慢
  • 增删快

思考为什么ArrayList的查询LinkedList比更快?

因为ArrayList开辟空间是连续的、而LinkedList不需要

泛型概述

Java是在JDK1.5中引入的一个新特性,其本质是参数化类型,把类型作为参数传递

常见的有泛型类、泛型接口、泛型方法

语法

<T,....>

T:tape 类型

E:element元素

K:key键值

V:value值

叫做类型占位符,表示一种引用类型。

好处:

  1. 提供代码的重用性
  2. 防止类型转换异常,提高代码的安全性

泛型类

/**
 *LinkedList的使用
 */
public class Demp07 {
    public static void main(String[] args) {
        LinkedList linkedList = new LinkedList();
        Student s1 = new Student("A",97);
        Student s2 = new Student("B",98);
        Student s3 = new Student("C",99);

        //增
        linkedList.add(s1);
        linkedList.add(s2);
        linkedList.add(s3);
        System.out.println("长度:"+linkedList.size());
        System.out.println(linkedList.toString());

        //删
//        linkedList.remove(s1);
//        linkedList.remove(new Student("C",99));
//        linkedList.clear();

        //遍历
        for (int i = 0; i < linkedList.size(); i++) {
            System.out.println(linkedList.get(i));
        }

        for (Object ob:linkedList) {
            Student s = (Student)ob;
            System.out.println(s.toString());
        }

        Iterator iterator = linkedList.listIterator();
        while (iterator.hasNext()){
            Student student = (Student) iterator.next();
            System.out.println(student.toString());
        }

        ListIterator listIterator = linkedList.listIterator();
        while (listIterator.hasNext()){
            Student student2 = (Student) listIterator.next();
            System.out.println(student2.toString());
        }

        //判断
        System.out.println(linkedList.contains(s1));
        System.out.println(linkedList.isEmpty());
        System.out.println(linkedList.indexOf(s1));

    }
}

泛型接口

/**
 * 泛型接口
 * 语法:方法<T>
 *T表示类型占位符,表示一种引用类型,可编写多个用,隔开
 */
public interface MyInterface<T> {
    //不能泛型静态常量
    String name = "牧之";
    T server(T t);
}
public class MyInterfaceImpl implements  MyInterface<String>{
    @Override
    public String server(String t) {
        System.out.println(t);
        return t;
    }
}
//在实现接口时,不确定泛型类型的操作
//将实现类也变成泛型类MyInterfaceImpl2<T>
public class MyInterfaceImpl2<T> implements MyInterface<T>{
    @Override
    public T server(T t) {
        System.out.println(t);
        return null;
    }
}

泛型方法

//在实现接口时,不确定泛型类型的操作
//将实现类也变成泛型类MyInterfaceImpl2<T>
public class MyInterfaceImpl2<T> implements MyInterface<T>{
    @Override
    public T server(T t) {
        System.out.println(t);
        return null;
    }
}
//泛型方法的使用
MyGenericmethod myGenericmethod = new MyGenericmethod();
//自动装箱,输入的类型是根据你传入的数据类型决定的
myGenericmethod.show("你好");
myGenericmethod.show(3);
myGenericmethod.show(3.14);

泛型好处

好处:提供代码的重用性

例如该段代码

//泛型方法的使用
MyGenericmethod myGenericmethod = new MyGenericmethod();
//自动装箱,输入的类型是根据你传入的数据类型决定的
myGenericmethod.show("你好");
myGenericmethod.show(3);
myGenericmethod.show(3.14);

可以传入多个数据类型,如果是普通方法,会涉及到方法的重载

防止类型转换异常,提高代码的安全性

泛型对象之间不能相互赋值

泛型集合

概念:参数化类型、类型安全的集合,强制集合元素的类型必须一致

特点:

  • 编译时即可检查,而非运行时抛出异常
  • 访问时,不必类型转换(拆箱)
  • 不同泛型之间引用不能 ,泛型不存在多态
/**
 * 泛型集合
 */
public class Demo01 {
    public static void main(String[] args) {
        ArrayList<String> arrayList = new ArrayList<String>();
        arrayList.add("李冰");
        arrayList.add("里牧之");
//        arrayList.add(23);
//        arrayList.add(24);

        //ArrayList arrayList = new ArrayList();
        //错误:java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
        //当传入多种类型的数据,会出现类型转换异常
//        for (Object o:arrayList) {
//            String s = (String) o;
//            System.out.println(s);
//        }
        for (String s:arrayList) {
            System.out.println(s);
        }

        //可以通过泛型,输出多个不同的数据类型
        ArrayList<Student> arrayList2 = new ArrayList<Student>();
        Student s1 = new Student("A",97);
        Student s2 = new Student("B",98);
        Student s3 = new Student("C",99);
        arrayList2.add(s1);
        arrayList2.add(s2);
        arrayList2.add(s3);

        Iterator<Student> iterator = arrayList2.iterator();
        while (iterator.hasNext()){
            Student s = iterator.next();
            System.out.println(s.toString());
        }
    }
}

Set集合概述

特点:无序,无下标,元素不可重复

方法:均继承Collection接口

Set接口使用

/**
 * 测试Set的使用
 *  特点:无序,无下标,元素不可重复
 */
public class Demo01 {
    public static void main(String[] args) {
        Set<String> set = new HashSet();

        //增加
        set.add("张三");
        set.add("李四");
        set.add("王二");
        //元素不能重复
//        set.add("张三");
        System.out.println(set.size());
        System.out.println(set.toString());

        //删除
//        set.remove("张三");
//        System.out.println(set.toString());

        //遍历
        //foreach
        System.out.println("----------遍历------------");
        for (String s : set) {
            System.out.println(s);
        }

        //迭代器
        Iterator<String> iterator = set.iterator();
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }

        //判断
        System.out.println(set.contains("张三"));
        System.out.println(set.isEmpty());
    }
}

HashSet的使用【重点】

  • 基于HashCode计算元素存放的位置
  • 当存入元素的哈希码相同时,会调用equals进行确认,如结果为true,则拒绝后者存入

HashSet的存储方式

新建集合 HashSet<String> hashSet = new HashSet<String>();

添加元素 hashSet.add( );

删除元素 hashSet.remove( );

遍历操作

  1. 增强for for( type type : hashSet)
  2. 迭代器 Iterator<String> it = hashSet.iterator( );

判断 hashSet.contains( ); hashSet.isEmpty();

/**
 * HashSet集合的使用
 * 存储结构:数组+链表+红黑树
 * 存储过程:【重点】
 * (1)根据hashcode计算保存位置,如果此位置为空(没有数据),直接保存,如果不为空,执行第二步
 * (2)再执行equals方法,如为true,则认为是重复的,否则形成链表
 */
public class Demo03 {
    public static void main(String[] args) {

        //创建集合
        HashSet<Person> hashSet = new HashSet<>();
        Person p1 = new Person("张三",20);
        Person p2 = new Person("李四",21);
        Person p3 = new Person("王二",22);

        //增加
        hashSet.add(p1);
        hashSet.add(p2);
        hashSet.add(p3);
//        hashSet.add(p3);这种方式添加是重复的
        //这种是不会重复的,相当于内存中开辟了新的内存地址
        //在重写equals(1)的方法后,依然可以添加,则代表hashcode值相等,如要不添加,需要从equals方法介入
        hashSet.add(new Person("张三",20));
        System.out.println(hashSet.size());
        System.out.println(hashSet);

        //删除
//        hashSet.remove(p1);
        //重写equals后,可以删除
        hashSet.remove(new Person("张三",20));
//        System.out.println(hashSet.size());


        //遍历
        //增强for
        System.out.println("-----------------");
        for (Object o: hashSet) {
            System.out.println(o);
        }

        //迭代器
        Iterator iterator = hashSet.iterator();
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }

        //判断
        System.out.println(hashSet.contains("p1"));
        System.out.println(hashSet.isEmpty());

    }
}

重写后的hashcode和equals方法【重点】

@Override
public int hashCode(){
    int n1 = this.name.hashCode();
    int n2 = this.age;
    return n1+n2;
}

@Override
public boolean equals(Object obj){
    //如果是同一个就不比了
    if (this==obj){
        return true;
    }
    if (obj==null){
        return  false;
    }
    if (obj instanceof Person){
        Person p = (Person)obj;
        if (this.name.equals(p.getName())&&this.age==p.getAge()){
            return true;
        }
    }
    return false;
}

嫌麻烦可以直接使用IDE的自动重写方法(alt+insert)

HashSet的补充

  • 基于HashCode计算元素存放位置
    • 利用31这个质数,减少散列冲突
      • 31提高执行效率 31 * i = (i << 5) - i 转为移位操作
    • 当存入元素的哈希码相同时,会调用equals进行确认,如果结果为true,则拒绝后者存入

当前版本的源码跟学习的课程不同

@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 && name.equals(person.name);
}

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

TreeSet概述

存储结构:红黑树

  • 基于排列顺序实现元素不重复。
  • 实现了SortedSet接口,对集合元素自动排序。
  • 元素对象的类型必须实现Comparable接口,指定排序规则。
  • 通过CompareTo方法确定是否为重复元素

在存入数据时,实现Comparable接口

     	TreeSet<Person> treeSet = new TreeSet<>();
        Person p1 = new Person("zhangsan",20);
        Person p2 = new Person("lisi",21);
        Person p3 = new Person("wanger",22);

        treeSet.add(p1);
        treeSet.add(p2);
        treeSet.add(p3);
        System.out.println("元素个数"+treeSet.size());
        System.out.println(treeSet.toString());


        //删除
//        treeSet.remove(new Person("zhangsan",20));可以删除
        System.out.println(p1);

//需要继承Comparable接口  并重写compareTo方法
public class Person implements Comparable<Person>{  
//先比较姓名,后比较年龄
@Override
public int compareTo(Person o) {
    //compareTo如果参数字符串等于此字符串,则返回值 0;
    //如果此字符串小于字符串参数,则返回一个小于 0 的值;
    //如果此字符串大于字符串参数,则返回一个大于 0 的值。
    int n1 = this.getName().compareTo(o.getName());
    int n2 = this.age-o.age;
    //如果n1等于0,则代表名字相等,返回n2否则返回n1
    return n1==0?n2:n1;
}

Comparator接口

/**
 * Treeset集合的使用
 * Comparator:实现定制的比较
 * Comparable:可比较的
 */

public class Demo06 {
    public static void main(String[] args) {
        //创建集合,并制定比较规则
        TreeSet<Person> treeSet = new TreeSet<>(new Comparator<Person>() {
            @Override
            public int compare(Person o1, Person o2) {
                int n1 = o1.getAge()- o2.getAge();
                int n2 = o2.getName().compareTo(o2.getName());
                return n2==0?n1:n2;
            }
        });

        Person p1 = new Person("zhangsan",20);
        Person p2 = new Person("lisi",21);
        Person p3 = new Person("wanger",22);

        treeSet.add(p1);
        treeSet.add(p2);
        treeSet.add(p3);
        System.out.println("元素个数"+treeSet.size());
        System.out.println(treeSet.toString());

    }
}

TreeSet案列

/**
 * 使用TreeSet按照字符串长度进行比较
 */
public class Demo07 {
    public static void main(String[] args) {
        //创建集合,并制定比较规则
        TreeSet<String> treeSet = new TreeSet<>(new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                int n1 = o1.length()-o2.length();
                int n2 = o1.compareTo(o2);
                return n1==0?n2:n1;
            }

        });

        treeSet.add("zhangsan");
        treeSet.add("lisi");
        treeSet.add("wangwu");
        System.out.println("元素个数"+treeSet.size());
        System.out.println(treeSet.toString());
    }
}

Map集合概述

Map父接口

特点:存储一堆数据(Key-value),无序,无下标,键不可重复,值可重复。

方法

  • V put(K key,V value)将对象存放到集合中,关联键值。key重复则覆盖原有值
  • Object get(Obeject key)根据键获取对应的值
  • Set<Key> //返回所有的key
  • Collection<V> values()返回包含所有值的Collection集合
  • Set<Map.Entry<K,V>>键值匹配的set集合

keySet是吧key的值遍历到一个集合中,所以Value需要再次遍历导致效率变低

Entry是直接把key和value变成一个个映射对,所以效率高

/**
 * Map接口的使用
 * 特点:
 * (1)存储键值对
 * (2)键不能重复,值可以重复
 */
public class Demo01 {
    public static void main(String[] args) {
        //创建集合
        Map<String,String> map = new HashMap<>();

        //增加元素
        map.put("cn","中国");
        map.put("uk","英国");
        map.put("ua","美国");
        map.put("cn", "zhongguo"); // cn由于不能重复,value值就被覆盖了
        System.out.println(map.size());
        System.out.println(map.toString());

        //删除元素
        //map是根据键值进行删除的
//        map.remove("uk");
        System.out.println(map.size());

        //遍历
        //使用keySet只能先键值,需要value还需要map.get(key)
//        Set<String> keyset = map.keySet();
        for (String key:map.keySet()) {
            System.out.println(key+"----"+map.get(key));
        }


        //EntrySet()方法
//        Set<Map.Entry<String,String>> entrySet = map.entrySet();
        for (Map.Entry<String,String> entry:map.entrySet()) {
            System.out.println(entry.getKey()+entry.getValue());
        }

        //判断
        System.out.println(map.containsKey("cn"));
        System.out.println(map.containsValue("中国"));
        System.out.println(map.isEmpty());
    }
}

HashMap使用

/**
 * HashMap的使用
 * 存储结构:哈希表(数组+链表+红黑树)
 *
 * 使用key可使hashcode和equals作为重复
 */
public class Demo02 {
    public static void main(String[] args) {
        //创建对象
        HashMap<Student,String> hashMap = new HashMap<>();
        Student s1 = new Student(10,"张三");
        Student s2 = new Student(11,"李四");
        Student s3 = new Student(12,"王五");
        //增
        hashMap.put(s1,"北京");
        hashMap.put(s2,"上海");
        hashMap.put(s3,"杭州");
//        hashMap.put(s3,"南京");算重复元素
        //在未重写hashcode和equals方法前,new是开辟堆中的新的空间,可以重复,重写后就不可以了
        hashMap.put(new Student(10,"张三"),"北京");

        System.out.println(hashMap.size());
        System.out.println(hashMap.toString());

        //删
//        hashMap.remove(s1);
//        System.out.println(hashMap.size());

        //遍历
        //keyset
        System.out.println("------------------");
        for (Student s:hashMap.keySet()){
            System.out.println(s.toString()+hashMap.get(s));
        }

        //entry
        for (Map.Entry<Student,String> entry: hashMap.entrySet()) {
            System.out.println(entry.getKey()+entry.getValue());
        }

        //判断
        System.out.println(hashMap.containsKey("s1"));
        System.out.println(hashMap.containsValue("杭州"));
        System.out.println(hashMap.isEmpty());
    }
}

HashMap源码分析

存储结构:

1、JDK1.8之前,采用的是哈希表(数组+链表)

2、JDK1.8之后,采用哈希表(数组+链表+红黑树)

源码分析总结:

1、HashMap刚创建时,table(哈希桶)为null,节省空间。当添加第一个元素时,容量调整为16

2、当元素个数大于阈值(16*0.75=12)时,会进行扩容,扩容后的大小为原来的两倍,目的是减少调整元素的个数

3、JDK1.8 当每个链表长度大于8时,并且数组长度大于64, 会调整为红黑树,目的提高效率

4、JDK1.8 当链表长度小于6时,调整为链表

5、JDK1.8 以前,链表是头插入,之后是尾部插入

题外话:

HashSet的底层也是使用的HashMap

HashTable和Properties

HashTable(了解即可,已经弃用)

JDK1.0就存在,线程安全,运行效率低,不允许null作为key和value

Properties(可在IO框架最后一节有讲)

HashTable的子类,要求key和value都是String。通常用于配置文件的读取

TreeMap的使用

实现了SortedMap接口(是Map的子接口),可以对key进行自动排序

存储结构:红黑树

//treemap方法需要定制比较方法后,才能输出,否则会出现类型转换错误
// ClassCastException
    
public class Demo03 {
    public static void main(String[] args) {
        TreeMap<Student,String> treeMap = new TreeMap();
        Student s1 = new Student(10,"张三");
        Student s2 = new Student(11,"李四");
        Student s3 = new Student(12,"王五");
        treeMap.put(s1,"初一");
        treeMap.put(s2,"初二");
        treeMap.put(s3,"初三");
        System.out.println(treeMap.size());
        System.out.println(treeMap.toString());
    }
}
//重写的方法
  public class Student implements Comparable<Student>
       @Override
    public int compareTo(Student s) {
        int n1 = this.age-s.getAge();
        return n1;
    }

补充:TreeSet和TreeMap的区别

实际上TreeSet的底层依然使用的是TreeMap

Colletions工具类

概念:集合工具类,定义了除了存取以外的集合常用方法

/**
 * Colletion工具类的使用
 */
public class Demo04 {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        list.add(10);
        list.add(12);
        list.add(1);
        list.add(31);
        list.add(11);
        list.add(41);

        //sort排序
        System.out.println(list.toString());
        Collections.sort(list);
        System.out.println(list.toString());

        //binarySearcher二分查找
        int n1 =  Collections.binarySearch(list,31);
        System.out.println(n1);

        //copy复制
        //这个方法在时,要求两个数组的长度一致,导致需要给新集合一个长度
        List<Integer> list2 = new ArrayList<>();
        for (int i = 0; i < list.size(); i++) {
            list2.add(i);
        }
        Collections.copy(list2,list);
        System.out.println(list2);

        //reverse反转
        Collections.reverse(list);
        System.out.println(list);

        //shuffle打乱
        Collections.shuffle(list);
        System.out.println(list);


        //补充
        //list集合转成数组,使用toArray方法
        System.out.println("-----------");
        Integer[] arr = list.toArray(new Integer[0]);
        System.out.println(arr.length);
        System.out.println(Arrays.toString(arr));


        //数组转成集合
        String[] s = {"a","b","c"};
        //asList转换后的集合是受限集合,无法增加和删除
        List<String> l = Arrays.asList(s);
        System.out.println(l);

        //基本类型数组转换成集合是,需要修改为包装类
        Integer[] i = {1,2,3,4,5,6};
        List<Integer> l2 = Arrays.asList(i);
        System.out.println(l2);


        //错误写法,这样写导致数组又变成数组了
//        int[] i = {1,2,3,4,5,6};
//        List<int[]> l2 = Arrays.asList(i);
//        System.out.println(l2);
    }
}