Java基础--集合

泛型

泛型介绍

泛型是一种未知的,不确定的数据类型。
泛型主要用于代码重构,代码设计。
举例:

 ArrayList<E>中的E就是一个泛型,是一个未知的数据类型。

泛型虽然是未知的数据类型,但是并不是一直未知,一直不确定,在我们使用一个类的时候,这个泛型表示的数据类型会被确定下来。
举例:

 ArrayList<Student> 此时这个泛型类型E表示的就是Student类型。

泛型也可以省略,如果省略泛型相当于泛型是Object
泛型好处:
  1. 省略了向下转型的代码。
  2. 将运行时期的问题提前到了编译时期。

泛型擦除:Java中的泛型都是伪泛型,只在源代码阶段有效,一旦编译,泛型就会消失.

泛型类

泛型是一种未知的,一种不确定的数据类型。
如果在定义类的时候,类名后面写上,就表示在类的范围内定义了一个泛型类型T(不确定的数据类型T)
这种未知的类型T等到我们使用这个类的时候就会被确定出来。
这个T可以使用其他字母代替。
例如:

 public class myClass<T> {
        
 }

泛型方法

泛型方法的定义格式:

 修饰符 <泛型> 返回值类型 方法名(参数列表) {
  方法体;
  return 返回值;
 }

在方法上定义的泛型,需要等到调用方法的时候才能确定。

泛型接口

如果在定义接口的时候在接口名后面写上,那么这个接口就是一个泛型接口了。
泛型T表示一种未知的不确定的数据类型。
泛型接口的使用:
  1. 实现类实现接口的时候,可以直接明确接口中的泛型类型。
  2. 实现类实现接口的时候,可以不指定接口中的泛型,等到使用实现类的时候再指定。

泛型通配符

泛型之间是没有继承关系的。
比如:

        ArrayList<Object> 不是 ArrayList<String>的父类

如果想要接受任何类型类型的泛型,可以使用泛型通配符
?表示泛型通配符,可以匹配任何类型的泛型。
泛型通配符要放在方法参数位置被动匹配, 不要主动使用。

泛型限定

如果限制泛型通配符的取值范围,那么可以使用泛型限定。
格式:

        <? extends A>:泛型要么是A类,要么是A类的子类。 上限。
        <? super A>: 泛型要么是A类,要么是A类的父类。 下限。

Collection集合(单列集合)

Collection介绍

Collection是所有单列集合的根接口,所有的单列集合都会实现这个接口。
单列集合继承体系:
|–Collection
  |–List
    |–ArrayList
    |–LinkedList
  |–Set
    |–HashSet实现类
    |–TreeSet实现类

常见方法

(常用)public boolean add(E e) : 把给定的对象添加到当前集合中 。
public void clear() :清空集合中所有的元素。
public boolean remove(E e) : 把给定的对象在当前集合中删除。
public boolean contains(Object obj) : 判断当前集合中是否包含给定的对象。
public boolean isEmpty() : 判断当前集合是否为空。
(常用)public int size() : 返回集合中元素的个数。
public Object[] toArray() : 把集合中的元素,存储到数组中

Collection是一个接口,如果要用,需要使用实现类,最常用的实现类是ArrayList
Collection中没有索引有关的方法,因为不是所有的集合都有索引

Iterator迭代器

基本使用

我们之前都是使用for循环结合索引的方式遍历集合,但是这种遍历方式并不适用于所有的集合,因为不是所有的集合都有索引。
有一种通用的遍历集合的方式,可以遍历任何的集合,这个遍历方式迭代器遍历。
迭代器就是遍历集合的一个工具,内部有一个光标,最开始指向了集合的最开头。
在Collection中有一个方法,可以获取迭代器

	Iterator<E> iterator():获取集合的迭代器。

Iterator是一个接口,表示迭代器,里面的方法:

        boolean hasNext():判断是否还有元素可以获取。
        E next():获取光标位置的元素,将光标向后移动。

迭代器的遍历步骤;
  1. 通过集合调用iterator方法获取迭代器对象
  2. 通过迭代器对象调用hasNext方法判断是否有元素可以获取
  3. 如果有元素可以获取,那么就调用next方法获取元素,并将光标向后移动。

并发修改异常

如果在迭代器迭代器遍历的过程中使用集合方法对集合进行增删操作,将来有可能会引发并发修改异常(ConcurrentModificationException)
解决方法:
  1.改为普通for循环进行索引
  2.使用Iterator的remove方法进行删除.
  并发修改异常是由于增删元素后再次遍历调用迭代器的next()方法而引起的.在一些场合下,循环中只删除一个元素,可以使用return来跳出循环,从而避免该异常.

增强for循环

在JDK5的时候,多了一个新的遍历方式叫做增强for循环(foreach),可以遍历数组或集合。
格式:

        for (数据类型 变量名 : 容器) {
            ...
        }

格式解释:
  数据类型:要遍历的容器中保存的是什么类型的数据,这个数据类型就写什么。
  变量名: 表示容器中的每一个元素。
  容器: 要遍历的容器,可以是数组,也可以是集合。

增强for循环遍历数组

增强for是一种语法糖,语法糖指的是本质没有变,只不过写法更加的简洁了。
增强for遍历数组,本质还是使用的普通for循环。

增强for循环遍历集合

增强for遍历集合,本质使用的是迭代器遍历。
增强for的好处和缺点:
  好处: 写法简洁,省去了对索引的操作。
  缺点: 因为省略了索引,所以无法再遍历的过程中操作索引。如果要在遍历过程中操作索引,还是需要使用普通for循环。

List接口

介绍

List是Collection下面的一个子接口,有三个特点:
  1. 有序(按照什么顺序存,就按照什么顺序取)
  2. 有索引(可以根据索引获取元素)
  3. 可以重复(可以存放重复元素)

常见方法

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

ArrayList集合

ArrayList是List接口下面最常用的实现类。
ArrayList内部是在使用数组保存数据。
ArrayList的特点是查询快,增删慢。

LinkedList集合

LinkedList也是List接口下面的实现类
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) :将元素推入此列表所表示的堆栈。

Collections工具类

Collections是一个操作集合的工具类。

 static void shuffle(List<?> list):对集合中的内容打乱顺序。
 static void sort(List list):对集合中的元素进行排序(自然排序)
 static void sort(List list, Comparator c):对集合中的内容进行排序,参数c表示比较器(比较器排序)
 static boolean addAll(Collection c, T... elements):批量添加元素。
	参数c:表示向哪个集合添加元素。
	参数elements:表示要添加那些元素。 该参数是可变参数,可以向该参数位置传递任意个数据。

如果事物本身就具备比较的功能,那么我们可以直接使用sort方法进行排序,这种方式叫做自然排序。
如果事物本身不具备比较的功能,那么我们可以找一个法官帮这些对象进行排序,这种方式叫做比较器排序。

自然排序

 static void sort(List list):对集合中的元素进行排序(自然排序)

注意:
  使用上面这个sort方法排序的集合,里面的泛型必须要实现Comparable接口。
  如果要进行排序,集合中的元素必须要具备比较的功能,如果类实现Comparable接口,那么表示该类的对象就具备了比较的功能。
自然排序指的是事物本身就具备比较的功能,所以我们可以使用上面的sort方法对保存该事物的集合进行排序。
在实现Comparable接口后,需要重写compareTo方法.并且在使用自然排序时,会自动调用该方法.
排序规则:
 &emasp;升序:我(调用者)减他(参数)
 &emasp;降序:他(参数)减我(调用者)
想要根据什么属性进行排序, 就让什么属性相减

//升序
@Override
public int compareTo(Person o) {
    return this.age - o.getAge();
}

比较器排序

static void sort(List list, Comparator c):对集合中内容排序(比较器排序)。参数list是要排序的集合。参数c表示比较器。

如果某个事物本身不具备比较的功能,那么我们就不能使用自然排序的方式对集合直接进行排序了。
如果事物不具备比较的功能,那么我们可以找一个法官(比较器)帮这些对象去比较,这样集合也可以进行排序。
Comparator是一个接口,该接口表示比较器,如果要用需要使用实现类,这个实现类需要我们自己定义。
步骤:
  1. 创建集合并添加元素
  2. 定义一个类,实现Comparator接口。
  3. 重写compare方法,并在该方法中定义比较的规则。
  4. 调用Collections的sort方法,传递集合和比较器进行排序。
compare会在比较器排序时由系统自动调用,compare方法的作用是比较两个对象谁大谁小.
如果返回值是正数, 表示第一个参数对象大于第二个参数对象(o1大于o2)
如果返回值是0, 表示两者相等
如果返回值是负数,表示第一个参数对象小于第二个参数对象(o1小于o2)
公式:
  升序就是一减二
  想要根据什么属性排序,就让什么属性相减.

//升序
@Override
public int compare(Student o1, Student o2) {
    return o1.getAge() - o2.getAge();
}

Set接口

特点

Set是Collection下面的一个子接口。
Set接口有以下特点:
  1. 无索引(不能根据索引获取元素的)
  2. 不可重复(不能保存重复元素)
  3. 无序的(大部分Set集合满足的特点)(按照什么顺序存,不一定按照什么顺序取)
Set是一个接口,如果要用需要使用实现类,Set接口下最常用的实现类是HashSet
HashSet满足Set接口的所有的特点(无序,无索引,不可重复)

set接口遍历

因为Set集合是没有索引的,所以不能使用普通for遍历,可以使用迭代器或增强for遍历,强烈推荐增强for.

哈希值

哈希值其实就是一个int数字,我们可以将哈希值看成对象的一个标识(特征码)
在Object中有一个方法叫做hashCode,可以获取对象的哈希值。

    int hashCode():获取对象的哈希值。

Object中的hashCode方法,哈希值的计算方式是根据对象的地址值计算的。
对象的哈希值根据地址值计算一般来说意义不大,我们更多的是希望哈希值是根据属性计算的,如果两个对象的属性完全相同,哈希值也应该相同。
如果想要自己定义哈希值的计算规则,需要重写hashCode方法。
哈希值是对象的一个标识,但并不是唯一的标识,对象的哈希值允许重复。

哈希表

哈希表就是一个Node数组,只不过数组中的每一个元素都是一个链表.当我们添加元素的时候,会先计算这个对象的哈希值,然后根据这个对象的哈希值,将对象添加到数组的指定位置.
哈希冲突:如果两个对象出现在哈希表数组的同一个元素(链表)位置,这个就是哈希冲突.
在HashMap中,如果两个元素发生了哈希冲突,那么会使用拉链法解决.此时,会将元素放到链表首端,将原来的元素地址记在该元素的Node中.
HashMap中node对象会存有4个内容,hash:哈希值; key:键; value:值; next:下个元素地址
再哈希:再哈希是指对哈希表进行扩容,如果哈希表中桶的使用比例超过了加载因子(0.75),就会进行再哈希.
比如原来哈希表长度为16,当桶的使用数量超过了16*0.75=12,就会进行再哈希对哈希表进行扩容,再哈希之后会变成32.(当创建对象并得到哈希值后,将哈希值对16取余从而将对象添加到对应的位置,当Node数组中有12个位置存有对象,就会进行再哈希)

HashSet保证唯一性

HashSet判断唯一性的过程:
 1.先比较对象的哈希值。
  如果哈希值不同,肯定是不同的对象。
  如果哈希值相同,不一定是同一个对象。
 2.如果哈希值相同,还会调用equals进行比较。
  如果equals的结果是true,表示对象相同。
  如果equals的结果是false,表示对象不同
结论:
如果使用HashSet存储自定义对象并保证唯一性(对象的属性相同就看成是同一个对象),需要同时重写hashCode和equals,缺一不可。

Map集合(双列集合)

介绍

Map是一个接口,我们通常所说的Map集合都是Map接口的实现类集合.
Map接口下最常用的实现类叫做HashMap
双列集合中每个元素都是由键(key)和值(value)组成的(键值对)
一个键对应一个值

常见方法

Map是双列集合,里面的每一个元素都是一个键值对,一个键对应一个值。
Map<K,V>有两个泛型,K表示键的数据类型, V表示值的数据类型。
Map中常见的方法:

    (常用)V put(K key, V value):向Map集合中添加元素。key表示键,value表示值。 在调用put方法添加元素时,如果该键已经存在,那么会使用新的值覆盖掉原来的值。
    (常用)V get(Object key):根据键获取对应的值。
    boolean containsKey(Object key):判断Map集合中是否包含指定的键。
    V remove(Object key):根据键删除整个的元素。返回被删除的元素的值。

Map集合遍历

Map集合有两种遍历方式
  keySet方式(键找值)【推荐】
  entrySet方式(键值对)

keySet方法

Map集合不能通过迭代器或者增强for直接进行遍历。
如果要遍历Map集合,我们可以获取到Map集合中所有的键,将所有的键放入到Set集合中, 然后遍历Set集合,拿到集合中的每一个键,然后根据键去Map集合中获取对应的值。
Map中有一个方法叫做keySet,可以获取到所有的键.

	Set<K> keySet():获取Map集合中所有的键,并放入到Set集合中返回。

keySet遍历步骤:
  1. 调用Map集合的keySet方法获取到所有的键,然后放入到Set集合中。
  2. 遍历Set集合,拿到里面的每一个键。
  3. 调用Map集合的get方法,根据键获取对应的值。

entrySet方法

Map集合还有一种遍历方式是通过Entry对象的方式进行遍历。
Map集合中每一个元素其实都是一个键值对,每一个键值对都是一个Entry对象。
如果要通过Entry对象的方式遍历Map集合,我们需要先获取到Map集合中所有的Entry对象,将这些Entry对象放入到一个Set集合,然后遍历Set集合,拿到Set集合中每一个Entry对象,根据该Entry对象获取键以及对应的值。
在Map集合中有一个方法叫做entrySet,可以获取到所有的Entry对象.

    Set<Map.Entry<K,V>> entrySet():获取到所有的Entry对象并放入到Set集合中返回。

Entry中获取键和值的方法:

	K getKey():获取到Entry对象中的键。
	V getValue():获取Entry对象中的值。

entrySet遍历步骤:
  1. 调用Map集合的entrySet方法获取到所有的Entry对象并放入到Set集合中返回
  2. 遍历Set集合,拿到里面的每一个Entry对象
  3. 调用Entry对象的getKey和getValue获取到键和值

HashMap判断键的唯一性

HashMap保证唯一性(键)的方式和HashSet是一模一样。 因为HashSet内部就是在使用HashMap保存数据。
判断唯一性的方式:
  1. 先比较两个对象的哈希值。
    如果对象的哈希值不同,肯定是不同的对象。
    如果对象的哈希值相同,不一定是同一个对象。
  2. 然后比较两个对象的equals方法
    如果equals方法结果是true,表示两个对象相同。
    如果equals方法结果是false,表示两个对象不同。
如果HashMap要保证键的唯一性(属性相同就看成是同一个对象),需要同时重写hashCode和equals方法.

LinkedHashMap

LinkedHashMap是Map接口下一个不常用的实现类.
内部除了有一个哈希表之外还有一个链表,链表可以保证有序.

TreeMap

TreeMap也是Map接口下的实现类。
TreeMap可以根据键进行自然排序或者比较器排序。
构造方法:

    TreeMap():根据键进行自然排序
    TreeMap(Comparator comparator):根据键进行比较器排序。参数是比较器对象
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值