一、集合的分类
- Java中集合分为Collection和Map集合
- Collection存取元素的基本单位:单个元素
- Map:键值对
二、Collection集合
-
特点:
- 常用方法:
1、Iterator接口
- java.util.Iterator接口主要用于描述迭代器对象,可以遍历Collection集合中的所有元素
- java.util.Collection接口继承Iterable接口,所以所有实现Collection接口的实现类都可以实现该接口
- 常用方法如下:
2、for each循坏
- Java5推出的增强型for循坏语句,可以应用于数组和集合的遍历
- 语法格式: for(元素类型 变量名:数组/集合名称){
循坏体;
} - 执行流程:不断地从数组/集合中取出一个元素赋值给变量名并执行循坏体,直到取完所有元素为止
3、List集合
3.1 基本分类
- java.util.List集合是Collection集合的子集合,该集合允许有元素重复,并且有序
- 主要实现类:ArrayList类、LinkedList类、Stack类、Vector类
- ArrayList:底层是动态数组,支持下标管理,增删元素不方便
- LinkedList:底层是双向链表,访问不方便,增删元素方便
- 可以认为ArrayList和LinkedList的方法在逻辑上完全一样,只是在性能上有一定的差别,ArrayList更适合于随机访问而LinkedList更适合于插入和删除;在性能要求不是特别苛刻的情形下可以忽略这个差别
- Stack:底层是动态数组,先进后出(LIFO),栈
- Vector:底层是动态数组,与ArrayList相比是线程安全的类,效率较低,开发基本不用
3.2 常用方法
4、Queue集合
4.1 基本概念
- java.util.Queue集合是Collection集合的子集合,与List是平级关系
- 描述队列,先进先出(FIFO)
- 主要实现类是LinkedList,该类在增删方面有优势
4.2 常用方法
5、Set集合
5.1 分类
- java.util.Set集合是Collection的子集合,与List集合平级
- 没有先后顺序,不允许重复
- 主要实现类:HashSet、TreeSet和LinkedHashSet
- HashSet:底层是哈希表
- 哈希表:JDK8之前,底层是数组+链表;JDK8开始后,底层是数组+链表+红黑树
- TreeSet:底层是红黑树
- 其中LinkedHashSet类与HashSet类的不同之处在于内部维护了一个双向链表,链表中记录了元素的迭代顺序,也就是元素插入集合中的先后顺序,因此便于迭代
5.2 常用方法
参考Collection集合中的方法
5.3 元素放入HashSet集合中的原理
- 根据hashCode计算保存的位置,如果位置为空,则直接保存,否则执行第二步。
- 执行equals方法,如果方法返回true,则认为是重复,拒绝存储,否则形成链表。
- 思考:为什么要求重写equals方法后还要重写hashCode方法?
- 答:(两者关系:hashcode不相同,用equals()方法判断的返回的一定为false。hashcode相同,equals()方法返回值不能确认,可能为true,可能为false) 如果只重写了equals方法,而不重写hashcode的方法,会造成hashcode的值不同,而equals()方法判断出来的结果为true。
5.4 TreeSet集合
- 二叉树主要指每个节点最多只有两个子节点的树形结构。
- 满足以下3个特征的二叉树叫做有序二叉树。由于TreeSet集合的底层采用红黑树进行数据的管理,当有新元素插入到TreeSet集合时,需要使用新元素与集合中已有的元素依次比较来确定新元素的合理位置。
- a.左子树中的任意节点元素都小于根节点元素值;
- b.右子树中的任意节点元素都大于根节点元素值;
- c.左子树和右子树的内部也遵守上述规则;
- 比较元素大小的规则有两种方式:自然排序的规则比较单一,而比较器的规则比较多元化,而且比较器优先于自然排序;
- 使用元素的自然排序规则进行比较并排序,让元素类型实现java.lang.Comparable接口;
- 使用比较器规则进行比较并排序,构造TreeSet集合时传入java.util.Comparator接口;
三、Map集合
1、基本概念
- java.util.Map<K,V>集合中存取元素的基本单位是:单对元素,其中类型参数如下:key不允许重复,一个key只能对应一个value
- K- 此映射所维护的键(Key)的类型,相当于目录
- V- 映射值(Value)的类型,相当于内容
- 主要实现类:HashMap类、TreeMap类、LinkedHashMap类、HashTable类、Properties类
- HashMap:哈希表
- TreeMap:红黑树
- 其中LinkedHashMap类与HashMap类的不同之处在于内部维护了一个双向链表,链表中记录了元素的迭代顺序,也就是元素插入集合中的先后顺序,因此便于迭代。
- HashTable类是古老的Map实现类,与HashMap类相比属于线程安全的类,且不允许null作为key或value的数值
- Properties类是HashTable类的子类,在大数据量情况下有着优良的查询性能
- 经常用于根据key检索value的业务场景
2、常用方法
3、元素放入HashMap集合的原理
- 使用元素的key调用hashCode方法获取对应的哈希码值,再由某种哈希算法计算在数组中的索引位置。
- 若该位置没有元素,则将该键值对直接放入即可。
- 若该位置有元素,则使用key与已有元素依次比较哈希值,若哈希值不相同,则将该元素直接放入。
- 若key与已有元素的哈希值相同,则使用key调用equals方法与已有元素依次比较。
- 若相等则将对应的value修改,否则将键值对直接放入即可
4、相关常量
- DEFAULT_INITIAL_CAPACITY : HashMap的默认容量是16。
- DEFAULT_LOAD_FACTOR:HashMap的默认加载因子是0.75。
- threshold:扩容的临界值,该数值为:容量填充因子,也就是12。
- TREEIFY_THRESHOLD:若Bucket中链表长度大于该默认值则转化为红黑树存储,该数值是8。
- MIN_TREEIFY_CAPACITY:桶中的Node被树化时最小的hash表容量,该数值是64。
四、Collections类
1、基本概念
java.util.Collections类主要提供了对集合操作或者返回结合的静态方法,即工具类
2、常用方法
五、泛型
1、基本概念
- 通常情况下集合中可以存放不同类型的对象,是因为将所有对象都看做Object类型放入的,因此从集合中取出元素时也是Object类型,为了表达该元素真实的数据类型,则需要强制类型换,而强制类型转换可能会引发类型转换异常。
- 为了避免上述错误的发生,从Java5开始增加泛型机制,也就是在集合名称的右侧使用<数据类型>的方式来明确要求该集合中可以存放的元素类型,若放入其它类型的元素则编译报错。
- 泛型只在编译时期有效,在运行时期不区分是什么类型
- 常见形式有泛型类、泛型接口、泛型方法。
- 语法:
- <T,…> T称为类型占位符,表示一种引用类型。
- 好处:
- 提高代码的重用性。
- 防止类型转换异常,提高代码的安全性
2、自定义泛型接口
泛型接口和普通接口的区别就是后面添加了类型参数列表,可以有很多个类型参数,如:<E,T,...>等
/**
* 泛型接口
* 语法:接口名<T>
* 注意:不能创建泛型静态常量
*/
public interface MyInterface<T> {
//创建常量
String nameString="tang";
T server(T t);
}
/**
* 实现接口时确定泛型类
*/
public class MyInterfaceImpl implements MyInterface<String>{
@Override
public String server(String t) {
System.out.println(t);
return t;
}
}
//测试
MyInterfaceImpl myInterfaceImpl=new MyInterfaceImpl();
myInterfaceImpl.server("xxx");
//xxx
/**
* 实现接口时不确定泛型类
*/
public class MyInterfaceImpl2<T> implements MyInterface<T>{
@Override
public T server(T t) {
System.out.println(t);
return t;
}
}
//测试
MyInterfaceImpl2<Integer> myInterfaceImpl2=new MyInterfaceImpl2<Integer>();
myInterfaceImpl2.server(2000);
//2000
3、自定义泛型类
- 泛型类和普通类的区别就是类名后面添加了类型参数列表,可以有多个类型参数,如:<E, T, .. >等。
- 实例化泛型类时应该指定具体的数据类型,并且是引用数据类型而不是基本数据类型。
- 父类有泛型,子类可以选择保留泛型也可以选择指定泛型类型。
- 子类必须是“富二代”,子类除了指定或保留父类的泛型,还可以增加自己的泛型。
/** * 泛型类 * 语法:类名<T> * T是类型占位符,表示一种引用类型,编写多个使用逗号隔开 * */ public class myGeneric<T>{ //1.创建泛型变量 //不能使用new来创建,因为泛型是不确定的类型,也可能拥有私密的构造方法。 T t; //2.泛型作为方法的参数 public void show(T t) { System.out.println(t); } //泛型作为方法的返回值 public T getT() { return t; } }
/** * 注意: * 1.泛型只能使用引用类型 * 2.不同泛型类型的对象不能相互赋值 */ public class testGeneric { public static void main(String[] args) { //使用泛型类创建对象 myGeneric<String> myGeneric1=new myGeneric<String>(); myGeneric1.t="tang"; myGeneric1.show("he"); myGeneric<Integer> myGeneric2=new myGeneric<Integer>(); myGeneric2.t=10; myGeneric2.show(20); Integer integer=myGeneric2.getT(); } }
4、自定义泛型方法
- 泛型方法就是我们输入参数的时候,输入的是泛型参数,而不是具体的参数。我们在调用这个泛型方法的时需要对泛型参数进行实例化。
- 泛型方法的格式:[访问权限] <泛型> 返回值类型 方法名([泛型标识 参数名称]) { 方法体; }
- 在静态方法中使用泛型参数的时候,需要我们把静态方法定义为泛型方法
/** * 泛型方法 * 语法:<T> 返回类型 */ public class MyGenericMethod { public <T> void show(T t) { System.out.println("泛型方法"+t); } }
//测试 MyGenericMethod myGenericMethod=new MyGenericMethod(); myGenericMethod.show("tang"); myGenericMethod.show(200); myGenericMethod.show(3.14);
5、通配符
- 有时候我们希望传入的类型在一个指定的范围内,此时就可以使用泛型通配符了。
- 如:之前传入的类型要求为Integer类型,但是后来业务需要Integer的父类Number类也可以传入。
- 泛型中有三种通配符形式:
- <?> 无限制通配符:表示我们可以传入任意类型的参数。
- <? extends E> 表示类型的上界是E,只能是E或者是E的子类。
- <? super E> 表示类型的下界是E,只能是E或者是E的父类