• Java集合与泛型


    集合介绍

      数组:一旦创建对象,分配内存空间,不可改变空间大小(无法动态扩容)

      Collection:可以动态扩容,从而避免数组下标越界。缺点:扩容会消耗你的内存,所以使用集合比使用数组内存开销大。

    集合体系结构

    ArrayList集合

      特征:本质是一个Object [] 数组

      可以向集合放入元素,也可以从集合中获取元素,还可以删除集合中的元素

      既然是一个数组,放入元素和获取元素(根据下标获取)速度快,删除首部元素和中间元素效率低

    为什么要使用泛型?

       你可以将任意类型的数据放入ArrayList,取出数据必须强制转换

       泛型:能够在编译期对放入和取出的对象(元素)做类型检查

       定义泛型:类型不确定,一旦你在某个类上定义了泛型,可以将泛型作为属性、参数、返回值

       使用泛型:必须明确数据类型,数据类型必须在<>中定义

    什么时候使用ArrayList?

      如果你需要频繁创建元素,频繁获取元素,可以使用

      优点:创建和查询(获取)元素快

    什么时候不能使用ArrayList?

      当你频繁创建和频繁删除元素,不要使用ArrayList

      缺点:删除首部元素和中间元素效率低

    如何创建一个ArrayList类型的对象

     使用new的方式创建

    package com.whsxt.day5.arraylist;
    
    import java.util.ArrayList;
    import java.util.List;
    
    public class TestArrayList1 {
    	public static void main(String[] args) {
    		System.out.println("start");
    		//ArrayList list = new ArrayList();
    		//ArrayList是一个List
    		//new ArrayList 是实实在在的对象,在堆内存中创建
    		// list 在栈内存中:用来操作堆内存中的ArrayList
    		// list遥控器   new ArrayList()电视机
    		List list = new ArrayList();
    		//调用ArrayList的add(e)方法,将对象放入ArrayList 
    		//第一次调用list对象的add(e)方法添加元素,会在ArrayList内部的数组中分配10个空间的元素
    		list.add("tom1");
    		list.add("tom2");
    		list.add("tom3");
    		list.add("tom4");
    		list.add("tom5");
    		list.add("tom6");
    		list.add("tom7");
    		list.add("tom8");
    		list.add("tom9");
    		list.add("tom10");
            //还有新元素添加到ArrayList中,不会出现“数组下标越界异常”
    		list.add("tom11");
    		list.add("tom12");
    		System.out.println("end");
    	}
    }
    	String str1 = "a" + 1 + 2;		// a12
        String str2 = 'a' + 1 + "2";    // 982 (a的ASCll码为97)97+1+"2"=982
        String str3 = 1 + 2 + "a";		// 3a
    

    小结

      第一次调用list对象的add(e)方法添加元素,会在ArrayList内部的数组中分配10个空间的元素,当数组空间全部使用完毕,还有新元素添加到ArrayList中,不会出现“数组下标越界异常”,此时数组会扩容

      ArrayList是一个可以动态扩容的数组

    ArrayList扩容(重点)

      当我创建ArrayList对象,里面的数组大小0,第一次调用add(E)方法,数组大小为10,当元素全部占满数组大小为10,如果继续添加元素,数组大小15

      0---->10---->15--->22

    int size =15;
    int newCapacity= (size>>1)+size;    1111>>1  111+1111=22
    扩容规律:原始长度>>1 + 原始长度
    

    小结

      在调用add(e)方法添加元素之前,先判断数组有没有空间存储新元素,如果有不会扩容,如果没有才扩容

      每次扩容ArrayList里面的Object elementData[] 指向都会发生改变

    扩容机制:在原始数组的基础上重新拷贝一份新数组,此时新数组会存储原始数组里面的元素

    import java.util.ArrayList;
    import java.util.List;
    
    public class TestArrayList1 {
    	public static void main(String[] args) {
    		System.out.println("start");
    		//ArrayList list = new ArrayList();
    		//ArrayList是一个List
    		//new ArrayList 是实实在在的对象,在堆内存中创建
    		// list 在栈内存中:用来操作堆内存中的ArrayList
    		// list遥控器   new ArrayList()电视机
    		List list = new ArrayList();
    		//调用ArrayList的add(e)方法,将对象放入ArrayList 
    		//第一次调用list对象的add(e)方法添加元素,会在ArrayList内部的数组中分配10个空间的元素
    		list.add("tom1");
    		list.add("tom2");
    		list.add("tom3");
    		list.add("tom4");
    		list.add("tom5");
    		list.add("tom6");
    		list.add("tom7");
    		list.add("tom8");
    		list.add("tom9");
    		list.add("tom10");
    		list.add("tom11");
    		list.add("tom12");
    		list.add("tom13");
    		list.add("tom14");
    		list.add("tom15");
    		list.add("tom16");
    		System.out.println("end");
    	}
    }
    

    ArrayList其它方法

      get(int index): 根据下标获取集合中的元素

      size():获取集合实际的大小

      remove(int index):根据下标从集合中删除元素

      remove(E): 根据元素内容从集合中删除元素

      contains(): 判断某个元素在集合中是否存在,true存在,false不存在

    import java.util.ArrayList;
    import java.util.List;
    
    public class TestArrayList2 {
    	public static void main(String[] args) {
    		List list = new ArrayList();
    		list.add("Tom");
    		list.add(123);
    		list.add(3.14);
    		list.add('M');
    		list.add(true);
    		//获取集合的第一个元素
    		Object element =list.get(0);
    		int size =list.size();
    		System.out.println(element);	//5
    
    		System.out.println("删除之前 size="+size);
    		//删除集合中第一个元素
    		list.remove(0);
    		int size2=list.size();		//4
    
    		System.out.println("删除之后 size="+size2);
    		//判断Abc是否在集合中存在  结果false  Abc在集合中不存在
    		boolean result =list.contains("Abc");
    		System.out.println(result);
    	}
    }
    

    以下代码存在的问题:

      1、 放入集合的类型在编译器不能确定,可以放入任意类型

      2、 由问题已引入问题2:元素类型不专一

      3、 获取元素必须使用强制类型转换

      4、 强制转换的代码不安全 int obj= (int) list.get(0); 可能会出现运行时类型转换异常ClassCastException

    要求:解决该问题

    目的:放入集合的元素类型必须要专一,要么全部放String、要么全部放Boolean

    ​ 能够提供编译器的类型检查,例如我规定了集合中只能放置String,如果你放置了Boolean,会提示编译失败

    package com.whsxt.day5.arraylist;
    
    import java.util.ArrayList;
    import java.util.List;
    
    public class TestArrayList2 {
    	public static void main(String[] args) {
    		List list = new ArrayList();
    		list.add("Tom");
    		list.add(123);
    		list.add(3.14);
    		list.add('M');
    		list.add(true);
    		int obj= (int) list.get(0);
    		System.out.println(obj);		
    	}
    }
    
    

    泛型

      SinceJDK1.5,提供编译期检查,解决放入集合元素类型不一致的问题,例如:我定义一个ArrayList集合并且规定了只能放置String,如果你放置其他类型就会编译报错

      泛型
        1、定义泛型(数据类型不确定)

        2、使用泛型 (必须明确数据类型)

      定义泛型语法
        < > 用于定义泛型, 钻石运算符,泛型运算符

    public class 类名称<类型>{
        
    }
    

    使用泛型:

    import java.util.ArrayList;
    import java.util.List;
    
    public class TestArrayList2 {
    	public static void main(String[] args) {
    		//创建一个ArrayList对象,支持泛型,规定了ArrayList只能存放String类型的元素(对象)
    		List<String> list = new ArrayList<>();
    		list.add("Tom");
    		//编译错误:不能讲int类型的数据放入ArrayList,因为ArrayList在定义的时候使用了泛型,规定了只能存放String类型
    		//list.add(123);
    		//使用泛型好处:获取元素不用强制类型转换
    		String name = list.get(0);
    		System.out.println(name);
    		//编译错误:获取元素也能够提供编译检查,由于泛型规定了只能放置String,那么使用非String类型接收,就会编译错误
    		//int num = list.get(0);
    		//小结:定义ArrayList使用泛型,好处能够在add()方法和get()方法提供编译期类型检查
    	}
    }
    

    Java泛型不支持基本类型

    import java.util.ArrayList;
    import java.util.List;
    
    public class TestArrayList3 {
    	public static void main(String[] args) {
    		//泛型:不支持基本类型
    		//List<double> list = new ArrayList<>();
    		List<Integer> list = new ArrayList<>();
    		list.add(1);
    		list.add(1);
            
    		//Java提供了自动装箱和自动拆箱,放入ArrayList的元素是包装类型,取出元素可以用基本类型接受
    		int num = list.get(0);
    		System.out.println(num);
    		System.out.println(list);
    	}
    }
    

    自定义泛型

    /**
     * @author caojie
     * 自己定义一个泛型类
     * T: Type  类型
     * E: Element 元素
     * K: Key 键
     * V: Value 值
     * Foo<T>:我定义的类型Foo支持泛型
     * 可以将泛型用于属性,也可以用于参数,该可以用于返回值
     */
    public class Foo<T> {
    	/**
    	 * 定义一个属性,名称叫做type,它在编译期是一个不确定的类型
    	 */
    	private T type;
    
    	// 编译错误:泛型属性名称必须跟定义泛型的类型一致<T>  private T type;
    	// private E element;
    	/**
    	 * T:表示返回类型是一个泛型
    	 * @return
    	 */
    	public T getType() {
    		return type;
    	}
        
    	/**
    	 * T type 表示参数是一个泛型
    	 * @param type
    	 */
    	public void setType(T type) {
    		this.type = type;
    	}
    }
    
    /**
     * @author caojie
     * 可以定义多个泛型,但是必须使用逗号分隔
     * @param <K>
     * @param <V>
     */
    public class FooTwo<K,V> {
    
    	private K key;
    	private V value;
    
    	public K getKey() {
    		return key;
    	}
    	public void setKey(K key) {
    		this.key = key;
    	}
    
    	public V getValue() {
    		return value;
    	}
    	public void setValue(V value) {
    		this.value = value;
    	}
    	
    }
    
    public class FooThree<T> {
    	private T [] types;
    
    	public T [] getTypes() {
    		return types;
    	}
    
    	public void setTypes(T [] types) {
    		this.types = types;
    	}  
    }
    
    /**
     * @author caojie
     * 使用定义的泛型
     */
    public class TestFoo {
    	public static void main(String[] args) {
    		// 不使用泛型,程序编译期不能提供类型检查,运行期就会出现异常
    		/* Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
    		   at com.whsxt.day5.generic.TestFoo.main(TestFoo.java:14) */
    		// Foo foo = new Foo();
    		// foo.setType("Tom");
    		// int num=(int)foo.getType();
    		// System.out.println(num);
            
    		Foo<String> foo = new Foo<>();
    		foo.setType("Tomson");
    		String name = foo.getType();
    		// 使用泛型能够提供编译期类型检查
    		// int num = foo.getType();
    		System.out.println(name);
    	}
    }
    
    import java.util.Arrays;
    
    public class TestFooThree {
    	public static void main(String[] args) {
    		FooThree<String> foo =new FooThree<>();
    		String names[] = {"TOm","Jerry","Jason"};
    		foo.setTypes(names);
    		String result[] =foo.getTypes();
    		System.out.println(Arrays.toString(result));
    	}
    }
    
    public class Test2FooThree {
    	public static void main(String[] args) {
    		FooThree<Integer> foo = new FooThree<>();
    		//int arrs[]= {10,20,30};
    		//Java虽然能支持自动装箱和自动拆箱,但是无法运用到泛型
    		//foo.setTypes(arrs);
    		Integer arrs[]= {110,20,30};
    		foo.setTypes(arrs);
    	}
    }
    

    使用泛型自定义一个ArrayList

    import java.util.Arrays;
    
    /**
     * @author caojie
     * 自定义的ArrayList,支持泛型
     */
    public class ArrayList<E> {
    
    	/**
    	 * ArrayList核心是一个数组
    	 */
    	private  Object elementData [];
    	
    	/**
    	 * ArrayList大小
    	 */
    	private int size;
        
        public ArrayList() {
    		//调用带参数的构造方法
    		this(10);
    	}
    	
    	/**
    	 * 带有一个参数的构造方法
    	 * @param capacity 初始容量
    	 */
    	public ArrayList(int capacity) {
    		elementData = new Object[capacity];
    	}
    	
    	/**
    	 * 返回集合的大小(实际容量,不是数组长度)
    	 * @return 集合大小
    	 */
    	public int size() {
    		return size;
    	}
    	
    	/**
    	 * 根据下标获取集合的元素
    	 * @param index 外界传入的下标
    	 * @return 返回集合的元素
    	 */
    	@SuppressWarnings("unchecked")
    	public E get(int index) {
    		//条件成立:下标是非法的,无法获取数组元素,抛出数组下标越界异常
    		if(index<0 || index>=size) {
    			throw new ArrayIndexOutOfBoundsException("size:"+size+" index:"+index);
    		}
    		return (E) elementData[index];
    	}
    	
        // 定义扩容次数
    	private int index = 0;
    	public void add(E element) {
    		// 新元素加入到ArrayList中,先检查容量,如果容量不够了,需要扩容
    		int length = elementData.length;
    		// 条件成立:表示elementData数组容量已经满了,需要扩容
    		if(size>=length) {
    			//新容量= (旧数组长度>>1)+旧数组长度
    			int newCapacity = (length>>1)+length;
    			elementData =Arrays.copyOf(elementData,newCapacity);
    			index++;
    			System.out.println("扩容"+index+"次");
    		}
    		//新加入的元素填充到数组中
    		elementData[size++]= element;
    	}
    	
    }
    

    测试泛型

    package com.whsxt.day5.generic;
    
    public class TestArrayList {
    	public static void main(String[] args) {
    		/*
    		 * 使用无参构造方法创建ArrayList对象扩容太频繁(23次)
    		 * 不要频繁扩容,也不要永远不扩容(性能消耗大)
    		 * 理想方案:扩容次数15次以内
    		 * 注意:每当你定义ArrayList的时候,思考一个问题,我的ArrayList大概需要容纳多少元素,调用对应的有参数构造方法
    		 * */
    		ArrayList<Integer> list = new ArrayList<>(2000);
    		for(int i=0;i<100000;i++) {
    			list.add(i+10);
    		}
    		System.out.println(list.size());
    	}
    }
    
  • 相关阅读:
    python面向对象开发
    python迭代器和生成器
    python 集合
    python 字典
    python 元组
    python列表
    python字符串方法
    dom节点操作
    vue 跨域配置代理 get/post 请求
    Vuecli版本调整
  • 原文地址:https://www.cnblogs.com/lyang-a/p/15078562.html
Copyright © 2020-2023  润新知