ArrayList集合、Collections、树形结构、Set

ArrayList集合、Collections、树形结构、Set

第一章 List的子类

1.1 ArrayList集合

java.util.ArrayList集合数据存储的结构是数组结构。元素增删慢,查找快,由于日常开发中使用最多的功能为查询数据、遍历数据,所以ArrayList是最常用的集合。

  • public ArrayList() 构造一个初始容量为10的空列表
  • public ArrayList(int initialCapacity) 创建一个指定初始容量的列表
  • public ArrayList(Collection<? extends E> c) 创建一个ArrayList 存储Collection<? extends E> c集合中的元素
public class Demo01_ArrayList {
    public static void main(String[] args) {
        //底层并没有直接创建一个长度为10的数组 而是用一个长度为0的数组 为其赋值
        ArrayList<String> list = new ArrayList<>();

        //底层创建一个指定容量的数组
        ArrayList<String> list2 = new ArrayList<>(20);


        List<String>  list3 = new ArrayList<>();
        list3.add("aaa");
        list3.add("bbb");

        //底层创建一个数组 长度为 传进来集合中元素的个数
        ArrayList<String> list4 = new ArrayList<>(list3);
        System.out.println(list4);
    }
}
  • ArrayList 集合的扩容如下图

在这里插入图片描述

public class Demo02_ArrayList {
    public static void main(String[] args) {
        //底层创建了长度为0 的数组
        ArrayList<String> list = new ArrayList<>();
        //当添加第一个元素时  扩容为一个长度为10的数组
        list.add("aaa");
        list.add("aaa");
        list.add("aaa");
        list.add("aaa");
        list.add("aaa");
        list.add("aaa");
        list.add("aaa");
        list.add("aaa");
        list.add("aaa");
        list.add("aaa");
        //当添加第11个元素时  进入第2次扩容 每次扩容为原来数组长度的1.5倍   newcapacity = old + old/2
        list.add("bbb");
    }
}
  • 自定义ArrayList集合
    代码示例
public class MyArrayList<E> implements Iterable<E>{

    private Object[] arr ;
    private Object[] EMPTY_ARR = {};
    private int size;
    public MyArrayList(){
        arr =EMPTY_ARR;
    }
    public boolean add(E e){
        grow();
        arr[size++] = e;
        return true;
    }
    public void grow(){
        if(arr.length == 0){
            arr = new Object[10];
        }

        if(size+1 -arr.length>0){
            int old = arr.length;
            int newCapacity = old + (old>>1);

            arr =  Arrays.copyOf(arr,newCapacity);
        }
    }

    public String toString(){
        StringBuilder sb = new StringBuilder("[");

        for (int i = 0; i < size; i++) {
            sb.append(arr[i]);

            if(i == size -1){
                sb.append("]");
            }else{
                sb.append(", ");
            }
        }
        return  sb.toString();
    }


    //获取元素
    public E get(int index){
        checkIndex(index);

        return (E) arr[index];
    }

    public void checkIndex(int index){
        if(index<0 || index>=size){
            throw new IndexOutOfBoundsException("索引越界:"+index);
        }
    }

    //获取长度
    public int size(){
        return  size;
    }

    //修改指定索引元素
    public E set(int index,E e){
        checkIndex(index);

        //保存被修改的元素
        E old = (E) arr[index];

        arr[index] = e;

        return old;
    }
    //删除指定索引的元素
    public E remove(int index){
        checkIndex(index);

        //保存被删除的元素
        E old = (E) arr[index];

        //准备删除
        //计算移动元素的个数
        int moveNum = size - index - 1;
        if(moveNum>0){
            System.arraycopy(arr,index+1,arr,index,moveNum);
        }
        arr[--size] = null;

        return old;
    }

    //指定索引 添加元素
    public void add(int index,E e){
        checkAddIndex(index);

        grow();

        int moveNum  =  size - index;

        if(moveNum>0){
            System.arraycopy(arr,index,arr,index+1,moveNum);
        }
        arr[index] = e;
        size++;
    }

    //检查添加元素的索引  添加元素索引可以等于size
    public  void checkAddIndex(int index){
        if(index<0 || index>size){
            throw new IndexOutOfBoundsException("索引越界:"+index);
        }
    }

    public Iterator<E>  iterator(){
        return  new MyItr();
    }


    private class MyItr<E> implements Iterator<E>{
        int cursor = 0;

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

        @Override
        public E next() {
            //定义变量 保存 光标的值
            int i = cursor;

            //让光标向后移动一位
            cursor = i+1;
            //返回 光标没移动之前的值
            return (E) arr[i];
        }
    }
}
  • 练习
public class Test_MyArrayList {
    public static void main(String[] args) {
        MyArrayList<String> list = new MyArrayList<>();

        list.add("1");//1               1
        list.add("2");//3               2
        list.add("3");//4               2
        list.add("4");//4  ---> null    3
                                        //4

        System.out.println(list.toString());

//        for(int i = 0;i<list.size();i++){
//            String s = list.get(i);
//            System.out.println(s);
//        }

//        //修改指定索引处元素
//        String set = list.set(1, "aaa");
//        System.out.println("被修改的元素为:"+set);
//        System.out.println(list);

//        String remove = list.remove(1);
//        System.out.println("被删除的元素为:"+remove);
//        System.out.println(list);

        list.add(4,"aaa");
        System.out.println(list);


        Iterator<String> it = list.iterator();
        while(it.hasNext()){
            String s = it.next();
            System.out.println(s);
        }
        System.out.println("-------------------------");

        for(String s : list){
            System.out.println(s);
        }


    }
}

1.2 LinkedList集合

java.util.LinkedList集合数据存储的结构是链表结构。方便元素添加、删除的集合。

实际开发中对一个集合元素的添加与删除经常涉及到首尾操作,而LinkedList提供了大量首尾操作的方法。这些方法我们作为了解即可

  • public void addFirst(E e):将指定元素插入此列表的开头。
  • public void addLast(E e):将指定元素添加到此列表的结尾。
  • public E getFirst():返回此列表的第一个元素。
  • public E getLast():返回此列表的最后一个元素。
  • public E removeFirst():移除并返回此列表的第一个元素。
  • public E removeLast():移除并返回此列表的最后一个元素。
  • public E pop():从此列表所表示的堆栈处弹出一个元素。
  • public void push(E e):将元素推入此列表所表示的堆栈。
  • public boolean isEmpty():如果列表不包含元素,则返回true。
public class Demo01_LinkedList {
    public static void main(String[] args) {
        LinkedList<Integer> list = new LinkedList<>();

        list.addLast(200);

        list.add(1);
        list.add(2);
        list.add(3);
        list.add(4);

        list.addFirst(100);

        System.out.println(list);

        System.out.println("-------------------------------");
        LinkedList<String> stack = new LinkedList<>();

        //压栈
        stack.push("aaa");
        stack.push("bbb");
        stack.push("ccc");

        System.out.println(stack);

        //弹栈
        String pop = stack.pop();
        System.out.println(pop);
        System.out.println(stack);

        System.out.println(stack.pop());//bbb
        System.out.println(stack);
    }
}

LinkedList是List的子类,List中的方法LinkedList都是可以使用,这里就不做详细介绍,我们只需要了解LinkedList的特有方法即可。在开发时,LinkedList集合也可以作为堆栈,队列的结构使用。

第二章 Collections类

2.1 Collections常用功能

  • java.utils.Collections是集合工具类,用来对集合进行操作,构造方法私有,所有方法静态。

    常用方法如下:

  • public static void shuffle(List<?> list) :打乱集合顺序。

  • public static <T> void sort(List<T> list):根据元素的自然顺序 对指定列表按升序进行排序

  • public static <T> void sort(List<T> list,Comparator<? super T> ):将集合中元素按照指定规则排序自然排序 集合中的元素 必须实现自然排序接口 Comparable 重写compareTo方法。

代码演示:

创建一个Personpublic class Person implements Comparable<Person>{
    private String name;
    private int age;


    public Person() {
    }

    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 +
                '}';
    }

    @Override
    public int compareTo(Person other) {
        //this和other的比较
//        return this.age - other.age; //升序  this -other

        return other.age - this.age ; // 降序  other -this
    }
}


public class Demo01_Collections {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("abc");
        list.add("Abc");
        list.add("cbc");
        list.add("xbc");
        list.add("ybc");
        //首字母的ASCII排序
        Collections.sort(list);
        System.out.println(list);
        System.out.println("---------------------------");
        List<Person> personList = new ArrayList<>();
        personList.add(new Person("柳岩",38));
        personList.add(new Person("唐嫣",18));
        personList.add(new Person("金莲",138));
        personList.add(new Person("大郎",8));
        Collections.sort(personList);//Person类中对compareTo方法进行了重写 重新定义了排序的规则
        System.out.println(personList);
    }
}

public static void sort(List list,Comparator c) 传入比较器 指定比较规则

public class Demo02_Collections {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        list.add(10);
        list.add(40);
        list.add(20);
        list.add(30);
//        Collections.sort(list);
       Comparator<Integer> c =  new Comparator<Integer>(){
            @Override
            public int compare(Integer o1, Integer o2) {
                return o2 - o1; //降序
            }
       };
       //通过传入的比较器进行排序  如果传入比较器 就不再使用自然排序了 而是使用比较器指定的规则排序
       Collections.sort(list,c);
        System.out.println(list);
        Collections.sort(list, new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {return o1 - o2;
            }
        });
        System.out.println(list);
    }
}

练习:创建一个Student类 成员变量有 学号和分数 ,创建一个测试类 创建list集合添加学生,对象集合中的元素按照分数进行排序 升序排列

public class Student implements Comparable<Student>{

    private String id;
    private double score;

    public Student() {
    }

    public Student(String id, double score) {
        this.id = id;
        this.score = score;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public double getScore() {
        return score;
    }

    public void setScore(double score) {
        this.score = score;
    }

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

    @Override
    public int compareTo(Student o) {//重写compareTo方法,定义排序规则
        //return (int) (this.score - o.score);

        //如果分数相同
        if(this.score == o.score){
            //按照学号排序
            return  this.id.compareTo(o.id);
        }

        return  Double.compare(this.score,o.score);
    }
}




//创建Test类
public class Test {
    public static void main(String[] args) {
        List<Student> list = new ArrayList<>();

        list.add(new Student("001",90.1));
        list.add(new Student("002",89.9));
        list.add(new Student("005",100));
        list.add(new Student("003",100));
        list.add(new Student("004",60));


        Collections.sort(list);

        System.out.println(list);
    }
}
结果如下
[Student{id='004', score=60.0}, Student{id='002', score=89.9}, Student{id='001', score=90.1}, Student{id='003', score=100.0}, Student{id='005', score=100.0}]

第三章 数据结构–树

树基本结构介绍

树具有的特点:

  1. 每一个节点有零个或者多个子节点 node
  2. 没有父节点的节点称之为根节点
  3. 每一个非根节点有且只有一个父节点
  4. 除了根节点外,每个子节点可以分为多个不相交的子树

在这里插入图片描述

名词含义
节点指树中的一个元素
节点的度节点拥有的子树的个数,二叉树的度不大于2
叶子节点度为0的节点,也称之为终端结点
高度叶子结点的高度为1,叶子结点的父节点高度为2,以此类推,根节点的高度最高
根节点在第一层,以此类推
父节点若一个节点含有子节点,则这个节点称之为其子节点的父节点
子节点子节点是父节点的下一层节点
兄弟节点拥有共同父节点的节点互称为兄弟节点

二叉查找树

二叉查找树的特点:

  1. 左子树上所有的节点的值均小于等于他的根节点的值
  2. 右子树上所有的节点值均大于或者等于他的根节点的值
  3. 每一个子节点最多有两个子树

案例演示(20,18,23,22,17,24,19)数据的存储过程;

网站地址:https://www.cs.usfca.edu/~galles/visualization/Algorithms.html

遍历获取元素的时候可以按照"左中右"的顺序进行遍历;

注意:二叉查找树存在的问题:会出现"瘸子"的现象,影响查询效率

平衡二叉树

概述

为了避免出现"瘸子"的现象,减少树的高度,提高我们的搜素效率,又存在一种树的结构:“平衡二叉树”

规则:它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树

如下图所示:

在这里插入图片描述

如下图所示,左图是一棵平衡二叉树,根节点10,左右两子树的高度差是1,而右图,虽然根节点左右两子树高度差是0,但是右子树15的左右子树高度差为2,不符合定义,

所以右图不是一棵平衡二叉树。

旋转

在构建一棵平衡二叉树的过程中,当有新的节点要插入时,检查是否因插入后而破坏了树的平衡,如果是,则需要做旋转去改变树的结构。

左旋:

左旋就是将节点的右支往左拉,右子节点变成父节点,并把晋升之后多余的左子节点出让给降级节点的右子节点;

左旋

右旋:

将节点的左支往右拉,左子节点变成了父节点,并把晋升之后多余的右子节点出让给降级节点的左子节点

右旋

举个例子,像上图是否平衡二叉树的图里面,左图在没插入前"19"节点前,该树还是平衡二叉树,但是在插入"19"后,导致了"15"的左右子树失去了"平衡",

所以此时可以将"15"节点进行左旋,让"15"自身把节点出让给"17"作为"17"的左树,使得"17"节点左右子树平衡,而"15"节点没有子树,左右也平衡了。

由于在构建平衡二叉树的时候,当有新节点插入时,都会判断插入后时候平衡,这说明了插入新节点前,都是平衡的,也即高度差绝对值不会超过1。当新节点插入后,

有可能会有导致树不平衡,这时候就需要进行调整,而可能出现的情况就有4种,分别称作左左,左右,右左,右右

左左

左左即为在原来平衡的二叉树上,在节点的左子树的左子树下,有新节点插入,导致节点的左右子树的高度差为2,如下即为"10"节点的左子树"7",的左子树"4",插入了节点"5"或"3"导致失衡。

左左调整其实比较简单,只需要对节点进行右旋即可,如下图,对节点"10"进行右旋,

在这里插入图片描述

左右

左右即为在原来平衡的二叉树上,在节点的左子树的右子树下,有新节点插入,导致节点的左右子树的高度差为2,如上即为"11"节点的左子树"7",的右子树"9",

插入了节点"10"或"8"导致失衡。

在这里插入图片描述

左右的调整就不能像左左一样,进行一次旋转就完成调整。我们不妨先试着让左右像左左一样对"11"节点进行右旋,结果图如下,右图的二叉树依然不平衡,而右图就是接下来要

讲的右左,即左右跟右左互为镜像,左左跟右右也互为镜像。

在这里插入图片描述

左右这种情况,进行一次旋转是不能满足我们的条件的,正确的调整方式是,将左右进行第一次旋转,将左右先调整成左左,然后再对左左进行调整,从而使得二叉树平衡。

即先对上图的节点"7"进行左旋,使得二叉树变成了左左,之后再对"11"节点进行右旋,此时二叉树就调整完成,如下图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kMdIOy9g-1681126743179)(img/1562645351977.png)]

右左

右左即为在原来平衡的二叉树上,在节点的右子树的左子树下,有新节点插入,导致节点的左右子树的高度差为2,如上即为"11"节点的右子树"15",的左子树"13",

插入了节点"12"或"14"导致失衡。

在这里插入图片描述

前面也说了,右左跟左右其实互为镜像,所以调整过程就反过来,先对节点"15"进行右旋,使得二叉树变成右右,之后再对"11"节点进行左旋,此时二叉树就调整完成,如下图:

在这里插入图片描述

右右

右右即为在原来平衡的二叉树上,在节点的右子树的右子树下,有新节点插入,导致节点的左右子树的高度差为2,如下即为"11"节点的右子树"13",的左子树"15",插入了节点

"14"或"19"导致失衡。

在这里插入图片描述

右右只需对节点进行一次左旋即可调整平衡,如下图,对"11"节点进行左旋。

在这里插入图片描述

平衡树是高度平衡的,频繁的插入会引起频繁的reblance,导致效率下降;

红黑树

概述

红黑树是一种自平衡的二叉查找树,是计算机科学中用到的一种数据结构,它是在1972年由Rudolf Bayer发明的,当时被称之为平衡二叉B树,后来,在1978年被

Leoj.Guibas和Robert Sedgewick修改为如今的"红黑树"。它是一种特殊的二叉查找树,红黑树的每一个节点上都有存储位表示节点的颜色,可以是红或者黑;

红黑树不是高度平衡的,它的平衡是通过"红黑树的特性"进行实现的;

红黑树的特性:

  1. 每一个节点或是红色的,或者是黑色的。
  2. 根节点必须是黑色
  3. 每个叶节点(Nil)是黑色的;(如果一个节点没有子节点或者父节点,则该节点相应的指针属性值为Nil,这些Nil视为叶节点)
  4. 如果某一个节点是红色,那么它的子节点必须是黑色(不能出现两个红色节点相连的情况)
  5. 对每一个节点,从该节点到其所有后代叶节点的简单路径上,均包含相同数目的黑色节点;

如下图所示就是一个
在这里插入图片描述

在进行元素插入的时候,和之前一样; 每一次插入完毕以后,使用黑色规则进行校验,如果不满足红黑规则,就需要通过变色,左旋和右旋来调整树,使其满足红黑规则;

红黑树可以通过红色节点和黑色节点尽可能的保证二叉树的平衡,从而来提高效率。

第四章 Set接口

java.util.Set接口和java.util.List接口一样,同样继承自Collection接口,它与Collection接口中的方法基本一致,并没有对Collection接口进行功能上的扩充,只是比Collection接口更加严格了。与List接口不同的是,Set接口中元素无序,并且都会以某种规则保证存入的元素不出现重复。

public class Demo01_Set {
    public static void main(String[] args) {
        Set<String> set = new HashSet<>();
        set = new TreeSet<>();
        set = new LinkedHashSet<>();

        set.add("1柳岩");
        set.add("3唐嫣");
        set.add("2金莲");
        set.add("4大郎");
        set.add("4大郎");

        System.out.println(set);


        //迭代器
        Iterator<String> it = set.iterator();

        while(it.hasNext()){
            String name = it.next();
            System.out.println(name);
        }

        for (String s : set) {
            System.out.println(s);
        }


//        Set<Integer> set2 = new HashSet<>();
//        set2 = new TreeSet<>();
//
//        for (int i = 0; i < 100000; i++) {
//            set2.add(i);
//        }
//        System.out.println(set2);
    }
}

Set集合有多个子类,这里我们介绍其中的java.util.TreeSetjava.util.HashSetjava.util.LinkedHashSet这三个集合。

tips:Set集合取出元素的方式可以采用:迭代器、增强for。

4.1 TreeSet集合介绍

使用元素的[自然顺序]对元素进行排序,或者根据创建 set 时提供的 [Comparator] 进行排序,具体取决于使用的构造方法。 这个集合的特点:可以对集合中存储的数据进行排序,并且保证元素的唯一.但是如果想要对集合存储的元素进行排序的话,要么存储的类型实现自然排序的接口Comparable,要么TreeSet集合在创建时,使用比较器Comparator,来指定比较的规则.

构造方法描述
public TreeSet()构造一个新的空 set,该 set 根据其元素的自然顺序进行排序,集合中的元素必须实现自然排序接口,Comparable接口 重写CompareTo方法,如果CompareTo方法 返回值为0 说明两个元素相同 不添加到集合。
public TreeSet(Comparator<? super E> comparator)参构造 需要传入一个比较器指定比较规则 如果传入比较器 自然排序不生效,此时 Set集合中的元素可以不实现Comparable接口 如果重写的compare方法 返回0 说明两个元素相同 不添加到集合。

代码演示:

//定义Personn类
public class Person implements Comparable<Person>{
    private String name;
    private int age;

    public Person() {
    }

    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 +
                '}';
    }

    @Override
    public int compareTo(Person o) {
        if(this.age == o.age){
            return  this.name.compareTo(o.name);
        }


        return this.age - o.age;
    }
}





public class Demo02_TreeSet {
    public static void main(String[] args) {
        Set<Person> set = new TreeSet<>(new Comparator<Person>() {
            @Override
            public int compare(Person o1, Person o2) {
                if(o2.getAge() == o1.getAge()){
                    return  o2.getName().compareTo(o1.getName());
                }
                return o2.getAge() - o1.getAge();
            }
        });

        set.add(new Person("柳岩",38));
        set.add(new Person("唐嫣",18));
        set.add(new Person("金莲",138));
        set.add(new Person("大郎",8));
        set.add(new Person("西门庆",8));

        System.out.println(set);
    }
}


结果如下
[Person{name='金莲', age=138}, Person{name='柳岩', age=38}, Person{name='唐嫣', age=18}, Person{name='西门庆', age=8}, Person{name='大郎', age=8}]

TreeSet集合 如何保证元素唯一
1.如果实现Comparable接口 compareTo方法 返回0 说明元素相同 添加失败
2.如果使用比较器 compare方法 返回0 说明元素相同 添加失败

public class Demo03_TreeSet {
    public static void main(String[] args) {
        TreeSet<Integer> set = new TreeSet<>(new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o1 - o2;
            }
        });

//        set.add(null);
        set.add(3);
        set.add(8);
        set.add(2);
        set.add(5);
        set.add(5);

        System.out.println(set);


    }
}

效果如下:

OK!那么TreeSet集合是如何保证元素唯一,和进行排序的呢?我们来查看源码及图示解释
TreeSet集合底层使用Treemap
TreeSet集合添加元素原理

//TreeSet集合继承了AbstractSet实现了多个接口这里为了看起来方便 没有写出
public class TreeSet<E> {
    //因为TreeSet底层使用的是TreeMap集合的键 所以值就是一个常量 我们可以不管
	private static final Object PRESENT = new Object();
    
  	//底层调用的TreeMap的添加元素的方法  put方法 如果put方法返回值为null说明没有重复元素添加成功
    //如果put方法返回值不为null说明元素重复 添加失败
    public boolean add(E e) {
        return m.put(e, PRESENT)==null;
    }
}
public class TreeMap<K,V>  extends AbstractMap<K,V>{
    //在TreeMap中定义了一个比较器
     private final Comparator<? super K> comparator;
    //TreeMap底层用的是红黑树 Entry<K,V>就是节点 root代表是根节点
    //Entry中的K就是我们在Set中存储的数据
  	 private transient Entry<K,V> root;
     //集合长度
     private transient int size = 0;
    //定义一个变量 记录实际修改次数
     private transient int modCount = 0;
  
  
  	 public V put(K key, V value) {
       
       //创建一个节点t 记录root
        Entry<K,V> t = root;
       //如果 t==null说明没有根节点 第一次添加数据
       
        if (t == null) {
          	//比较传入过来的数据 为什么自己跟自己比较呢 这是在做检查
            //看看传入的元素能不能做比较 说白了就是是否实现了Comparable接口
            //如果传入的是null则抛空指针异常 .
            //如果不是null则进行比较 此时判断比较器是否是null 如果不是null用比较器比较
          // 如果比较器是null则用Comparable接口进行比较  如果在创建集合时没有传入比较器
          //那么比较器的值就是null 而类又没实现Comparable接口 这时就会抛异常
            compare(key, key); // type (and possibly null) check
			
            //使用传入过来的数据 创建一个新节点赋值给root
            root = new Entry<>(key, value, null);
            //设置长度为1
            size = 1; 
            //实际修改次数为+1
            modCount++;
          	//添加成功返回null
            return null;
        }
       
       //如果t!=null说明不是第一次添加数据 已经有了节点
        //定义一个变量 用来记录比较的结果
        int cmp;
        //定义一个父节点
        Entry<K,V> parent;
        //创建一个比较器对象 并使用传入的比较器为其赋值
        Comparator<? super K> cpr = comparator;
        //如果比较器不为null则使用比较器来进行判断
        if (cpr != null) {
           //使用do...whlie循环反复进行比较
            do {
                //将t节点赋值给父节点
                parent = t;
                //使用 传入过来的值 与 根节点的值进行比较
                cmp = cpr.compare(key, t.key);
                //如果比较后的值 小于0  说明传入过来的数值小于 t
                //则将t节点 设置为t左边的节点 也就是t右边的节点不需要再比较
                if (cmp < 0)
                    t = t.left;
                //如果比较后的值  大于0  说明传入过来的数值大于t
                //则将t节点 设置为t右边的节点 也就是t左边的节点不再需要比较了
                else if (cmp > 0)
                  
                    t = t.right;
                //如果比较后的数值 为0  如果值相同了 
                //在map集合中 会将之前的值覆盖掉 
                //但是我们是set集合 这里不考虑 就知道这种情况是相同了
                //但是返回的不是null 如果没有返回null则添加失败
                else
                    return t.setValue(value);
              
              //一直循环 直到t为null的时候 也就是下面没有节点的时候才跳出循环
            } while (t != null);
        }
       //如果比较器为null则使用自然排序判断 比较方式与比较器相同
        else {
            if (key == null)
                throw new NullPointerException();
            @SuppressWarnings("unchecked")
                Comparable<? super K> k = (Comparable<? super K>) key;
            do {
                parent = t;
                cmp = k.compareTo(t.key);
                if (cmp < 0)
                    t = t.left;
                else if (cmp > 0)
                    t = t.right;
                else
                    return t.setValue(value);
            } while (t != null);
        }
        //如果都判断完毕 说明没有重复元素 这时 t的位置就是新插入节点的父节点
       //那么使用传入过来的数据 创建一个新的Entry节点   
        Entry<K,V> e = new Entry<>(key, value, parent);
        //如果上面比较的小于0 将新的节点传入到左边 如果大于0将新的节点插入到右边
        if (cmp < 0)
            parent.left = e;
        else
            parent.right = e;
        //插入节点后 会进行调整红黑树
        fixAfterInsertion(e);
        //长度+1
        size++;
        //实际修改数+1
        modCount++;
        //返回null告知添加成功
        return null;
    }
     final int compare(Object k1, Object k2) {
        return comparator==null ? ((Comparable<? super K>)k1).compareTo((K)k2)
            : comparator.compare((K)k1, (K)k2);
    }

}

对于TreeSet集合的其他方法,这里就不再介绍了,和Set集合基本保持一致,没有索引,只能使用迭代器进行遍历

4.2 HashSet集合介绍

java.util.HashSetSet接口的一个实现类,它所存储的元素是不可重复的,并且元素都是无序的(即存取顺序不能保证不一致)。java.util.HashSet底层的实现其实是一个java.util.HashMap支持,由于我们暂时还未学习,先做了解。

HashSet是根据对象的哈希值来确定元素在集合中的存储位置,因此具有良好的存储和查找性能。保证元素唯一性的方式依赖于:hashCodeequals方法。

我们先来使用一下Set集合存储,看下现象,再进行原理的讲解:

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

    public Person() {
    }

    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 +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Person person = (Person) o;

        if (age != person.age) return false;
        return name != null ? name.equals(person.name) : person.name == null;
    }

    @Override
    public int hashCode() {
        int result = name != null ? name.hashCode() : 0;
        result = 31 * result + age;
        return result;
    }
}

public class Demo01_HashSet {
    public static void main(String[] args) {
        Set<String> set = new HashSet<>();

        set.add("abc");
        set.add("bcd");
        set.add("abc");

        System.out.println(set);

        System.out.println("------------------------------------------");
        Set<Person> set2 = new HashSet<>();

        set2.add(new Person("柳岩",38));
        set2.add(new Person("唐嫣",18));
        set2.add(new Person("唐嫣",18));

        System.out.println(set2);
    }
}



输出结果如下,说明集合中不能存储重复元素:

bcd
abc

tips:根据结果我们发现字符串"abc"只存储了一个,也就是说重复的元素set集合不存储。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值