11.4 泛型方法
11.4.1 泛型方法的调用(重要)
泛型的形式有两种:
1、泛型类和泛型接口
【修饰符】 class 类名<类型变量>{
}
【修饰符】 interface 接口名<类型变量>{
}
2、泛型方法
【修饰符】 <类型变量> 返回值类型 方法名(【形参列表】)【throws 异常类型列表】{
}
泛型方法的<类型变量>的具体类型在方法被调用时,通常由实参的类型自动推断。
演示案例:
在java.util.Arrays工具类:
public static <T> List<T> asList(T... a)
List<Integer> integers = Arrays.asList(1, 2, 3, 4, 5);
List<String> strings = Arrays.asList("hello", "java", "world");
在java.util.Collection<E>接口中:
public Object[] toArray():返回笼统的Object[],不管元素是什么类型。
public <T> T[] toArray(T[] a):可以根据元素的具体类型,创建对应的数组,然后通过toArray方法,把元素放进去。
import org.junit.Test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
public class TestGenericMethod {
@Test
public void test1(){
List<Integer> integers = Arrays.asList(1, 2, 3, 4, 5);
System.out.println(integers);
}
@Test
public void test2(){
List<String> strings = Arrays.asList("hello", "java", "world");
System.out.println(strings);
}
@Test
public void test3(){
List<String> strings = Arrays.asList("hello", "java", "world");
// strings.add("atguigu");//java.lang.UnsupportedOperationException不支持该操作
//Arrays.asList方法返回的List集合是一个不可变集合,即不能再添加元素
// strings.remove("hello");
System.out.println(strings);
System.out.println(strings.getClass());
//class java.util.Arrays$ArrayList
}
@Test
public void test4(){
List<Object> objects = Arrays.asList("hello", "java", 1, 2, 3, new TestGenericMethod());
}
@Test
public void test5(){
Collection<String> list = new ArrayList<>();
list.add("hello");
list.add("java");
list.add("world");
Object[] objects = list.toArray();
//String[] strings = (String[]) objects;
/*'
String是Object的子类
String[]是Object[]的子类,也不代表
运行时类型如果是Object[]类型,也不能向下转型为String[]
*/
//查看元素的值,以及元素的字符串长度
for (int i = 0; i < objects.length; i++) {
System.out.println(objects[i] + "->" + ((String)objects[i]).length());
}
System.out.println("---------------------");
String[] arr = new String[list.size()];
list.toArray(arr);
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i] + "->" + arr[i].length());
}
}
}
11.4.2 泛型方法的声明
3、自定义泛型方法
案例:我们编写一个集合工具类,实现将多个元素都添加到一个Collection集合中。
import java.util.Collection;
public class MyTools {
//我们编写一个集合工具类,实现将多个元素都添加到一个Collection集合中。
public static <T> void addAllToCollection(Collection<T> c, T... elements){
for (int i = 0; i < elements.length; i++) {
c.add(elements[i]);
}
}
}
@Test
public void test6() {
Collection<String> list = new ArrayList<>();
MyTools.addAllToCollection(list,"hello","world","java");
list.add("atguigu");
System.out.println(list);
}
@Test
public void test7() {
Collection<Integer> list = new ArrayList<>();
MyTools.addAllToCollection(list,1,2,3,4,5);
list.add(6);
System.out.println(list);
}
@Test
public void test8() {
Collection<Object> list = new ArrayList<>();
MyTools.addAllToCollection(list,1,2,3,4,5);
list.add(6);
System.out.println(list);
}
11.4.3 区别
4、在泛型类/泛型接口名后面声明的泛型,与在方法的返回值类型前面声明的泛型有什么区别?
(1)声明的位置不同
(2)作用范围不同
(3)泛型方法本身可以是静态的,或非静态的
但是泛型类或泛型接口上面声明的泛型绝对不可以在静态成员上使用。
(4)<类型变量>确定具体类型的时机不同
在泛型类/泛型接口名后面声明的泛型,在对象创建时,或子类/实现类继承/实现时确定。
在方法的返回值类型前面声明的泛型,在方法被调用时,自动由实参的类型确定。
结论:
如何选择在哪里声明泛型?
原则:这个泛型的类型使用范围是什么,如果只在单个方法上使用,和其他成员无关,那么使用泛型方法更简洁。
如果需要在多个方法上“同时”使用,即它们在多个成员上是一致的,那么只能在类或接口名后面声明。
11.5 <类型变量>
1、类型变量声明的位置:
(1)在定义类或定义接口时,在类名或接口名后面声明
【修饰符】 class 类名<类型变量>{
}
【修饰符】 interface 接口名<类型变量>{
}
(2)在定义方法是,在返回值类型前面声明
【修饰符】 <类型变量> 返回值类型 方法名(【形参列表】)【throws 异常类型列表】{
}
2、<类型变量>什么时候确定具体的类型?
(1)类名<类型变量>或接口名<类型变量>
- 用泛型类或泛型接口声明变量并创建对象时
java.util.ArrayList<E>
ArrayList<String> list = new ArrayList<>();
- 在子类继承泛型父类或实现泛型父接口时
java.lang.Comparable<T>
public class Student implements Comparable<Student>{
}
public class Student<T>{
private String name;
private T score;
}
public class ChineseStudent extends Student<String>{
}
public class ChineseStudent<E> extends Student<E>{//相当于用E来指定T的类型
}
(2)泛型方法的<类型变量>指定,在方法调用时由实参自动推断
3、<类型变量>必须指定为引用数据类型,不能是基本数据类型
4、强烈建议,<类型变量>用单个的大写字母表示,,等
5、<类型变量>的上限
1、<泛型变量>的上限声明
(1)<类型变量 extends 上限>
(2)上限可以多个,如果有多个,多个之间使用&连接,表示同时满足这几个上限
(3)如果上限有多个,类只能写1个,而且必须是最左边的1个
(4)无论上限是类还是接口,<>这里的关键字都用extends
(5)如果类型变量定义了上限,当泛型擦除时,默认按照“第1个”上限处理。
如果类型变量没有定义上限,当泛型擦除时,默认按照Object处理。
2、泛型擦除:使用泛型类或泛型接口时,没有手动为<泛型变量>直到具体的类型
ArrayList<E>
ArrayList list = new ArrayList();//没有为<E>指定具体类型
案例:
定义一个学生类XueSheng,它包含姓名和成绩两个属性。
但是成绩的类型无法确定?
语文老师:
成绩:85,96等
数学老师:
成绩:85.5,90.0等
成绩必须是Number类型或其子类。
public class XueSheng<T extends Number & Comparable> {
private String name;
private T score;
//...
public XueSheng() {
}
public XueSheng(String name, T score) {
this.name = name;
this.score = score;
}
}
import org.junit.Test;
public class TestXueSheng {
@Test
public void test1(){
XueSheng<Integer> x1 = new XueSheng<>();
XueSheng<Double> x2 = new XueSheng<>();
// XueSheng<String> x3 = new XueSheng<>(); //错误,String不是Number的子类
XueSheng x4 = new XueSheng();//泛型擦除
}
}
11.6 泛型通配符<?>(了解)
3、泛型通配符
(1)什么情况下会使用泛型通配符?
当我们在使用一个泛型类或泛型接口声明变量时,还无法确定<E>的具体类型,
那么此时就有两种解决方案:
A:泛型擦除 ==> 编译器会弹警告
B:使用泛型通配符<?>
例如:使用ArrayList<E>声明一个变量时,无法确定<E>的具体类型时,可以使用ArrayList<?>
(2)泛型通配符有3种形式
A:泛型类<?>
泛型接口<?>
<?>代表任意引用数据类型。
B:泛型类<? extends 上限>
泛型接口<? extends 上限>
<?>代表上限或上限的子类类型,而且这里的上限只能写1个类型。
C:泛型类<? super 下限>
泛型接口<? super 下限>
<?>代表下限或下限的父类类型,而且这里的上限只能写1个类型。
(3)<?>,<? extends 上限>,<? super 下限>
指定的类型必须通过所有可能类型的检查。
import org.junit.Test;
import java.util.ArrayList;
public class TestWildcard {
@Test
public void test1(){
ArrayList<String> list1 = new ArrayList<>();
//...
ArrayList<Integer> list2 = new ArrayList<>();
//
ArrayList<Double> list3 = new ArrayList<>();
//
ArrayList<Object> list4 = new ArrayList<>();
//
print(list1);
print(list2);
print(list3);
print(list4);
print2(list1);
print2(list2);
print2(list3);
print2(list4);
// print3(list1);//错误,String不是Number的子类
print3(list2);
print3(list3);
// print3(list4);//错误,Object不是Number的子类
// print4(list1);//String不是Number的父类
// print4(list2);//Integer不是Number的父类
// print4(list3);//Double不是Number的父类
print4(list4);//Object是Number的父类
}
public void print(ArrayList list){//泛型擦除
for (Object o : list) {
System.out.println(o);
}
}
public void print2(ArrayList<?> list){
//假设加一个操作,在list集合中添加如下几个元素之后再遍历
//<?>的编译时类型,代表任意类型,运行时<?>可能是String,Integer,Double...等
//泛型的目标就是把运行时的类型检查移到了编译时,挨个检查完之后,没有一个值可以通过所有类型检查的
/* list.add("hello");
list.add(1);
list.add(1.0);*/
list.add(null);
for (Object o : list) {
System.out.println(o);
}
}
public void print3(ArrayList<? extends Number> list){
//假设加一个操作,在list集合中添加如下几个元素之后再遍历
/*
<?>的编译时类型,代表任意类型,运行时<?>可能是Integer,Double,Short,BigInteger...等
//泛型的目标就是把运行时的类型检查移到了编译时,挨个检查完之后,没有一个值可以通过所有类型检查的
*/
/* list.add("hello");
list.add(1);
list.add(1.0);*/
list.add(null);
for (Number number : list) {
System.out.println(number);
}
}
public void print4(ArrayList<? super Number> list){
//假设加一个操作,在list集合中添加如下几个元素之后再遍历
/*
<?>的编译时类型,代表任意类型,运行时<?>可能是Number,Object...等
//泛型的目标就是把运行时的类型检查移到了编译时,挨个检查完之后,没有一个值可以通过所有类型检查的
*/
// list.add("hello");//不通过Number的检查
list.add(1);
list.add(1.0);
for (Object o : list) {
System.out.println(o);
}
}
}
11.7 与<?>的区别(了解)
4、<T>代表任意类型,<?>代表任意类型,它们有什么区别?
(1)独立性:
<T>的T可以独立使用,<?>的?不可以独立使用,
<?>,<? extends 上限>,<? super 下限>必须依赖于泛型类或泛型接口
(2)同一个方法中,不同位置的<T>代表同一种类型,但是<?>不一定
(3)<T>必须在类上,或方法的返回值类型前面声明一下,<?>不需要特别声明
<T>声明时,可以指定上限,而且是多个上限。
<?>在使用时,可以指定1个上限,或1个下限。
import org.junit.Test;
import java.util.ArrayList;
public class TestDifferent {
public <T extends Object & Comparable & Cloneable> void m1(T t){
}
public void m2(ArrayList<?> t){
}
public <T> void m3(ArrayList<T> t){
}
public <T> void m4(ArrayList<T> list1, ArrayList<T> list2) {
}
public void m5(ArrayList<? extends Object > list1, ArrayList<? super Integer> list2) {
}
@Test
public void test1(){
ArrayList<String> list1 = new ArrayList<>();
//...
ArrayList<Integer> list2 = new ArrayList<>();
//
// m4(list1, list2);//错误,因为list1和list2的<>中的类型不一致
m5(list1, list2);
}
}
12.1 Set集合
1、Collection系列的集合,以java.util.Collection<E>接口为根接口,
但是Collection<E>接口没有直接的实现类,而是提供更加具体的子接口,
例如:Set<E>、List<E>、Queue<E>等子接口的实现类。
2、Set系列的集合共同特点:元素不可重复。
(1)HashSet:完全无序、元素不可重复。
(2)TreeSet:按照元素的大小顺序排列(有序)、元素不可重复。
(3)LinkedHashSet:遍历结果按照元素的添加顺序(有序)、元素不可重复。
3、TreeSet依赖于Comparable接口,Comparator接口
TreeSet认为元素的标准是Comparable接口的compareTo方法,
或Comparator接口的compare方法
的返回值结果是0就是重复。
4、LinkedHashSet和HashSet依赖于hashCode和equals方法
先以hashCode为依据,如果hashCode相同,再看equals方法
如果hashCode不相同,就不看equals方法
这么设计的原因是为了效率更高。
5、LinkedHashSet是HashSet的子类,
它会把添加到set中的元素用一个“双向链表”按照添加的顺序连接起来。
import java.util.Objects;
public class Student {
private int id;
private String name;
public Student() {
}
public Student(int id, String name) {
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + ''' +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
if (id != student.id) return false;
return Objects.equals(name, student.name);
}
@Override
public int hashCode() {
int result = id;
result = 31 * result + (name != null ? name.hashCode() : 0);
return result;
}
}
import org.junit.Test;
import java.util.*;
public class TestSet {
@Test
public void test1(){
//测试元素不可重复
HashSet<String> set = new HashSet<>();
set.add("hello");
set.add("hello");
set.add("java");
set.add("java");
System.out.println(set);//不可重复
}
@Test
public void test2(){
//测试元素不可重复
ArrayList<String> list = new ArrayList<>();
list.add("hello");
list.add("hello");
list.add("java");
list.add("java");
System.out.println(list);//可重复
}
@Test
public void test3(){
//测试元素不可重复
TreeSet<String> set = new TreeSet<>();
set.add("hello");
set.add("hello");
set.add("java");
set.add("java");
System.out.println(set);//不可重复
}
@Test
public void test4(){
//测试元素不可重复
LinkedHashSet<String> set = new LinkedHashSet<>();
set.add("hello");
set.add("hello");
set.add("java");
set.add("java");
System.out.println(set);//不可重复
}
@Test
public void test5(){
//测试顺序问题
HashSet<String> set = new HashSet<>();
set.add("hello");
set.add("world");
set.add("java");
set.add("hahahha");
System.out.println(set);//[world, java, hello ,hahaha ]
}
@Test
public void test6(){
//测试顺序问题
TreeSet<String> set = new TreeSet<>();//按照String类自然排序Comparable的compareTo比较元素大小
set.add("hello");
set.add("world");
set.add("java");
set.add("abc");
set.add("abc");
System.out.println(set);//[abc, abc, hello, java, world]
}
@Test
public void test7(){
//测试顺序问题
LinkedHashSet<String> set = new LinkedHashSet<>();
set.add("hello");
set.add("world");
set.add("java");
set.add("hahaha");
set.add("hahaha");
System.out.println(set);//[hello, world, java, hahaha, hahaha]
}
@Test
public void test8(){
//测试顺序问题
TreeSet<String> set = new TreeSet<>(new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o1.length() - o2.length();
}
});//按照定制排序Comparator的compare方法比较元素大小
set.add("hello");
set.add("world");
set.add("java");
set.add("hahahaha");
set.add("hahahaha");
System.out.println(set);//[java, hello, hahahaha]
}
@Test
public void test9(){
//测试顺序问题
TreeSet<String> set = new TreeSet<>(new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
int result = o1.length() - o2.length();
return result != 0 ? result : o1.compareTo(o2);
}
});//按照定制排序Comparator的compare方法比较元素大小
set.add("hello");
set.add("world");
set.add("java");
set.add("hahahaha");
set.add("hahahaha");
System.out.println(set);//[java, hello, world, hahahaha]
}
@Test
public void test10(){
HashSet<Student> set = new HashSet<>();
set.add(new Student(1,"张三"));
set.add(new Student(2,"李四"));
set.add(new Student(2,"李四"));
System.out.println(new Student(2,"李四").hashCode());
System.out.println(new Student(2,"李四").hashCode());
System.out.println(set);
}
}
12.2 List集合
12.2.1 List接口
1、List系列的集合共同特征:有序的,可以根据“下标”进行操作,元素可以重复。
2、List接口的常用方法,比Collection根接口多一些方法:
API:
(1)增
boolean add(E e) :添加一个对象到当前集合中
boolean addAll(Collection c) :添加一组对象到当前集合中
void add(int index, E element) :添加一个对象到当前集合的[index]位置
boolean addAll(int index, Collection<? extends E> c) :添加一组对象到当前集合中,位置在[index]
(2)删
void clear() :清空集合中所有的元素
boolean remove(Object o) :从当前集合中删除1个对象
boolean removeAll(Collection c) :从当前集合中删除一组对象
this集合 = this集合 - this集合 ∩ c集合
boolean retainAll(Collection c) :从当前集合中删除一组对象
this集合 = this集合 ∩ c集合
boolean removeIf(Predicate filter) :从当前集合中删除一组对象(Java8引入的)
E remove(int index) :删除[index]位置的元素,并且会返回被删除的元素
(3)改
E set(int index, E element) :用element替换[index]位置的元素,并返回被替换的元素
(4)查
int size() :元素个数
boolean contains(Object o) :判断当前集合中是否包含o对象
boolean containsAll(Collection c) :判断当前集合中是否包含c集合的所有元素
判断c是否是否是当前集合的“子集”
boolean isEmpty() :判断是否为空
List<E> subList(int fromIndex, int toIndex) :截取[fromIndex, toIndex)范围的元素
E get(int index) :获取[index]位置的元素
int indexOf(Object o) :查询o在当前集合中的下标,第一次出现的下标
int lastIndexOf(Object o) :查询o在当前集合中的下标,最后一次出现的下标
(5)遍历
Object[] toArray() :把集合中所有元素放到一个Object[]数组中返回,然后用遍历数组的方式遍历元素。
这种方式不是特别好。
为了遍历,还要创建一个新数组。
Iterator iterator():返回一个迭代器对象,用于遍历当前集合。
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
public class TestList {
@Test
public void test1(){
ArrayList<String> list = new ArrayList<>();
list.add("hello");
list.add("java");
list.add(0,"hahahaha");
System.out.println(list);//[hahahaha, hello, java]
}
@Test
public void test2(){
ArrayList<String> list = new ArrayList<>();
list.add("hello");
list.add("java");
list.add(10,"hahahaha");//java.lang.IndexOutOfBoundsException: Index: 10, Size: 2
}
@Test
public void test3(){
ArrayList<String> list = new ArrayList<>();
list.add("hello");
list.add("java");
ArrayList<String> other = new ArrayList<>();
other.add("world");
other.add("hahahaha");
list.addAll(0, other);
System.out.println(list);
}
@Test
public void test4(){
ArrayList<String> list = new ArrayList<>();
list.add("hello");
list.add("java");
list.add("world");
list.add("hahahaha");
list.add("li");
System.out.println(list.remove(0));
System.out.println(list.remove(0));
System.out.println(list.remove(0));
System.out.println("剩余:" + list.size());
}
@Test
public void test5(){
ArrayList<String> list = new ArrayList<>();
list.add("hello");
list.add("java");
list.add("world");
list.add("hahahaha");
list.add("li");
//实现先进(添加)先出(删除)
while(list.size()>0){
System.out.println(list.remove(0));
}
}
@Test
public void test6(){
ArrayList<String> list = new ArrayList<>();
list.add("hello");
list.add("java");
list.add("world");
list.add("hahahaha");
list.add("li");
//实现先进(添加)后出(删除) 或 后进先出
while(list.size()>0){
System.out.println(list.remove(list.size()-1));
}
}
@Test
public void test7() {
ArrayList<String> list = new ArrayList<>();
list.add("hello");
list.add("java");
list.add("world");
list.add("hahahaha");
list.add("li");
System.out.println("[0]被替换的元素:" + list.set(0, "haha"));
System.out.println("[3]被替换的元素:" + list.set(3, "xixi"));
System.out.println(list);
}
@Test
public void test8(){
ArrayList<String> list = new ArrayList<>();
list.add("hello");
list.add("java");
list.add("world");
list.add("hahahaha");
list.add("li");
List<String> subList = list.subList(1, 3);
System.out.println(subList);
}
@Test
public void test9(){
ArrayList<String> list = new ArrayList<>();
list.add("hello");
list.add("java");
list.add("world");
list.add("hello");
list.add("hello");
System.out.println(list);
System.out.println(list.get(1));
System.out.println(list.indexOf("hello"));
System.out.println(list.lastIndexOf("hello"));
}
}
12.2.2 ListIterator列表迭代器
ListIterator<E> listIterator()
ListIterator<E> listIterator(int index)
ListIterator<E>也是一个接口,是Iterator的子接口。仅限于遍历List系列的集合。
boolean hasNext(); 是否还有下一个元素
E next();获取下一个元素
boolean hasPrevious();是否还有前一个元素
E previous();获取前一个元素
int nextIndex():获取下一个元素下标
int previousIndex():获取前一个元素下标
void add(E e):在迭代器遍历过程中添加一个元素
void remove() :在迭代器遍历过程中删除一个元素
void set(E e) :在迭代器遍历过程中替换一个元素
import org.junit.Test;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.ListIterator;
public class TestListIterator {
@Test
public void test1(){
ArrayList<String> list = new ArrayList<>();
list.add("hello");
list.add("java");
list.add("world");
list.add("hello");
list.add("hello");
//实际开发中常用遍历方式一:foreach
for (String s : list) {
System.out.println(s);
}
System.out.println("---------------------");
//实际开发中常用遍历方式二:Iterator
Iterator<String> iterator = list.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
System.out.println("---------------------");
//实际开发中常用遍历方式三:ListIterator,从前往后遍历
ListIterator<String> listIterator = list.listIterator();
while(listIterator.hasNext()){
// System.out.println(listIterator.nextIndex() + ":" +listIterator.next());
System.out.println(listIterator.next() + ":" + listIterator.nextIndex() );
}
System.out.println("------------------------");
ListIterator<String> listIterator2 = list.listIterator(list.size());
while(listIterator2.hasPrevious()){
// System.out.println(listIterator2.previousIndex() +":" + listIterator2.previous());
System.out.println(listIterator2.previous() + ":" + listIterator2.previousIndex() );
}
}
@Test
public void test2(){
ArrayList<String> list = new ArrayList<>();
list.add("hello");
list.add("java");
list.add("world");
//在hello的后面添加一个元素"atguigu"
ListIterator<String> listIterator = list.listIterator();
while(listIterator.hasNext()){
String element = listIterator.next();
if("hello".equals(element)){
//不要调用集合的add,remove,等方法
listIterator.add("hahahaha");
}
}
System.out.println(list);
}
@Test
public void test3(){
ArrayList<String> list = new ArrayList<>();
list.add("hello");
list.add("java");
list.add("world");
//在hello的前面添加一个元素"atguigu"
ListIterator<String> listIterator = list.listIterator(list.size());
while(listIterator.hasPrevious()){
String element = listIterator.previous();
if("hello".equals(element)){
//不要调用集合的add,remove,等方法
listIterator.add("hahahaha");
}
}
System.out.println(list);
}
}
12.2.3 List实现类
java.util.List接口的实现类们:
(1)ArrayList:动态数组
(2)Vector:动态数组
(3)LinkedList:双向链表(它和动态数组的对比,等讲完链表结构再说)
(4)Stack:它是Vector的子类,栈结构的封装,体现“先进后出”。
Vector与ArrayList的区别:
(1)Vector是旧版的,线程安全的,默认的扩容机制是2倍,默认的初始化容量是10
(2)ArrayList是新版的,线程不安全的,默认的扩容机制是1.5倍。
默认的初始化容量(原来是10,现在JDK7之后是0)
为什么初始化容量要从10改为0?为了尽量节省不必要的内存开销。
public ArrayList method(){
//根据业务场景,返回一些元素对象,放到ArrayList中
ArrayList list = new Arraylist();
//....
return list;
}
ArrayList什么时候创建数组呢?
在第一次添加add元素时再创建数组,创建的数组长度默认还是10。
无论是Vector还是ArrayList,都是可以手动指定初始化容量?
ArrayList(int initialCapacity)
Vector(int initialCapacity)
什么情况下要手动指定初始化容量,有什么好处?
当对集合中的元素个数有预判的时候,提前指定合适的初始化容量,可以避免扩容带来的时空开销。
Stack:比List接口又增加了几个方法,以便凸显它的“先进后出”的特点。
(1)boolean empty():是否为空
(2)E peek():返回栈顶元素,但是不出栈
(3)E pop():弹出栈顶元素
(4)E push(E item) :把元素压入栈,新添加的元素在栈顶,之前添加的在栈底
(5)int search(Object o):查询是否存在o对象
import org.junit.Test;
import java.util.Stack;
public class TestStack {
@Test
public void test1(){
Stack<String> stack = new Stack<>();
stack.push("hello");
stack.push("world");
stack.push("java");
stack.push("hahahaha");
System.out.println(stack);//[hello, world, java, hahahaha]
while(!stack.empty()){
// System.out.println(stack.pop());
System.out.println(stack.peek());
}
}
}
12.2.4 自定义动态数组
import java.util.Arrays;
import java.util.Iterator;
/*
自定义动态数组类
实现java.lang.Iterable<T>接口,允许该集合使用foreach和Iterator遍历
重写 Iterator iterator()
*/
public class MyArrayList<E> implements Iterable<E>{
private Object[] elements = new Object[5];//用来存储元素的数组
private int size;//用来记录实际元素的个数
public void add(E e){
grow();//扩容
elements[size++] = e;
}
public void add(int index, E e){
/*
(1)是否需要扩容
(2)检查下标
实际存储的元素的范围[0, size-1],可以插入的位置是[0,size]
(3)把[index]及其后面的元素 右移,腾出[index]位置
(4)把新元素放到[index]位置
(5)元素个数增加
*/
grow();//扩容
if(index<0 || index>size){
throw new IndexOutOfBoundsException("插入位置有误,[0, "+size+"]");
}
/*
假设 elements.length=6, size=4, index=2
//已经存在的元素下标[0, 3]
elements[3]->elements[4]
elements[2]->elements[3]
一共移动了size-index
*/
System.arraycopy(elements, index , elements,index+1, size-index);
elements[index] = e;
size++;
}
private void grow() {
if(size >= elements.length){
elements = Arrays.copyOf(elements, elements.length + (elements.length>>1));
}
}
public void remove(int index){
/*
(1)检查index
实际存储的元素的范围[0, size-1]
(2)把[index]及其后面的元素 往左移动,覆盖[index]位置的元素
(3)把elements[size-1] = null;
(4)元素个数减少
*/
checkExistIndex(index);
/*
假设 elements.length=6, size=4, index=2
//已经存在的元素下标[0, 3]
elements[3]->elements[2]
一共移动了size-index-1
*/
System.arraycopy(elements, index +1, elements,index, size-index-1);
/* elements[size-1] = null;
size--;*/
elements[--size] = null;
}
/*
删除一个元素,如果有重复的,只删除第一个找到的元素
*/
public void remove(Object obj){
/*
(1)第一步找obj的下标
(2)如果存在,调用remove(index)
*/
int index = indexOf(obj);
if(index != -1){
remove(index);
}
}
public int indexOf(Object obj){
if(obj == null){
for (int i = 0; i < size; i++) {
// if(obj == elements[i]){//对
if(elements[i]==null){
return i;
}
}
}else {
for (int i = 0; i < size; i++) {
if(obj.equals(elements[i])){
return i;
}
}
}
return -1;
}
public int lastIndexOf(Object obj){
if(obj == null){
for (int i = size-1; i >=0; i--) {
// if(obj == elements[i]){//对
if(elements[i]==null){
return i;
}
}
}else {
for (int i = size-1; i >=0; i--) {
if(obj.equals(elements[i])){
return i;
}
}
}
return -1;
}
public boolean contains(Object obj){
return indexOf(obj) != -1;
}
public E get(int index){
/*
(1)检查index
实际存储的元素的范围[0, size-1]
(2)返回[index]位置的元素
*/
checkExistIndex(index);
return (E) elements[index];
}
public void set(int index, E e){
/*
(1)检查index
实际存储的元素的范围[0, size-1]
(2)把[index]位置的元素用e覆盖
*/
checkExistIndex(index);
elements[index] = e;
}
private void checkExistIndex(int index) {
if(index <0 || index >=size){
throw new IndexOutOfBoundsException("有效位置有误,[0, "+(size-1)+"]");
}
}
@Override
public Iterator<E> iterator() {
return new Itr();
}
private class Itr implements Iterator<E>{
int cursor;//游标,默认值是0
@Override
public boolean hasNext() {
//游标的合理范围[0, size-1]
return cursor < size;
}
@Override
public E next() {
return (E) elements[cursor++];
}
}
@Override
public String toString() {
String str = "[";
for (int i = 0; i < size; i++) {
str += elements[i] + (i < size-1 ?",":"]");
}
return str;
}
}
import org.junit.Test;
public class TestMyArrayList {
@Test
public void test1(){
MyArrayList<String> list = new MyArrayList<>();
list.add("hello");
list.add("java");
list.add(0,"chai");
list.add("world");
list.add("atguigu");
for (String s : list) {
System.out.println(s);
}
}
@Test
public void test2(){
MyArrayList<String> list = new MyArrayList<>();
list.add("hello");
list.add("java");
list.add("chai");
list.add("world");
list.add("hahaha");
list.remove(4);
list.remove(2);
list.remove(0);
for (String s : list) {
System.out.println(s);
}
}
@Test
public void test4() {
MyArrayList<String> list = new MyArrayList<>();
list.add("hello");
list.add("java");
list.add("chai");
list.add("world");
list.add("hahaha");
System.out.println(list.indexOf(null));
}
@Test
public void test5() {
MyArrayList<String> list = new MyArrayList<>();
list.add("hello");
list.add(null);
list.add("chai");
list.add("world");
list.add("hahaha");
System.out.println(list.indexOf("world"));
}
@Test
public void test6() {
MyArrayList<String> list = new MyArrayList<>();
list.add("hello");
list.add(null);
list.add("chai");
list.add("world");
list.add("hahaha");
System.out.println(list.indexOf(null));
}
@Test
public void test7() {
MyArrayList<String> list = new MyArrayList<>();
list.add("hello");
list.add(null);
list.add("chai");
list.add("world");
list.add("hahaha");
System.out.println(list);
list.remove(null);
System.out.println(list);
list.remove("chai");
System.out.println(list);
}
}