前言

    java的集合机制,本质上是一个数据容器,像之前学过的数组,字符串都是一种数据容器。但是集合它提供的功能更加强大,便捷。里面提供的方法的底层源代码采用的也是优秀的效率算法。其他数据容器能操作的,集合都能操作。而且代码更加简洁,思路更加清晰,运行的效率更加高。因此,完全掌握完集合。编程的技能会进一步提高。

    已经对集合的神奇之处迷恋到无法自拔。学完集合,才发觉我以前的代码都是垃圾的说。

一 集合

首先放一张图给出集合的层次体系结构:

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) 复制集合。