List的三个子类
List的三个子类的特点
-
ArrayList:
底层数据结构是数组,查询快,增删慢。
线程不安全,效率高。 -
Vector:
底层数据结构是数组,查询快,增删慢。
线程安全,效率低。 -
LinkedList:
底层数据结构是链表,查询慢,增删快。
线程不安全,效率高。使用时要针对他们的特点来选取最合适的一种。
ArrayList概述
可调整大小的数组实现List接口。 实现所有可选列表操作,并允许所有元素,包括null 。 除了实现List 接口之外,该类还提供了一些方法来操纵内部使用的存储列表的数组的大小。
Arraylist中的特有方法
-
int indexOf (Object o)
返回此列表中指定元素的第一个出现的索引,或 - 如果此列表不包含元素,则 - 1。 -
int lastIndexOf (Object o)
返回此列表中指定元素的最后一个发生的索引,或 - 如果此列表不包含元素,则 - 1。 -
E remove(int index)
移除此列表中指定位置上的元素 -
boolean remove(Object o)
移除此列表中首次出现的指定元素。 -
E set(int index, E element)
用指定的元素替代此列表中指定位置上的元素。
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]
}
}
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("---------------------");
}
}
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的特有功能演示
- void add(int index, E element)
在此Vector中的指定位置插入指定的元素 - void addElement(E obj)
将指定的组件添加到此向量的末尾,将其大小增加1 - void 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();//返回被删除的元素
}
}
eg: 去除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]
}
}
泛型
泛型的概述
泛型是一种把类型明确的工作推迟到创建对象或者调用方法的时候采取明确的特殊的类型。它的实质是参数化类型,即把类型当做参数一样传递。
泛型的格式
public class 类名<数据类型 , …> {}
这里的数据类型只能是引用数据类型。
泛型的优势
把运行时期的问题提前到了编译期间
避免了强制类型转换
优化了程序设计,解决了黄色警告
泛型只在编译器有效,在运行期就会被擦除
eg:
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());
eg: 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}
*/
泛型接口
泛型接口与泛型类的定义及使用基本相同。泛型接口常被用在各种类的生产器中。
格式:
public interface 接口名<泛型类型>
public interface MyGenericInterface<T> {
public T sayHello();
}
- 实现泛型接口的子类已经传入泛型实参,即已经指定类型
public class MyTest implements MyGenericInterface<String>{
@Override
public String returnHello() {
return "helloword";
}
}
- 实现泛型接口的子类没有传入泛型实参,即没有指定类型
这时候这个子类也要定义成泛型
public class MyTest2<G> implements MyGenericInterface<G>{
@Override
public G returnHello() {
return null;
}
}
泛型通配符
泛型通配符<?>: 任意类型,如果没有明确,就是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 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);
}
}
}
}
eg: 集合嵌套
我们先来新建一个学生类
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}