Java集合框架

Java集合框架

什么是集合

  • 概念:对象的容器,定义了对多个对象进行操作的常用方法,和数组类似。可以实现数组的功能。
  • 和数组的区别:
    1. 数组长度固定,集合长度不固定
    2. 数组可以存储基本类型和引用类型,集合只能存储引用类型。如果要存储基本类型,需要用到包装类装箱。
  • 位置:java.util.*

Collection体系集合

Collection父接口

  • 特点:代表一组任意类型的对象,无序、无下标(不能用普通for循环)、不能重复
  • 方法:
    • boolean add(Object obj) //添加一个对象。
    • boolean addAll(Collection c) //将一个集合中的所有对象添加到此集合中。
    • void clear() //清空此集合中的所有对象。
    • boolean contains(Object o) //检查此集合中是否包含o对象。
    • boolean equals(Object o) //比较此集合是否与指定对象相等。
    • boolean isEmpty() //判断此集合是否为空。
    • boolean remove(Object o) //在此集合中移除o对象。
    • int size() //返回此集合中的元素个数。
    • Object[] toArray() //将此集合转换成数组。
  • Collection接口的使用:基本方法
package collections;

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

/**
 * 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);

        // 2.删除元素
        collection.remove("榴莲");
        System.out.println("删除之后元素个数:" + collection.size());
        System.out.println("========榴莲已被删除========");
        // 3.清空元素
//        collection.clear();

        // 4.遍历元素【重点】
        // 4.1 方法一:增强for循环,没有下标
        for (Object object :
                collection) {
            System.out.println(object);
        }
        System.out.println("========没有榴莲了哦========");
        // 4.2 方法二:使用迭代器(专门用来遍历集合的一种方式)
        //hasNext(); 判断有没有下一个元素
        //next(); 获取下一个元素
        //remove(); 删除当前元素
        Iterator it = collection.iterator();
        // 迭代器迭代过程中不允许使用 Collection 的删除方法。
        // 不然会报错 并发修改异常,但是可以使用迭代器自身的删除方法
        while (it.hasNext()) {
            String s = (String) it.next();
            System.out.println(s);
//            collection.remove(s); 报错,因为迭代器正在使用这个元素,你给删了,合适吗?
//            it.remove(); 但是用迭代器自己的方法是可以删的!
        }

        // 5.判断
        System.out.println(collection.contains("西瓜"));
        System.out.println(collection.isEmpty());
    }
}

迭代器Iterator的三个方法:

迭代器工作时,使用collections修改元素,会报错:

        Iterator it = collection.iterator();
        // 迭代器迭代过程中不允许使用 Collection 的删除方法。
        // 不然会报错 并发修改异常,但是可以使用迭代器自身的删除方法
        while (it.hasNext()) {
            String s = (String) it.next();
            System.out.println(s);
			collection.add("猕猴桃"); // 报错,迭代器不允许并发修改
            // 因为迭代器正在使用这个元素,你给删了,合适吗?
//            it.remove(); //但是用迭代器自己的方法是可以的!
        }
  • Collections接口的使用:保存学生信息

学生类:

package collections.demo02;

/**
 * 学生类
 */
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 +
                '}';
    }
}
package collections.demo02;

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

/**
 * 保存学生信息
 */
public class Demo02 {
    public static void main(String[] args) {
        // 新建 Collection 对象
        Collection collection = new ArrayList();
        Student s1 = new Student("张三", 20);
        Student s2 = new Student("李四", 18);
        Student s3 = new Student("王五", 22);
        // 1.添加数据
        collection.add(s1);
        collection.add(s2);
        collection.add(s3);
        collection.add(s3); // 可以添加重复的,ArrayList
        System.out.println("元素个数:" + collection.size());
        System.out.println(collection.toString());
        // 2.删除
        collection.remove(s1);
//        collection.remove(new Student("王五", 22)); // 这个对象和以前的对象是不一样的
        // 3.清空
//        collection.clear(); // 虽然把集合中的元素清空了,但是这三个对象还是存在的
        System.out.println("删除之后:" + collection.size());
        // 4。遍历
        // 4.1 增强for循环
        for (Object object :
                collection) {
            System.out.println(object);
        }
        System.out.println("==========================================");
        // 4.2 迭代器: hasNext(); next(); remove()
        // 迭代过程中不能使用collection的删除方法
        Iterator it = collection.iterator();
        while (it.hasNext()) {
            Student s = (Student) it.next();
            System.out.println(s.toString());
        }
        // 5.判断
        System.out.println(collection.contains(s2));
        System.out.println(collection.isEmpty());
    }
}

结果:

元素个数:4
[Student{name='张三', age=20}, Student{name='李四', age=18}, Student{name='王五', age=22}, Student{name='王五', age=22}]
删除之后:3
Student{name='李四', age=18}
Student{name='王五', age=22}
Student{name='王五', age=22}
==========================================
Student{name='李四', age=18}
Student{name='王五', age=22}
Student{name='王五', age=22}
true
false

Collection子接口

  1. List 接口
  2. Set 接口

List集合

List子接口

  • 特点:有序(添加元素和获取元素、遍历元素顺序是一致的)、有下标(可以用for循环)、元素可以重复。

  • 方法:

    • 包含Collection的方法。

    • void add(int index,Object o) // 在index位置插入对象o。

    • boolean addAll(int index,Collection c) // 将一个集合中的元素添加到此集合中的index位置。

    • Object get(int index) // 返回集合中指定位置的元素。

    • List subList(int fromIndex,int toIndex) // 返回fromIndex和toIndex之间的集合元素。

List子接口的使用

List集合使用(1)
package collections.list;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;

/**
 * List子接口的使用
 */
public class Demo01 {
    public static void main(String[] args) {
        // 1.先创建集合对象
        List list = new ArrayList<>();
        // 2.添加元素
        list.add("小米");
        list.add("苹果");
        list.add("三星");
        list.add(0, "华为"); // 0的意思是在第一个位置添加
        System.out.println("元素个数:" + list.size());
        System.out.println(list.toString());
        // 3. 删除元素
        list.remove("苹果");
        list.remove(2);
        System.out.println("删除之后:" + list.size());
        System.out.println(list.toString());
        System.out.println("==========================================");

        // 4.遍历
        // 4.1普通for
        for (int i = 0; i < list.size(); i++) {
            System.out.println(list.get(i));
        }
        System.out.println("==========================================");

        // 4.2增强for
        for (Object object :
                list) {
            System.out.println(object);
        }
        System.out.println("==========================================");

        // 4.3迭代器
        Iterator it = list.iterator();
        while(it.hasNext()) {
            System.out.println(it.next());
        }
        System.out.println("==========================================");

        // 4.4【列表迭代器】,可以向前或向后遍历,还可以添加、删除、修改替换原宿,还可以获得下标
        ListIterator listIterator = list.listIterator();
        while(listIterator.hasNext()) { // 从前往后遍历
            System.out.println(listIterator.nextIndex() + ":" + listIterator.next());
        }
        System.out.println("==========================================");
        while(listIterator.hasPrevious()) { // 从后往前
            System.out.println(listIterator.previousIndex() + ":" + listIterator.previous());
        }
        System.out.println("==========================================");

        // 5.判断
        System.out.println(list.contains("苹果"));
        System.out.println(list.isEmpty());
        System.out.println("==========================================");

        // 6.获取位置
        System.out.println(list.indexOf("华为"));
    }
}

结果:

元素个数:4
[华为, 小米, 苹果, 三星]
删除之后:2
[华为, 小米]
==========================================
华为
小米
==========================================
华为
小米
==========================================
华为
小米
==========================================
0:华为
1:小米
==========================================
1:小米
0:华为
==========================================
false
false
==========================================
0
List集合使用(2)
package collections.list;

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

/**
 * List子接口的使用
 */
public class Demo02 {
    public static void main(String[] args) {
        // 创建集合
        List list = new ArrayList();
        // 1.添加数字数组(jdk1.5之后自动装箱,这里20不是基本类型int,是包装类Integer)
        list.add(20);
        list.add(30);
        list.add(40);
        list.add(50);
        list.add(60);
        list.add(70);
        System.out.println("元素个数:" + list.size());
        System.out.println(list);

        // 2.删除
//        list.remove(20); // 这样删除元素会报错,数组下标最大为4
//        list.remove(0); // 可以通过下标删除对应的值
//        list.remove((Object) 20); 可以这样删
//        list.remove(new Integer(70)); 也可这样删
        System.out.println("删除元素:" + list.size());

        // 3.补充方法:sublist:返回子集合,含头不含尾
        List subList = list.subList(1, 3);
        System.out.println(subList);
    }
}

结果:

元素个数:6
[20, 30, 40, 50, 60, 70]
删除元素:6
[30, 40]

List实现类

  • ArrayList(数组列表集合)【重点】:
    • 数组结构实现(底层是数组),查询快(数组空间连续)、增删慢;
    • JDK1.2加入,运行效率快、线程不安全。
    • 默认初始容量:10
  • Vector(向量集合):现在用的不多了,和ArrayList比较像
    • 数组结构实现,查询快、增删慢;
    • JDK1.0加入,运行效率慢、但是线程安全。
  • LinkedList(链表集合):
    • 链表结构实现,增删快、查询慢。(双向链表,前一个节点指向后一个节点)
ArrayList使用

在Student学生类中重写了equals方法

package collections.demo02;

/**
 * 学生类
 */
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 +
                '}';
    }

    @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;
            // 4.比较属性
            if (this.name.equals(s.getName()) && this.age == s.age) {
                return true;
            }
        }
        // 5.不满足条件返回false
        return false;
    }
}
package collections.list;

import collections.demo02.Student;

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

/**
 * ArrayList的使用
 */
public class Demo03 {
    public static void main(String[] args) {
        // 创建集合
        ArrayList arrayList = new ArrayList();
        // 1.添加元素
        Student s1 = new Student("liu", 20);
        Student s2 = new Student("zhang", 21);
        Student s3 = new Student("tang", 19);
        arrayList.add(s1);
        arrayList.add(s2);
        arrayList.add(s3);
        System.out.println("元素个数:" + arrayList.size());
        System.out.println(arrayList);
        System.out.println("===========删除元素============");
        // 2.删除元素
//        arrayList.remove(0); // 通过下标删除
//        arrayList.remove(s1);
        arrayList.remove(new Student("liu", 20)); //调用equals(this==obj),若要实现这个删除,就重写equals方法
        System.out.println("删除之后:" + arrayList.size());
        System.out.println(arrayList);
        // 3.遍历元素【重点】
        // 3.1使用迭代器
        System.out.println("===========使用迭代器============");
        Iterator iterator = arrayList.iterator();
        while (iterator.hasNext()) {
            Student student = (Student) iterator.next();
            System.out.println(student.toString());
        }
        // 3.2使用列表迭代器
        System.out.println("===========使用列表迭代器(顺序)============");
        ListIterator listIterator = arrayList.listIterator();
        while (listIterator.hasNext()) {
            Student student = (Student) listIterator.next();
            System.out.println(student.toString());
        }
        System.out.println("===========使用列表迭代器(逆序)============");
        while (listIterator.hasPrevious()) {
            Student student = (Student) listIterator.previous();
            System.out.println(student.toString());
        }
        // 4.判断
        System.out.println("===========判断============");
        // 重写了equals方法,此时会找到new的对象
        System.out.println(arrayList.contains(new Student("tang", 19)));
        System.out.println(arrayList.isEmpty());
        // 5.查找
        System.out.println("===========查找============");
        System.out.println(arrayList.indexOf(new Student("tang", 19)));
    }
}

结果:

元素个数:3
[Student{name='liu', age=20}, Student{name='zhang', age=21}, Student{name='tang', age=19}]
===========删除元素============
删除之后:2
[Student{name='zhang', age=21}, Student{name='tang', age=19}]
===========使用迭代器============
Student{name='zhang', age=21}
Student{name='tang', age=19}
===========使用列表迭代器(顺序)============
Student{name='zhang', age=21}
Student{name='tang', age=19}
===========使用列表迭代器(逆序)============
Student{name='tang', age=19}
Student{name='zhang', age=21}
===========判断============
true
false
===========查找============
1
ArrayList源码分析
  • 默认容量大小:

    • DEFAULT_CAPACITY = 10
    • 注意:如果没有向集合中添加任何元素时,数组的容量为0,任意添加一个元素,容量变为10
  • 存放元素的数组:

    • elementData
  • 实际元素个数

    • size
  • add()方法

    • 待补充

    • 扩容:每次都是原来的1.5倍

Vector使用
package collections.vector;

import java.util.Enumeration;
import java.util.Vector;

/**
 * Vector集合的使用
 */
public class Demo01 {
    public static void main(String[] args) {
        // 创建集合
        Vector vector = new Vector();
        // 1.添加元素
        vector.add("草莓");
        vector.add("苹果");
        vector.add("西瓜");
        System.out.println("元素个数:" + vector.size());
        System.out.println("=========删除=========");
        // 2.删除
//        vector.remove(0);
        vector.remove("西瓜");
//        vector.clear();
        System.out.println(vector);

        // 3.遍历
        // 使用枚举器
        System.out.println("=========枚举器遍历=========");
        Enumeration elements = vector.elements();
        while(elements.hasMoreElements()) {
            String o = (String) elements.nextElement();
            System.out.println(o);
        }

        // 4.判断
        System.out.println("=========判断=========");
        System.out.println(vector.contains("西瓜"));
        System.out.println(vector.isEmpty());

        // 5.其他方法
        System.out.println("=========获取第一个元素=========");
        System.out.println(vector.firstElement());
        System.out.println("=========获取最后一个元素=========");
        System.out.println(vector.lastElement());
        System.out.println("=========获取下标为0的元素=========");
        System.out.println(vector.get(0));
        System.out.println("=========获取下标为1的元素=========");
        System.out.println(vector.elementAt(1));
    }
}

结果:

元素个数:3
=========删除=========
[草莓, 苹果]
=========枚举器遍历=========
草莓
苹果
=========判断=========
false
false
=========获取第一个元素=========
草莓
=========获取最后一个元素=========
苹果
=========获取下标为0的元素=========
草莓
=========获取下标为1的元素=========
苹果
LinkedList使用

存储结构:双向链表

package collections.linkedlist;

import collections.demo02.Student;

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

/**
 * LinkedList的使用
 * 双向链表
 */
public class Demo01 {
    public static void main(String[] args) {
        // 创建集合
        LinkedList linkedList = new LinkedList();
        // 1.添加元素
        Student s1 = new Student("张三", 20);
        Student s2 = new Student("李四", 18);
        Student s3 = new Student("王五", 22);
        linkedList.add(s1);
        linkedList.add(s2);
        linkedList.add(s3);
        System.out.println("元素个数:" + linkedList.size());
        System.out.println(linkedList);

        // 2.删除
        System.out.println("=========删除=========");
//        linkedList.remove(s1);
        linkedList.remove(new Student("张三", 20));
        System.out.println("删除之后:" + linkedList.size());
//        linkedList.clear(); // 清空

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

        // 3.2增强for
        System.out.println("=========增强for遍历=========");
        for (Object o : linkedList) {
            System.out.println(o);
        }

        // 3.3迭代器
        System.out.println("=========迭代器=========");
        Iterator iterator = linkedList.iterator();
        while (iterator.hasNext()) {
            Student s = (Student) iterator.next();
            System.out.println(s);
        }

        // 3.4列表迭代器
        System.out.println("=========列表迭代器=========");
        ListIterator listIterator = linkedList.listIterator();
        while (listIterator.hasNext()) {
            Student s = (Student) listIterator.next();
            System.out.println(s);
        }
        // 4.判断
        System.out.println("=========判断=========");
        System.out.println(linkedList.contains("张三"));
        System.out.println(linkedList.isEmpty());
        // 5.获取位置
        System.out.println("=========获取位置=========");
        System.out.println(linkedList.indexOf(s2));
    }
}

结果:

元素个数:3
[Student{name='张三', age=20}, Student{name='李四', age=18}, Student{name='王五', age=22}]
=========删除=========
删除之后:2
=========for遍历=========
Student{name='李四', age=18}
Student{name='王五', age=22}
=========增强for遍历=========
Student{name='李四', age=18}
Student{name='王五', age=22}
=========迭代器=========
Student{name='李四', age=18}
Student{name='王五', age=22}
=========列表迭代器=========
Student{name='李四', age=18}
Student{name='王五', age=22}
=========判断=========
false
false
=========获取位置=========
0
LinkedList源码分析
  • size:集合大小

  • first:头节点

  • last:根节点

  • add()方法:

    • public boolean add(E e) {
          linkLast(e);
          return true;
      }
      
    • void linkLast(E e) {
          final Node<E> l = last;
          final Node<E> newNode = new Node<>(l, e, null);
          last = newNode;
          if (l == null)
              first = newNode;
          else
              l.next = newNode;
          size++;
          modCount++;
      }
      
    • Node类

    • private static class Node<E> {
          E item; // 节点中的实际数据
          Node<E> next; // 后一个节点
          Node<E> prev; // 前一个节点
      
          Node(Node<E> prev, E element, Node<E> next) {
              this.item = element;
              this.next = next;
              this.prev = prev;
          }
      }
      
ArrayList和LinkedList区别
  • ArrayList:必须开辟连续空间,查询快,增删慢。
  • LinkedList:无需开辟连续空间,查询慢,增删快。(删除的元素并没有被垃圾回收器回收,只是从集合中删除了)

泛型

  • Java泛型是JDK1.5中引入的一个新特性,其本质是参数化类型,把类型作为参数传递。
  • 常见形式有泛型类泛型接口泛型方法
  • 语法:
    • <T,…> T称为类型占位符,表示一种引用类型。
  • 好处:
    1. 提高代码的重用性:一个方法可以传递任何类型的数据。
    2. 防止类型转换异常,提高代码的安全性

泛型类

package collections.generic;

/**
 * 泛型类
 * 在类名的后面加<T...>,表示一种引用类型,可以写多个,中间用逗号隔开
 */
public class MyGeneric<T> {
    // 使用泛型T
    // 1.创建变量
    // 可以创建变量,但是不能实例化,不能保证传过来的类型构造方法一定能用
    // 不能保证一定有无参构造
    T t;

    // 2.泛型作为方法参数
    public void show(T t) {
        System.out.println(t);
    }
    // 3.泛型作为方法的返回值
    public T getT() {
        return t;
    }
}
package collections.generic;

public class TestCeneric {
    public static void main(String[] args) {
        // 使用泛型类创建对象
        //注意:1.泛型只能用引用类型 2.不同泛型类型对象之间不能相互赋值
        MyGeneric<String> myGeneric = new MyGeneric<>(); // JDK1.7之后<>里可以不写
        myGeneric.t = "hello";
        myGeneric.show("大家好");
        System.out.println(myGeneric.getT());

        MyGeneric<Integer> myGeneric1 = new MyGeneric<>();
        myGeneric1.t = 100;
        myGeneric.show("200");
        System.out.println(myGeneric1.getT());
    }
}

结果:

大家好
hello
200
100

泛型接口

package collections.generic;

/**
 * 泛型接口
 * 语法  接口名<T>
 * 注意:不能泛型静态常量
 * @param <T>
 */
public interface MyInterface<T> {
    String name = "张三";
    // 泛型接口
    T server(T t);
}
package collections.generic;

/**
 * 接口实现类 方式一
 * 创建实现类的时候就已经确定了泛型是什么(String)
 */


public class MyInterfaceImpl implements MyInterface<String> {

    @Override
    public String server(String s) {
        System.out.println(s);
        return s;
    }

}
package collections.generic;
/**
 * 接口实现类 方式二
 * 现在还不确定泛型是什么,由 MyInterfaceImpl2 来决定
 */

public class MyInterfaceImpl2<T> implements MyInterface<T> {
    @Override
    public T server(T t) {
        System.out.println(t);
        return t;
    }
}
package collections.generic;

public class TestGeneric2 {
    public static void main(String[] args) {
        MyInterfaceImpl impl = new MyInterfaceImpl();
        impl.server("12345");

        MyInterfaceImpl2 impl2 = new MyInterfaceImpl2();
        impl2.server(1000);
    }
}

结果:

12345
1000

泛型方法

package collections.generic;

/**
 * 泛型方法
 * 语法:<T>放在方法返回值类型前面
 */
public class MyGenericMethod {
    // 泛型方法
    public <T> T show(T t) {
        System.out.println("泛型方法" + t);
        return t;
    }
}
package collections.generic;

// 泛型方法的使用
public class TestGeneric3 {
    public static void main(String[] args) {
        MyGenericMethod myGenericMethod = new MyGenericMethod();
        myGenericMethod.show("中国加油");
        myGenericMethod.show(200);
        myGenericMethod.show(3.14);
    }
}

泛型集合

  • 概念:参数化类型、类型安全的集合,强制集合元素的类型必须一致。
  • 特点:
    • 编译时即可检查,而非运行时抛出异常。
    • 访问时,不必类型转换(拆箱)。
    • 不同泛型之间引用不能互相赋值,泛型不存在多态。
package collections.generic;

import collections.demo02.Student;

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

public class Demo01 {
    public static void main(String[] args) {
        // 在创建集合对象时已经确定了对象类型为 String
        ArrayList<String> arrayList = new ArrayList<>();
//        arrayList.add(1234); 报错,只能添加字符串类型
        arrayList.add("xxx");
        for (String string : arrayList) {
            System.out.println(string);
        }

        ArrayList<Student> arrayList1 = new ArrayList<>();
        Student student1 = new Student("a",11);
        Student student2 = new Student("b",12);
        Student student3 = new Student("c",13);
        arrayList1.add(student1);
        arrayList1.add(student2);
        arrayList1.add(student3);
        Iterator<Student> iterator = arrayList1.iterator();
        while (iterator.hasNext()) {
            Student s = iterator.next();
            System.out.println(s);
        }
    }
}

Set集合

Set子接口

  • 特点:无序、无下标、元素不可重复。
  • 方法:全部继承自Collection中的方法,一模一样,没有提供新的方法。

Set实现类

  • HashSet【重点】

    • 基于HashCode实现元素不重复。
    • 线程不安全。
    • 当存入元素的哈希码相同时,会调用equals确认,若果结果为true,则拒绝后者存入。
  • LinkedHashSet:

    • 跟HashSet一模一样,只是有序。
  • TreeSet:

    • 基于排列顺序实现元素不重复。
    • 存储结构为红黑树(二叉树的一种,平衡二叉树)
    • 实现了SortedSet接口,对集合元素自动排序。
    • 元素对象的类型必须实现Comparable接口,指定排序规则。
    • 通过compareTo方法确定是否为重复元素。
  • 红黑树:红黑树(Red Black Tree) 是一种自平衡二叉查找树,是在计算机科学中用到的一种数据结构,典型的用途是实现关联数组

  • 红黑树是每个结点都带有颜色属性的二叉查找树,颜色或红色或黑色。 在二叉查找树强制一般要求以外,对于任何有效的红黑树我们增加了如下的额外要求:

    性质1. 结点是红色或黑色。

    性质2. 根结点是黑色。

    性质3. 所有叶子都是黑色。(叶子是NIL结点)

    性质4. 每个红色结点的两个子结点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色结点)

    性质5. 从任一节结点其每个叶子的所有路径都包含相同数目的黑色结点。

    这些约束强制了红黑树的关键性质: 从根到叶子的最长的可能路径不多于最短的可能路径的两倍长。结果是这个树大致上是平衡的。因为操作比如插入、删除和查找某个值的最坏情况时间都要求与树的高度成比例,这个在高度上的理论上限允许红黑树在最坏情况下都是高效的,而不同于普通的二叉查找树。

    是性质4导致路径上不能有两个连续的红色结点确保了这个结果。最短的可能路径都是黑色结点,最长的可能路径有交替的红色和黑色结点。因为根据性质5所有最长的路径都有相同数目的黑色结点,这就表明了没有路径能多于任何其他路径的两倍长。

    因为红黑树是一种特化的二叉查找树,所以红黑树上的只读操作与普通二叉查找树相同。

    当我们在对红黑树进行插入和删除等操作时,对树做了修改,那么可能会违背红黑树的性质。

    为了保持红黑树的性质,我们可以对相关结点做一系列的调整,通过对树进行旋转(例如左旋和右旋操作),即修改树中某些结点的颜色及指针结构,以达到对红黑树进行插入、删除结点等操作时,红黑树依然能保持它特有的性质(五点性质)。

Set接口使用

package collections.set;

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

/**
 * 测试Set接口的使用
 * 特点:1.无序、没有下标
 *      2.不能重复
 */
public class Demo01 {
    public static void main(String[] args) {
        // 创建集合
        Set<String> set = new HashSet<>();
        // 1.添加数据
        set.add("苹果");
        set.add("华为");
        set.add("小米");
        set.add("小米");
        System.out.println("数据个数:" + set.size());
        System.out.println(set);
        System.out.println("=======删除========");

        // 2.删除
        set.remove("华为");
        System.out.println(set);

        // 3.遍历
        // 3.1增强for
        System.out.println("=======增强for遍历========");
        for (String s : set) {
            System.out.println(s);
        }
        System.out.println("=======迭代器========");

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

        // 4.判断
        System.out.println(set.isEmpty());
        System.out.println(set.contains("华为"));
    }
}

结果:

数据个数:3
[苹果, 华为, 小米]
=======删除========
[苹果, 小米]
=======增强for遍历========
苹果
小米
=======迭代器========
苹果
小米
=======判断========
false
false
HashSet使用
  • 存储结构:哈希表
package collections.set.hashset;

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

/**
 * HashSet集合的使用
 * 存储结构:哈希表(数组+链表+红黑树(JDK1.8之后多了红黑树))
 */
public class Demo01 {
    public static void main(String[] args) {
        // 新建集合
        HashSet<String> hashSet = new HashSet<>();
        // 1.添加元素
        hashSet.add("liu");
        hashSet.add("jia");
        hashSet.add("qi");
        hashSet.add("jiaqi");
        hashSet.add("liu");
        System.out.println(hashSet.size());
        System.out.println(hashSet);
        System.out.println("======删除======");

        // 2.删除
        hashSet.remove("liu");
        System.out.println("删除之后:" + hashSet.size());
        System.out.println("======增强for遍历======");

        // 3.遍历
        // 3.1增强for
        for (String s : hashSet) {
            System.out.println(s);
        }
        System.out.println("======迭代器遍历======");

        // 3.2迭代器
        Iterator<String> iterator = hashSet.iterator();
        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }
        System.out.println("======判断======");

        // 4.判断
        System.out.println(hashSet.isEmpty());
        System.out.println(hashSet.contains("liu"));
    }
}

结果:

4
[jia, qi, liu, jiaqi]
======删除======
删除之后:3
======增强for遍历======
jia
qi
jiaqi
======迭代器遍历======
jia
qi
jiaqi
======判断======
false
false
HashSet存储方式
  • 存储过程:
    1. 根据hashcode计算保存的位置,如果此位置为空,则直接保存,如果不为空则执行第二步。
    2. 再执行equals方法,如果equals方法为true,则认为是重复,否则,不重复,形成链表。
package collections.set.hashset;

import java.util.Objects;

public class Person {
    private String name;
    private int age;

    public Person(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 "Person{" + "name='" + name + '\'' + ", age=" + age + '}';
    }

//    // 手动重写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.name) && this.age == p.age) {
//                return true;
//            }
//        }
//        return false;
//    }

    //快捷键重写  Alt+insert,选择equals and hashCode

    @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);
    }
}
package collections.set.hashset;

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

/**
 * HashSet的使用
 * 存储结构:哈希表(数据+链表+红黑树(JDK1.8之后多了红黑树))
 * 存储过程:1.根据hashCode计算保存的位置,如果此位置为空,则直接保存,如果不为空则执行第二步
 * 2.再执行equals方法,如果equals方法为true,则认为是重复,否则,形成链表
 */
public class Demo02 {
    public static void main(String[] args) {
        // 创建集合
        HashSet<Person> persons = new HashSet<>();
        // 1.添加数据
        Person person1 = new Person("liu", 20);
        Person person2 = new Person("jia", 22);
        Person person3 = new Person("liujia", 21);
        Person person4 = new Person("liujia", 21);

        persons.add(person1);
        persons.add(person2);
        persons.add(person3);
        persons.add(person3); // 重复的不能添加
        persons.add(person4); // 新对象重复的可以添加
        // 新对象重复的可以添加进去
        // 如果重写hashCode方法和equals,名字相同,年龄相同就不能加进来了
        persons.add(new Person("liu", 20));

        System.out.println("元素个数:"+persons.size());
        System.out.println(persons);

        // 2.删除操作
        System.out.println("======删除======");
//        person.remove(person3);
        persons.remove(new Person("liu", 20)); // 能删,因为重写了hashCode方法
        System.out.println("删除之后:" + persons);

        // 3、遍历
        // 3.1增强for
        System.out.println("======增强for遍历======");
        for (Person person : persons) {
            System.out.println(person);
        }
        // 3.2迭代器
        System.out.println("======迭代器遍历======");
        Iterator<Person> iterator = persons.iterator();
        while(iterator.hasNext()) {
            System.out.println(iterator.next());
        }
        // 4.判断
        System.out.println(persons.isEmpty());
        System.out.println(persons.contains("liu"));
    }
}

结果

元素个数:3
[Person{name='jia', age=22}, Person{name='liujia', age=21}, Person{name='liu', age=20}]
======删除======
删除之后:[Person{name='jia', age=22}, Person{name='liujia', age=21}]
======增强for遍历======
Person{name='jia', age=22}
Person{name='liujia', age=21}
======迭代器遍历======
Person{name='jia', age=22}
Person{name='liujia', age=21}
false
false
HashSet补充
TreeSet的使用
package collections.set.treeset;

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

/**
 * TreeSet的使用
 * 存储结构:红黑树
 */
public class Demo01 {
    public static void main(String[] args) {
        // 创建集合
        TreeSet<String> treeSet = new TreeSet<>();
        // 1.添加元素
        treeSet.add("xyz");
        treeSet.add("abc");
        treeSet.add("hello");
        treeSet.add("xyz"); // 重复
        System.out.println("元素个数:" + treeSet.size());
        System.out.println(treeSet);

        // 2.删除
        System.out.println("======删除======");
        treeSet.remove("xyz");
        System.out.println("删除之后:" + treeSet.size());

        // 3.遍历
        // 3.1增强for
        System.out.println("======增强for遍历======");
        for (String s : treeSet) {
            System.out.println(s);
        }
        // 3.2迭代器
        System.out.println("======迭代器遍历======");
        Iterator<String> it = treeSet.iterator();
        while(it.hasNext()) {
            System.out.println(it.next());
        }
        // 4.判断
        System.out.println("======判断======");
        System.out.println(treeSet.contains("abc"));
        System.out.println(treeSet.isEmpty());
    }
}

结果:

元素个数:3
[abc, hello, xyz]
======删除======
删除之后:2
======增强for遍历======
abc
hello
======迭代器遍历======
abc
hello
======判断======
true
false

复杂例子:

在Person类中重写了compareTo方法:

package collections.set.hashset;

import java.util.Objects;

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

    public Person(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 "Person{" + "name='" + name + '\'' + ", age=" + age + '}';
    }

//    // 手动重写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.name) && this.age == p.age) {
//                return true;
//            }
//        }
//        return false;
//    }

    //快捷键重写  Alt+insert,选择equals and hashCode

    @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);
    }
    // 先按姓名比,然后再按年龄比
    @Override
    public int compareTo(Person o) {
        // 先比姓名
        int n1 = this.getName().compareTo(o.getName());
        // 再比年龄
        int n2 = this.age - o.getAge();
        return n1 == 0 ? n2 : n1;
    }
}
package collections.set.treeset;

import collections.set.hashset.Person;

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

/**
 * 使用TreeSet保存数据
 * 存储结构:红黑树
 * 要求:元素必须实现Comparable接口,comPareTo()方法的返回值为0,则认为是重复元素
 */
public class Demo02 {
    public static void main(String[] args) {
        // 创建集合
        TreeSet<Person> persons = new TreeSet<>();
        Person person1 = new Person("liu", 20);
        Person person2 = new Person("jia", 22);
        Person person3 = new Person("liujia", 21);
        Person person4 = new Person("liujia", 21);
        // 1.添加元素
        persons.add(person1);
        persons.add(person2);
        persons.add(person3);
        System.out.println("元素个数:" + persons.size());
        System.out.println(persons);

        // 2.删除
        System.out.println("========删除========");
        persons.remove(person1);
        // 这里不用重写 hashCode 和 equals 也可以,但是要重写compareTo
        // 因为重写了compareTo的比较方式所以可以删除
//        persons.remove(new Person("liujia", 21));
        System.out.println(persons.size());

        // 3.遍历
        // 3.1 增强for
        System.out.println("========增强for遍历========");
        for (Person person : persons) {
            System.out.println(person);
        }
        // 迭代器
        System.out.println("========迭代器遍历========");
        Iterator<Person> it = persons.iterator();
        while (it.hasNext()) {
            System.out.println(it.next());
        }

        // 4.查找
        System.out.println(persons.contains(person4));
        System.out.println(persons.contains(new Person("liujia", 21)));
    }
}

结果:

元素个数:3
[Person{name='jia', age=22}, Person{name='liu', age=20}, Person{name='liujia', age=21}]
========删除========
2
========增强for遍历========
Person{name='jia', age=22}
Person{name='liujia', age=21}
========迭代器遍历========
Person{name='jia', age=22}
Person{name='liujia', age=21}
true
true
Comparator接口

Comparator:实现定制比较(比较器),不需要再到Person里实现接口。

Comparable:可比较的,在Person里实现接口

package collections.set.treeset;

import collections.set.hashset.Person;

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

/**
 * TreeSet集合的使用
 * Comparator:实现定制比较(比较器)
 * Comparable:可比较的
 */
public class Demo03 {
    public static void main(String[] args) {
        // 创建集合,并指定比较规则
        TreeSet<Person> persons = new TreeSet<>(new Comparator<Person>() { // 匿名内部类实现
            @Override
            public int compare(Person o1, Person o2) {
                int n1 = o1.getAge() - o2.getAge();
                int n2 = o1.getName().compareTo(o2.getName());
                return n1 == 0 ? n2 : n1;
            }
        });

        Person person1 = new Person("liu", 20);
        Person person2 = new Person("jia", 22);
        Person person3 = new Person("liujia", 21);
        Person person4 = new Person("zhangsan", 21);

        persons.add(person1);
        persons.add(person2);
        persons.add(person3);
        persons.add(person4);
        System.out.println(persons);
    }
}

结果:

[Person{name='liu', age=20}, Person{name='liujia', age=21}, Person{name='zhangsan', age=21}, Person{name='jia', age=22}]

重写的compareTo方法中是先比较年龄再比较姓名,所以年龄小的在前面,年龄一样ASCII码值小的在前面。

TreeSet案例
package collections.set.treeset;

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

/**
 要求:使用TreeSet集合实现字符串按照长度进行排序
 * helloworld  zhang  lisi   wangwu   beijing  xian   nanjing
 */
public class Demo04 {
    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("helloworld");
        treeSet.add("zhang");
        treeSet.add("lisi");
        treeSet.add("wangwu");
        treeSet.add("beijing");
        treeSet.add("xian");
        treeSet.add("nanjing");
        System.out.println(treeSet);
    }
}

结果:

[lisi, xian, zhang, wangwu, beijing, nanjing, helloworld]

先比较长度,长度一样再比较ASSCII码大小。

Map体系集合

Map父接口

  • 特点:
    • 存储一堆数据(Key—Value),无序、无下标,键不可重复,值可重复。
  • 方法:
    • V put (k key,V value) // 将对象存入到集合中,关联键值。key重复则覆盖原值。
    • Object get (Object key) // 根据键获取对应的值。
    • Set // 返回所有Key
    • Collection values() // 返回包含所有值的Collection集合。
    • Set<Map.Entry<K, V>> // 键值匹配的Set集合。

其他可以查看API文档。

Map接口使用

package collections.map;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * Map接口的使用
 * 特点:
 * 1.存储键值对
 * 2.键不能重复,值可以重复
 * 3.无序
 */
public class Demo01 {
    public static void main(String[] args) {
        // 创建Map集合
        Map<String, String> map = new HashMap<>();
        // 1.添加元素
        map.put("CN", "中国");
        map.put("UK", "英国");
        map.put("USA", "美国");
        // Key不能重复,添加重复的Key时,新的value会替换原有的value
        map.put("CN", "China");
        System.out.println("元素个数:" + map.size());
        System.out.println(map);

        // 2.删除
        System.out.println("========删除========");
        map.remove("USA");
        System.out.println("删除之后:" + map.size());

        // 3.遍历
        // 3.1 keySet方法,返回值为所有的key的Set集合
        System.out.println("========keySet()遍历========");
        Set<String> keyset = map.keySet();
        for (String key : keyset) {
            // map.get() 通过键获取对应的值
            System.out.println(key +"是" + map.get(key));
        }
        // 3.2 entrySet方法,返回值为Map.Entry:映射对、键值对,其中包含key和value,效率高于keySet()
        System.out.println("========entrySet()遍历========");
        Set<Map.Entry<String, String>> entries = map.entrySet();
        // 内部接口加上Map.前缀
        for (Map.Entry<String, String> entry : entries) {
            System.out.println(entry.getKey() + "是" + entry.getValue());
        }

        // 3.3简写
        System.out.println("========还能这样简写!?========");
        for (Map.Entry<String, String> entry : map.entrySet()) {
            System.out.println(entry.getKey() + "是" + entry.getValue());
        }

        // 4.判断
        System.out.println("========判断========");
        System.out.println(map.isEmpty());
        System.out.println(map.containsKey("CN"));
        System.out.println(map.containsValue("美国"));
    }
}

结果:

元素个数:3
{USA=美国, UK=英国, CN=China}
========删除========
删除之后:2
========keySet()遍历========
UK是英国
CN是China
========entrySet()遍历========
UK是英国
CN是China
========还能这样简写!?========
UK是英国
CN是China
========判断========
false
true
false
  • entrySet() 效率高于 keySet()。

Map集合的实现类

HashMap【重点】:

  • JDK1.2版本,线程不安全,最好在单线程下使用,运行效率快;允许用null作为key或是value。
  • 默认初始容量:16

Hashtable:

  • JDK1.0版本,线程安全,运行效率慢;不允许null作为key或是value。现在已经不再使用了。

Properties:

  • Hashtable的子类,要求key和value都是String,通常用于配置文件的读取,在后面的I/O流会遇到。
HashMap集合

JDK1.2版本,线程不安全,运行效率快;允许用null作为key或是value。

存储结构:哈希表(数组+链表+红黑树(JDK1.8之后))

HashMap的使用

Student类重写hashCode和equals方法

package collections.map.hashmap;

import java.util.Objects;

public class Student {
    private String name;
    private int stuNo;

    public Student() {
    }

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

    public String getName() {
        return name;
    }

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

    public int getStuNo() {
        return stuNo;
    }

    public void setStuNo(int stuNo) {
        this.stuNo = stuNo;
    }

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

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return stuNo == student.stuNo && name.equals(student.name);
    }

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

HashMap使用key的hashCode和equals比较作为判断元素是否重复的依据,重写改变判断依据。

package collections.map.hashmap;

import java.util.HashMap;
import java.util.Map;

/**
 * HashMap集合的使用
 * 存储结构:哈希表(数组+链表+红黑树(JDK1.8以后))
 * 使用key的hashCode和equals比较作为判断元素是否重复的依据
 */
public class Demo01 {
    public static void main(String[] args) {
        // 创建集合
        HashMap<Student, String> students = new HashMap<>();
        // 1.添加元素
        Student s1 = new Student("liu", 1);
        Student s2 = new Student("jia", 2);
        Student s3 = new Student("qi", 3);
        students.put(s1, "刘");
        students.put(s2, "嘉");
        students.put(s3, "奇");
        // 加不进去,键不允许重复,值可以重复
        students.put(s3, "嘉奇");
        // 能加进去,要想实现姓名和学号一样就加不进去,就要重写hashCode和equals方法咯
        students.put(new Student("jia", 2), "嘉2");
        System.out.println("元素个数:" + students.size());
        System.out.println(students);

        // 2.删除
        System.out.println("========删除=============");
        students.remove(s1);
        System.out.println("删除之后:" + students);

        // 3.遍历
        // 3.1keySet
        System.out.println("========keySet遍历=============");
        for (Student key : students.keySet()) {
            System.out.println(key.toString() + ":" + students.get(key));
        }
        // 3.2entrySet
        System.out.println("========entrySet遍历=============");
        for (Map.Entry<Student, String> entry : students.entrySet()) {
            System.out.println(entry.getKey() + ":" + entry.getValue());
        }

        // 4.判断
        System.out.println("========判断=============");
        System.out.println(students.containsKey(new Student("liu", 1)));
        System.out.println(students.containsValue("嘉2"));
    }
}

结果:

元素个数:3
{Student{name='jia', stuNo=2}=2, Student{name='liu', stuNo=1}=, Student{name='qi', stuNo=3}=嘉奇}
========删除=============
删除之后:{Student{name='jia', stuNo=2}=2, Student{name='qi', stuNo=3}=嘉奇}
========keySet遍历=============
Student{name='jia', stuNo=2}:2
Student{name='qi', stuNo=3}:嘉奇
========entrySet遍历=============
Student{name='jia', stuNo=2}:2
Student{name='qi', stuNo=3}:嘉奇
========判断=============
false
true
HashMap源码分析

数组默认初始容量为16(1 << 4 1左移4位 1 * (2^4) 即16)

static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16

数组最大容量 2 ^ 30

static final int MAXIMUM_CAPACITY = 1 << 30;

默认的加载因子

static final float DEFAULT_LOAD_FACTOR = 0.75f;

默认的树化阈值:JDK1.8后加入红黑树,当链表的长度大于8,数组的长度大于等于64时,链表将会调整为红黑树,进一步提高查找效率。

static final int TREEIFY_THRESHOLD = 8;
static final int MIN_TREEIFY_CAPACITY = 64;

当链表的长度小于6,就将该树调整为链表:

static final int UNTREEIFY_THRESHOLD = 6;

键值对其实就是一个个Node

static class Node<K,V> implements Map.Entry<K,V> {
    final int hash;
    final K key;
    V value;
    Node<K,V> next;

table:保存的哈希表,刚创建好时table = null;

transient Node<K,V>[] table;

元素的个数:刚创建好时size= 0;

transient int size;

构造方法(给加载因子赋值0.75)

public HashMap() {
    this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
}

put方法:

public V put(K key, V value) {
    return putVal(hash(key), key, value, false, true);
}
if ((tab = table) == null || (n = tab.length) == 0)
    n = (tab = resize()).length;
Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
table = newTab;

总结:

  1. HashMap刚创建时,没有添加任何元素,table为null,容量为0,当添加第一个元素时,table容量调整为16。
  2. 当数组元素大于阈值(16*0.75=12)时,会进行扩容,扩容后大小为原来的2倍,目的是减少调整元素的个数。比如,当元素的数量超过12时,数组的容量就会调整为32。
  3. JDK1.8后当每个链表的长度大于8,且数组元素的个数大于等于64时,会调整为红黑树,目的是提高代码执行效率。
  4. JDK1.8后当链表的长度小于6时,调整成链表。
  5. JDK1.8之前,链表是头插入,JDK1.8以后是尾插入。
HashMap和HashSet的区别
public HashSet() {
    map = new HashMap<>();
}

实际上HashSet底层用的就是HashMap。

public boolean add(E e) {
    return map.put(e, PRESENT)==null;
}

实际上HashSet用的就是HashMap中的key来保存数据。

Hashtable和Properties

Hashtable已经不用了,Priperties在后边的I/O流部分会进一步解释。

TreeMap集合

存储结构:红黑树

TreeMap的使用

在Student类中重写了compareTo方法,比较学号stuNo

package collections.map.hashmap;

import java.util.Objects;

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

    public Student() {
    }

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

    public String getName() {
        return name;
    }

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

    public int getStuNo() {
        return stuNo;
    }

    public void setStuNo(int stuNo) {
        this.stuNo = stuNo;
    }

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

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return stuNo == student.stuNo && name.equals(student.name);
    }

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

    @Override
    public int compareTo(Student o) {
        int n2 = this.stuNo - o.getStuNo();
        return n2;
    }
}

如果不重写方法也可以在新建集合时定制比较:

// 新建集合 定制比较
        TreeMap<Student, String> treeMap = new TreeMap<>(new Comparator<Student>() {
            @Override
            public int compare(Student o1, Student o2) {
                int n1 = o1.getStuNo() - o2.getStuNo();
                return n1;
            }
        });
package collections.map.treemap;

import collections.map.hashmap.Student;

import java.util.Comparator;
import java.util.Map;
import java.util.TreeMap;
import java.util.TreeSet;

/**
 * TreeMap的使用
 * 存储结构:红黑树
 */
public class Demo01 {
    public static void main(String[] args) {
        // 新建集合
//        TreeMap<Student, String> treeMap = new TreeMap<>();
        // 新建集合 定制比较
        TreeMap<Student, String> treeMap = new TreeMap<>(new Comparator<Student>() {
            @Override
            public int compare(Student o1, Student o2) {
                int n1 = o1.getStuNo() - o2.getStuNo();
                return n1;
            }
        });
        // 1.添加元素 要实现Comparable接口
        Student s1 = new Student("liu", 1);
        Student s2 = new Student("jia", 2);
        Student s3 = new Student("qi", 3);
        treeMap.put(s1, "北京");
        treeMap.put(s2, "上海");
        treeMap.put(s3, "江苏");
        // 加不进去,但是会替换掉value
        treeMap.put(new Student("qi", 3), "南京");
        System.out.println("元素个数:" + treeMap.size());
        System.out.println(treeMap);

        // 2.删除
        System.out.println("========删除=============");
        treeMap.remove(s3);
//        treeMap.remove(new Student("qi", 3)); 能删除,因为compareTo比较的是学号
        System.out.println("删除之后:" + treeMap.size());

        // 3.遍历
        // 3.1keySet
        System.out.println("========keySet()遍历=============");
        for (Student key : treeMap.keySet()) {
            System.out.println(key + ":" + treeMap.get(key));
        }
        // 3.2entrySet
        System.out.println("========entrySet()遍历=============");
        for (Map.Entry<Student, String> entry : treeMap.entrySet()) {
            System.out.println(entry.getKey() + ":" + entry.getValue());
        }

        // 4.判断
        System.out.println(treeMap.containsKey(new Student("liu", 1)));
        System.out.println(treeMap.containsValue("南京"));
    }
}

结果:

元素个数:3
{Student{name='liu', stuNo=1}=北京, Student{name='jia', stuNo=2}=上海, Student{name='qi', stuNo=3}=南京}
========删除=============
删除之后:2
========keySet()遍历=============
Student{name='liu', stuNo=1}:北京
Student{name='jia', stuNo=2}:上海
========entrySet()遍历=============
Student{name='liu', stuNo=1}:北京
Student{name='jia', stuNo=2}:上海
true
false
TreeMap和TreeSet的关系

TreeSet的底层其实就是调用TreeMap

public TreeSet() {
    this(new TreeMap<>());
}

TreeSet的add方法调用的就是map.put()方法

public boolean add(E e) {
    return m.put(e, PRESENT)==null;
}

Collections工具类

  • 概念:集合工具类,定义了除了存取以外的集合常用方法。
  • 方法:
    • public static void reverse(List<?> list) // 反转集中元素的顺序
    • public static void shuffle(List<?> list) // 随机重置集合元素的顺序
    • public static void sort(List list) // 升序排序(元素类型必须实现Comparable接口)
package collections;

import java.util.*;

/**
 * Collections工具类的使用
 */
public class Demo01 {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        list.add(20);
        list.add(15);
        list.add(30);
        list.add(25);
        list.add(5);
        // 1.sort排序 (默认是升序,从小到大排序)
        System.out.println("==========sort排序==========");
        System.out.println("排序之前:" + list);
        Collections.sort(list); // 也可以自定义排序规则
        System.out.println("排序之后:" + list);

        // 2.binarySearch 二分查找 输出下标 没找到则返回负数
        System.out.println("==========binarySearch==========");
        int i = Collections.binarySearch(list, 15);
        System.out.println(i);

        // 3.copy 复制
        System.out.println("==========copy==========");
        List<Integer> list1 = new ArrayList<>();
        // 先令list1集合大小和list集合大小一致
        for (int j = 0; j < list.size(); j++) {
            list1.add(0);
        }
        // 直接复制会报错,这里的copy方法使用前提是两个集合大小一样,显然没有元素的list1大小为0
        Collections.copy(list1, list);
        System.out.println(list1);

        // 4.reverse 反转
        System.out.println("==========reverse==========");
        Collections.reverse(list1);
        System.out.println("反转之后:" + list1);

        // 5.shuffle 打乱
        System.out.println("==========shuffle==========");
        Collections.shuffle(list1);
        System.out.println("打乱之后:" + list1);

        // 6.补充:
        // 6.1 list集合转数组
        System.out.println("==========list集合转数组==========");
        /* 这里的长度设置如果小于list长度,则返回的数组长度是一样的;
        大于list长度,返回的数组长度就会是设置的长度 */
        Integer[] integers = list.toArray(new Integer[0]);
        System.out.println("设置的数组长度:" + integers.length);
        System.out.println(Arrays.toString(integers));
        System.out.println("==========设置的长度大于list==========");
        // 设置的数组长度大于list,则默认为null
        Integer[] integers1 = list.toArray(new Integer[10]);
        System.out.println("设置的数组长度:" + integers1.length);
        System.out.println(Arrays.toString(integers1));

        // 6.2 数组转list集合
        System.out.println("==========数组转list集合==========");
        String[] names = {"张三", "李四", "王五"};
        // 转完的集合是一个受限集合,不能添加、删除元素;长度是固定的
        List<String> list2 = Arrays.asList(names);
        System.out.println(list2);

        System.out.println("==========基本类型的数组转成集合需要修改为包装类型==========");
        // 基本类型的数组转换后 list中是一个数组,只有一个数据
//        int[] nums = {100, 200, 300, 400, 500};
//        List<int[]> list3 = Arrays.asList(nums);
        Integer[] nums = {100, 200, 300, 400, 500};
        List<Integer> list3 = Arrays.asList(nums);
        System.out.println(list3);
    }
}

结果:

==========sort排序==========
排序之前:[20, 15, 30, 25, 5]
排序之后:[5, 15, 20, 25, 30]
==========binarySearch==========
1
==========copy==========
[5, 15, 20, 25, 30]
==========reverse==========
反转之后:[30, 25, 20, 15, 5]
==========shuffle==========
打乱之后:[30, 20, 15, 5, 25]
==========list集合转数组==========
设置的数组长度:5
[5, 15, 20, 25, 30]
==========设置的长度大于list==========
设置的数组长度:10
[5, 15, 20, 25, 30, null, null, null, null, null]
==========数组转list集合==========
[张三, 李四, 王五]
==========基本类型的数组转成集合需要修改为包装类型==========
[100, 200, 300, 400, 500]

集合总结

  • 集合的概念:

    • 对象的容器,和数组类似(但长度可以改变),定义了多个对象进行操作的常用方法。
  • List集合

    • 有序、有下标、元素可以重复。(ArrayList(存储结构:数组)、LinkedList(存储结构:双向链表)、Vector)
  • Set集合

    • 无序、无下标、元素不可重复。(HashSet(存储结构:哈希表:数组+链表+红黑树)、TreeSet(存储结构:红黑树))
  • Map集合

    • 存储一堆数据,无序、无下标、键不可以重复,值可以重复。(HashMap(哈希表)、HashTable、TreeMap(红黑树))
  • Collections:

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

磕磕绊绊的也是挤着时间学完了集合这一块,理论知识还需巩固,实践经验还没有… 继续加油吧!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Luck1y

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值