黑马程序员:集合

来源:互联网 发布:手机ssh连接linux 编辑:程序博客网 时间:2024/06/15 15:57

------- android培训java培训、期待与您交流! -------

一、 集合框架

Iterable:(接口)|-Collection:(接口)|-List:(接口)|-ArrayList|-LinkedList|-Vector|-Set:(接口)|-HashSet|-LinkedHashSet|-TreeSetIterator:迭代器|-ListIterator:列表迭代器(List集合特有的迭代器)Map:(接口)|-HashMap|-TreeMap|-HashTable|-PropertiesUtilities:工具类Collections:集合工具类Arrays:数组工具类</span>

二、 List集合 

List集合的特点:

         List集合代表一个元素有序的,可重复的集合,集合中每一个元素都有其对应的顺序索引。List集合允许使用重复元素,可以通过索引访问指定位子的集合 元素。List集合默认按照元素的添加顺序设置元素索引,例如第一个元素索引为0,第二个添加的元素索引为1...

List是Collection接口的子接口,可以使用Collection接口中的全部方法。而且List集合时有序的,因此List集合中增加了一些根据索引来操作集合汇总元素的方法:

void add(int index,Object element):将元素插入到List集合的index处。boolean addAll(int index,Collection c):将集合c包含的所有元素插入到List集合的index处。Object get(int index):返回集合index索引处的元素。int indexOf(Object o):返回对象o在List集合中第一次出现的位置索引。int lastIndexOf(Object o):返回对象o在List集合中最后一次出现的位置索引。Object remove(int index):删除并返回index索引处的元素。Object set(int index, Object element):将index处的元素替换为element对象,返回新元素。List subList(int fromIndex,int toIndex):返回从索引fromIndex(包含)到索引toIndex(不包含)</span>

         List集合中元素因为有角标所以有了类似于数组的获取元素的方式。在随List集合中的元素进行迭代时,如果想在迭代过程中对元素进行操作(比如满足条件添加新元素),会发生ConcurrentModificationException并发修改异常。导致这种问题的原因是:集合引用和迭代器引用在同时操作元素,通过集合获取到对应迭代后,在迭代中进行集合引用的元素添加,迭代器并不知道,所以会出现异常情况。

         基于上边的问题,List与Set集合不同,List集合不但提供了iterator()方法外,还额外提供了一个listIterator()方法,该方法返回一个ListIterator对象,ListIterator接口继承了Iterator接口,提供了专门操作List的方法。ListIterator接口在Iterator接口基础上增加了如下方法,在迭代过程中对元素进行增删改查:

         booleanhasPrevious():返回该迭代器关联的集合是否还有上一个元素。         Objectprevious():返回迭代器的上一个元素。         voidadd():在指定位置插入一个元素。         voidremove():从列表中移除由next或者previous返回的最后一个元素。         voidset(E e):用指定元素替换由next或者previous返回的最后一个元素。         intnextIndex():返回对 next 的后续调用所返回元素的索引。

1、ArrayList

         底层的数据结构使用的是数组结构,特点在于查询速度很快,但是增删稍慢,线程不同步。

ArrayList集合代码示例:
/*需求:将自定义的对象作为元素存到ArrayList集合中,并去除重复元素。同姓名同年龄,视为重复元素。思路:1.对人描述,将数据封装进人对象2.定义容器。将人存入。3.取出List集合判断元素是否相同,依据的是元素的equals方法。*/import java.util.*;class Person{private String name;private int age;Person(String name, int age){this.name = name;this.age = age;}public String getName(){return name;}public int getAge(){return age;}public String toString(){return name+"..."+age;} //重写equals()方法来判断相同元素。public boolean equals(Object obj){if(!(obj instanceof Person))throw new RuntimeException("不是Person对象");Person p = (Person)obj;return this.name.equals(p.name) && this.age==(p.age);}}class ArrayListDemo{public static void main(String[] args){ArrayList al = new ArrayList();al.add(new Person("lisi1",31));al.add(new Person("lisi2",32));al.add(new Person("lisi2",32));al.add(new Person("lisi3",33));al.add(new Person("lisi4",34));al.add(new Person("lisi4",34));sop(al.toString());al = singleElement(al);sop(al.toString());Iterator it = al.iterator();while(it.hasNext()){Person p = (Person)it.next();sop(p.getName()+":"+p.getAge());}}public static ArrayList singleElement(ArrayList al){//定义一个临时容器newAl,判断如果不包含该元素就添加进集合,否则不添加。ArrayList newAl = new ArrayList();Iterator it = al.iterator();while(it.hasNext()){Object obj = it.next();if(!newAl.contains(obj))//contains的底层原理就是equals,所以要复写equalsnewAl.add(obj);//调用contains运行equals}return newAl;}public static void sop(Object obj){System.out.println(obj);}}

2、LinkedList

 底层数据结构使用的是链表结构,特点是增删的速度很快,查询速度慢。

         LinkedList的特有方法:

void addFirst(E e):将指定元素插入此列表的开头。 void addLast(E e):将指定元素添加到此列表的结尾。 E getFirst():返回此列表的第一个元素。 E getLast():返回此列表的最后一个元素。 E removeFirst():移除并返回此列表的第一个元素。 E removeLast():移除并返回此列表的最后一个元素。 (在JDK1.6出现以上方法的替代方法)
LinkedList集合代码示例:
/*需求:使用linkedList模拟一个堆栈或者队列的数据结构堆栈:先进后出:如同一个杯子队列:先进先出:First in First out: FIFO:如同一个水管。*/import java.util.*;class DuiLie{private LinkedList link;DuiLie(){link = new LinkedList();}public void myAdd(Object obj){link.addFirst(obj);}public Object myGet(){//return link.removeLast();//先进先出return link.removeFirst();//先进后出}public boolean isNull(){return link.isEmpty();}}class LinkedListDemo{public static void main(String[] args){DuiLie dl = new DuiLie();dl.myAdd("java1");dl.myAdd("java2");dl.myAdd("java3");dl.myAdd("java4");while(!dl.isNull()){System.out.println(dl.myGet());}sop(dl.isNull());//true,元素被全部去除}public static void sop(Object obj){System.out.println(obj);}}

3、Vector

 底层的数据结构是数组结构,线程是同步的,查询和增删速度都很慢,被ArrayList取代。

         ArrayList和Vector的显著区别是:ArrayList是线程不安全的,当多个线程访问同一个ArrayList集合时,如果有超过一个线程修改了ArrayList集合,则程序必须手动保证该线程集合的同步性;但是Vector集合则是线程安全的,无需程序保证该集合的同步性。因为Vector线程安全,所以它的性能要比ArrayList低的多。推荐使用ArrayList,并且Collections工具类可以将ArrayList变成线程安全的。

三、 Set集合

Set集合的特点:

         Set接口中的方法和Collection中方法一致的。Set接口取出方式只有一种:Iterator迭代器。Set元素是无序的(存入和取出的顺序不一定一致),该集合体系没有索引。Set集合不允许包含相同的元素,如果试图把两个相同的元素加入同一个Set集合中,则添加操作失败,add()方法返回false,且新元素不会被加入。Set集合判断两个元素是否相同使用的是equals方法,只要两个对象用equals方法返回true,Set就不会接受这两个对象;反之,只要两个对象用equals方法返回true,Set就会接受这两个对象。

1、HashSet

         底层数据结构式哈希表,线程不同步的,高效的。当想HashSet集合中存入一个元素时,HashSet会调用该对象的hashCode()方法来得到该对象的hashCode值,然后根据该对象的hashCode值决定该对象在HashSet集合中的寻出位置。

         HashSet集合保证元素唯一性:通过元素的hashCode方法,和equals方法完成的。当元素的hashCode值相同时,才继续判断元素的equals是否为true。如果为true,那么视为相同元素,不存。如果为false,那么存储。 如果hashCode值不同,那么不判断equals,从而提高对象比较的速度。

HashSet是线程不同步的,如果有多线程访问一个HashSet集合,需要用代码保证其同步。

         HashSet集合代码示例:

/*需求:往HashSet存入自定义对象,姓名和年龄相同为同一个对象*/import java.util.*;class HashSetDemo{public static void main(String[] args){HashSet hs = new HashSet();hs.add(new Person("al",11));hs.add(new Person("a2",12));hs.add(new Person("a2",12));hs.add(new Person("a3",13));hs.add(new Person("a4",14));sop(hs.toString());Iterator it = hs.iterator(); while(it.hasNext()){Person p  = (Person)it.next();sop(p.getName()+"..."+p.getAge());}}public static void sop(Object obj){ System.out.println(obj);}}class Person{private String name;private int age;Person(String name, int age){this.name = name;this.age = age;}public String getName(){return name;}public int getAge(){return age;}public String toString(){return name+"="+age;}//重写hashCode()方法public int hashCode(){return name.hashCode()+age;//return name.hashCode()+age*39;尽量保证hashCode的唯一性}//重写equals()方法public boolean equals(Object obj){if(!(obj instanceof Person))throw new RuntimeException("不是Person对象");Person p = (Person)obj;return this.name.equals(p.name) && this.age==(p.age);} }

         LinkedHashSet:HashSet的子类,LinkedHashSet也是根据元素的hashCode值来决定元素的存储位置,但它同时使用链表维护元素的次序,这样可以使元素看起来是以插入的顺序保存的。也就是说,当遍历LinkedHashSet集合里的元素时,LinkedHashSet将会按元素添加顺序来访问集合里的元素。所以性能略低于HashSet。

哈希表的原理: 

1,对对象元素中的关键字(对象中的特有数据),进行哈希算法的运算,并得出一个具体的算法值,这个值 称为哈希值。

2,哈希值就是这个元素的位置。 

3,如果哈希值出现冲突,再次判断这个关键字对应的对象是否相同。如果对象相同,就不存储,因为元素重复。如果对象不同,就存储,在原来对象的哈希值基础 +1顺延。

4,存储哈希值的结构,我们称为哈希表。 

5,既然哈希表是根据哈希值存储的,为了提高效率,最好保证对象的关键字是唯一的。 这样可以尽量少的判断关键字对应的对象是否相同,提高了哈希表的操作效率。

 对于ArrayList集合,判断元素是否存在,或者删元素底层依据都是equals方法。 

对于HashSet集合,判断元素是否存在,或者删除元素,底层依据的是hashCode方法和equals方法。

2、TreeSet

         TreeSet集合底层数据结构是二叉树,保证元素唯一性的依据是compareTo方法的结果是否为0,如果return 0,视为两个对象重复。

TreeSet集合可以对集合中元素进行排序,排序需要依据元素自身具备的比较性。如果元素不具备比较性,在运行时会发生ClassCastException异常。 所以需要元素实现Comparable接口,强制让元素具备比较性,复写compareTo方法。 依据compareTo方法的返回值,确定元素在TreeSet数据结构中的位置。

注意:在进行比较时,如果判断元素不唯一,比如:同姓名,同年龄,才视为同一个人。 在判断时,需要分主要条件和次要条件,当主要条件相同时,再判断次要条件,按照次要条件排序。 

TreeSet集合排序有两种方式:

1、让元素自身具备比较性,需要让元素对象实现Comparable接口,并覆盖compareTo方法。 这种方式成为元素的自然排序,或者默认排序。

         原理:Comparable接口中定义了一个compareTo(Objectobj)方法,该方法返回一个整数值,实现该接口的类必须实现该方法,实现给接口的类的对象就可以比较大小了。当一个对象调用该方法与另一个对象进行比较时,例如:obj1.compareTo(obj2),如果该方法返回0,则表明这两个对象相等,如果返回一个正整数,则表明obj1>obj2;如果返回一个负整数,则表明obj1<obj2。

2、让集合自身具备比较性,需要定义一个实现Comparator接口的比较器,并覆盖compare方法,并将该对象作为实际参数传递给TreeSet集合的构造函数。

原理:Comparator接口中包含一个intcompare(T o1,T o2)方法,该方法用于比较o1和o2的大小:如果该方法返回正整数,则表明o1>o2;如果返回0,则表明o1=o2;如果方法返回负整数,则表明o1<o2。

TreeSet两种排序代码示例:
import java.util.*;class TreeSetDemo {public static void main(String[] args){method1();sop("----------------------");method2();sop("----------------------");method3();}//按照元素自然顺序排序(其实String类中已经实现了Comparable接口并覆写了comparaTo方法)public static void method1(){TreeSet ts = new TreeSet();ts.add("acbd");ts.add("cd");ts.add("cbd");ts.add("bcbd");Iterator it = ts.iterator();while(it.hasNext()){sop(it.next());}sop("按照自然顺序排序");}public static void method2(){TreeSet ts = new TreeSet();ts.add(new Student("zhangsan",21));ts.add(new Student("lisi",22));ts.add(new Student("nlisi",22));ts.add(new Student("wangwu",23));ts.add(new Student("zhaoliu",24));Iterator it = ts.iterator();while(it.hasNext()){Student s = (Student)it.next();sop(s.getName()+"...."+s.getAge());}sop("按照年龄排序,如果年龄相等,按照姓名字母排序");}public static void method3(){TreeSet ts = new TreeSet(new MyComparator());ts.add(new Student("zhangsan",21));ts.add(new Student("lisi",22));ts.add(new Student("lisi",23));ts.add(new Student("wangwu",23));ts.add(new Student("zhaoliu",24));Iterator it = ts.iterator();while(it.hasNext()){Student s = (Student)it.next();sop(s.getName()+"...."+s.getAge());}sop("按照姓名排序,如果姓名相同,按照年龄升序排序");}public static void sop(Object obj){System.out.println(obj);}}class Student implements Comparable{private int age;private String name;Student(String name,int age){this.name = name;this.age = age;}public int getAge(){return age;}public String getName(){return name;}public int compareTo(Object obj)//按照年龄排序,如果年龄相等,按照姓名排序{if(!(obj instanceof Student))throw new RuntimeException("不是学生对象");Student s = (Student)obj;if(this.age>s.age)return 1;if(this.age==s.age){return this.name.compareTo(s.name);}return -1;}}//按照姓名排序,如果姓名相同,按照年龄升序排序class MyComparator implements Comparator{public int compare(Object o1,Object o2){Student s1 = (Student)o1;Student s2 = (Student)o2;int num = s1.getName().compareTo(s2.getName());if(num==0)return new Integer(s1.getAge()).compareTo(new Integer(s2.getAge()));return num;}}

使用集合的技巧: 
看到Array就是数组结构,有角标,查询速度很快。 
看到link就是链表结构:增删速度快,而且有特有方法。addFirst; addLast; removeFirst(); removeLast(); getFirst();getLast(); 
看到hash就是哈希表,就要想要哈希值,就要想到唯一性,就要想到存入到该结构的中的元素必须覆盖hashCode,equals方法。 
看到tree就是二叉树,就要想到排序,就想要用到比较。 

四、 迭代器

Iterable接口中有一个抽象方法:iterator(),其返回值类型是一个Iterator接口类型。

Iterator接口也是Java集合框架成员,但它与Collection、Map集合不同。前者称为迭代器,主要用于遍历Collection集合中的元素,后者主要用于盛装其他对象。Iterator接口隐藏了各种Collection实现类的底层细节,向应用程序提供了遍历Collection集合元素的统一编程接口。

Iterator接口中定义了三个抽象方法:

boolean hasNext():如果被迭代的集合元素还没有被遍历,则返回true。

Object next():返回集合里的下一个元素。

void remove():删除集合中上一次next方法返回的元素。

注意:

Set和List接口的实现子类复写iterator()方法,其方法内部返回一个实现Iterator接口的子类,子类复写Iterator接口中的三个抽象方法。于是我们在使用迭代器时,Iterator it = al.iterator();其实是多态-子类对象指向接口父类引用。于是我们看到it.hasNext()、it.next(),运行时使用的是子类的方法。

五、 Map集合

Map集合的特点:

         Map集合保存具有映射关系的数据,因此Map集合里保存着两组值,一组值用于保存Map中的key,另外一组值用于保存Map集合中的value。key和value可以是任何引用类型的数据。Map的key不允许重复,即同一个Map对象的任何两个key通过equals方法比较总是返回false。

1、HashTable:底层数据结构是哈希表数据,是线程安全的。不可以存储null键、null值。

         |-Properties:在“黑马程序员:IO流”中有具体讲解。

2、HashMap :底层数据结构是哈希表数据结构,是线程不安全的。可以存储null键、null值,替代HashTable

3、TreeMap:底层数据结构是二叉树数据结构,可以对Map集合中的键进行指定顺序的排序。

Map集合中处了一些添加、获取、判断、删除方法外,有两个重要的方法:

Set<K>keySet()返回此映射中包含的键的Set 视图。

Set<Map.Entry<K,V>>entrySet():返回此映射中包含的映射关系的Set 视图。

因为Map集合中没有迭代器,怎么获取Map集合中的所有元素呢?把Map集合转换为Set集合,用Set集合中的Iterator迭代器间接取出Map集合的所有元素。

有两种方式取出Map集合中的所有元素:

1、Set<K>keySet():将Map集合中的所有键存放在Set集合中,然后通过Set集合迭代器的方式取出所有的键,再根据get(key)方法获取到Map集合键对应的值。

2、Set<Map.Entry<K,V>>entrySet():将Map集合的映射关系存放在Set集合中,这个关系的类型是Map.Entry,然后根据getKey(),getValue()方法获取到Map集合的键和值。

取出Map集合元素的两种方式代码示例:

import java.util.*;class MapDemo{public static void main(String[] args){Map<String,String> m = new HashMap<String,String>();m.put("01","DaZhuang01");m.put("04","DaZhuang04");m.put("02","DaZhuang02");m.put("03","DaZhuang03");//将Map集合中的所有键存放在Set集合中Set<String> keySet = m.keySet();Iterator<String> it = keySet.iterator();while(it.hasNext()){String key = it.next();//获取Map集合中的keyString value = m.get(key);//通过键获取Map集合中的vlauesop("key:"+key+"______"+"value:"+value);}//将Map集合的映射关系存放在Set集合中Set<Map.Entry<String,String>> entrySet = m.entrySet();Iterator<Map.Entry<String,String>> i = entrySet.iterator();while(i.hasNext()){Map.Entry<String,String> me = i.next();//把获取到的键值对存放在Map.Entry类型的引用变量中。String key = me.getKey();//获取keyString value = me.getValue();//获取valuesop("key:"+key+"......"+"value:"+value);}}public static void sop(Object obj){System.out.println(obj);}}

六、  Collections工具类: 

Collections是个java.util下的类,是针对集合类的一个工具类,提供一系列静态方法,实现对集合的查找、排序、替换、线程安全化(将非同步的集合转换成同步的)等操作。

 Collection是个java.util下的接口,它是各种集合结构的父接口,继承于它的接口主要有Set和List,提供了关于集合的一些操作,如插入、删除、判断一个元素是否其成员、遍历等。 

Collections工具类的常用方法:

Collections.sort(list);//list集合进行元素的自然顺序排序。 

Collections.sort(list,new ComparatorByLen());///按指定的比较器方法排序。

Collections.max(list); //返回list中字典顺序最大的元素。 

int index = Collections.binarySearch(list,"zz");//二分查找,返回角标。

Collections.reverseOrder();//逆向反转排序。 

Collections.shuffle(list);//随机对list中的元素进行位置的置换。

将线程不安全的集合进行同步控制的方法:

Collection coll= Collections.synchronizedCollection(new ArrayList());

List  list = Collections.synchronizedList(new ArrayList()); 

Map map = Collections.synchronizedMap(new Map()); 

七、 Arrays工具类: 

用于操作数组对象的工具类,里面都是静态方法。

1、将数组转换成list集合: Arrays.asList(arr);

String[] arr = {"abc","kk","qq"}

List<String> list = Arrays.asList(arr);//将arr数组转成list集合。 

将数组转换成集合,有什么好处呢?用aslist方法,将数组变成集合;可以通过list集合中的方法来操作数组中的元素:isEmpty()、contains、indexOf、set;  

注意(局限性):数组是固定长度的,数组转换为集合后,也不可以使用集合对象增加或者删除等,会改变数组长度的功能方法。比如add、remove、clear。(会报不支持操作异常UnsupportedOperationException); 

如果数组中存储的引用数据类型,数组元素直接作为集合的元素存在,就可以直接用集合方法操作。 如果数组中存储的是基本数据类型,asList会将数组实体作为集合元素存在。

2、集合变数组:toArray(); 

用的是Collection接口中的方法:toArray(); 

ArrayList<String> al = newArrayList<String>();               

al.add("abc1");

al.add("abc2");

al.add("abc3");

String[] arr = al.toArray(new String[3]);//创建长度为3的数组是最优的。

如果给toArray传递的指定类型的数据长度小于了集合的size,那么toArray方法,会自定再创建一个该类型的数组,长度为集合的size。 

如果传递的指定的类型的数组的长度大于了集合的size,那么toArray方法,就不会创建新数组,直接使用该数组即可,并将集合中的元素存储到数组中,其他为存储元素的位置默认值null。 

所以,在传递指定类型数组时,最好的方式就是指定的长度和size相等的数组。 将集合变成数组:限定了对集合中的元素进行增删操作,只能获取这些元素。

为避免误导初学者,本博客如有错误或者不严谨的地方,请在下方给予评论,以便及时修改!谢谢... ...

-------------------------android培训java培训、期待与您交流! ----------------------

详细请查看:www.itheima.com

0 0
原创粉丝点击