1集合
集合是java中提供的一种容器,可以用来存储多个数据。
数组的长度是固定的。集合的长度是可变的。集合中存储的元素必须是引用类型数据。
1.1ArrayList集合存储元素(复习)
例:
public class Person { private String name; private int age; public Person() { super(); } public Person(String name, int age) { super(); 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; } public String toString() { return "Person [name=" + name + ", age=" + age + "]"; } }
import java.util.ArrayList; public class Test1 { public static void main(String[] args) { //集合复习:存储基本数据类型 ArrayList<Double> arr1=new ArrayList<Double>(); arr1.add(1.6); arr1.add(2.3); arr1.add(3.6); for(int i=0;i<arr1.size();i++){ System.out.print(arr1.get(i)+" "); } System.out.println(); //集合存储引用数据类型 ArrayList<Person> arr2=new ArrayList<Person>(); arr2.add(new Person("张三",18));//存匿名对象 arr2.add(new Person("小红帽",8)); arr2.add(new Person("大灰狼",55)); for(int i=0;i<arr2.size();i++){ System.out.println(arr2.get(i)); //调用toString方法,已重写 } } }
1.2集合的继承实现关系
常用的:
1.3 Collection接口
Collection接口是集合中的顶层接口,它中定义的所有功能子类都可以使用。
Collection 是层次结构中的根接口。Collection 表示一组对象,这些对象也称为 collection 的元素。一些 collection 允许有重复的元素,而另一些则不允许。一些 collection 是有序的,而另一些则是无序的。
(这里的有序不是指元素在值上的排序,而是指存、取的顺序一致,怎么存就怎么取,有索引下标)
1.3.1 Collection常用方法
Collection是接口,所以全是抽象方法。
需要用多态new对象。
例:
import java.util.Collection; import java.util.ArrayList; public class CollectionTest { public static void main(String[] args) { //创建对象 Collection<String> col=new ArrayList<String>(); //添加元素 col.add("中国"); col.add("你好"); col.add("java"); //判断集合中是否包含某元素 boolean flag=col.contains("java"); if(flag){ System.out.println("true,包含元素"); } //移除元素 boolean dels=col.remove("你好"); //这个是返回布尔值 if(dels){ System.out.println("移除成功"); } //向下转型 ArrayList<String> arr=null; //定义成全局,不然遍历时取不到 if(col instanceof ArrayList){ arr=(ArrayList<String>)col; } System.out.println(); //遍历 System.out.println("遍历结果:"); for(int i=0;i<arr.size();i++){ System.out.print(arr.get(i)+" "); } System.out.println(); System.out.println(); //转成数组 Object[] strs=col.toArray(); System.out.println("遍历数组:"); for(int i=0;i<strs.length;i++){ //String str=(String)strs[i];//这里是Object型数组,如果想转成String数组,不能直接强转,要给元素转型 System.out.print(strs[i]+" "); } System.out.println(); System.out.println(); //清除内容 col.clear(); System.out.println("清除后:"); for(int i=0;i<arr.size();i++){ System.out.print(arr.get(i)+" "); } } }
注意:
1注意方法的返回值类型
2一定要指定泛型,不然还要强转
例:会有警告
1.4 Iterator迭代器
1.4.1定义
java中提供了很多个集合,它们在存储元素时,采用的存储方式不同。
List有序(有下标索引),set无序(看上面的继承体系图)
要取出这些集合中的元素,可通过一种通用的获取方式来完成。
迭代定义:
Collection集合元素的通用获取方式:在取元素之前先要判断集合中有没有元素,如果有,就把这个元素取出来,继续再判断,如果还有就再取出出来。一直把集合中的所有元素全部取出。这种取出方式专业术语称为迭代。
集合中把这种取元素的方式描述在Iterator接口中。
1.4.2 Iterator接口的常用方法
hasNext()方法:用来判断集合中是否有下一个元素可以迭代。如果返回true,说明可以迭代。
next()方法:用来返回迭代的下一个元素,并把指针向后移动一位。
1.4.3 Iterator接口的对象
Iterable对象里有一个iterator() 方法,可以返回Iterator对象,Collection继承了这个方法,那么其子类或实现类就都有了这个方法,可以用这个方法创建Iterator对象。
例:(Iterator接口也可以使用<>来控制迭代元素的类型的)
import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; public class IteratorTest { public static void main(String[] args) { Collection<Integer> col=new ArrayList<Integer>(); col.add(1); col.add(2); col.add(3); col.add(4); //获取迭代器对象 Iterator<Integer> it=col.iterator(); //循环 while(it.hasNext()){ int i=it.next(); //自动拆箱 System.out.println(i); } } }
注意:
1)next()只能用一次,再调用,指针还会往后走,想判断,要先把值获取到,再判断值。
例:
import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; public class I2 { public static void main(String[] args) { Collection<Integer> col=new ArrayList<Integer>(); col.add(1); col.add(2); col.add(3); col.add(4); //获取迭代器对象 Iterator<Integer> it=col.iterator(); //用循环 while(it.hasNext()){ if(it.next()==2){ System.out.println(it.next()); } } } }
说明next()又向后走了,所以要改成这样:
while(it.hasNext()){ int i=it.next(); if(i==2){ System.out.println(i); } }
还可以for循环,但不常用
import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; public class I2 { public static void main(String[] args) { Collection<Integer> col=new ArrayList<Integer>(); col.add(1); col.add(2); col.add(3); col.add(4); for(Iterator<Integer> it=col.iterator();it.hasNext();){ System.out.println(it.next()); } } }
2)在进行集合元素取出时,如果集合中已经没有元素了,还继续使用迭代器的next方法,将会发生java.util.NoSuchElementException没有集合元素的错误。
例:
1.5集合元素的向下转型
集合中可以存储任何对象,那么存放进去的数据就不再是原来的类型了,而是提升成了Object。
如果集合中存放的是多个对象,这时进行向下转型会发生类型转换异常。
3增强for循环
增强for循环是JDK1.5以后出来的一个高级for循环,专门用来遍历数组和集合的。它的内部原理其实是个Iterator迭代器,所以在遍历的过程中,不能对集合中的元素进行增删操作。
3.1格式:
for(元素的数据类型 变量 : Collection集合or数组){
}
例:
import java.util.ArrayList; import java.util.Collection; public class forTest { public static void main(String[] args) { Collection<String> col=new ArrayList<String>(); col.add("你好"); col.add("快乐"); //使用增强for for(String str:col){ System.out.println(str); //取到的就是String对象 } //使用增强for遍历数组 int[] arr={1,2,3,4,5}; for(int i:arr){ System.out.print(i+" "); //i就是每个值 } } }
3.2对比:
1)迭代器只能迭代集合,增强for能遍历集合和数组。
2)新for循环必须有被遍历的目标。目标只能是Collection或者是数组。只用来遍历,不进行任何操作。
遍历数组时,如果仅为遍历,可以使用增强for。
如果要对数组的元素进行操作,使用老式for循环,(因为可以通过下标操作)
3.3总结:
遍历集合:三种方式
遍历数组:两种方式
import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; public class Test1 { public static void main(String[] args) { showList(); showArr(); } public static void showList(){ Collection<String> col=new ArrayList<String>(); col.add("abc"); col.add("ABC"); col.add("12345"); //for循环遍历 System.out.println("for循环遍历集合:"); ArrayList<String> arr=null; if(col instanceof ArrayList){ arr=(ArrayList<String>)col; } for(int i=0;i<arr.size();i++){ System.out.print(arr.get(i)+" "); } //迭代器 System.out.println(); System.out.println("迭代器集合:"); Iterator<String> it=col.iterator(); while(it.hasNext()){ System.out.print(it.next()+" "); } //增强for System.out.println(); System.out.println("增强for遍历集合:"); for(String str:col){ System.out.print(str+" "); } } public static void showArr(){ System.out.println(); int[] arr={1,2,3,4,5}; System.out.println("普通for遍历数组:"); for(int i=0;i<arr.length;i++){ System.out.print(arr[i]+" "); } System.out.println(); System.out.println("增强for遍历数组:"); for(int i:arr){ System.out.print(i+" "); } } }
4泛型
前面一些例子,集合中是可以存放任意对象的,提升成Object类型,这时容易发生
ClassCastException类型转换异常,泛型就是为了解决这个问题。
4.1定义
使用集合时,必须明确集合中元素的类型,这种方式称为泛型。
泛型,用来灵活地将数据类型应用到不同的类、方法、接口当中。将数据类型作为参数进行传递。
定义格式:
修饰符 class 类名<代表泛型的变量> { }
修饰符 interface接口名<代表泛型的变量> { }
E就是一个变量,是element的意思,在什么时候明确:
1)实现或继承时明确
Public class MyList<String> implements list<E>{}
Public class MyList<E> implements list<String>{}
2)new对象时明确
Api中带<E>的方法都是如此
4.2使用泛型的好处
将运行时期的ClassCastException,转移到了编译时期变成了编译失败。避免了类型强转的麻烦。
Tips:
泛型:又叫伪泛型,编译时不进.class文件 (注释也不进)
只在java代码中对集合存储数据类型进行约束
4.3泛型通配符
当定义方法时,无法确定具体集合中的元素类型是什么,可以使用泛型通配符<?>,用来占空。
例:
import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.Iterator; public class Test02 { public static void main(String[] args) { ArrayList<String> arr=new ArrayList<String>(); arr.add("中国"); arr.add("java"); HashSet<Integer> set=new HashSet<Integer>(); set.add(1); set.add(2); showAll(arr); showAll(set); } //泛型通配符 public static void showAll(Collection<?> col){ Iterator<?> it=col.iterator(); while(it.hasNext()){ System.out.println(it.next()); } } }
但是一旦使用泛型的通配符后,只能使用Object类中的共性方法,集合中元素自身方法无法使用。(可以向下转型,但是太麻烦了)
4.4泛型限定
泛型里面没有向上向下转型的概念。所以要定好。
限定泛型的上限:
格式:? extends E
? 代表接收E类型或者E的子类型的元素
限定泛型的下限:
格式:? super E
? 代表接收E类型或者E的父类型的元素(父类型,不只是父类)
例:
//员工类 public abstract class Emp { public abstract void work(); }
//经理类 public class Manager extends Emp{ public void work() { System.out.println("经理管理"); } }
//服务员类 public class Waiter extends Emp{ public void work() { System.out.println("服务员上菜"); } }
//厨师类 public class Cooker extends Emp{ public void work() { System.out.println("厨师炒菜"); } }
import java.util.ArrayList; public class Test03 { //四个类:员工,经理,服务员,厨师 都有work方法 //传入三个类调用其work方法 public static void main(String[] args) { Manager m=new Manager(); ArrayList<Manager> arr1=new ArrayList<Manager>(); arr1.add(m); Waiter w=new Waiter(); ArrayList<Waiter> arr2=new ArrayList<Waiter>(); arr2.add(w); Cooker c=new Cooker(); ArrayList<Cooker> arr3=new ArrayList<Cooker>(); arr3.add(c); working(arr1); working(arr2); working(arr3); ArrayList<Object> arr4=new ArrayList<Object>(); arr4.add("这是一个Object类型的数据"); arr4.add("这是一个Object类型的数据2"); working2(arr4); } public static void working(ArrayList<? extends Emp> arr){ //限定泛型的上限 Emp emp=arr.get(0); emp.work(); } public static void working2(ArrayList<? super Emp> arr){ //限定泛型的下限 for(int i=0;i<arr.size();i++){ System.out.println(arr.get(i)); } } }
这里如果是接口,也可以用extends。