day17_集合框架4(Collctions,Arrays,JDK1.5新特性)

 

1.Collection与Collections区别和联系:

/*
Collection与Collections区别和联系:
Collection集合类的顶级接口,继承与他的接口主要有Set 和List.
Collections针对集合操作的一个工具类它提供一系列静态方法实现对各种集合的搜索、排序、线程安全化等操作。

Collections特点:
  内部并未封装特有数据成员->因此所有的方法均为静态.
为什么要有Collections?
 例如:我想要对List集合中的元素进行排序?
      ->这时为了弥补这个不足(当然了这只是其中一点)->Collections中 
public static <T extends Comparable<? super T>> void sort(List<T> list)
*/

2.Collections中的静态方法:

①.排序

/*
static <T extends Comparable<? super T>> void sort(List<T> list) 
          根据元素的自然顺序 对指定列表按升序进行排序。 
static <T> void sort(List<T> list, Comparator<? super T> c) 
          根据指定比较器产生的顺序对指定列表进行排序。 

分析下这个泛型方法:(回顾下)
  在没有泛型时: public static void sort(List list)
  ->这样做是不安全的,提高安全性,限定集合中元素的类型->
  ->public static <T> void sort(List<T> list) 
  ->如果T为自定义类:例如Student->自身不具备比较性->无法排序
  ->令Student实现Comparable->具备自然顺序
  ->public static<T extends Comparable> void sort(List<T> list)
    (更安全,把运行时期可能出现的错误提到了编译时期)
  ->Comparable上也有泛型
  ->public static<T extends Comparable<? super T>> void sort(List<T> list)
    (? super T提高了扩展性:Student/Student的父类型)
*/
package collections;
import java.util.*;

class CollectionsDemo{
 
 public static void print(Object obj){
  
  System.out.println(obj);
 }
 
 public static void maxList(List<String> list){
  
  print("max: "+ Collections.max(list));//列表中的最大(按自然顺序比较)元素 //ce
  print("maxLength: "+Collections.max(list,new StrLenComp()));//列表中的最大(按照指定比较器比较)元素 //abc
 
 }

 
 
 public static void main(String[] args){
     List<String> list =new ArrayList<String>();
     list.add("abc");
     list.add("ce");
     list.add("c");
     list.add("be");//注意用的是ArrayList集合,底层为数组结构->可以存入重复元素
     
     print(list);//abc ce c be
     Collections.sort(list);
     print("sort: "+list);//abc be c ce//排序改变了集合中元素的原有顺序
     Collections.sort(list,new StrLenComp());
     print("sortLength: "+list);//c be ce abc
      
     maxList(list);

 }
}
//按指定比较器(按长度进行比较)对List集合排序
class StrLenComp implements Comparator<String>{

   public int compare(String str_1,String str_2){
          
    int result = str_1.length()-str_2.length();
    if(result==0)
      return str_1.compareTo(str_2);
    return result;
   }
}
sort

②二分查找

/*
static <T> int binarySearch(List<? extends Comparable<? super T>> list, T key) 
          使用二分搜索法搜索指定列表,以获得指定对象。(列表中的元素必须具备自然顺序) 
static <T> int binarySearch(List<? extends T> list, T key, Comparator<? super T> c) 
          使用二分搜索法搜索指定列表,以获得指定对象。 (此时,必须指定比较器,而自然顺序可具备也可无)
*/
package collections;
import java.util.Collections;
import java.util.List;
import java.util.ArrayList;
import java.util.Comparator;
class CollectionsDemo2 
{
    public static void print(Object obj){
  
     System.out.println(obj);
    }
    public static void main(String[] args){ 
    
      List<String> list = new ArrayList<String>();
      list.add("abc");
      list.add("a");
      list.add("c");
      list.add("ef");
      
      Collections.sort(list);//二分查找的前提:序列必须有序
      print("listSort: "+list);
      print("listBinarySearch: "+Collections.binarySearch(list,"d"));
      print ("index: "+biSearch(list,"d"));
      
      
      Collections.sort(list,new StrLenComp());
      print("listLength: "+list);
      print("listBinarySearch: "+Collections.binarySearch(list,"d",new StrLenComp()));
      print ("index: "+biSearch(list,"d",new StrLenComp()));
    }
    
    //模拟public static <T> int binarySearch(List<? extends Comparable<? super T>> list,T key)方法
    public static int biSearch(List<String> list,String key){
    
     int max,min,mid;
     max=list.size()-1;
     min=0;
     while(max>=min){
       mid = (max + min)>>1;
       String str=list.get(mid);
       int result = key.compareTo(str);
       if(result==0)
           return mid;
       else
           if(result>0)
             min=mid+1;
           else
              max=mid-1;
      }
      return -min-1;//返回-插入位置-1
    
    }
    //使用指定比较器
     public static int biSearch(List<String> list,String key,Comparator<String> comparator){
    
     int max,min,mid;
     max=list.size()-1;
     min=0;
     while(max>=min){
       mid = (max + min)>>1;
       String str=list.get(mid);
       int result =comparator.compare(key,str);//与上面唯一不同使用了compare方法
       if(result==0)
           return mid;
       else
           if(result>0)
             min=mid+1;
           else
              max=mid-1;
      }
      return -min-1;
    
    }
}

biSearch

③替换

package collections;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/*
1.public static <T> void fill(List<? super T> list,T obj)
使用指定元素替换指定列表中的所有元素。 
此方法以线性时间运行。 

2.static <T> boolean replaceAll(List<T> list, T oldVal, T newVal) 
  使用另一个值替换列表中出现的所有某一指定值。(如果没有指定的值,原集合不变) 

3.static void shuffle(List<?> list) 
          使用默认随机源对指定列表进行置换.
  使用此方法将对集合中的元素进行随机排放
   也就是说每次运行打印集合,元素的位置可能均不同
4.static void swap(List<?> list, int i, int j) 
          在指定列表的指定位置处交换元素。 
*/

class CollectionsDemo3{

 public static void main(String[] args){
 
  ArrayList<String> al = new ArrayList<String>();
  al.add("abc");
  al.add("a"); 
  al.add("c");
  al.add("ef");
  CollectionsDemo.print(al);//打印al集合
  
  Collections.replaceAll(al,"ef","gg");//把集合中的ef替换成gg
  CollectionsDemo.print(al);//再次打印
  
  Collections.swap(al,0,3);//角标为0和3的元素交换
  CollectionsDemo.print(al);
  
  Collections.reverse(al);//反转al集合
                          //其实reverse底层用的就是swap进行两两交换
  CollectionsDemo.print(al);
  
  
  Collections.fill(al,"c");//"c"替换了al中的所有元素
                           //c为Stirng类型,那么第一个参数类型限定为List<? super String>(List<String>/List<Object>)
                           //这里如果第二个 
  fill(al,0,2,"mm");//角标0,1元素替换为mm
  CollectionsDemo.print(al);
 }

/*
自定义方法将List集合中的(指定)部分元素替换成指定元素
*/
 public static<T> void fill(List<? super T> list,int start,int end,T obj){
    
    for(int i=start;i<end;++i)//包含头,不包含尾
       list.set(i,obj);
    }

}
替换

④逆转

/*
public static <T> Comparator<T> reverseOrder()
  返回一个比较器,它强行逆转实现了Comparable接口的对象collection 的自然顺序.

public static <T> Comparator<T> reverseOrder(Comparator<T> cmp)
  返回一个比较器,它强行逆转指定比较器的顺序。
  如果指定比较器为 null,则此方法等同于 reverseOrder()
 (换句话说,它返回一个比较器,该比较器将强行逆转实现了Comparable接口的对象collection的自然顺序)。 

*/
package collections;
import java.util.Collections;
import java.util.TreeSet;
import java.util.Comparator;
import java.util.Iterator;
/*
//过去写法
如果想要按照字符串自然顺序逆序输出
这时需要自定义比较器
*/
class MyCmp implements Comparator<String>{
  public int compare(String str_1,String str_2){
  
    return str_2.compareTo(str_1);
  }
}

//按字符串长度升序排序
class MyCmpLen implements Comparator<String>{
  public int compare(String str_1,String str_2){
   
    int num=str_1.length()-str_2.length();
    if(num==0)
      return str_1.compareTo(str_2);
    return num;
  }

}

class CollectionsDemo4 {
   
   public static void main(String[] args){
    
    //TreeSet<String> at = new TreeSet<String>(new MyCmp());//过去写法
    //TreeSet<String> at = new TreeSet<String>(Collections.reverseOrder());//算法和自定义比较器中算法大致相同
    
    //TreeSet<String> at = new TreeSet<String>(new MyCmpLen());    
    TreeSet<String> at = new TreeSet<String>(Collections.reverseOrder(new MyCmpLen()));//逆转指定比较器的顺序  
    at.add("g");
    at.add("abc");
    at.add("ef");
    for(Iterator<String>it=at.iterator();it.hasNext();){
     
      System.out.println(it.next());
    }
   }

}
逆转

3.线程同步的集合:

/*
 当有多个线程需要操作集合时,可能引发安全问题,
 这时候需要用到同步(某一时刻,只允许一个线程对集合进行添加/删除...)
 利用Collections中的方法返回一个支持同步(线程安全)的集合对象
 
*/
//底层源码:
   public static <T> Collection<T> synchronizedCollection(Collection<T> c) {
        return new SynchronizedCollection<>(c);
    }
  
  static class SynchronizedCollection<E> implements Collection<E>, Serializable {
        private static final long serialVersionUID = 3053995032091335093L;

        final Collection<E> c;  // Backing Collection
        final Object mutex;     // Object on which to synchronize

        SynchronizedCollection(Collection<E> c) {
            if (c==null)
                throw new NullPointerException();
            this.c = c;
            mutex = this;//这里用的是new SynchronizedCollection<>(c)锁
        }
        SynchronizedCollection(Collection<E> c, Object mutex) {
            this.c = c;
            this.mutex = mutex;
        }
        
        //下面均复写了Collection中方法,封装在了同步代码块中
        //这些方法都使用了同一个锁mutex(互斥)
        //保证了线程安全
        public int size() {
            synchronized (mutex) {return c.size();}
        }
        public boolean isEmpty() {
            synchronized (mutex) {return c.isEmpty();}
        }
        public boolean contains(Object o) {
            synchronized (mutex) {return c.contains(o);}
        }
        public Object[] toArray() {
            synchronized (mutex) {return c.toArray();}
        }
        public <T> T[] toArray(T[] a) {
            synchronized (mutex) {return c.toArray(a);}
        }

        public Iterator<E> iterator() {
            return c.iterator(); // Must be manually synched by user!
        }
        /*
         当使用时迭代器时,需要人为同步,例:
          ArrayList<String> at = new ArrayList<String>();
          Collection c = Collections.synchronizedCollection(at);
          
        
           Synchronized(c){//c锁和集合中的方法使用的是同一个锁
                           //也就是说当一个线程访问集合中方法,不能在访问迭代器方法
             
            Iterator i = c.iterator();// Must be in the synchronized block
                                      //这是因为在获取迭代器过程中不允许,添加/删除集合中元素
              while (i.hasNext())     //假设该语句不在同步中,一个线程在获取到迭代器后,另一个线程对集合进行了添加
                 foo(i.next());       //再去遍历该集合会发出并发修改异常
         }
        */

        public boolean add(E e) {
            synchronized (mutex) {return c.add(e);}
        }
        public boolean remove(Object o) {
            synchronized (mutex) {return c.remove(o);}
        }

        public boolean containsAll(Collection<?> coll) {
            synchronized (mutex) {return c.containsAll(coll);}
        }
        public boolean addAll(Collection<? extends E> coll) {
            synchronized (mutex) {return c.addAll(coll);}
        }
        public boolean removeAll(Collection<?> coll) {
            synchronized (mutex) {return c.removeAll(coll);}
        }
        public boolean retainAll(Collection<?> coll) {
            synchronized (mutex) {return c.retainAll(coll);}
        }
        public void clear() {
            synchronized (mutex) {c.clear();}
        }
        public String toString() {
            synchronized (mutex) {return c.toString();}
        }
        private void writeObject(ObjectOutputStream s) throws IOException {
            synchronized (mutex) {s.defaultWriteObject();}
        }
    }

4.操作数组的工具类:Arrays

①数组转换成集合:

/*
static <T> List<T> asList(T... a):将指定数组转换成List集合
*/
package arrays;
import java.util.Arrays;
import java.util.List;
class ArraysDemo{
   
   public static void main(String[] args){
   
    int[][] arr={{1,3},{4}};
    System.out.println(Arrays.toString(arr));//打印的将是两个一维数组的字符串表示形式(Object中的toString方法)
    
    List<int[]> list = Arrays.asList(arr);//list集合中存放的两个一维数组对象
     
     String[] str={"ab","cd","ef"};  
     List<String> list_2=Arrays.asList(str);
     System.out.println("list: "+list+"\n"+"list_2: "+list_2);
    
     
     /*
      数组->集合优点:
       把数组变成集合,可以使用集合的思想和集合中的方法,
       例如:我想要判断某个元素是否存在,当转换集合后,
       利用contains方法
       
       注意:将数组变成集合,不可以使用集合的增删方法.
       因为数组的长度是固定的.
       如果使用add/remove->编译时:UnsupportOperationException(不支持的操作异常)
     */
     
     /*
       1.如果数组中的元素都是对象,那么变成集合时,数组
         中的元素直接转换成集合中的元素.
       
       2.如果数组中的元素都是基本数据类型或是数组,那么会将数组对象的字符串表示形式作为集合中元素存在
        int[] arr={1,3,4};
        List<int[]> list = Arrays.asList(arr);//集合中元素类型为数组对象的字符串形式
       Integer[] in={1,3,4};
       List<Integer> list_2= Arrays.asList(in);//注意集合中元素类型为Integer
     */

   }

}
数组转集合

②集合转成数组

/*
集合转换成数组:
 利用集合中的toArray()方法
 Object[] toArray() 
          返回按适当顺序包含列表中的所有元素的数组(从第一个元素到最后一个元素)。 
<T> T[] toArray(T[] a) 
          返回按适当顺序(从第一个元素到最后一个元素)包含列表中所有元素的数组;返回数组的运行时类型是指定数组的运行时类型。 
*/
package collections;
import java.util.ArrayList;
import java.util.Arrays;
class CollectionsDemo6{

  public static void main(String[] args){

    ArrayList<String>at=new ArrayList<String>();
    at.add("ab");
    at.add("cd");

//方法一:
    //String[] str=(String[])at.toArray();//返回类型为Object[],如果强转:Object[]->String[]
                                        //虽然能通过编译,但运行时发生异常,如下
    /*
    Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object; can
     not be cast to [Ljava.lang.String;
        at collections.CollectionsDemo6.main(7_CollectionsDemo6.java:18)
    不允许做这样的转换,试想如果Objec[]类型的数组中存放的是Object对象,String无法操作
    [:代表一位数组
    L:代表这个数组是引用数据类型的数组.基本数据类型的每种类型都有自已对应的标识符.(int->I) 
    "java.lang.String"是数组元素的类型,标识这个数组是什么类型的数组. 
    
    
     String[] str={"abc","def"};
     Object[] obj=str;
     System.out.println(obj[1].length());//找不到方法length()说明数组中元素类型均为Object
                                         //说明Object[] obj=str;数组中的元素自动类型提升为Object
    */

    //可以这样使用
    Object[] obj = at.toArray();
    System.out.println(((String)obj[1]).length());//注意obj[1]的类型为Object,当我需要字符串长度时:((String)obj[1]).length();
     

//方法二:
     String[] arr=at.toArray(new String[0]);
     System.out.println(Arrays.toString(arr));//[ab,cd]

     String[] arr_2=at.toArray(new String[6]);
     System.out.println(Arrays.toString(arr_2));//[ab,cd,null,null,null,null]
     /*
      这时候有个问题,到底指定多长?  
      at.toArray(new String[at.size()]);//最优
     根据以上例子发现:
       当指定类型的数组长度小于集合的size,那么该方法内部会创建一个新的数组,长度为集合的size
       当指定类型的数组长度大于集合的size,就不在使用新创建的数组,而使用传递进来的数组,该数组中没有被使用元素将被置为null.
     */
    
  }

}
/*
为什么要将集合变数组?
  为了限定对元素的操作.(例如返回数组,数组长度和元素已确定,无法再增/删.但如果返回集合,依然可以增删)
*/
集合转数组

JDK1.5新特性:

①增强for循环(类似其它语言中的foreach)

/*
JDK 1.5:
高级for循环
在API中有这样一句话:
 public interface Iterable<T>
 实现这个接口允许对象成为 "foreach" 语句的目标。
 注意:Map集合并没有实现该接口,也就是说不能成为"foreach" 语句的目标.
格式:
for(数据类型 变量名:被遍历的集合(Collection)或者数组){//数据类型:集合中元素类型/数组中元素类型


}
迭代的变量必须在()中定义
1.与迭代器对比: 增强for对集合进行遍历只能获取元素,而不能对集合进行操作(增/删). 迭代器除了遍历,还可以进行remove集合中元素. 如果使用ListIterator,还以可以对集合使用 增 删 改 查 等. 2.传统for和高级for区别: 高级for有一个局限性:必须有被遍历的目标. 例如: 打印"hello"10次->只能使用传统for,而高级for没有遍历目标 建议在遍历数组的时候,使用传统for可以操作(控制)角标 例如: int[] arr={1,2,3}; for(int i=0;i<arr.length;++i){ System.out.print(arr[i]); if(i!=arr.length-1) System.out.print(','); } for(int i : arr){ System.out.println(i);//无法控制当遍历到最后一个元素不再打印',' }
*/ package collections; import java.util.ArrayList; import java.util.Iterator; import java.util.HashMap; import java.util.Map; class ForEach{ public static void main(String[] args){ ArrayList<String> al = new ArrayList<String>(); al.add("heixiu"); al.add("haha"); al.add("houhou"); /* JDK 1.5之前一直使用迭代器遍历集合: for(Iterator<String> it = al.iterator();it.hasNext();) System.out.println(it.next()); */ //JDk 1.5之后可以使用增强for循环: for(String s : al){ //String s指向了集合中元素,s指向的对象在不断变化 //这里之所以定义了一个String类引用,因为上面有泛型限定 //如果在创建集合对象时未使用泛型->该位置为Object类型 //->别忘了Object可以指向任意类型对象 //s="fail";//该语句仅仅改变了s的指向,而不改变集合中的元素 System.out.println(s); } System.out.println(); /* 其实增强for底层依然使用的是迭代器, 但是增强的for循环简化了书写 但有局限性(在上面的总结中) */ //遍历基本类型的数组 int[] arr ={1,2,3}; for(int i : arr){ System.out.println(i); } System.out.println(); //该如何使用高级for遍历Map集合? //这时候应该想到利用entrySet()/keySet()方法 HashMap<Integer,String> hs = new HashMap<Integer,String>(); hs.put(1,"ac"); hs.put(2,"e"); hs.put(3,"dd"); for(Map.Entry<Integer,String> me : hs.entrySet()){ System.out.println(me.getKey()+"..."+me.getValue()); } System.out.println(); for(Integer i : hs.keySet()){ System.out.println(i+"..."+hs.get(i)); } } }

增强for

②可变参数

/*
 JDK 1.5 版本出现的新特性.
 注意:方法的可变参数一定要定义在参数列表最后面
*/
class ParamMethodDemo
{  
    //过去写法,必须传入一个数组引用
    /*
    public static void show(int[] arr){
     
     for(int i=0;i<arr.length;++i){
       System.out.println(arr[i]);
     }
    
    }
    */
    //可变参数,不能与上面的方法同时存在,不会构成重载
     /*
       可变参数其实就是上一种数组参数的简写形式 
       不用每一次都手动的建立数组对象
       只要将要操作的元素作为参数传递即可.
       隐式将这些参数封装成了数组
      */
    public static void show(int... arr){
    
     System.out.println(arr);//打印数组对象字符串形式
                             //说明内部被封装成数组,长度根据实参决定
     for(int i=0;i<arr.length;++i){
       System.out.println(arr[i]);
     }
    }
    
    //可变参数与其它参数
    /*
    public static void show(int... arr,int i){//① 编译失败
     
     
     }
     */

    public static void show(String str,int... arr){
    
    }
    public static void main(String[] args) 
    {
      //当操作多个同一类型数据时,需要不断new数组,因为每次操作个数不确定
      /*
      int[] arr_1 ={1,2,3,4};
      int[] arr_2={2,3};
      show(arr_1);
      show(arr_2);
      */
     
      //show(1,2,3);
      //show(2);
      //show(new int[4]);
      
      
      //show(1,2,3,"haha");//编译失败,再来看一个更典型的
      show(1,2,3,4);//① 它会把1,2,3,4封装到一个数组中,第二个参数无法传入
      
      show("heihei",1,2,3);
    }

}
可变参数

 

③静态导入(import static)

/*
JDK 1.5新特性:
静态导入(StaticImport)
1.在包中讲过:当类名重名时,需要指定具体的包名
  例如:
  packa/Demo.class
  packb/Demo.class

  import packa.*;
  improt packb.*;
  
  //new Demo();//到底使用的是那个包中的Demo?->出现了不确定->加包名限定
  new packa.Demo();

2.当方法重名时,指定具备所属的对象或者类
*/
package importstatic;
import java.util.Arrays;
//import static java.util.Arrays.*;//导入了Arrays中所有的静态成员
                                   //在该源文件中用到Arrays中的静态成员,可以不用写类名
                                  //但是注意上面的第二点

import static java.util.Arrays.sort; //这样写,只导入了Arrays中的静态成员sort(可能为静态变量/静态方法)
import static java.util.Arrays.binarySearch;

//import static java.lang.System.*;
import static java.lang.System.out;//这个就是导入的System中的静态变量out

class ImportStatic{//extends Object

  public static void main(String[] args){
   
    String[] str={"dbc","f","dc"};
    //Arrays.sort(str);
    sort(str);
    //System.out.println(Arrays.binarySearch(str,"f"));
    out.println(binarySearch(str,"f"));
    
    out.println(Arrays.toString(str));//注意该位置的Arrays不能省,因为Arrays中和Object中均有toString()方法
    /*
    不写Arrays,错误: 无法将类 Object中的方法 toString应用到给定类型;
    这是因为先加载的Object类,当编译器在Object中找到toString方法
    它会直接拿去匹配.
   */
 }
}
静态导入
posted @ 2013-04-23 23:14  伊秋  阅读(369)  评论(0编辑  收藏  举报