Java基础之集合知识点总结二

本文深入讲解Java泛型的概念、使用场景及其带来的好处。通过多个示例介绍了如何在类、接口和方法中应用泛型,包括泛型类、泛型接口、泛型方法等。此外,还探讨了泛型的高级应用,如泛型限定、通配符等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


泛型:集合中存储了不同类型的对象,取出时,容易在运行时时期发生ClassCastException类型转换异常。
例子1.集合中存储不同类型时,出现错误。
package cn.itheima.day12;
import java.util.ArrayList;
import java.util.Iterator;
public class GenericDemo {
	public static void main(String[] args) {
		ArrayList al = new ArrayList();
		al.add("haha");
		al.add("hehe");
		al.add(new Integer(3));
		Iterator it = al.iterator();
		while(it.hasNext()){
			String s = (String)it.next();
			System.out.println(s+"::"+s.length());
		}
		
	}
}
为了避免这个问题的发生。如果在存储的时候就明确了集合要操作的数据类型,这样取出就没有问题了,就需要在定义集合时,就立刻明确元素的类型。其实借鉴于数组。可以通过<>来明确元素的类型。

例子2:使用泛型,改变例子1,使得错误避免。

package cn.itheima.day12;
import java.util.ArrayList;
import java.util.Iterator;
public class GenericDemo {
	public static void main(String[] args) {
		ArrayList<String> al = new ArrayList<String>();
		al.add("haha");
		al.add("hehe");
		Iterator<String> it = al.iterator();
		while(it.hasNext()){
			String s = it.next();
			System.out.println(s+"::"+s.length());
		}
	}
}

泛型的好处    1,将运行时期出现的ClassCastException问题,转移到了编译时期。
    2,避免了强制转换的麻烦。
泛型其实是JDK1.5版本以后出现的一个安全机制。泛型其实给编译器使用的。泛型的表现形式就是<>。
编码时,什么时候使用泛型呢?
在API中,只要用到的类或者接口的旁边有<>时,就要明确具体类型。泛型的使用其实就是给<>传递实际参数,而这个参数就是一个具体引用数据类型。
例子3.使用泛型,完成自定义比较器的定义,使得自定义的Person按照姓名来排序。
package cn.itheima.day12;
import java.util.Comparator;
import java.util.Iterator;
import java.util.TreeSet;
/**
 * 现在要对Person对象按照年龄进行排序,使用TreeSet集合
 * @author wl-pc
 */
public class GenericDemo2 {
	public static void main(String[] args) {
		TreeSet<Person4> ts = new  TreeSet<Person4>(new CompareByName1());
		ts.add(new Person4("lisi1", 21));
		ts.add(new Person4("lisi3", 23));
		ts.add(new Person4("lisi4", 24));
		ts.add(new Person4("lisi2", 22));
		ts.add(new Person4("lisi6", 26));
		//System.out.println(ts);
		Iterator<Person4> it = ts.iterator();
		while(it.hasNext()){
			Person4 p = it.next();
			System.out.println(p.getName()+"---"+p.getAge());
		}
	}
}
//自定义一个比较器(按照name排序)
class CompareByName1 implements Comparator<Person4>{
	@Override
	public int compare(Person4 p1, Person4 p2) {
		int num = p1.getName().compareTo(p2.getName());
		return num==0?p1.getAge()-p2.getAge():num;
	}
	
}
//默认的比较器(按照年龄排序)
class Person4 implements Comparable<Person4>{
	private String name;
	private int age;
	public Person4(String name, int age) {
		this.name = name;
		this.age = age;
	}
	public String getName() {
		return name;
	}
	public int getAge() {
		return age;
	}
	@Override
	public String toString() {
		return name+"----"+age;
	}
	//复写equals()方法
	//注意:这个地方是不能使用泛型的。
    public boolean equals(Object obj){
    	if(this==obj)
    		return true;
    	if(!(obj instanceof Person4))
    		return false;
    	Person4 p = (Person4)obj;
    	return this.name.equals(p.name) && this.age==p.age;
    }
	//复写hashCode()方法
    public int hashCode() {
    	final int NUM = 38;
    	return name.hashCode() + age*NUM;
    };
	@Override
	public int compareTo(Person4 p) {
		int temp = this.age - p.age;
		return temp==0?this.name.compareTo(p.name):temp;
	}
}

什么时候使用泛型类呢?
      当类要操作的引用数据类型不确定的时候,可以使用泛型来定义,也就是定义一个类型参数。具体要操作什么类型的对象,有使用该类的使用者来明确,将具体的类型做为实际参数传递给<>.
例子4,泛型类的使用实例演示。
package cn.itheima.day12;
class Student{
	private String name;
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public Student(String name) {
		super();
		this.name = name;
	}
	
}
//使用泛型定义一个工具类
//使用泛型解决安全隐患
//将泛型定义到类上,在该类中都有效。
class Tool<T>{
	private T t;
	public void setT(T t){
		this.t = t;
	}
	public T getT(){
		return t;
	}
}
public class GenericDemo3 {
	public static void main(String[] args) {
		Tool<Student> t = new Tool<Student>();
		t.setT(new Student());
		Student s = t.getT();
	}
}
当泛型定义在类上,该泛型作用于整个类。当该建立对象时,就明确了具体类型。那么凡是使用了类上定义的泛型的方法,操作的类也就固定了。
例子5:希望类中的方法,可以操作任意类型而不受类中泛型限制。
             解决方法:可以将泛型定义在方法上。也就是泛型方法。
package cn.itheima.day12;
class Tool1<T>{
	public void show(T t){
		System.out.println("show:"+t);
	}
	public <Q> void print(Q q){
		System.out.println("print:"+q);
	}
	//当类中定义静态方法时,静态方法时不可以访问类上的方法的
	//因为类上的泛型只有建立对象才可以明确具体的类型。所以
	//静态方法如果操作的引用数据类型不确定,只能讲泛型定义到方法上。
	public static <W> void method(W w){
		System.out.println("method:"+w);
	}
}
public class GenericDemo4 {
	public static void main(String[] args) {
         Tool1<String> t = new Tool1<String>();
         t.show("haha");
         t.print(new Integer(1));
         t.method("asc");
         t.method(2);
	}
}
当类中定义static方法时,静态方法是不可以直接方位类上的泛型,因为类上的泛型只有通过建立对象才可以明确具体类型。 所以静态方法如果操作的引用数据类型不确定,只能将泛型定义在方法上。
在静态方法上定义泛型,必须定义在static关键字之后。
例如:public static <W> void method(W w){
                  System.out.println("method:"+w);
          }
当方法中操作的应用数据类型不确定,而且和对应的对象执行的类型也不一定一致。这时就将泛型定义在方法上。
例子6,泛型接口实例。
package cn.itheima.day12;
/**
 * 定义泛型接口。
 * @author wl-pc
 */
interface Inter<T>{
	public void show(T t);
}
class InterImpl<T> implements Inter<T>{
	@Override
	public void show(T t) {
		System.out.println("t:"+t);
	}
}
public class GenericDemo5 {
	public static void main(String[] args) {
	      Inter<String> t = new InterImpl<String>();
	      t.show("hehe");
	}
}
例子7 泛型:通配符:?实例演示。
package cn.itheima.day12;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
/**
 * 泛型:通配符。?  代表任意类型。
 * @author wl-pc
 * Collection :
	boolean containsAll(Collection<?> c):判断一个集合中是否包含另一个集合中的元素。
	//这时不用boolean containsAll(Collection<T> c),因为这里要用T了,
	//则下面的也要用T,这时就不能判断不同类型的集合了。所以不确定类型时用?号声明。
	//如果用T就声明了一个具体类型的变量。
	//containsAll底层用的方法就是equals。 "ABC".equals(new Integer(3)); -->return false;
	class MyCollection<T>
	{
		boolean containsAll(Collection<?> c)
		{	
		}
	}
 */
public class GenericDemo6 {
	public static void main(String[] args) {
         ArrayList<String> al = new ArrayList<String>();
         al.add("abc1");
         al.add("abc2");
         al.add("abc3");
        HashSet<Integer> hs = new HashSet<Integer>();
         hs.add(1);
         hs.add(3);
         hs.add(4);
         show(al);
         show(hs);
	}
	public static void show(Collection<?> coll){
		Iterator<?> it = coll.iterator();
		while(it.hasNext()){
			System.out.println(it.next());
		}
	}
}
传入的集合有一个特点,就是元素类型,要么是Person,要么是Person的子类型。这时可以使用泛型的高级应用,泛型的限定。
在泛型方法中,不可以使用具体类型的方法,最多只能使用Object类中方法。
定义T只能固定一种类型,定义?可以是任意类型。若只想操作Person或者Person的子类类型。使用泛型的限定。? extends E:接收E类型或者E的子类型。
       泛型的限定            ? extends E:接收E类型或者E的子类型。
            ? super E:接收E类型或者E的父类型。
例子8. 若只想操作Person或者Person的子类类型。使用泛型的限定。
package cn.itheima.day12;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
class Person5{
   private String name;
   Person5(String name){
	   this.name = name;
   }
   public String getName(){
	   return name;
   }
}
class Student2 extends Person5{
	public Student2(String name) {
        super(name);
	}
}
public class GenericDemo7 {
	public static void main(String[] args){
		 ArrayList<Person5> al =new ArrayList<Person5>();
		 al.add(new Person5("p1"));
		 al.add(new Person5("p2"));
		 al.add(new Person5("p3")); 
		//定义集合要保证左右两边的类型一致。
	//两边只有一边定义泛型,也是可以支持,至少新老版本兼容。但是一样会出现,安全提示信息。
		 ArrayList<Student2> als = new ArrayList<Student2>();
		 als.add(new Student2("s1"));
		 als.add(new Student2("s2"));
		 als.add(new Student2("s3"));
		 
		 show(al);
		 show(als);
	}
	
	
	/**
	 * 传入的集合有一个特点,就是元素类型,要么是Person,要么是Person的子类型。
		这时可以使用泛型的高级应用,泛型的限定。
	在泛型方法中,不可以使用具体类型的方法比如:getName(),最多只能使用Object类中方法。
定义T只能固定一种类型,定义?可以是任意类型。若只想操作Person或者Person的子类类型。
		使用泛型的限定。? extends E:接收E类型或者E的子类型。
		泛型的限定:
		? extends E:接收E类型或者E的子类型。
		? super E:接收E类型或者E的父类型。
         比较时,只要是当前的类型或者当前类型的父类型,就用? super E
         当往里面存入对象时,接收的是指定的类型或者这个类型的子类型,就用? extends E
	 * @param coll
	 */
	//使用泛型定义show()方法,显示集合信息
	public static void show(Collection<? extends Person5> coll){
		Iterator<? extends Person5> it = coll.iterator();
		while(it.hasNext()){
			System.out.println(it.next().getName());
		}
	}
}
例子9:演示 “? super E”(接收E类型或者E的父类型)用法。
package cn.itheima.day12;
import java.util.Comparator;
import java.util.TreeSet;
public class GenericDemo08 {
	public static void main(String[] args) {
		//TreeSet(Comparator<? super E> comparator) 
		TreeSet<Student1> ts = new TreeSet<Student1>(new CompareByName2());
		ts.add(new Student1("s1"));
		ts.add(new Student1("s2"));
		ts.add(new Student1("s3"));
		System.out.println(ts);
		TreeSet<Worker> ts1 = new TreeSet<Worker>(new CompareByName2());
		ts1.add(new Worker("w1"));
		ts1.add(new Worker("w2"));
		ts1.add(new Worker("w3"));
		System.out.println(ts1);
	}
}
//? super E//只要能接收集合中的元素对象即可所以Student可以 Person也可以。
class CompareByName2 implements Comparator<Person6>{
	@Override
	public int compare(Person6 p1, Person6 p2) {
		return p1.getName().compareTo(p2.getName());
	}
}
class Person6{
	private String name;
    Person6(String name) {
		this.name = name;
	}
    public String getName(){
    	return name;
    }
    public String toString(){
    	return "name:"+name;
    }
}
class Worker extends Person6{
	Worker(String name) {
		super(name);
	}
}
class Student1 extends Person6{
	Student1(String name){
		super(name);
	}
}
例子10:演示addAll()的用法.
package cn.itheima.day12;
import java.util.ArrayList;
import java.util.Iterator;
public class GenericDemo09 {
	public static void main(String[] args) {
         ArrayList<Person7> al  =new ArrayList<Person7>();
		 al.add(new Person7("p1"));
		 al.add(new Person7("p2"));
		 System.out.println(al);
		 ArrayList<Person7> al1  =new ArrayList<Person7>();
		 al1.add(new Person7("p3"));
		 al1.add(new Person7("p4"));
		 System.out.println(al1);
		 al.addAll(al1);
		 System.out.println(al);
		 ArrayList<Student3> al2 = new ArrayList<Student3>();
		 al2.add(new Student3("ss1"));
		 al2.add(new Student3("ss2"));
		 al2.add(new Student3("ss3"));
		 al.addAll(al2);   //父类的引用指向子类的对象。
		 System.out.println(al);
		 Iterator<Person7> it = al.iterator();
		 while(it.hasNext()){
			 Person7 p = it.next();
			 System.out.println(p.getName());
		 }
	}
}
class Person7{
	private String name;
    Person7(String name) {
		this.name = name;
	}
    public String getName(){
    	return name;
    }
    public String toString(){
    	return "name:"+name;
    }
}
class Worker1 extends Person7{
	Worker1(String name) {
		super(name);
	}
}
class Student3 extends Person7{
	Student3(String name){
		super(name);
	}
}

集合框架中的另一个顶层接口Map
Map集合特点    1,Map是一个双列集合,Collection是单列集合。
    2,Map一次存一对元素,同时键值对的形式。键和值有对应关系。Collection是一次存一个元素。
    3,Map集合必须要保证集合中键的唯一性。
    比方:Collection 是单身汉,Map集合存的是一对夫妻。
Map集合中常见的功能    1,添加       v  put(k,v):将k和v作为元素存储如map集合,当存入了相同的k时,新的值会覆盖原来的值,并返回原来的值。
       void putAll(map);
    2,删除。
       clear();清除。
       vremove(k):按照键删除,返回被删除的键对应的值。
    3,判断       booleancontainsKey(Object key) 
       booleancontainsValue(Object value);
       booleanisEmpty();
    4,获取       intsize():获取map集合的元素个数。
       vget(k): 通过键获取值。还可以作为判断某一个键是否存在的依据。
       Collectionvalues():获取map集合中所有的值。
       SetkeySet():获取map集合中所有的键。
       SetentrySet():获取的是键值的映射的关系。将映射关系封装成对象存入到了Set集合。
Map集合居然没有迭代器。是的。迭代器是Collection集合具备的。Map集合的取出元素的原理:就是将Map集合先转成Set集合,再进行迭代。
Map
    |--Hashtable:底层是哈希表数据结构。是同步的。不允许null作为键,null作为值。
          |--Properties:用于配置文件的定义和操作,使用频率非常高,同时键和值都是字符串。是集合中可以和IO技术相结合的对象,到了IO在学习它的特有和io相关的功能。
    |--HashMap:底层也是哈希表数据结构。是不同步的。允许null作为键,null作为值。替代了Hashtable.
           |--LinkedHashMap:可以保证HashMap集合有序。存入的顺序和取出的顺序一致。
    |--TreeMap:可以用来对Map集合中的键进行排序.
例子11.演示Map集合中常见的功能。
package cn.itheima.day13;
import java.util.HashMap;
public class HashMapDemo {
	public static void main(String[] args) {
		//存储学号和姓名(注意:泛型中只能传引用数据类型,不能传基本数据类型)
		HashMap<Integer, String> hm = new HashMap<Integer, String>();
		//添加元素
		hm.put(1, "wangwu");
		hm.put(2, "lisi");
		hm.put(3, "zhangsan");
		hm.put(4, "zhaoliou");
		System.out.println(hm.size());
		System.out.println(hm);
		//获取
		System.out.println(hm.get(2));
		//删除
		//System.out.println(hm.remove(3));
		//清除
		//hm.clear();
		System.out.println(hm.containsKey(2));
	}
}
如何获取集合中所有元素呢?
1,获取所有的键,在通过对所有的键进行遍历,在遍历中通过get方法获取每一个键的对应的值。使用 Map集合中 keySet方法:获取Map集合中键的集合。
       Set<Integer>keySet =  hm.keySet();
       //对set集合进行迭代器。
       Iterator<Integer>it = keySet.iterator();
       while(it.hasNext()){
           Integeri = it.next();
           Strings = hm.get(i);
           System.out.println(i+"::"+s);
       }
2,将map集合中的键值关系取出,并封装成一个键值关系对象。再存储到一个set集合中。键值映射关系封装对象后的数据类型是:Map.Entry。Entry无非就是一个Map接口中的内部静态接口。作为一个键值关系对象的数据类型存在。使用entrySet获取map集合中的所有键值。
    Set<Map.Entry<Integer, String>> entrySet = hm.entrySet();
     Iterator<Map.Entry<Integer,String>> it1 = entrySet.iterator();
       while(it1.hasNext()){
           Map.Entry<Integer, String> me = it1.next();
           Integer key = me.getKey();
           String name = me.getValue();
           System.out.println(key+"----"+name);
       }
例子12:Map集合的两种取出方式实例代码。
package cn.itheima.day13;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
public class HashMapDemo2 {
	public static void main(String[] args) {
		//存储学号和姓名(注意:泛型中只能传引用数据类型,不能传基本数据类型)
		HashMap<Integer, String> hm = new HashMap<Integer, String>();
		//添加元素
		hm.put(1, "wangwu");
		hm.put(2, "lisi");
		hm.put(3, "zhangsan");
		hm.put(4, "zhaoliou");
		hm.put(5, "yangyan");
		hm.put(6, "zhouqi");
		//获取Map集合中的所有的元素,方法一:
		Set<Integer> keySet = hm.keySet();
		Iterator<Integer> it = keySet.iterator();
		while(it.hasNext()){
			Integer key = it.next();
			String name = hm.get(key);
			System.out.println(key+"------"+name);
		}
		//获取Map集合中的所有的元素,方法二:
		Set<Map.Entry<Integer, String>> entrySet = hm.entrySet();
		Iterator<Map.Entry<Integer, String>> it1 = entrySet.iterator();
		while(it1.hasNext()){
			Map.Entry<Integer, String> me = it1.next();
			Integer key = me.getKey();
			String name = me.getValue();
			System.out.println(key+"----"+name);
		}
	}
}<pre><span style="font-family:Microsoft YaHei;">例子13. 每一个学生都有自己的归属地。学生有姓名和年龄。将学生封装成student对象。因为学生对象和归属地有对应关系。所以用map集合存储该关系。键:Student。值:String同姓名同年龄的学生视为同一个人。</span>
package cn.itheima.day13;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
/**
    练习:每一个学生都有自己的归属地。
		学生有姓名和年龄。
		将学生封装成student对象。
		因为学生对象和归属地有对应关系。
		所以用map集合存储该关系。
		键:Student。
		值:String
		同姓名同年龄的学生视为同一个人。
 * @author wl-pc
 */
public class HashMapTest {
	public static void main(String[] args) {
         HashMap<Student, String> hm = new HashMap<Student, String>();
         //想要保证有序,即存入的元素和取出的元素的顺序一致。
         //这时可以使用一个在hash表中加入链表结构的对象,叫做LinkHashMap.
         //该对象是hashMap的子类。
         hm = new LinkedHashMap<Student, String>();
         hm.put(new Student("lisi1", 21), "上海");
         hm.put(new Student("lisi6", 26), "河南");
         hm.put(new Student("lisi4", 24), "北京");
         hm.put(new Student("lisi1", 21), "上海");
         hm.put(new Student("lisi7", 27), "广州");
         hm.put(new Student("lisi4", 24), "北京");
         hm.put(new Student("lisi3", 23), "香港");
         Set<Map.Entry<Student, String>> entrySet = hm.entrySet();
         Iterator<Map.Entry<Student, String>> it = entrySet.iterator();
         while(it.hasNext()){
        	 Map.Entry<Student, String> me = it.next();
        	 Student stu = me.getKey();
        	 String address = me.getValue();
        	 System.out.println(stu.getName()+"::"+stu.getAge()+"----"+address);
         }
	}
}
/**
 *  要保证学生对象的唯一性。需要建立学生对象自身的判断相同的依据。
	而且要根据学生的判断条件来定义依据。
	因为是存放了Hash表中,所以要覆盖hashCode方法,和equals方法。
 * @author wl-pc
*/
class Student{
	private String name;
	private int age;
	Student(String name,int age){
		this.name = name;
		this.age = age;
	}
	//覆盖hashCode()方法
	@Override
	public int hashCode() {
		final int NUM = 23;
		return name.hashCode() + age*NUM;
	}
	//覆盖equals()方法
	@Override
	public boolean equals(Object obj) {
		if(this==obj)
			return true;
		if(!(obj instanceof Student))
			return false;
		Student stu = (Student)obj;
		//判断同姓名和同年龄的是同一个student
		return this.name.equals(stu.name) && this.age == stu.age;
	}
	public String getName(){
		return name;
	}
	public int getAge(){
		return age;
	}
}

例子14, 利用TreeMap集合对学生的年龄进行排序获取。
package cn.itheima.day13;
import java.util.Collection;
import java.util.Comparator;
import java.util.Iterator;
import java.util.Set;
import java.util.TreeMap;
/**
   需求:对学生的年龄进行排序获取。
 * @author wl-pc
 */
public class TreeMapDemo {
	public static void main(String[] args) {
         TreeMap<Student1, String> hm = new TreeMap<Student1, String>(new CompareByName());
         hm.put(new Student1("lisi2", 22), "上海");
         hm.put(new Student1("lisi3", 23), "香港");
         hm.put(new Student1("lisi6", 26), "河南");
         hm.put(new Student1("lisi7", 27), "广州");
         hm.put(new Student1("lisi4", 24), "北京");
        Set<Student1> keySet = hm.keySet();
        Iterator<Student1> it = keySet.iterator();
        while(it.hasNext()){
        	Student1 stu = it.next();
        	String address = hm.get(stu);
        	System.out.println(stu.getName()+"::"+stu.getAge()+"---"+address);
        }
        //获取所有的学生的归属地
        Collection<String> coll = hm.values();
        Iterator<String> it1 = coll.iterator();
        while(it1.hasNext()){
        	String address = it1.next();
        	System.out.println("address::"+address);
        }
	}
}
//定义集合比较器
class CompareByName implements Comparator<Student1>{
	@Override
	public int compare(Student1 s1, Student1 s2) {
		int temp = s1.getName().compareTo(s2.getName());
		return temp==0?new Integer(s1.getAge()).compareTo(new Integer(s2.getAge())):temp;
	}
}
/**
 *  要保证学生对象的唯一性。需要建立学生对象自身的判断相同的依据。
	而且要根据学生的判断条件来定义依据。
	因为是存放了Hash表中,所以要覆盖hashCode方法,和equals方法。
 * @author wl-pc
*/
class Student1 implements Comparable<Student1>{
	private String name;
	private int age;
	Student1(String name,int age){
		this.name = name;
		this.age = age;
	}
	//复写元素自身比较方法
	@Override
	public int compareTo(Student1 stu) {
		int num = this.age -stu.age;
		return num==0?this.name.compareTo(stu.name):num;
	}
	//覆盖hashCode()方法
	@Override
	public int hashCode() {
		final int NUM = 23;
		return name.hashCode() + age*NUM;
	}
	//覆盖equals()方法
	@Override
	public boolean equals(Object obj) {
		if(this==obj)
			return true;
		if(!(obj instanceof Student))
			return false;
		Student1 stu = (Student1)obj;
		//判断同姓名和同年龄的是同一个student
		return this.name.equals(stu.name) && this.age == stu.age;
	}
	public String getName(){
		return name;
	}
	public int getAge(){
		return age;
	}
}
例子15:."cbxzbvavdvgd"获取字符串中,每一个字母出现的次数。要求结果是:a(1)b(2)c(1)d(2)g(1)v(3)x(1)z(1)
当分析问题时,发现对象间存在映射关系,这时应该第一个想到用map集合存储。分析该练习发现。每一个字母和自己的出现的次数都有对应,而且最终结果,字母都是唯一的。那么这时就想到了map集合。因为从结果中看到打印的字母都有顺序。所以可以使用map集合中treemap集合。
思路:
       1,将字符串变成字符数组,因为要操作字符串中的每一个字母。
       2,遍历数组,将每一个字母都作为键去map集合获取值。
       3,如果获取的值为null。说明该键不存在,就将该键和1存入到集合中。如果该值不为null,说明该键已经存在于map集合,并有对应的值(次数)。那么就将该值取出,并自增后,再将该键和新的值存入到map集合。因为键相同,那么新值会覆盖旧值。
       4,遍历结束map集合中就已经具备了每一个字母对应的次数。
       5,将map集合中的数据变成字符串打印。
package cn.itheima.day13;
import java.util.Iterator;
import java.util.TreeMap;
/**
 * 需求:"cbxzbvavdvgd"获取字符串中,每一个字母出现的次数。
                 要求结果是:a(1)b(2)c(1)d(2)g(1)v(3)x(1)z(1)
 * @author wl-pc
 */
public class MapTest {
	public static void main(String[] args) {
		String str= "c+b-xz+bv-avdv*gd";
		charCount(str);
	}
	public static void charCount(String str){
		TreeMap<Character, Integer> tm = new TreeMap<Character, Integer>();
		//将字符串变成字符数组
		char[] chs = str.toCharArray();
		//遍历数组
		//因为要对每一个字母进行计数,可以定义一个变量
		int count = 0;
		for(int x=0;x<chs.length;x++){
			if((chs[x]>='a' && chs[x]<='z' || chs[x]>='A' && chs[x] <='Z')){
				//将字母作为键去查map集合,获取对应的值。
				Integer i = tm.get(chs[x]);
				if(i!=null){
					count = i;
				}
				count++;
				tm.put(chs[x], count);
				count = 0;
			}
		}
		System.out.println(mapToString(tm));
	}
	//将map集合变成字符串
	private static String mapToString(TreeMap<Character, Integer> tm){
		StringBuilder sb = new StringBuilder();
		Iterator<Character> it = tm.keySet().iterator();
		while(it.hasNext()){
			Character key = it.next();
			Integer value = tm.get(key);
			sb.append(key+"("+value+")");
		}
		return sb.toString();
	}
}

例子35:描述,heima
                 |--Andriod
                     |--01  lisi
                     |--02  wangwu
                 |--IOS
                     |--01  zhaoliou
                     |--02  zhouqi

package cn.itheima.day13;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;
/**
 * 需求:heima
 *        Andriod    IOS
 *           Andriod:
 *              01  lisi
 *              02  wangwu
 *           IOS:
 *              01  zhaoliou
 *              02  zhouqi
 * @author wl-pc*
 */
public class MapDemo {
	public static void main(String[] args) {
		//集合中嵌套集合
        //heima的集合的键是String类型的,而值是一个HashMap集合类型
		HashMap<String,HashMap<String, String> > heima = new HashMap<String,HashMap<String,String>>();
		//而andriod和ios都是一个HashMap集合,它们的键是String类型的,而值也是一个String类型的。
		HashMap<String, String> andriod = new HashMap<String, String>();
		HashMap<String, String> ios = new HashMap<String, String>();
		heima.put("heima", andriod);
		heima.put("ios", ios);
		andriod.put("01","lisi");
		andriod.put("02", "wangwu");
		ios.put("01", "zhaoliou");
		ios.put("02", "zhouqi");
		//循环外层大的HashMap集合
		Set<String> keySet = heima.keySet();
		Iterator<String> it = keySet.iterator();
		while(it.hasNext()){
			String key = it.next();
			HashMap<String, String> hm = heima.get(key);
			System.out.println(hm.toString());
			show(hm);
		}
   	}
	//循环大集合中的小集合
	private static void show(HashMap<String, String> hm) {
		Set<String> keySet = hm.keySet();
		Iterator<String> it1 = keySet.iterator();
		while(it1.hasNext()){
			String num = it1.next();
			String name = hm.get(num);
			System.out.println(num+"::"+name);
		}
	}
}
例子16:将学员的信息封装成Student类,然后一一的获取学生信息。
package cn.itheima.day13;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;
/**
 * 需求:heima
 *        Andriod    IOS
 *           Andriod:
 *              01  lisi
 *              02  wangwu
 *           IOS:
 *              01  zhaoliou
 *              02  zhouqi
 * @author wl-pc
 */
public class MapDemo2 {
	public static void main(String[] args) {
		//集合中嵌套集合
        //heima的集合的键是String类型的,而值是一个HashMap集合类型
		HashMap<String,ArrayList<Student3>> heima = new HashMap<String,ArrayList<Student3>>();
		//而andriod和ios都是一个HashMap集合,它们的键是String类型的,而值也是一个String类型的。
		ArrayList<Student3> andriod = new ArrayList<Student3>();
		ArrayList<Student3> ios = new ArrayList<Student3>();
		heima.put("heima", andriod);
		heima.put("ios", ios);
		andriod.add(new Student3("01", "lisi"));
		andriod.add(new Student3("02", "wangwu"));
		ios.add(new Student3("01", "zhaoliou"));
		ios.add(new Student3("02", "zhouqi"));
		//循环外层大的HashMap集合
		Set<String> keySet = heima.keySet();
		Iterator<String> it = keySet.iterator();
		while(it.hasNext()){
			String className = it.next();
			ArrayList<Student3> hm = heima.get(className);
			System.out.println(hm.toString());
			show(hm);
		}
   	}
	//循环大集合中的小集合
	private static void show(ArrayList<Student3> al) {
		Iterator<Student3> it1 = al.iterator();
		while(it1.hasNext()){
			Student3 stu = it1.next();
			System.out.println(stu.getNum()+"::"+stu.getName());
		}
	}
}
class Student3{
	private String num;
	private String name;
	public Student3(String num,String name) {
		this.num = num;
		this.name = name;
	}
	public String getNum(){
		return num;
	}
	public String getName(){
		return name;
	}
}
例子17:有一堆元素。需要唯一不?不需要。选择List集合。需要增删吗?不需要,选择ArrayList。但是想要让元素按指定顺序排序。这时就可以考虑使用集合框架的工具类来完成,看看工具类中是否有方法可以完成搞定。
工具类有两个。
    Collections
    Arrays
 这两个工具类的特点:类中的方法都是静态的。不需要创建对象,直接使用类名调用即可。
     Collections:是集合对象的工具类。提供了操作集合的工具方法。
     Arrays:是数组的工具类,提供了对数组的工具方法。
     Collections:
         List synchronizedList(List):可以将一个不同步的list集合转成一个同步的list集合。
         XXX synchroizedXXX(XXX):将非同步的集合变成同步集合的方法。(XXXX可以是Set,还可以是Map)
ArrayList和Vertor有什么区别?
      一个安全,一个不安全。一个效率高,一个效率低。一般用ArrayList替换了Vertor。
当用多线程访问ArrayList时,怎么解决安全问题?
     1.可以自己加锁。
     2.集合框架工具类当中的Collections中的一个synchronizedList(list)方法,将非同步集合转成同步集合。
集合面试题最常见:
Collection和Collections的区别。
例子17:Collections集合对象的方法演示。
package cn.itheima.day13;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.TreeSet;
/**
 * 需求:有一堆元素,想要让元素按指定顺序排序。
 * @author wl-pc
 */
public class CollectionsDemo {
	public static void main(String[] args) {
		//method_1();
		//method_2();
		//method_3();
		//method_4();
		//method_5();
		method_6();
	}
	//演示其他方法
	private static void method_6() {
		ArrayList<String> al = new ArrayList<String>();
		al.add("abcd");
		al.add("cba");
		al.add("aaaa");
		al.add("zz");
		al.add("a");
		//Collections.fill(al, "AA");  //将集合中的元素替换成指定的元素AA
		//Collections.reverse(al);  //将集合中的元素反转,头尾对调
		//Collections.swap(al, 1, 3); //对集合al中的数据元素进行位置置换
		//Collections.shuffle(al);  //对集合中的元素进行随机的位置置换。
		Collections.replaceAll(al, "cba", "heima");
		System.out.println(al);
	}
	private static void method_5() {
		ArrayList<String> al = new ArrayList<String>();
		al.add("abcd");
		al.add("cba");
		al.add("aaaa");
		al.add("zz");
		al.add("cba");
		al.add("nba");
		//因为二分查找必须是对有序的集合,也就是必须要有角标的集合。
		Collections.sort(al);
		int index = Collections.binarySearch(al, "cba");
		System.out.println("index="+index);
		int index1 = halfSearch(al,"cba");
		System.out.println("index1="+index1);
	}
	//演示list集合的二分查找的原理
	private static int halfSearch(List<String> list, String key) {
		int max,min,mid;
		max = list.size()-1;
		min = 0;
		while(min<max){
			mid = (max+min)>>1;
		    String temp = list.get(mid);
		    int num = key.compareTo(temp);
		    if(num>0)  //代表key > temp,这时候小的角标的地方要变化
		    	min = mid+1;
		    else if (num<0)  //代表key < temp,这时候大的角标的地方要变化
				max = mid-1;
		    else 
				return mid;
		}
		return -min-1;
	}
	//Collections.max(); //按照自然顺序获取元素其中的最大值
	private static void method_4() {
		ArrayList<String> al = new ArrayList<String>();
		al.add("abcd");
		al.add("cba");
		al.add("aaaa");
		al.add("zz");
		al.add("cba");
		al.add("nba");
		//String max = Collections.max(al);  //按照自然顺序获取其中的最大值
		//按照指定的比较器来获取其中的最大值
		String max= Collections.max(al, new CompareByLen());
		System.out.println("max="+max);
	}
	//自定义的得到元素的最大值的方法分析,泛型限定
	/*public static <T extends Object &  Comparable<? super T>> T getMax(Collection<? extends T> al)
	{
	}*/
	//反转元素:Collections.reverseOrder();
	private static void method_3() {
		TreeSet<String> ts = new TreeSet<String>(Collections.reverseOrder());
		ts.add("cba");
		ts.add("aaaa");
		ts.add("zz");
		ts.add("a");
		ts.add("nba");
		System.out.println(ts);	
	}
	//演示:Collections.reverseOrder(Comparator com);逆转顺序的比较器
	private static void method_2() {
		ArrayList<String> al = new ArrayList<String>();
		al.add("abcd");
		al.add("cba");
		al.add("aaaa");
		al.add("zz");
		al.add("cba");
		al.add("nba");
		//在排序时,传递一个可以逆转顺序的比较器即可,reverseOrder();
		//Collections.reverseOrder(al,Collections.reverseOrder());
		//按照长度排序,想要让长度从长到短排序,只要让比较器逆转即可。
		Collections.sort(al,Collections.reverseOrder(new CompareByLen()));
		Iterator<String> it = al.iterator();
		while(it.hasNext()){
			System.out.println(it.next());
		}
	}
	//演示:Collections.sort();集合排序
	private static void method_1() {
		ArrayList<String> al = new ArrayList<String>();
		al.add("abcd");
		al.add("cba");
		al.add("aaaa");
		al.add("zz");
		al.add("cba");
		al.add("nba");
		//Collections.sort();可以对List集合(只能对List集合)进行按照元素的自然排序给元素排序
		//Collections.sort(al);
		Collections.sort(al,new CompareByLen());
		Iterator<String> it = al.iterator();
		while(it.hasNext()){
			System.out.println(it.next());
		}
	}
}
//定义集合比较器实现按照元素的长度进行比较
class CompareByLen implements Comparator<String>{
	@Override
	public int compare(String s1, String s2) {
		int num = s1.length()-s2.length();
		return num==0?s1.compareTo(s2):num;
	}
}
例子18:Arrays集合的数组变集合的方法实例演示。
package cn.itheima.day13;
import java.util.Arrays;
import java.util.List;
/**
 * Arrays工具类用来操作数组。
 * @author wl-pc
 */
public class ArraysDemo {
	public static void main(String[] args) {
		//Arrays中有一个重要的方法,将数组变为集合
		String[] arr = {"heima","csdn","itcast"};
		List<String> list = java.util.Arrays.asList(arr);   //将数组变为集合
	        //数组变为集合后,就可以使用集合的方法来操作数组,而不用自己在定义指针进行数组的操作。
		//但是有些方法是不可以使用的,例如:只要改变集合的长度的方
		//法都是不可以使用的,因为数组是固定长度的。
		boolean b = list.contains("heima");
		System.out.println("b="+b);
		//list.add("wangling");  //数组长度是不可以改变的(也就是不能增删),所以有不支持UnsupportedOperationException异常出现
		//System.out.println(list);
		/*int[] arr1 = {2,4,3,8,7,9};
		int index = Arrays.binarySearch(arr1,4);  //折半查找
		System.out.println("index="+index);*/
		mothod();
	}
	private static void mothod() {
		int[] arr2 ={3,4,6};   
                //当数组中的元素时引用数据类型时,变为集合后,就将数组中的元素作为集合中的元素存在。
                //当数组中的元素时基本数据类型时,变为集合后,会将数组变成集合中的元素。
		List<int[]> list1 = Arrays.asList(arr2);
		System.out.println(list1);
		System.out.println(Arrays.toString(arr2));  //将数组直接变成字符串。
	}
}
例子19:集合变数组的实例演示。
package cn.itheima.day13;
import java.util.ArrayList;
import java.util.Arrays;
/**
 * 集合变为数组。
 * 用的是Collection接口中的一个toArray()方法完成的。
 * @author wl-pc
 * 将集合变成数组有什么用呢?
	     其实是限定了对元素的增删操作。
 */
public class CollectionToArray {
	public static void main(String[] args) {
		ArrayList<String> al = new ArrayList<String>();
		al.add("heima");
		al.add("csdn");
		al.add("itcast");
		String[] arr = al.toArray(new String[al.size()]);  //将集合变为数组
		/**
		 * 	给toArray方法传递指定类型的数组。
			长度该怎么定义呢?
			当指定的长度小于集合的长度,
		该方法内部会自动创建一个该类型的新数组长度和集合长度一致 。用于存储集合中的元素。
	
			如果指定的数组长度大于集合的长度,
			那么该方法就不会创建新数组。而是使用传递进来的数组,
			存储完集合的元素后,其他的未存储的位置为null。
	
			所以在定义数组是,最好定义长度和集合长度相同的数组。这样就不用创建新数组了。
			而且也不会出现空位为null 。
	
			将集合变成数组有什么用呢?
			其实是限定了对元素的增删操作。
		 */
		System.out.println(Arrays.toString(arr));
	}
}
例子20.JDK1.5后的新特性,增强for循环。
package cn.itheima.day13;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
/**
 * JDK1.5之后的新特性。
 * @author wl-pc
 * Collection就有一个父接口Iterator
 * 该接口的出现封装了iterator方法,并提供了一个增强for循环的方法。
 * 格式:
 * for( 元素类型  变量  :  数组或者Collection集合){
 * }
 * 增强for循环与普通循环有什么不同?
 *     增强for循环不能定义变量,而普通for循环可以。
 *     增强for循环使用时必须要有被比遍历的目标。
 *     而且只能遍历数组和Collection集合,简化了迭代。
 *     传统for循环更为普遍。
 *  建议遍历数组使用传统的for循环,如果只为遍历可以用增强for循环。   
*/
public class ForeachDemo {
	public static void main(String[] args) {
		ArrayList<String> al = new ArrayList<String>();
		al.add("abcd");
		al.add("cba");
		al.add("aaaa");
		al.add("zz");
		al.add("a");
		//注意:用foreach循环只能是遍历元素的内容,不能对元素做改变的操作
		//如果要对遍历的集合做出操作,就用迭代器,itertor可以操作元素
		for (String list : al) {
			System.out.println(list);
		}
		//迭代器具备在迭代过程中对元素操作,比如:remove()
		//通过ListItertor具备在迭代过程中队元素进行增删改查。
		/*Iterator<String> it = al.iterator();
		while(it.hasNext()){
			System.out.println(it.next());
		}*/
		int[] arr1 = {3,2,4,5,7};
		for (int i : arr1) {
			System.out.println("i="+i);
		}
		//要对Map集合进行遍历,可以使用foreach循环吗?答:不可以直接使用,可以间接使用
		//因为Map集合不是Collection集合的子接口,也就没有itertor迭代器,
		//所以不能直接使用,只能把Map集合变为Set集合,在使用foreach循环。
		HashMap<String, String> hm = new HashMap<String, String>();
		hm.put("01", "lisi");
		hm.put("02", "zhangsan");
		hm.put("03", "wangwu");
		hm.put("04", "zhaoliou");
		Set<Map.Entry<String, String>> entrySet = hm.entrySet();
		Iterator<Map.Entry<String, String>> it = entrySet.iterator();
		/*while(it.hasNext()){
			Map.Entry<String, String> key = it.next();
			String num =key.getKey();
			String name = key.getValue();
			System.out.println(num+"::"+name);
		}*/
		for (Map.Entry<String, String> entry : entrySet) {
			System.out.println(entry.getKey()+"::"+entry.getValue());
		}
	}
}
当一个函数操作的参数类型一致,但是参数个数不一致的时候,可以定义成该类型的数组参数。这样,使用者,就可以把元素封装成一个数组。在进行传递即可。我们发现,还要new一个数组,也麻烦。所以jdk1.5以后出现了新特性。
    就是可变参数,在指定数据类型的后面加上三个点 ,其实就是一个数组类型的参数,
    定义数组和定义一个可变参数,有哪里不一样了呢?
    以前定义一个int[] 类型的参数,调用必须要定义好一个数组,才往里传递。而现在定义一个int...  类型的参数,调用者,直接往该函数中传递元素即可。在运行时,自动会将这些实际参数封装到一个该类型的数组当中。也就是调用省去了封装数组的步骤,变简单了。这个升级也是简化书写。
注意:如果函数上有多个参数,可变参数一定要定义在参数列表最后面。否则编译失败。
例子21. Java的新特性:可变参数实例演示。
package cn.itheima.day13;
/**
 * Java的新特性:可变参数。
 * @author wl-pc
 */
public class ParamterDemo {
	public static void main(String[] args) {
		//show(5);
		//show1(5,6);
		//int[] arr = {2,5,6,8,3};
		//show3(arr);
		show4(2,4,6,7,8);
	}
	//public static void method(int... arr,int a)  //这个定义是错误的。
	//定义一个可变参数的的方法
	private static void show4(int... arr) {  //可变参数,其实arr就是一个数组类型的参数
		for (int i : arr) {
			System.out.println(i);
		}
	}
        //定义一个int型的数组来存放变量
	/*public static void show3(int[] arr){
		//System.out.println(Arrays.toString(arr));
		for (int i : arr) {
			System.out.println(i);
		}
	}*/
	/*private static void show(int x) {
		System.out.println("x="+x);
	}
	private static void show1(int x,int y) {
		System.out.println("x="+x+",y="+y);
	}*/
}
例子22:JDK.1.5 特性:静态导入实例演示。
package cn.itheima.day13;
import java.util.ArrayList;
//import java.util.Arrays;
//import java.util.Collections;
import java.util.*;
import static java.util.Collections.*;  //导入了一个类中的所有静态成员。
import static java.util.Arrays.*;
/**
 * JDK.1.5 特性:静态导入。
 * @author wl-pc
 *  注意:当方法重复时,必须制定所属的类名。
 */
public class StaticImportDemo {
	public static void main(String[] args) {
		ArrayList<Integer> al = new ArrayList<Integer>();
		al.add(1);
		al.add(3);
		al.add(5);
		al.add(7);
		al.add(9);
		//Collections.sort(al);
		sort(al);
		System.out.println(al);
		//int index = Collections.binarySearch(al, 5);
		int index = binarySearch(al, 5);
		System.out.println("index="+index);
		int[] arr = {3,5,6};
		sort(arr);
		System.out.println(Arrays.toString(arr));
	}
}





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值