首先要明确Collections和Collection是两个完全不同的概念,Collection是集合框架中的一员,它是一个根接口,他有很多子接口,其中有我们最常用的List和Set集合。Collections是集合框架的工具类,它有很多方法可以对很多集合进行操作,并且它没有构造函数,它提供的方法都是静态的,因为它能操作的集合都共享它的方法,所以将这些方法定义成静态的。
举个最简单的例子,当我们想要使用一个集合,不许要保证里面的元素唯一,所以我们只可以使用List集合,我们要对List集合排序,但是List集合没有排序功能,所以既可以使用集合框架工具类的Collections的Sort()方法,对List集合进行排序。这就是Collections的强大之处。
1,sor有两个方法:
public static <T extends Comparable<? super T>> void sort(List<T> list)
<T extends Comparable<? super T>>的含义是:List集合里面存储的元素是未知类型,我们将这个未知类型假设为T类型,也就是泛型,使用该Sort方法,T类型需要实现Comparable接口,而Comparable接口也要定义成泛型,它操作的类型是T类型,但是为了提高它的扩展性,所以允许Comparable接口的泛型定义成T或者其父类型。
public static <T> void sort(List<T> list,Comparator<? super T> c)
第二各参数传入的是自定义的比较器,对于没有比较性的对象,必须建立自己的比较器。
注意:Collections类的Sort方法是不能操作Set集合的,原因是Set集合有TreeSet子类,具有排序的方法。
2,max()的两个方法:
public static <T extends Object & Comparable<? super T>> T max(Collection<? extends T> coll)
该方法使用来获取集合中排序后的末尾的那个元素,比如:List集合中存入字符串,使用默认的比较器,该方法会返回List集合里面的字符串按自然顺序排序后的最后一个字符串。
Collection<? extends T> coll的含义是:集合的泛型类型是T或者T的子类型。
<T extends Object & Comparable<? super T>>的含义是:T必须是Object类的子类,并且T要实现Comparable接口,Comparable接口的泛型是T或者T的父类型。
需求:自定义一个List集合,里面存储不同的字符串,调用sort和max方法,使用默认的比较器输出List集合中字符串结果,然后自定义一个比较器,比较字符串长度,在调用sort和max方法传入自定义比较器,输出结果。
import java.util.*;
public class CollectionsDemo1
{
public static void main(String[] args) {
ArrayList<String> al = new ArrayList<>();
al.add("sda");
al.add("ss");
al.add("dd");
al.add("klsfj");
System.out.println(al);
//字符串是具有比较性的,所以sort方法采用的是字符串自然排序
Collections.sort(al);
//打印字符串的自然顺序的排序结果
System.out.println(al);
System.out.println(Collections.max(al)); //返回自然派序的最后一个
//使用自定义比较器,对List集合进行排序,该比较器比较的是字符串长度。
Collections.sort(al,new myComp());
System.out.println(al);
System.out.println(Collections.max(al,new myComp()));//返回最大长度
}
}
//定义比较字符串长度的比较器
class myComp implements Comparator<String>
{
public int compare(String s1,String s2) {
if(s1.length() > s2.length())
return 1;
if(s1.length() < s2.length())
return -1;
return s1.compareTo(s2);
}
}
3,binarySearch()的两个方法:
二分查找的前提是集合的元素必须要有序。
public static <T> int binarySearch(List<? extends Comparable<? super T>> list,
T key)
该方法是按照默认的比较器,也就是自然顺序对List集合中的元素进行二分查找。比如字符串,会按照字典顺序查找所需要的元素的角标。
public static <T> int binarySearch(List<? extends T> list,
T key,
Comparator<? super T> c)
该方法按照自定义的比较器对集合的元素进行查找。
没有比较器的二分查找的原理代码:
public static <T> int myBinarySearch(List<? extends Comparable<? super T>> list,T key) {
int min = 0;
int max = list.size() - 1;
int mid = 0;
while(min<max) {
mid = (min + max)>>1;
String str = list.get(mid);
int num = str.compareTo(key);
if(num>0)
max = mid -1;
else if(num<0)
min = mid +1;
else
return mid;
}
return -num-1;//这是Collections集合的默认原理,当找不到元素所在位置时,将该元素应该所在的位置-1
}
有自定义比较器的二分查找原理:
public static <T> int myBinarySearch(List<? extnds Comparable<? super T>> list,T key,Comparator<? super T> c) {
int min = 0 ;
int max = list.size()-1;
int mid = 0;
while(min < max) {
mid = (min + max)>>1;
String str = list.get(mid);
int num = c.compare(str,key);
if(num>0)
max = mid - 1;
else if(num<0)
min = mid + 1;
else
return mid;
}
return -min-1;
}
需求:同样是对 List 集合的 String 类型的字符串元素进行操作,按照字符串长度,使用 binarySearch() ,进行二分查找。
import java.util.*;
public class CollectionsDemo2
{
public static void main(String[] args) {
ArrayList<String> al = new ArrayList<>();
al.add("sda");
al.add("ss");
al.add("dd");
al.add("klsfj");
//首先集合里面的的元素必须是有顺序的
Collections.sort(al,new myComp());
System.out.println(al);
//使用自定义比较器,按照字符串长度查找
int n = Collections.binarySearch(al,"sss",new myComp());
//如果二分查找没有索要查找元素,则返回的是:-(该插入位置)-1
System.out.println(n);
}
}
//自定义比较器,按照字符串长度比较
class myComp implements Comparator<String>
{
public int compare(String s1,String s2) {
if(s1.length() > s2.length())
return 1;
if(s1.length() < s2.length())
return -1;
return s1.compareTo(s2);
}
}
4,public static <T> void fill(List<? super T> list,T obj)
使用指定元素替换指定列表中的所有元素。
public static <T> boolean replaceAll(List<T> list, T oldVal, T newVal)
将List集合中的旧值替换成成指定的新值。
public static void reverse(List<?> list)
将集合中的元素顺序反转。
import java.util.*;
public class CollectionTest
{
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("na");
list.add("bbb");
list.add("sdg");
list.add("dg");
System.out.println(list);
//将List集合中的所有元素替换成yyy
//Collections.fill(list,"yyy");
//System.out.println(list);
//替换List集合中的一部分元素,替换角标为1——2的元素为yyy
tihuan(list,1,3);
//反转替换后的集合元素顺序
Collections.reverse(list);
System.out.println(list);
}
//替换过程代码
public static void tihuan(List<String> list,int s,int e) {
for(int i=s;i<e;i++) {
list.remove(i);
list.add(i,"yyy");
}
System.out.println(list);
}
}
二,Collections的reverseOrder()方法:
reverseOrder方法是Collections集合工具类的一个重要方法,所以单独拿出来总结。
public static <T> Comparator<T> reverseOrder()
public static <T> Comparator<T> reverseOrder(Comparator<T> cmp)
该方法返回一个Comparator比较器,但是该比较器是默认比较器的逆向比较方法。
比如说字符串的默认比较方式是自然顺序,该顺序是通过实现Comparable接口,重写它的compareTo方法,来按照自然顺序比较字符串。当我们将字符串作为元素存储到List集合中时,通过Collections类的sort()方法对字符串排序,那么结果将会是自然顺序排序后的字符串,如果我们将Collections.reverseOrder()作为一个参数传递给sort方法,那么结果将和自然顺序相反。该方法是可以应用到TreeSet集合的,因为TreeSet集合允许接收一个比较器。
请看谢列需求:
需求:将不同长度的字符串作为元素传递给TreeSet集合,按照字符串的长短输出结果,反别输出字符串从长到短和从短到长的两种不同的结果。
按照以前的思维我们通常是定义两个比较器,按别通过返回不同的值,来确定字符串的长短,但是学习了Collections的reverseOrder()方法后,我们可以直接返回一个与原比较器相反的比较器来进行操作。
import java.util.*;
public class CollectionsDemo3
{
public static void main(String[] args) {
/* ArrayList<> list = new ArrayList<>();
list.add("shgsj");
list.add("asg");
list.add("dsh");
System.out.println("原List集合:" + list);
Collections.sort(list);
System.out.println("自然排序的结果:" + list);
Collections.sort(list,Collections.reverseOrder());
System.out.println("自然顺序的逆序:" + list);
*/
//创建一个TreeSet集合,将自定义比较器的反向比较器传递给Set集合
TreeSet<String> ts = new TreeSet<>(Collections.reverseOrder(new myC()));
//TreeSet<String> ts = new TreeSet<>(Collections.reverseOrder());
ts.add("fssssgdsf");
ts.add("sfsf");
ts.add("sgrrf");
ts.add("sdf");
sop(ts);
iter(ts); //按照长度然后进行逆序排序;
}
//对集合中的元素进行迭代输出
public static void iter(TreeSet<String> ts) {
Iterator<String> it = ts.iterator();
while (it.hasNext())
{
sop(it.next());
}
}
public static void sop(Object obj) {
System.out.println(obj);
}
}
//定义一个比较器,按照字符串长度比较
class myC implements Comparator<String>
{
public int compare(String s1,String s2) {
if(s1.length() > s2.length())
return 1;
if(s1.length() < s2.length())
return -1;
return s1.compareTo(s2);
}
}
三,之前我们说过Collection中的ArrayList集合几乎替代了Vector,ArrayList和Vector集合的用法是一模一样的,虽然Vector集合比ArrayList集合多了线程同步功能,但是因为它的效率低所以即使在多线程中,我们依然还是用ArrayList,然后自己加锁,其实集合框架工具类已经为我们提供了给List集合加锁的方法,我们只需要给该方法传入一个线程不同步的集合,他就能返回给我们一个线程同步的集合。
这些方法是:
public static <T> Collection<T> synchronizedCollection(Collection<T> c)
public static <T> List<T> synchronizedList(List<T> list)
public static <K,V> Map<K,V> synchronizedMap(Map<K,V> m)
public static <T> Set<T> synchronizedSet(Set<T> s)
public static <K,V> SortedMap<K,V> synchronizedSortedMap(SortedMap<K,V> m)
public static <T> SortedSet<T> synchronizedSortedSet(SortedSet<T> s)
四,数组工具类Arrays:
Arrays类和集合框架工具类Collections一样,提供了很多直接操作数组的静态法方发,很多静态方法我们之前已经使用过。
数组和List集合之间是可以相互转换的,转换方法就是Arrays提供的asList()静态方法。
但是将数组转换成集合要注意一下几个问题:
1,数组转换成集合后,不可以对该集合进行增删操作,因为数组的长度是固定不变的,但是可以进行查询操作,包括contains,indexOf,get,subList等操作;
2 ,如果数组是基本类型的数组,那么转换成集合后,集合的元素不是数组中的所有元素,而是这个数组对象。如果我们打印基本数据类型数组转换成集合后的结果,将看到的是一个地址值,也就是说转换后的集合只有一个元素。下面我们将通过一个程序进行验证。
import java.util.*;
public class ArraysDemo
{
public static void main(String[] args) {
//返回chs数组的字符串表示形式。没学该方法之前我们只能使用StringBuild进行这样的操作。
/*char[] chs = {'a','d','g','r'};
String s = Arrays.toString(chs);//静态方法引用一定不能忘了类
sop(s);//输出:[a,d,g,r]
*/
int[] i = {1,3,6,2,1};//如果将基本数据类型数组转换成集合那么,集合中的元素将是这个数组,而不是数组的元素
List<int[]> list = Arrays.asList(i);
sop(list);//打印结果是:[[I@10be6858]
Integer[] li = {1,2,4,6,7}; //引用了数据类型的拆箱和装箱,数组中存储的元素是对象
List<Integer> list2 = Arrays.asList(li);
sop(list2);
//list2.add(new Integer(6));//会抛出UnsupportedOperationException。
sop(list2.contains("6"));
}
public static void sop(Object obj) {
System.out.println(obj);
}
}
3,既然数组可以转换成集合,那么集合也一定可以转换成数组,集合转换成数组是使用Collection集合框架中的toArray()方法。
这个方法有两种形式:
Object[] toArray()
<T> T[] toArray(T[] a)
前一个是返回Object类型的数组,后一个是返回泛型类型的数组。
(1)为什么要将集合变成数组呢?为了限制对集合中元素的操作,不需要进行增删操作了,可以将集合变成数组,别的用户就不能进行增删操作了。
当将集合转成数组的时候,如果指定的数组的长度小于等于集合的size,则会生成集合size大小的数组,当指定数组的长度大于集合的size的时候,会生成指定大小的数组,这个大小比集合的size大多少,就对应生成多少个null值放入数组的空位;所以,指定数组的大小最好是集合的size的大小;
import java.util.*;
public class CollectionArrays
{
public static void main(String[] args) {
ArrayList<String> al = new ArrayList<>();
al.add("dzsfg04");
al.add("dzsfg01");
al.add("dzsfg02");
al.add("dzsfg03");
//String[] st = al.toArray(new String[0]);
String[] st = al.toArray(new String[al.size()]);
System.out.println(Arrays.toString(st));
}
public static void sop(Object obj) {
System.out.println(obj);
}
}
五,总结jdk1.5的几个新特性;
我们知道jdk1.5可以说是java发展的一个重大转折点,因为在这个版本增加了很多革命性的功能,所以这是很重要的一个知识点。面试可能会被问到。
1,泛型
2,增强for循环
3,可变参数
4,静态导入
5,自动装箱和拆箱
6,枚举
7,元数据
泛型我们已经学习过,增强for循环也已经在java基础知识里面总结过,在这里在比较一下增强for循环和普通for有什么区别:高级for必须要有被遍历的目标;比如打印100次hello world,高级for不能完成,传统for却可以。所以建议对数组进行遍历的时候采用传统的for循环,但是集合框架的遍历,高级for也可以使用。
增强的for循环:
语法:
for(数据类型 变量名 :被遍历的集合(Collection)或者数组)
{
}
重点要掌握增强for循环对List集合和对Map集合的操作:
import java.util.*;
public class EnhanceFor
{
public static void main(String[] args) {
ArrayList<String> al = new ArrayList<>();
al.add("sdfg");
al.add("sdfdf");
al.add("sdaafa");
//使用迭代器迭代List集合
Iterator<String> it = al.iterator();
while (it.hasNext())
{
System.out.println(it.next());
}
//使用增强for循环迭代List集合
for (String s: al )
{
System.out.println(s);
}
//增强for循环对Map集合的迭代
TreeMap<Integer,String> tm = new TreeMap<>();
tm.put(1,"fasd");
tm.put(2,"hasd");
tm.put(3,"kasd");
//方法一:keySet()
Set<Integer> st = tm.keySet();
for (Integer num: st)
{
System.out.println(num + "----" + tm.get(num));
}
//方法二:entrySet()
Set<Map.Entry<Integer,String>> en = tm.entrySet();
for(Map.Entry<Integer,String> me: en)
{
System.out.println(me.getKey() + "======" + me.getValue());
}
}
}
可变参数:在Arrays数组工具类中的asList(T... args)方法上,可以就看到它的参数就是一个可变参数的。可变参数其实就是数组的简写形式,不用每一次都手动的建立数组对象,只要将要操作的元素作为参数传递即可。其实底层是JVM将这些参数封装到了一个数组中。
public class Kebiancanshu
{
public static void main(String[] args) {
show("sagj",3,5);
show("ds",1,3,5);
show("sda");//可变参数为空,长度为0
public static void show(int i,int j) {
System.out.println(i+j);
}
public static void show(int i,int j,int k) {
System.out.println(i+k+j);
}
public static void show(String s,int... i) {
System.out.println(i.length);
}
}
静态导入:导入的是所用静态类或对象中的静态成员。
特别注意:(1)当类名重名时,需要导入具体的包名;(2)当方法名重名是指定具体的类或者对象。
静态导入示例:
import static java.util.Arrays.*;//导入了java.util.Arrays的所有静态成员;
public class StaticImportDemo
{
public static void main(String[] args) {
String[] arr = {"sg","sadg","stga"};
//Arrays.sort(arr);
//可以省略Arrays
sort(arr);
System.out.println(Arrays.toString(arr));//为什么这个Arrays不可以删呢?
//那是因为StaticImportDemo类是Object的子类,Object超类有自己的toString()
//如果不加Arrays,它会认为引用的是超类的toString方法,会抛异常。
}
}