java基础_09_集合

集合

用于存储对象(对象的引用地址)

集合的结构图

委屈 

 

Collection

|--List:元素是有序的,按存入次序依次存放,元素可以重复。因为该集合体系有索引。

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

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

|--vector  : 底层是数组数据结构。 线程同步。 被ArrayList替代了。

 

|--Set :元素是无序的,按指定排序方法存放,元素不可以重复

|--HashSet : 底层数据结构是哈希表

|--TreeSet  : 底层数据结构是二叉树,可以对Set集合中的元素进行排序。

     保证元素唯一性的依据:compareTo 方法 return 0;

 

        Set 集合的功能和Collection 是一致的,其实,Set底层就是使用了Map集合。

 

Map 

|--Hashtable : 底层是哈希表数据结构,不可以存入null键null值。该集合是线程同步的。JDK1.0  效率低

|--HashMap: 底层是哈希表数据结构,允许使用null值和null键,该集合是不同步的。将hashtable替代,jdk1.2 效率高

|--TreeMap : 底层是二叉树数据结构。线程不同步。可以用于给Map集合中的键进行排序。

 

Collection 接口

     集合的根接口。

     1,  add方法参数类型是Objext 以便于接收任意类型对象

     2,集合中存储的都是对象的引用(地址)

     3,集合转数组。

         例:String s  =  new String[集合.size()] ;

  为什么要将集合变数组?

  为了限定对元素的操作,(不需要进行增删了,只能查就行)

  注:Arrays 类中有大量操作数组的静态方法。

 

Collections 类

     此类完全由在 collection 上进行操作或返回 collection 的静态方法组成.

     常用方法:

       sort(List<T> list)       根据元素的自然顺序 对指定列表按升序进行排

   swap(List<?> list, int i, int j)在指定列表的指定位置处交换元素

  shuffle(List<?> list)对集合元素随机排序

   reverse(List<?> list)反转指定列表中元素的顺序

   reverseOrder()  返回一个比较器,它强行逆转实现Comparable 接口的对collection 自然顺序

   reverseOrder(Comparator<T> cmp)返回一个比较器,它强行逆转指定比较器的顺序

  例 :

   TreeSet<String> ts = new TreeSet<String>(Collections.reverseOrder(new Mycomparator())); 

   class Mycomparator implements Comparator<String>

  {

     public int compare(String s1,String s2)

     {

        int num = s1.length()-s2.length();

        if(num>0)

             return 1;

         if(num<0)

              return -1;

         return s1.compareTo(s2);

      }

   }

 

下面是一些常用的集合:

List 集合

特点:所存入的元素是按存入顺序存放的,元素可以重复。

List集合特有的迭代器 : ListIterator  是Iterator的子接口。

如果想要操作如添加,修改等,就需要使用其子接口 ListIterator

 

如何将数组转变成集合呢?

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

 

数组转集合有什么好处?

可以使用集合的思想和方法来操作数组中的元素。

 

注意:将数组变成集合,不可以使用集合的增删方法。

因为数组的长度是固定的。

如果增删会发生 UnsupportedOperationException 异常。

 

注意: 如果数组中的元素都是对象,那么变成集合时,数组中的元素就直接转成集合中的素。

            如果数组中的元素都是基本数据类型,那么会将 该数组 作为集合的元素存在。

例:

String s [] ={"a","bbbd","kcd","z"};  

List<String> list = Arrays.asList(s);

int a [] = {2,4,5};

List<int []> li = Arrays.asList(a);  //2,4,5表示的是基本数据类型

Integer num[] = {2,4,5};

List<Integer> li2 = Arrays.asList(num); //2,4,5会自动装箱,本身就是对象了

 

下面两个常用的List集合,如果想支持随机访问,而不必在首尾的任何位置插入或删除元素,那么使用ArrayList 集合,如果要对集合进行频繁的添加和删除操作,对集合的首尾操作多,那么使用LinkedList 集合更好。它提供了很多处理元素的方法。

    1,ArrayList 

       底层的数据结构使用的是数组结构。

        特点:查询速度很快,但是增删稍慢。线程不同步。

        该类封装了一个动态再分配的Object[]数组,每个ArrayList对象有一个capacity.这个capacity表示存储列表中元素的数组的容量,在向集合中加入元素时,capacity在自动增加。

       注意:contains(Object o) 方法,内部自动调用equals方法来比较的。

          (o==null ? e==null : o.equals(e)) 

 例:

//去重复元素

 public static ArrayList method(ArrayList al)
    	{
    		List newal = new ArrayList();
    
    		Iterator it = al.iterator();
    
    		while(it.hasNext())
    		{
    		        Object obj = it.next();
    			if(!newal.contains(obj))
    				newal.add(obj);
    		}
    		return newal;
    	}

 

2,LinkedList 

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

   该类添加了一些处理列表两端元素的方法。实现所有可选的列表操作,并且允许所有元素

  (包括null)。除了实现 List 接口外,LinkedList 类还为在列表的开头及结尾 getremove 和 insert 元素提供了统一的命名方法

3,Vector

        底层是数组数据结构。 线程同步。 被ArrayList替代了。

    枚举就是Vector特有的取出元素方式

    其实枚举和迭代是一样的,因为枚举的名称以及方法的名称都过长,所以被换代器取代了 

    使用方法:

 
 
	   

Vector v = new Vector();

v.add("java001");

v.add("java002");

Enumeration en = v.elements();

while(en.hasMoreElements())

{

System.out.println(en.nextElement());

}

 

4,Stack

   Stack 类表示后进先出(LIFO)的对象堆栈。它通过五个操作对类 Vector 进行了扩展 ,允许将向量视为堆栈。它提供了通常的 push 和 pop 操作,以及取堆栈顶点的 peek 方法、测试堆栈是否为空的 empty 方法、在堆栈中查找项并确定到堆栈顶距离的 search 方法.

 

Set 集合

特点:所存入的元素按指定排序方法存放元素(即不是按存入顺序存放),不可以重复元素。

Set底层是使用了Map集合。

1,HasHSet 

   数据结构是哈希表,线程是非同步的

   保证元素唯一性的原理:

   判断元素的hashCode值是否相同。如果相同,还会继续判断元素的equals方法,是为true 如果不同,不会判断equals

   一般使用HashSet 都须要覆盖hashCode()、equals() 两个方法

   例:

往HashSet 集合中存入自定义对象。

假设:姓名和年龄相同为同一个人,是重复元素。

public int hashCode() //自动调用 ,复写父类。调用这个才有机会去调用equals方法

           {

               return  name.hashCode()+age*37;//只要返回的hashCode值唯一就行 

           }

           public boolean equals(Object obj) //复写父类

          {

              if(!(obj instanceof Person)) 

                      return false;

              Person p = (Person)obj;

              return this.name.equals(p.name)  &&  this.age == p.age;

           }

   注意:对于判断元素是否存在,以及删除等操作,依赖的方法是元素的hashCode和equals方法

       记住:排序时,当主要条件相同时,一定要判断一下次要条件。

 

 hashCode()作用:

 hashCode()值可以说成是对象在内存中计算出来的一个值,如果值等,说明对象是同一个对象,所以这里程序员可以通过复写hashCode()来判断让其那些是同一类型。为了提高效率,让其在查找集合中是否有相同对象的时候,把hashCode()值在内存中进行了分区存放的,这样查找起来就效率提高了。正因为这样,所以在一个对象存入hash集合中的时候,就不要去更改对象中的值了,否则就会产生内存泄漏(就是内存浪费)。为什么呢?因为更改了值后,导致hashCode的改变,导致存放的区域发生了变化,所以再去删除这个对象的时候,这个对象已不在这个区域了,是无法删除到这个对象的,删除的是没改之前的对象,而这个对象已没用了。放在这里就是一处内存泄漏。

 

2,TreeSet 

   底层数据结构是二叉树。 

   特点:可以对Set集合中的元素进行排序。

   但必须指明排序要求。

   例:

TreeSet ts = new TreeSet(new MyCompare()); 

new MyCompare() 就是比较器

   保证元素唯一性的依据:compareTo 方法 return 0;

 

 排序方式:   两种

 

TreeSet排序的第一种方式:

让元素自身具备比较性。这种方式也称为元素的自然顺序,或者叫做默认顺序。

               使用方法:

1,该类实现Comparable接口

2,覆盖compareTo方法

例 :

    class Student implements Comparable  // 1,实现Comparable接口 该接口强制让学生具备比较性
    { 
      public int compareTo(Object obj)  // 2,覆盖compareTo方法,是自动调用的
    	{
    		//按照学生的年龄进行排序
    		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;
    
    /*
    注意:如果想实现按怎么进来的怎么出去
    只须 compareTo方法 return -1;或 return 1; 即可。
    因为二叉树:存放的时候是大的放在右边,小的放在左边,
          默认输出从小到大。
     -1 表示小 0 表示等 1表示大
    */
    }
    }

TreeSet的第二种排序方式:

           当元素自身不具备比较性时,或者具备的比较性不是所需要的。

           这时就需要让集合自身具备比较性。

            使用方法:

           1,定义比较器,实现Comparator接口

           2,覆盖compare方法

           3,将比较器对象作为参数传递给TreeSet集合的构造函数。

           例:

                需求:现在要求按学生姓名排序。 

    class MyCompare implements Comparator		//1,定义比较器,实现Comparator接口
    {
    	public int compare(Object o1,Object o2)	//2,复写compare方法
    	{
    		Student s1 = (Student)o1;
    		Student s2 = (Student)o2;
    
    		int num = s1.getName().compareTo(s2.getName());
    		if(num==0)
    		{
    			if(s1.getAge()>s2.getAge())
    				return 1;
    			if(s1.getAge()<s2.getAge())
    				return -1;
    			return 0;
    		}
    		return num;
    	}
    }

          注意:如果自身具有比较性,但又传入了比较器,那么以比较器为主。

 

Map 集合

Map接口不是Collection接口的继承。

和Set很像,其实,Set底层就是使用了Map集合。

 

什么时候使用map集合呢?

当数据之间存在映射关系时,就可以使用map集合,因为Map集合中存放就是映射关系。

 

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

Map集合的取出原理:将Map集合转成Set集合,在通过迭代器取出。

 

一,Set<K>  keySet 

将Map中所有的键存入到Set集合,因为Set具备迭代器。

所以可以用迭代方式取出所有的键,在根据get方法。获取每一个键对应的值。

例: 

    Map<String,String> map = new HashMap<String,String>();
    
    Set<String> keySet = map.keySet(); //1,获取Map集合的所有键,保存到Set集合

    Iterator<String> it1 = keySet.iterator();  以键值迭代
    while (it1.hasNext())
    {
       String skey = it1.next();
    	  String svalue = map.get(skey);   通过键值获取对应的值。
    
    	  System.out.println("key:"+skey+"_____"+svalue);
    }

 

二,Set<Map.Entry<k,v>>  entrySet 

将Map集合中的映射关系存入到Set集合中,而这个关系的数据类型就是:Map.Entry然后在通过迭代器来取数据。

例:

    Map<String,String> map = new HashMap<String,String>();
    
    //将Map集合中的映射关系取出,存入到Set集合中
    Set<Map.Entry<String,String>> entrySet = map.entrySet();
    Iterator<Map.Entry<String,String>> it = entrySet.iterator();
    while (it.hasNext())
    {
      Map.Entry<String,String> me = it.next();//迭代器中返回的值的类型和泛型<>里的类型一样
    	 String key = me.getKey();
    	 String value = me.getValue();
    	 System.out.println(key+"..."+value);
    }

 

 1,HashMap 

   底层是哈希表数据结构,允许使用null值和null键,该集合是不同步的。将hashtable替代,jdk1.2 效率高 , 适合在集合中插入,删除和定位元素。

   例:

   Map<String,String> map = new HashMap<String,String>();

   map.put("01","zhangsan1");

   map.put("01","zhangsan1");//如果出现添加相同的键,那么后添加的值会覆盖原有键对应值,并返回被覆盖的值。

  

  判断一个键是否存在?

  可以通过get方法的返回值来判断一个键是否存在。通过返回null来判断

  System.out.println( map.get("04") );//返回null

 

  获取map集合中的 Collection 视图。

  Collection<String> coll = map.values();

 

2,TreeMap 

   底层是二叉树数据结构。线程不同步。可以用于给Map集合中的键进行排序。

   如果想按自然顺序或自定义顺序排序,使用该集合。

   例:

字符串“osjfosj0fwamfw0fimdsoja”获取该字符串中的字母出现的次数。

希望打印结果:a(1)b(3)c(2)....

思路:通过结果可以看到,每一个都有对应的次数,说明字母和次数之间都有映射关系。

所以选择Map集合,因为输出结果是有序的,所以使用TreeMap

步骤:

1,将字符串转换成字符数组,因为要对每一个字母进行操作。

2,定义一个map集合,因为打印结果的字母有顺序,所以使用TreeMap集合

3,遍历字符数组

   将每一个字母作为键去查map集合

   如果返回null ,将该字母和次数1存入到map集合

   如果返回不是null ,说明字母已存在,把对应的次数加1

4,遍历map集合以指定形式打印。

     public static void charCount2(String str)
    	{
    		char chs[] = str.toCharArray();  //1,返回字符数组
    
    		//泛型里放的是引用对象
    		TreeMap<Character,Integer> tm = new TreeMap<Character,Integer>();
    		
    		int count = 0;
    		for(int i=0;i<chs.length;i++)
    		{
    			Integer value = tm.get(chs[i]);  //不存在就返回null
    			if(value!=null)
    				count = value;
    			count++;
    			tm.put(chs[i],count); //加入集合,如有就更新这对值
    			count = 0;
    		}
    				
    		Set<Map.Entry<Character,Integer>> entrySet = tm.entrySet();
    		Iterator<Map.Entry<Character,Integer>> it = entrySet.iterator();
    
    		while (it.hasNext())
    		{
    			Map.Entry<Character,Integer> me = it.next();
    			Character ch = me.getKey();
    			Integer value = me.getValue();
    			System.out.print(ch+"("+value+")");
    		}
    	}

3,Hashtable

  底层是哈希表数据结构,不可以存入null键null值。该集合是线程同步的。JDK1.0 效率低

 

扩展练习:

一个学校有多个班级,一个班级有多个学生(学号  姓名)。

拿一个集合来放班级。 (学校---班级)

拿一个集合来放学生。(班级--学生) 

如:

学校集合,存放 班级 

    HashMap<String,HashMap<String,String>> czbk = 
    new HashMap<String,HashMap<String,String>>();
    
    班级集合,存放 学生(学号  姓名)
    HashMap<String,String> yure = new HashMap<String,String>();
    
    HashMap<String,String> jiuye = new HashMap<String,String>();
    
    //学校增加班级
    czbk.put("yureban",yure);
    czbk.put("jiuyeban",jiuye);
    
    //班级增加学生
    yure.put("01","zhansan");
       yure.put("02","lisi");
       jiuye.put("01","wangwu");
       jiuye.put("02","zhaoliu");

          //输出也是,先取班级,再通过班级取出学生信息。或也可以使用其它的方法来完成,但使用这种方法呢,我们还得对建立Student类,来保存 学生信息,如果使用Map键值对呢,就可以不用写。

       例:

    学校集合,存放 班级
    HashMap<String,List<Student>> czbk = new HashMap<String,List<Student>>();	
    班级集合,存放 学生对象
    List<Student> yure = new ArrayList<Student>();
    List<Student> jiuye = new ArrayList<Student>();
    
    //学生信息
    class Student(){   }  

 

Comparable 与 Comparator 区别?

Comparable :适用于一个类自身具有比较性,即具有自然顺序的时候使用。

Comparator : 定义自己的比较方式(比较器),即本身不具备比较性,或具有的比较性,不是把需要的。

 

注意:当两种排序都存在时,以比较器(Comparator )为主。(比较常用)

一般写好的代码不要去修改,实现接口就行了。(接口:功能扩展)

例:

按字符串长度排序 

字符串本身具备比较性,但是它的比较方式不是所需要的,这时就只能使用比较器Comparator 

TreeSet ts = new TreeSet(new StrLenComparator());

class StrLenComparator implements Comparator //比较器

{

public int compare(Object o1,Object o2)

{

String s1 = (String)o1;

String s2 = (String)o2;

int num = s1.length()-s2.length();

if(num==0)

return s1.compareTo(s2);

return num;

}

}

 

Iterator 迭代器

什么是迭代器?

      就是从集合里取出元素的方式。

     因为每一种集合都有取出元素操作,所以把这个取方式封装成了一个类 Iterator

 

使用方法:

      集合:ArrayList a1 = new ArrayList() ;

       Iterator it = a1.iterator();  //获取迭代器,用于取出集合中的元素

      while(it.hasNext())   // 正向取出

     {

           System.out.println(it.next());

     }

    注意:在迭代时,不可以通过 集合对象 的方法操作集合中的元素。因为会发生并发操作异常

               所以,在迭代器时,只能用迭代器的方法操作元素。

   但Iterator方法有限,只能对元素进行判断,取出,删除等操作.

 

   List集合特有的迭代器 :ListIterator 

  它可以添加,修改,按任一方向遍历列表获得迭代器在列表中的当前位置等。该接口只能通过List集合的ListIterator方法获取。

              例:

                       ListIterator li = a1.listIterator(al.size()) ; //这里注意指定指针位置。

                        while(li.hasPrevious())//从后往前取出

                      {

                             System.out.prinln( li.previous() );

                       }

 

心得:

这么多集合,我们在使用集合的时候要注意应该选用什么样的集合,选对集合,对于我们操作对象什么都是很方便的,List集合中存放的元素是无序的,适用于对元素无须排序,而且元素可以重复出现。Set集合呢,元素是不能重复出现的,存放的元素进行过排列的。适用于要对对象按一定要求进行排序,去除重复元素等。这里排序工作可以由Compareble或Comparetor (自定义比较器)来完成。Set集合底层就是由Map集合来完成的。Map集合呢,适用于保存那些有映射关系的。List 、Set 集合都是使用Iterator 迭代器来输出集合内容,对于Liste还有一个特有的ListIterator 迭代方法,这个提供了操作集合的一些方法,如:删除,修改等。Map集合的输出要先通过自己的方法,转换成Set集合,然后Set集合在通过Iteraor来输出。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章