第一讲.概述
- 集合就是用来存放多个数据的,感觉数据结构这门课很大一部分讲的就是这个
- 集合和数组是不一样的,数组长度不可变、无法保存具有映射关系的数据(图中键值对的概念)等,Java集合类就灵活得多。
- Java的集合大致分为Set、List、Map三种体系,Set代表无序、不可重复的集合;List代表有序、重复的集合;Map代表具有映射关系的集合。Java5以后又出现了Quene体系集合,代表队列。
- Java集合的体系框架图如下:这里面涉及到继承关系、实现方式还有工具类(Collections)及相关比较方法(Comparator)。
第二讲.Collection接口和Iterator遍历器
- 集合的几大功能:增删改查,Collection接口里基本都有定义
- 集合一些共性的方法如下:注意集合中存放的是元素对象的引用;
- //这里ArrayList和HashSet是Collection接口的典型实现类
- Collection a = new ArrayList();
- a.add("SunWuKong");//添加元素
- a.add(6);//集合里不能存放基本类型的元素,但Java支持自动装箱
- a.remove(6);//删除
- a.size();//c集合里元素的个数
- a.contains("SunWuKong"));//是否包含某个元素
- System.out.println(a);//调用toString()方法打印集合元素
- Collection b = new HashSet();
- b.add("SunWuKong");
- b.add("ZhuBaJie");
- a.containsAll(b);//A集合是否包含B集合中的全部元素
- b.retainAll(a);//B集合中只剩A集合也包含的元素,即A,B的交集
- a.removeAll(b);//把A集合中与B集合相同的元素删除,即A-B
- a.clear();//删除A集合中的所有元素
- Iterator遍历集合元素,注意Iterator并不是把集合元素本身传给迭代变量,只是把集合元素的值复制一份传给迭代变量,所以修改迭代变量的值无法改变集合元素本身。利用foreach方法遍历集合元素也是如此。
- //所谓迭代器Iterator,就是集合取出元素的方式所抽象出的接口
- //设计方式:实际上Iterator被设计为Collection接口的内部接口,它统一了集合的取出方式
- //Iterator接口里定义了三个方法:next(),hasNext(),remove()
- Collection c = new ArrayList();
- c.add("Java01");
- c.add("Java02");
- c.add("Java03");
- c.add("Java04");
- Iterator it = c.iterator();
- while(it.hasNext()){
- System.out.println(it.next());
- //注意,使用迭代器对集合进行迭代访问时,不能通过集合的add()、remove()等方法改变集合元素。
- //否则会引发java.util.Concurrent ModificationException并发修改异常
- //只允许用Iterator的remove()方法删除上一次next()返回的元素。
- it.remove();
- }
- System.out.println(c);
第三讲.List集合
- 元素是有序的,元素可重复,因为该集合体系有索引。
- List中的共性方法
- //由于有索引,List提供一些操作角标的共性方法
- int index =2;
- String element = "JavaTest";
- List list = new ArrayList();
- list.add("Java01");
- list.add("Java02");
- list.add("Java03");
- list.add("Java04");
- //list.add(index,elememt);//在索引处插入元素,addAll(index,Collection)同理
- //remove(index);//删
- //set(index,element);//改
- //get(index);//查
- //System.out.println(subList(2,3))//subList(from,to)截取子List,包含头不含尾
- System.out.println(list);
- List中特有的ListIterator列表迭代器
- //List特有的ListInterator迭代器,在Iterator上添加了三个方法:
- //hasPrevious()与Object previous()可以前向遍历,void add()在当前迭代位置添加元素
- ListIterator it = list.listIterator();
- while(it.hasNext()){
- System.out.println(it.next());
- it.add("---------华丽分割线-----------");
- }
- while(it.hasPrevious()){//前向遍历
- System.out.println(it.previous());
- //这里不能it.add(element)了,否则无限循环了。。
- }
- ArrayList:底层为数组数据结构,查找快、增删慢。默认的数组长度为10,如果超出了就延长50%(就是再new个更长点的数组把之前的元素copy过去)。
- Vector:底层数组数据结构,Vector线程安全(ArrayList()线程不安全),Vector是JDk1.0就存在了(元老级),集合框架JDK1.2才出现。由于性能不好,被ArrayList替代了。Vector有个elements()返回Enumeration枚举,它被Iterator替代,但1.0的一些老类(如合并流)还在使用。
- 枚举Enumeraton其实和迭代Iterator是一样的,Iterator在之后的版本中替代了枚举,下面这段代码展示其用法:
- Vector v = new Vector();
- v.add("Element1");
- v.add("Element2");
- v.add("Element3");
- Enumeration en = v.elements();//带element的都是Vector的特有方法
- while(en.hasMoreElements)//由于名字过长,就被Iterator替代了
- //虽然已经被替代了,但是由于像合并流SequenceInputStream也是1.0版本的,它用的是枚举,所以还是有必要了解的
- System.out.println(en.nextElement());
- LinkedList:底层为链表数据结构,查找慢、增删快,它的一些特有方法如下:
- //LinkedListd的特有方法:
- //addFirst(),addLast(),
- //getFirst(),getLast(),
- //removeFirst(),removeLast()
- //上面这些方法(当然不包括add),当列表为空时都返回NoSuchElement异常
- //后期JDK1.6出现了这些方法的升级版,失败时就返回null
- //peek(),获取不删除, 还有peekFirst(),peekLast()
- //poll(),获取并删除, 还有pollFirst(),pollLast()
- //push(),压栈;pop(),出栈
- //这些都是栈操作的一些叫法。。因为链表适合于实现堆栈(频繁增删嘛)。
第四讲.泛型
- 泛型是JDK5.0后引入的新概念, 避免了强制类型转换。就是写的时候可以定义一个形式上的类型,等到调用再时指定具体类型。具体的在下面的代码中就能见到,比较容易理解。
- 泛型限定:向上限定,? extends E:可以接收E和E的子类型。?表示通配符,在函数传参时候用到。例子:Collection<E>的
addAll(Collection<? extendsE> c),代表它可以添加子类型组成的集合。
- 泛型限定:向下限定,? super E:可以接受E和E的福类型。TreeSet<E>的构造方法
TreeSet(Comparator<? superE> comparator)
,代表只要为父类实现了比较器,其子类也能使用。
第五讲.Set集合
- Set是无序的(存入和取出的顺序不一致),元素不能重复
- Set的方法和Collection的方法是一致的,只是有元素不重复的限制。所有Set实现类都有自己的去重方法,保证元素唯一性。
- HashSet:底层数据结构为哈希表,就是按照哈希值分区存放的表。它通过哈希值进行散列,期望令数据尽量均匀分散分布。取元素时就按照哈希值查表,所以较快。
- Object中定义了int hashCode()方法计算哈希值,toStrint()方法返回的字符串就是"类名@哈希值"。HashSet就是通过hashCode()加boolean equals(Object obj)去重的,如果哈希值相同就去调用equals(),为true才说明两元素相同;如果元素哈希值就不同了,那么直接就认定不相同就不会比较equals了(做一下对比:ArrayList中的contains方法判断元素相同就只依赖equals了)。我们可以通过覆写hashCode和equals方法来定义自己的HashSet去重方法。(注:Object中的equals()方法通过return this==obj方式判断相等。)
- TreeSet:底层数据结构为二叉树,可以集合元素保序存放。当存入自定义对象时,一种是要声明其实现Comparable<T>接口并覆写int compareTo(T obj)方法,这种就叫做对象的自然顺序;另一种在new TreeSet时传入比较器接口Comparator<T>(需要实现一个int compare(T o1,T o2)的方法)实现类实例,可以做到强制按给定顺序排序。当然比较操作返回0就可以判断元素是否相等了,不过Java还是会再判断一下equals来保持程序的健壮性。
- 这里写了一个覆写各种去重相关函数的类Person
- class Person implements Comparable<Person>//泛型的写法,Comparable就是一个泛型接口
- {
- private String name;
- private int age;
- boolean equals(Object obj){//注意参数为Object类型,没泛型的事!
- if(!obj instanceof Person)
- return false;
- Person p = (Person)obj;
- return this.name==p.name&&this.age==p.age;
- }
- int hashCode(){
- return name.hashCode()+age*19;//乘个素数,降低偶然相等可能性
- }
- int compareTo(Person p){
- //按年龄排序
- if(this.age!=p.age)
- return this.age-p.age;
- else//如果主要条件相同了就比较次要条件
- return this.name.compareTo(p.name);
- }
- }
- TreeSet小练习:按照字符串长度排序
- //应用匿名内部类
- TreeSet<String> set=new TreeSet<String>(new Comparator<String>(){
- public int compare(String s1,String s2){
- return s1.length()-s2.length();
- }
- });
- set.add("hahahahaha");
- set.add("ha");
- set.add("hahaha");
- set.add("haha");
- set.add("hahahaha");
- for (String item:set)
- System.out.println(item);
- }
第六讲.Map集合
- Map集合存放键值对。Map与Collection同级的。Collection由Map实现。
- Map的共性方法
- //添加
- put(K key,V value);putAll(Map<? extends K,? extends V> m);
- //删除
- clear();remove(Object key);
- //判断
- containsKey(Object key);containsValue(Object value);isEmpty();
- //获取
- get(Object key);size();
- values() 返回此映射中包含的值的 Collection 视图。
- //较重要的两个Set
- entrySet()返回映射关系的Set,之后会提到
- keySet()返回键组成的Set
- 通过keySet()方法遍历Map:
- Map<String,String> map = new HashMap<String,String>();
- map.put("孙悟空","五指山");
- map.put("猪八戒","高老庄");
- map.put("沙和尚","流沙河");
- map.put("唐三藏","女儿国");
- for(String item:map.keySet())
- System.out.println(item+"::"+map.get(item));
- Map中的内部类Map.Entry,代表了映射关系,通过entrySet()方法遍历Map:
- //Map.Entry就是把Key和Value封装成一个类了
- //Map和Set关系密切,从Java源代码看,Java是先实现了Map,然后通过包装一个所有value都为null的Map就实现了Set
- for(Map.Entry<String,String> item:map.entrySet())
- System.out.println(item.getKey()+"::"+item.getValue());
- HashMap和HashTable,两者关系完全与ArrayList和Vector的关系相同。HashTable比较古老的类,线程安全,不允许空键空值。HashMap运行使用null作为key和value。它们底层通过哈希表实现,可以详见HashSet,不同的是Map里比较的是key。
- TreeMap可以按照key排序,详见TreeSet,同样有实现Comparable接口和创建时传入Comparator实现类实例两者排序方式。
- 小练习:获取字符串中字母出现的次数
- //获取字符串中字母出现的次数
- String str = "saweasdfareaefasffewafaegare";
- Map<Character,Integer> map = new HashMap<Character,Integer>();
- for(Character ch:str.toCharArray())
- if(map.containsKey(ch))
- map.put(ch,map.get(ch)+1);
- else
- map.put(ch,1);
- for(Character ch:map.keySet())
- System.out.println(ch+"("+map.get(ch)+")");
第七讲.工具类Collections和Arrays
- Collections专门用来操作集合的工具类,里面全是静态方法。充分地运用了泛型。
- Collections.sort()方法:sort(List<T> list, Comparator<? super T> c) 根据指定比较器产生的顺序对指定列表进行排序。
- Collections.synchronizedList()方法:public static <T> List<T> synchronizedList(List<T> list),返回同步列表。
- 还有max,binarySearch,fill:把集合中所有元素用指定值替换,repalceAll:老值替换为新值,reverse,reverseOrder:返回一个逆向比较器,等方法详见JDK。
- Arrays专门用来操作数组
- Arrays也有很多操作数组的便捷方法,像sort排序,toString()显示数组元素,asList()转化为列表(不能变长度,不能进行增删)
- 为了限定地元素的操作,需要把集合变为数组,不让对其进行增删。Collection提供toArray()方法把集合转变为数组。