前言
java的集合机制,本质上是一个数据容器,像之前学过的数组,字符串都是一种数据容器。但是集合它提供的功能更加强大,便捷。里面提供的方法的底层源代码采用的也是优秀的效率算法。其他数据容器能操作的,集合都能操作。而且代码更加简洁,思路更加清晰,运行的效率更加高。因此,完全掌握完集合。编程的技能会进一步提高。
已经对集合的神奇之处迷恋到无法自拔。学完集合,才发觉我以前的代码都是垃圾的说。
一 集合
首先放一张图给出集合的层次体系结构:
Java中的集合层次结构分为单列集合(Collection)和双列集合(Map)。单列和双列的直接理解就是,集合的每个项能存储多少个数据。Collection以及它的子类在每个项中能存储一个数据,因此是单列集合。Map以及它的子类一次性能存储两个数据,键和值,因此是双列集合。接下来逐个分析这两个体系。
一、Collection 体系
1.特点:
Collection是一个怎么样的集合呢?与Map相比,最大的特点是它是个单列集合。但我们不能说Collection是有序的,至少它的Set(散列)是一个无序的。注意了,我这里的有序不是指从小到大的顺序排列,而是指集合存进去和取出来是否一样,能保证一样就是有序,不能确保一样就是无序。我们也不能说它是由索引的。因为它的子类List由索引,而set没有索引。所以这里对 Collection的特征描述就是,
一个具有迭代器,存储单列数据的顶层接口。
在这里我要再提一个多态情况:父类的引用指向子类的对象。后面会经常出现这样的情况:
Collection c = new ArrayList<String>();
2.常用方法:
boolean add(E e) :添加一个元素到末尾
boolean removeAll(Collection A) 移除此 collection A中那些也包含在指定collection 中的所有元素
boolean contains(Object o) 判断是否包含某个元素
iterator() :返回迭代器
boolean remove(Object o) 如果存在元素则移除成功
Object[] toArray() :将元素数组化
int size() 返回此 collection 中的元素数。
boolean isEmpty() 是否为空
3.应用:
上面的每个方法都是较为常用的。例如:
boolean add(E e) 添加元素到末尾,注意了。Collection没有添加指定索引的元素。这个是它子类List的独特方法
boolean contains(Object o) 包含某个元素 这个必须好用,不需要再用for循环来遍历集合,会使代码简便很多
Object[] toArray() :将元素数组化 将集合返回里面的元素返回一个对象数组,是一种便捷的遍历方式和获取数据方式
4.遍历方法:
1.利用方法toArray(),可以把集合转换成数组,然后遍历数组即可
2.使用迭代器Iterator():返回可以进行迭代器进行迭代的方法
Collection不能用for循环遍历,因为它还不是一个有序的容器,要想使用for循环,只能用toArray()方法,先转换成一个有序的数组,再用for循环遍历
后面会讲到,for循环分为两种,基于索引值的称为普通for循环;基于底层实现为迭代器的称为增强for循环(也叫foreach);
另外,在选择是用迭代器遍历还是普通for循环遍历。有个判断方法是看集合本身有没有索引值。Iterator能够遍历Collection所有集合。普通for循环只能遍历有序有索引的List体系的集合。Set体系无序的集合不能遍历。
5.总结:
毕竟Collection是顶层接口,重点掌握它的应用:
contain()方法,是否包含
toArray() 转为数组
Iterator() 迭代器
案例1:
/*
* 第一题:分析以下需求,并用代码实现
1.生成10个1至100之间的随机整数(不能重复),存入一个List集合
2.然后利用迭代器和增强for循环分别遍历集合元素并输出
3.如:15 18 20 40 46 60 65 70 75 91(可以无序)
*/
public class Test01 {
public static void main(String[] args) {
Random random = new Random();
List<Integer> list = new ArrayList<>();
int count = 0;
while (count < 10) {
int num = random.nextInt(100) + 1;
int i = 0;
for (; i < list.size(); i++) {
if (list.get(i) == num) {
break;
}
}
if (i == list.size()) {
list.add(num);
count++;
}
}
Iterator<Integer> it = list.iterator();
System.out.println("迭代器输出:");
while (it.hasNext()) {
System.out.println(it.next());
}
System.out.println("--------------");
System.out.println("增强for循环输出:");
for (int num : list) {
System.out.println(num);
}
// System.out.println(list);
}
//使用list.contain()方法做出的效果
public void getNoRepeatRandomNums(ArrayList<Integer> list) {
Random r = new Random();
while(list.size()<10)
{
int num = r.nextInt(100) + 1;
if(!list.contains(num))
list.add(num);
}
}
}
上面的案例是我原先没注意到顶层接口Collection 有个contain方法,一直都是用for循环来解决,结果是正确的。但是算法的性能是差的,还有大量for 循环,代码是不美观的。这不是一个程序员的修养。
二、Collection子类一:List集合
1.特点List继承Collection的所有方法,但是独有地形成了一个有序,有索引,元素可以重复的集合体系。
2.独特方法(Collection的方法,List集合同样具备):
void add(int index, E element) 在列表的指定位置插入指定元素。因为允许重复,因此会一定添加成功
E get(int index) 根据索引值获取元素
remove(int position) 根据索引值删除元素
3.在这里List集合与父类相比,能够根据索引值来修改数据结构,和获取数据方法
这里注意一点:add()方法总是能执行成功,因为List本身的特性是允许重复。所以总是能添加成功
4.相比下面要讲的子类结构,List缺少一个修改方法set,所以不再多做延伸。直接看它的最终完善子类ArrayList和LinkedList
三、List子类一:ArrayList数组集合
1,特点:ArrayList子类算是有序集合的最后子类了。里面提供了完整的获取,判断,增加,删除,修改,查询等功能。
2.常用方法():
Object clone() 返回此 ArrayList 实例的浅表副本。
indexOf(Object o) 返回此列表中首次出现的指定元素的索引,或如果此列表不包含元素,则返回 -1。
int lastIndexOf(Object o) 返回此列表中最后一次出现的指定元素的索引,或如果此列表不包含索引,则返回 -1。
E remove(int index) 根据索引移除此列表中指定位置上的元素。并返回被移除的元素
boolean remove(Object o) 移除此列表中首次出现的指定元素(如果存在)。
E set(int index, E element) 用指定的元素替代此列表中指定位置上的元素。
void removeRange(int fromIndex, int toIndex) 移除列表中索引在 fromIndex(包括)和 toIndex(不包括)之间的所有元素
3.4种遍历方式:
1).利用toArray()方法转换成对象数组Object[]。进行普通for循环遍历
2).根据有索引的特性,利用get()和size()方法,直接用普通for循环遍历(适用于获取,查询,删除,修改等情况)
3).用增强for循环进行遍历(只适用于获取元素;因为底层代码是迭代器实现,不能用来做删除和增加遍历)
增强for由于底层也是使用的迭代器,无法在遍历的时候增删元素
4).用迭代器Iterator遍历(适用情况同增强for循环。)
4.特点:查询快,增删慢(连续空间存储的数据结构共有的特点)
5.应用:
ArryaList是个灵活强大的数组容器。相比普通的数组容器,它的size长度是灵活可变的。这样有了以下的使用特点,也是比较值得注意的:
(1) 增删:在利用ArrayList的add()方法做添加元素的时候,当你在指定位置插入(添加)一个元素时,其后面的元素会自动向后移动一个单位空间。所以有时你用for(int i = 0;i<arrayList.size();i++)遍历,匹配到一个插入点A时,成功插入B,数组的结构由原来的AC变成了ABC 。这时候,如果想要下次遍历到C而不是B的话。在add()后面要补上一个i++;这样就不会做到重复遍历了。
相同原理下,在remove()删除元素的时候,假设原来的数据结构为ABC。在遍历到删除点A,在删除了A以后,BC是自动向前移动了一个单位,B代替了A的位置。此时,你也应该在remove()下一句代码补上一个i--。这样子才不会错过B的遍历。
以上分析,可以看出在插入和删除的情况下,数组型的集合发生了大规模的改动,在空间上的性能,比用链表做成的LinkedList是差了很多。
(2)优点是ArrayList是个允许重复元素的集合。所以大部分情况下用起来是很顺手的。
四、List子类二:LinkedList链表集合
1.理解 LinkedList集合的底层实现是一个链表的数据结构,但是它同样也是一个有序集合,也有索引值。上面ArrayList的遍历情况对于LinkedList同样试用。根据链表结构这个空间不连续存储的特点:增删快,查询慢。是和ArrayList的最大区别。所以在使用大量修改,增加,删除数据的时候,用LinkedList最为方便。一般情况下,还是想用ArrayList。比较熟悉吧。大数据大规模修改的也不是我们这种初学者所能经常接触的啦。
顺便理解一下链表这种数据结构:是通过结点来链接组成的一种数据结构,每个结点存储地址值,值,下一个节点地址值这三个。这样形成每个结点都和前后结点有互相关系。当你插入一个结点的时候,只需要改动两个结点:告诉前结点,你的下一个结点现在是我了,告诉插入的结点,你的下一个结点是被你占了位置的结点。三角关系自己处理。其他结点根本不晓得发生啥情况。
LinkedList因为有一些特有的方法。可以用来模仿另外两个数据结构:栈,队列
(1)栈的特点:先进后出。看两个现实案例:
1).弹夹:往弹夹子弹里装子弹。当子弹全部装满的时候。第一个子弹想要发射,就必须等待前面的子弹都打光。
最后一个装进去的子弹,是打出去的第一发。
2).小巷:在一个很窄的小巷里面,最里面是一堵墙。前天你第一个往这个小巷停车。今天想把车开出去。必须要等前天进来的车都开出去了,才有出口。
因此,栈的本质特点是一个出入口相同并且只有一个的容器。最先进去的排在最后面出来。最后出去的总能第一个出来
(2)队列的特点:先进先出:
当一个元素添加进来的时候,前面的元素都会自动从当前位置向出口方向移动一个单位。
2.独特方法:
void addFirst(E e) 插入第一个
void addLast(E e) 插入最后一个
E getFirst() 获得第一个
E getLast() 获得最后一个
E removeFirst() 移除第一个
E removeLast() 移除最后一个
上面的独特方法阿。用得少,目前所知是用来模仿栈和队列是好用的。
综上,用自己的理解完成了对有序集合的总结。
接下来甩出几个案例:
案例2:
/*
* 模仿一种纸牌规则像向每个玩家随机发两张牌,玩家两张牌的总点数达到最大时。就获得胜利
*/
public class CollectonsTest {
public static void main(String[] args) {
ArrayList<Integer> cardsList = new ArrayList<>();
ArrayList<Player> playerList = new ArrayList<>();
// 添加牌组到卡牌列表中
for (int i = 0; i < 13; i++) {
for (int j = 0; j < 4; j++)
cardsList.add(i + 1);
}
// 创建玩家,存进玩家列表
for (int i = 0; i < 4; i++) {
Player p = new Player(new int[] {}, "玩家" + (i + 1));
playerList.add(p);
}
// 洗牌:工具类Collections的shuffle(List list)方法:随机改变一次集合的顺序(打乱)
Collections.shuffle(cardsList);
// System.out.println(cardsList);
// 开始发牌:每人发两张。从列表中去掉发出的牌
for (int i = 0; i < playerList.size(); i++) {
Player p = playerList.get(i);
int cards[] = new int[2];
for (int j = 0; j < 2; j++) {
// 实际场景是总会发第一张牌。remove方法会返回被删除的元素。然后删除,后面的元素
// 自动向前靠前1位
cards[j] = cardsList.remove(0);
}
p.setCard(cards);
}
// 发牌完毕,开始比较每个玩家的牌面
System.out.println("玩家名称" + "\t" + "牌面" + "\t" + "总和");
int max = 0;
for (int i = 0; i < playerList.size(); i++) {
Player player = playerList.get(i);
String name = player.getName();
int cards[] = player.getCard();
int sum = cards[0] + cards[1];
max = Math.max(max, sum);
// 打印信息
System.out.print(name + "\t" + "(" + cards[0] + "," + cards[1] + ")" + "\t" + sum);
System.out.println();
}
System.out.println("最大点数:" + max);
System.out.println("剩余牌组情况:");
System.out.println("还剩" + cardsList.size() + "张牌:");
for (int i = 0; i < cardsList.size(); i++) {
if (i % 4 == 0)
System.out.println();
System.out.print(cardsList.get(i) + "\t");
}
}
}
class Player {
String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Player(String name) {
super();
this.name = name;
}
int[] cards;
public int[] getCard() {
return cards;
}
public void setCard(int[] card) {
this.cards = card;
}
public Player(int[] card, String name) {
super();
this.cards = card;
this.name = name;
}
public Player() {
super();
}
}
/*
*输出结果: 玩家名称 牌面 总和 玩家1 (3,13) 16 玩家2 (9,7) 16 玩家3 (2,10) 12 玩家4 (13,12) 25 最大点数:25
*/
案例3:
/*
* 第三题:分析以下需求,并用代码实现
1.有如下代码:
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("a");
list.add("a");
list.add("c");
list.add("c");
list.add("a");
list.add("d");
noRepeat(list);
System.out.println(list);
}
2.定义一个noRepeat()方法,要求对传递过来集合中进行元素去重
public static void noRepeat(List<String> list){
}
*/
public class Test03 {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("a");
list.add("a");
list.add("c");
list.add("c");
list.add("a");
list.add("d");
setNoRepeatList(list);
System.out.println(list);
}
//垃圾写法
public static void noRepeat(List<String> list) {
for (int i = 0; i < list.size(); i++) {
String iStr = list.get(i);
for (int j = i + 1; j < list.size(); j++) {
if (iStr == list.get(j)) {
list.remove(j);
j--;
}
}
}
}
//高级写法
public static void setNoRepeatList(List<String> srcList) {
ArrayList<String> list = new ArrayList<>();
for (int i = 0; i < srcList.size(); i++) {
if (!list.contains(srcList.get(i)))
list.add(srcList.get(i));
}
srcList.clear();
srcList.addAll(list);
}
}
案例3出现了垃圾写法和高级写法。再次提到了善于综合运用集合方法,来使自己的代码达到最简洁程度
有序集合体系总结完毕
五、Collection子类二:Set散列集合
1.特点:无序,无索引,元素唯一的接口。
Set是单列集合Collection下的无序,无索引,元素不重复的集合接口。可以说与List接口完全相反。在这里要提到set接口如何做到不重复的根本原因的话,是一个叫做哈希值的东东:hashCode。以我现在的程度理解,hashCode由所有java类的根接口Object开始就存在的,代表了所有对象的地址值。。set接口在底层代码实现是根据一个算法算出存入的每个数据的hash值。每个存入的hash值都不一样。然后调用equals方法比较数据之间的hash值。也就是比较对象的地址值而不是内容值
set接口无序无索引的情景,就像是一锅粥,往里面扔东西。存进去遍历,与取出遍历的结果不保证一致。、
要完全掌握Set接口,最重要理解底层实现hashCode 代表对象的地址值。和equals()方法比较对象的地址值而不是内容值
2.常用方法:能使用Collection的所有方法
添加元素add()做了重写。保证添加不重复
3.应用场景:
元素去重;根据boolean add(E e)判断是否添加成功。同样的元素添加进去会返回false;
六、hashSet
HashSet实现Set接口,由哈希表(实际上是一个HashMap实例)支持。它不保证set 的迭代顺序;特别是它不保证该顺序恒久不变,此类允许使用null元素。
运用HashSet要掌握它的无序,无索引,元素不重复的特点。在默认情况下,当存入hashSet的类型是自己自定义的对象,相同内容是不会被视作为相等的。因为默认的情况下。hashcode对于两个对象的地址值表示是不想等的。euqals通过对比对象之间的hashCode是否相等来判断是否重复
要想让相同内容值的对象相等,要重写自定义对象中的hashCode()方法和equals方法。在Eclipse中的快捷键是shift+alt+s+h自动生成。
要是不理解。请查看hasSet的底层源代码。
案例1 :输入一串字符串,对该字符串进行去重
public class Test02 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("输入一行字符串:");
String lines = sc.nextLine();
HashSet<Character> set = new HashSet<>();
for (int i = 0; i < lines.length(); i++) {
Character ch = lines.charAt(i);
set.add(ch);
}
for (Character ch : set)
System.out.println(ch);
}
}
案例2:重写对象的hashcode()和equals方法,来判定两个内容值相等的对象不重复。
import java.util.HashSet;
public class HashSetDemo2 {
public static void main(String[] args) {
// 集合使用方法
// -创建集合对象
HashSet<Student> hs = new HashSet<>();
// -创建元素对象
Student s1 = new Student("zhangsan", 18);
Student s2 = new Student("lisi", 20);
Student s3 = new Student("lisi", 20);
// hs.add(new Student("lisi",22));
// hs.add(new Student("wangwu",28));
// -添加元素到集合
hs.add(s1);
hs.add(s2);
hs.add(s3);// s2和s3是在内容上是相等的。因为对象本身已经重写hashCode()和equals()方法。所以能添加成功
// -遍历集合
for (Student s : hs) {
System.out.println(s);
}
}
}
class Student {
String name;
int age;
public Student() {
super();
// TODO Auto-generated constructor stub
}
public Student(String name, int age) {
super();
this.name = name;
this.age = age;
}
public int getAge() {
return age;
}
public String getName() {
return name;
}
public void setAge(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + "]";
}
@Override
public boolean equals(Object obj) {
System.out.println("==========");
Student s = (Student) obj;// 向下转型,可以获取子类特有的成员
if (s.getAge() != this.getAge())//比较年龄,如果不相等则返回false;
return false;
if (s.getName() != this.getName())//比较姓名,如果不相等则返回false;
return false;
return true;
// obj = (Student) obj;
}
@Override
public int hashCode() {
// TODO Auto-generated method stub
return age+name.hashCode();
}
}
七,集合的迭代器Iterator
前面分析,我们直到凡是属于Collection体系下的子类都具有迭代器的功能。从List->ArrayList、LinkedList有序有索引元素可重复的集合,Set->hashSet无序无索引元素不唯一的集合,都可以使用迭代器来遍历。这里要注意三点情况
1.迭代器相当于集合的一个副本,要是在迭代器遍历的时候,集合发生了增删的情况。迭代器会引发并发异常,终止程序运行。唯一的解决办法是避免集合自己去增删,而让迭代器做增删功能。
2.增强for循环的底层是由迭代器实现的。因此,也要遵循迭代器的规则。在遍历获取元素或者修改元素值的时候好用。增删元素不好用。
3.普通for循环是通过索引值来遍历的。有索引值集合可以遍历。没有索引值的集合无法遍历。但是迭代器能够遍历有索引值,和没有索引值的集合
八、map
map是双列集合的顶层接口。存储的特点是"key-value"(K-V泛型)两个数据。也称为"键-值"对。键的值是唯一的。有重复的键添加到map时。会覆盖前面的键值。
1.常用方法:
1).boolean containsKey(K key):查找是否包含关键值 key。是就返回true,否则就返回false;
2).boolean containsValue(V value):查找是否有包含一个值,是就返回true,否则返回false;
3).V get(K key):通过关键字key获取与它对应的值
4).V put(K key,V value) 插入一组"键-值"对应的数据项。如果键已经存在,将返回被覆盖的值。特别注意的是,在map中,键是唯一的,但是当插入同样的键,也可以插入成功。能够覆盖前面的值。下面将印证这一个覆盖的结论。
案例1:印证HashMap的put()方法:
public class MapPutMethodTest {
public static void main(String[] args) {
HashMap<Integer, String> hashMap = new HashMap<>();
String s1 = hashMap.put(1,"我是1号");
String s2 = hashMap.put(1, "我来覆盖1号");
System.out.println("s1="+s1);
System.out.println("s2="+s2);
}
}
//输出结果
/*
s1=null
s2=我是1号
*/
上面第一次插入(1,"我是1号") s1的结果是null,原因是1在map中没有找到重复,是唯一的。没有替代任何值。因此s1 = null;
第二次插入(1,"我来覆盖1号"); s2 的结果是“我是1号”,原因是1在map是已经存在的。再插入关键字 1 就返回了被替代的值"我是1号";
5).remove(K key);根据关键字Key 删除它的数据
6).Set(K) keySet(); 返回不重复的关键字Set集合。应用于遍历
7).Set(Map.Entry(K,V)) entrySet():返回对象是map内部类的Entry实体 的Set集合,Entry包含了键和值。应用于遍历
2.遍历方法:
Map本身不具备迭代器。但是与具有迭代器的Set集合有方法关联。因此遍历Map的方法是,获取Map的Set集合,然后再用Set集合的迭代器遍历。
这里有两个方法获取map的Set集合:
1).Set<K> keySet()方法
2).Set(Map.Entry(K,V) 方法
通过案例来说明两种方法遍Map:
案例1:Set<K> keySet()
public class MapDemo4 {
public static void main(String[] args) {
Map<String,String> map = new HashMap<>();
map.put("谢霆锋", "张柏芝");
map.put("陈冠希", "钟欣桐");
map.put("李亚鹏", "王菲");
//获取所有的键值
Set<String> keys = map.keySet();
for(String key:keys)
{
String value = map.get(key);
System.out.println("键:"+key +"---"+"值:"+value);
}
}
/*
*输出结果:
*
* 键:谢霆锋---值:张柏芝
键:陈冠希---值:钟欣桐
键:李亚鹏---值:王菲
*/
}
案例2:Set(Map.Entry(K,V)
public static void main(String[] args) {
Map<String, String> map = new HashMap<>();
map.put("杨过", "小龙女");
map.put("张无忌", "周芷若");
map.put("段誉", "王语嫣");
//获取Entry对象
Set<Map.Entry<String, String>> entrys = map.entrySet();
for(Map.Entry<String, String> entry:entrys)
{
String hunsband = entry.getKey();
String wife = entry.getValue();
System.out.println(hunsband+"-----"+wife);
}
}
/*输出结果:
*
* 杨过-----小龙女
段誉-----王语嫣
张无忌-----周芷若
*/
Entry实体是Map的内部类,有个很形象的证明是像是一张结婚证,通过key关键字是丈夫,找到value值是老婆。
3.应用:
1)统计类算法使用Map的集合插入覆盖特点,能够达到性能最优。
举个例子:从字符串中"Hello,my name is Simons ,I Love Java"中统计各个字符出现的次数。
普通算法:遍历字符串,取出每个字符。与前面的字符作比较,有出现重复就跳过该字符。没有出现重复就与后面的字符作比较,出现重复就加1。理论上算法的复杂度为(m*n);
Map插入算法:以字符为键,统计次数为值 作为数据建立Map。遍历该字符串。对于每个字符,使用put方法插入。如果不重复。插入这个值。如果重复,也插入该数据,只是统计次数在原先字符的基础上+1;理论上的算法复杂度为(m);
/*
* 第三题:分析以下需求,并用代码实现
1.利用键盘录入,输入一个字符串
2.统计该字符串中各个字符的数量(提示:字符不用排序)
3.如:
用户输入字符串"wuyanzu-he^wo~shuo!ta@zui$shuai??"
程序输出结果:
!=1
e=1
@=1
$=1
a=3
n=1
o=2
h=3
i=2
-=1
w=2
u=5
t=1
s=2
~=1
^=1
?=2
z=2
y=1
*/
public class Test03 {
public static void main(String[] args) {
//method1();
method2();
}
//牛逼算法
private static void method2() {
Scanner scanner = new Scanner(System.in);
String lines = scanner.nextLine();
char[] chs = lines.toCharArray();
Map<Character, Integer> map = new HashMap<>();
for (Character ch : chs) {
if (!map.containsKey(ch)) {
map.put(ch, 1);
} else {
map.put(ch, map.get(ch)+1);
}
}
}
//垃圾算法
private static void method1() {
Scanner sc = new Scanner(System.in);
String strs = sc.nextLine();
HashSet<Character> set = new HashSet<>();
for (int i = 0; i < strs.length(); i++)
set.add(strs.charAt(i));
HashMap<Character, Integer> hashMap = new HashMap<>();
for (Character ch : set) {
int count = 0;
for (int i = 0; i < strs.length(); i++) {
if (ch == strs.charAt(i))
count++;
}
hashMap.put(ch, count);
}
// 遍历map
for (char key : hashMap.keySet()) {
int value = hashMap.get(key);
System.out.println(key + ":" + value);
}
}
}
这个案例算法1:利用set先对集合去重。再遍历Set集合,每个元素与原先的重复数据集合比较。最后将统计结果放入Map中。过程复杂,代码复杂。差评
算法2:直接用map来插入。性能好,代码简单
/*
* 键盘录入一个字符串,分别统计出其中英文字母、空格、数字和其它字符的数量,输出结果:”
其他=1, 空格=2, 字母=12, 数字=6”
*/
public class Test03 {
public static void main(String[] args) {
System.out.println("请输入一个字符串:");
Scanner sc = new Scanner(System.in);
String line = sc.nextLine();
System.out.println("line的长度:" + line.length());
HashMap<String, Integer> map = new HashMap<>();
String chTypeStr = "";
for (int i = 0; i < line.length(); i++) {
char ch = line.charAt(i);
if (ch == ' ')
chTypeStr = "空格";
else if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z'))
chTypeStr = "字母";
else if (ch >= '0' && ch <= '9')
chTypeStr = "数字";
else
chTypeStr = "其他";
if (!map.containsKey(chTypeStr))
map.put(chTypeStr, 1);
else
map.put(chTypeStr, map.get(chTypeStr) + 1);
}
int sumCount = 0;
for (String typeKey : map.keySet()) {
int typeCount = map.get(typeKey);
System.out.println(typeKey + "=" + typeCount);
sumCount += typeCount;
}
System.out.println("总和:" + sumCount);
}
}
/*
* 输出结果:
*
* 请输入一个字符串:
SDJKSAHLIAHSKJ!@@#!@1512451 33746ddd
line的长度:38
其他=6
数字=12
字母=17
空格=3
总和:38
*/
Map在这里先总结完毕。用最常用的HashMap来举例Map在程序中的优越使用情况。
九、集合工具类Collections 的使用
注意了,Collections多了一个s,是一个工具类,像之前使用过的Arrays,Math工具类一样,用来操作集合。关于工具类的制作方法,首先是构造方法私有化,防止被创造对象。接着是成员(包括成员变量和成员方法)添加静态,可以直接使用"类名.成员"的格式调用。
1.Collections用来操作集合。具体有以下常用方法:
1).void sort(List<T> list):根据集合中的自然元素排序(从小到大);
2).int binarySearch(List<T> list,T key):在排序完成的前提上, 用二分查找的算法在集合list中,根据关键字key查找它的索引值。
3).void reserve(List<T> list): 反转集合的元素
4).void shuffle(List<T> list): 打乱一次集合的元素原有的顺序。像洗牌一样的道理
5).void swap(List<T> list,int i,int j): 在指定的集合中,交换两个元素的位置
6).void copy(List<T> dest,List<T> src) 复制集合。