目录
List接口
-
List接口存储元素特点:有序、可重复
-
List接口特有方法
void | add(int index, E element) 在列表的指定位置插入指定元素(可选操作,效率较低)。 |
E | get(int index) 返回列表中指定位置的元素。 |
int | indexOf(Object o) 返回此列表中第一次出现的指定元素的索引;如果此列表不包含该元素,则返回 -1。 |
int | lastIndexOf(Object o) 返回此列表中最后出现的指定元素的索引;如果列表不包含此元素,则返回 -1。 |
E | remove(int index) 移除列表中指定位置的元素(可选操作)。 |
E | set(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(最常用)
-
ArrayList默认初始化容量为10 ( 先创建了一个长度为0的数组,当添加子一个元素时,初始化容量10)
-
ArrayList 底层是Object类型的数组 Object[];
-
ArrayList 集合的扩容,增长到元容量的1.5倍
-
效率优化:尽量预估元素个数,给定一个初始化容量,减少扩容
-
ArrayList是非线程安全的
数组优点:检索效率比较高 (每个元素占用空间大小相同,内存地址是连续的。)
数组缺点:随机增删元素效率比较低 (向数组末尾添加元素效率很高,不受影响)
- 因为每次增删需要移动数组,保证数组连续性
无法存储大数据量 (很难找到一块非常巨大的连续内存空间)
- 构造方法
ArrayList() 构造一个初始容量为 10 的空列表。 |
ArrayList(int initialCapacity) 构造一个具有指定初始容量的空列表。 |
ArrayList(Collection<? extends E> c) 构造一个包含指定 collection 的元素的列表,这些元素是按照该 collection 的迭代器返回它们的顺序排列的。 |
boolean | addAll(Collection<? extends E> c) 按指定集合的Iterator返回的顺序将指定集合中的所有元素追加到此列表的末尾。 |
void | forEach(Consumer<? super E> action) 对 |
E | get(int index) 返回此列表中指定位置的元素。 |
boolean | removeAll(Collection<?> c) 从此列表中删除指定集合中包含的所有元素。 |
boolean | removeIf(Predicate<? super E> filter) 删除满足给定谓词的此集合的所有元素。 |
LinkedList
- LinkedList 集合底层为双向链表,内存地址不连续
-
优点: 随机增删元素效率较高。(链表上大元素在空间存储上内存地址不连续,因此增删元素时不会有大量元素位移)
-
缺点:查询检索效率较低。(不能通过数学表达式计算被查找元素,每一次查找都是从头开始遍历)
-
LinkedList是非线程安全的
- LinkedList集合有初始化容量吗?没有,最初这个链表没有任何元素。first和last引用都是null;
- 特有方法 (首尾相关)
void | addFirst(E e) 将指定元素插入此列表的开头。 |
void | addLast(E e) 将指定元素添加到此列表的结尾。 |
E | getFirst() 返回此列表的第一个元素。 |
E | getLast() 返回此列表的最后一个元素。 |
了解↓ | |
E | removeFirst() 移除并返回此列表的第一个元素。 |
E | removeLast() 移除并返回此列表的最后一个元素。 |
boolean | removeFirstOccurrence(Object o) 从此列表中移除第一次出现的指定元素(从头部到尾部遍历列表时)。 |
boolean | removeLastOccurrence(Object o) 从此列表中移除最后一次出现的指定元素(从头部到尾部遍历列表时)。 |
boolean | offerFirst(E e) 在此列表的开头插入指定的元素。 |
boolean | offerLast(E e) 在此列表末尾插入指定的元素。 |
实现队列结构 先进先出 (消息队列) | |
boolean | offer(E e) //入队 将指定元素添加到此列表的末尾(最后一个元素)。 |
E | poll() //出队 获取并移除此列表的头(第一个元素) |
实现栈形结构 先进后出 (压栈弹栈) | |
void | push(E e) //压栈 将元素推入此列表所表示的堆栈。 |
E | pop() //弹栈 从此列表所表示的堆栈处弹出一个元素。 |
E | peek() 获取但不移除此列表的头(第一个元素)。 |
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 | addAll(Collection<? super T> c, T... elements) 将所有指定元素添加到指定 collection 中。 |
static | sort(List<T> list) //(要求T实现了Comparable接口) 根据元素的自然顺序 对指定列表按升序进行排序。 |
static | sort(List<T> list, Comparator<? super T> c) //传入排序规则 根据指定比较器产生的顺序对指定列表进行排序。 |
static | binarySearch(List<? extends Comparable<? super T>> list, T key) //(要求T实现了Comparable接口) 使用二分搜索法搜索指定列表,以获得指定对象。 |
static | binarySearch(List<? extends T> list, T key, Comparator<? super T> c) //传入排序规则,在此规则下二分查找 使用二分搜索法搜索指定列表,以获得指定对象。 |
static | max(Collection<? extends T> coll) 根据元素的自然顺序,返回给定 collection 的最大元素。 |
static | max(Collection<? extends T> coll, Comparator<? super T> comp) 根据指定比较器产生的顺序,返回给定 collection 的最大元素。 |
static | min(Collection<? extends T> coll) 根据元素的自然顺序 返回给定 collection 的最小元素。 |
static | min(Collection<? extends T> coll, Comparator<? super T> comp) 根据指定比较器产生的顺序,返回给定 collection 的最小元素。 |
static void | reverse(List<?> list) 反转指定列表中元素的顺序。 |
static void | shuffle(List<?> list) //洗牌 打乱顺序 使用默认随机源对指定列表进行置换。 |
static void | swap(List<?> list, int i, int j) 在指定列表的指定位置处交换元素。 |
static int | frequency(Collection<?> c, Object o) 返回指定 collection 中等于指定对象的元素数。 |
static | replaceAll(List<T> list, T oldVal, T newVal) 使用另一个值替换列表中出现的所有某一指定值。 |
| synchronizedList(List<T> list) 返回指定列表支持的同步(线程安全的)列表。 |
把List修改为不可修改的集合 | |
static | unmodifiableCollection(Collection<? extends T> c) 返回指定 collection 的不可修改视图。 |
static | 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{}