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; } }
②二分查找
/* 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; } }
③替换
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)); } } }
②可变参数
/* 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方法 它会直接拿去匹配. */ } }