Java基础2——集合框架
Collection和Map是集合的顶层接口
先看Collection,这个接口定义了以下方法:
添加功能:
boolean add(Object obj):添加一个元素
boolean addAll(Collection c):添加一个集合的元素
删除功能
void clear():移除所有元素
boolean remove(Object o):移除一个元素
boolean removeAll(Collection c):移除一个集合的元素(是一个还是所有)
判断功能
boolean contains(Object o):判断集合中是否包含指定的元素
boolean containsAll(Collection c):判断集合中是否包含指定的集合元素(是一个还是所有)
boolean isEmpty():判断集合是否为空
获取功能
Iterator<E> iterator()(重点)
长度功能
int size():元素的个数
面试题:数组有没有length()方法呢?字符串有没有length()方法呢?集合有没有length()方法呢?
交集功能
boolean retainAll(Collection c):两个集合都有的元素?思考元素去哪了,返回的boolean又是什么意思呢?
把集合转换为数组
Object[] toArray()
List和Set是Collection集合的两个子接口
List是一个有序的队列,每一个元素都有它的索引,第一个元素的索引值是0
ArrayList、Vector、LinkedList、Stack是List的四个实现类,其中比较常用的是ArrayList(线性表)、LinkedList(链表)
Set是一个不允许有重复元素的集合
Set的实现类有HastSet和TreeSet,其中比较常用的是HashSet
Collection通用方法举例
Collection c=new ArrayList(); Student s1=new Student("aaa",15); Student s2=new Student("bbb",16); Student s3=new Student("ccc",17); Student s4=new Student("ddd",18); Student s5=new Student("eee",19); Student s6=new Student("fff",20); //添加一个或多个元素 c.add(s1); c.add(s2); //======== Collection c1=new ArrayList(); c1.add(s3); c1.add(s4); c.addAll(c1); //======== Collection c2=new ArrayList(); c2.add(s5); c2.add(s6); c.addAll(c2); //======== //移除一个或多个元素 c.remove(s1); c.removeAll(c1); //查看某个元素是否存在于集合中 System.out.println(c.contains(s2)); System.out.println(c.containsAll(c2)); //判断集合是否为空 System.out.println(c.isEmpty()); //判断集合中有几个元素 System.out.println(c.size()); //集合转数组 此处不可以用Student[]类型来接收,存入集合的元素都将变成Object类型的 Object[] objs=c.toArray(); //清空集合 c.clear(); //迭代遍历 Iterator it=c.iterator(); while(it.hasNext()){ Student s=(Student)it.next(); System.out.println(s.name+" "+s.age); }
List集合特有的功能
/* * List集合的特有功能: * A:添加功能 * void add(int index,Object element):在指定位置添加元素 * B:获取功能 * Object get(int index):获取指定位置的元素 * C:列表迭代器 * ListIterator listIterator():List集合特有的迭代器 * D:删除功能 * Object remove(int index):根据索引删除元素,返回被删除的元素 * E:修改功能 * Object set(int index,Object element):根据索引修改元素,返回被修饰的元素 */ Student s1=new Student("aaa",15); Student s2=new Student("bbb",16); Student s3=new Student("ccc",17); Student s4=new Student("ddd",18); Student s5=new Student("eee",19); Student s6=new Student("fff",20); List list=new ArrayList(); list.add(s1); list.add(s2); //在指定位置添加元素,位置可以是0 1 2,否则会报IndexOutOfBoundsException越界异常 list.add(0,s3); list.add(3,s4); //获取到指定位置的元素 Student s=(Student)list.get(2); System.out.println(s.name+" "+s.age); //删除指定索引的元素 list.remove(0); //设置指定位置的元素 list.set(0, s5); System.out.println("-----------------"); //其实这里可以通过ListIterator实现遍历 //而且ListIterator可以实现逆向遍历,但是必须先正向遍历,才能逆向遍历,所以一般无意义,不使用 Iterator it=list.iterator(); while(it.hasNext()){ Student ss=(Student)it.next(); System.out.println(ss.name+" "+ss.age); }
/* * 问题? * 我有一个集合,如下,请问,我想判断里面有没有"world"这个元素,如果有,我就添加一个"javaee"元素,请写代码实现。 * * ConcurrentModificationException:当方法检测到对象的并发修改,但不允许这种修改时,抛出此异常。 * 产生的原因: * 迭代器是依赖于集合而存在的,在判断成功后,集合的中新添加了元素,而迭代器却不知道,所以就报错了,这个错叫并发修改异常。 * 其实这个问题描述的是:迭代器遍历元素的时候,通过集合是不能修改元素的。 * 如何解决呢? * A:迭代器迭代元素,迭代器修改元素 * 元素是跟在刚才迭代的元素后面的。 * B:集合遍历元素,集合修改元素(普通for) * 元素在最后添加的。 */ List list=new ArrayList(); list.add("hello"); list.add("world"); list.add("java"); for (int x = 0; x < list.size(); x++) { String s = (String) list.get(x); if ("world".equals(s)) { list.add("javaee"); } } System.out.println("list:" + list);
ArrayList里面没有什么特有的功能
LinkedList特有的功能↓
A:添加功能
public void addFirst(Object e)
public void addLast(Object e)
B:获取功能
public Object getFirst()
public Obejct getLast()
C:删除功能
public Object removeFirst()
public Object removeLast()
数组中必须存放同种类型的元素,那想要约束一个集合必须存储同种类型的元素就要用到泛型,集合中的泛型只能是引用类型
泛型可以把运行时的错误提前到编译期,同时避免了强制从Object到特定类型的类型转换,同时在Eclipse中也没有黄色的警告线了
ArrayList<String> array = new ArrayList<String>(); array.add("hello"); array.add("world"); array.add("java"); Iterator<String> it = array.iterator(); while (it.hasNext()) { String s = it.next();//不必强转 System.out.println(s); }
泛型类
如下代码:早期的时候,我们使用Object来代表任意的类型。向上转型是没有任何问题的,但是在向下转型的时候其实隐含了类型转换的问题。也就是说这样的程序其实并不是安全的。
public class ObjectTool { private Object obj; public Object getObj() { return obj; } public void setObj(Object obj) { // Object obj = new Integer(30); this.obj = obj; } }
public class ObjectToolDemo { public static void main(String[] args) { ObjectTool ot = new ObjectTool(); // 正常使用 ot.setObj(new Integer(27)); Integer i = (Integer) ot.getObj(); System.out.println("年龄是:" + i); ot.setObj(new String("林青霞")); String s = (String) ot.getObj(); System.out.println("姓名是:" + s); System.out.println("---------"); ot.setObj(new Integer(30)); // ClassCastException 出异常了 String ss = (String) ot.getObj(); System.out.println("姓名是:" + ss); } }
所以Java在JDK5后引入了泛型,提高程序的安全性:
public class ObjectTool<T> { private T obj; public T getObj() { return obj; } public void setObj(T obj) { this.obj = obj; } }
public class ObjectToolDemo { public static void main(String[] args) { ObjectTool<String> ot = new ObjectTool<String>(); // ot.setObj(new Integer(27)); //这个时候编译期间就过不去 ot.setObj(new String("林青霞")); String s = ot.getObj(); System.out.println("姓名是:" + s); ObjectTool<Integer> ot2 = new ObjectTool<Integer>(); // ot2.setObj(new String("风清扬"));//这个时候编译期间就过不去 ot2.setObj(new Integer(27)); Integer i = ot2.getObj(); System.out.println("年龄是:" + i); } }
泛型还可以用于方法和接口
泛型方法
上面的泛型类有一个很不好的地方就是setObj的参数的类型只能是和ObjectTool里面的泛型类一样的类的类型,这样就很不灵活,所以产生了泛型方法
public class ObjectTool { public <T> void show(T t) { System.out.println(t); } }
public class ObjectToolDemo { public static void main(String[] args) { // 定义泛型方法后 ObjectTool ot = new ObjectTool(); ot.show("hello"); ot.show(100); ot.show(true); } }
ObjectTool类本身还可以有泛型,例如ObjectTool<Boolean>,ObjectTool本身的泛型类型和它里面的成员方法的泛型类型没有任何关系
泛型接口
public interface Inter<T> { public abstract void show(T t); }
//第一种情况:已经知道该是什么类型的了,用的很少 public class InterImpl implements Inter<String> { public void show(String t) { System.out.println(t); } }
//第二种情况:还不知道是什么类型的,用的很多 public class InterImpl<T> implements Inter<T> { public void show(T t) { System.out.println(t); } }
public class InterDemo { public static void main(String[] args) { // 第一种情况的测试 Inter<String> i = new InterImpl(); i.show("hello"); // 第二种情况的测试 Inter<Integer> ii = new InterImpl<Integer>(); ii.show(100); Inter<String> iii = new InterImpl<String>(); iii.show("hello"); } }
泛型通配符
/* * 泛型高级(通配符) * ?:任意类型,如果没有明确,那么就是Object以及任意的Java类了 * ? extends E:向下限定,E及其子类 * ? super E:向上限定,E极其父类 */ public class GenericDemo { public static void main(String[] args) { // 泛型如果明确的写的时候,前后必须一致 Collection<Object> c1 = new ArrayList<Object>(); // Collection<Object> c2 = new ArrayList<Animal>(); // Collection<Object> c3 = new ArrayList<Dog>(); // Collection<Object> c4 = new ArrayList<Cat>(); // ?表示任意的类型都是可以的 Collection<?> c5 = new ArrayList<Object>(); Collection<?> c6 = new ArrayList<Animal>(); Collection<?> c7 = new ArrayList<Dog>(); Collection<?> c8 = new ArrayList<Cat>(); // ? extends E:向下限定,E及其子类 // Collection<? extends Animal> c9 = new ArrayList<Object>(); Collection<? extends Animal> c10 = new ArrayList<Animal>(); Collection<? extends Animal> c11 = new ArrayList<Dog>(); Collection<? extends Animal> c12 = new ArrayList<Cat>(); // ? super E:向上限定,E极其父类 Collection<? super Animal> c13 = new ArrayList<Object>(); Collection<? super Animal> c14 = new ArrayList<Animal>(); // Collection<? super Animal> c15 = new ArrayList<Dog>(); // Collection<? super Animal> c16 = new ArrayList<Cat>(); } } class Animal { } class Dog extends Animal { } class Cat extends Animal { }
JDK1.5新特性——可变参
/* * 可变参数:定义方法的时候不知道该定义多少个参数 * 格式: * 修饰符 返回值类型 方法名(数据类型… 变量名){ * * } * * 注意: * 这里的变量其实是一个数组 * 如果一个方法有可变参数,并且有多个参数,那么,可变参数肯定是最后一个 */ public ArgsTest{ public static void main(String args[]){ int r=sum(10,20,30); System.out.println(r); } public static int sum(int... a){ int sum=0; for(int i:a){ sum+=a[i]; } return sum; } }
Arrays.asList传入可变参作为参数
/* * public static <T> List<T> asList(T... a):把数组转成集合 * * 注意事项: * 虽然可以把数组转成集合,但是集合的长度不能改变。 */ public class ArraysDemo { public static void main(String[] args) { // 定义一个数组 // String[] strArray = { "hello", "world", "java" }; // List<String> list = Arrays.asList(strArray); List<String> list = Arrays.asList("hello", "world", "java"); // UnsupportedOperationException // list.add("javaee"); // UnsupportedOperationException // list.remove(1); list.set(1, "javaee"); for (String s : list) { System.out.println(s); } } }
以上是List集合的相关类及其方法的总结,List集合是有序的,即存和取的顺序是一致的,List集合里面的元素可以重复
而Set集合是无序的,存和取的顺序不一致,而且取的时候不保证顺序永久不变,但是作为集合来说,它肯定有自己的存储顺序,Set集合里面的元素是唯一的
Set的一个很重要的实现类是HashSet
此外还有LinkedHashSet,该类的底层数据结构由哈希表和链表组成,哈希表保证元素的唯一性,链表保证元素有顺序,即存进去和取出来的元素顺序相同
Collection和Collections的区别
Collection:是单列集合的顶层接口,有子接口List和Set。
Collections:是针对集合操作的工具类,有对集合进行排序和二分查找的方法
要知道的方法
public static <T> void sort(List<T> list):排序 默认情况下是自然顺序。
public static <T> int binarySearch(List<?> list,T key):二分查找
public static <T> T max(Collection<?> coll):最大值
public static void reverse(List<?> list):反转
public static void shuffle(List<?> list):随机置换
同Collection并列的还有另外一个顶层集合接口——Map
Map中存储的是键值对,类似js里面的json
Map<String,String> hm = new HashMap<String,String>(); hm.put("it002","hello"); hm.put("it003","world"); hm.put("it001","java"); //方式1 键找值 set是键的集合 Set<String> set = hm.keySet(); for(String key : set) { String value = hm.get(key); System.out.println(key+"---"+value); } //方式2 键值对对象找键和值 set2是键值对的集合 Set<Map.Entry<String,String>> set2 = hm.entrySet(); for(Map.Entry<String,String> me : set2) { String key = me.getKey(); String value = me.getValue(); System.out.println(key+"---"+value); }