API-集合
集合类概述
1.集合的由来:
我们学习的是面向对象语言,而面向对象语言对事物的描述是通过对象体现的,为了方便对多个对象进行操作,我们就必须把这多个对象进行存储。而要想存储多个对象,就不能是一个基本的变量,而应该是一个容器类型的变量,目前所学过的容器类型是数组和StringBuffer。但是StringBuffer的结果是一个字符串,不一定满足我们的要求,所以我们只能选择数组,这就是对象数组。而对象数组又不能适应变化的需求,因为数组的长度是固定的,这个时候,为了适应变化的需求,Java就提供了集合类供我们使用。
2.对象数组内存图解
3.数组和集合类的区别:
● A:长度区别:
数组的长度固定
集合长度可变
● B:内容不同:
一个数组存储的是同一种类型的元素
而一个集合可以存储不同类型的元素
● C:元素的数据类型问题:
数组可以存储基本数据类型,也可以存储引用数据类型
集合只能存储对象
4.集合类的特点:
集合只用于存储对象,集合长度是可变的,集合可以存储不同类型的对象。
5.集合的继承体系及图解:
集合是存储多个元素的,而我们需要对这些元素进行不同的操作,为了针对不同的需求,Java就提供了不同的集合类。这多个集合类的数据结构(数据的存储方式)不同,结构不同不重要的,重要的是能够存储元素,并且还要能够使用这些元素。而这多个集合类是有共性的内容的,把这些集合类的共性内容不断的向上提取,最终就能形成集合的继承体系结构。
Collection集合
Collection接口概述
Collection 层次结构中的顶层接口。Collection 表示一组对象,这些对象也称为 collection 的元素。一些 collection 允许有重复的元素,而另一些则不允许。一些 collection 是有序的,而另一些则是无序的。
Collection接口的成员方法
1.添加功能:
● boolean add(Object obj):添加一个元素
● boolean addAll(Collection c):添加一个集合的元素
2.删除功能:
● void clear():移除所有元素
● boolean remove(Object o):移除一个元素
● boolean removeAll(Collection c):移除一个集合的元素,只要有一个元素被移除了,就返回true
3.判断功能:
● boolean contains(Object o):判断集合中是否包含指定的元素
● boolean containsAll(Collection c):判断集合中是否包含指定的集合元素,只有包含所有的元素,才叫包含
● boolean isEmpty():判断集合是否为空
4.获取功能:
● Iterator iterator()(重点)
5.长度功能:
● int size():元素的个数(相当于Length()方法)
6.交集功能:
● boolean retainAll(Collection c):两个集合都有的元素。哪个集合调用交集,元素就去那个集合(A),参数列表里的集合元素(B)不变,返回的boolean代表那个集合(A)元素是否发生过改变,没变过返回false,变过返回true
7.把集合转换为数组:
● Object[] toArray()
//代码体现
import java.util.ArrayList;
import java.util.Collection;
public class CollectionDemo {
public static void main(String[] args) {
// 测试不带All的方法
// 创建集合对象
// Collection c = new Collection(); //错误,因为接口不能实例化
Collection c = new ArrayList();
// boolean add(Object obj):添加一个元素
// 由ArrayList()源码可知这个boolean始终返回true,说明可以往ArrayList()添加重复的元素,
//也就是无需判断也可以
// System.out.println("add:"+c.add("hello"));
c.add("hello");
c.add("world");
c.add("java");
// void clear():移除所有元素
// c.clear();
// boolean remove(Object o):移除一个元素
// System.out.println("remove:" + c.remove("hello"));
// 删除集合不存在的元素,返回false
// System.out.println("remove:" + c.remove("javaee"));
// boolean contains(Object o):判断集合中是否包含指定的元素
// System.out.println("contains:"+c.contains("hello"));
// 判断集合不存在的元素,返回false
// System.out.println("contains:"+c.contains("android"));
// boolean isEmpty():判断集合是否为空
// System.out.println("isEmpty:"+c.isEmpty());
//int size():元素的个数
System.out.println("size:"+c.size());
System.out.println("c:" + c);
}
}
//代码体现
import java.util.ArrayList;
import java.util.Collection;
/*
* boolean addAll(Collection c):添加一个集合的元素
* boolean removeAll(Collection c):移除一个集合的元素,只要有一个元素被移除了,就返回true
* boolean containsAll(Collection c):判断集合中是否包含指定的集合元素,只有包含所有的元素,才叫包含
* boolean retainAll(Collection c):两个集合都有的元素?思考元素去哪了,返回的boolean又是什么意思呢?
*/
public class CollectionDemo2 {
public static void main(String[] args) {
// 创建集合1
Collection c1 = new ArrayList();
c1.add("abc1");
c1.add("abc2");
c1.add("abc3");
c1.add("abc4");
// 创建集合2
Collection c2 = new ArrayList();
// c2.add("abc1");
// c2.add("abc2");
// c2.add("abc3");
// c2.add("abc4");
c2.add("abc5");
c2.add("abc6");
c2.add("abc7");
// boolean addAll(Collection c):添加一个集合的元素
// 重复的也会添加进去
// System.out.println("addAll:" + c1.addAll(c2));
//boolean removeAll(Collection c):移除一个集合的元素
//只要有一个元素被移除了,就返回true。
//System.out.println("removeAll:"+c1.removeAll(c2));
//boolean containsAll(Collection c):判断集合中是否包含指定的集合元素(是一个还是所有)
//只有包含所有的元素,才叫包含
// System.out.println("containsAll:"+c1.containsAll(c2));
//boolean retainAll(Collection c):两个集合都有的元素,思考元素去哪了,返回的boolean又是什么意思呢?
//假设有两个集合A,B。
//A对B做交集,最终的结果保存在A中,B不变。
//返回值表示的是A是否发生过改变。
System.out.println("retainAll:"+c1.retainAll(c2));
System.out.println("c1:" + c1);
System.out.println("c2:" + c2);
}
}
遍历Collection集合
Object[] toArray()
//代码体现
import java.util.ArrayList;
import java.util.Collection;
/*
* 集合的遍历。其实就是依次获取集合中的每一个元素。
*
* Object[] toArray():把集合转成数组,可以实现集合的遍历
*/
public class CollectionDemo3 {
public static void main(String[] args) {
// 创建集合对象
Collection c = new ArrayList();
// 添加元素
c.add("hello"); // 这里相当于多态中的向上转型 Object obj = "hello";
c.add("world");
c.add("java");
System.out.println(c.size());
// 遍历
// Object[] toArray():把集合转成数组,可以实现集合的遍历
Object[] objs = c.toArray();
for (int x = 0; x < objs.length; x++) {
// 这里相当于调了Object中的toString()方法
// System.out.println(objs[x]);
// 我知道元素是字符串,我在获取到元素的的同时,还想知道元素的长度。
// System.out.println(objs[x] + "---" + objs[x].length());
// 上面的实现不了,原因是Object中没有length()方法
// 我们要想使用字符串的方法,就必须把元素还原成字符串
// 向下转型
String s = (String) objs[x];
System.out.println(s + "---" + s.length());
}
}
}
Iterator iterator()
1.Iterator iterator():迭代器,集合的专用遍历方式。迭代器是依赖于集合而存在的
● Iterator接口方法:Object next():获取元素,并移动到下一个位置。
注意:NoSuchElementException:没有这样的元素,超出所拥的有元素范围。
● Iterator接口方法:boolean hasNext():如果仍有元素可以迭代,则返回 true。
问题1:遍历能用while循环写,也能用for循环,好处是用完后迭代器就是垃圾,坏处是结构不清晰
问题2:不要多次使用it.next()方法,因为每次使用都是访问一个对象。
//代码体现
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
/*
* Iterator iterator():迭代器,集合的专用遍历方式
* Object next():获取元素,并移动到下一个位置。
* NoSuchElementException:没有这样的元素,因为你已经找到最后了。
* boolean hasNext():如果仍有元素可以迭代,则返回 true。(
*/
public class IteratorDemo {
public static void main(String[] args) {
// 创建集合对象
Collection c = new ArrayList();
// 创建并添加元素
c.add("hello");
c.add("world");
c.add("java");
// Iterator iterator():迭代器,集合的专用遍历方式
Iterator it = c.iterator(); // 实际返回的肯定是子类对象,这里是多态
// 在每次获取前,先判断是否有下一个元素,有就获取,没有就不理它
while (it.hasNext()) {
// System.out.println(it.next());
String s = (String) it.next();
System.out.println(s);
// NoSuchElementException 不要多次使用it.next()方法
// System.out.println(((Student) it.next()).getName() + "---"
// + ((Student) it.next()).getAge());
}
// for循环改写
// for(Iterator it = c.iterator();it.hasNext();){
// Student s = (Student) it.next();
// System.out.println(s.getName() + "---" + s.getAge());
// }
}
}
迭代器使用图解和原理解析
迭代器设置为接口的原因
因为Java中提供了很多的集合类,而这些集合类的数据结构是不同的,所以存储和遍历的方式也是不同的,如果迭代器定义成具体的子类那就会使实现的方法固定,所以就没有定义迭代器类。而无论是哪种集合的遍历,都应该具备获取元素的功能并辅以判断功能,这样在获取前先判断就更不容易出错。也就是说获取和判断功能应该是一个集合遍历所具备的,而每种集合遍历的方式又有所不同,所以就得把这两个功能提取出来,并且不提供具体实现,这就是接口。
真正的具体的实现类由真正的具体的子类实现,因为集合结构不同,只能由最低层的子类实现才能达到所有集合使用迭代器的目的,比如Colldection下面有List和Set,而List和Set下面又有各种子类,所以具体实现都不在这三个中实现,而是在下面的各种子类(比如下面源码的ArrayList)中实现,而因为迭代器接口只设置了一个方法,而其子类想同时实现获取和判断功能,那只重写一个方法肯定实现不了,所以就得在该具体子类中设置内部类,才能同时创建多个方法。
如下部分源码体现
// Inteator接口
public interface Inteator {
boolean hasNext();
Object next();
}
// Iterable接口
public interface Iterable {
// 创建一个方法,返回值为Iterator
Iterator iterator();
}
public interface Collection extends Iterable {
// 重写Iterable接口的iterator()方法,但是不提供具体实现
public abstract Iterator iterator();
}
public interface List extends Collection {
// 重写Collection接口的iterator()方法,但是不提供具体实现
public abstract Iterator iterator();
}
public class ArrayList implements List {
// 重写List接口的iterator()方法,具体子类提供具体实现,编译看左边,运行看右边
// 返回值是Iterator接口,说明需要其具体实现类,通过内部类实现
public Iterator iterator() {
// 返回内部类对象,在内部类实现两个方法
return new Itr();
}
// 内部类私有
private class Itr implements Iterator {
public boolean hasNext() {}
public Object next(){}
}
}
Collection c = new ArrayList();
c.add("hello");
c.add("world");
c.add("java");
Iterator it = c.iterator(); //new Itr();
while(it.hasNext()) {
String s = (String)it.next();
System.out.println(s);
}
补充:在写代码时应注意这两个问题
注意:
● A:自己的类名不要和API中的类名相同。
● B:在Eclipse上复制代码的时候,很容易把那个类所在的包也导入过来,容易出现不能理解的问题。
集合的遍历步骤
1.创建集合对象
2.创建元素对象
3.把元素对象添加到集合中
4.遍历集合
● 通过集合对象获取迭代器对象
● 通过迭代器对象的hasNext()方法判断是否有元素
● 通过迭代器的next()方法获取元素并移动到下一个位置
List集合
List接口概述
● 有序的(存储和取出一致) collection(也称为序列)。此接口的用户可以对列表中每个元素的插入位置进行精确地控制。用户可以根据元素的整数索引(在列表中的位置)访问元素,并搜索列表中的元素。
● 与 set 不同,列表通常允许重复的元素。
List接口的成员方法
1.添加功能:
● void add(int index,Object element):在指定位置添加元素
2.获取功能:
● Object get(int index):获取指定位置的元素
3.删除功能:
● Object remove(int index):根据索引删除元素,返回被删除的元素
4.修改功能:
● Object set(int index,Object element):根据索引修改元素,返回被修改的元素
//代码体现
import java.util.ArrayList;
import java.util.List;
public class ListDemo {
public static void main(String[] args) {
// 创建集合对象
List list = new ArrayList();
// 创建元素对象并把元素添加到集合
list.add("张三");
list.add("李四");
list.add("王五");
// 添加功能:void add(int index,Object element):在指定位置添加元素
list.add(1, "赵六");
// 超过索引:IndexOutOfBoundsException
// list.add(5, "孙七");
// 可以在王五后面
list.add(4, "孙七");
// 获取功能:Object get(int index):获取指定位置的元素
list.get(0);
// 超过索引:IndexOutOfBoundsException
// list.get(5);
// 删除功能:Object remove(int index):根据索引删除元素,返回被删除的元素
list.remove(1);
// 超过索引:IndexOutOfBoundsException
// list.get(5);
// 修改功能:Object set(int index,Object element):根据索引修改元素,返回被修改的元素
System.out.println(list.set(3, "赵六"));
// 输出集合:
System.out.println(list);
}
}
List集合的遍历功能
1.size()和get()方法结合使用:
2.迭代器遍历
//代码体现
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class ListDemo2 {
public static void main(String[] args) {
// 创建集合对象
List list = new ArrayList();
// 添加元素
list.add("张三");
list.add("李四");
list.add("王五");
// size()和get()方法结合使用
for (int x = 0; x < list.size(); x++) {
String s = (String) list.get(x);
System.out.println(s);
}
// 迭代器遍历
Iterator it = list.iterator();
while (it.hasNext()) {
String s = (String) it.next();
System.out.println(s);
}
}
}
5.列表迭代器:
● ListIterator listIterator():List集合特有的迭代器。该迭代器继承了Iterator迭代器,所以,就可以直接使用hasNext()和next()方法。
● ListIterato接口的特有功能:
Object previous():获取上一个元素
boolean hasPrevious():判断是否有元素
注意:这两个方法主要实现逆向遍历,但是必须先正向遍历,因为迭代器指针得先移动到最后的位置,然后才能逆向遍历,所以一般无意义,不使用。
//代码体现
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
public class ListDemo {
public static void main(String[] args) {
// 创建集合对象
List list = new ArrayList();
// 添加元素
list.add("张三");
list.add("李四");
list.add("王五");
// ListIterator listIterator()
ListIterator lit = list.listIterator(); // 子类对象
while (lit.hasNext()) {
String s = (String) lit.next();
System.out.println(s);
}
while (lit.hasPrevious()) {
String s = (String) lit.previous();
System.out.println(s);
}
}
}
List三个子类的特点
1.ArrayList:
● 底层数据结构是数组,查询快,增删慢。
● 线程不安全,效率高。
2.Vector:(基本不用)
● 底层数据结构是数组,查询快,增删慢。
● 线程安全,效率低。
3.LinkedList:
● 底层数据结构是链表,查询慢,增删快。
● 线程不安全,效率高。
4.使用优先级
● 要安全:Vector。(即使要安全,也不用这个了,后面有替代的)
● 不要安全:查询多用ArrayList,增删多用LinkedList
如果什么都不懂,就用ArrayList。
ArrayList的几个案例
1.去除ArrayList集合中字符串内容相同的值(思路一:创建新集合比较)
//代码体现
import java.util.ArrayList;
import java.util.Iterator;
public class ArrayListDemo {
public static void main(String[] args) {
// 创建集合对象
ArrayList array = new ArrayList();
// 创建元素对象并添加
array.add("张三");
array.add("李四");
array.add("王五");
array.add("李四");
array.add("王五");
array.add("王五");
array.add("李四");
// 创建一个空的新的集合
ArrayList newArray = new ArrayList();
// 遍历旧集合,把旧集合的元素添加到新集合,添加前先判断也没有相同元素的,有就不添加
Iterator it = array.iterator();
while (it.hasNext()) {
String s = (String) it.next();
if (!newArray.contains(s)) {
newArray.add(s);
}
}
// 遍历新集合
for (int x = 0; x < newArray.size(); x++) {
String s = (String) newArray.get(x);
System.out.println(s);
}
}
}
2.去除ArrayList集合中字符串内容相同的值且不能创建新集合(思路二:使用选择排序的思想,把前面的值与后面的一一比较,有相同的值就删除)
import java.util.ArrayList;
import java.util.Iterator;
public class ArrayListDemo {
public static void main(String[] args) {
// 创建集合对象
ArrayList array = new ArrayList();
// 创建元素对象并添加
array.add("张三");
array.add("李四");
array.add("王五");
array.add("李四");
array.add("王五");
array.add("王五");
array.add("李四");
// 使用选择排序的思想
for (int x = 0; x < array.size() - 1; x++) {
for (int y = x + 1; y < array.size(); y++) {
// 把前面的值与后面的一一比较
if (array.get(x).equals(array.get(y))) {
// 有相同的值就删除
array.remove(y);
// 注意:删除后,下面的元素会顶替删除元素的位置,如果是连续相同的元素就会被漏掉,所以要y--保证长度不变
y--;
}
}
}
// 遍历集合
Iterator it = array.iterator();
while (it.hasNext()) {
String s = (String) it.next();
System.out.println(s);
}
}
}
3.去除集合中自定义对象的重复值(对象的成员变量值都相同)
//代码体现
import java.util.ArrayList;
import java.util.Iterator;
//学生类
class Student {
private int age;
private String name;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Student(int age, String name) {
super();
this.age = age;
this.name = name;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Student other = (Student) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
/*
* 按照和字符串一样的操作,结果所有对象都进去了新集合 通过简单的分析,了解问题出现在了contains()方法判断上。
* 而这个判断功能是集合自己提供的,所以应该需要知道它是如何判断的。 通过看源码可知contains()方法的底层依赖的是equals()方法。
* 而如果学生类中没有equals()方法,则默认使用的是父类Object的equals()方法
* 而Object()的equals()默认比较的是地址值,所以,所有对象都进去了。因为new的东西,地址值都不同。
* 所以要按照需求,比较成员变量的值,就得重写equals()。
*/
public class ArrayListDemo3 {
public static void main(String[] args) {
// 创建集合对象
ArrayList array = new ArrayList();
// 创建学生对象
Student s1 = new Student(20, "张三");
Student s2 = new Student(25, "李四");
Student s3 = new Student(30, "王五");
Student s4 = new Student(20, "张三");
Student s5 = new Student(25, "张三");
Student s6 = new Student(30, "王五");
// 添加元素
array.add(s1);
array.add(s2);
array.add(s3);
array.add(s4);
array.add(s5);
array.add(s6);
// 创建一个空的新的集合
ArrayList newArray = new ArrayList();
// 遍历旧集合,把旧集合的元素添加到新集合,添加前先判断也没有相同元素的,有就不添加
Iterator it = array.iterator();
while (it.hasNext()) {
Student s = (Student) it.next();
if (!newArray.contains(s)) {
newArray.add(s);
}
}
// 遍历新集合
for (int x = 0; x < newArray.size(); x++) {
Student s = (Student) newArray.get(x);
System.out.println(s.getName() + "---" + s.getAge());
}
}
}
Vector类的特有功能
1.添加功能:
● public void addElement(Object obj):后被add()替代
2.获取功能:
● public Object elementAt(int index):后被get()替代
● public Enumeration elements():后被Iterator iterator()替代
boolean hasMoreElements():后被hasNext()替代
Object nextElement():后被next()替代
补充:JDK升级的原因:
A:为了更安全
B:为了提高效率
C:简化书写
//代码体现
import java.util.Enumeration;
import java.util.Vector;
public class VectorDemo {
public static void main(String[] args) {
// 创建集合对象
Vector v = new Vector();
// 添加功能
v.addElement("张三");
v.addElement("李四");
v.addElement("王五");
// 遍历
for (int x = 0; x < v.size(); x++) {
String s = (String) v.elementAt(x);
System.out.println(s);
}
Enumeration en = v.elements(); // 返回的是实现类的对象
while (en.hasMoreElements()) {
String s = (String) en.nextElement();
System.out.println(s);
}
}
}
LinkedList的特有功能
1.添加功能
● public void addFirst(Object e):添加第一个元素
● public void addLast(Object e):添加最后一个元素
2.获取功能
● public Object getFirst():获取第一个元素
● public Obejct getLast():获取最后一个元素
3.删除功能
● public Object removeFirst():移除第一个元素,并返回该元素
● public Object removeLast():移除最后一个元素,并返回该元素
//代码体现
import java.util.LinkedList;
public class LinkedListDemo {
public static void main(String[] args) {
// 创建集合对象
LinkedList link = new LinkedList();
// 添加元素
link.add("张三");
link.add("李四");
link.add("王五");
// public void addFirst(Object e):添加第一个元素
link.addFirst("陈二");
// public void addLast(Object e):添加最后一个元素
link.addLast("赵六");
// public Object getFirst():获取第一个元素
System.out.println("getFirst:" + link.getFirst());
// public Obejct getLast():获取最后一个元素
System.out.println("getLast:" + link.getLast());
// public Object removeFirst():移除第一个元素,并返回该元素
System.out.println("removeFirst:" + link.removeFirst());
// public Object removeLast():移除最后一个元素,并返回该元素
System.out.println("removeLast:" + link.removeLast());
// 输出对象名
System.out.println("link:" + link);
}
}
LinkedList案例
用LinkedList模拟栈数据结构的集合,并测试
意思是:定义一个集合类,在这个集合类内部可以使用LinkedList模拟。
import java.util.LinkedList;
//自定义的栈集合
class MyStack {
// 定义一个LinkedList的私有变量
private LinkedList link;
// 定义构造方法,把LinkedList对象赋值给私有变量link,默认调用LinkedList对象
public MyStack() {
link = new LinkedList();
}
// 定义一个添加元素方法,没次都是添加在首位
public void add(Object obj) {
link.addFirst(obj);
}
// 定义一个获取元素的方法
public Object get() {
// 这个不行,因为每次都只会获取第一个
// return link.getFirst();
// 获取完第一个后就删除
return link.removeFirst();
}
// 定义一个判断功能
public boolean isEmpty() {
return link.isEmpty();
}
}
// MyStack的测试
public class MyStackDemo {
public static void main(String[] args) {
// 创建集合对象
MyStack ms = new MyStack();
// 添加元素
ms.add("张三");
ms.add("李四");
ms.add("王五");
// 先判断是否越界,再获取元素
while(!ms.isEmpty()){
System.out.println(ms.get());
}
}
}
集合遍历之并发修改异常
ConcurrentModificationException:当方法检测到对象的并发修改,但不允许这种修改时,抛出此异常。
1.产生的原因:
● 迭代器是依赖于集合而存在的,迭代器被造出来后它所要遍历的集合已经固定,而如果在判断成功(比如while中的hasNext()判断成功)后,往该集合中添加了一个新元素,而此时的迭代器还是默认访问以前的集合,并不知道添加了新元素,所以报错。这个错叫并发修改异常。
● 这个问题描述的是:在迭代时,不可以通过集合对象的方法操作集合中的元素,因为会发生并发修改异常。所以在迭代时,只能用迭代器的方法操作元素,可是Iterator方法是有限的,只能对元素进行判断、取出、删除操作,如果想要其他的操作,比如添加,修改等,就需要使用其子接口ListIterator,该接口只能通过List集合的ListIterator方法获取。
2.解决办法:
A:迭代器迭代元素,迭代器修改元素。而Iterator迭代器却没有添加功能,所以使用其子接口ListIterator
● 元素是跟在迭代的元素后面的。
B:不用迭代器,集合遍历元素,集合修改元素(普通for)
● 元素在最后添加的。
//代码体现
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
public class ListIteratorDemo {
public static void main(String[] args) {
// 创建List集合对象
List list = new ArrayList();
// 添加元素
list.add("张三");
list.add("李四");
list.add("王五");
// 迭代器遍历,并发修改异常
// Iterator it = list.iterator();
// while (it.hasNext()) {
// String s = (String) it.next();
// 在张三后添加元素赵六
// if ("张三".equals(s)) {
// list.add("赵六");
// }
// }
// 方式1:迭代器迭代元素,迭代器修改元素
// 而Iterator迭代器却没有添加功能,所以使用其子接口ListIterator
// ListIterator lit = list.listIterator();
// while (lit.hasNext()) {
// String s = (String) lit.next();
// 在张三后添加元素赵六
// if ("张三".equals(s)) {
// lit.add("赵六");
// }
// }
// 方式2:集合遍历元素,集合修改元素(普通for)
for (int x = 0; x < list.size(); x++) {
String s = (String) list.get(x);
// 在最后添加元素赵六
if ("张三".equals(s)) {
list.add("赵六");
}
}
System.out.println("list:" + list);
}
}
泛型
1.泛型由来:早期的Object类型可以接收任意的对象类型,向上转型是没有任何问题的,但是在向下转型的时候隐含了类型转换的问题。比如在ArrayList案例中定义的集合元素都是同一种类型,在遍历的时候也是以该类型遍历,但是如果集合中有其他类型数据,控制台就会报错,所以Java在JDK5后提供了泛型来解决这个安全问题。
2.泛型:是一种把类型明确的工作推迟到创建对象或者调用方法的时候才去明确的特殊的类型。参数化类型,把类型当作参数一样的传递。(即明确告诉你该集合为什么类型,当添加了不是该集合类型的元素jvm就会在编译器报错,没添加泛型的则运行时才报错)
3.格式:<数据类型>
● 此处的数据类型必须是引用类型。
4.好处:
● 把运行时期的问题提前到了编译期间
● 避免了强制类型转换
● 优化了程序设计,解决了黄色警告线
5. 泛型的使用:看API,如果类,接口,抽象类后面有跟< E >就说明要使用泛型。一般来说就是在集合中使用。
public class ArrayListDemo2 {
public static void main(String[] args) {
// 创建集合对象
// JDK7的新特性:泛型推断。根据前面推出后面,建议不用。
// ArrayList<Student> array = new ArrayList<>();
ArrayList<Student> array = new ArrayList<Student>();
// 创建元素对象
Student s1 = new Student("张三", 20);
Student s2 = new Student("李四", 25);
Student s3 = new Student("王五", 30);
// 添加元素
array.add(s1);
array.add(s2);
array.add(s3);
// 遍历
Iterator<Student> it = array.iterator();
while (it.hasNext()) {
// 避免了强制类型转换
Student s = it.next();
System.out.println(s.getName() + "---" + s.getAge());
}
System.out.println("------------------");
for (int x = 0; x < array.size(); x++) {
Student s = array.get(x);
System.out.println(s.getName() + "---" + s.getAge());
}
}
}
泛型应用
泛型类
1.泛型类:把泛型定义在类上
2.格式:public class 类名<泛型类型1,…>
● 注意:泛型类型必须是引用类型,在类中定义时因为不知道以后对象具体是用什么引用类型,所以类里面的泛型可以写任何东西来代替,一般是简单易懂的符号最好。如public class 类名< C >。C仅仅表示一个符号。
//代码体现
public class ObjectTool<T> {
private T obj;
public T getObj() {
return obj;
}
public void setObj(T obj) {
this.obj = obj;
}
}
泛型方法
1.泛型方法:把泛型定义在方法上,让方法可以接收任意类型数据,并且可以与类区分开
2.格式:public <泛型类型> 返回值类型 方法名(泛型类型 变量名)
//代码体现
public class ObjectTool {
public <T> void show(T t) {
System.out.println(t);
}
}
泛型接口
1.泛型接口:把泛型定义在接口上
2.格式:public interface 接口名<泛型类型1…>
泛型高级(通配符)
1.泛型通配符<?>
● 任意类型,如果没有明确,那么就是Object以及任意的Java类了
2.? extends E
● 向下限定,E及其子类
3.? super E
● 向上限定,E及其父类
//代码体现
import java.util.ArrayList;
import java.util.Collection;
/*
* 泛型高级(通配符)
* ?:任意类型,如果没有明确,那么就是Object以及任意的Java类了
* ? extends E:向下限定,E及其子类
* ? super E:向上限定,E及其父类
*/
public class GenericDemo {
public static void main(String[] args) {
// 泛型如果明确的写的时候,前后必须一致
Collection<Object> c1 = new ArrayList<Object>();
// Collection<Object> c2 = new ArrayList<Animal>();
// Collection<Object> c3 = new ArrayList<Dog>();
// Collection<Object> c4 = new ArrayList<Cat>();
// ?表示任意的类型都是可以的
Collection<?> c5 = new ArrayList<Object>();
Collection<?> c6 = new ArrayList<Animal>();
Collection<?> c7 = new ArrayList<Dog>();
Collection<?> c8 = new ArrayList<Cat>();
// ? extends E:向下限定,E及其子类
// Collection<? extends Animal> c9 = new ArrayList<Object>();
Collection<? extends Animal> c10 = new ArrayList<Animal>();
Collection<? extends Animal> c11 = new ArrayList<Dog>();
Collection<? extends Animal> c12 = new ArrayList<Cat>();
// ? super E:向上限定,E及其父类
Collection<? super Animal> c13 = new ArrayList<Object>();
Collection<? super Animal> c14 = new ArrayList<Animal>();
// Collection<? super Animal> c15 = new ArrayList<Dog>();
// Collection<? super Animal> c16 = new ArrayList<Cat>();
}
}
class Animal {
}
class Dog extends Animal {
}
class Cat extends Animal {
}
JDK5新特性
JDK5的新特性:自动拆装箱,泛型,增强for,静态导入,可变参数,枚举
增强for
1.增强for概述:简化数组和Collection集合的遍历
2.格式:
for(元素数据类型 变量 : 数组或者Collection集合) {
使用变量即可,该变量就是元素
}
3.好处:简化了数组和集合的遍历。
4.注意事项:增强for的目标要判断是否为null,即增强for是用来替迭代器。所以一般使用前要判断集合是否为null,如果集合为null就造迭代器对象使用就会报错
public class ForDemo {
public static void main(String[] args) {
// 定义一个int数组
int[] arr = { 1, 2, 3, 4, 5 };
// 增强for
for (int x : arr) {
System.out.println(x);
}
// 定义一个字符串数组
String[] strArray = { "张三", "李四", "王五", "赵六" };
// 增强for
for (String s : strArray) {
System.out.println(s);
}
// 定义一个集合
ArrayList<String> array = new ArrayList<String>();
array.add("张三");
array.add("李四");
array.add("王五");
// 增强for
for (String s : array) {
System.out.println(s);
}
List<String> list = null;
// NullPointerException
// NullPointerException
// 这个s是从list里面获取出来的,在获取前,它肯定还要做一个判断
// 由反编译知道这就是迭代器的功能,如果集合为null就造迭代器对象使用就会报错
if (list != null) {
for (String s : list) {
System.out.println(s);
}
}
// 增强for其实是用来替代迭代器的
//并发修改异常:ConcurrentModificationException
// for (String s : array) {
// if ("world".equals(s)) {
// array.add("javaee");
// }
// }
// System.out.println("array:" + array);
}
}
静态导入
1.静态导入概述:可以直接导入到方法的级别
2.格式:import static 包名….类名.方法名;
比如:import static java.lang.Math.abs;
使用:System.out.println(abs(-100));
3.注意事项:方法必须是静态的。如果有多个同名的静态方法,容易不知道该使用哪个,这个时候如果要使用,必须加前缀。由此可见,静态导入意义不大,所以一般不用,但是要能看懂。
比如:System.out.println(java.lang.Math.abs(-100));
可变参数
1.可变参数概述:定义方法的时候不清楚该定义多少个参数,而定义这个使用的时候则多少参数都可以
2.格式:修饰符 返回值类型 方法名(数据类型… 变量){}
3.注意:这里的变量其实是一个数组。如果一个方法的参数列表有可变参数和多个参数,那么,可变参数肯定要放在最后一个
//代码体现
public static int sum(int... a) {
int s = 0;
for(int x : a){
s +=x;
}
return s;
}
4.Arrays工具类中的一个方法
● public static List asList(T… a):把数组转成集合
● 注意事项:虽然可以把数组转成集合,但是集合的长度不能改变。
集合的几个案例
// 学生类
public class Student {
private String name;
private int age;
public Student() {
super();
}
public Student(String name, int age) {
super();
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
import java.util.ArrayList;
/*
* 集合的嵌套遍历
* 需求:把多个集合放到一个集合中遍历:ArrayList<ArrayList<Student>>
*/
public class ArrayListDemo {
public static void main(String[] args) {
// 创建大集合
ArrayList<ArrayList<Student>> bigArrayList = new ArrayList<ArrayList<Student>>();
// 创建第一个学生集合
ArrayList<Student> firstArrayList = new ArrayList<Student>();
// 创建学生
Student s1 = new Student("唐僧", 30);
Student s2 = new Student("孙悟空", 29);
Student s3 = new Student("猪八戒", 28);
Student s4 = new Student("沙僧", 27);
Student s5 = new Student("白龙马", 26);
// 学生进班
firstArrayList.add(s1);
firstArrayList.add(s2);
firstArrayList.add(s3);
firstArrayList.add(s4);
firstArrayList.add(s5);
// 把第一个班级存储到学生系统中
bigArrayList.add(firstArrayList);
// 创建第二个班级的学生集合
ArrayList<Student> secondArrayList = new ArrayList<Student>();
// 创建学生
Student s11 = new Student("诸葛亮", 30);
Student s22 = new Student("司马懿", 28);
Student s33 = new Student("周瑜", 26);
// 学生进班
secondArrayList.add(s11);
secondArrayList.add(s22);
secondArrayList.add(s33);
// 把第二个班级存储到学生系统中
bigArrayList.add(secondArrayList);
// 创建第三个班级的学生集合
ArrayList<Student> thirdArrayList = new ArrayList<Student>();
// 创建学生
Student s111 = new Student("宋江", 40);
Student s222 = new Student("吴用", 35);
Student s333 = new Student("高俅", 30);
Student s444 = new Student("李师师", 22);
// 学生进班
thirdArrayList.add(s111);
thirdArrayList.add(s222);
thirdArrayList.add(s333);
thirdArrayList.add(s444);
// 把第三个班级存储到学生系统中
bigArrayList.add(thirdArrayList);
// 遍历集合
for (ArrayList<Student> array : bigArrayList) {
for (Student s : array) {
System.out.println(s.getName() + "---" + s.getAge());
}
}
}
}
import java.util.ArrayList;
import java.util.Random;
/*
* 获取10个1-20之间的随机数,要求不能重复
*
* 用数组实现,但是数组的长度是固定的,长度不好确定。
* 所以我们使用集合实现。
*
* 分析:
* A:创建产生随机数的对象
* B:创建一个存储随机数的集合。
* C:定义一个统计变量。从0开始。
* D:判断统计遍历是否小于10
* 是:先产生一个随机数,判断该随机数在集合中是否存在。
* 如果不存在:就添加,统计变量++。
* 如果存在:就不搭理它。
* 否:不搭理它
* E:遍历集合
*/
public class RandomDemo {
public static void main(String[] args) {
// 创建产生随机数的对象
Random r = new Random();
// 创建一个存储随机数的集合。
ArrayList<Integer> array = new ArrayList<Integer>();
// 定义一个统计变量。从0开始。
int count = 0;
// 判断统计遍历是否小于10
while (count < 10) {
//先产生一个随机数
int number = r.nextInt(20) + 1;
//判断该随机数在集合中是否存在。
if(!array.contains(number)){
//如果不存在:就添加,统计变量++。
array.add(number);
count++;
}
}
//遍历集合
for(Integer i : array){
System.out.println(i);
}
}
}
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Scanner;
/*
* 键盘录入多个数据,以0结束,要求在控制台输出这多个数据中的最大值
*
* 分析:
* A:创建键盘录入数据对象
* B:键盘录入多个数据,我们不知道多少个,所以用集合存储
* C:以0结束,这个简单,只要键盘录入的数据是0,我就不继续录入数据了
* D:把集合转成数组
* E:对数组排序
* F:获取该数组中的最大索引的值
*/
public class ArrayListDemo {
public static void main(String[] args) {
// 创建键盘录入数据对象
Scanner sc = new Scanner(System.in);
// 键盘录入多个数据,我们不知道多少个,所以用集合存储
ArrayList<Integer> array = new ArrayList<Integer>();
// 以0结束,这个简单,只要键盘录入的数据是0,我就不继续录入数据了
while (true) {
System.out.println("请输入数据:");
int number = sc.nextInt();
if (number != 0) {
array.add(number);
} else {
break;
}
}
// 把集合转成数组
// public <T> T[] toArray(T[] a)
Integer[] i = new Integer[array.size()];
// Integer[] ii = array.toArray(i);
array.toArray(i);
// System.out.println(i);
// System.out.println(ii);
// 对数组排序
// public static void sort(Object[] a)
Arrays.sort(i);
// 获取该数组中的最大索引的值
System.out.println("数组是:" + arrayToString(i) + "最大值是:"
+ i[i.length - 1]);
}
public static String arrayToString(Integer[] i) {
StringBuilder sb = new StringBuilder();
sb.append("[");
for (int x = 0; x < i.length; x++) {
if (x == i.length - 1) {
sb.append(i[x]);
} else {
sb.append(i[x]).append(", ");
}
}
sb.append("]");
return sb.toString();
}
}
Set集合
1.Set集合概述:一个不包含重复元素的 collection。
2.注意:虽然Set集合的元素无序,但是,作为集合来说,它肯定有自己的存储顺序,如果输入的顺序恰好和它的存储顺序一致,那也代表不了有序,多存储一些数据,就能看到效果。
HashSet集合
1.HashSet集合概述:
● 不保证 set 的迭代顺序
● 特别是它不保证该顺序恒久不变。
2.HashSet如何保证元素唯一性:
● 底层数据结构是哈希表(元素是链表的数组)
● 哈希表依赖于哈希值存储
● 添加功能底层依赖两个方法:
int hashCode()
boolean equals(Object obj)
HashSet集合的add()方法源码分析
1.保证元素唯一性源码体现:
// 代码体现
import java.util.HashSet;
public class HastSetCode {
public static void main(String[] args) {
//创建集合对象
HashSet<String> hs = new HashSet<String>();
//创建元素对象并添加到集合
hs.add("张三");
hs.add("李四");
hs.add("王五");
hs.add("王五");
hs.add("李四");
//遍历集合
for(String s : hs) {
System.out.println(s);
}
}
}
// 源码分析
// 集合顶层Collection
interface Collection {
...
}
// Set接口继承Collection
interface Set extends Collection {
...
}
// HashSet成为Set接口的具体实现类
class HashSet implements Set {
// HashSet创建对象时调用构造方法
public HashSet() {
// 创建了HashMap对象,多态
map = new HashMap<>();
}
// 一个静态常量,对该方法意义不大,因为传进来的参数是e
PRESENT:private static final Object PRESENT = new Object();
// 注意HashMap是实现了接口Map的类,这里相当于前面模拟栈结构那个案例
// 只不过这里是把接口定义成私有变量
map:private transient HashMap<E,Object> map;
// 通过eclipse进去add()源码
public boolean add(E e) { // 张三第一次到这里,e=张三
// 通过调用map的put()方法,这里相当于调用HashMap的put()方法
return map.put(e, PRESENT)==null;
}
}
class HashMap implements Map {
// HashMap的put()方法
public V put(K key, V value) { //调用put,张三传到这:k=e=张三
// 这里是判断哈希表是否为空,如果空就开辟空间
if (table == EMPTY_TABLE) {
// 开辟空间
inflateTable(threshold);
}
// 判断对象是否为null,张三不是null
if (key == null)
return putForNullKey(value);
// 计算张三的哈希值,调用HashMap的hash()方法
// 这里hash()方法的哈希值只跟对象(张三)的hashCode()方法有关
int hash = hash(key);
// 在哈希表中查找有没有对应的哈希值,第一次的表为空,
//找不到直接跳过循环
// 直接添加元素,并把本身的哈希值添加到表中,
//后面的李四王五也是一样
int i = indexFor(hash, table.length);
// 这里是判断元素是否重复,当第二个王五到这里,
//会把在表中找到的相同哈希值赋值给e
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
// 定义一个Object k变量
Object k;
// 然后第二个王五和第一个王五比较哈希值,一样的话继续比较地址值或比较
// equals()方法,有一个一样的就返回原来的元素,相当于删除元素
// 注意:这里之所以能比较成功是因为张三等是String类型元素,而String
//类中重写了hashCode()方法和equals()方法,所以可以比较字符串的哈希
//值和内容是否相同;如果是对象作为引用参数,会因为没有重写hashCode()方法和
//equals()方法,所以默认比较Object()的hashCode()方法和equals()方
//法,而因每个对象的地址不同,导致哈希码值都不一样,即e.hash != hash,
//所以创建的对象都不会被删除,相同对象也会被添加到集合,因此在使用
//HashSet前得先重写Object()的hashCode()方法和equals()方法
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
// 进这里就相当于没添加元素
}
}
modCount++; // 统计变量++
addEntry(hash, key, value, i); // 把张三李四王五添加进去
return null;
}
// 初始化值意义不大
transient int hashSeed = 0;
// HashMap的hash()方法
final int hash(Object k) { // k=key=e=张三
int h = hashSeed;
if (0 != h && k instanceof String) {
return sun.misc.Hashing.stringHash32((String) k);
}
h ^= k.hashCode(); // 这里相当于调用对象(张三)的hashCode()方法
h ^= (h >>> 20) ^ (h >>> 12);
return h ^ (h >>> 7) ^ (h >>> 4);
}
}
总结:通过源码分析,知道了HashSet底层依赖的是hashCode()和equals()方法。,而String类中重写了hashCode()方法和equals()方法,所以可以比较字符串的哈希值和内容是否相同;但如果对象作为引用参数,会因为没有重写hashCode()方法和equals()方法,而默认去比较Object()的hashCode()方法和equals()方法,而因每个对象的地址不同,导致哈希码值都不一样,所以创建的对象都不会被删除,相同对象也会被添加到集合,所以在使用HashSet前得先重写Object()的hashCode()方法和equals()方法。
add()方法底层依赖 两个方法:hashCode()和equals()。
* 步骤:
* 首先比较哈希值
* 如果相同,继续走,比较地址值或者走equals()
* 如果不同,就直接添加到集合中
* 按照方法的步骤来说:
* 先看hashCode()值是否相同
* 相同:继续走equals()方法
* 返回true: 说明元素重复,就不添加
* 返回false:说明元素不重复,就添加到集合
* 不同:就直接把元素添加到集合
* 如果类没有重写这两个方法,默认使用的Object()。一般来说不会相同。
* 而String类重写了hashCode()和equals()方法,所以,它就可以把内容相同的字符串去掉。
* 只留下一个。
hashCode()方法的重写
因为不是String类型的对象都要重写hashCode()方法和equals()方法(equals()方法重写可在“API-常用类中看”),而要比较对象是否相同就得通过equals()方法,对象相等则hashCode一定相等; hashCode相等对象未必相等。即哈希值一样,并不代表两个对象一样,只有通过equals()方法才能知道是否是不同对象(比如两个相同内容的字符串哈希值一样,地址值却不一样),由上面源码可知hashCode()方法要相同才能比较equals()方法,所以重写hashCode()方法只需要让它返回一个值即可,这样每个对象的哈希值就都一样了,但是这样效率会低,因为每拿到一个对象就得和前面所有对象比较。因此得进行优化,让对象的哈希值尽量不同,而对象的哈希值是与成员变量相关的,所以重写hashCode()方法,让其返回一个成员变量相加的值,如果是基本类型就直接相加,引用类型就调用该类型的hashCode()方法(默认调用Object的)再相加,这样就能使哈希值不同,减少比较次数。但是还有可能因为引用类型+基本类型的值相同(比如对象s1:name.hashCode()=40,age=30,对象s2:name.hashCode()=20,age=50),所以得再后面乘个常量避免该情况。
//分析所得
@Override
public int hashCode() {
// return 0;效率低
// 因为成员变量值影响了哈希值,所以把成员变量值相加即可
// return this.name.hashCode() + this.age;
// 为了区分下面这种情况,乘以一些整数
// s1:name.hashCode()=40,age=30
// s2:name.hashCode()=20,age=50
return this.name.hashCode() + this.age * 15;
}
// 直接生成
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
hashCode()重写保证元素唯一性图解:
LinkedHashSet集合概述
● 底层数据结构由哈希表和链表组成,元素有序唯一
● 由链表保证元素有序(存储和取出是一致)
● 由哈希表保证元素唯一
TreeSet集合
1.TreeSet集合概述:能够对元素按照某种规则进行排序,特点:排序和唯一。
● 使用元素的自然顺序对元素进行排序(自然排序)
● 或者根据创建 set 时提供的 Comparator 进行排序(比较器排序)
● 具体取决于使用的构造方法。
2.TreeSet保证元素的排序和唯一性的原因:
● 底层数据结构是红黑树(红黑树是一种自平衡的二叉树)
3.TreeSet存储元素唯一和自然排序图解:
TreeSet的add()方法源码解析:
// 因为TreeSet是基于 TreeMap类的 NavigableSet接口实现,所以顺着add()进去看源码只能
//看到 NavigableSet接口,不能看到其具体实现类,所以就得去看TreeMap类的put()方法
// 各集合和接口关系
interface Collection {...}
interface Set extends Collection {...}
interface NavigableMap {}
class TreeMap implements NavigableMap {
// TreeMap的无参构造方法
public TreeMap() {
comparator = null;
}
// TreeMap的带参构造方法
public TreeMap(Comparator<? super K> comparator) {
this.comparator = comparator;
}
// TreeMap的put()方法
public V put(K key, V value) {
// 这里相当于在造根节点
Entry<K,V> t = root;
if (t == null) {
compare(key, key); // type (and possibly null) check
root = new Entry<>(key, value, null);
size = 1;
modCount++;
return null;
}
int cmp;
Entry<K,V> parent;
// 如果TreeSet用了无参构造,就相当于自然排序,
//k=null,由TreeMap的无参构造方法可知
// 如果TreeSet用了带参构造,就相当于比较器排
//序,k!=null,由TreeMap的带参构造方法可知
Comparator<? super K> cpr = comparator;
if (cpr != null) {
do {// 比较器排序
parent = t;
cmp = cpr.compare(key, t.key);
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else
return t.setValue(value);
} while (t != null);
}
else {
if (key == null)
throw new NullPointerException();
// 把对象转成Comparable接口k,多态
Comparable<? super K> k = (Comparable<? super K>) key;
do {// 自然排序
parent = t;
// 真正的比较是依赖于元素的compareTo()方法,
//而这个方法是定义在 Comparable里面的。所以,
//你要想重写该方法,就必须实现 Comparable接口。
//这个接口表示的就是自然排序。
cmp = k.compareTo(t.key);
// 小的放左边
if (cmp < 0)
t = t.left;
// 大的放右边
else if (cmp > 0)
t = t.right;
// 相等不添加
else
return t.setValue(value);
} while (t != null);
}
Entry<K,V> e = new Entry<>(key, value, parent);
if (cmp < 0)
parent.left = e;
else
parent.right = e;
fixAfterInsertion(e);
size++;
modCount++;
return null;
}
}
class TreeSet implements Set {
// 把接口NavigableMap定义成了私有变量m,跟前面栈那个案例一样
private transient NavigableMap<E,Object> m;
// 带参构造,需要的是NavigableMap接口的实现类
TreeSet(NavigableMap<E,Object> m) {
this.m = m;
}
// 无参构造
public TreeSet() {
// 这里是调用了该类的带参构造,创建了TreeMap对象,并把对象赋给了this.m
this(new TreeMap<E,Object>());
}
public boolean add(E e) {
// 所以这里的m就是TreeMap,调用的也是TreeMap的put()方法
return m.put(e, PRESENT)==null;
}
}
总结:TreeSet集合保证元素排序和唯一性的原理:
● 唯一性:是根据比较器的返回值是否是0来决定,是0证明元素相等就不添加
● 排序:
A:自然排序(元素具备比较性)
让元素所属的类实现自然排序接口 Comparable
B:比较器排序(集合具备比较性) 重写比较器排序方法,根据比较器返回值的大小按红黑树存储
让集合的构造方法接收一个比较器接口的子类对象 Comparator
compareTo()重写案例
/*
* 需求:按照年龄排序学生对象
* 如果一个类的元素要想能够进行自然排序,就必须实现自然排序接口
*/
// 创建学生类
public class Student implements Comparable<Student> {
private String name;
private int age;
public Student() {
super();
}
public Student(String name, int age) {
super();
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public int compareTo(Student s) {
// return 0; 如果返回的是0,则默认是重复元素,不添加到集合
// return 1; 返回的是正数,则按照升序添加
// return -1; 返回的是负数,则按照降序添加
// 这里返回什么,其实应该根据我的排序规则来做
// 按照年龄排序,主要条件
int num = this.age - s.age;
// 次要条件
// 年龄相同的时候,还得去看姓名是否也相同
// 如果年龄和姓名都相同,才是同一个元素
int num2 = num == 0 ? this.name.compareTo(s.name) : num;
return num2;
}
}
// 测试类
import java.util.TreeSet;
/*
* TreeSet存储自定义对象并保证排序和唯一。
*
* A:你没有告诉我们怎么排序
* 自然排序,按照年龄从小到大排序
* B:元素什么情况算唯一你也没告诉我
* 成员变量值都相同即为同一个元素
*/
public class TreeSetDemo2 {
public static void main(String[] args) {
// 创建集合对象
TreeSet<Student> ts = new TreeSet<Student>();
// 创建元素
Student s1 = new Student("linqingxia", 27);
Student s2 = new Student("zhangguorong", 29);
Student s3 = new Student("wanglihong", 23);
Student s4 = new Student("linqingxia", 27);
Student s5 = new Student("liushishi", 22);
Student s6 = new Student("wuqilong", 40);
Student s7 = new Student("fengqingy", 22);
// 添加元素
ts.add(s1);
ts.add(s2);
ts.add(s3);
ts.add(s4);
ts.add(s5);
ts.add(s6);
ts.add(s7);
// 遍历
for (Student s : ts) {
System.out.println(s.getName() + "---" + s.getAge());
}
}
}
TreeSet的比较器排序使用
// 创建学生类
public class Student {
private String name;
private int age;
public Student() {
super();
}
public Student(String name, int age) {
super();
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
// 创建比较器Comparator接口的具体实现类
import java.util.Comparator;
// 限定是学生类
public class MyComparator implements Comparator<Student> {
@Override
// 所以这里得传两个学生对象进行比较
public int compare(Student s1, Student s2) {
// 这里的this指的是MyComparator,所以不行
// int num = this.name.length() - s.name.length();
// 在前面自然排序中,,在学生类的this相当于s1
// 而s相当于s2
// 主要条件:比较姓名长度是否相同
int num = s1.getName().length() - s2.getName().length();
// 次要条件:相同则比较姓名内容
int num2 = num == 0 ? s1.getName().compareTo(s2.getName()) : num;
// 次要条件:都相同则比较年龄
int num3 = num2 == 0 ? s1.getAge() - s2.getAge() : num2;
return num3;
}
}
// 测试类
// 需求:请按照姓名的长度排序
public class TreeSetDemo {
public static void main(String[] args) {
// 创建集合对象
// TreeSet<Student> ts = new TreeSet<Student>(); //自然排序
// public TreeSet(Comparator comparator) //比较器排序
// TreeSet<Student> ts = new TreeSet<Student>(new MyComparator());
// 如果一个方法的参数是接口,那么真正要的是接口的实现类的对象
// 而匿名内部类就可以实现这个东西,这种方法也是常用的,避免了经常改学生类代码
TreeSet<Student> ts = new TreeSet<Student>(new Comparator<Student>() {
public int compare(Student s1, Student s2) {
// 主要条件:比较姓名长度是否相同
int num = s1.getName().length() - s2.getName().length();
// 次要条件:相同则比较姓名内容
int num2 = num == 0 ? s1.getName().compareTo(s2.getName()) : num;
// 次要条件:都相同则比较年龄
int num3 = num2 == 0 ? s1.getAge() - s2.getAge() : num2;
return num3;
}
});
// 创建元素
Student s1 = new Student("linqingxia", 27);
Student s2 = new Student("zhangguorong", 29);
Student s3 = new Student("wanglihong", 23);
Student s4 = new Student("linqingxia", 27);
Student s5 = new Student("liushishi", 22);
Student s6 = new Student("wuqilong", 40);
Student s7 = new Student("fengqingy", 22);
Student s8 = new Student("linqingxia", 29);
// 添加元素
ts.add(s1);
ts.add(s2);
ts.add(s3);
ts.add(s4);
ts.add(s5);
ts.add(s6);
ts.add(s7);
ts.add(s8);
// 遍历
for (Student s : ts) {
System.out.println(s.getName() + "---" + s.getAge());
}
}
}
TreeSet案例
//代码体现
import java.util.HashSet;
import java.util.Random;
/*
* 编写一个程序,获取10个1至20的随机数,要求随机数不能重复。
*
* 分析:
* A:创建随机数对象
* B:创建一个HashSet集合
* C:判断集合的长度是不是小于10
* 是:就创建一个随机数添加
* 否:不搭理它
* D:遍历HashSet集合
*/
public class HashSetDemo {
public static void main(String[] args) {
// 创建随机数对象
Random r = new Random();
// 创建一个Set集合
HashSet<Integer> ts = new HashSet<Integer>();
// 判断集合的长度是不是小于10
while (ts.size() < 10) {
int num = r.nextInt(20) + 1;
ts.add(num);
}
// 遍历Set集合
for (Integer i : ts) {
System.out.println(i);
}
}
}
// 代码体现
import java.util.Comparator;
import java.util.Scanner;
import java.util.TreeSet;
/*
* 键盘录入5个学生信息(姓名,语文成绩,数学成绩,英语成绩),按照总分从高到低输出到控制台
*
* 分析:
* A:定义学生类
* B:创建一个TreeSet集合
* C:总分从高到底如何实现呢?
* D:键盘录入5个学生信息
* E:遍历TreeSet集合
*/
public class TreeSetDemo {
public static void main(String[] args) {
// 创建一个TreeSet集合
TreeSet<Student> ts = new TreeSet<Student>(new Comparator<Student>() {
@Override
public int compare(Student s1, Student s2) {
// 总分从高到低
int num = s2.getSum() - s1.getSum();
// 总分相同的不一定语文相同
int num2 = num == 0 ? s1.getChinese() - s2.getChinese() : num;
// 总分相同的不一定数序相同
int num3 = num2 == 0 ? s1.getMath() - s2.getMath() : num2;
// 总分相同的不一定英语相同
int num4 = num3 == 0 ? s1.getEnglish() - s2.getEnglish() : num3;
// 姓名还不一定相同呢
int num5 = num4 == 0 ? s1.getName().compareTo(s2.getName())
: num4;
return num5;
}
});
System.out.println("学生信息录入开始");
// 键盘录入5个学生信息
for (int x = 1; x <= 5; x++) {
Scanner sc = new Scanner(System.in);
System.out.println("请输入第" + x + "个学生的姓名:");
String name = sc.nextLine();
System.out.println("请输入第" + x + "个学生的语文成绩:");
String chineseString = sc.nextLine();
System.out.println("请输入第" + x + "个学生的数学成绩:");
String mathString = sc.nextLine();
System.out.println("请输入第" + x + "个学生的英语成绩:");
String englishString = sc.nextLine();
// 把数据封装到学生对象中
Student s = new Student();
s.setName(name);
s.setChinese(Integer.parseInt(chineseString));
s.setMath(Integer.parseInt(mathString));
s.setEnglish(Integer.parseInt(englishString));
// 把学生对象添加到集合
ts.add(s);
}
System.out.println("学生信息录入完毕");
System.out.println("学习信息从高到低排序如下:");
System.out.println("姓名\t语文成绩\t数学成绩\t英语成绩");
// 遍历集合
for (Student s : ts) {
System.out.println(s.getName() + "\t" + s.getChinese() + "\t"
+ s.getMath() + "\t" + s.getEnglish());
}
}
}
// 定义学生类
public class Student {
// 姓名
private String name;
// 语文成绩
private int chinese;
// 数学成绩
private int math;
// 英语成绩
private int english;
public Student(String name, int chinese, int math, int english) {
super();
this.name = name;
this.chinese = chinese;
this.math = math;
this.english = english;
}
public Student() {
super();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getChinese() {
return chinese;
}
public void setChinese(int chinese) {
this.chinese = chinese;
}
public int getMath() {
return math;
}
public void setMath(int math) {
this.math = math;
}
public int getEnglish() {
return english;
}
public void setEnglish(int english) {
this.english = english;
}
public int getSum() {
return this.chinese + this.math + this.english;
}
}
Map集合
Map集合概述
1.Map集合特点:将键映射到值的对象,一个映射不能包含重复的键,每个键最多只能映射到一个值
2.Map集合和Collection集合的不同:
● Map集合存储元素是成对出现的,Collection集合存储元素是单独出现的
● Map集合的键是唯一的,值是可重复的。Collection的子体系Set是唯一的,List是可重复的
● Map集合的数据结构值针对键有效,跟值无关
● Collection集合的数据结构是针对元素有效
Map集合的成员方法
1.添加功能:
● V put(K key,V value):添加元素。
如果键是第一次存储,就直接存储元素,返回null
如果键不是第一次存在,就用值把以前的值替换掉,返回以前的值
2.删除功能:
● void clear():移除所有的键值对元素
● V remove(Object key):根据键删除键值对元素,并把值返回
3.判断功能:
● boolean containsKey(Object key):判断集合是否包含指定的键
● boolean containsValue(Object value):判断集合是否包含指定的值
● boolean isEmpty():判断集合是否为空
4.获取功能:
● Set<Map.Entry<K,V>> entrySet():返回键值对对象的集合
● V get(Object key):根据键获取值
● Set keySet():获取集合中所有键的集合
● Collection values():获取集合中所有值的集合
5.长度功能:
● int size():返回集合中的键值对的对数
// 代码体现
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class MapDemo {
public static void main(String[] args) {
// 创建集合对象
Map<String, String> map = new HashMap<String, String>();
// 添加元素
// V put(K key,V value):添加元素。
// System.out.println("put:" + map.put("张三", "20"));
// System.out.println("put:" + map.put("张三", "25"));
map.put("张三", "20");
map.put("李四", "25");
map.put("王五", "30");
map.put("赵六", "40");
// void clear():移除所有的键值对元素
// map.clear();
// V remove(Object key):根据键删除键值对元素,并把值返回
// System.out.println("remove:" + map.remove("张三"));
// System.out.println("remove:" + map.remove("孙七"));
// boolean containsKey(Object key):判断集合是否包含指定的键
// System.out.println("containsKey:" + map.containsKey("张三"));
// System.out.println("containsKey:" + map.containsKey("孙七"));
// boolean isEmpty():判断集合是否为空
// System.out.println("isEmpty:"+map.isEmpty());
// V get(Object key):根据键获取值
System.out.println("get:" + map.get("张三"));
System.out.println("get:" + map.get("孙七")); // 返回null
// Set<K> keySet():获取集合中所有键的集合
Set<String> set = map.keySet();
for (String key : set) {
System.out.println(key);
}
// Collection<V> values():获取集合中所有值的集合
Collection<String> con = map.values();
for (String value : con) {
System.out.println(value);
}
//int size():返回集合中的键值对的对数
System.out.println("size:"+map.size());
// 输出集合名称
System.out.println("map:" + map);
}
}
Map集合的遍历
1.方式一:
● 获取所有的键
● 遍历键的集合,获取得到每一个键
● 根据键去找值
// 代码体现
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class MapDemo {
public static void main(String[] args) {
// 创建集合对象
Map<String, String> map = new HashMap<String, String>();
// 创建元素并添加
map.put("张三", "20");
map.put("李四", "25");
map.put("王五", "30");
map.put("赵六", "40");
// 获取所有的键
Set<String> set = map.keySet();
// 遍历键的集合,获取得到每一个键
for (String key : set) {
// 根据键去找值
String value = map.get(key);
System.out.println(key + "---" + value);
}
}
}
2.方式二:
● 获取所有键值对对象的集合
● 遍历键值对对象的集合,得到每一个键值对对象
● 根据键值对对象获取键和值
// 代码体现
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class MapDemo02 {
public static void main(String[] args) {
// 创建集合对象
Map<String, String> map = new HashMap<String, String>();
// 创建元素并添加
map.put("张三", "20");
map.put("李四", "25");
map.put("王五", "30");
map.put("赵六", "40");
// 获取所有键值对对象的集合,Map.Entry<K,V>是接口,所以返回的是其子类对象
Set<Map.Entry<String, String>> set = map.entrySet();
// 遍历键值对对象的集合,得到每一个键值对对象
for (Map.Entry<String, String> me : set) {
// 根据键值对对象获取键和值,接口的两个方法
// getKey():返回与此对象对应的键。
// getValue():返回与此对象对应的值。
String key = me.getKey();
String value = me.getValue();
System.out.println(key + "---" + value);
}
}
}
3.Map集合的两种遍历方式图解:
HashMap类
HashMap类概述
● 是基于哈希表的Map接口实现。哈希表的作用是用来保证键的唯一性的。
HashMap案例
● HashMap<String,String>
// 代码体现
import java.util.HashMap;
import java.util.Set;
/*
* HashMap:是基于哈希表的Map接口实现。
* 哈希表的作用是用来保证键的唯一性的。
*
* HashMap<String,String>
* 键:String
* 值:String
*/
public class HashMapDemo {
public static void main(String[] args) {
// 创建集合对象
HashMap<String, String> hm = new HashMap<String, String>();
// 创建元素并添加元素
// String key1 = "it001";
// String value1 = "马云";
// hm.put(key1, value1);
hm.put("it001", "马云");
hm.put("it003", "马化腾");
hm.put("it004", "乔布斯");
hm.put("it005", "张朝阳");
hm.put("it002", "裘伯君"); // wps
hm.put("it001", "比尔盖茨");
// 遍历
Set<String> set = hm.keySet();
for (String key : set) {
String value = hm.get(key);
System.out.println(key + "---" + value);
}
}
}
● HashMap<Integer,String>
// 代码体现
import java.util.HashMap;
import java.util.Set;
/*
* HashMap<Integer,String>
* 键:Integer
* 值:String
*/
public class HashMapDemo2 {
public static void main(String[] args) {
// 创建集合对象
HashMap<Integer, String> hm = new HashMap<Integer, String>();
// 创建元素并添加元素
// Integer i = new Integer(27);
// Integer i = 27;
// String s = "林青霞";
// hm.put(i, s);
hm.put(27, "林青霞");
hm.put(30, "风清扬");
hm.put(28, "刘意");
hm.put(29, "林青霞");
// 下面的写法是八进制,但是不能出现8以上的单个数据
// hm.put(003, "hello");
// hm.put(006, "hello");
// hm.put(007, "hello");
// hm.put(008, "hello");
// 遍历
Set<Integer> set = hm.keySet();
for (Integer key : set) {
String value = hm.get(key);
System.out.println(key + "---" + value);
}
// 下面这种方式仅仅是集合的元素的字符串表示
// System.out.println("hm:" + hm);
}
}
● HashMap<String,Student>
// 学生类
public class Student {
private String name;
private int age;
public Student() {
super();
}
public Student(String name, int age) {
super();
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Student other = (Student) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
}
// 代码体现
import java.util.HashMap;
import java.util.Set;
/*
* HashMap<String,Student>
* 键:String 学号
* 值:Student 学生对象
*/
public class HashMapDemo3 {
public static void main(String[] args) {
// 创建集合对象
HashMap<String, Student> hm = new HashMap<String, Student>();
// 创建学生对象
Student s1 = new Student("周星驰", 58);
Student s2 = new Student("刘德华", 55);
Student s3 = new Student("梁朝伟", 54);
Student s4 = new Student("刘嘉玲", 50);
// 添加元素
hm.put("9527", s1);
hm.put("9522", s2);
hm.put("9524", s3);
hm.put("9529", s4);
// 遍历
Set<String> set = hm.keySet();
for (String key : set) {
// 注意了:这次值不是字符串了
// String value = hm.get(key);
Student value = hm.get(key);
System.out.println(key + "---" + value.getName() + "---"
+ value.getAge());
}
}
}
● HashMap<Student,String>
// 代码体现
import java.util.HashMap;
import java.util.Set;
/*
* HashMap<Student,String>
* 键:Student
* 要求:如果两个对象的成员变量值都相同,则为同一个对象。
* 值:String
*/
public class HashMapDemo4 {
public static void main(String[] args) {
// 创建集合对象
HashMap<Student, String> hm = new HashMap<Student, String>();
// 创建学生对象
Student s1 = new Student("貂蝉", 27);
Student s2 = new Student("王昭君", 30);
Student s3 = new Student("西施", 33);
Student s4 = new Student("杨玉环", 35);
Student s5 = new Student("貂蝉", 27);
// 添加元素
hm.put(s1, "8888");
hm.put(s2, "6666");
hm.put(s3, "5555");
hm.put(s4, "7777");
hm.put(s5, "9999");
// 遍历
Set<Student> set = hm.keySet();
for (Student key : set) {
// 底层依赖hashCode()和equals()方法,所以得重写学生类方法
String value = hm.get(key);
System.out.println(key.getName() + "---" + key.getAge() + "---"
+ value);
}
}
}
HashMap和Hashtable的区别
1.Hashtable和HashMap的区别:(除了以下两个区别,其他几乎一样,HashMap就是用来替代Hashtable的)
● Hashtable:线程安全,效率低。不允许null键和null值
● HashMap:线程不安全,效率高。允许null键和null值
// 代码体现
import java.util.Hashtable;
public class HashtableDemo {
public static void main(String[] args) {
// HashMap<String, String> hm = new HashMap<String, String>();
Hashtable<String, String> hm = new Hashtable<String, String>();
hm.put("it001", "hello");
// hm.put(null, "world"); //NullPointerException
// hm.put("java", null); // NullPointerException
System.out.println(hm);
}
}
LinkedHashMap类
1.LinkedHashMap:是Map接口的哈希表和链接列表实现,具有可预知的迭代顺序。
● 由哈希表保证键的唯一性
● 由链表保证键盘的有序(存储和取出的顺序一致)
// 代码体现
import java.util.LinkedHashMap;
import java.util.Set;
public class LinkedHashMapDemo {
public static void main(String[] args) {
// 创建集合对象
LinkedHashMap<String, String> hm = new LinkedHashMap<String, String>();
// 创建并添加元素
hm.put("2345", "hello");
hm.put("1234", "world");
hm.put("3456", "java");
hm.put("1234", "javaee");
hm.put("3456", "android");
// 遍历
Set<String> set = hm.keySet();
for (String key : set) {
String value = hm.get(key);
System.out.println(key + "---" + value);
}
}
}
TreeMap类概述
1.键是红黑树结构,可以保证键的排序和唯一性
2.TreeMap案例
● TreeMap<String,String>
// 代码体现
import java.util.Set;
import java.util.TreeMap;
/*
* TreeMap:是基于红黑树的Map接口的实现。
*
* TreeMap<String,String>
* 键:String
* 值:String
*/
public class TreeMapDemo {
public static void main(String[] args) {
// 创建集合对象
TreeMap<String, String> tm = new TreeMap<String, String>();
// 创建元素并添加元素
tm.put("hello", "你好");
tm.put("world", "世界");
tm.put("java", "爪哇");
tm.put("world", "世界2");
tm.put("javaee", "爪哇EE");
// 遍历集合
Set<String> set = tm.keySet();
for (String key : set) {
String value = tm.get(key);
System.out.println(key + "---" + value);
}
}
}
● TreeMap<Student,String>
// 学生类
public class Student {
private String name;
private int age;
public Student() {
super();
}
public Student(String name, int age) {
super();
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
import java.util.Comparator;
import java.util.Set;
import java.util.TreeMap;
/*
* TreeMap<Student,String>
* 键:Student
* 值:String
*/
public class TreeMapDemo2 {
public static void main(String[] args) {
// 创建集合对象
TreeMap<Student, String> tm = new TreeMap<Student, String>(
new Comparator<Student>() {
@Override
public int compare(Student s1, Student s2) {
// 主要条件
int num = s1.getAge() - s2.getAge();
// 次要条件
int num2 = num == 0 ? s1.getName().compareTo(
s2.getName()) : num;
return num2;
}
});
// 创建学生对象
Student s1 = new Student("潘安", 30);
Student s2 = new Student("柳下惠", 35);
Student s3 = new Student("唐伯虎", 33);
Student s4 = new Student("燕青", 32);
Student s5 = new Student("唐伯虎", 33);
// 存储元素
tm.put(s1, "宋朝");
tm.put(s2, "元朝");
tm.put(s3, "明朝");
tm.put(s4, "清朝");
tm.put(s5, "汉朝");
// 遍历
Set<Student> set = tm.keySet();
for (Student key : set) {
String value = tm.get(key);
System.out.println(key.getName() + "---" + key.getAge() + "---"
+ value);
}
}
}
● TreeMap案例3:
import java.util.Scanner;
import java.util.Set;
import java.util.TreeMap;
/*
* 需求 :"aababcabcdabcde",获取字符串中每一个字母出现的次数要求结果:a(5)b(4)c(3)d(2)e(1)
*
* 分析:
* A:定义一个字符串(可以改进为键盘录入)
* B:定义一个TreeMap集合
* 键:Character
* 值:Integer
* C:把字符串转换为字符数组
* D:遍历字符数组,得到每一个字符
* E:拿刚才得到的字符作为键到集合中去找值,看返回值
* 是null:说明该键不存在,就把该字符作为键,1作为值存储
* 不是null:说明该键存在,就把值加1,然后重新存储该键和值
* F:定义字符串缓冲区变量
* G:遍历集合,得到键和值,进行按照要求拼接
* H:把字符串缓冲区转换为字符串输出
*
* 录入:linqingxia
* 结果:result:a(1)g(1)i(3)l(1)n(2)q(1)x(1)
*/
public class TreeMapDemo {
public static void main(String[] args) {
// 定义一个字符串(可以改进为键盘录入)
Scanner sc = new Scanner(System.in);
System.out.println("请输入一个字符串:");
String line = sc.nextLine();
// 定义一个TreeMap集合
TreeMap<Character, Integer> tm = new TreeMap<Character, Integer>();
//把字符串转换为字符数组
char[] chs = line.toCharArray();
//遍历字符数组,得到每一个字符
for(char ch : chs){
//拿刚才得到的字符作为键到集合中去找值,看返回值
Integer i = tm.get(ch);
//是null:说明该键不存在,就把该字符作为键,1作为值存储
if(i == null){
tm.put(ch, 1);
}else {
//不是null:说明该键存在,就把值加1,然后重写存储该键和值
i++;
tm.put(ch,i);
}
}
//定义字符串缓冲区变量
StringBuilder sb= new StringBuilder();
//遍历集合,得到键和值,进行按照要求拼接
Set<Character> set = tm.keySet();
for(Character key : set){
Integer value = tm.get(key);
sb.append(key).append("(").append(value).append(")");
}
//把字符串缓冲区转换为字符串输出
String result = sb.toString();
System.out.println("result:"+result);
}
}
集合的嵌套遍历
HashMap嵌套HashMap
import java.util.HashMap;
import java.util.Set;
/*
* HashMap嵌套HashMap
*
* 传智播客
* jc 基础班
* 陈玉楼 20
* 高跃 22
* jy 就业班
* 李杰 21
* 曹石磊 23
*
* 先存储元素,然后遍历元素
*/
public class HashMapDemo2 {
public static void main(String[] args) {
// 创建集合对象
HashMap<String, HashMap<String, Integer>> czbkMap = new HashMap<String, HashMap<String, Integer>>();
// 创建基础班集合对象
HashMap<String, Integer> jcMap = new HashMap<String, Integer>();
// 添加元素
jcMap.put("陈玉楼", 20);
jcMap.put("高跃", 22);
// 把基础班添加到大集合
czbkMap.put("jc", jcMap);
// 创建就业班集合对象
HashMap<String, Integer> jyMap = new HashMap<String, Integer>();
// 添加元素
jyMap.put("李杰", 21);
jyMap.put("曹石磊", 23);
// 把基础班添加到大集合
czbkMap.put("jy", jyMap);
//遍历集合
Set<String> czbkMapSet = czbkMap.keySet();
for(String czbkMapKey : czbkMapSet){
System.out.println(czbkMapKey);
HashMap<String, Integer> czbkMapValue = czbkMap.get(czbkMapKey);
Set<String> czbkMapValueSet = czbkMapValue.keySet();
for(String czbkMapValueKey : czbkMapValueSet){
Integer czbkMapValueValue = czbkMapValue.get(czbkMapValueKey);
System.out.println("\t"+czbkMapValueKey+"---"+czbkMapValueValue);
}
}
}
}
HashMap嵌套ArrayList
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Set;
/*
*需求:
*假设HashMap集合的元素是ArrayList。有3个。
*每一个ArrayList集合的值是字符串。
*元素我已经完成,请遍历。
*结果:
* 三国演义
* 吕布
* 周瑜
* 笑傲江湖
* 令狐冲
* 林平之
* 神雕侠侣
* 郭靖
* 杨过
*/
public class HashMapIncludeArrayListDemo {
public static void main(String[] args) {
// 创建集合对象
HashMap<String, ArrayList<String>> hm = new HashMap<String, ArrayList<String>>();
// 创建元素集合1
ArrayList<String> array1 = new ArrayList<String>();
array1.add("吕布");
array1.add("周瑜");
hm.put("三国演义", array1);
// 创建元素集合2
ArrayList<String> array2 = new ArrayList<String>();
array2.add("令狐冲");
array2.add("林平之");
hm.put("笑傲江湖", array2);
// 创建元素集合3
ArrayList<String> array3 = new ArrayList<String>();
array3.add("郭靖");
array3.add("杨过");
hm.put("神雕侠侣", array3);
//遍历集合
Set<String> set = hm.keySet();
for(String key : set){
System.out.println(key);
ArrayList<String> value = hm.get(key);
for(String s : value){
System.out.println("\t"+s);
}
}
}
}
ArrayList嵌套HashMap
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Set;
/*
ArrayList集合嵌套HashMap集合并遍历。
需求:
假设ArrayList集合的元素是HashMap。有3个。
每一个HashMap集合的键和值都是字符串。
元素我已经完成,请遍历。
结果:
周瑜---小乔
吕布---貂蝉
郭靖---黄蓉
杨过---小龙女
令狐冲---任盈盈
林平之---岳灵珊
*/
public class ArrayListIncludeHashMapDemo {
public static void main(String[] args) {
// 创建集合对象
ArrayList<HashMap<String, String>> array = new ArrayList<HashMap<String, String>>();
// 创建元素1
HashMap<String, String> hm1 = new HashMap<String, String>();
hm1.put("周瑜", "小乔");
hm1.put("吕布", "貂蝉");
// 把元素添加到array里面
array.add(hm1);
// 创建元素1
HashMap<String, String> hm2 = new HashMap<String, String>();
hm2.put("郭靖", "黄蓉");
hm2.put("杨过", "小龙女");
// 把元素添加到array里面
array.add(hm2);
// 创建元素1
HashMap<String, String> hm3 = new HashMap<String, String>();
hm3.put("令狐冲", "任盈盈");
hm3.put("林平之", "岳灵珊");
// 把元素添加到array里面
array.add(hm3);
// 遍历
for (HashMap<String, String> hm : array) {
Set<String> set = hm.keySet();
for (String key : set) {
String value = hm.get(key);
System.out.println(key + "---" + value);
}
}
}
}
HashMap嵌套HashMap嵌套HashMap(三层嵌套)
// 学生类
public class Student {
private String name;
private int age;
public Student() {
super();
}
public Student(String name, int age) {
super();
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Set;
/*
* 为了更符合要求:
* 这次的数据就看成是学生对象。
*
* 传智播客
* bj 北京校区
* jc 基础班
* 林青霞 27
* 风清扬 30
* jy 就业班
* 赵雅芝 28
* 武鑫 29
* sh 上海校区
* jc 基础班
* 郭美美 20
* 犀利哥 22
* jy 就业班
* 罗玉凤 21
* 马征 23
* gz 广州校区
* jc 基础班
* 王力宏 30
* 李静磊 32
* jy 就业班
* 郎朗 31
* 柳岩 33
* xa 西安校区
* jc 基础班
* 范冰冰 27
* 刘意 30
* jy 就业班
* 李冰冰 28
* 张志豪 29
*/
public class HashMapDemo {
public static void main(String[] args) {
// 创建大集合
HashMap<String, HashMap<String, ArrayList<Student>>> czbkMap = new HashMap<String, HashMap<String, ArrayList<Student>>>();
// 北京校区数据
HashMap<String, ArrayList<Student>> bjCzbkMap = new HashMap<String, ArrayList<Student>>();
ArrayList<Student> array1 = new ArrayList<Student>();
Student s1 = new Student("林青霞", 27);
Student s2 = new Student("风清扬", 30);
array1.add(s1);
array1.add(s2);
ArrayList<Student> array2 = new ArrayList<Student>();
Student s3 = new Student("赵雅芝", 28);
Student s4 = new Student("武鑫", 29);
array2.add(s3);
array2.add(s4);
bjCzbkMap.put("基础班", array1);
bjCzbkMap.put("就业班", array2);
czbkMap.put("北京校区", bjCzbkMap);
// 晚上可以自己练习一下
// 上海校区数据自己做
// 广州校区数据自己做
// 西安校区数据
HashMap<String, ArrayList<Student>> xaCzbkMap = new HashMap<String, ArrayList<Student>>();
ArrayList<Student> array3 = new ArrayList<Student>();
Student s5 = new Student("范冰冰", 27);
Student s6 = new Student("刘意", 30);
array3.add(s5);
array3.add(s6);
ArrayList<Student> array4 = new ArrayList<Student>();
Student s7 = new Student("李冰冰", 28);
Student s8 = new Student("张志豪", 29);
array4.add(s7);
array4.add(s8);
xaCzbkMap.put("基础班", array3);
xaCzbkMap.put("就业班", array4);
czbkMap.put("西安校区", xaCzbkMap);
// 遍历集合
Set<String> czbkMapSet = czbkMap.keySet();
for (String czbkMapKey : czbkMapSet) {
System.out.println(czbkMapKey);
HashMap<String, ArrayList<Student>> czbkMapValue = czbkMap
.get(czbkMapKey);
Set<String> czbkMapValueSet = czbkMapValue.keySet();
for (String czbkMapValueKey : czbkMapValueSet) {
System.out.println("\t" + czbkMapValueKey);
ArrayList<Student> czbkMapValueValue = czbkMapValue
.get(czbkMapValueKey);
for (Student s : czbkMapValueValue) {
System.out.println("\t\t" + s.getName() + "---"
+ s.getAge());
}
}
}
}
}
Collection集合总结
Collection
|--List 有序,可重复
|--ArrayList
底层数据结构是数组,查询快,增删慢。
线程不安全,效率高
|--Vector
底层数据结构是数组,查询快,增删慢。
线程安全,效率低
|--LinkedList
底层数据结构是链表,查询慢,增删快。
线程不安全,效率高
|--Set 无序,唯一
|--HashSet
底层数据结构是哈希表。
如何保证元素唯一性的呢?
依赖两个方法:hashCode()和equals()
开发中自动生成这两个方法即可
|--LinkedHashSet
底层数据结构是链表和哈希表
由链表保证元素有序
由哈希表保证元素唯一
|--TreeSet
底层数据结构是红黑树。
如何保证元素排序的呢?
自然排序
比较器排序
如何保证元素唯一性的呢?
根据比较的返回值是否是0来决定
Collections类
Collections类概述
● 是针对集合进行操作的工具类,都是静态方法。
Collections类的成员方法
● public static void sort(List list):排序 默认情况下是自然顺序。
● public static int binarySearch(List<?> list,T key):二分查找
● public static T max(Collection<?> coll):最大值
● public static void reverse(List<?> list):反转
● public static void shuffle(List<?> list):随机置换
// 代码体现
import java.util.Collections;
import java.util.List;
import java.util.ArrayList;
public class CollectionsDemo {
public static void main(String[] args) {
// 创建集合对象
List<Integer> list = new ArrayList<Integer>();
// 添加元素
list.add(30);
list.add(20);
list.add(50);
list.add(10);
list.add(40);
System.out.println("list:" + list);
// public static <T> void sort(List<T> list):排序 默认情况下是自然顺序。
// Collections.sort(list);
// System.out.println("list:" + list);
// [10, 20, 30, 40, 50]
// public static <T> int binarySearch(List<?> list,T key):二分查找
// System.out
// .println("binarySearch:" + Collections.binarySearch(list, 30));
// System.out.println("binarySearch:"
// + Collections.binarySearch(list, 300));
// public static <T> T max(Collection<?> coll):最大值
// System.out.println("max:"+Collections.max(list));
// public static void reverse(List<?> list):反转
// Collections.reverse(list);
// System.out.println("list:" + list);
//public static void shuffle(List<?> list):随机置换
Collections.shuffle(list);
System.out.println("list:" + list);
}
}
Collection和Collections的区别
● Collection:是单列集合的顶层接口,有子接口List和Set。
● Collections:是针对集合操作的工具类,有对集合进行排序和二分查找的方法
/**
* @author Administrator
* 学生类
*/
public class Student implements Comparable<Student> {
private String name;
private int age;
public Student() {
super();
}
public Student(String name, int age) {
super();
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public int compareTo(Student s) {
int num = this.age - s.age;
int num2 = num == 0 ? this.name.compareTo(s.name) : num;
return num2;
}
}
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
/*
* Collections可以针对ArrayList存储基本包装类的元素排序,存储自定义对象可不可以排序呢?
*/
public class CollectionsDemo {
public static void main(String[] args) {
// 创建集合对象
List<Student> list = new ArrayList<Student>();
// 创建学生对象
Student s1 = new Student("林青霞", 27);
Student s2 = new Student("风清扬", 30);
Student s3 = new Student("刘晓曲", 28);
Student s4 = new Student("武鑫", 29);
Student s5 = new Student("林青霞", 27);
// 添加元素对象
list.add(s1);
list.add(s2);
list.add(s3);
list.add(s4);
list.add(s5);
// 排序
// 自然排序
// Collections.sort(list);
// 比较器排序
// 如果同时有自然排序和比较器排序,以比较器排序为主
Collections.sort(list, new Comparator<Student>() {
@Override
public int compare(Student s1, Student s2) {
int num = s2.getAge() - s1.getAge();
int num2 = num == 0 ? s1.getName().compareTo(s2.getName())
: num;
return num2;
}
});
// 遍历集合
for (Student s : list) {
System.out.println(s.getName() + "---" + s.getAge());
}
}
}
案例:斗地主
import java.util.ArrayList;
import java.util.Collections;
/*
* 模拟斗地主洗牌和发牌
*
* 分析:
* A:创建一个牌盒
* B:装牌
* C:洗牌
* D:发牌
* E:看牌
*/
public class PokerDemo {
public static void main(String[] args) {
// 创建一个牌盒
ArrayList<String> array = new ArrayList<String>();
// 装牌
// 黑桃A,黑桃2,黑桃3,...黑桃K
// 红桃A,...
// 梅花A,...
// 方块A,...
// 定义一个花色数组
String[] colors = { "♠", "♥", "♣", "♦" };
// 定义一个点数数组
String[] numbers = { "A", "2", "3", "4", "5", "6", "7", "8", "9", "10",
"J", "Q", "K" };
// 装牌
for (String color : colors) {
for (String number : numbers) {
array.add(color.concat(number));
}
}
array.add("小王");
array.add("大王");
// 洗牌
Collections.shuffle(array);
// System.out.println("array:" + array);
// 发牌
ArrayList<String> fengQingYang = new ArrayList<String>();
ArrayList<String> linQingXia = new ArrayList<String>();
ArrayList<String> liuYi = new ArrayList<String>();
ArrayList<String> diPai = new ArrayList<String>();
for (int x = 0; x < array.size(); x++) {
if (x >= array.size() - 3) {
diPai.add(array.get(x));
} else if (x % 3 == 0) {
fengQingYang.add(array.get(x));
} else if (x % 3 == 1) {
linQingXia.add(array.get(x));
} else if (x % 3 == 2) {
liuYi.add(array.get(x));
}
}
// 看牌
lookPoker("风清扬", fengQingYang);
lookPoker("林青霞", linQingXia);
lookPoker("刘意", liuYi);
lookPoker("底牌", diPai);
}
public static void lookPoker(String name, ArrayList<String> array) {
System.out.print(name + "的牌是:");
for (String s : array) {
System.out.print(s + " ");
}
System.out.println();
}
}
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.TreeSet;
/*
1. 思路:
2. A:创建一个HashMap集合
3. B:创建一个ArrayList集合
4. C:创建花色数组和点数数组
5. D:从0开始往HashMap里面存储编号,并存储对应的牌
6. 同时往ArrayList里面存储编号即可。
7. E:洗牌(洗的是编号)
8. F:发牌(发的也是编号,为了保证编号是排序的,就创建TreeSet集合接收)
9. G:看牌(遍历TreeSet集合,获取编号,到HashMap集合找对应的牌)
*/
public class PokerDemo {
public static void main(String[] args) {
// 创建一个HashMap集合
HashMap<Integer, String> hm = new HashMap<Integer, String>();
// 创建一个ArrayList集合
ArrayList<Integer> array = new ArrayList<Integer>();
// 创建花色数组和点数数组
// 定义一个花色数组
String[] colors = { "♠", "♥", "♣", "♦" };
// 定义一个点数数组
String[] numbers = { "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q",
"K", "A", "2", };
// 从0开始往HashMap里面存储编号,并存储对应的牌,同时往ArrayList里面存储编号即可。
int index = 0;
for (String number : numbers) {
for (String color : colors) {
String poker = color.concat(number);
hm.put(index, poker);
array.add(index);
index++;
}
}
hm.put(index, "小王");
array.add(index);
index++;
hm.put(index, "大王");
array.add(index);
// 洗牌(洗的是编号)
Collections.shuffle(array);
// 发牌(发的也是编号,为了保证编号是排序的,就创建TreeSet集合接收)
TreeSet<Integer> fengQingYang = new TreeSet<Integer>();
TreeSet<Integer> linQingXia = new TreeSet<Integer>();
TreeSet<Integer> liuYi = new TreeSet<Integer>();
TreeSet<Integer> diPai = new TreeSet<Integer>();
for (int x = 0; x < array.size(); x++) {
if (x >= array.size() - 3) {
diPai.add(array.get(x));
} else if (x % 3 == 0) {
fengQingYang.add(array.get(x));
} else if (x % 3 == 1) {
linQingXia.add(array.get(x));
} else if (x % 3 == 2) {
liuYi.add(array.get(x));
}
}
// 看牌(遍历TreeSet集合,获取编号,到HashMap集合找对应的牌)
lookPoker("风清扬", fengQingYang, hm);
lookPoker("林青霞", linQingXia, hm);
lookPoker("刘意", liuYi, hm);
lookPoker("底牌", diPai, hm);
}
// 写看牌的功能
public static void lookPoker(String name, TreeSet<Integer> ts,
HashMap<Integer, String> hm) {
System.out.print(name + "的牌是:");
for (Integer key : ts) {
String value = hm.get(key);
System.out.print(value + " ");
}
System.out.println();
}
}
集合总结
1.集合总结:
Collection(单列集合)
List(有序,可重复)
ArrayList
底层数据结构是数组,查询快,增删慢
线程不安全,效率高
Vector
底层数据结构是数组,查询快,增删慢
线程安全,效率低
LinkedList
底层数据结构是链表,查询慢,增删快
线程不安全,效率高
Set(无序,唯一)
HashSet
底层数据结构是哈希表。
哈希表依赖两个方法:hashCode()和equals()
执行顺序:
首先判断hashCode()值是否相同
是:继续执行equals(),看其返回值
是true:说明元素重复,不添加
是false:就直接添加到集合
否:就直接添加到集合
最终:
自动生成hashCode()和equals()即可
LinkedHashSet
底层数据结构由链表和哈希表组成。
由链表保证元素有序。
由哈希表保证元素唯一。
TreeSet
底层数据结构是红黑树。(是一种自平衡的二叉树)
如何保证元素唯一性呢?
根据比较的返回值是否是0来决定
如何保证元素的排序呢?
两种方式
自然排序(元素具备比较性)
让元素所属的类实现Comparable接口
比较器排序(集合具备比较性)
让集合接收一个Comparator的实现类对象
Map(双列集合)
A:Map集合的数据结构仅仅针对键有效,与值无关。
B:存储的是键值对形式的元素,键唯一,值可重复。
HashMap
底层数据结构是哈希表。线程不安全,效率高
哈希表依赖两个方法:hashCode()和equals()
执行顺序:
首先判断hashCode()值是否相同
是:继续执行equals(),看其返回值
是true:说明元素重复,不添加
是false:就直接添加到集合
否:就直接添加到集合
最终:
自动生成hashCode()和equals()即可
LinkedHashMap
底层数据结构由链表和哈希表组成。
由链表保证元素有序。
由哈希表保证元素唯一。
Hashtable
底层数据结构是哈希表。线程安全,效率低
哈希表依赖两个方法:hashCode()和equals()
执行顺序:
首先判断hashCode()值是否相同
是:继续执行equals(),看其返回值
是true:说明元素重复,不添加
是false:就直接添加到集合
否:就直接添加到集合
最终:
自动生成hashCode()和equals()即可
TreeMap
底层数据结构是红黑树。(是一种自平衡的二叉树)
如何保证元素唯一性呢?
根据比较的返回值是否是0来决定
如何保证元素的排序呢?
两种方式
自然排序(元素具备比较性)
让元素所属的类实现Comparable接口
比较器排序(集合具备比较性)
让集合接收一个Comparator的实现类对象
2.使用那种集合得看需求:
是否是键值对形式:
是:Map
键是否需要排序:
是:TreeMap
否:HashMap
不知道,就使用HashMap。
否:Collection
元素是否唯一:
是:Set
元素是否需要排序:
是:TreeSet
否:HashSet
不知道,就使用HashSet
否:List
要安全吗:
是:Vector(其实我们也不用它,后面我们讲解了多线程以后,我在给你回顾用谁)
否:ArrayList或者LinkedList
增删多:LinkedList
查询多:ArrayList
不知道,就使用ArrayList
不知道,就使用ArrayList
3.集合的常见方法及遍历方式:
Collection:
add()
remove()
contains()
iterator()
size()
遍历:
增强for
迭代器
|--List
get()
遍历:
增强for
迭代器
普通for
|--Set
遍历:
增强for
迭代器
Map:
put()
remove()
containskey(),containsValue()
keySet()
get()
value()
entrySet()
size()
遍历:
根据键找值
根据键值对对象分别找键和值
要求:
我讲解过的任意一个集合,我要求你存储什么,你就能够存储什么。
并且,还要能够遍历出来。
4.ArrayList,LinkedList,HashSet,HashMap(掌握)
● 存储字符串和自定义对象数据并遍历
5.集合的嵌套遍历(理解)