綠 青 色

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());
	}
}
posted @ 2021-07-30 10:46  LYANG-A  阅读(52)  评论(0编辑  收藏  举报