Day22.List集合、ArrayList、LinkedList、集合工具类、泛型、通配符

目录

List接口

ArrayList(最常用)

LinkedList

Collections 集合工具类

使用泛型

泛型(Generics)

自定义泛型

类型通配符


List接口

  • List接口存储元素特点:有序、可重复

  • List接口特有方法

 voidadd(int index, E element)
          在列表的指定位置插入指定元素(可选操作,效率较低)。
 Eget(int index)
          返回列表中指定位置的元素。
 intindexOf(Object o)
          返回此列表中第一次出现的指定元素的索引;如果此列表不包含该元素,则返回 -1。
 intlastIndexOf(Object o)
          返回此列表中最后出现的指定元素的索引;如果列表不包含此元素,则返回 -1。
 Eremove(int index)
          移除列表中指定位置的元素(可选操作)。
 Eset(int index, E element)
          用指定元素替换列表中指定位置的元素(可选操作)。
  • 获取List集合元素
        //通过下标获取元素
        Object ob = myList.get(1);
        System.out.println(ob);

        //因为有下标,所以List集合有自己特有的遍历方式
        for (int i = 0; i < myList.size(); i++) {
            Object obj = myList.get(i);
            System.out.println(obj);
        }

ArrayList(最常用)

  1. ArrayList默认初始化容量为10 ( 先创建了一个长度为0的数组,当添加子一个元素时,初始化容量10)

  2. ArrayList 底层是Object类型的数组 Object[];

  3. ArrayList 集合的扩容,增长到元容量的1.5倍

  4. 效率优化:尽量预估元素个数,给定一个初始化容量,减少扩容

  5. ArrayList是非线程安全的

  • 数组优点:检索效率比较高 (每个元素占用空间大小相同,内存地址是连续的。)

  • 数组缺点:随机增删元素效率比较低 (向数组末尾添加元素效率很高,不受影响)

  • 因为每次增删需要移动数组,保证数组连续性
    • 无法存储大数据量 (很难找到一块非常巨大的连续内存空间)

  • 构造方法
ArrayList()
          构造一个初始容量为 10 的空列表。
ArrayList(int initialCapacity)
          构造一个具有指定初始容量的空列表。
ArrayList(Collection<? extends E> c)
          构造一个包含指定 collection 的元素的列表,这些元素是按照该 collection 的迭代器返回它们的顺序排列的。

booleanaddAll(Collection<? extends E> c)

按指定集合的Iterator返回的顺序将指定集合中的所有元素追加到此列表的末尾。

voidforEach(Consumer<? super E> action)

Iterable的每个元素执行给定的操作,直到所有元素都被处理或动作引发异常。

Eget(int index)

返回此列表中指定位置的元素。

booleanremoveAll(Collection<?> c)

从此列表中删除指定集合中包含的所有元素。

booleanremoveIf(Predicate<? super E> filter)

删除满足给定谓词的此集合的所有元素。

LinkedList

  • LinkedList 集合底层为双向链表,内存地址不连续
  • 优点: 随机增删元素效率较高。(链表上大元素在空间存储上内存地址不连续,因此增删元素时不会有大量元素位移)

  • 缺点:查询检索效率较低。(不能通过数学表达式计算被查找元素,每一次查找都是从头开始遍历)

  • LinkedList是非线程安全的

  • LinkedList集合有初始化容量吗?没有,最初这个链表没有任何元素。first和last引用都是null;

  • 特有方法 (首尾相关)
 voidaddFirst(E e)
          将指定元素插入此列表的开头。
 voidaddLast(E e)
          将指定元素添加到此列表的结尾。
 EgetFirst()
          返回此列表的第一个元素。
 EgetLast()
          返回此列表的最后一个元素。
了解↓
 EremoveFirst()
          移除并返回此列表的第一个元素。
 EremoveLast()
          移除并返回此列表的最后一个元素。
 booleanremoveFirstOccurrence(Object o)
          从此列表中移除第一次出现的指定元素(从头部到尾部遍历列表时)。
 booleanremoveLastOccurrence(Object o)
          从此列表中移除最后一次出现的指定元素(从头部到尾部遍历列表时)。
 booleanofferFirst(E e)        
          在此列表的开头插入指定的元素。 
 booleanofferLast(E e)
          在此列表末尾插入指定的元素。
实现队列结构 先进先出 (消息队列)
 booleanoffer(E e)        //入队
          将指定元素添加到此列表的末尾(最后一个元素)。
 Epoll()            //出队
          获取并移除此列表的头(第一个元素)
实现栈形结构 先进后出 (压栈弹栈)
 voidpush(E e)         //压栈
          将元素推入此列表所表示的堆栈。
 Epop()             //弹栈
          从此列表所表示的堆栈处弹出一个元素。
 Epeek()
          获取但不移除此列表的头(第一个元素)。
LikedList 专属迭代器 (略)  ListIterator 接口下
ListIterator<E>listIterator(int index)
          返回此列表中的元素的列表迭代器(按适当顺序),从列表中指定位置开始。
  • 代码
        List list = new LinkedList();
        list.add("123");
        list.add("456");
        for (int i = 0; i < list.size(); i++) {
            System.out.println(list.get(i));
        }
  • Vector(不常用)
  • 底层也是数组,初始化容量:10,扩容之后是原容量的二倍。(ArrayList扩容为1.5倍)
  • Vector是线程安全的,但效率较低。由于现在有其他方案保证线程安全,较少使用。
        List list = new Vector();
        list.add("123");
        list.add("456");
  •  LinkedList本身不具备索引,但是通过算法,实现了遍历查询功能。

源码↓

Collections 集合工具类

java.util.Collections;

static<T> booleanaddAll(Collection<? super T> c, T... elements)
          将所有指定元素添加到指定 collection 中。
static<T extends Comparable<? super T>>
void
sort(List<T> list) //(要求T实现了Comparable接口)
          根据元素的自然顺序 对指定列表按升序进行排序。
static<T> voidsort(List<T> list, Comparator<? super T> c) //传入排序规则
          根据指定比较器产生的顺序对指定列表进行排序。
static<T> intbinarySearch(List<? extends Comparable<? super T>> list, T key)              //(要求T实现了Comparable接口)
          使用二分搜索法搜索指定列表,以获得指定对象。
static<T> intbinarySearch(List<? extends T> list, T key, Comparator<? super T> c)        //传入排序规则,在此规则下二分查找
          使用二分搜索法搜索指定列表,以获得指定对象。
static<T extends Object & Comparable<? super T>> Tmax(Collection<? extends T> coll)
          根据元素的自然顺序,返回给定 collection 的最大元素。
static<T> Tmax(Collection<? extends T> coll, Comparator<? super T> comp)
          根据指定比较器产生的顺序,返回给定 collection 的最大元素。
static<T extends Object & Comparable<? super T>>
T
min(Collection<? extends T> coll)
          根据元素的自然顺序 返回给定 collection 的最小元素。
static<T> Tmin(Collection<? extends T> coll, Comparator<? super T> comp)
          根据指定比较器产生的顺序,返回给定 collection 的最小元素。
static voidreverse(List<?> list)
          反转指定列表中元素的顺序。
static voidshuffle(List<?> list) //洗牌 打乱顺序
          使用默认随机源对指定列表进行置换。
static voidswap(List<?> list, int i, int j)
          在指定列表的指定位置处交换元素。
static intfrequency(Collection<?> c, Object o)
          返回指定 collection 中等于指定对象的元素数。
static<T> booleanreplaceAll(List<T> list, T oldVal, T newVal)
          使用另一个值替换列表中出现的所有某一指定值。

static

<T> List<T

synchronizedList(List<T> list)
          返回指定列表支持的同步(线程安全的)列表。
把List修改为不可修改的集合
static<T> Collection<T>unmodifiableCollection(Collection<? extends T> c)
          返回指定 collection 的不可修改视图。
static <T> List<T>unmodifiableList(List<? extends T> list)
          返回指定列表的不可修改视图。

如何将线程不安全的ArrayList集合转换为线程安全的呢?

        List list = new ArrayList();    //非线程安全
        Collections.synchronizedList(list); //无法看效果,因为多线程没学
        //listj集合就变成线程安全的了
        list.add("123");
        list.add("456");

排序:

        List<String> list = new ArrayList();
        //排序 注:需要list集合中元素实现了Comparable接口,或传入Comparator比较器
        list.add("abf");
        list.add("abc");
        Collections.sort(list);

        //对Set集合如何排序?
        Set<String> set = new HashSet<>();
        set.add("abc");
        set.add("bbc");
        //将Set集合转换成List集合
        List<String> myList = new ArrayList<>((set));
        Collections.sort(myList)

使用泛型

  • 泛型之前

    在面向对象编程语言中,多态算是一种泛化机制。例如,你可以将方法的参数类型设置为基类,那么该方法就可以接受从这个基类中导出的任何类作为参数,这样的方法将会更具有通用性。此外,如果将方法参数声明为接口,将会更加灵活。

    通过继承设计通用程序
    在Java增加泛型类型之前,通用程序的设计就是利用继承实现的,例如,ArrayList类只维护一个Object引用的数组,Object为所有类基类

这显然不是我们所期望的,如果程序有潜在的错误,我们更期望在编译时被告知错误,而不是在运行时报异常。

public class test {
    public static void main(String[] args) {
        List list = new ArrayList();
        Cat cat = new Cat();
        Dog dog = new Dog();
        list.add(cat);
        list.add(dog);


        Iterator it = list.iterator();
        while(it.hasNext()){
            //取出语法,通过迭代器取出的就是Object
            Object obj = it.next();
            //obj中没有move方法,无法调用,需要向下转型!
            if(obj instanceof Animal){
                Animal a = (Animal)obj;
                a.move();                   //动物在移动
            }
        }
    }
}
class Animal{
    public void move(){
        System.out.println("动物在移动");
    }
}
class Cat extends Animal{
    public void shout(){
        System.out.println("喵");
    }
}
class Dog extends  Animal{
    public void shout(){
        System.out.println("汪");
    }
}
  • 面临的问题
  • 当我们获取一个值的时候,必须进行强制类型转换。
  • 当我们插入一个值的时候,无法约束预期的类型。假定我们预想的是利用stringValues来存放String集合,因为ArrayList只是维护一个Object引用的数组,我们无法阻止将Integer类型(Object子类)的数据加入stringValues。然而,当我们使用数据的时候,需要将获取的Object对象转换为我们期望的类型(String),如果向集合中添加了非预期的类型(如Integer),编译时我们不会收到任何的错误提示。但当我们运行程序时却会报异常:

Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
at generic.BeforeGeneric.main(BeforeGeneric.java:24)

这显然不是我们所期望的,如果程序有潜在的错误,我们更期望在编译时被告知错误,而不是在运行时报异常。

泛型(Generics)

泛型就是参数化类型,也就是说把我们要操作的类型作为了一个参数,比如我们创建集合的时候,允许我们可以指定集合中元素的数据类型。

优点:使集合中存储的元素类型统一,把运行期可能存在的强制转换风险问题,提前到编译期解决。简化额代码

缺点:导致集合中存储元素缺乏多样性

大多数业务中,集合中存储的元素类型是统一的,所以这种泛型特性被大家所认可

public class test {
    public static void main(String[] args) {
        //指定的list集合中只能存储Animal,否则会编译报错
        List<Animal> list = new ArrayList<Animal>();
        list.add(new Cat());
        list.add(new Dog());
        //表示迭代器迭代的是Animal类型
        Iterator<Animal> it = list.iterator();
        while(it.hasNext()){
            Animal a = it.next();
            //这里不需要强制类型转换
            a.move();
            if(a instanceof Cat){
                Cat c = (Cat)a;
                c.shout();
            }
        }
    }
}
class Animal{
    public void move(){
        System.out.println("动物在移动");
    }
}
class Cat extends Animal{
    public void shout(){
        System.out.println("喵");
    }
}
class Dog extends  Animal{
    public void shout(){
        System.out.println("汪");
    }
}

在JDK8之后,引入了自动类型判断机制(又称为钻石表达式)

List<String> stringList = new ArrayList<String>();

List<String> stringList = new ArrayList<>();        //自动类型判断
    public static void main(String[] args) {
        //         ArrayList<这里的类型会自动判断>
        List<Animal> list = new ArrayList<>();

        list.add(new Cat());
        list.add(new Dog());
        Iterator<Animal> it = list.iterator();
        while(it.hasNext()){
            Animal a = it.next();
        }
    }

自定义泛型

泛型:可以指定不确定的数据类型。

  • 泛型类

【修饰符】 class 类名<类型变量列表> 【extends 父类】 【implements 父接口们】{
}

  • 泛型接口

【修饰符】 interface 接口名<类型变量列表> 【implements 父接口们】{
}

  • 泛型方法

【修饰符】 <类型变量列表> 返回值类型 方法名(【形参列表】)【throws 异常列表】{   //...}

  • 自定义泛型类

//1. Tiger 后面泛型,所以我们把 Tiger 称为自定义泛型类
//2. E,T 泛型的标识符,一般是单个大写字母
//3. 泛型标识符可以有多个
//4. 普通成员类可以使用泛型 (属性、方法)
//5. 使用泛型的数组,不能初始化、
//6. 被静态修饰无法使用泛型
class Tiger<E,T>{
    String name;
    E e;    //属性使用到方法  Element,元素 Type,类型
    T t;

    public Tiger(String name, E e, T t) {//构造器使用泛型
        this.name = name;
        this.e = e;
        this.t = t;
        T[] ts ;//= new T[8];  因为不能确定T的类型,数组创建无法在内存开辟空间
    }

    //static E r2;                //因为静态是和类相关的,在类加载时,对象还没有创建
    //public static void t1(T t){}//所以,如果静态方法和静态属性使用了泛型,Jvm无法完成初始化

    public E getE() {     //返回类型使用泛型
        return e;
    }
    public void setR(E e) {//方法使用泛型
        this.e = e;
    }
}
  • 自定义泛型接口

public interface test2 <E,T>{
    int e = 10;
    //T neme; 不能这样使用 接口定义的属性都是常量 final static
    void hi(E e);

    default void ss (T t){  //在jdk8中,使用默认方法也可以使用泛型
    }
}
//在继承接口时,指定泛型类型  (如果没有指定,则为Object类型)
interface in extends test2<String,Double>{
}
//实现接口时,指定泛型类型,方法自动重写
class in2 implements test2<String,Integer>{
    @Override
    public void hi(String s) {
    }
    @Override
    public void ss(Integer integer) {
        test2.super.ss(integer);
    }
}
  • 自定义泛型方法

public class test2{
    public static void main(String[] args) {
        Car car = new Car();
        car.fly("aaa",100);//当我们调用方法时,传入参数,编译器就会确定类型
    }
}
//泛型方法,可以定义在普通类中,也可以定义在泛型类中
class Car{    //普通类
    public void run(){      //普通方法
    }
    public <E,T> void fly(E e,T t){    //泛型方法 !注意声明位置!

    }
}
class fish<E,T>{         //泛型类
    public void run(){}     //普通方法
    public<U,M> void eat(U u,M m){  //泛型方法
    }
    //注:下面的hi方法不是泛型方法,而是hi方法使用了类声明的泛型
    public void hi(E e){
    }
}

类型通配符 <?>

部分通配符

*  星号,    匹配任何字符 (如导包:java,util.* 、查询所有SELECT*
? 问号,    匹配任意一个字符         (如正则表达式?代表0或1次)
[] 中括号,匹配括号中的一个字符  ( 如正则表达式 [a-z] )
ls *会列出当前目录下所有文件,*匹配了所有的文件名,而ls *a匹配所有a开头的文件。
需要注意的是,如果当前目录下有目录名匹配成功,会列出该目录下所有文件

*可以代替任意个数字符,?只能代替一个。
 

当我们无法确定泛型内使用的具体类型,可以使用<?>通配符

<?> 任意类型   

<? extends 上限>  设定通配符上限

<? super 下限>      设定通配符下限

public class temp2 {
    public static void main(String[] args) {
        //<?>:表示任意类型
        //List<Object> list = new ArrayList<Integer>(); 报错,左右<>需要统一
        List<?> list1 = new ArrayList<Integer>();
        test1(list1);

        //<? extends Animal>设定通配符上限
        List<? extends Animal> list2;
        list2 = new ArrayList<Animal>();    //默认Animal
        list2 = new ArrayList<>(Cat);       //可以为Animal子类

        //<? extends Animal>设定通配符下限
        List<? super Animal> list3;
        list3 = new ArrayList<Animal>();    //默认Animal
        list3 = new ArrayList<Object>();    //可以为Animal父类

    }
    //需求:参数List的泛型是任意类型
    //不适合获取,也不能添加 通常用于反转、赋值、打乱顺序等操作
    //例如: static void reverse(List<?> list) 反转操作
    public static void test1(List<?> list){
        //list.add(new Cat());  //编译期无法确定类型,无法添加
        //list.add(new Dog());
        //list.add(new Object());

        //不适合获取
        Object o = list.get(0);
    }
    
    //需求2:参数list的泛型类型任意,但是最大是Animal类型
    //适合获取数据,不能添加数据
    //例:二分查找static <T> int binarySearch(List<? extends Comparable<? super T>> list, T key)

    public static void test2(List<? extends Animal> list){
        //不能添加数据 (有可能是Cat、Dog、Animal,编译期无法确定类型无法添加)
        //list.add(new Cat());
        //list.add(new Dog());
        //list.add(new Animal());
        //list.add(new Object());
        //适合获取
        Animal animal = list.get(0);    //一定是Animal

    }

    //需求3:参数list的泛型类型任意,但是最小是Animal类型
    //适合添加数据,不适合获取数据
    //例如: static <T> boolean addAll(Collection<? super T> c, T... elements)
    public static void test3(List<? super Animal> list){
        list.add(new Cat());      //编译期确定一定为Animal子类,可以添加
        list.add(new Dog());
        list.add(new Animal());
        //list.add(new Object());   //编译期不确定Animal还是其父类,无法添加

        Object object = list.get(0);//不适合返回数据,返回值是Object
    }
}

class Animal {
}
class Cat extends Animal{}

class Dog extends Animal{}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值