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


集合是用于存储对象的一个工具。
集合和数组的特点:
  相同点       集合和数组都一个容器。
  不同点:
       集合:
           1,可以存储对象,只能存储对象。
           2,集合的长度的是可变的。
       数组:
           1,可以存储对象,也可以存储基本数据类型值。
           2,数组长度的是固定的。
注意:CollectionDemo.java 使用了未经检查或不安全的操作。
注意:要了解详细信息,请使用 -Xlint:unchecked 重新编译。
Java编译器认为该成存在安全隐患。友情提示。但并不是编译失败,所以可以不用理会。其实是因为类型的原因导致的,等到了泛型技术学完,就没有该提示了。
例子1:Collection集合中的几个常用的方法。
package cn.itheima.day11;
import java.util.ArrayList;
import java.util.Collection;
public class CollectionDemo {
	public static void main(String[] args) {
		Collection coll = new ArrayList();  //创建一个集合对象,也就是一个容器
		coll.add("abc1");
		coll.add("abc2");
		coll.add("abc3");  //添加集合元素
		//简单的获取集合中所有的元素,直接将集合元素打印到控制台上
		System.out.println("集合元素:"+coll);
		//删除集合元素
		/*boolean b = coll.remove("abc2");	
		System.out.println("b="+b);
		System.out.println("删除后集合元素:"+coll);*/
		//获取集合的长度
		//int size = coll.size();
		//System.out.println("size="+size);
		//清除集合中的所有元素
		//coll.clear();
		//System.out.println("清除集合中的所有元素:"+coll);
		//判断集合是否为空
		//boolean b = coll.isEmpty();  //其实内部依据的是size()方法,如果size()=0,就返回true
		//System.out.println("b="+b);
		//判断集合中是否包含一个元素
		boolean b = coll.contains("abc1");
		System.out.println("b="+b);
	}
}
例子2:深入了解Collection集合对象的一些方法。
package cn.itheima.day11;
import java.util.ArrayList;
import java.util.Collection;
public class CollectionDemo2 {
	public static void main(String[] args) {
		Collection coll = new ArrayList();  //创建一个集合对象,也就是一个容器
		coll.add("abc1");
		coll.add("abc2");
		coll.add("abc5");  //添加集合元素
		//简单的获取集合中所有的元素,直接将集合元素打印到控制台上
		System.out.println("集合元素:"+coll);
		Collection coll2 = new ArrayList();
		coll2.add("abc3");
		coll2.add("abc2");
		coll2.add("abc1");
		coll2.add("abc4");
		Collection coll3 = new ArrayList();
		coll3.add("abc7");
		coll3.add("abc8");
		//添加一堆元素
		//coll.addAll(coll2);  //将coll2中的元素添加到coll集合中。
		//System.out.println("添加后的集合元素:"+coll);
		//判断一堆元素是否存在
		//boolean b = coll.containsAll(coll3);
		//System.out.println("b="+b);
		//删除一堆元素
		//boolean c = coll.removeAll(coll3);  //removeAll会将coll中包含的和coll3相同的元素删除。
		//System.out.println("c="+c);
		//System.out.println("删除后的集合元素"+coll);
		//获取两个集合的交集
		//retainAll()会将coll和coll2中相同的元素保留在coll中
		//所以coll中存储的就是交集的元素。当coll集合中的元素发生变化时
		//retainAll()方法返回true.当coll集合中的元素本身就是交集的元素,
		//不发生变化,那么返回false.
		boolean b = coll.retainAll(coll2);
		System.out.println("b="+b);
		System.out.println("交集元素"+coll);
	}
}
集合容器都具备取出方式,取出方式并不是一个方法,因为一个方法不足以完成取出,取出是有多个功能而完成,为了方便使用,就将这些功能封装成了对象。该对象在描述时,这些功能会直接使用到具体集合中的数据和集合特点,所以这个取出元素的描述类,就定义在集合内部,也就是一个内部类。
因为这个类在直接访问集合中的数据,所以定义在类的内部,根据集合自身的数据结构的特点,将具体的方法都定义完成后,需要将对象对外提供出去,让用户使用,所以就暴露了一个方法,来完成对内部类的访问。为了提高扩展性,发现只要是容器就具备取出功能,但是数据结构不同,取出的具体实现一样,这时为了扩展,就抽取出了一个接口。这个接口就是Iterator。所以每个对象都有返回该Iterator接口的实例。
总结    1.  集合容器都具有取出方式。
    2.  容器的取出方式都符合一个规则。
    3.  每个容器本身都具有自身的数据结构,但是取出的规则都一致。
例子说明:机器手臂-取出布娃娃。
例子3:Collection集合中对元素的获取方法。
package cn.itheima.day11;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
/**
 * Collection集合中对元素的获取方法。
 * @author wl-pc
*/
public class IteratorDemo {
	public static void main(String[] args) {
       Collection coll = new ArrayList();
       coll.add("abc1");
       coll.add("abc2");
       coll.add("abc3");
       //只要是Collection集合体系,迭代器就是通用的取出方式。
       //Iterator it = coll.iterator();
       //while(it.hasNext()){
    	   //System.out.println(it.next());
       //}
       for (Iterator it = coll.iterator();it.hasNext();) {
		   System.out.println(it.next());
	   }
	}
}
例子4:自定义Person对象,定义姓名和年龄,将对象存储进集合中,并取出其姓名或者年龄。
package cn.itheima.day11;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
/**
 * 练习:自定义Person对象,定义姓名和年龄,
 * 将对象存储进集合中,并取出其姓名或者年龄。
 * @author wl-pc
*/
/**
 *先要对对象进行描述。
 */
class Person{
	private String name;
	private int age;
	Person(String name,int age) {
		this.name=name;
		this.age=age;
	}
	public String getName(){
		return name;
	}
	public int getAge(){
		return age;
	}
}
//add(Object obj)   //可以接收任意类型的对象,但是任意类型的对象被提升为Object
//所以在取出元素时,取出来的也是Object.
public class CollectionTest {
	public static void main(String[] args) {
		//创建一个集合
		Collection coll = new ArrayList(); //集合中存储的都是集合的引用
		coll.add(new Person("张三", 24));
		coll.add(new Person("李四", 34));
		coll.add(new Person("王五", 44));
		Iterator it = coll.iterator();  //获取迭代器后,迭代器中持有也是对象的引用。
		while(it.hasNext()){
		//在进行迭代使用时,next()方法在循环中,建议只定义一次,定义多时,会导致数据错误。
			//当next()方法没有获取到的元素时,会发生NoSuchElementException异常。
			Object object = it.next();
			Person p = (Person)object;
			System.out.println(p.getName()+"-----"+p.getAge());
		}
	}
}
例子5:演示List集合中常见的共性方法
package cn.itheima.day11;
import java.util.ArrayList;
import java.util.List;
/**
 * Collection
 *    |--List:该容器的元素是有序的(即存储的顺序和取出的顺序)
 *            该集合中的元素都是有索引的(也就是角标),该集合可以存储重复的元素。
 *    |--Set
* List集合中常见的共性方法。
 *   1.添加元素。
 *   2.删除元素。
 *   3.修改元素。
 *   4.获取元素。
*/
public class ListDemo {
	public static void main(String[] args) {
		List list = new ArrayList();
		list.add("abc1");
		list.add("abc2");
		list.add("abc3");
		System.out.println("原集合元素:"+list);
		//添加功能,在指定的位置插入元素
		//list.add(1,"haha");  //在1索引处插入元素haha,其他元素依次顺延。
		//System.out.println("插入后元素:"+list);
		//按照指定索引删除元素,会返回被删除的元素。
		//System.out.println("remove(1)"+list.remove(1));
		//按照指定位置的元素进行修改,会返回被返回的元素。
		//System.out.println("set(0,wang)"+list.set(0, "wang"));
		//System.out.println(list);
		//获取索引元素指定的元素
		//System.out.println("get(1):"+list.get(1));
		//通过元素获取到第一次出现的位置
		//System.out.println("indexOf(abc2):"+list.indexOf("abc2"));
		//根据头尾角标获取子列表
		//List newList = list.subList(0, 2); //全取list.subList(0,list.size());
		//System.out.println("自定义的列表:"+newList);
		//通过List的特有方式,获取集合中的元素。
		for(int x=0;x<list.size();x++){
			System.out.println(list.get(x));
		}
		
	}
}
例子6:在迭代集合元素的同时,操作集合元素。
package cn.itheima.day11;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class ListIteratorDemo {
	public static void main(String[] args) {
		List list = new ArrayList();
		list.add("abc1");
		list.add("abc2");
		list.add("abc3");
		list.add("abc4");
		//这时候有需求,当it.next满足了某些条件,做出一些操作(比如:增加新元素,删除元素)
		Iterator it = list.iterator();
		while(it.hasNext()){
			String s = (String)it.next();
			if(s.equals("abc2"))
				//it.remove();
	list.add("haha");  //会抛出并发修改异常:java.util.ConcurrentModificationException
			System.out.println("s="+s);
		}
		System.out.println(list);
	}
}
在进行迭代过程中,如果出现了迭代器和容器同时对元素进行操作的情况很容易引发ConcurrentModificationException并发修改异常.
解决方法:要么使用集合的方法操作元素,要么使用迭代器的方法操作元素。不要同时使用。
很遗憾的是 迭代器Iterator中只有三个操作,判断hasNext,获取next,删除remove。想要其他的操作时,比如添加,这个迭代器就不可以使用了。
这时怎么办呢?注意:对于List集合。有一个新的迭代方式。 就是ListIterator 列表迭代器。ListIterator本身也是Iterator的子接口。并提供了更多的迭代过程中的操作。在迭代过程中,如果需要增删改查元素的操作,需要列表迭代器。
但是注意:该迭代器,只能用于List集合。
例子7:演示一下ListIterator列表迭代器的使用方法。
package cn.itheima.day11;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
public class ListIteratorDemo2 {
	public static void main(String[] args) {
		List list = new ArrayList();
		list.add("abc1");
		list.add("abc2");
		list.add("abc3");
		list.add("abc4");
		//这时候有需求,当it.next满足了某些条件,做出一些操作(比如:增加新元素,删除元素)
		ListIterator lit = list.listIterator();  //ListIterator列表迭代器,可以有增,删,改,查等操作。
		while(lit.hasNext()){
			String s = (String)lit.next();
			if(s.equals("abc2"))
				//lit.add("haha");  //添加
			    lit.set("qq");    //修改
			System.out.println("s="+s);
		}
		boolean b = lit.hasPrevious();  //hasPrevious()有没有前一个集合元素,hasNext()有没有后一个集合元素
		System.out.println("b="+b);
		System.out.println(list);
	}
}
List:有序,可重复,有索引。(面试重点) 
     |--ArrayList:底层数据结构是数组结构。线程不安全的。所以ArrayList的出现替代了Vector.但是查询的速度很快,增删慢.
     |--Vector:底层数据结构是数组结构。jdk1.0版本。线程安全的。无论增删还是查询都非常慢。
     |--LinkedList:底层是链表数据结构。线程不安全的,同时对元算的增删操作效率很高 
可变长度的数组:
    ArrayList内部封装了一个默认长度为10的数组。当超出长度时,集合内部会自动生成一个新的数组。将原数组中的元素复制到新数组中,在将新元素添加到新数组。那么,新数组到底多长呢?
    一般:ArrayList 50%延长,Vector100%延长。
LinkedList的特有方法    1)addFirst();
         addLast();在jdk1.6以后。         offerFirst();
         offerLast();替代 
    2)获取元素,集合的长度不改变。如果集合中没有元素,那么该方法会发生异常NoSuchElementException
         getFirst():
         getLast();在jdk1.6以后。         peekFirst();
         peekLast();替代 
         如果集合元素没有,该方法不会抛出异常,而是返回null。
    3)获取元素(指的是删除元素后,元素返回回来显示),但是该元素会被删除出集合,集合的长度会改变。如果集合中没有元素,那么该方法会发生异常NoSuchElementException
         removeFirst():
         removeLast(); 在jdk1.6以后。         pollFirst();
         pollLast();替代 
        如果集合元素没有,该方法不会抛出异常,而是返回null。
例子8:LinkedList对象的常见方法举例。
package cn.itheima.day11;
import java.util.LinkedList;

public class LinkedListDemo {
	public static void main(String[] args) {
		LinkedList link =new LinkedList();
		link.addFirst("abc1");
		link.addFirst("abc2");
		link.offerFirst("abc3");
		link.offerFirst("abc4");  //打印abc4  abc3  abc2  abc1
		//要是用addLast()添加以上的元素时,打印的就是abc1  abc2  abc3  abc4
		System.out.println(link);
		//System.out.println("头部元素="+link.getFirst());
		//System.out.println("尾部元素="+link.getLast());
		System.out.println("头部元素="+link.peekFirst());
		System.out.println("尾部元素="+link.peekLast());
		//System.out.println("删除头部元素="+link.removeFirst());
		//System.out.println("删除后的头部元素="+link.getFirst());
		System.out.println("删除头部元素="+link.pollFirst());
		System.out.println("删除后的头部元素="+link.peekFirst());
                //取出元素的方法
		while(!link.isEmpty()){
			System.out.println(link.removeFirst());
		}
	}
}
      Vector中提供了一个独特的取出方式,就是枚举Enumeration。此接口Enumeration的功能与 Iterator 接口的功能是重复的,Enumeration的名称和方法的名称过程,书写很麻烦。所以被Iterator所取代。
例子9: Vector中独特的取出方式实例演示。
package cn.itheima.day11;
import java.util.Enumeration;
import java.util.Vector;
public class VectorDemo {
	public static void main(String[] args) {
        Vector v = new Vector();
        v.addElement("abc1");
        v.addElement("abc2");
        v.addElement("abc3");
        v.addElement("abc4");
        Enumeration en = v.elements();
        while(en.hasMoreElements()){
        	System.out.println(en.nextElement());
        }
	}
}
例子10. 去除ArrayList集合中的重复元素。最好该元素还是自定义的。比如Person,如果同姓名,同年龄视为相同元素。
package cn.itheima.day11;
import java.util.ArrayList;
import java.util.Iterator;
/**
 * 练习1:去除ArrayList集合中的重复元素。最好该元素还是自定义的。
 *	           比如Person,如果同姓名,同年龄视为相同元素。
 * @author wl-pc
 *思路:1.定义一个新的ArrayList容器,用于存储不同的元素
 *     2.遍历已有的ArrayList,将遍历到的元素存储到新的集合中。
 *     3.在把遍历到的元素的存入到新的集合中时,这时要判断该元素
 *       是否在新的集合中,如果新集合中存在,则不存入,如果新集合
 *       中没有,则存入元素。注意:判断是否存在可以通过contains方法完成
 *     4.循环结束,新集合中存储的就是不同的元素。
 */
class Person1{
	private String name;
	private int age;
	public Person1(String name, int age) {
		super();
		this.name = name;
		this.age = age;
	}
	/**
	 * 必须建立Person对象比较是否相同的自定义对象的方式,
	 * 所以不能再使用Object中的equals()方法,而是需要覆盖该方法,
	 * 建立自身的比较相同的依据。
	 * 标记同姓名,同年龄为相同的对象。
	 * @return
	 */
	public boolean equals(Object obj){
		if(this==obj){
			return true;
		}
		if(!(obj instanceof Person1)){  //instanceof 判断obj是否是Person1的数据类型。
			return false;
		}
		Person1 p = (Person1)obj;
		return this.name.equals(p.name) && this.age==p.age;
	}
	public String getName() {
		return name;
	}
	public int getAge() {
		return age;
	}
	@Override
	public String toString() {
		return name +"--" + age ;
	}
}
public class ListTest {
	public static ArrayList getSingleElement(ArrayList al){
		ArrayList newList = new ArrayList();
		Iterator it = al.iterator();
		while(it.hasNext()){
			Object obj = it.next();
if(!newList.contains(obj)){   //contains()方法底层依赖的是元素的equals()方法,来判断元素相同
                              //Person的equals()方法是从Object中继承而来的,而Object中的equals()方法它比较的是地址值,
		              //而在new Person()时,每次new一个Person就会有一个新的地址值
			      newList.add(obj);
			}
		}
		return newList;
	}
	public static void main(String[] args) {
		ArrayList al = new ArrayList();
		al.add(new Person1("abc3", 23));
		al.add(new Person1("abc1", 21));
		//al.add(new Person1("abc3", 23));
		al.add(new Person1("abc2", 22));
		//al.add(new Person1("abc1", 21));
		//al.add(new Person1("abc2", 22));
		/*al.add("abc1");
		al.add("abc3");
		al.add("abc2");
		al.add("abc3");
		al.add("abc1");
		al.add("abc2");*/
		/*
		ArrayList判断元素是否相同,底层依据的是元素的equals方法。
		无论是contains,还是remove都会去使用equals判断元素是否相同。
		所以在往ArrayList集合存储自定义元素时,最好建立该元素特有的判断对象是否相同的依据。
		也就是需要覆盖equals方法。
		*/
		System.out.println(al);
		//al = getSingleElement(al);
		//System.out.println(al);
		boolean b = al.remove(new Person1("abc2", 22));
		System.out.println(b);
		System.out.println(al);
	}
}

例子11. 使用LinkedList模拟一个堆栈或者队列数据结构。
                  堆栈结构:先进后出。First In Last Out  FILO
                  队列结构:先进先出。First In First Out FIFO
 应该定义一个对象,内部使用的LinkedList。 对外提供添加获取的功能,可以完成对应的结构效果。

package cn.itheima.day11;
import java.util.LinkedList;
/**
 * 使用LinkedList模拟一个堆栈或者队列的数据结构。
 * 特点:堆栈结构:先进后出
 *      队列结构:先进先出
 * @author wl-pc
 */
class DuiLie{
   private LinkedList link;
   public DuiLie() {
	link = new LinkedList();
   }
   public void add(Object obj){
	   link.addFirst(obj);
   }
   public Object get(){
	  // return link.removeFirst();  //堆栈
	   return link.removeLast();  //队列
   }
   public boolean isNull(){
	   return link.isEmpty();
   }
}
public class ListTest2 {
	public static void main(String[] args) {
         DuiLie dl = new DuiLie();
         dl.add("abc1");
         dl.add("abc2");
         dl.add("abc3");
         dl.add("abc4");
		 while(!dl.isNull()){
			  System.out.println(dl.get());
		 }
	}
}

例子12.将字符串中的数值进行排序,生成一个新的字符串原字符串为“90 -1 23 0 8 14”
   思路:
       1,先要把数值取出。
       2,存储到int数组中。
       3,对int数组排序。
       4,将int数组变成字符串。
package cn.itheima.day11;
/**
 * 例子12.将字符串中的数值进行排序,生成一个新的字符串
                 原字符串为“90 -1 23 0 8 14”
		   思路:
			1,先要把数值取出。
			2,存储到int数组中。
			3,对int数组排序。
			4,将int数组变成字符串。
 */
class Tool {
	// 对数值或字符串进行排序的功能。
	public static String sortNumberString(String str, String reg) {
          String[] arr = splitString(str, reg);
          int[] temp = stringToIntArray(arr);
          sortArray(temp);
          return toString(temp, reg);
	}
	// 1.将字符串进行切割,先把数值取出来
	private static String[] splitString(String str, String reg) {
		return str.split(reg);
	}
	// 2.将字符串数组String[]转成int[]数组。
	private static int[] stringToIntArray(String[] arr) {
		int[] temp = new int[arr.length];
		for (int x = 0; x < arr.length; x++) {
			temp[x] = Integer.parseInt(arr[x]);
		}
		return temp;
	}
	// 3.对int数组进行排序
	private static void sortArray(int[] arr) {
		// Arrays.sort(arr);
		for (int x = 0; x < arr.length - 1; x++) {
			for (int y = x+1; y < arr.length; y++) {
				if (arr[x] > arr[y]) {
					int temp = arr[x];
					arr[x] = arr[y];
					arr[y] = temp;
				}
			}
		}
	}
	// 4.将int数组变成字符串
	private static String toString(int[] arr, String reg) {
		StringBuilder sb = new StringBuilder();
		for (int x = 0; x < arr.length; x++) {
			if (x != arr.length - 1) {
				sb.append(arr[x] + reg);
			} else {
				sb.append(arr[x]);
			}
		}
		return sb.toString();
	}
}
public class Test {
	public static void main(String[] args) {
          String s =Tool.sortNumberString("90 -1 23 0 8 14"," ");
          System.out.println(s);
	}
}
例子13,以下是一个多线程的运行实例,要求写出这个线程运行的过程。
package cn.itheima.day11;
class MyThread extends Thread{
	public void run(){
		try {
			Thread.currentThread().sleep(1000);
		} catch (InterruptedException e) {
		}
		System.out.println("MyThread running");
	}
}
public class ThreadTest {
	public static void main(String[] args) {
		MyThread t = new MyThread();
		t.run();
		t.start();
		System.out.println("Thread Test");
	}
}
  //要求写出运行过程。                                                      
  /*
   当JVM运行ThreadTest类时,就启动了一个主线程,那么主线程执行的代码都在main方法中。
     第一条语句:MyThread t = new MyThread();主线程创建了一个线程对象。
     第二句:t.run();调用了该对象的run()方法,注意这时,只有一个主线程,这时主线程就到了run方法中行, 当执行到了sleep方法时,主线程会睡眠3秒,这时的程序是没有任何执行。CUP是不处理的当3秒结束后,主线程重新获取执行资格,并获取到执行权后,执行输出语句,打印MyThread running
     第三句:t.start();开启了一个线程,这时该线程就多了一个执行路径,该线程要执行的代码都在run()方法中。
这一开启就有了两种情况:
   情况一:
      主线程执行完t.start()方法后,还持有执行权,继续向下执行,将输出语句打印Thread Test,这时主线程结束。这时就只剩下一个线程了Thread-0,它就可以获取执行权,开始执行run()方法,当执行到sleep()时,睡眠3秒,这时程序是不会被运行的,当3秒后,该线程获取执行权,继续执行输出语句,打印MyThread running,该线程Thread-0结束。程序结束。
   情况二:
          当主线程执行完t.start()方法后,开启一个新的线程Thread-0,这时该线程就具备了执行资格,有可能同时获取到了执行权,那么这时主线程就处于临时阻塞状态,也就是主线程具备着执行资格,但是还没有执行权,Thread-0就到了run()方法中执行,执行到sleep()睡眠3秒,这时Thread-0即释放了执行权,又释放了执行资格,这时,主线程就有机会获取到执行权,并执行main()方法中的输出语句,打印 Thread Test,主线程结束,当Thread-0在3秒后,恢复到临时阻塞状态,就具备了执行资格就有机会获取执行权,然后执行输出语句,打印MyThread running,然后程序结束。
   */
Collection
     |--List:有序(存储的顺序和取出的顺序一致。)元素可以重复,元素都有索引。
           |--ArrayList:
           |--LinkedList:
     |--Set:无序,不可以重复元素。Set接口的方法和Collection中的方法一致。Set接口取出元素的方法只有迭代器。
           |--HashSet:底层数据结构是哈希表。哈希表这种结构,其实就是对哈希值的存储。而且每一个对象都有自己的哈希值。因为Object类中的有一个方法hashCode方法。
如何保证元素唯一性的呢?
通过判断元素的hashCode方法,和equals方法完成的。当hashCode值相同是,会在判断一次euqals方法的返回值是否为true。如果hashCode值不相同,就确定元素的哈希表中的位置,就不用在判断equals了。
哈希表中有一个桶结构。每一个桶都有一个哈希值,当哈希值相同,但是equals为false是,这些元素都存放一个桶里。
           |--TreeSet:可以对Set集合中的元素进行排序。数据结构是二叉树数据结构。这种结构,可以提高排序性能。它又是如何保证元素唯一性的呢?
            是根据比较方法的返回值确定的。只要返回的是0.就代表元素重复。
例子14:HashSet集合类的使用演示。
package cn.itheima.day12;
import java.util.HashSet;
import java.util.Iterator;
public class HashSetDemo {
	public static void main(String[] args) {
		HashSet hs = new HashSet();
		hs.add("abc2");
		hs.add("abc1");
		hs.add("abc3");
		hs.add("abc1");
		hs.add("abc3");
		hs.add("abc2");
		System.out.println("size:"+hs.size());
		Iterator it = hs.iterator();
		while(it.hasNext()){
			System.out.println(it.next());
		}
	}
}
HashSet集合是如何保证元素唯一性的?
    HashSet集合保证元素唯一性,依赖的是元素的hashCode方法和euqals方法。当元素的哈希值不同时,元素都有自己的独立位置。不需要在判断元素的equals方法,当元素的哈希值相同时,这时元素在哈希表中位置相同,这时就需要再判断一次元素的内容是否相同。就需要调用元素的equals方法进行一次比较。如果equals返回是true。那么视为两个元素为重复元素。只储存一个。如果返回是false,那么这两个元素不是重复元素,会存储在同一个哈希值上。为了建立自定义对象判断元素是否重复的依据。需要覆盖hashCode方法,和equals方法。而且最好依据对象的特有条件来建立hashcode和euqals的实现。
例子15.HashSet中存储自定义的对象,如果姓名和年龄都相同就视为相同对象。就不进行存储了。
package cn.itheima.day12;
import java.util.HashSet;
import java.util.Iterator;
public class HashSetDemo2 {
	public static void main(String[] args) {
		//往	HashSet集合中存储自定义的对象。
		HashSet hs = new HashSet();
		hs.add(new Person("lisi1", 21));
		hs.add(new Person("lisi6", 26));
		hs.add(new Person("lisi3", 23));
		hs.add(new Person("lisi6", 26));
		hs.add(new Person("lisi5", 25));
		
		Iterator it = hs.iterator();
		while(it.hasNext()){
			Person p = (Person)it.next();
			System.out.println(p.getName()+"------"+p.getAge());
		}
	}
}
//这时要求:如果姓名和年龄都相同就视为相同对象。就不进行存储了。
class Person{
	private String name;
	private int age;
	public Person(String name, int age) {
		this.name = name;
		this.age = age;
	}
	//复写hashCode()方法
	public int hashCode(){
		System.out.println(this+"-----hashCode");
		final int NUMBER = 3; 
		return name.hashCode() + age*NUMBER;
		//return 1;
	}
	//复写equals()方法
	public boolean equals(Object obj){
		System.out.println(this+"---equals----"+obj);
		if(this == obj)
			return true;
		if(!(obj instanceof Person))
			return false;
		Person p = (Person)obj;
		return this.name.equals(p.name) && this.age==p.age;
	}
	public String getName() {
		return name;
	}
	public int getAge() {
		return age;
	}
	@Override
	public String toString() {
		return name+"----"+age;
	}
}
总结细节特点:
    ArrayList:判断包含,以及删除,都是依据元素的equals方法。
    HashSet:判断包含,以及删除,都是依据元素的hashCode方法。当hashCode值相同时,在判断一次equals方法。
例子16.HashSet的remove()方法的使用方法,即特点。
package cn.itheima.day12;
import java.util.HashSet;
public class HashSetDemo3 {
	public static void main(String[] args) {
		//往	HashSet集合中存储自定义的对象。
		HashSet hs = new HashSet();
		hs.add(new Person1("lisi1", 21));
		//hs.add(new Person("lisi6", 26));
		//hs.add(new Person("lisi3", 23));
		hs.add(new Person1("lisi6", 26));
		hs.add(new Person1("lisi1", 21));
		hs.add(new Person1("lisi3", 23));
		hs.add(new Person1("lisi5", 25));
		System.out.println("---------------");
		System.out.println("contains:"+hs.contains(new Person1("lisi6",26)));
	System.out.println("remove:"+hs.remove(new Person1("lisi1", 21)));		 System.out.println(hs);
	}
}
//这时要求:如果姓名和年龄都相同就视为相同对象。就不进行存储了。
class Person1{
	private String name;
	private int age;
	public Person1(String name, int age) {
		this.name = name;
		this.age = age;
	}
	//复写hashCode()方法
	public int hashCode(){
		System.out.println(this+"-----hashCode");
		final int NUMBER = 3; 
		return name.hashCode() + age*NUMBER;
		//return 1;
	}
	//复写equals()方法
	public boolean equals(Object obj){
		System.out.println(this+"---equals----"+obj);
		if(this == obj)
			return true;
		if(!(obj instanceof Person1))
			return false;
		Person1 p = (Person1)obj;
		return this.name.equals(p.name) && this.age==p.age;
	}
	public String getName() {
		return name;
	}
	public int getAge() {
		return age;
	}
	@Override
	public String toString() {
		return name+"----"+age;
	}
}
treeset集合使用于给元素进行排序的那么自定义元素本身不具备比较性,treeset集合是无法对元素进行排序的。所以,在自定义对象时,需要对象具备一个扩展功能,用于比较的,而java已经提供了接口,可以让实现它的对象具备比较性。那么自定义类,要想要被treeset排序,就需要实现Comparable接口。以具备比较功能。比较的时候,要注意,主要条件和次要条件。如果主要条件相同,一定要比较次要条件。
例子17.自定义对象Person,想要对人对象按照年龄的从小到大排序。
package cn.itheima.day12;
import java.util.TreeSet;
public class TreeSetDemo2 {
	public static void main(String[] args) {
		TreeSet ts = new TreeSet();
		ts.add(new Person2("lisi1", 21));
		ts.add(new Person2("lisi3", 23));
		ts.add(new Person2("lisi4", 24));
		ts.add(new Person2("lisi2", 22));
		ts.add(new Person2("lisi6", 26));
		ts.add(new Person2("lisi2", 23));
		System.out.println(ts);
	}
}
//这时要求:如果姓名和年龄都相同就视为相同对象。就不进行存储了。
class Person2 implements Comparable{
	private String name;
	private int age;
	public Person2(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;
	}
	@Override
	public int compareTo(Object obj) {
		Person2 p = (Person2)obj;
		//写法一(按面向对象的方式):先比较主要条件,如果主要条件相同时在比较次要条件。
		//compareTo()按字典的顺序比较两个字符串。
		int num  =new Integer(this.age).compareTo(new Integer(p.age));
		return num==0?this.name.compareTo(p.name):num;
		//写法二:
		/**
		 * int num = this.age - p.age;
		 * return num==0?this.name.compareTo(p.name):num;
		 */
		//return 1;
	}
}
当treeset集合中存储的元素不具备比较功能。或者具备的比较功能不是所需要的。
例如:Person对象中的自然排序是按照年龄排序。
现在需求:但是现在需求是想按姓名排序。改源代码这种方式想都不要想。有这种想法就是犯罪。该如何解决这个问题?
既然元素具备的比较方式不满足应用。这时,可以让集合自身具备比较性。需要集合一初始化就具备比较功能。因为要在添加元素前具备。就需要在构造函数进行初始化。
只要将一个实现了Comparator接口的子类对象作为参数传递给TreeSet集合的构造函数即可。这样该集合就具备了比较功能。
建议使用第二种排序方式。
例子18,定义使用比较器来按照姓名进行对Person排序。
package cn.itheima.day12;
import java.util.Comparator;
import java.util.TreeSet;
public class TreeSetDemo3 {
	public static void main(String[] args) {
		TreeSet ts = new  TreeSet(new CompareByName());
		ts.add(new Person3("lisi1", 21));
		ts.add(new Person3("lisi3", 23));
		ts.add(new Person3("lisi4", 24));
		ts.add(new Person3("lisi2", 22));
		ts.add(new Person3("lisi6", 26));
		System.out.println(ts);
	}
}
//自定义一个比较器
class CompareByName implements Comparator{
	@Override
	public int compare(Object o1, Object o2) {
		Person3 p1 = (Person3)o1;
		Person3 p2 = (Person3)o2;
		int num = p1.getName().compareTo(p2.getName());
		return num==0?p1.getAge()-p2.getAge():num;
	}
}
class Person3{
	private String name;
	private int age;
	public Person3(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;
	}
}
总结:
TreeSet排序方式有两种。
      1,让元素自身具备比较性。其实是让元素实现Comparable接口,覆盖compareTo方法,此方法有一个参数。这称为元素的自然排序。
      2,当元素自身不具备比较性,或者元素具备的比较性不是所需要的,可以让集合自身具备比较性。
定义一个比较器:其实就是定义一个类,实现Comparator接口。覆盖compare方法,此方法有两个参数。将Comparator接口的子类对象作为参数传递给TreeSet的构造函数。
当元素自身具备比较性,同时TreeSet集合也具备比较器,这时以比较器为主.
到了这里,一般在描述一个对象时,如果该对象封装了具体的数据,会出现很多这样的对象比如:员工,学生对象等.这时就需要进行容器的存储.那么描述该类对象时,一定要复写几个方法.
       1,hashCode()
       2,equals()
       3,toString()
前三个方法如果不定义,本身对象中就有,但是,是继承于Object中的,而Object的排序都是按照自身的地址值,是不合适的,所以对象要定义依赖对象自身特征的条件判断。建立自己的特点。
       4,最好实现Comparable接口让该类具备自然排序功能。
       5.自身的构造函数。
建立对象自身判断是否相同的依据,同时让对象具备基本的比较性。
例子19,对字符串进行长度排序,使用TreeSet.
package cn.itheima.day12;
import java.util.Comparator;
import java.util.Iterator;
import java.util.TreeSet;
/**
 * 对字符串进行长度排序,使用TreeSet.
 * @author wl-pc
 *  思路: 字符串都是对象,可以存储到集合中。
 *        还要对字符串排序,所以选择TreeSet集合。
 *        
 *   注意:字符串本身就具有自然排序,但是是按照字典的顺序。
 *        但是,和所需要求不符,这时,就只能使用比较器排序。
 */
public class TreeSetTest {
	public static void main(String[] args) {
         TreeSet ts = new TreeSet(new CompareByLen());
         ts.add("abc");
         ts.add("z");
         ts.add("na");
         ts.add("cccpd");
         ts.add("sdssdfs");
         ts.add("bca");
         Iterator it = ts.iterator();
         while(it.hasNext()){
        	System.out.println(it.next()); 
         }
	}
}
//定义一个按照字符串长度排序的比较器。
class CompareByLen implements Comparator{
	@Override
	public int compare(Object o1, Object o2) {
		String s1 = (String)o1;
		String s2 = (String)o2;
		//方法一
		//int num = s1.length() - s2.length();
		//return num==0?s1.compareTo(s2):num;
		//方法二
		int num = new Integer(s1.length()).compareTo(new Integer(s2.length()));
		return num==0?s1.compareTo(s2):num;
	}
}






  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值