0
点赞
收藏
分享

微信扫一扫

day21_数据结构丶单列集合的常用实现类丶Collections工具类丶可变参数

秀儿2020 2022-03-30 阅读 61
java

常见的数据结构

概述

数据结构 : 其实就是存储数据和表示数据的方式数据结构内容比较多,细细的学起来也是相对费功夫的,不可能达到一蹴而就。我们将常见的数据结构:堆栈、队列、数组、链表和红黑树 这几种给大家介绍一下,作为数据结构的入门,了解一下它们的特点即可。

stack,又称堆栈,它是运算受限的线性表,其限制是仅允许在表的一端进行插入和删除操作,不允许在其他任何位置进行添加、查找、删除等操作。 简单的说:采用该结构的集合,对元素的存取有如下的特点

  • 先进后出(即,存进去的元素,要在后它后面的元素依次取出后,才能取出该元素)。例如,子弹压进弹夹,先压进去的子弹在下面,后压进去的子弹在上面,当开枪时,先弹出上面的子弹,然后才能弹出下面的子弹。
  • 栈的入口、出口的都是栈的顶端位置

这里两个名词需要注意:

  • 压栈就是存元素。即,把元素存储到栈的顶端位置,栈中已有元素依次向栈底方向移动一个位置。
  • 弹栈就是取元素。即,把栈的顶端位置元素取出,栈中已有元素依次向栈顶方向移动一个位置。

队列

队列queue,简称队,它同堆栈一样,也是一种运算受限的线性表,其限制是仅允许在表的一端进行插入,而在表的另一端进行取出并删除。简单的说,采用该结构的集合,对元素的存取有如下的特点:

  • 先进先出(即,存进去的元素,要在后它前面的元素依次取出后,才能取出该元素)。例如,小火车过山洞,车头先进去,车尾后进去;车头先出来,车尾后出来。
  • 队列的入口、出口各占一侧例如,下图中的左侧为入口,右侧为出口。

数组

数组:Array,是有序的元素序列,数组是在内存中开辟一段连续的空间,并在此空间存放元素。就像是一排出租屋,有100个房间,从001到100每个房间都有固定编号,通过编号就可以快速找到租房子的人。简单的说,采用该结构的集合,对元素的存取有如下的特点:

  • 查找元素快:通过索引,可以快速访问指定位置的元素

  •  增删元素慢

               指定索引位置增加元素:需要创建一个新数组,将指定新元素存储在指定索引位置,再把原数组元素根据索引,复制到新数组对应索引的位置。如下图

             指定索引位置删除元素需要创建一个新数组,把原数组元素根据索引,复制到新数组对应索引的位置,原数组中指定索引位置元素不复制到新数组中。如下图  

链表

  • 链表:linked list,由一系列结点node(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。我们常说的链表结构有单向链表与双向链表,那么这里给大家介绍的是单向链表简单的说,采用该结构的集合,对元素的存取有如下的特点:

 简单的说,采用该结构的集合,对元素的存取有如下的特点:

  • 多个结点之间,通过地址进行连接。例如,多个人手拉手,每个人使用自己的右手拉住下个人的左手,依次类推,这样多个人就连在一起了。
  • 查找元素慢:想查找某个元素,需要通过连接的节点,依次向后查找指定元素。
  • 增删元素快:只需要修改链接下一个元素的地址值即可

树具有的特点:

  • 每一个节点有零个或者多个子节点
  • 没有父节点的节点称之为根节点,一个树最多有一个根节点。
  • 每一个非根节点有且只有一个父节点

常见的树结构

  • 二叉树:如果树中的每个节点的子节点的个数不超过2,那么该树就是一个二叉树。
  • 平衡二叉树:它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树
  • 红黑树:是一种自平衡的二叉查找树。作用:提高搜索效率

List接口

概述

java.util.List接口继承自Collection接口,是单列集合的一个重要分支,习惯性地会将实现了List接口的对象称为List集合。

List接口特点

  • 它是一个元素存取有序的集合。例如,存元素的顺序是11、22、33。那么集合中,元素的存储就是按照11、22、33的顺序完成的)。
  • 它是一个带有索引的集合,通过索引就可以精确的操作集合中的元素(与数组的索引是一个道理)。
  • 集合中可以有重复的元素

List接口新增常用方法

List作为Collection集合的子接口,不但继承了Collection接口中的全部方法,而且还增加了一些根据元素索引来操作集合的特有方法。

  • public void add(int index, E element): 将指定的元素,添加到该集合中的指定位置上。
  • public E get(int index):返回集合中指定位置的元素。
  • public E remove(int index): 移除列表中指定位置的元素, 返回的是被移除的元素。
  • public E set(int index, E element):用指定元素替换集合中指定位置的元素,返回值的更新前的元素。

代码示例

package demo04;

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

public class Demo {
    public static void main(String[] args) {
        // 创建list集合,限制集合中元素的类型为String类型
        List<String> list = new ArrayList<>();

        // 往集合中添加一些元素
        list.add("张三");
        list.add("李四");
        list.add("王五");
        System.out.println(list);// [张三, 李四, 王五]

        // 在索引为1的位置添加赵六
        list.add(1, "赵六");
        System.out.println(list);// [张三, 赵六, 李四, 王五]

        // 获取索引为1的元素
        System.out.println("索引为1的元素:"+list.get(1));// 索引为1的元素:赵六

        // 删除索引为1的元素
        String removeE = list.remove(1);
        System.out.println("被删除的元素:"+removeE);// 被删除的元素:赵六
        System.out.println(list);//  [张三, 李四, 王五]

        // 把索引为0的元素替换为田七
        String setE = list.set(0, "田七");
        System.out.println("被替换的元素:"+setE);// 被替换的元素:张三
        System.out.println(list);// [田七, 李四, 王五]
    }
}

List的常用子类

ArrayList类

  • java.util.ArrayList集合数据存储的结构是数组结构。元素增删慢,查找快,由于日常开发中使用最多的功能为查询数据、遍历数据,所以ArrayList是最常用的集合。许多程序员开发时非常随意地使用ArrayList完成任何需求,并不严谨,这种用法是不提倡的。

LinkedList类

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

实际开发中对一个集合元素的添加与删除经常涉及到首尾操作,而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):将元素推入此列表所表示的堆栈。

代码示例

package demo04;


import java.util.LinkedList;
public class Demo {
    public static void main(String[] args) {
        // 创建LinkedList集合,限制集合元素的类型为String类型
        LinkedList<String> list = new LinkedList<>();

        // 往集合中添加元素
        list.add("张三");
        list.add("李四");
        list.add("王五");
        System.out.println(list);// [张三, 李四, 王五]

        // 在集合的首尾添加一个元素
        list.addFirst("赵六");
        list.addLast("田七");
        System.out.println(list);// [赵六, 张三, 李四, 王五, 田七]

        // 获取集合的首尾元素
        String firstE = list.getFirst();
        String lastE = list.getLast();
        System.out.println("第一个元素是:"+firstE);// 第一个元素是:赵六
        System.out.println("最后一个元素是:"+lastE);// 最后一个元素是:田七

        // 删除首尾元素
        String removeFirst = list.removeFirst();
        String removeLast = list.removeLast();
        System.out.println("被删除的第一个元素是:"+removeFirst);// 被删除的第一个元素是:赵六
        System.out.println("被删除的最后一个元素是:"+removeLast);// 被删除的最后一个元素是:田七
        System.out.println(list);//  [张三, 李四, 王五]

        // pop  --->删除第一个元素
        String popE = list.pop();
        System.out.println("被删除的第一个元素是:"+popE);// 被删除的第一个元素是:张三
        System.out.println(list);// [李四, 王五]

        // push --->添加一个元素在开头
        list.push("富贵");
        System.out.println(list); // [富贵, 李四, 王五]
    }
}

常见的实现类还有。Vector:底层数据结构动态数组。Stack:底层数据结构栈 .....

Set集合

概述

  • Set接口是Collection的子接口,set接口没有提供额外的方法。但是比Collection接口更加严格了。Set 集合不允许包含相同的元素,如果试把两个相同的元素加入同一个 Set 集合中,则添加操作失败。
  • Set集合没有索引,所以遍历元素的方式就只有:增强for循环,或者迭代器
  • Set的常用实现类有:HashSet、TreeSet、LinkedHashSet。凡是实现了Set接口的类都叫做Set集合
import java.util.HashSet;

public class Test {
    public static void main(String[] args) {
        // 创建HashSet集合对象,限制集合元素的类型为Integer
        HashSet<Integer> hashSet = new HashSet<>();
        //添加数据
        hashSet.add(1);
        hashSet.add(2);
        hashSet.add(3);
        hashSet.add(1);
        hashSet.add(16);
        hashSet.add(6);
        /*
            HashSet集合: 元素存取无序,元素不可重复,元素无索引
         */
        System.out.println(hashSet);// [16, 1, 2, 3, 6]
    }

}

Set接口的常用子类

HashSet类

HashSet 是 Set 接口的典型实现,大多数时候使用 Set 集合时都使用这个实现类。 java.util.HashSet底层的实现其实是一个java.util.HashMap支持,然后HashMap的底层物理实现是一个哈希表。

哈希表底层结构

JDK1.8之前哈希表底层采用数组+链表实现,即使用数组处理冲突,同一hash值的链表都存储在一个数组里。但是当位于一个桶中的元素较多,即hash值相等的元素较多时,通过key值依次查找的效率较低。

而JDK1.8之后,哈希表存储采用数组+链表+红黑树实现,当链表长度超过阈值8时,将链表转换为红黑树,这样大大减少了查找时间。简单的来说,哈希表是由数组+链表+红黑树(JDK1.8增加了红黑树部分)实现的。如下图所示。

HashSet保证元素唯一原理:依赖hashCode()和equals()方法

  1. 创建一个默认长度为16的数组
  2. 当存储元素的时候,就会调用该元素的hashCode()方法计算该元素的哈希值,存储到数组对应的位置
  3. 判断该哈希值对应的位置上,是否有元素:
  4. 如果该哈希值对应的位置上,没有元素,就直接存储
  5. 如果该哈希值对应的位置上,有元素,说明产生了哈希冲突
  6. 产生了哈希冲突,就得调用该元素的equals方法,与该位置上的所有元素进行一 一比较。如果比较的时候,有任意一个元素与该元素相同,那么就不存储。 如果比较完了,没有一个元素与该元素相同,那么就直接存储

HashSet存储自定义类型元素

给HashSet中存放自定义类型元素时,需要重写对象中的hashCode和equals方法,建立自己的比较方式,才能保证HashSet集合中的对象唯一.

自定义类

package demo02;

import java.util.Objects;

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

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
    //其他代码省略

    @Override
    //重写   equals
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return age == student.age &&
                Objects.equals(name, student.name);
    }

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

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

定义测试类

import java.util.HashSet;

public class Test {
    public static void main(String[] args) {
        // 创建HashSet集合对象,限制集合元素的类型为Student
        HashSet<Student> hashSet = new HashSet<>();

        //添加元素,我们认为属性值一样就是同一个对象
        hashSet.add(new Student("张三",18));
        hashSet.add(new Student("王五",18));
        hashSet.add(new Student("李四",19));
        hashSet.add(new Student("张三",18));
        hashSet.add(new Student("赵六",15));
        hashSet.add(new Student("张三",18));

        System.out.println(hashSet);
        /*
         输出结果 如下
        [Student{name='王五', age=18}, Student{name='张三', age=18}, Student{name='赵六', age=15}, Student{name='李四', age=19}]
          */
    }

}

LinkedHashSet类

我们知道HashSet保证元素唯一,可是元素存放进去是没有顺序的,那么我们要保证有序,怎么办呢?在HashSet下面有一个子类java.util.LinkedHashSet,它是链表和哈希表组合的一个数据存储结构。LinkedHashSet是HashSet的子类,比父类多维护了元素的添加顺序。当且仅当,你既想要元素不可重复,又要保证元素的添加顺序时,再使用它。

import java.util.HashSet;
import java.util.LinkedHashSet;

public class Test {
    public static void main(String[] args) {
        // 创建LinkedHashSet集合对象,限制集合元素的类型为Integer
        HashSet<Integer> set = new LinkedHashSet<>();

        // 往集合中存储数据
        set.add(1);
        set.add(2);
        set.add(3);
        set.add(4);
        set.add(5);
        set.add(4);
        
        /*
            LinkedHashSet集合: 元素存取有序,元素无索引,元素不可重复(唯一)
                采用哈希表+链表结构,由哈希表保证元素唯一,由链表保证元素存取有序
         */
        System.out.println(set); //[1, 2, 3, 4, 5]
        
    }
}

TreeSet类

TreeSet集合是Set接口的一个实现类,底层依赖于TreeMap,是一种基于红黑树的实现,其特点为

  • 元素唯一
  • 元素没有索引
  • 使用元素的自然排序对元素进行排序,或者根据创建 TreeSet 时提供的 Comparator 比较器 进行排序,具体取决于使用的构造方法。

构造方法

  • public TreeSet():根据其元素的自然排序进行排序
  • public TreeSet(Comparator<E> comparator):    根据指定的比较器进行排序 指定规则排序

如何排序?

  • 自然排序:让待添加的元素类型实现Comparable接口,并重写compareTo方法
  • 定制排序:创建Set对象时,指定Comparator比较器接口,并实现compare方法

代码示例

package demo02;

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

public class Test {
    public static void main(String[] args) {
        // 按照默认规则排序---->默认升序
        // 创建TreeSet集合,限制集合中元素的类型为Integer类型
        TreeSet<Integer> set = new TreeSet<>();

        // 往集合中存储数据
        set.add(300);
        set.add(100);
        set.add(200);
        set.add(500);
        set.add(400);
        set.add(400);
        System.out.println(set);// [100, 200, 300, 400, 500]

        // 按照指定规则排序---->降序
        // 创建TreeSet集合,限制集合中元素的类型为Integer类型
        TreeSet<Integer> set1 = new TreeSet<>(new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                /*
                    指定排序规则:
                    前减后  升序
                    后减前  降序
                    前:第一个参数  后:第二个参数
                 */
                return o2 - o1;
            }
        });
        // 往集合中存储数据
        set1.add(300);
        set1.add(100);
        set1.add(200);
        set1.add(500);
        set1.add(400);
        set1.add(400);
        System.out.println(set1); //[500, 400, 300, 200, 100]
    }
}

Collections工具类

Collections 是一个操作 Set、List 和 Map 等集合的工具类。Collections 中提供了一系列静态的方法对集合元素进行排序、查询和修改等操作,还提供了对集合对象设置不可变、对集合对象实现同步控制等方法。

常用方法:

  • public static void shuffle(List<?> list) :打乱集合顺序。
  • public static <T> void sort(List<T> list):将集合中元素按照默认规则排序。默认规则就是事先写好的规则。集合元素所属的类一定要实现Comparable接口,重写compareTo方法,在compareTo方法中指定排序规则
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class Test {
    public static void main(String[] args) {
        // 创建List集合,限制集合中元素的类型为Integer类型
        List<Integer> list = new ArrayList<>();

        // 往集合中添加元素
        list.add(3);
        list.add(1);
        list.add(2);
        list.add(5);
        list.add(4);

        System.out.println("打乱顺序之前的集合:" + list);// 打乱顺序之前的集合:[3, 1, 2, 5, 4]
        // 打乱顺序
        Collections.shuffle(list); // 随机打乱顺序
        System.out.println("打乱顺序之后的集合:" + list);// 打乱顺序之后的集合:[2, 1, 4, 5, 3]

        // 将集合中元素按照默认规则排序
        Collections.sort(list);
        System.out.println("排序之后的集合:" + list); // 排序之后的集合:[1, 2, 3, 4, 5]
    }
}

如果集合中的元素是我们自定义的类,要使用默认规则进行排序,就必须实现Comparator接口中的compare方法来指定排序规则,代码示例

public class Student implements Comparable<Student> {
    private int age;

    public Student(int age) {
        this.age = age;
    }

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

    @Override
    //排序规则
    public int compareTo(Student o) {
        /*
             排序规则:
		        前减后  升序
    	        后减前  降序
    	        前: this   后:参数
         */
        //按照年龄从大到小排序
        return o.age - this.age;
    }
    //其他代码省略
}

测试类

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

public class Demo {
    public static void main(String[] args) {
        // 创建List集合,限制集合中元素的类型为Student类型
        List<Student> list = new ArrayList<>();

        // 往集合中添加元素
        list.add(new Student(3));
        list.add(new Student(2));
        list.add(new Student(311));
        list.add(new Student(23));
        list.add(new Student(0));
        System.out.println("排序之前的集合:"+list);
        // 输出结果: 排序之前的集合:[Student{age=3}, Student{age=2}, Student{age=311}, Student{age=23}, Student{age=0}]
        
        // 将集合中元素按照默认规则排序
        Collections.sort(list);
        System.out.println("排序之后的集合:"+list);
        //输出结果 :排序之后的集合:[Student{age=311}, Student{age=23}, Student{age=3}, Student{age=2}, Student{age=0}]
    }
}
  • public static <T> void sort (List<T> list,Comparator<? super T> ):将集合中元素按照指定规则排序。
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class Test {
    public static void main(String[] args) {
        // 创建List集合,限制集合中元素的类型为Integer类型
        List<Integer> list = new ArrayList<>();

        // 往集合中添加元素
        list.add(3);
        list.add(1);
        list.add(2);
        list.add(5);
        list.add(4);

        System.out.println("打乱顺序之前的集合:" + list);// 打乱顺序之前的集合:[3, 1, 2, 5, 4]

        // 将集合中元素按照默认规则排序
        Collections.sort(list);
        System.out.println("排序之后的集合:" + list); // 排序之后的集合:[1, 2, 3, 4, 5]

        //对集合中的元素按指定规则排序
        Collections.sort(list, new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                // 指定排序规则
                // 前减后  升序
                // 后减前  降序
                // 前: 第一个参数o1  后:第二个参数o2
                return o2-o1;
            }
        });
        System.out.println("排序之后的集合:" + list); // 排序之后的集合:[5, 4, 3, 2, 1]
    }
}

其他方法:

  • public static void reverse(List<?> list):反转指定列表List中元素的顺序。
  • public static void swap(List<?> list,int i,int j):将指定 list 集合中的 i 处元素和 j 处元素进行交换
  • public static int frequency(Collection<?> c,Object o):返回指定集合中指定元素的出现次数
  • public static <T> void copy(List<? super T> dest,List<? extends T> src):将src中的内容复制到dest中

Collections工具类中还有许许多多的方法,我们可以根据实际需求去API中查询使用

可变参数

前提:JDK1.5之后,如果我们定义一个方法需要接受多个参数,并且多个参数类型一致,我们可以对其简化.

public class Test {
    public static void main(String[] args) {
        getSum(1, 2, 3, 4);
    }

    //定义了可变参数的方法
    public static void getSum(int... number) {
        // 使用:把number可变参数当成数组使用
        int sum = 0;
        for (int i : number) {
            sum += i;
        }
        System.out.println("方法参数和:" + sum); //方法参数和:10
    }
}

 注意事项:

  • 可以变参数可以接收的参数数量是0 到n 个
  • 可变参数可以接收该类型的数组
  • 一个方法只能有一个可变参数
   /*
    编译报错,因为一个方法,只能有一个可变参数
    public static void method1(int... nums,String... strs){

    }*/
  • 如果方法中有多个参数,可变参数要放到最后。
   /*
    编译报错,因为如果方法有多个参数,可变参数一定要放在末尾
    public static void method2(int... nums,String  str){

    }
    */

在Collections中也提供了添加一些元素方法:

  • public static <T> boolean addAll(Collection<T> c, T... elements) :往集合中添加一些元素。
import java.util.ArrayList;
import java.util.Collections;

public class Test {
    public static void main(String[] args) {
        // 创建ArrayList集合,限制集合元素的类型为Integer类型
        ArrayList<Integer> arrayList = new ArrayList<>();
        // 往list集合中添加批量元素
        Collections.addAll(arrayList, 1111111, 2, 3, 45, 6, 7, 8);
        System.out.println(arrayList); //[1111111, 2, 3, 45, 6, 7, 8]
    }

}
举报

相关推荐

0 条评论