集合

一.集合的引入

1.数组:存储同一种数据类型的集合容器。

2.数组的特点:
(1) 只能存储同一种数据类型的数据。
(2) 一旦初始化,长度固定。 

(3) 数组中的元素与元素之间的内存地址是连续的。

3.注意:Object类型的数组可以存储任意类型的数据,因为Object类是所有类的一个父类。

4.实例:

public class Demo1 {
	public static void main(String[] args) {
		Object[] arr = new Object[3];
		arr[0] = "abc";
		arr[1] = 'a';
		arr[2]  = 12;
		for(int i = 0;i<arr.length;i++){
			System.out.println("数组中的元素:"+arr[i]);
		}
	}
}

运行结果如下图所示:


5.集合引言:

比如要收集每个人的兴趣爱好,如果用数组String[] arr= new String[1000];兴趣爱好数组长度给的太长浪费,给的太短又不够,数组的长度不好操控,集合可以帮我们解决这个问题。

6.集合:集合是存储对象数据的集合容器。

7.集合比数组的优势:
(1) 集合可以存储任意类型的对象数据,数组只能存储同一种数据类型的数据(Object类型的数组除外)。

(2) 集合的长度是会发生变化的,数组的长度是固定的。

8.集合类体系:

---| Collection:单例集合的根接口(集合类可以划分为List和Set两种,List和Set是Collection的子接口) 

---------| List:如果是实现了List接口的集合类,存储元素具备的特点: 有序,可重复。

---------------| ArrayList:底层是维护了一个Object数组实现 的, 特点: 查询速度快,增删慢。

---------------| LinkedList:底层是使用了链表数据结构(链接列表)实现的,特点: 查询速度慢,增删快。

---------------| Vector(了解即可):底层也是维护了一个Object的数组实现的,实现与ArrayList是一样的,但是                                                        Vector是线程安全的,操作效率低。

---------| Set:如果是实现了Set接口的集合类,存储元素具备特点: 无序,不可重复。

---------------| HashSet:底层是使用了哈希表来支持的,特点: 存取速度快。

---------------| TreeSet:如果元素具备自然顺序的特性,那么就按照元素自然顺序的特性进行排序存储。

二.Collection类

1.在Java中有很多的容器类,所以需要为这些容器类提供一个规范,如果实现了它的接口那么就可以认为是一个容器。Collection只是定义了集合类中最基本的规范,另外用了两个子接口在它的基础上又扩展了一些功能,增加了一些约束,就形成了List和Set两个派别的集合类。

2.Collection接口中的方法:

(1) 增加的方法:

① add(E e):添加成功返回true,添加失败返回false。

public class Demo1 {
	public static void main(String[] args) {
		//Collection c = new Collection();会报错,因为Collection是一个接口,接口是不能new的,但是我们又要用这个方法,我们就可以使用多态,我们可以创建它的实现类
		Collection c = new ArrayList();
		//集合可以存储任意类型的数据
		c.add("abc");
		c.add(1);
		c.add(3.14);
		System.out.println("集合的元素:"+c);
	}
}

运行结果如下图所示:


注意:集合的长度是会发生变化的,也就是说不管存多少数据都可以,如果长度不够用会自动增长。

public class Demo2 {
	public static void main(String[] args) {
		Collection c = new ArrayList();
		c.add("张三");
		c.add("李四");
		System.out.println("添加成功吗?"+c.add("狗娃"));
		System.out.println("集合的元素:"+c);
	}
}

运行结果如下图所示:


② addAll(Collection c):把一个集合的元素添加到另外一个集合中去。

public class Demo4 {
	public static void main(String[] args) {
		Collection c = new ArrayList();
		c.add("张三");
		c.add("李四");
		System.out.println("添加成功吗?"+c.add("狗娃"));
		
		//创建集合
		Collection c2 = new ArrayList();
		c2.add("王五");
		c2.add("狗子");
		c.addAll(c2);//把c2的元素添加到c集合中去。
		System.out.println("集合的元素:"+c);
	}
}

运行结果如下图所示:


(2) 删除的方法:

① clear():移除此collection中的所有元素;

public class Demo5 {
	public static void main(String[] args) {
		Collection c = new ArrayList();
		c.add("张三");
		c.add("李四");
		System.out.println("添加成功吗?"+c.add("狗娃"));
		System.out.println("执行clear前集合中的元素:"+c);
		c.clear();//clear()清空集合中的元素
		System.out.println("执行clear后集合中的元素:"+c);
	}
}

运行结果如下图所示:


② remove(Object o):指定集合中的元素删除,删除成功返回true,删除失败返回false。

public class Demo6 {
	public static void main(String[] args) {
		Collection c = new ArrayList();
		c.add("张三");
		c.add("李四");
		System.out.println("添加成功吗?"+c.add("狗娃"));
		System.out.println("执行remove前集合中的元素:"+c);
		System.out.println("删除成功吗?"+c.remove("狗娃"));  
		System.out.println("删除成功吗?"+c.remove("美美")); 
		System.out.println("执行remove后集合中的元素:"+c);
	}
}

运行结果如下图所示:


③removeAll(Collection  c):移除此 collection 中那些也包含在指定 collection 中的所有元素。

public class Demo7 {
	public static void main(String[] args) {
		Collection c = new ArrayList();
		c.add("张三");
		c.add("李四");
		System.out.println("添加成功吗?"+c.add("狗娃"));
		
		//创建集合
		Collection c2 = new ArrayList();
		c2.add("王五");
		c2.add("狗子");
		c2.add("狗娃");
		System.out.println("执行removeAll之前集合中的元素:"+c);
		c.removeAll(c2);//删除c集合中与c2的交集元素。
		System.out.println("c2集合中的元素:"+c2);
		System.out.println("执行removeAll之后集合中的元素:"+c);
	}
}

运行结果如下图所示:


④ retainAll(Collection  c) :保留交集元素,其他非交集元素删除掉。

public class Demo8 {
	public static void main(String[] args) {
		Collection c = new ArrayList();
		c.add("张三");
		c.add("李四");
		System.out.println("添加成功吗?"+c.add("狗娃"));
		
		//创建集合
		Collection c2 = new ArrayList();
		c2.add("王五");
		c2.add("狗子");
		c2.add("狗娃");
		System.out.println("执行retainAll之前集合中的元素:"+c);
		c.retainAll(c2);//删除c集合中与c2的非交集元素。
		System.out.println("c2集合中的元素:"+c2);
		System.out.println("执行retainAll之后集合中的元素:"+c);
	}
}

运行结果如下图所示:


(3) 查看的方法:

① size():返回此 collection 中的元素数。 

public class Demo9 {
	public static void main(String[] args) {
		Collection c = new ArrayList();
		c.add("张三");
		c.add("李四");
		System.out.println("添加成功吗?"+c.add("狗娃"));
		System.out.println("查看集合中的元素个数:"+c.size());
		System.out.println("集合的元素:"+c);
	}
}

运行结果如下图所示:


(4) 判断的方法:

① isEmpty():如果此 collection 不包含元素,则返回 true。

② contains(Object o):如果此 collection 包含指定的元素,则返回 true。

public class Demo10 {
	public static void main(String[] args) {
		Collection c = new ArrayList();
		c.add("张三");
		c.add("李四");
		System.out.println("判断集合是否为空?"+c.isEmpty());
		System.out.println("判断集合中是否存在指定的元素:"+ c.contains("李四"));
	}
}

运行结果如下图所示:


③ containsAll(Collection<?> c):如果此 collection 包含指定 collection 中的所有元素,则返回 true。 

class Person{
	int id; 
	String name;
	public Person(int id, String name) {
		this.id = id;
		this.name = name;
	}
	@Override
	public String toString() {
		return "{编号:"+this.id+" 姓名:"+ this.name+"}";
	}
	@Override
	public boolean equals(Object obj) {
		Person p = (Person)obj;
		return this.id == p.id ;
	}
	//java规范: 一般重写equlas方法我们都会重写hashCode方法的。
	@Override
	public int hashCode() {
		return this.id;
	}
}
public class Demo11 {
	public static void main(String[] args) {
		// 集合中添加自定义的元素
		Collection c = new ArrayList();
		c.add(new Person(110, "狗娃"));
		c.add(new Person(119, "狗剩"));
		c.add(new Person(120, "铁蛋"));
		
		Collection c2 = new ArrayList();
		c2.add(new Person(110, "狗娃"));
		c2.add(new Person(119, "狗剩"));
		c2.add(new Person(104, "陈狗剩"));

		System.out.println("c集合有包含c2集合中的所有元素吗?" + c.containsAll(c2));

		// 如果在现实生活中,只要身份证编号一致,那么就为同一个人。
		System.out.println("存在该元素吗?" + c.contains(new Person(120, "铁蛋"))); // 其实contains方法内部是依赖于equals方法进行比较的。equals方法默认是比较两个对象的内存地址的
	}
}

运行结果如下图所示:


(5) 迭代的方法:

① toArray():返回包含此 collection 中所有元素的数组。

public class Demo1 {
	public static void main(String[] args) {
		Collection c = new ArrayList();
		c.add("狗娃");
		c.add("狗剩");
		c.add("铁蛋");
		c.add("美美");
	
		//遍历集合的元素------>方式一: 可以使用toArray方法。
		Object[] arr = c.toArray(); // toArray()  把集合的元素存储到一个 Object的数组中 返回。
		for(int i = 0 ; i<arr.length ; i++){
			System.out.print(arr[i]+",");
		}
	}
}

运行结果如下图所示:


② iterator():返回在此 collection 的元素上进行迭代的迭代器。 

public class Demo2 {
	public static void main(String[] args) {
		Collection c = new ArrayList();
		c.add("狗娃");
		c.add("狗剩");
		c.add("铁蛋");
		c.add("美美");
		Iterator it = c.iterator();  //返回一个迭代器    疑问:iterator()方法返回的是一个接口类型,为什么接口又可以调用方法可以使用呢?  虽然iterator返回了一个接口类型,但是iterator 实际 上返回的是iterator接口的实现类对象。
		System.out.println("有元素可以遍历吗?"+it.hasNext());
		//既然有元素可以遍历就需要去获取元素。
		System.out.println("获取元素:"+it.next());//next方法每次只会抓取一个元素。要抓取四个元素就需要四个next方法。
		System.out.println("获取元素:"+it.next());
		System.out.println("获取元素:"+it.next());
		System.out.println("获取元素:"+it.next());
		//如果抓取完以后没有元素可以迭代,再继续使用next就会报异常
		//System.out.println("获取元素:"+it.next());
	}
}

运行结果如下图所示:


Demo2中重复代码的改进:

//Demo2中重复代码的改进
public class Demo3 {
	public static void main(String[] args) {
		Collection c = new ArrayList();
		c.add("狗娃");
		c.add("狗剩");
		c.add("铁蛋");
		c.add("美美");
		Iterator it = c.iterator();  //返回一个迭代器    疑问:iterator()方法返回的是一个接口类型,为什么接口又可以调用方法可以使用呢?  虽然iterator返回了一个接口类型,但是iterator 实际 上返回的是iterator接口的实现类对象。
		while(it.hasNext()){ // hasNext() 问是否有元素可以遍历。
			System.out.println("元素:"+ it.next()); //获取元素
		}
	}
}

运行结果如下图所示:


3.补充:迭代器

(1) 迭代器的作用:就是用于抓取集合中的元素。

(2) 迭代器的方法:

① hasNext():问是否有元素可以继续遍历?如果有元素可以遍历,返回true,否则返回false 。

② next():获取元素。

③ remove():移除迭代器最后一次返回 的元素。也就是从迭代器指向的Collection中移出迭代器返回的最后一个元素。

(3) 迭代器的原理


现在集合中有四个元素,当我们取c.iterator()的时候,执行到那一句话会获取到一个迭代器,获取到迭代器就相当于获取到容器中的爪子一样,一旦获取到迭代器,迭代器中就有一个指针指向了集合中的第一个元素。

(4) 实例:

① 实例一

public class Demo4 {
	public static void main(String[] args) {
		Collection c = new ArrayList();
		c.add("狗娃");
		c.add("狗剩");
		c.add("铁蛋");
		c.add("美美");
		Iterator it = c.iterator();  
		it.next();
		it.next();
		it.remove();//如果Iterator it = c.iterator();后直接运行这句话,那么是会报错的,因为迭代器没有获取到元素。
		System.out.println("集合的元素:"+c);
	}
}

运行结果如下图所示:


② 实例二

public class Demo5 {
	public static void main(String[] args) {
		Collection c = new ArrayList();
		c.add("狗娃");
		c.add("狗剩");
		c.add("铁蛋");
		c.add("美美");
		Iterator it = c.iterator();  
		//这里的While循环实际上做的是清空集合的元素。
		while(it.hasNext()){
			it.next();
			it.remove();
		}
		System.out.println("集合的元素:"+c);
	}
}

运行结果如下图所示:


(5) NoSuchElementException 没有元素的异常,在迭代中经常见。 

出现的原因: 没有元素可以被迭代了。

三.List类

1.List:如果是实现了List接口的集合类,该集合类具备的特点:有序,可重复。

2.有序:集合的有序不是指自然顺序,而是指添加进去的顺序与元素出来的顺序是一致的。

3.List接口中特有方法

注意:List是一个接口,不能直接new,只能new它的实现类。

(1) 添加的方法:

① add(int index, E element):在列表的指定位置插入指定元素 。

public class Demo1 {
	public static void main(String[] args) {
		List list=  new ArrayList();
		list.add("狗娃");
		list.add("狗剩");
		list.add("铁蛋");  //把元素添加到集合的末尾处。
		list.add("狗娃");
		list.add(1, "张三"); // 把元素添加到集合中的指定索引值位置上。也就是在狗娃和狗剩之间插入张三
		System.out.println("集合的元素:"+list);
	}
}

运行结果如下图所示:


② addAll(int index, Collection<? extends E> c):将指定 collection 中的所有元素都插入到列表中的指定位置。

public class Demo2 {
	public static void main(String[] args) {
		List list=  new ArrayList();
		list.add("狗娃");
		list.add("狗剩");
		list.add("铁蛋");  //把元素添加到集合的末尾处。
		
		List list2 = new ArrayList();
		list2.add("张三");
		list2.add("李四");
		list.addAll(2,list2); //把list2的元素添加到list集合指定索引值的位置上。也就是在狗剩和铁蛋之间插入list2集合中的元素
		System.out.println("集合中的元素:"+list);
	}
}

运行结果如下图所示:


(2) 获取的方法:

① get(int index):返回列表中指定位置的元素。 

public class Demo3 {
	public static void main(String[] args) {
		List list=  new ArrayList();
		list.add("狗娃");
		list.add("狗剩");
		list.add("铁蛋");  //把元素添加到集合的末尾处。
		
		System.out.println("get方法获取元素:"+list.get(1)); //根据索引值获取集合中的元素
		//使用get方法遍历集合的元素:
		//注意:只有在List接口下的实现类才能使用这种方式遍历,因为这个get方法是list接口特有的方法。
		for (int i = 0; i < list.size() ; i++) {
			System.out.print(list.get(i)+",");
		}
	}
}

运行结果如下图所示:


② indexOf(Object o):返回此列表中第一次出现的指定元素的索引;如果此列表不包含该元素,则返回 -1。 

③ lastIndexOf(Object o):返回此列表中最后出现的指定元素的索引;如果列表不包含此元素,则返回 -1。 

④ subList(int fromIndex, int toIndex):返回列表中指定的 fromIndex(包括 )和 toIndex(不包括)之间的部分视图。 
public class Demo4 {
	public static void main(String[] args) {
		List list=  new ArrayList();
		list.add("狗娃");
		list.add("狗剩");
		list.add("铁蛋");  //把元素添加到集合的末尾处。
		list.add("狗娃");
		System.out.println("集合的元素是:"+ list);
		System.out.println("找出指定元素第一次出现在集合中 的索引值:"+ list.indexOf("狗娃"));
		System.out.println("找出指定元素第一次出现在集合中 的索引值:"+ list.indexOf("张三"));
		System.out.println("找指定的元素最后一次出现在集合中的索引值:"+list.lastIndexOf("狗娃"));
		List subList = list.subList(1, 3); //指定开始与结束的索引值截取集合中的元素。包头不包尾
		System.out.println("子集合的元素是:"+ subList);
	}
}

运行结果如下图所示:


(3) 修改的方法: 

① set(int index, E element):用指定元素替换列表中指定位置的元素。

public class Demo5 {
	public static void main(String[] args) {
		List list=  new ArrayList();
		list.add("狗娃");
		list.add("狗剩");
		list.add("铁蛋");  //把元素添加到集合的末尾处。
		System.out.println("执行set操作前集合中的元素:"+list);
		list.set(2, "张三"); //使用指定的元素替换指定索引值位置的元素。
		System.out.println("执行set操作后集合中的元素:"+list);
	}
}

 运行结果如下图所示:


(4) 迭代的方法:

① listIterator():返回此列表元素的列表迭代器(按适当顺序)。 返回的是一个List接口中特有的迭代器。

public class Demo6 {
	public static void main(String[] args) {
		List list=  new ArrayList();
		list.add("狗娃");
		list.add("狗剩");
		list.add("铁蛋");  //把元素添加到集合的末尾处。
		list.add("美美");
		
		ListIterator it = list.listIterator();
		it.next();
		System.out.println("获取上一个元素:"+it.previous());
	}
}

运行结果:


4.List接口中特有的方法具备的特点: 操作的方法都存在索引值
(1) 记住一句话:以后凡是用集合类的时候,如果想操作索引值, 那么就应该用List接口下的集合类,其他接口下的集合类是没有索引值的。

(2) 注意:只有List接口下面的集合类才具备索引值,其他接口下面的集合类都没有索引值。

5.补充:List接口下的迭代器

注意:每取到一个迭代器,那么迭代器就有一个指针指向了第0个元素。

(1) ListIterator特有的方法:

① hasPrevious():判断是否存在上一个元素。
② previous():当前指针先向上移动一个单位,然后再取出当前指针指向的元素。先移再取

③ next();  先取出当前指针指向的元素,然后指针向下移动一个单位。先取再移

public class Demo7 {
	public static void main(String[] args) {
		List list=  new ArrayList();
		list.add("狗娃");
		list.add("狗剩");
		list.add("铁蛋");  //把元素添加到集合的末尾处。
		list.add("美美");
		
		ListIterator it = list.listIterator();
		while(it.hasNext()){
			it.next();
		}
		
		while(it.hasPrevious()){
			System.out.println("元素:"+ it.previous());
		}
	}
}

运行结果如下图所示:


④ add(E e):把当前元素插入到当前指针指向的位置上。

public class Demo8 {
	public static void main(String[] args) {
		List list=  new ArrayList();
		list.add("狗娃");
		list.add("狗剩");
		list.add("铁蛋");  //把元素添加到集合的末尾处。
		list.add("美美");
		
		ListIterator it = list.listIterator();
		it.add("张三");//把张三插入到狗娃的前面
		System.out.println("集合中的元素:"+list);
	}
}

运行结果如下图所示:


⑤ set(E e):替换迭代器最后一次返回的元素。

public class Demo9 {
	public static void main(String[] args) {
		List list=  new ArrayList();
		list.add("狗娃");
		list.add("狗剩");
		list.add("铁蛋");  //把元素添加到集合的末尾处。
		list.add("美美");
		
		ListIterator it = list.listIterator();
		it.next();
		it.next();
		System.out.println("执行set操作前集合中的元素:"+list);
		it.set("张三");//把狗剩替换为张三
		System.out.println("执行set操作后集合中的元素:"+list);
	}
}

运行结果如下图所示:


(2) 使用迭代器要注意的事项:

① 注意事项一:

public class Demo10 {
	public static void main(String[] args) {
		List list = new ArrayList();
		list.add("张三");
		list.add("李四");
		list.add("王五");
		
		ListIterator it = list.listIterator();	//获取到迭代器
		while(it.hasNext()){
			System.out.print(it.next()+",");
			it.add("aa"); // 把元素添加到当前指针指向位置
		}
		System.out.println("\r\n集合的元素:"+ list);
	}
}

运行结果如下图所示:


通过上述代码和运行结果我们可以看出,每迭代一次加一个元素进去,实际上并不会出现死循环,遍历出来后并不会把新加进去的元素遍历,但实际上已经加了进去,当我们输出集合中的元素时是已经把新的元素加进去了,只不过在迭代的过程中,没有把新加进去的元素遍历出来而已,因为如果遍历出来就是一个死循环了。

 

它的设计过程如下: 目前每next一次,游标就向下移动一次,第一次执行add操作,实际上是把aa加入到张三和李四之间,如果这时候把aa遍历出来,这时候指针就指向aa,那就会没完没了的添加aa,实际上已经插入进去,那么它的设计是如何做到跳过它而不遍历呢? 通过查看源代码我们发现,执行add操作时,会把游标向下移动一位就会跳过这个新加入的元素不去遍历。 这样设计的原因就是为了避免死循环。

② 注意事项二:

public class Demo11 {
	public static void main(String[] args) {
		List list = new ArrayList();
		list.add("张三");
		list.add("李四");
		list.add("王五");
		
		ListIterator it = list.listIterator();	//获取到迭代器
		while(it.hasNext()){
			System.out.print(it.next()+",");
			list.add("aa"); // 把元素添加到集合的末尾处
		}
		System.out.println("\r\n集合的元素:"+ list);
	}
}

运行结果如下图所示:


通过上述代码和运行结果我们发现:如果改成list.add("aa");这时候每迭代一次就会在末尾加一个aa,首先指针刚开始指向张三,执行next操作把张三取出来后就会在末尾添加aa元素,这时候指针就不能再跳了,如果跳到末尾,那么就只会遍历一个元素而已,所以这样直接会报错,不允许这样操作。因为如果这样操作,迭代主要是把这个集合中的元素全部输出,如果跳过只会遍历一个元素,不符合预期;如果不跳过的话那么就会出现死循环,所以就不允许这样操作。

③ 注意事项三:

public class Demo12 {
	public static void main(String[] args) {
		List list = new ArrayList();
		list.add("张三");
		list.add("李四");
		list.add("王五");
		
		ListIterator it = list.listIterator();	//获取到迭代器,迭代器创建
		list.add("aa");//如果只有这一个语句是没有问题的,因为这句话后面已经没有迭代器的用法了。
		//如果在后面一旦出现迭代器的用法,不管是add还是next都会报错。如果把迭代器的用法移到上面又会正常。
		//只要添加元素的后面没有迭代器的方法就没有问题,如果该变了元素的个数,后面还有迭代器的语句,那么就是在中间修改它,都是不允许的。
		System.out.println("\r\n集合的元素:"+ list);
	}
}

运行结果如下图所示:


④ 总结:

迭代器在变量元素的时候要注意事项: 在迭代器迭代元素 的过程中,不允许使用集合对象改变集合中的元素 个数(不管是添加元素add还是删除元素remove都不允许),如果需要添加或者删除只能使用迭代器的方法进行操作。如果使用过了集合对象改变集合中元素个数那么就会出现ConcurrentModificationException异常。
 
注意:只要不改变个数就可以,如果是set方法就可以用集合对象去操作。 

什么是迭代元素 的过程中?: 这个过程就是迭代器从创建到使用结束的时间。

四.ArrayList类

1.ArrayList 底层是维护了一个Object数组实现 的, 特点: 查询速度快,增删慢。

2.ArrayList特点的原理:


(1) 我们用ArrayList实际上是在用Object数组来存储元素,数组中的元素与元素之间的内存地址是连续的,所以它的查询速度是很快的。比如它的内存地址是0x98,0x99,一开始就用指针指向了第0个元素,如果要执行查询操作比如list.get(100);查询100号的元素是多少,因为它们之间的内存地址是连续的,所以可以让指针直接移动100个单位直接可以找得到目标的元素,不连续就不可以这样做。
(2) 那为什么增加慢呢?假设目前Object数组的长度(容量)是7,假设现在添加第8个元素的时候,现在长度不够,就需要创建一个容量是原来数组1.5倍的新数组,长度为10,还要把旧数组中的内容拷贝到新数组中去,这个过程是很繁琐的。所以每次添加的时候都要首先检查长度够不够用,检查需要时间;当容量不够用时还需要把旧数组中的元素拷贝到新数组中,如果遇到数据量比较大的时候,过程会很漫长。

(3) 那为什么删除也慢呢?比如现在要把王五删了,那么现在就会空出一个位置,它就会把后面的元素再往前面挪动,把前面的空格填补上去,所以效率也是很低的。

3.什么时候使用ArrayList? 

如果目前的数据是查询比较多,增删比较少的时候,那么就使用ArrayList存储这批数据。比如:高校的图书馆。

4.笔试题目:使用ArrayList无参的构造函数创建一个对象时,默认的容量是多少? 如果长度不够使用时又自增增长多少?

答:ArrayList底层是维护了一个Object数组实现的,使用无参构造函数时,Object数组默认的容量是10,当长度不够时,自动增长0.5倍。

5.ArrayList 特有的方法(很少用):

(1) ensureCapacity(int minCapaci上ty):用来指定初始容量,一般很少用,因为ArrayList有一个构造方法就可以指定初始容量,我们一般习惯用构造方法去指定。

(2) trimToSize():将此ArrayList实例的容量调整为列表的当前大小。比如说现在Object数组默认的长度是10,假设现在只添加了3个元素,那么现在还有7个位置是空的,这个方法就是把后面的7个删掉,节省内存空间,一般也不使用,因为如果以后还要添加元素,那么就还会执行拷贝的动作。效率又会降低,还不如用内存去换取效率更合适。

6.实例:

(1) 需求:编写一个函数清除集合中重复元素。 如果书号是一样就视为重复元素。要求:遍历集合元素的时候必须使用迭代器。get迭代器。

(2) 分析:先创建一个全新的集合,然后从存有重复元素的集合中把元素挨个取出,与全新集合中的元素比较,看全新集合中是否有这个元素,(每取出一个元素都问一下这个新的集合中是否有这个元素),没有就放进这个新的集合中,如果有就不要这个元素了。此时这个新的集合中存放的元素就没有重复元素了。

(3) 实例:

class Book{	
	int id;
	String name;// 名字

	public Book(int id, String name) {
		this.id = id;
		this.name = name;
	}
	
	@Override
	public String toString() {
		return "{ 书号:"+ this.id+" 书名:"+ this.name+" }";
	}
	
	@Override
	public boolean equals(Object obj) {
		Book book =(Book)obj;
		return this.id==book.id;
	}
}
public class Demo7 {	
	public static void main(String[] args) {
		ArrayList list=  new ArrayList();
		list.add(new Book(110,"java编程思想"));
		list.add(new Book(220,"java核心技术"));
		list.add(new Book(330,"深入javaweb"));
		list.add(new Book(110,"java神书"));
		ArrayList list2 = clearRepeat(list);
		System.out.println("新集合的元素是:"+ list2);
	}
	public static ArrayList  clearRepeat(ArrayList list){
		//创建一个新的集合
		ArrayList newList = new ArrayList();
		//获取迭代器遍历旧的集合
		Iterator it = list.iterator();
		while(it.hasNext()){
			Book book = (Book) it.next();  //从旧集合中获取的元素
			if(!newList.contains(book)){
				//如果新集合没有包含该书籍,那么就存储到新集合中
				newList.add(book);
			}
		}
		return newList;	
	}
}

(4) 运行结果:


五.LinkedList类

1.LinkedList:底层是使用了链表数据结构(链接列表)实现的,特点:查询速度慢,增删快。

2.Linkedlist的实现原理:


当我们往Linkedlist中添加数据的时候,它的一个元素被分成两块,比如添加张三,在Linkedlist中一个元素由两部分组成,一部分存储着张三,另外一部分存储着下个元素的内存地址,这样就好像是链子一样,一环扣着一环。

3.Linkedlist的特点的原理:


(1) 那么为什么它的查询速度会慢呢?
因为它的元素与元素之间的内存地址不是连续的,比如要找第100号元素,那么对于链表数据结构来说,它就不能直接再移动100个单元,因为它的内存地址并不连续,它只能挨个把所有的元素都遍历完,直到找到它要的那个元素为止。挨个遍历的速度是很慢的。
(2) 那么为什么它的增加速度会快呢?
比如要在张三和李四之间插入一个狗娃,只需要把张三和李四之间的连线断开,让张三的指针域记录狗娃的内存地址,让狗娃的指针域记录李四的内存地址。

(3) 那么为什么它的删除的速度也快呢?

比如要把王五删除,只需要让李四的指针域不再指向王五的内存地址,这时候就没有人指向王五,一旦没有变量指向它,那么它就会变成一个垃圾对象,就没有人再去使用它。

4.Linkedlist特有的方法:

(1) 添加的方法:

① addFirst(E e):将指定元素插入此列表的开头。 
② addLast(E e):将指定元素添加到此列表的结尾。

public class Demo1 {
	public static void main(String[] args) {
		LinkedList list= new LinkedList();
		list.add("张三");
		list.add("李四");
		list.add("王五");
		list.addFirst("狗娃"); //把元素添加到集合的首位置上。
		list.addLast("狗剩");  //把元素添加到集合的末尾处。
		System.out.println("集合中的元素:"+ list);
	}
}

运行结果如下图所示:


(2) 获取的方法:

① getFirst():返回此列表的第一个元素。 
② getLast():返回此列表的最后一个元素。 

public class Demo2 {
	public static void main(String[] args) {
		LinkedList list= new LinkedList();
		list.add("张三");
		list.add("李四");
		list.add("王五");
		System.out.println("集合中的元素:"+ list);
		System.out.println("获取集合中首位置的元素:"+list.getFirst());
		System.out.println("获取集合中末尾的元素:"+ list.getLast());		
	}
}

运行结果如下图所示:


(3) 删除的方法:

public class Demo3 {
	public static void main(String[] args) {
		LinkedList list= new LinkedList();
		list.add("张三");
		list.add("李四");
		list.add("王五");
		System.out.println("执行删除操作前集合中的元素:"+ list);
		System.out.println("删除集合中的首位置元素并返回:"+ list.removeFirst());
		System.out.println("删除集合中的末尾元素并返回:"+ list.removeLast());
		System.out.println("执行删除操作后集合中的元素:"+ list);
	}
}

运行结果如下图所示:


(4) 数据结构:


① 栈:先进后出,主要是用于实现堆栈数据结构的存储方式。

push(E e):将元素推入此列表所表示的堆栈。 每次都是把元素放入栈顶。

pop():从此列表所表示的堆栈处弹出一个元素。
public class Demo4 {
	public static void main(String[] args) {
		LinkedList list= new LinkedList();
		list.add("张三");
		list.add("李四");
		list.add("王五");
		
		list.push("狗娃");   //将该元素插入此集合的开头处。 
		System.out.println("执行pop操作前集合中的元素:"+ list);
		System.out.println("删除集合的首元素:"+list.pop()); // 移除并返回集合中的第一个元素 
		System.out.println("执行pop操作后集合中的元素:"+ list);
	}
}

运行结果如下图所示:


② 队列:先进先出,引入这两个方法主要是为了让我们可以使用LinkedList模拟队列数据结构的存储方式。
offer(E e):将指定元素添加到此列表的末尾(最后一个元素)。

poll():获取并移除此列表的头(第一个元素)。

public class Demo5 {
	public static void main(String[] args) {
		LinkedList list= new LinkedList();
		list.add("张三");
		list.add("李四");
		list.add("王五");
		
		list.offer("狗剩");
		System.out.println("执行offer操作后集合中的元素:"+ list);
		System.out.println("删除集合的首元素: "+list.poll());
		System.out.println("执行poll操作后集合中的元素:"+ list);
	}
}

运行结果如下图所示:


(5) 返回逆序的迭代器对象      

① descendingIterator():返回以逆向顺序在此双端队列的元素上进行迭代的迭代器。

public class Demo6 {
	public static void main(String[] args) {
		LinkedList list= new LinkedList();
		list.add("张三");
		list.add("李四");
		list.add("王五");
		Iterator  it = list.descendingIterator();
		while(it.hasNext()){
			System.out.println(it.next());
		}
	}
}

运行结果如下图所示:


5.实例一:

(1) 需求:使用LinkedList实现堆栈数据结构的存储方式与队列的数据结构存储方式。

(2) 实例:

// 使用LinkedList模拟堆栈的数据结构存储方式
class StackList{
	LinkedList list;
	
	public StackList(){
		list = new LinkedList();
	}
	//进栈
	public void add(Object o){
		list.push(o);
	}
	//弹栈 : 把元素删除并返回。
	public Object pop(){
		return list.pop();
	} 
	//获取元素个数
	public int size(){
		return list.size();
	}
}

//使用LinkedList模拟队列的存储方式
class TeamList{
	LinkedList list;
	public TeamList(){
		list = new LinkedList();
	}
	
	public void add(Object o){
		list.offer(o);
	}
	
	public Object remove(){
		return list.poll();
	}
	
	//获取元素个数
	public int size(){
		return list.size();
	}
}
public class Demo9 {	
	public static void main(String[] args) {
		TeamList list=  new TeamList();
		list.add("张三");
		list.add("李四");
		list.add("王五");
		
		int size = list.size();
		for(int i = 0 ; i<size ; i++){
			System.out.println(list.remove());
		}
	}
}

(3) 运行结果:


6.实例二:

(1) 需求:使用LinkedList存储一副扑克牌,然后实现洗牌功能。

(2) 实例:

//扑克类
class Poker{
	String  color; //花色
	String num;	//点数
	public Poker(String color, String num) {
		super();
		this.color = color;
		this.num = num;
	}
	@Override
	public String toString() {
		return "{"+color+num+"}";
	}
}

public class Demo2 {
	public static void main(String[] args) {
		LinkedList pokers = createPoker();
		shufflePoker(pokers);
		showPoker(pokers);
	}
	//洗牌的功能
	public static void shufflePoker(LinkedList pokers){
		//创建随机数对象
		Random random = new Random();
		for(int i = 0 ; i <100; i++){ 
			//随机产生两个索引值
			int index1 = random.nextInt(pokers.size());
			int index2 = random.nextInt(pokers.size());
			//根据索引值取出两张牌,然后交换两张牌的顺序
			Poker poker1 = (Poker) pokers.get(index1);
			Poker poker2 = (Poker) pokers.get(index2);
			pokers.set(index1, poker2);
			pokers.set(index2, poker1);
		}	
	}
	//显示扑克牌
	public static void showPoker(LinkedList pokers){
		for(int i = 0 ; i<pokers.size() ; i++){
			System.out.print(pokers.get(i));
			//换行
			if(i%10==9){
				System.out.println();
			}
		}
	}
	//生成扑克牌的方法
	public static LinkedList createPoker(){
		//该集合用于存储扑克对象。
		LinkedList list = new LinkedList();		
		//定义数组存储所有的花色与点数
		String[] colors = {"黑桃","红桃","梅花","方块"};
		String[] nums = {"A","2","3","4","5","6","7","8","9","10","J","Q","K"};
		for(int i = 0 ; i < nums.length ; i++){
			for(int j = 0 ; j<colors.length ; j++){
				list.add(new Poker(colors[j], nums[i]));
			}
		}
		return list;
	}
}

(3) 运行结果:


7.实例三

(1) 需求:编写一个函数根据人的年龄排序存储。

(2) 实例:

class Person{
	String name;
	int age;
	public Person(String name, int age) {
		super();
		this.name = name;
		this.age = age;
	}
	
	@Override 
	public String toString() {
		return "{ 名字:"+ this.name+" 年龄:"+ this.age+"}";
	}
}

public class Demo3 {
	public static void main(String[] args) {
		LinkedList list = new LinkedList();
		list.add(new Person("狗娃", 7));
		list.add(new Person("狗剩", 17));
		list.add(new Person("铁蛋", 3));
		list.add(new Person("美美", 30));
		
		//编写一个函数根据人的年龄及逆行排序存储。
		for(int i= 0 ; i<list.size() -1 ; i++){
			for(int j = i+1 ; j<list.size() ; j++){
				//符合条件交换位置
				Person p1 = (Person) list.get(i);
				Person p2 = (Person) list.get(j);
				if(p1.age>p2.age){
					//交换位置
					list.set(i, p2);
					list.set(j, p1);			
				}
			}
		}
		System.out.println(list);
	}
}

(3) 运行结果:


六.Vector类(了解即可)

1.Vector:底层也是维护了一个Object的数组实现的,实现与ArrayList是一样的,但是Vector是线程安全的,操作效率低。

2.笔试题:说出ArrayList与Vector的相同点与不同点?

(1) 相同点:ArrayList与Vector底层都是使用了Object数组实现的。

(2) 不同点: 

① ArrayList是线程不同步的,操作效率高。 Vector是线程同步的,操作效率低。

② ArrayList是jdk1.2出现,Vector是jdk1.0的时候出现的。

3.Vector实例:

public class Demo1 {
	public static void main(String[] args) {
		Vector v  =  new Vector();
		//添加元素
		v.addElement("张三");
		v.addElement("李四");
		v.addElement("王五");
		//迭代该集合
		Enumeration e = v.elements(); //获取迭代器
		while(e.hasMoreElements()){
			System.out.println(e.nextElement());
		}
	}
}

运行结果如下图所示:


七.Set类

1.Set:如果是实现了Set接口的集合类,具备的特点:无序,不可重复。

注意:无序指的是添加元素的顺序与元素出来的顺序是不一致的。

2.实例:

public class Demo1 {
	public static void main(String[] args) {
		Set set = new HashSet();//Set是一个接口,不能直接new,只能new它的实现类。
		set.add("王五");
		set.add("张三");
		set.add("李四");
		System.out.println("添加成功吗?"+set.add("李四"));
		System.out.println(set);
	}
}

运行结果如下图所示:


八.HashSet

1.HashSet:底层是使用了哈希表来支持的,特点: 存取速度快。

2. HashSet的存储原理:

(1) 实例

class Person{
	int id;
	String name;
	public Person(int id, String name) {
		super();
		this.id = id;
		this.name = name;
	}
	
	@Override
	public String toString() {
		return "{ 编号:"+ this.id+" 姓名:"+ this.name+"}";
	}
	
	@Override
	public int hashCode() {
		System.out.println("=======hashCode=====");
		return this.id;
	}
	
	@Override
	public boolean equals(Object obj) {
		System.out.println("======equals======");
		Person p = (Person)obj;
		return this.id==p.id;
	}
}

public class Demo2 {
	public static void main(String[] args) {
		HashSet set = new HashSet();
		set.add(new Person(110,"狗娃"));
		set.add(new Person(220,"狗剩"));
		set.add(new Person(330,"铁蛋"));
		//在现实生活中只要编号一致就为同一个人.
		System.out.println("添加成功吗?"+set.add(new Person(110,"狗娃")));
		System.out.println("集合的元素:"+set);	
	}
}

(2) 运行结果:


(3) HashSet的存储原理:

往Haset添加元素的时候,HashSet会先调用元素的hashCode方法得到元素的哈希值 ,然后通过元素的哈希值经过移位等运算,就可以算出该元素在哈希表中的存储位置。会有以下两种情况:
① 情况1:如果算出元素存储的位置目前没有任何元素存储,那么该元素可以直接存储到该位置上。
② 情况2:如果算出该元素的存储位置目前已经存在有其他的元素了,那么会调用该元素的equals方法与该位置的元素再比较一次,如果equals返回的是true,那么该元素与这个位置上的元素就视为重复元素,不允许添加,如果equals方法返回的是false,那么该元素运行 添加。

(4) 分析:


① 当它存储狗娃的时候,HashSet就会调用狗娃的hashCode()方法,得到一个哈希码值,一个哈希码值相当于一个对象的内存地址,经过HashSet一些特有的算法之后,它就可以算出狗娃应该存储在哪一个位置上,接着算出狗剩和铁蛋应该存储的位置,再存储狗娃的时候,这个狗娃和上一个狗娃的HashCode码值并不一致,不一致的话算出来的位置也不一样,它的运算过程就是这样。
② 所以如果要让第二个狗娃存不进去,那么就要重写Person的hashCode方法,让这个狗娃和上一个狗娃算出的位置是一样的。

③ 每添加一个元素都会调用这个元素的hashCode方法。

④ 那么第一个狗娃存储的位置已经被占用,为什么第二个狗娃还可以存?那是因为哈希表的其中的一个特点是:桶式结构,它并不是一个萝卜一个坑,它是桶式结构,所以第一个元素进来后,第二个元素还可以进。一个位置可以存放有多个元素的。

⑤ 那么重写hashCode方法后为什么第二个狗娃还是可以存进去? 是因为调用第二个狗娃的equals方法和第一个狗娃比较的话,因为没有重写equals方法,比较的是这两个对象的内存地址,所以比较结果返回的是false,所以还是可以添加进去,所以要想使第二个狗娃添加不进去,还要重写equals方法,

⑥ 综上所述,所以会调用4次hashCode方法,调用1次equals方法。

3.疑问:为什么HashSet的存取速度快?

答:因为它是通过一个元素的内存地址来算出它的存储位置的,就像学校教室给学生安排座位表,说出一个学生的名字就可以给它根据一定的算法安排一个座位;同理,比如有人来教室找人,只要说出这个人的名字算出的位置也是一样的。

4.实例一

(1) 需求:接受键盘录入用户名与密码,如果用户名与密码已经存在集合中,那么就是视为重复元素,不允许添加到HashSet中。

(2) 实例:

class User{
	String userName;
	String password;
	public User(String userName, String password) {
		super();
		this.userName = userName;
		this.password = password;
	}
	
	@Override
	public String toString() {
		return "{ 用户名:"+this.userName+" 密码:"+ this.password+"}";
	}

	@Override
	public boolean equals(Object obj) {
		User user = (User)obj;
		return this.userName.equals(user.userName)&&this.password.equals(user.password);
	}
	
	@Override
	public int hashCode() { 
		return userName.hashCode()+password.hashCode();
	}
}

public class Demo3 {
	public static void main(String[] args) {
		Scanner scanner = new Scanner(System.in);
		HashSet set = new HashSet();
		while(true){
			System.out.println("请输入用户名:");
			String userName = scanner.next();
			System.out.println("请输入密码:");
			String password = scanner.next();
			//创建一个对象
			User user = new User(userName, password);
			if(set.add(user)){
				System.out.println("注册成功...");
				System.out.println("当前的用户有:"+ set);
			}else{
				System.out.println("注册失败...");
			}
		}
	}
}

(3) 运行结果:


5.实例二

(1) 实例:

public class Demo4 {
	public static void main(String[] args) {
		String str1 = "hello";
		String str2 = new String("hello");
		System.out.println("两个是同一个对象吗?"+(str1==str2));
		System.out.println("str1的hashCode:"+ str1.hashCode());
		System.out.println("str2的hashCode:"+ str2.hashCode());
	}
}

(2) 运行结果:


(3) 疑问:为什么不同对象的hashCode有可能会相同?

答:因为hashCode默认情况下表示的是内存地址,String类已经重写了Object的hashCode方法了。
注意: 如果两个字符串的内容一致,那么返回的hashCode 码肯定也会一致的。 

九.TreeSet类

1.TreeSet:如果元素具备自然顺序的特性,那么就按照元素自然顺序的特性进行排序存储。

2.疑问:什么是自然顺序?

答:自然顺序就是abcd,1234,只要往treeSet中存储,treeSet就会自动帮你排序存储。我们一输出tree的时候,结果就是排好序的数据。

3.实例:

(1) 实例一:

public class Demo3 {
	public static void main(String[] args) {
		TreeSet tree = new TreeSet();
		tree.add(1);
		tree.add(10);
		tree.add(7);
		tree.add(19);
		tree.add(9);
		System.out.println(tree);
	}
}

运行结果如下图所示:


(2) 实例二:

public class Demo4 {
	public static void main(String[] args) {
		TreeSet tree = new TreeSet();
		tree.add('b');
		tree.add('f');
		tree.add('a');
		tree.add('c');
		System.out.println(tree);
	}
}

运行结果如下图所示:


4.TreeSet中添加自定义元素

(1) 以下程序运行后会报错,因为TreeSet会对里面的元素进行排序,但是目前我们并没有告诉TreeSet按照什么来排序,并没有告诉它要比较的是什么。

//添加自定义元素
class  Emp1{
	int id;
	String name;
	int salary;
	public Emp1(int id, String name, int salary) {
		this.id = id;
		this.name = name;
		this.salary = salary;
	}
	
	@Override
	public String toString() {
		return "{ 编号:"+  this.id+" 姓名:"+ this.name+" 薪水:"+ this.salary+"}";
	}
}
public class Demo5 {
	public static void main(String[] args) {
		TreeSet tree = new TreeSet();
		tree.add(new Emp1(110, "张三", 100));
		tree.add(new Emp1(113, "李四", 200));
		tree.add(new Emp1(220, "王五", 300));
		tree.add(new Emp1(120, "狗娃", 500));
		System.out.println("集合的元素:" + tree);
	}
}

运行结果如下图所示:


(2) treeSet添加自定义元素要注意的事项:
① 往TreeSet添加元素的时候,如果元素本身具备了自然顺序的特性,那么就按照元素自然顺序的特性进行排序存储。
② 往TreeSet添加元素的时候,如果元素本身不具备自然顺序的特性,那么该元素所属的类必须要实现Comparable接口,把元素的比较规则定义在compareTo(T o)方法上。 
③ 如果比较元素的时候,compareTo方法返回的是0,那么该元素就被视为重复元素,不允许添加.(注意:TreeSet与HashCode、equals方法是没有任何关系。它依赖的是自己的比较规则compareTo)
④ 往TreeSet添加元素的时候, 如果元素本身没有具备自然顺序的特性,而元素所属的类也没有实现Comparable接口,那么必须要在创建TreeSet的时候传入一个比较器。
⑤ 往TreeSet添加元素的时候,如果元素本身不具备自然顺序的特性,而元素所属的类已经实现了Comparable接口, 在创建TreeSet对象的时候也传入了比较器那么是以比较器的比较规则优先使用。

(3) 如何自定义定义比较器: 自定义一个类实现Comparator接口即可,把元素与元素之间的比较规则定义在compare方法内即可。
自定义比较器的格式 :

class  类名  implements Comparator{
 			
}

(4) 推荐使用:使用比较器(Comparator)。 
因为如果使用Comparable的话,它的比较规则只能在这个类里面使用,如果用Comparator的话,不仅在这个类里可以用,在其他地方也可以使用。可提高复用性。

(5) 实例:

① 实现Comparable接口

/*
compareTo方法中如果返回的是负整数,那么就是小于
compareTo方法中如果返回的是零,那么就是等于
compareTo方法中如果返回的是正整数,那么就是大于
返回的是负整数、零或正整数,分别表示此对象是小于、等于还是大于指定对象。 
 */
//添加自定义元素
class  Emp2 implements Comparable<Emp2>{
	int id;
	String name;
	int salary;
	public Emp2(int id, String name, int salary) {
		this.id = id;
		this.name = name;
		this.salary = salary;
	}
	
	@Override
	public String toString() {
		return "{ 编号:"+  this.id+" 姓名:"+ this.name+" 薪水:"+ this.salary+"}";
	}
	//元素与元素之间的比较规则定义在compareTo方法上。
	@Override 
	// 返回的是负整数、零或正整数,根据此对象是小于、等于还是大于指定对象。 
	public int compareTo(Emp2 o) {
		Emp2 e = (Emp2) o;
		return this.salary- e.salary;
	}	
}
public class Demo6 {
	public static void main(String[] args) {
		// 创建TreeSet的时候传入比较器
		TreeSet tree = new TreeSet();
		tree.add(new Emp2(110, "张三", 100));
		tree.add(new Emp2(113, "李四", 200));
		tree.add(new Emp2(220, "王五", 300));
		tree.add(new Emp2(120, "狗娃", 500));
		System.out.println("集合的元素:" + tree);
	}
}

运行结果如下图所示:


② 自定义一个比较器

class  Emp5 {
	int id;
	String name;
	int salary;
	public Emp5(int id, String name, int salary) {
		this.id = id;
		this.name = name;
		this.salary = salary;
	}
	
	@Override
	public String toString() {
		return "{ 编号:"+  this.id+" 姓名:"+ this.name+" 薪水:"+ this.salary+"}";
	}
}
//自定义一个比较器
class MyComparator implements Comparator{
	//根据第一个参数小于、等于或大于第二个参数分别返回负整数、零或正整数。 
	@Override
	public int compare(Object o1, Object o2) {
		Emp5 e1 = (Emp5) o1;
		Emp5 e2 = (Emp5) o2;
		return e1.id - e2.id;
	}
}
public class Demo9 {
	public static void main(String[] args) {	
		// 创建一个比较器对象
		MyComparator comparator = new MyComparator();
		// 创建TreeSet的时候传入比较器
		TreeSet tree = new TreeSet(comparator);
		tree.add(new Emp5(110, "张三", 100));
		tree.add(new Emp5(113, "李四", 200));
		tree.add(new Emp5(220, "王五", 300));
		tree.add(new Emp5(120, "狗娃", 500));
		System.out.println("集合的元素:" + tree);
	}
}

运行结果如下图所示:


5.TreeSet的存储原理

(1) 实例:

class  Emp3 implements Comparable<Emp3>{
	int id;
	String name;
	int salary;
	public Emp3(int id, String name, int salary) {
		this.id = id;
		this.name = name;
		this.salary = salary;
	}
	
	@Override
	public String toString() {
		return "{ 编号:"+  this.id+" 姓名:"+ this.name+" 薪水:"+ this.salary+"}";
	}
	//元素与元素之间的比较规则定义在compareTo方法上。
	@Override 
	// 返回的是负整数、零或正整数,根据此对象是小于、等于还是大于指定对象。 
	public int compareTo(Emp3 o) {
		Emp3 e = (Emp3) o;
		System.out.println(this.name+"compare"+ e.name);//输出比较的过程
		return this.salary- e.salary;
	}	
}
public class Demo7 {
	public static void main(String[] args) {
		// 创建TreeSet的时候传入比较器
		TreeSet tree = new TreeSet();
		tree.add(new Emp3(110, "张三", 200));
		tree.add(new Emp3(113, "李四", 300));
		tree.add(new Emp3(220, "王五", 100));
		tree.add(new Emp3(120, "狗娃", 500));
		System.out.println("集合的元素:" + tree);
	}
}

(2) 运行结果:


(3) 分析过程:


① 首先张三是第一个添加的元素,没人和他比,那么他就自己和自己比较,现在张三就是树根元素,实际上这一步可以省略掉。
② 当添加李四的时候,首先要和树根元素比较,如果它比树根元素小那么就放在树根元素的左边,如果它比树根元素大就放在树根元素的右边,所以李四和张三比较一次,李四放在张三节点的右边。
③ 当添加王五的时候,它也会从树根进来,先和树根元素进行比较,发现它比树根元素小,那么就放在树根元素的左边,所以王五放在张三的左边。
④ 如果我们添加tree.add(new Emp(320, "狗剩", 500));这时它就会被视为重复元素,因为compareTo方法返回的是0,在TreeSet中它不是根据hashCode也不是根据equals方法来判断是否为重复元素的,它是依靠compareTo方法如果返回0就视为重复元素。这时候狗剩就无法添加进去。

⑤ 当添加狗娃的时候,它也会从树根进来,先和树根元素进行比较,它比树根元素大,那么就放在张三的右边,在右边又遇到李四,所以还需要和李四比较一次,比李四大,放在李四的右边。

(4) 二叉树算法自动调整节点实例:





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

处女座的码农

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值