Java基础——集合框架(四)
2015-07-27 20:16
731 查看
集合的工具类
Collections是专门对集合进行操作的工具类。
比如说排序:
我们知道,List集合是有序的(按照存进去的顺序进行存储),因为他底层数据结构是数组,有角标,里边有索引,所以也可以有重复元素。
现在如果我们想对List集合中的元素进行排序呢?
//Collections工具类的简单演示 import java.util.*; public class CollectionsDemo { public static void main(String[] args) { sortDemo(); } public static void sortDemo() { String s1 = "dgsch"; String s2 = "aaa"; String s3 = "zz"; String s4 = "qq"; String s5 = "cdfg"; List<String> list = new ArrayList<String>(); list.add(s1); list.add(s2); list.add(s3); list.add(s4); list.add(s5); sop(list); //使用工具类进行排序 Collections.sort(list); sop(list); } public static void sop(Object obj) { System.out.println(obj); } }
运行结果:
结果可以看出,Collections的sort方法可以使List集合中的元素进行排序,排序方式是自然排序。
现在需求变了,我们要按照字符串的长度进行排序。
可以在Collections的sort方法里传入比较器。
//使用Collections的排序方法,但是用自己定义的排序方式 import java.util.*; //自定义比较器 class StrLenCompartor implements Comparator<String> { public int compare(String s1,String s2) { int num = new Integer(s1.length()).compareTo(new Integer(s2.length())); if (num==0) return s1.compareTo(s2); return num; } } public class CollectionsDemo { public static void main(String[] args) { sortDemo(); } public static void sortDemo() { String s1 = "dgsch"; String s2 = "aaa"; String s3 = "zz"; String s4 = "qq"; String s5 = "cdfg"; List<String> list = new ArrayList<String>(); list.add(s1); list.add(s2); list.add(s3); list.add(s4); list.add(s5); sop(list); //使用工具类进行排序,将自定义比较器作为参数传递进来 Collections.sort(list,new StrLenCompartor()); sop(list); } public static void sop(Object obj) { System.out.println(obj); } }
运行结果为:
运行结果可以看出来,集合按照自定义的方式进行排序。
还有一个方法是可以直接获取集合中元素的最大值,Collections.max(T t),这里就不进行演示了。
集合工具类的二分法查找
//Collections的二分法查找 import java.util.*; public class CollectionsDemo2 { public static void main(String[] args) { binarySearchDemo(); } public static void binarySearchDemo() { String s1 = "dgsch"; String s2 = "aaa"; String s3 = "zz"; String s4 = "qq"; String s5 = "cdfg"; List<String> list = new ArrayList<String>(); list.add(s1); list.add(s2); list.add(s3); list.add(s4); list.add(s5); sop(list); Collections.sort(list,new StrLenCompartor());//先对集合中的元素进行排序 sop(list); int index = Collections.binarySearch(list,"aaa");//调用二分法查找的时候要把集合和查找的元素都传进来 sop("aaa.index="+index); int index2 = Collections.binarySearch(list,"aaaa"); sop("aaaa.index="+index2); } public static void sop(Object obj) { System.out.println(obj); } }
运行结果为:
运行结果可以发现,当要找的元素不存在时,返回的值不是-1,而是-(插入点)-1; 这样的结果在数组的二分法查找中也是这样的。
而且需要注意的是,使用二分法查找的时候,集合中的元素必须是已经排好序的。
那么,要我们自己定义二分法查找元素呢?
//自定义二分法查找 import java.util.*; public class CollectionsDemo2 { public static void main(String[] args) { binarySearchDemo2(); } public static void binarySearchDemo2() { String s1 = "dgsch"; String s2 = "aaa"; String s3 = "zz"; String s4 = "qq"; String s5 = "cdfg"; List<String> list = new ArrayList<String>(); list.add(s1); list.add(s2); list.add(s3); list.add(s4); list.add(s5); //首先对集合进行排序,记住,在二分查找之前一定要进行排序,而且在自定义的二分法中,排序规则需要一致。 Collections.sort(list,new StrLenCompartor()); sop(list); int index = halfSearch(list,"aaa",new StrLenCompartor()); sop(index); } /* public static int halfSearch(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); if (str.compareTo(key)>0)//如果集合中的元素不具备比较性,这一步是不可以进行的。 { max = mid-1; } else if (str.compareTo(key)<0) { min = mid+1; } else { return mid; } } return -min-1;//如果集合中没有指定的元素,返回-min-1,这里的min是指定元素的插入点 } */ public static int halfSearch(List<String> list,String key,Comparator<String> cmpt) { int max,min,mid; max = list.size()-1; min = 0; while (max>min) { mid = (max+min)>>1; String str = list.get(mid); //这里不能直接对元素进行比较,所以用到了参数中传进来的比较器 //直接使用比较器中的compare方法进行比较 int num = cmpt.compare(str,key); if (num>0) { max = mid-1; } else if (num<0) { min = mid+1; } else { return mid; } } return -min-1;//如果集合中没有指定的元素,返回-min-1,这里的min是指定元素的插入点 } public static void sop(Object obj) { System.out.println(obj); } } //自定义比较器 class StrLenCompartor implements Comparator<String> { public int compare(String s1,String s2) { int num = new Integer(s1.length()).compareTo(new Integer(s2.length())); if (num==0) return s1.compareTo(s2); return num; } }
运行结果为:
运行结果可以看出,集合先按照元素长度进行排序,然后按照自定义二分法查表查到元素的角标位置。
注意:在进行二分法查找之前,一定要进行排序,
在以上示例代码中(假设元素自身没有比较性),二分法查找元素使用到了比较器,排序也用到了比较器,两者比较器必须相同。
集合中元素的替换和反转
//集合中元素的替换和反转 import java.util.*; public class CollectionsDemo3 { public static void main(String[] args) { String s1 = "dgsch"; String s2 = "aaa"; String s3 = "zz"; String s4 = "qq"; String s5 = "cdfg"; List<String> list = new ArrayList<String>(); list.add(s1); list.add(s2); list.add(s3); list.add(s4); list.add(s5); sop(list);//[dgsch, aaa, zz, qq, cdfg] //将集合中的元素替换成指定的元素 //fillDemo(list); //sop(list);//[qq, qq, qq, qq, qq] //反转集合中的元素 //reverseDemo(list); //sop(list);//[cdfg, qq, zz, aaa, dgsch] //用指定的元素,替换集合中指定的元素。 raplaceAllDemo(list,"aaa","haha"); sop(list);//[dgsch, haha, zz, qq, cdfg] } public static void sop(Object obj) { System.out.println(obj); } public static void fillDemo(List<String> list) { Collections.fill(list,"qq"); } public static void reverseDemo(List<String> list) { Collections.reverse(list); } public static void raplaceAllDemo(List<String> list,String s,String port) { Collections.replaceAll(list,s,port); } }
那么看一个练习,将集合中的指定范围的对象,全部替换成指定的对象,
//练习,已知fill方法可以将集合中的所有元素替换成指定的元素 //那么需求是,要将指定范围的元素换成指定的元素 import java.util.*; public class CollectionsTest { public static void main(String[] args) { //先创建集合,在集合中添加字符串对象 String s1 = "dgsch"; String s2 = "aaa"; String s3 = "zz"; String s4 = "qq"; String s5 = "cdfg"; List<String> list = new ArrayList<String>(); list.add(s1); list.add(s2); list.add(s3); list.add(s4); list.add(s5); //定义两个变量 int start,end; start = 1; end = 3; //用ListIterator方法对集合进行迭代, for (Iterator<String> it = list.listIterator();it.hasNext() ; ) { String s = it.next(); //只要字符串所在的角标符合条件,将该字符串对象替换成另一个对象。 if (start<=list.indexOf(s) && list.indexOf(s)<=end) { Collections.replaceAll(list,s,"zzzzzz"); } } sop(list); } public static void sop(Object obj) { System.out.println(obj); } }
运行结果为:
反转集合中元素的方法——Collections.reverseOrder()
以上的例子中,使用Collections工具类给List集合进行排序,是可以排的,
但是不可以给Set集合进行排序,因为Set集合有自己的排序方法。
现在如果要对Set集合中已经排序的元素进行反转,该怎么办呢?
用自定义比较器的方法
//给Set集合中的元素进行顺序的转换 import java.util.*; public class CollectionsDemo4 { public static void main(String[] args) { //先创建集合,在集合中添加字符串对象 Set<String> set = new TreeSet<String>(new MyReverse()); set.add(new String("aaaaa")); set.add(new String("bdfg")); set.add(new String("vdfeggdc")); set.add(new String("cc")); set.add(new String("q")); //打印集合中的元素 //sop(set); //[aaaaa, bdfg, cc, q, vdfeggdc]*********原始顺序是按照自然顺序进行排序的。 sop("逆转后的集合顺序为:"+set); } public static void sop(Object obj) { System.out.println(obj); } } //按照之前学过的传自己比较器的方式,也可以进行顺序的逆转。 //只要把s1和s2的比较顺序调换就行了。 class MyReverse implements Comparator<String> { public int compare(String s1,String s2) { int num = s2.compareTo(s1); return num; } }
运行结果:
方法二: 用集合工具类的方法reverseOrder()
//给Set集合中的元素进行顺序的转换 import java.util.*; public class CollectionsDemo4 { public static void main(String[] args) { //先创建集合,在集合中添加字符串对象(在创建集合的时候就开始调用工具类的方法) Set<String> set = new TreeSet<String>(Collections.reverseOrder()); //Collections.reverseOrder()能强行逆转集合中的自然比较方法 set.add(new String("aaaaa")); set.add(new String("bdfg")); set.add(new String("vdfeggdc")); set.add(new String("cc")); set.add(new String("q")); //打印集合中的元素 //sop(set); //[aaaaa, bdfg, cc, q, vdfeggdc]*********原始顺序是按照自然顺序进行排序的。 sop("逆转后的集合顺序为:"+set); } public static void sop(Object obj) { System.out.println(obj); } }
运行结果:
但是如果集合不是自然排序,而是用自己定义的比较器进行排序的呢?
//给Set集合中的元素进行顺序的转换 import java.util.*; public class CollectionsDemo4 { public static void main(String[] args) { //先创建集合,在集合中添加字符串对象(在创建集合的时候就开始调用工具类的方法,里边可以传入自定义的比较器) Set<String> set = new TreeSet<String>(Collections.reverseOrder(new StrLenCompartor())); //Collections.reverseOrder(new 比较器)可以逆转比较器中的排序方式 set.add(new String("aaaaa")); set.add(new String("bdfg")); set.add(new String("vdfeggdc")); set.add(new String("cc")); set.add(new String("q")); //打印集合中的元素 //sop(set); //[q, cc, bdfg, aaaaa, vdfeggdc]*********只使用比较器的结果是按照长度顺序进行排序的。 sop("逆转后的集合顺序为:"+set); //[vdfeggdc, aaaaa, bdfg, cc, q]**********使用工具类方法,传入比较器的结果是按照长度顺序反转过来的顺序排列的。 } public static void sop(Object obj) { System.out.println(obj); } } //自定义比较器,按照长度由短到长排序 class StrLenCompartor implements Comparator<String> { public int compare(String s1,String s2) { int num = new Integer(s1.length()).compareTo(new Integer(s2.length())); if (num==0) return s1.compareTo(s2); return num; } }
集合中线程安全的问题
我们知道很多升级后的集合存数对象的时候,为了提高效率,都用的是线程不同步的,
那么,如果真的遇到了多线程操作集合的问题呢?
java中的集合工具类(Collections)中有专门提供的线程安全的集合:
该方法的牛逼之处在于:你给我一个线程不安全的集合,我返回一个线程安全的集合。
集合工具类中的其他方法:
Collections.swap(List list,int i,int j)将List集合中的指定两个元素交换位置。
Collections.shuffle(List list)将集合中的元素排序方式打乱,每次运行结果都不一样,就像是洗牌的一样。
//shuffle方法的演示 import java.util.*; public class CollectionsDemo5 { public static void main(String[] args) { String s1 = "dgsch"; String s2 = "aaa"; String s3 = "zz"; String s4 = "qq"; String s5 = "cdfg"; List<String> list = new ArrayList<String>(); list.add(s1); list.add(s2); list.add(s3); list.add(s4); list.add(s5); sop(list); Collections.shuffle(list);//调用shuffle方法,使集合中的元素顺序打乱 sop(list); } public static void sop(Object obj) { System.out.println(obj); } }
运行结果为:
Arrays工具类
专门用来操作数组的工具类里边全都是静态方法。
Arrays.toString()直接打印数组中的内容:
//Arrays工具类的基本方法演示 import java.util.*; public class ArraysDemo { public static void main(String[] args) { int[] arr = {3,6,1}; sop(Arrays.toString(arr)); } public static void sop(Object obj) { System.out.println(obj); } }
List list Arrays.asList(arr):传进来一个数组,返回一个List集合
//Arrays工具类的基本方法演示 import java.util.*; public class ArraysDemo { public static void main(String[] args) { String[] strs = {"haha","hehe","nhao","llala","hiahia"}; //将字符串数组变成List集合 List<String> list = Arrays.asList(strs); sop(list); } public static void sop(Object obj) { System.out.println(obj); } }
把数组变成集合有什么好处呢?
可以使用集合的思想和方法来操作数组中的元素。
注意:将数组变成集合,不可以使用集合的增删方法,因为数组的长度是固定的。
contains()
get()
indexOf()
subList()这些方法都可以用。
如果进行增删操作的话会发生异常:UnsupportedOperationException(不支持操作异常)
sop(list.contains("haha")); //看返回的真假来判断集合中是否存在这样的元素,如果是数组的话需要遍历整个数组。
上边代码证实,存入String类型的数据,将数组变为集合的时候,打印出来集合的元素也是字符串,但是换成int类型的数组的时候打印出来的就是哈希值。
//Arrays工具类的基本方法演示 import java.util.*; public class ArraysDemo { public static void main(String[] args) { int[] arr = {3,5,78,1,5,6}; //将字符串数组变成List集合 List list = Arrays.asList(arr); //如果要写泛型的话应该写成这个样子。 //List<int[]> list = Arrays.asList(arr); sop(list); } public static void sop(Object obj) { System.out.println(obj); } }
如果想要打印出来的值不是哈希值得话,要这样写
//Arrays工具类的基本方法演示 import java.util.*; public class ArraysDemo { public static void main(String[] args) { Integer[] arr = {3,5,78,1,5,6}; //将字符串数组变成List集合 List<Integer> list = Arrays.asList(arr); sop(list); } public static void sop(Object obj) { System.out.println(obj); } }
注意:如果数组中的元素都是对象,那么变成集合的时候,数组中的元素就直接转成集合中的元素。
如果数组中的元素都是基本数据类型,那么会将该数组作为集合中的元素作为集合中的元素存在。
集合变数组
使用Collection接口中的toArray方法。
//将集合转变成数组 //使用Collection接口中的toArray方法 import java.util.*; public class CollectionToArray { public static void main(String[] args) { List<String> list = new ArrayList<String>(); list.add(new String("nihao")); list.add(new String("shijie")); list.add(new String("hello")); list.add(new String("world")); //开始转变,参数需要一个数组 String[] strs = list.toArray(new String[4]); //将数组中的元素直接打印出来,可以用到Arrays中的toString方法 System.out.println(Arrays.toString(strs)); } }
那么问题也就来了,
1,指定类型的数组到底要定义多长呢?
当指定类型的数组长度小于了集合的size,那么该方法内部会创建一个新的数组,长度为集合的size
当指定类型的数组长度小于了集合的size,就不会新创建数组,而是使用传递进来的数组。
所以创建一个刚刚好的数组最优。
2,为什么要将集合转变成数组?
为了限定对元素的操作。
数组有固定的长度,一旦确定就不可以再进行增删操作,
增强for循环
格式:for(数据类型 变量名 : 被便利的集合(Collection)或者数组) { }
jdk1.5新特性
作用:
用于对集合进行遍历
只能获取集合元素,但是不能对集合进行操作。
迭代器除了遍历,还可以进行remove集合元素的动作。
如果是用ListIterator,还可以再遍历过程中对集合进行增删改查的动作。
传统for和高级for有什么区别呢?
高级for有一个局限性,必须有被遍历的目标。
比如说打印100次hello world,还是传统for好使。
简单演示一下增强for循环的用法:
//增强for循环演示 import java.util.*; public class ForEachDemo { public static void main(String[] args) { List<String> list = new ArrayList<String>(); list.add(new String("nihao")); list.add(new String("shijie")); list.add(new String("hello")); list.add(new String("world")); //数据类型 变量名 : 被遍历的目标 for (String s: list) { System.out.println(s); } } }
增强for循环的应用:
//增强for循环的应用 import java.util.*; public class ForEachTest { public static void sop(Object obj) { System.out.println(obj); } public static void main(String[] args) { //增强for循环遍历数组 sop("增强for循环遍历数组"); int[] arr = {2,5,3,8,4}; for(int i:arr) { sop("i= "+i); } //用增强for循环遍历set集合中的元素 sop("增强for循环遍历set集合中的元素"); Set<String> set = new HashSet<String>(); set.add(new String("abc")); set.add(new String("dsaf")); set.add(new String("fdfd")); set.add(new String("ccc")); for(String s: set) { sop("s....."+s); } //用增强for循环遍历map集合中的元素 TreeMap<Integer,String> tm = new TreeMap<Integer,String>(); tm.put(1,"a"); tm.put(2,"b"); tm.put(3,"c"); tm.put(4,"d"); //方法一,使用keySet方法 sop("用增强for循环遍历map集合中的元素(keySet方法)"); Set<Integer> keySet = tm.keySet(); for(Integer i:keySet) { System.out.println(i+"--------"+tm.get(i)); } //方法二,使用entrySet方法 sop("用增强for循环遍历map集合中的元素(entrySet方法)"); Set<Map.Entry<Integer,String>> entrySet = tm.entrySet(); for(Map.Entry<Integer,String> me :entrySet) { System.out.println(me.getKey()+"*******"+me.getValue()); } } }
运行结果为:
可变参数
jdk1.5出现的心特性使用时注意:可变参数一定要定义在参数列表的最后边。
考虑这样一种情况:
public void show(int a,int b) { System.out.println(a+","+b); }
类似这样的show方法的功能,如果我需要打印三个参数的值呢?四个五个呢,创建多个重载方法是可以的,但是那样写太多了。
我们可以创建一个数组:
public void show(int[] arr) { System.out.println(Arrays.toString(arr)); }
这样也可以,但是不足之处在于,每次调用该方法都要定义一个数组,不太优。
可变参数方法:只要多个参数类型是同一种(int),可以让多个参数自动装箱封装进一个数组(arr)中去。
如果参数中需要其他的参数,记住可变参数一定要定义在参数列表的最后边。
//可变参数演示 public class ParamMethodDemo { public static void main(String[] args) { show(2,5,7,5,8,2); show("nihao",4,7,4,3,2,9); } public static void show(int...arr)//可变的参数数量,都会封装进数组中去 //格式:参数类型...数组名称 { System.out.println(arr.length); } //如果参数中需要其他类型的参数,将其他类型的参数放在最前边,把可变参数放在最后边 public static void show(String s,int...arr) { System.out.println(s+arr.length); } }
静态导入
jdk1.5新特性。使用集合工具类和数组工具类的时候,每次都要写Collections和Arrays,用很多次就写很多次。
一个一劳永逸的方法就是,静态导入,
因为Collections和Arrays里边都是静态方法,导入类之后,就可以直接调用类中的成员方法了。
具体导入方法和使用实例如下:
import java.util.*; //在这里静态导入 import static java.util.Arrays.*;//将Arrays 中的静态的成员导入进来 public class StaticImportDemo { public static void main(String[] args) { int[] arr = {3,5,8,5,3,6}; //给数组进行排序: sort(arr); //打印数组中的元素 System.out.println(Arrays.toString(arr)); //使用二分法查找元素 int index = binarySearch(arr,8); System.out.println(index); } }
运行结果:
注意:在以上代码中,打印数组中的元素一行中,必须写Arrays.toString(),
因为Arrays类继承的是Object类,而Object里边也有toString方法,
这里认为他是使用的Object中的toString方法,
所以必须在方法前指明类名。
记住:
当雷鸣重复时,需要指定具体的包名。
当方法重名时,需要指定具体的对象或者类。
相关文章推荐
- spring mvc中的@PathVariable
- 深入理解JAVA虚拟机笔记-05
- eclipse创建maven管理的web项目
- 【转】java中的TreeMap与TreeSet关系及实现(红黑树)
- 二叉树的三种排序Java实现
- java中的异常
- 你的Java日志,有没有用这些改进办法
- JAVA学习第十天
- Java调用存储过程返回数组
- JAVA 并发编程-线程池(七)
- 重学java23种设计模式(5)单例模式
- JAVA操作LDAP总结
- 《深入浅出struts2》--第八章,输入验证-内建验证程序required,requiredstring和stringlength
- SSH学习一 STRUTS2 DTO
- java入门相关
- spring中@value注解需要注意
- 重学java23种设计模式(4)原型模式
- java.lang.NumberFormatException: empty String
- 《深入浅出struts2》--第七章,类型转换-支持与Map一起使用
- poj 1001 java大精度