4 Day14--泛型+集合1

4.1  泛型

4.1.1     概念

public class LinkedList<E>

    extends AbstractSequentialList<E>

    implements List<E>, Deque<E>, Cloneable, java.io.Serializable{}

public interface Deque<E> extends Queue<E> {}

public interface Queue<E> extends Collection<E> {}

public interface Collection<E> extends Iterable<E> {}

我们上面的代码中出现的<?>是什么东西呢 它叫泛型,常用来和集合对象一同使用,所以我们在开始学习集合之前,必须先了解下什么是泛型。而且泛型概念非常重要,它是程序的增强器,它是目前主流的开发方式。

泛型是(Generics)是JDK1.5 的一个新特性,其实就是一个『语法糖』,本质上就是编译器为了提供更好的可读性而提供的一种小手段,小技巧,虚拟机层面是不存在所谓『泛型』的概念的。

4.1.2     作用

l  通过泛型的语法定义,约束集合元素的类型,进行安全检查,把错误显示在编译期

l  代码通用性更强,后面有案例

l  泛型可以提升程序代码的可读性,但它只是一个语法糖(编译后这样的东西就被删除,不出现在最终的源代码中),对于JVM运行时的性能是没有任何影响的。

4.1.3     泛型示例

我们创建一个ArrayList,上面看到eclipse提示有个黄线,什么意思呢?

ArrayList is a raw type. References to generic type ArrayList<E> should be parameterized.

ArrayList使用了泛型,在声明时需指定具体的类型<E>。

那我们把这个<>里的方式就称为泛型。上面的泛型有什么作用呢?就是在编译阶段就检查我们传入的参数类型是否正确。

有了泛型,我们可以看到人家要求存放String,而我故意存放的整数100,所以eclipse提示我们错误:

The method add(int, String) in the type List<String> is not applicable for the arguments (int)。

类型List<String>的add方法要求增加的类型为String类型,不正确不能存入。

4.1.4     泛型声明

泛型可以在接口、方法、返回值上使用:

java.util.List泛型接口/类:

public interface Collection<E> {}

泛型方法的声明:

public <E> void print(E e) {}

在方法返回值前声明了一个<E>表示后面出现的E是泛型,而不是普通的java变量。

4.1.5     常用名称

l  E - Element (在集合中使用,因为集合中存放的是元素)

l  T - Type(Java 类)

l  K - Key(键)

l  V - Value(值)

l  N - Number(数值类型)

l  ? - 表示不确定的java类型

4.1.6     用途:编译时类型检查

package seday12new;

 

import java.util.ArrayList;

import java.util.Iterator;

import java.util.List;

 

public class Test1 {

    public static void main(String[] args) {

       int[] a = new int[3];

       a[0]=1;

       a[1]=2;

       //int类型的数组,规定了数组里的数据类型,类型不对就报错。

//     a[2]="hello";

      

       //1,泛型的标志<>

       //2,泛型的好处:规定了数据的类型,不能想放什么数据就放什么类型,要遵守泛型规定的类型

       //3,泛型的数据类型只能是引用类型,不能是基本类型

       List<Integer> list = new ArrayList<Integer>();

       list.add(1);

       list.add(2);

//4,如果类型不对,把运行时期才会 报的错ClassCastException直接在编译时期就报出来

//     list.add("a");

//     list.add('b');

      

       Iterator it = list.iterator();

       while(it.hasNext()) {

           Integer s = (Integer) it.next();

           System.out.println(s);

       }

      

    }

}

4.1.7     用途:代码通用性更强

传统方式通过重载多态实现,方法同名,参数类型不同。

package javase.base.gennarics;

 

public class TestOldStyle {

    public static void print(Integer[] dArray) {

       for( Integer d : dArray) {

           System.out.println(d);

       }

    }

   

    public static void print( String[] sArray) {

       for( String s : sArray) {

           System.out.println(s);

       }

    }

   

    public static void main(String[] args) {

       Integer[] scores = new Integer[]{100,98,80};

       String[] names = new String[]{"语文","数学","英语"};

      

       TestOldStyle.print(scores);

       TestOldStyle.print(names);

    }

}

泛型方式

package javase.base.gennarics;

 

public class TestGenarics {

    public static <E> void print(E[] arr) {

       for(E e : arr) {

           System.out.println(e);

       }

    }

   

    public static void main(String[] args) {

       Integer[] scores = new Integer[]{ 100,98,80 };

       String[] names = new String[]{ "语文","数学","英语" };

       Double[] moneys = new Double[] { 10.1,20.2,30.3 };

      

       TestGenarics.print(scores);

       TestGenarics.print(names);

       TestGenarics.print(moneys);

    }

}

4.1.8     类型擦除

泛型只是在编译期间生存,编译后就被干掉了,真正运行时,大多情况下取而代之的是Object

下面的代码利用了jdk提供的强大的反射功能,后续会专门详细讲解,今天先初体验下其强大的功能。

package javase.generics;

 

import java.lang.reflect.Method;

import java.util.ArrayList;

import java.util.List;

 

//泛型类型擦除

public class TestGenerics {

    public static void main(String[] args) throws Exception {

       List<Integer> list = new ArrayList<Integer>();

      

       //1. 编译器按泛型检查,类型报错。这是在编译阶段

       //list.add("chenzs");

      

       //2. 但在实际运行时,泛型的地方就被替代为通用类型Object

       Class<?> clazz = list.getClass();

       Method m = clazz.getDeclaredMethod("add", Object.class);

      

       //3. 利用发射得到的对象是运行时对象,其就可以设置非整形的数据

       m.invoke(list, "chenzs");

      

      

       System.out.println(list.get(0));

    }

}

4.2  Collection接口

4.2.1     概述

英文名称Collection,是用来存放对象的数据结构。其中长度可变,而且集合中可以存放不同类型的对象。并提供了一组操作成批对象的方法。

数组的缺点:长度是固定不可变的,访问方式单一,插入、删除等操作繁琐。

4.2.2     集合的继承结构

Collection接口

-- List接口  : 数据有序,可以重复。

   -- ArrayList子类

   -- LinkedList子类

-- Set接口  : 数据无序,不可以存重复值

   -- HashSet子类

-- Map接口  : 键值对存数据

-- HashMap

Collections工具类

4.2.3     常用方法

boolean add(E e):添加元素。

boolean addAll(Collection  c):把小集合添加到大集合中 。

boolean contains(Object o) : 如果此 collection 包含指定的元素,则返回 true。

boolean isEmpty() :如果此 collection 没有元素,则返回 true。

Iterator<E> iterator():返回在此 collection 的元素上进行迭代的迭代器。

boolean remove(Object o) :从此 collection 中移除指定元素的单个实例。

int size() :返回此 collection 中的元素数。

Objec[] toArray():返回对象数组

4.2.4     练习1:测试常用方法

package seday11;

import java.util.ArrayList;

import java.util.Collection;

import java.util.Iterator;

public class Test_301_Collection {

       public static void main(String[] args) {

              Collection c = new ArrayList();//接口无法直接创建对象

              c.add("hello");//添加元素

              c.add("java");//添加元素

              c.add("~");//添加元素

              c.add(10);//jdk5后有自动装箱功能,相当于把10包装Integer.valueOf(10)

              System.out.println(c.remove("~"));//移除元素

              System.out.println(c.contains("a"));//判断包含关系

              System.out.println(c.size());//集合的长度

              System.out.println(c);

             

              //for遍历集合

              for(int i =0 ;i<c.size();i++) {

                     System.out.println(c.toArray()[i]);

              }

              //iterator迭代器遍历

              Iterator it = c.iterator();//对 collection 进行迭代的迭代器

              while (it.hasNext()) {//如果仍有元素,则返回 true

                     System.out.println(it.next());//返回迭代获取到的下一个元素

              }   } }

4.3  List接口

4.3.1     概述

有序的 collection(也称为序列)。此接口的用户可以对列表中每个元素的插入位置进行精确地控制。用户可以根据元素的整数索引(在列表中的位置)访问元素,并搜索列表中的元素。

4.3.2     特点

1 数据有序

2 允许存放重复元素

3 元素都有索引

4.3.3     常用方法

ListIterator<E> listIterator()

          返回此列表元素的列表迭代器(按适当顺序)。

 ListIterator<E> listIterator(int index)

          返回列表中元素的列表迭代器(按适当顺序),从列表的指定位置开始。

 void add(int index, E element)

          在列表的指定位置插入指定元素(可选操作)。

 boolean addAll(int index, Collection<? extends E> c)

          将指定 collection 中的所有元素都插入到列表中的指定位置(可选操作)。

 List<E> subList(int fromIndex, int toIndex)

     返回列表中指定的 fromIndex(包括 )和 toIndex(不包括)之间的部分视图。

E get(int index)

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

int indexOf(Object o)

          返回此列表中第一次出现的指定元素的索引;如果此列表不包含该元素,则返回 -1

4.3.4     练习1:测试常用方法

创建day12工程

创建cn.tedu.list

创建Test1_List.java

package cn.tedu.list;

 

import java.util.ArrayList;

import java.util.Iterator;

import java.util.List;

import java.util.ListIterator;

 

//这类用来测试List接口的常用方法

public class Test1_List {

 

    public static void main(String[] args) {

       //1、创建List对象

       //特点1List集合元素都有索引,可以根据索引直接定位元素

       List list = new ArrayList();

      

       //2、常用方法

       list.add(111);

       list.add(222);

       list.add(333);

       list.add(444);

       list.add('a');

       list.add("abc");

      

       list.add(3,666);//3的索引处添加指定元素

 

       //特点2:元素有序, 怎么存就怎么

       System.out.println(list);//[111, 222, 333, 666, 444, a, abc]

      

       Object obj = list.get(4);//get(m)-m是索引值,获取指定索引位置的元素

       System.out.println(obj);

      

      

       //3、迭代/遍历集合中的元素

       //使用Collection接口提供的iterator()

       Iterator it = list.iterator();

       while(it.hasNext()) {//判断集合里有没有下个元素

           Object o = it.next();

//         System.out.println(o);

       }

      

       //使用List接口提供的listIterator()

       //interfaceListIterator extends Iterator

       //区别:可以使用父接口的功能,同时拥有自己的特有功能,不仅顺序向后迭代还可以逆向迭代

       ListIterator it2 = list.listIterator();

       while(it2.hasNext()) {

           Object o = it2.next();

//         System.out.println(o);

       }

      

       System.out.println();

      

       List list2 = list.subList(1, 3);//subList(m,n)-m是开始索引,n是结束索引,其中含头不含尾类似于String.subString(m,n)

       System.out.println(list2);

      

    }

   

}

4.4  ArrayList

4.4.1     概述

1)     存在于java.util包中。

2)     内部用数组存放数据,封装了数组的操作,每个对象都有下标。

3)     内部数组默认初始容量是10。如果不够会以1.5倍容量增长。

4)     查询快,增删数据效率会降低。

4.4.2     创建对象

new ArrayList():初始容量是10

4.4.3     练习1:测试常用方法

常用API,包括下标遍历,迭代器遍历

package seday11;

import java.util.ArrayList;

public class Test3_AL {

       public static void main(String[] args) {

              ArrayList list = new ArrayList();

              list.add("aaa");//存入数据

              list.add("123");

              list.add("ccc");

              list.add("ddd");

              System.out.println(list);//list中内容

              System.out.println(list.size());//集合长度

              System.out.println(list.get(1));//根据下标获取元素

                    

              System.out.println();

              System.out.println(list.remove(2));//移除下标对应的元素

              System.out.println(list);

                    

                     //下标遍历

                     for (int i = 0; i < list.size(); i++) {

                            System.out.print(list.get(i));

                     }

 

       //Iterator迭代遍历,封装了下标

                     Iterator<String> it = list.iterator();

                     while (it.hasNext()) {//如果有数据

                            String s = it.next();//一个一个向后遍历

                            System.out.println(s);

                     }     

              }

}

4.5  LinkedList

4.5.1     概述

双向链表,两端效率高。底层就是数组和链表实现的。

4.5.2     常用方法

add()

get()

size()

remove(i)

remove(数据)

iterator()

addFirst()  addLast()

getFirst()  getLast()

removeFirst()  removeLast()

4.5.3     练习1:测试迭代器遍历

双向链表:下标遍历效率低,迭代器遍历效率高

package dd;

 

import java.util.ArrayList;

import java.util.Iterator;

import java.util.LinkedList;

 

public class tt {

       public static void main(String[] argsthrows Exception {

              LinkedList ll = new LinkedList ();

              for (int i = 0; i < 100000; i++) {

                     ll.add(100);

              }

              f1(ll);

              f2(ll);

       }

 

       private static void f2(LinkedList<Integer> ll) {

              long t = System.currentTimeMillis();

              Iterator it = ll.iterator();

              while(it.hasNext()) {

                     it.next();

              }

              t = System.currentTimeMillis()-t;

              System.out.println("=====iterator========="+t);//16

       }

 

       private static void f1(LinkedList<Integer> ll) {

              long t = System.currentTimeMillis();

              for (int i = 0; i < ll.size(); i++) {

                     ll.get(i);

              }

              long t1 = System.currentTimeMillis();

              System.out.println("~~~~for~~~~~~~"+(t1-t));//9078

       }

}

4.6  扩展

4.6.1     ArrayList扩容

ArrayList相当于在没指定initialCapacity时就是会使用延迟分配对象数组空间,当第一次插入元素时才分配10(默认)个对象空间。假如有20个数据需要添加,那么会分别在第一次的时候,将ArrayList的容量变为10 (如下图一);之后扩容会按照1.5倍增长。也就是当添加第11个数据的时候,Arraylist继续扩容变为10*1.5=15(如下图二);当添加第16个数据时,继续扩容变为15 * 1.5 =22个

ArrayList没有对外暴露其容量个数,查看源码我们可以知道,实际其值存放在elementData对象数组中,那我们只需拿到这个数组的长度,观察其值变化了几次就知道其扩容了多少次。怎么获取呢?只能用反射技术了。

4.6.2     HashMap扩容

成长因子:

static final float DEFAULT_LOAD_FACTOR = 0.75f;

前面的讲述已经发现,当你空间只有仅仅为10的时候是很容易造成2个对象的hashcode 所对应的地址是一个位置的情况。这样就造成 2个 对象会形成散列桶(链表)。这时就有一个加载因子的参数,值默认为0.75 ,如果你hashmap的 空间有 100那么当你插入了75个元素的时候 hashmap就需要扩容了,不然的话会形成很长的散列桶结构,对于查询和插入都会增加时间,因为它要一个一个的equals比较。但又不能让加载因子很小,如0.01,这样显然是不合适的,频繁扩容会大大消耗你的内存。这时就存在着一个平衡,jdk中默认是0.75,当然负载因子可以根据自己的实际情况进行调整。

 

package cn.tedu.io;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Scanner;

import javax.swing.text.DefaultEditorKit.CopyAction;

//测试文件复制
/*1.读取源文件的数据
 * 2.把数据写出到目标文件里去
 * 
 * */
public class Test1_Copy {

    public static void main(String[] args) {
        //1.接收键盘输入的源文件路径
        String formpath=new Scanner(System.in).nextLine();
        File from=new File(formpath);
        
        //2.接收键盘输入的目标文件路径
        String topath=new Scanner(System.in).nextLine();
        File to=new File(topath);
        
        //3.调用复制方法
        copy(from,to);

    }

    //复制方法
    /*针对释放资源时的优化方式2:jdk1.7对于IO自动资源管理的优化--
     * try with resource
     * */
    private static void copy(File from, File to) {
        
        try(//1.创建读取流和写出流--字节流/字符流/普通流/高级流
                InputStream in=new BufferedInputStream(
                        new FileInputStream(from));
                OutputStream out=new BufferedOutputStream(
                        new FileOutputStream(to));
                ) {
            
            // 2.读取源文件的数据
            
            int b=0;//定义变量记录到的数据
            while((b=in.read()) != -1) {
                //3.把数据写出到目标文件里去
                out.write(b);
            }
            
            
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        //        finally {
//            //4.释放资源--保证一定会被执行,放入finally块中
//            IOUtils.close(in);
//            IOUtils.close(out);
            try {
                in.close();
                out.close();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        
        
        
    }


 

package cn.tedu.io;

import java.io.Closeable;
import java.io.IOException;

public class IOUtils {

    
        //参数最好能够通用,也就是说,
        //最好能够把所有的字节流字符流高级普通流都释放掉--
        //多态Closeable.close()

        public static void close(Closeable c) {
            try {
                c.close();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    

}
 

 

package cn.tedu.collection;

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

//测试泛型
public class Test2_Gnerics {

    public static void main(String[] args) {
        /*泛型来想要模拟数组--统一数据类型--编译器就报错
         * 泛型的作用:
         * 1.检查集合中的元素类型
         * 2.如果不对编译期报错
         * */

        List list=new ArrayList();
        //集合中可以添加任意类型的数据--太自由
        list.add(1.1);
        list.add(10);
        list.add(true);
        list.add("jack");
        System.out.println(list);
        
        //想要约束集合中的元素类型?--泛型
        
        List<String> list2=new ArrayList<>();
        
        list2.add("1.1");
        //添加失败,因为元素的类型,没有通过泛型的类型String检查
//        list2.add(1);
        
        //泛型里约束的元素类型,必须是引用类型,不能是基本类型
        List<Integer> list3=new ArrayList<>();
        
        list3.add(1);
        list3.add(2);
        list3.add(3);
        list3.add(4);
    }

}
 

package cn.tedu.collection;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

//测试集合
public class Test3_Collection {

    public static void main(String[] args) {
        // 1.创建对象
        Collection<Integer> c=new ArrayList<>();
        
        //2.常用方法
        c.add(1);
        c.add(2);
        c.add(3);
        c.add(5);
        c.add(4);
        System.out.println(c);
        
        //c.clear();//清空集合
        System.out.println(c.contains(1));//判断是否包含指定元素
        System.out.println(c.equals(1));//判断集合是否和指定元素相等
        System.out.println(c.hashCode());//获取集合在内存中的哈希码值
        System.out.println(c.isEmpty());//判断集合是否为空
        System.out.println(c.remove(2));//移除指定元素
        System.out.println(c.size());//获取集合长度
        
        Object[] os=c.toArray();//把元素存入数组
        System.out.println(c.toArray(os));//
        
        //集合间的操作
        Collection<Integer> c2=new ArrayList<>();//
        c.add(1);
        c.add(2);
        c.add(3);
        System.out.println(c.addAll(c2));//把c2添加到c里面
        System.out.println(c);
        
        System.out.println(c.containsAll(c2));//判断c中是否包含c2
        //System.out.println(c.removeAll(c2));//删除交集元素
        System.out.println(c);
        
        System.out.println(c.retainAll(c2));//取差集
        
        //迭代集合/循环/遍历
        //Iterator<E> iterator();--返回可以迭代集合的迭代器
        Iterator<Integer> it=c.iterator();
        while(it.hasNext()) {//判断集合中是否有下一个元素,有返回true
            Integer in=it.next();//获取下一个元素
            System.out.println(in);
        }
    }

}
 

package cn.tedu.collection;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;

//测试List接口
public class Test4_List {

    public static void main(String[] args) {
        // 1.创建对象
        List<String> list=new ArrayList<>();
        //2.常用方法
        //继承自Collection接口的方法
        list.add("xiongda");
        list.add("xionger");
        list.add("guangtq");
        list.add(null);
        
        list.add("xiongda");
        list.add("xionger");
        //List特点:元素有序+元素可以重复+可以存null+元素都有下标
        System.out.println(list);
        
        //3.List接口特有方法--都是根据下标操作数据方式
        list.add(2, "美队");//在指定下标处,插入指定元素
        System.out.println(list.get(2));//根据下标获取元素
        System.out.println(list.indexOf("xiongda"));
        //0获取指定元素第一次出现的索引值
        System.out.println(list.lastIndexOf("xiongda"));
        //5获取指定元素最后第一次出现的索引值
        
        System.out.println(list.remove(2));
        //按照索引删除元素并返回被删除的元素
        System.out.println(list.set(1, "皮皮虾"));
        //把指定索引对应的值替换掉
        
        
        List<String> list2=list.subList(2,4);//[2,4)含头不含尾的截取子list
        System.out.println(list2);//[guangtq, null]
        
        //迭代List接口的方式:
        /*1.Iterator<E> iterator()--返回父接口
         * 2.ListIterator<E> listIterator()--返回子接口
         * 向后遍历  逆向遍历
         * 3.for循环
         * 4.增强for循环foreach
         * */
        
        //1.Iterator<E> iterator()
        Iterator<String> it=list.iterator();
        while(it.hasNext()) {//有下一个元素吗?
            String s=it.next();//获取下一个元素
            System.out.println(s);
        }
        
        //2.ListIterator<E> listIterator()--子接口list
        ListIterator<String> it2 = list.listIterator();
        while(it2.hasNext()) {//有下一个元素吗?
            String s=it2.next();//获取下一个元素
            System.out.println(s);
        }
        
        //3.for循环
        for (int i = 0; i < list.size(); i++) {
            System.out.println(list.get(i));
        }
        
        //4.增强for循环foreach
        /*for(数据的类型 变量名 : 要遍历的数据){}
         * */
        for (String s : list2) {
            System.out.println(s);
        }
        
        
        
        
        
    }

}
 

 

package cn.tedu.collection;

import java.util.ArrayList;

//测试ArrayList
public class Test5_ArrayList {

    public static void main(String[] args) {
        // 1.创建对象
        //ArrayList底层维护了一个数组Object[],用来存储多个元素
        //数组的默认大小是10,大于10的时候,底层会自动扩容,
        //扩容方式是:以前容量的1.5倍
        //int newCapacity = oldCapacity + (oldCapacity >> 1);
        ArrayList<Integer> list = new ArrayList<>();
        
        //2.常用方法
        /*继承自Collection接口的方法
         * 继承自List接口的方法
         * */
        list.add(1);
        

    }

}
 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值