文章目录
List的三个子类
List的三个子类的特点
- ArrayList:
底层数据结构是数组,查询快,增删慢。
线程不安全,效率高。 - Vector:
底层数据结构是数组,查询快,增删慢。
线程安全,效率低。 - LinkedList:
底层数据结构是链表,查询慢,增删快。
线程不安全,效率高。
使用时要针对他们的特点来选取最合适的一种。
返回顶部
ArrayList概述
可调整大小的数组的实现List接口。 实现所有可选列表操作,并允许所有元素,包括null 。 除了实现List 接口之外,该类还提供了一些方法来操纵内部使用的存储列表的数组的大小。
JDK1.8新增的一个方法也能遍历集合:
void forEach(Consumer<? super E> action)
执行特定动作的每一个元素的 Iterable直到所有元素都被处理或操作抛出异常
案例演示:Arraylist中的特有方法
-
int indexOf (Object o)
返回此列表中指定元素的第一个出现的索引,或 - 如果此列表不包含元素,或 - 1。 -
int lastIndexOf (Object o)
返回此列表中指定元素的最后一个发生的索引,或 - 如果此列表不包含元素,或 - 1。 -
List<E> subList ( int fromIndex, int toIndex)
返回一个视图之间的指定 fromIndex,包容,和 toIndex这份名单的部分,独家。
import java.util.ArrayList;
import java.util.List;
public class Demo {
public static void main(String[] args) {
ArrayList arrayList = new ArrayList();
arrayList.add(100);
arrayList.add(200);
arrayList.add(300);
arrayList.add(100);
arrayList.add(200);
arrayList.add(300);
arrayList.add(100);
arrayList.add(200);
arrayList.add(300);
int index = arrayList.indexOf(100);
System.out.println(index);//0
int lastIndexOf = arrayList.lastIndexOf(100);
System.out.println(lastIndexOf);//6
//通过首尾索引,截取一部分,集合中的元素,放到一个新的集合当中,含头不含尾
List subList = arrayList.subList(0, 6);
System.out.println(subList);
//[100, 200, 300, 100, 200, 300]
System.out.println(arrayList);
//[100, 200, 300, 100, 200, 300, 100, 200, 300]
//Lambda 表达式 JDK 1.8 之后引入的一个新特性
arrayList.forEach((x) -> System.out.println(x));
//上面是这个方法的简写,就是Lambda表达式
arrayList.forEach(new Consumer() {
@Override
public void accept(Object o) {
System.out.println(o);
}
});
}
}
案例演示:ArrayList 的几种遍历方法
import java.util.ArrayList;
import java.util.Iterator;
import java.util.ListIterator;
import java.util.function.Consumer;
public class Demo1 {
public static void main(String[] args) {
ArrayList arrayList = new ArrayList();
Student s1 = new Student("钢铁侠", 19);
Student s2 = new Student("蜘蛛侠", 20);
Student s3 = new Student("黑寡妇", 21);
Student s4 = new Student("绿巨人", 22);
Student s5 = new Student("蝙蝠侠", 23);
arrayList.add(s1);
arrayList.add(s2);
arrayList.add(s3);
arrayList.add(s4);
arrayList.add(s5);
// 遍历1
Iterator iterator = arrayList.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next().toString());
}
System.out.println("---------------------");
// 遍历2
ListIterator listIterator = arrayList.listIterator();
while (listIterator.hasNext()) {
System.out.println(listIterator.next().toString());
}
System.out.println("---------------------");
// 遍历3
for (int i = 0; i < arrayList.size(); i++) {
System.out.println(arrayList.get(i).toString());
}
System.out.println("---------------------");
// 遍历4
arrayList.forEach(new Consumer() {
@Override
public void accept(Object o) {
System.out.println(o.toString());
}
});
System.out.println("---------------------");
// 遍历4——1 Lambda表达式 其实是遍历4的简写
arrayList.forEach(o -> System.out.println(o.toString()));
}
}
class Student {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Student() {
}
public Student(String name, int age) {
this.age = age;
this.name = name;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
Vector概述
Vector类概述: Vector类实现了可扩展的对象数组。 像数组一样,它包含可以使用整数索引访问的组件。 但是, Vector的大小可以根据需要增长或缩小,以适应在创建Vector之后添加和删除项目。
案例演示: Vector的特有功能演示
void add(int index, E element)
在此Vector中的指定位置插入指定的元素void addElement(E obj)
将指定的组件添加到此向量的末尾,将其大小增加1void insertElementAt(E obj, int index)
在指定的index插入指定对象作为该向量中的一个 index 。E set(int index, E element)
用指定的元素替换此Vector中指定位置的元素。
import java.util.Enumeration;
import java.util.Vector;
public class VectorDemo {
public static void main(String[] args) {
Vector vector = new Vector();
vector.add(100);
//这里要特别注意不要越界,要么往后添加,要么往前面添加
vector.insertElementAt(200, 1);
vector.add(2, 300);
vector.addElement(400);
System.out.println(vector);//[100, 200, 300, 400]
//根据索引替换元素,返回原来的元素
Object set = vector.set(0, 1000);
System.out.println(set);//100
System.out.println(vector);//[1000, 200, 300]
/*获取功能*/
System.out.println(vector.firstElement());
System.out.println(vector.lastElement());
System.out.println(vector.get(0));
Object o = vector.elementAt(1);
System.out.println(o);
/*Vextor里面特有的迭代方式*/
Enumeration elements = vector.elements();
while (elements.hasMoreElements()) {
System.out.println(elements.nextElement());
}
//这里的hasMoreElements和nextElement与前面讲过的hasNext和next用法相似
}
}
LinkedList概述
双链表实现了List和Deque接口。 实现所有可选列表操作,并允许所有元素(包括null )。
所有的操作都能像双向列表一样预期。 索引到列表中的操作将从开始或结束遍历列表,以更接近指定的索引为准。
请注意,此实现不同步。 如果多个线程同时访问链接列表,并且至少有一个线程在结构上修改列表,则必须在外部进行同步。
案例演示:LinkedList的基本使用
public void addFirst (E e) 及 addLast(E e)
public E getFirst () 及 getLast()
public E removeFirst () 及 public E removeLast()
import java.util.LinkedList;
public class LinkedListDemo {
public static void main(String[] args) {
//LinkedList底层数据结构是链表,查询慢,增删快,线程安全效率高
LinkedList linkedList = new LinkedList();
/*添加*/
linkedList.addLast("张三");
linkedList.addLast("李四");
linkedList.addLast("王五");
linkedList.addLast("赵六");
linkedList.addLast("鬼脚七");
linkedList.addFirst("陈二");
/*获取*/
Object first = linkedList.getFirst();
System.out.println(first);
Object last = linkedList.getLast();
System.out.println(last);
Object e2 = linkedList.get(1);
System.out.println(e2);
/*删除*/
boolean b = linkedList.remove("张三");//返回布尔
Object o = linkedList.remove(0);//返回被删除的元素
Object o1 = linkedList.removeFirst();//返回被删除的元素
Object o2 = linkedList.removeLast();//返回被删除的元素
}
}
用LinkedList模拟栈数据结构的集合并测试
先来理一下思路:
import java.util.LinkedList;
public class Test {
public static void main(String[] args) {
// 栈的数据结构特点:先进后出
LinkedList linkedList = new LinkedList();
// 先进
linkedList.add(100);
linkedList.add(200);
linkedList.add(300);
linkedList.add(400);
linkedList.add(500);
for (int i = 0; i < linkedList.size(); i++) {
System.out.println(linkedList.get(i)+"已添加");
}
// 后出
while (!linkedList.isEmpty()){
Object removeLast = linkedList.removeLast();
System.out.println(removeLast+"已移除");
}
}
}
我们把LinkedList简单封装一下:
import java.util.LinkedList;
public class MyLinkedList {
private LinkedList linkedList;
public MyLinkedList() {
// 初始化LinkedList
linkedList = new LinkedList();
}
// 添加
public void add(Object obj) {
linkedList.addLast(obj);
}
// 获取,每获取一次,就把最后一个元素移除,并返回它
public Object get() {
Object o = linkedList.removeLast();
return o;
}
// 判空
public boolean isEmpty() {
return linkedList.isEmpty();
}
}
我们利用封装好的类来测试实现模拟栈数据结构的集合:
public class Test_MyLinkedList {
public static void main(String[] args) {
// new一个MyLinkedList对象
MyLinkedList myLinkedList = new MyLinkedList();
myLinkedList.add(100);
myLinkedList.add(200);
myLinkedList.add(300);
myLinkedList.add(400);
myLinkedList.add(500);
while (!myLinkedList.isEmpty()){
Object o = myLinkedList.get();
System.out.println(o);
}
}
}
去除ArrayList中重复字符串元素方式
import java.util.ArrayList;
import java.util.Iterator;
public class Test {
public static void main(String[] args) {
ArrayList arrayList = new ArrayList();
arrayList.add(1);
arrayList.add(2);
arrayList.add(3);
arrayList.add(4);
arrayList.add(5);
arrayList.add(1);
arrayList.add(2);
arrayList.add(3);
arrayList.add(4);
arrayList.add(5);
System.out.println(arrayList);//[1, 2, 3, 4, 5, 1, 2, 3, 4, 5]
// 去重,思路就是新建一个表,添加元素的条件是这张新表中没有这个元素
ArrayList newArrayList = new ArrayList();
// 遍历
Iterator iterator = arrayList.iterator();
while (iterator.hasNext()) {
Object o = iterator.next();
while (!newArrayList.contains(o)) {
newArrayList.add(o);
}
}
System.out.println(newArrayList);//[1, 2, 3, 4, 5]
}
}
泛型
关于泛型的知识参考了前辈的总结:
- 泛型的概述
泛型是一种把类型明确的工作推迟到创建对象或者调用法法的时候采取明确的特殊的类型。它的实质是参数化类型,即把类型当做参数一样传递。 - 泛型的格式
<数据类型>
这里的数据类型只能是引用数据类型。 - 泛型的优势
- 把运行使其的问题提前到了编译期间
- 避免了强制类型转换
- 优化了程序设计,解决了黄色警告
- 泛型只在编译器有效,在运行期就会被擦除
来看一个例子:
ArrayList list = new ArrayList();
list.add("abc");
list.add("xyz");
//手误插入int 100
list.add(100);
for (int i = 0; i < list.size(); i++) {
String item = (String) list.get(i);
System.out.println(item);
}
//运行后程序会崩溃
//java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
ArrayList可以存放任意类型,例子中误入了一个整型,使用时都以String的方式使用,因此程序崩溃了。为了解决类似这样的问题(在编译阶段就可以解决),泛型应运而生。我们将第一行声明初始化list的代码更改一下,编译器会在编译阶段就能够帮我们发现类似这样的问题:
ArrayList<String> list = new ArrayList<>();
list.add(100);//会报红线警告
//只能传入String类型的参数
如果不使用泛型,我们每次使用某些类型特有的方法是都要向下转型,特别麻烦:
ArrayList arrayList = new ArrayList();
arrayList.add("abc");
Object o = arrayList.get(0);
String s = (String)o;
System.out.println(s.length());
加上泛型后,就不用再向下转型:
ArrayList<String> stringArrayList = new ArrayList();
stringArrayList.add("abc");
String s1 = stringArrayList.get(0);
System.out.println(s1.length());
ArrayList存储自定义对象并遍历泛型版
- 新建一个学生类
public class Student {
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
- 用ArrayList存储学生对象并遍历
public class GenericDemo {
public static void main(String[] args) {
ArrayList<Student> students = new ArrayList<>();
students.add(new Student("子贡",30));
students.add(new Student("颜回",28));
for (Student student : students) {
System.out.println(student.toString());
}
}
}
/*Student{name='子贡', age=30}
* Student{name='颜回', age=28}
*/
泛型的由来
没有泛型以前,我们为了一个类的扩展性比较好,就用Object类型来做,比如我们来创建一个老师类:
public class Teacher {
private Object obj;
public void Teacher(){
}
public Object getObj() {
return obj;
}
public void setObj(Object obj) {
this.obj = obj;
}
}
每次使用这个类时创建的对象都要向下转型:
public class GenericDemo2 {
public static void main(String[] args) {
Teacher teacher = new Teacher();
teacher.setObj("孔夫子");
Object obj = teacher.getObj();
//向下转型
String s = (String) obj;
System.out.println(s);
}
}
早期的Object类型可以接收任意的对象类型,但是在实际的使用中,会有类型转换的问题,所以Java提供了泛型来解决这个安全问题。
泛型机制是DK1.5 之后引入的一种机制,所谓的泛型机制,就是把数据类型明确工作,推迟到创建对象,或者调用方法时,才去明确的一种机制。
- 泛型有三种使用方式:
- 泛型类
- 泛型接口
- 泛型方法
泛型类
- 格式:
public class 类名<数据类型 , ....> {}
- 案例演示:
//泛型类:泛型类型用于类的定义中
//X在这里就表示泛型,可以写成任意标识,常见的有T,E,K,V等
//在实例化泛型类的时候,必须指定X的具体类型
public class MyGeneric<X> {
//私有属性
private X x;
//有参构造时必须指明具体类型,必须是引用类型
public MyGeneric(X x) {
this.x = x;
}
public MyGeneric() {
}
public X getX() {
return x;
}
public void setX(X x) {
this.x = x;
}
}
MyGeneric显然是一个泛型类,那么泛型类实例化时就一定要指定类型吗?并非如此,如果指定类型,那么泛型就会起到它的作用,就会作出相应的限制。如果不指明类型,使用时就要向下转型:
public class MyTest {
public static void main(String[] args) {
//case1:指定类型
MyGeneric<String> stringMyGeneric = new MyGeneric("这里只能传String类型的参数了");
String x = stringMyGeneric.getX();
//你可以直接使用String类的方法
System.out.println(x.length());//17
//case2:不指定类型
MyGeneric myGeneric = new MyGeneric("想传什么类型的参数都可以");
//不知道什么类型,返回Object来代替
Object o = myGeneric.getX();
//如果要使用某个类型的方法,就要向下转型
String s = (String) o;
System.out.println(s.length());//12
}
}
注意,不能对确切的泛型类型使用instanceof操作,错误示范:
if(ex_num instanceof MyGeneric<String>){
}
泛型接口
泛型接口与泛型类的定义及使用基本相同。泛型接口常被用在各种类的生产器中。
- 格式:
public interface 接口名<泛型类型>
- 案例演示:
public interface MyGenericInterface<T> {
public T sayHello();
}
- case1:实现泛型接口的子类已经传入泛型实参,即已经指定类型
public class MyTest implements MyGenericInterface<String>{
@Override
public String returnHello() {
return "helloword";
}
}
- case2:实现泛型接口的子类没有传入泛型实参,即没有指定类型
- 这时候这个子类也要定义成泛型
public class MyTest2<G> implements MyGenericInterface<G>{
@Override
public G returnHello() {
return null;
}
}
泛型通配符
- A:泛型通配符
<?>
: 任意类型,如果没有明确,就是Object以及任意的Java类 - 类型通配符一般是使用?代替具体的类型实参而不是类型形参,可以把?看成所有类型的父类。是一种真实的类型。当操作类型时,不需要使用类型的具体功能时,只使用Object类中的功能。那么可以用 ? 通配符来表未知类型。
- 泛型上下边界
<? extends E>
:传入的类型实参必须是指定类型的子类型<? super E>
:传入的类型实参必须是指定类型的父类型- 案例演示:
public class Tester<X> {
private X x;
public Tester(X x) {
this.x = x;
}
}
public class myTest {
public static void main(String[] args) {
Tester<? extends Number> tester = new Tester(50);
Tester<? extends Number> tester1 = new Tester(60f);
Tester<? extends Number> tester2 = new Tester(60d);
Tester<? super Object> tester3 = new Tester(new Object());
}
}
泛型方法
- 格式:
public <泛型类型> 返回类型 方法名(泛型类型 变量名)
- 注意:在泛型类中使用了泛型形参的方法并不是泛型类
比如:
public class MyGenericFunction<T> {
//不是泛型方法,只是使用了泛型类的这个未知类型
public void genericPrint(T t){
System.out.println(t);
}
}
- 案例演示:
public class MyGenericFunction {
//泛型方法
public <T> void genericPrint(T t){
System.out.println(t);
}
}
public class Test {
public static void main(String[] args) {
MyGenericFunction myGenericFunction = new MyGenericFunction();
String s = "123456";
int i = 123456;
double d = 100.00;
//可以传入各种类型的实例化对象
myGenericFunction.genericPrint(s);
myGenericFunction.genericPrint(i);
myGenericFunction.genericPrint(d);
}
}
可变参数
如果定义方法的时候不知道该定义多少个参数怎么办?这个时候就可以用到可变参数。
- 格式:
修饰符 返回值类型 方法名(数据类型… 变量名){}
- 案例演示:
public class Tester {
public static void main(String[] args) {
int sum = add(1,2,3,4,5,6);
System.out.println(sum);//21
}
//这个方法用来返回不限量的整型数的和
public static int add(int... a) {
int sum = 0;
for (int s : a) {
sum += s;
}
return sum;
}
}
Arrays工具类的asList()方法的使用
数组工具类中有一个方法:
static <T > List < T >
asList(T...a)
返回一个受指定数组支持的固定大小的列表。
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class ArraysDemo {
public static void main(String[] args) {
List abc = Arrays.asList(10, 20, 30, 30, 30, 100);
for (Object o : abc) {
System.out.println(o);
}
System.out.println("----------------------");
//传递了一个数组,他把数组中的元素取出来放到集合中去
List<Integer> integers = Arrays.asList(new Integer[]{100, 200, 300});
System.out.println(integers);
//传递了多个数组,他将数组整体作为元素存到集合中去
List<Integer[]> integers1 =
Arrays.asList(
new Integer[]{100, 200,300},
new Integer[]{1003, 2005, 3005},
new Integer[]{1004, 20066, 300333});
System.out.println(integers1);
//遍历
for (Integer[] integers2 : integers1) {
for (Integer integer : integers2) {
System.out.println(integer);
}
}
}
}
集合嵌套
- 我们先来新建一个学生类
public class Students {
private String name;
private int age;
public Students(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Students{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
- 每一个学生都是对象,现在,我们把我们班的所有学生放进一个集合,那么隔壁班的所有学生也写进一个集合,再把这两个集合放进一个大的集合,就是年级。
import java.util.ArrayList;
public class Demo {
public static void main(String[] args) {
ArrayList<Students> class1 = new ArrayList<>();
ArrayList<Students> class2 = new ArrayList<>();
class1.add(new Students("张三", 19));
class1.add(new Students("李四", 20));
class2.add(new Students("王五", 21));
class2.add(new Students("赵六", 21));
ArrayList<ArrayList> grade = new ArrayList();
grade.add(class1);
grade.add(class2);
//遍历输出每一个同学的信息
for (ArrayList<Students> arrayList : grade) {
for (Students students : arrayList) {
System.out.println(students.toString());
}
}
}
}
//Students{name='张三',age=19}
//Students{name='李四',age=20}
//Students{name='王五',age=21}
//Students{name='赵六',age=21}