集合
集合
集合也是一种容器,也可以存放数据。随着我们学习技术的深入,那么我们在程序中创建的对象也会越来越多。这时在程序中就要想办法先把这些对象给存储起来,然后在需要使用这些对象的时候从容器中把对象取出,再去使用这些对象。
集合与数组的区别
-
从长度来讲
数组:需要固定长度。
集合:长度可以改变,可以根据保存的数据进行扩容。 -
从存储内容上
数组:可以存储基本类型数据,还可以存储引用类型的数据(比如:String和上述演示的Student类)。
集合:只能存储引用类型的数据,也就是说集合只能存储类的对象。 -
从存储类型上
数组:只能存储相同类型的数据。
集合:可以存储不同类型的数据,集合中可以存储任意类型的引用数据类型。
集合框架 Collection
集合框架就是集合各个类型的继承体系。
Collection接口中定义了单列集合框架中最共性的内容。
常用功能
作用 | |
---|---|
public boolean add(E e) | 把给定的对象添加到当前集合中 |
public void clear() | 清空集合中所有的元素 |
public boolean remove(E e) | 把给定的对象在当前集合中删除 |
public boolean contains(Object obj) | 判断当前集合中是否包含给定的对象 |
public boolean isEmpty() | 判断当前集合是否为空 |
public int size() | 返回集合中元素的个数 |
public Object[] toArray() | 把集合中的元素,存储到数组中 |
迭代器Iterator
Collection中没有索引,无法使用索引对集合进行遍历,所以我们通过迭代器来对Collection集合进行遍历。
Iterator的使用
Iterator接口不用我们自己去实现,JDK集合类中已经对其实现了。我们只要掌握如何获取迭代器对象,如何使用。
获取迭代器对象
Iterable是Collection的父接口,里面含有一个方法iterater用来获取迭代器对象。
所有的Collection的子类集合都可以调用方法,iterator进行获取迭代器对象。
Iterator的使用时注意事项
1)如果迭代器判断没有了元素,不能继续使用next方法获取元素,还继续取,否则会发生NoSuchElementException异常
2)当迭代器使用的过程中,不能直接使用集合对象进行增删数据,否则会发生ConcurrentModificationException异常
如果一定要删除,可以使用迭代器自身的方法:remove
获取的迭代器会与集合的元素有一一对应的关系,通过hasNext及next方法的配合使用,可以进行对集合中元素的遍历
增强for循环
采用迭代器实现,同样是用来遍历数组与集合
增强for循环也是迭代器实现的,所以不要在for循环内部进行直接对集合进行增删操作
Arraylist
ArrayList是大小可变的数组的实现,存储在集合内的数据称为元素。此类提供一些方法来操作内部存储的元素。ArrayList 中可不断添加元素,其大小也自动增长。
常用方法和遍历
- public boolean add(E e):将指定的元素添加到此集合的尾部。
- public E remove(int index) :移除此集合中指定位置上的元素。返回被删除的元素。
- public E get(int index) :返回此集合中指定位置上的元素。返回获取的元素。
- public int size() :返回此集合中的元素数。遍历集合时,可以控制索引范围,防止越界。
-
```java
//遍历
ArrayList<String> s = new ArrayList<>();
for (int i = 0; i < s.size(); i++) {
System.out.println(s.get(i));
}
存储基本类型
ArrayList集合不能存储基本类型,只能存储引用类型的数据。类似不能写,但是存储基本数据类型对应的包装类型是可以的。所以,想要存储基本类型数据,<>中的数据类型,必须转换后才能编写,转换写法如下:
基本类型 | 基本类型包装类 |
---|---|
byte | Byte |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
char | Character |
boolean | Boolean |
Ps:只有Integer和Character需要特殊记忆,其他基本类型只是首字母大写。
泛型
泛指任意的引用数据类型【就是使用的一种未知类型,具体在使用的时候进行确定】
ArrayList: E 就是一种泛型,可以认为就是一个类型的变量,可以用来具体制定某一种类型。
泛型也可以不写,如果不写,那么泛型指定类型默认为Object。泛型可以出现在类中,方法中,接口中。
使用的好处
- 可以避免在后期使用数据时,不必要的类型转化
- 让潜在的类型转化异常,提前到编译失败,强制程序员使用指定的数据
类自定义泛型及使用
/**泛型变量需要使用尖括号括起来,尖括号中可以有多个泛型变量,使用逗号分隔,泛型变量一般使用大写的字母表示。
public class 类名<A,B,I,X....>{
//可以使用泛型,定义成员变量,当做方法的参数类型...
}*/
public MyClass<E>{
E e;
public void setE(E e){
//E具体是什么类型,只有在使用的时候才能够确定
this.e = e;
}
}
泛型可以在创建对象的时候,具体指定类型,如果没有指定具体类型,则默认为Object类型。
ArrayList<E>
ArrayList<String> list = new ArrayList<>(); E --> String
public static void main(String[] args) {
MyGenericClass m1 = new MyGenericClass();
m1.setX("haha"); // 内部X的类型默认就是Object
MyGenericClass<String> m2 = new MyGenericClass<>();
m2.setX("Hello"); // 内部X的类型指定为String
}
也可以在被子类继承时直接指定:
public class MyClass<E>{
E e;
public void setE(E e){
//E具体是什么类型,只有在使用的时候才能够确定
this.e = e;
}
}
//子类
public class Zi extends MyClass<String>{
@Override
public void setE(String s){
super.setE(e);
}
}
方法自定义泛型及使用
格式:
/**
//在方法修饰符中添加一个泛型,这个泛型只能在本方法中使用
修饰符<泛型变量> 返回值类型 方法名(参数列表){
//代码块
}
//在具体调用该方法时,传入的数据是什么类型,那么该泛型就是什么类型
*/
public class Demo01 {
public static void main(String[] args) {
//方法中的泛型,可以在具体调用的时候具体指定
test("Hello");//E->String
test(1000);//E -> Integer
}
public static<E> void test(E e) {
String name = e.getClass().getName();
System.out.println("name = " + name);
}
}
/*public static void test2(E e) {
}*/
泛型方法中定义的泛型,只能在本方法中使用。
含有泛型的接口的自定义及使用
格式:
/**
public interface 接口名<A,B,...>{
//使用泛型
}
*/
interface MyCollection<E> {
void add(E e);
boolean remove(E e);
}
//接口的泛型可以在子类实现的时候指定
class MyArrayList implements MyCollection<String>{ //E ->String
@Override
public void add(String o) {
}
@Override
public boolean remove(String o) {
return false;
}
}
//如果子类无法确定泛型,可以在子类中类名后面定义一个泛型,在接口后面使用该泛型用,可以在创建这个子类对象的时候指定
class MyArrayList2<E> implements MyCollection<E> {//现在类名后面定义泛型,然后在接口名后面使用泛型
@Override
public void add(E e) {
}
@Override
public boolean remove(E e) {
return false;
}
}
泛型通配符
若是想要参数可以接受任意类型的泛型,可以使用泛型通配符:<?>
泛型统配符定义得集合,不支持增操作,只支持读取操作
受限泛型
上限:向上有限制 <? extends 类型>
支持的类型包括本类及其子类类型
下限:向下有限制 <? super 类型>
支持的类型包括本类及其父类类型