您的位置:首页 > 编程语言 > Java开发

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方法,

所以必须在方法前指明类名。

记住:

当雷鸣重复时,需要指定具体的包名。

当方法重名时,需要指定具体的对象或者类。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: