Java进阶—数据结构、List、Set、可变参数、Collections

一、数据结构

1.概述
常见的和集合相关的数据结构:栈、队列、数组、链表和红黑树。

2.栈(Stack)
栈:stack,又称为堆栈,它是运算受限的线性表,其限制是仅允许在【表的一端】进行插入和删除操作,不允许再其他任何位置进行添加、查找、删除等操作。

【先进后出】
入口和出口在集合的【同一侧】

存储元素到集合:入/压栈(从栈顶到栈底)
取出集合中元素:出/弹栈(从栈底到栈顶)

例如:
    入栈:123
    出栈:321

3.队列
队列:queue,简称队,它同堆栈一样,也是一种运算受限的线性表,其限制是仅允许在【表的一端】进行插入,而在【表的另一端】进行删除。

【先进先出】
入口和出口在集合的【两侧】

例如:
    存储:123
    取出:123

4.数组
数组:Array,是有序的元素序列,数组是在内存中开辟一段连续的空间,并在此空间存放元素。就像是一排出租屋,有100个房间,从001到100每个房间都有固定编号,通过编号就可以快速找到租房子的人。

【查询快】:数组的【地址是连续】的,我们通过数组的首地址可以找到数组,通过数组的索引可以快速查找某一个元素

【增删慢】:数组的长度是固定的,我们想要增加/删除一个元素,必须创建一个新的数组,把原数组的数据复制过来

例如:
    int[] arr = new int[]{1, 2, 3, 4};
    new出来的对象,会在堆内存中分配一个内存地址,然后将该地址赋值给arr,我们就可以通过arr找到这个数组的首地址,然后通过索引【快速找到】对应的数值

5.链表
链表:linked list,由一系列结点node(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。

链表中的每一个元素也称为一个结点,一个结点包含了一个数据源(存储数组),两个指针域(存储地址)。

所以一个结点为:自己的地址 + 数据 + 下一个结点的地址

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

【查询慢】:链表中【地址不是连续】的(不同于数组中地址是连续的),每次查询元素,都必须【从头开始】查询

【增删快】:链表结构,增加/删除一个元素,对链表的整体结构没有影响,所以增删快

单向链表和双向链:

【单向链表】:链表中只有一条链子,不能保证元素的顺序(存储元素和取出元素的顺序可能不一致)
        0x11 + 100 + 0x72  -->  0x72 + 200 + 0x99  -->  0x99 + 数据 + 0x11
        上个结点的地址可以记住下一个结点的地址,但是下一个结点的地址记不住上一个结点的地址,所以是【无序】的。

【双向链表】:链表中有两条链子,有一条链子是专门记录元素的顺序,是一个有序的集合
    0x11 + 100 + 0x72  <-->  0x72 + 200 + 0x99  <-->  0x99 + 数据 + 0x11
    上个结点的地址可以记住下一个结点的地址,下一个结点的地址也可以记住上一个结点的地址,所以是【有序】的。

【如果想要删除元素200,则只需要把第一个结点中的“下一个结点地址”由0x72改为0x99即可。所以增删快】

例如:
    数组就像是一排出租屋,每个房间都挨着,所以地址是连续的。
    链表也是很多屋子,但是不是挨着的,所以地址不是连续的。

6.红黑树
1.二叉树:binary tree,是每个【结点不超过2】的有序树(tree)。

简单的理解,就是一种类似于我们生活中树的结构,只不过每个结点上都最多只能有两个子节点。

二叉树是每个结点最多有两个子树的树结构,顶上的叫【根节点】,两边被称作“左子树”和“右子树”。

2.排序树/查找树

在二叉树的基础上,元素是有大小顺序的。
左子树小,右子树大。

特点:

查询速度非常的快

例如:

猜数字小游戏,1-100之间的数字,从50开始猜,一次减一半。能比较快速的猜出数字

对于排序树来说,首先找到根节点中的值,如果值偏大,就找左子树,否则就在右子树中寻找,所以【查询比较快】。

3.平衡树:左子树和右子树相等

4.不平衡树:左子树和右子树数量不一样。

5.红黑树:
特点:

趋近于【平衡树】,【查询的速度非常的快】,【查询叶子节点最大次数和最小次数不能超过2倍】。

约束:(了解)

1.节点可以是红色的或者是黑色的
2.根节点是黑色的
3.叶子节点(空节点)是黑色的
4.每个红色的节点的子节点都是黑色的
5.任何一个节点到其每一个叶子节点的所有路径上黑色节点数相同

二、List集合

1.概述
Collection是所有集合的最顶层接口,包含两个主要的子接口:java.util.List和java.util.Set

List接口的实现类有:ArrayList、LinkedList、Vector

Set接口的实现类有:HashSet、LinkedHashSet、TreeSet

2.java.util.List子接口

public interface List<E> extends Collection<E>

【有序】(存储和取出元素的顺序是一致的)的collection(也称为序列),与set不同,列表通常允许重复的元素。

3.List接口的特点

1.有序的集合,存储元素和取出元素的顺序是一致的(存储123,取出也是123)

2.有索引,包含了一些带索引的方法

3.允许存储重复的元素

4.List接口中带索引的方法(也是特有的方法)

public void add(int index, E element):将指定的元素,添加到该集合中的指定位置上

public E get(int index):返回集合中指定位置的元素

public E remove(int index):移除列表中指定位置的元素,返回的是被移除的元素。

public E set(int index, E element):用指定的元素替换集合中指定位置的元素,返回值的更新前的元素

5.注意事项

操作索引的时候,一定要防止索引越界异常

IndexOutOfBoundsException:索引越界异常,该异常一般集合会报。

ArrayIndexOutOfBoundsException:数组索引越界异常

StringIndexOutOfBoundsException:字符串索引越界异常

6.List集合遍历方式

1.使用普通的for循环

2.使用增强for循环

3.使用迭代器

7.ArrayList集合

1.ArrayList集合是List子接口的实现类;List接口的大小可变数组的实现。

2.数据存储的结构是【数组结构】,元素【增删慢】,【查询快】,由于日常开发中使用最多的功能为查询数据、遍历数据,所以ArrayList是最常用的集合。
  即ArrayList集合的底层是数组结构,数组存储的元素【地址是连续】的,所以查询快,增删慢。

3.注意:
    1.此实现不是同步的。不是同步的,说明是多线程的,多线程就意味着效率高。
    2.当查询需求比较多时,用ArrayList集合,因为查询快;但是当增删需求比较多时,用ArrayList集合就比较慢了,所以应该根据不同需求使用不同的集合。

8.LinkedList集合
java.util.LinkedList集合是List子接口的另一个实现类。

数据存储的结构是【链表】结构。【查询慢】,【增删快】。
LinkedList是一个双向链表。找到头(head)和尾(tail)非常方便,;里边有大量的【操作首尾元素】的方法。

注意,此实现不是同步的,所以这个集合也是一个多线程,多线程就意味着速度快。

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

总结,LinkedList集合的特点:
    1.底层是一个【链表结构】:查询慢、增删快
    2.里边包含了大量操作首尾元素的方法
    注意:使用LinkedList集合特有的方法,【不能使用多态】,因为使用多态以后,就不能使用子类特有的方法了,【除非向下转型】

    public void addFirst(E e):将指定元素插入此列表的开头
    public void addLast(E e):将指定元素添加到此列表的结尾     【等效于add()】
    public void push(E e):将元素推入此列表所表示的堆栈        【等效于addFirst()】

    public E getFirst():返回此列表的第一个元素
    public E getLast():返回此列表的最后一个元素

    public E removeFirst():移除并返回此列表的第一个元素
    public E removeLast():移除并返回此列表的最后一个元素
    pubic E pop():从此列表所表示的堆栈处弹出一个元素             【等效于removeFirst】

    public boolean isEmpty():如果列表不包含元素,则返回true

9.Vector集合(了解)
Vector集合是List子接口的另一个实现类。
Vector集合是一个单列集合。

Vector类可以实现可增长的对象数组。与数组一样,它包含可以使用整数索引进行访问的组件。但是,Vector的大小可以根据需要增大或缩小,以适应创建Vector后进行添加或移除项的操作。

与新collection实现不同,Vector是【同步】的。同步就意味是单线程,速度就慢。

三、Set集合

1.Set子接口
java.util.Set接口和java.util.List接口一样,同样继承自Collection接口。

与List接口不同的是,Set接口中【元素无序】,并且都会以某种规则保证存入的元素不出现重复。

Set接口的主要实现类:

java.util.HashSet

java.util.LinkedHashSet

2.遍历Set集合的方式

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

Set集合里的元素没有索引值,所以不能使用普通for

3.Set接口的特点

1.不允许存储重复的元素

2.没有索引,没有带索引的方法,也不能使用普通的for循环遍历

4.HashSet

public class HashSet<E> extends AbstractSet<E> implements Set<E>

HashSet集合是Set子接口的一个实现类。
由【哈希表】(实际上是一个HashMap实例)支持。它不保证set的迭代顺序,特别是他不保证该顺序恒久不变。

【注意,此实现不是同步的,也就是单线程的】。

HashSet集合的特点:

1.不允许存储重复的元素

2.没有索引,没有带索引的方法,也不能使用普通的for循环遍历

3.是一个【无序】的集合,存储元素和取出元素的顺序有可能不一致

4.底层是一个【哈希表结构】(【查询的速度非常快】)

5.哈希表
哈希表 = 数组 + 链表/红黑树

特点:查询速度快

数组结构:把元素进行了分组(相同哈希值的元素是一组)

链表结构/红黑树结构:把相同哈希值的元素连接到一起

存储数据到集合中:

先计算元素的哈希值,哈希值就是元素在数组中的存储位置,
两个元素不同,但是哈希值相同,这称为【哈希冲突】。

在数组中存储哈希值,然后不同的元素挂在不同的哈希值下边,
如果链表的长度超过了8位,那么就会把链表转换为红黑树(为了提高查询的速度)

6.为什么Set集合不允许存储重复的元素?

不能存储重复元素有一个前提:

存储的元素【必须重写】hashCode方法和equals方法。

哈希表 = 数组(初始容量为16) + 链表/红黑树

数组中存储的是元素的哈希值,然后通过链表/红黑树把元素挂在对应哈希值下边。

注意事项:

Set集合在调用add方法的时候,add方法会调用元素的hashCode方法和equals方法,判断元素是否重复。

hashCode()方法用于判断是不是会有哈希冲突

equals()方法用于判断,当发生哈希冲突时,是不是数据也是一样的

什么时候才会重写hashCode方法和equals方法?

用到Set集合的时候,需要重写,因为要保证元素不重复

实例分析:

// 创建HashSet集合对象
HashSet<String> set = new HashSet<>();
String s1 = new String("abc");
String s2 = new String("abc");
set.add(s1);

/*
    add方法会调用s1的hashCode方法,计算字符串"abc"的哈希值,哈希值是【96354】
    在集合中找有没有【96354】这个哈希值的元素,发现【没有】
    所以就会【把s1存储到集合中】
 */

set.add(s2);

/*
    add方法会调用s2的hashCode方法,计算字符串"abc"的哈希值,哈希值是【96354】
    在集合中找有没有【96354】这个哈希值的元素,发现【有】(【哈希冲突】)
    s2会调用equals方法和哈希值相同的元素进行比较,s2.equals(s1),返回【true】
    两个元素的哈希值相同,equals方法返回true,认定【两个元素相同】
    【就不会把s2存储到集合中】
 */

7.HashSet存储自定义类型元素

set集合保证元素唯一:

存储的元素(String, Integer, ..., Student, Person),必须重写hashCode方法和equals方法。

因为不同的元素和相同的元素都可能有相同的哈希值,所以需要根据equals方法进一步判断:

    元素相同,equals返回true
    
    元素不同,equals返回false

8.哈希值

哈希值:是一个十进制的整数,由【系统随机给出】(就是对象的地址值,是一个【逻辑地址】,是模拟出来得到地址,不是数据实际存储的【物理地址】)

在Object类中,有一个方法,可以获取对象的哈希值
int hashCode() 返回对象的哈希码值。

hahCode方法的源码:
    public native int hashCode();
    native:代表该方法调用的是【本地】操作系统的方法

之前说的对象的地址值,其实就是对象的哈希值,
toString()方法,打印的默认就是对象哈希值的十六进制表示,如果想要打印对象的属性,就要重写toString方法

toString()方法的源码:

return getClose().getName() + "@" + Integer.toHexString(hashCode());

其实打印出的地址值,就是对应哈希值的十六进制表示

9.LinkedHashSet集合
java.util.LinkedHashSet,它是【链表和哈希表】组合的一个数据存储结构。

public class LinkedHashSet<E> extends HashSet<E> implements Set<E>

LinkedHashSet集合特点:

底层是一个哈希表(数组 + 链表/红黑树) + 链表 :多了一条链表(记录元素的存储顺序),保证元素有序

四、可变参数

使用前提:

当方法的参数列表数据类型已经确定,但是参数的个数不确定,就可以使用可变参数。

使用格式:

定义方法时使用
修饰符 返回值类型 方法名称(数据类型... 变量名){}

可变参数的原理:

可变参数底层就是一个数组,根据传递参数个数不同,会创建不同长度的数组,来存储这些参数

传递的参数个数,可以是0个(不传递),1,2,...

可变参数的注意事项:

1.一个方法的参数列表,只能有一个可变参数

2.如果方法的参数有多个,那么可变参数必须写在参数列表的末尾

可变参数的特殊写法:

public static void method(Object... obj){}   // Object类型可以接收任意类型的参数

五、Collections

1.java.utils.Collections 是集合工具类,用来对集合进行操作。

2.Collections中常用方法

public static <T> boolean addAll(Collection<T> c, T... elements):往集合中添加一些元素

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>):将集合中元素按照指定规则排序

3.addAll()方法和shuffle()方法

ArrayList<String> list = new ArrayList<>();
//add()方法,一次只能添加一个元素,比较繁琐
//list.add("a");
//list.add("b");
//list.add("c");

// 使用集合工具类Collections中的addAll()方法
// 一次就可以添加多个元素
Collections.addAll(list, "a", "b", "c", "d", "e");

//打乱顺序:打乱集合顺序
Collections.shuffle(list);

4.sort()方法

public static <T> void sort(List<T> list):将集合中元素按照默认规则排序,即升序排序

public static <T> void sort(List<T> list, Comparator<? super T>):将集合中元素按照指定规则排序

注意事项:

1. 不管是Integer还是String,都【实现了一个接口】:Comparable<E>

      public final class Integer extends Number implements Comparable<Integer>
      
      public final class String implements java.io.Serializable, Comparable<String>, CharSequence
      
  在Comparable<E>接口中,【有一个用来排序的方法】:
      public int compareTo(T o);  只有【重写】了这个方法,【才能进行排序】。

      【Integer类和String类都重写了这个方法】

2.sort(List<T> List)【使用前提】:
   被排序的集合里边存储的元素,必须实现Comparable,重写接口中的方法compareTo定义排序的规则。
   【对谁进行排序,泛型就写谁】
   注意:只能传递list,不能传递set

3.Comparable接口的【排序规则】:
  自己(this) - 参数:升序
  参数 - 自己(this):降序

Comparator和Comparable的区别:

Comparable:自己(this)和别人(参数)比较,自己需要实现Comparable接口,重写比较的规则compareTo方法

Comparator:相当于找一个第三方的裁判,比较两个。Comparator排序规则:
    o1 - o2:升序。反之就是降序
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值