JavaSE基础-集合之Collection

448 阅读9分钟

一、Collection集合

0. Collection接口

0) .概述

a.定义的是所有单列集合中共性的方法
b.所有的单列集合都可以使用共性的方法
c.没有带索引的方法(因为Set集合没有索引)

1) .学习方法

a.学习顶层:学习顶层接口/抽象类中共性的方法
b. 使用底层:顶层无法实例化,通过使用底层熟悉规则

2) .常用方法

1.public boolean add(E e)
2.public void clear()
3.public boolean remove(E e)
4.public boolean contains(E e)
5.public boolean isEmpty()
6.public int size()
7.public object[] toArray

1. List接口

0. 概述

a. 有序(存储和取出元素顺序相同)
b. 允许存储重复的元素
c. 有索引,可以使用普通的for循环遍历

2. Set接口

0.概述

a.不允许存储重复元素
b. 无索引(不能使用普通的for循环遍历)

二、迭代器

1. Iterator接口概述

Iterator迭代器,即Collection集合元素的通用获取方式

在取元素之前先要判断集合中有没有元素,如果有,就把这个元素取出来,继续再判断,如果还有就再取出来,一直把集合中的所有元素全部取出。这种取出方式专业术语称为迭代

2. 常用方法

  1. boolean hasNext()判断是否有元素
  2. E next()取出集合中的下一个元素

3. 注意事项

Iterator迭代器,是一个接口,无法直接使用,需要使用Iterator接口的实现类对象,获取实现类的方式比较特殊

Collection接口有一个方法,为iterator(),这个方法返回的就是迭代器实现类的对象 Iterator<E>(其中的泛型E根据集合中的数据类型而定),iterator()返回在此Collection的元素上进行迭代的迭代器

4. 使用步骤(重点)

/*
1. 使用集合中的方法iterator()获取迭代器的实现类对象,使用Iterator接口接收(多态)
2. 使用Iterator接口中的方法hasNext()判断还有没有下一个元素(最开始,指针指向-1位置)
3. 使用Iterator接口中的方法next()取出集合中的下一个元素(同时会将指针向后移动一位) 
*/

5.增强for循环(foreach()语句)

0. 底层

增强for循环:底层使用的也是迭代器,使用for循环的格式,简化了迭代器的书写

1. 特性

Collection<E> extends Iterator<E>所有的单列集合都可以使用增强for循环public interface Iterator<T>实现这个接口允许对象成为foreach语句的目标(T->Type , E->Element)

2. 作用

用来遍历集合数组

3. 格式:

for(集合/数组的数据类型 变量名:集合名/数组名){
    sout(变量名);  
}

三、泛型

1. ArrayList特点

可以存储多种类型数据

//可以实现,但风险更大,因此多使用泛型
ArrayList list = new ArrayList();
list.add("abc");
list.add(11);

2. 泛型使用

1).定义含有泛型的类

泛型定义在类名后,用<>标识

创建对象的时候确定泛型的数据类型,如果创建时不标明,则默认为是Object类型

public class GenericClass<E>{
    private E name;
    ...
}

2).定义含有泛型的方法

泛型定义在方法的修饰符和返回值类型之间

调用方法时确定泛型类型即可

public <M> void method(M m){
    System.out.println(m);
}

3). 实现含有泛型的接口

泛型定义在接口之后,用 <>标识

a. 可以在实现类中确定类型 b. 也可以在创建实现类对象时确定类型(参考ArrayList<>)

a .在实现类中确定类型


public class GenericInterfaceImpl implements GenericInterface<String>{
    @Override
    public void method(String s){
        System.out.prinln(s);
    }
}

b. 在创建实现类对象时确定类型

public class GenericInterfaceImpl<I> implements GenericInterface<I>{
    @Override
    public void method(I i){
        System.out.println(i);
    }
}

3. 泛型通配符

? : 代表任意的数据类型,未要求统一数据类型(区别于T)

1). 使用方式

不能创建对象使用,只能作为方法的参数使用,定义存储时都不能使用

public class DemoGeneric{
    public static void main String(String[] args){
        ArrayList<Integer> list1 = new Arraylist<>();
        list1.add(1);
        list1.add(2);
        
        Arraylist<String> list2 = new Arraylist<>();
        list2.add("a");
        list2.add("b");
    }
    /*
    定义一个方法,可以遍历所有类型的ArrayList集合
    
    不能用Object类似多态一样,因为泛型没有继承概念
    */
    public static void printArray(ArrayList<?> list){
        Iterator<?> it = list.iterator();
        while(it.hasNext()){
        Object o = it.next();(多态)
        System.out.println(o);//直接打印o.toString()输出了子类o的内容
    }
        
    
}

2). 高级使用

泛型的上限限定: ? extends E:
代表使用的泛型只能是E类型的子类/本身

泛型的下限限定:? super E :
代表使用的泛型只能是E类型的父类/本身

四、List接口实现类

List extends Collection

1. ArrayList

1).List接口的大小可变数组(底层实现)的实现

具有查询快、增删慢的特点,大量查询情境下适用。

2).此实现不是同步的,是多线程的

2. LinkedList

1).List接口的双向链表的实现

具有查询慢、增删快的特点,大量增删情境下适用。

2).此实现不是同步的,是多线程的

3).具有很多操作表头和结尾元素的方法

getLast(),addFirst(),push(),pop()...

3.Vector

1).List接口的大小可变数组(底层实现)的实现

具有查询快、增删慢的特点。

2).此实现是同步的,不同于以上两个实现类,是非多线程的

五、哈希值

1.定义

系统随机给出的十进制整数,由系统随机给出 ,就是对象的地址值,但是是一个逻辑地址(模拟出来的),不是实际存储的物理地址,对象的toString()方法同样不是实际存储的物理地址因此不可看到两个对象的toString()hashCode()返回相同值,便认定两个对象的地址值相同

2.方法

Object类中有一个方法int hashCode()返回该对象哈希码值

3. 方法源码

/*
native:代表该方法调用的是本地操作系统的方法
*/
public native int hashCode(){
    public native int hashCode();
}

4.使用说明

public class DemoHashCode{
    public static void main(String[] args){
        Person p1 = new Person();
        int h1 = p1.hashCode();
        System.out.println(h1);//1967205423
        
        Person p2 = new Person();
        int h2 = p2.hashCode();
        System.out.println(h2);//42121758
        /*
            toString()方法的源码:
            return getClass().getName()+"@"+Integer.toHexString(hashCode());
        */
        //com.itheima.demo03.hashCode.Person@75412c2f
        //是1967205423的十六进制表示
        System.out.println(p1);
        
       /*
       即使重写Person的hashCode()的方法,让其返回1
       */
      
      //此处依然返回false,因为hashCode是模拟地址
      System.out.println(p1 == p2);
      
      
      /*
      String类重写了Object的hashCode()方法
      
      */
      String s1 = new String("abc");
      String s2 = new String("abc");
      
      System.out.println(s1.hashCode());//96354
      System.out.println(s2.hashCode());//96354
      //特例,内容不同,哈希值相同
      System.out.println("重地".hashCode());//1179395
      System.out.println("通话".hashCode());//1179395
      
    }
}

5.一些疑问

1).String类重写了equals()方法,为什么还要重写hashCode()方法?

hashCode()方法是利用哈希算法(散列算法),将对象数据根据其特征使用特定算法定义到一个地址上,这样后进入的数据只要看对应的hashCode地址上是否有值,再用equals()方法比较,如果直接用equals()方法比较,比较次数多,降低效率

2).hashCode重写为什么*31

31*i == (i<<5)-i,方便机器实现,同时记住31是一个奇素数,好计算,左移5位性能最好

6.跪一个帖子的回复,直接引用了

1.hashcode是用来查找的,如果你学过数据结构就应该知道,在查找和排序这一章有 例如内存中有这样的位置
0 1 2 3 4 5 6 7
而我有个类,这个类有个字段叫ID,我要把这个类存放在以上8个位置之一,如果不用hashcode而任意存放,那么当查找时就需要到这八个位置里挨个去找,或者用二分法一类的算法。 但如果用hashcode那就会使效率提高很多。 我们这个类中有个字段叫ID,那么我们就定义我们的hashcode为ID%8,然后把我们的类存放在取得得余数那个位置。比如我们的ID为9,9除8的余数为1,那么我们就把该类存在1这个位置,如果ID是13,求得的余数是5,那么我们就把该类放在5这个位置。这样,以后在查找该类时就可以通过ID除 8求余数直接找到存放的位置了。

2.但是如果两个类有相同的hashcode怎么办那(我们假设上面的类的ID不是唯一的),例如9除以8和17除以8的余数都是1,那么这是不是合法的,回答是:可以这样。那么如何判断呢?在这个时候就需要定义 equals了。 也就是说,我们先通过 hashcode来判断两个类是否存放某个桶里,但这个桶里可能有很多类,那么我们就需要再通过 equals 来在这个桶里找到我们要的类。 那么。重写了equals(),为什么还要重写hashCode()呢? 想想,你要在一个桶里找东西,你必须先要找到这个桶啊,你不通过重写hashcode()来找到桶,光重写equals()有什么用啊

7.总结

1.使用hashcode()方法提前校验,可以避免每一次比对都调用equals()方法,提高效率
2.保证是同一个对象,如果重写了equals方法,而没有重写hashcode方法,会出现equals相等的对象,hashcode不相等的情况,重写hashcode方法就是为了避免这种情况的出现。
3. 一句话,hashCode()的存在是为了方便查找

六、Set接口实现类

Set extends Collection
常用实现类有

1. HashSet

1).HashSet实现Set接口,由哈希表(实际上是一个HashMap实例)支持,不保证有序;

因为底层是哈希表结构,因此查询速度非常快

2).此实现不是同步的,是多线程的

3).不存储重复元素的原理

前提: 存储的元素必须重写hashCode()equals()方法

Set集合在调用add()方法的时候,add()方法会调用元素的hashCode()方法和equals()方法,判断元素是否重复:
判断时先调用hashCode()方法,如果返回值相等(哈希冲突)则调用equals()方法判断元素值是否相同

2. LinkedHashSet

1). 有序、Set接口的哈希表和链表实现

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

七、集合工具类Collections

1.Collections概述

java.util.Collections用来对集合进行操作

2.常用方法

addAll(Collection<T>),
shuffle(List<?> list)打乱顺序,
sort(List<T> list)默认规则排序,
sort(List<T> list,Comparator<? super T>)指定规则排序

?泛型占位符未要求统一类型,T泛型占位符要求同一种类型

3. 指定规则排序方法

//匿名内部类实现接口
Collections.sort(list,new Comparator<Integer>(){
    @Override
    public int compare(Integer o1,Integer o2){
        //return o1-o2;//升序
        return o2-o1;//降序
    }
});