集合是用于存储对象的一个工具。 集合和数组的特点: 相同点: 集合和数组都一个容器。 不同点: 集合: 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; } }