1. Collection
1.1 概述
-
通过继承体系发现,集合中两大分类List和Set, 并且 两个都继承了Collection,那也就意味着Collection中的方法是List和Set都有的。
-
Collection 集合只能保存引用数据类型,不能保存基本数据类型
-
因为 底层就是 Object[] 数组,既然是Object 所以 即可以说只能保存Object单一元素,也可以说可以保存任何元素,因为Object是祖类,所有类型都会发生向上转型(多态)
-
基本类型真的不能存吗? 真的,因为基本类型和Object没有关系,但是 可以把基本类型转换为对应的包装类类型,而 包装类 是Object的子类,这样就能间接存储基本类型了
-
常用方法 :
-
boolean add() : 添加元素
-
void clear() : 清空集合
-
boolean remove() 删除某个元素
-
boolean isEmpty() 判断集合中是否为空
-
boolean contains() : 是否包含某个元素
-
int size() 返回集合中 元素个数
下面是关于上述方法的一些代码测试:
public static void main(String[] args) {
//接口不能创建对象,我们以ArrayList为例,由于发生多态,丢失了ArrayList的特有方法
//所以我们现在能够调用的方法都是Collection中的
Collection c1=new ArrayList();
//判断集合中是否为空
System.out.println(c1.isEmpty());
//添加元素
c1.add("as");
//12为int类型的,会先自动装箱为Integer类型的,然后再向上转型为Object类型的
c1.add(12);
Object o1=new Object();
c1.add(o1);
// size不是数组的长度,而是数组中已经添加的元素个数
System.out.println(c1.size());
//转数组
Object[] arr=c1.toArray();
for (Object object : arr) {
System.out.println(object);
}
//删除,12为int类型的,自动装箱成Integer类型的,然后向上转型成Object类的
c1.remove(12);
//重新获取数组
arr=c1.toArray();
for (Object object : arr) {
System.out.println(object);
}
// 清空数组中元素
c1.clear();
//输出现有数组中元素的个数
System.out.println(c1.size());
}
来深入理解一下
- boolean contains(Object o) 判断集合中是否包含某个元素
- boolean remove(Object o)在集合中删除指定元素
- 这两个方法,底层都会去调用equals方法进行比较
- 比如c.contains(“asd”)会用asd调用String的equals方法,挨个和集合中元素进行比较
- remove也是一样,判断谁就用谁调用equals方法,和集合中元素进行比较
- 所以如果是存储的自定义类型(类对象),就需要根据需求覆写equals方法
public class Collection03 {
public static void main(String[] args) {
//创建集合
Collection c=new ArrayList();
Integer i1=new Integer(1);
Integer i2=new Integer(2);
//包装类是Object类的子类
c.add(i1);
c.add(i2);
Integer i4=new Integer(2);
//false 因为==比较引用类型比较的是地址,new了两次
System.out.println(i4==i2);
//true,因为调用的是Integer类的equals方法,重写了equals方法,比较的是值
System.out.println(i4.equals(i2));
// true,因为contains会自动调用 i4的equals方法和集合元素进行比较
// 由于 i4是Integer类型,因为Integer覆写了equals方法,所以比较的是值
System.out.println(c.contains(i4));
Manager m1 = new Manager(2, "张三");
Manager m2 = new Manager(2, "张三");
c.add(m1);
// true , 因为添加了m1,肯定是true
System.out.println(c.contains(m1));
// false,尽管m1和m2中数据一样,但是会调用equals方法,如果Manager中没有覆写equals方法,
// Object中的equals方法会默认比较内存地址,m2和m1的内存地址不同,所以不存在
System.out.println(c.contains(m2));
// 需求 : 如果编号和姓名一致 就认为是同一个对象
// 在Manager中覆写了equals方法之后,再比较 就是true了
System.out.println(c.contains(m2));
}
}
class Manager {
private int no;
private String name;
@Override
public String toString() {
return "Manager [no=" + no + ", name=" + name + "]";
}
@Override
public boolean equals(Object obj) {
System.out.println("=========");
if (this == obj) {
return true;
}
if (obj instanceof Manager) {
Manager m2 = (Manager) obj;
if (this.name.equals(m2.name) && this.no == m2.no) {
return true;
}
}
return false;
}
public int getNo() {
return no;
}
public void setNo(int no) {
this.no = no;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Manager(int no, String name) {
super();
this.no = no;
this.name = name;
}
}
2.Iterator
Iterator是迭代器,迭代器是一种模式,它可以使序列类型的数据结构的遍历行为和被遍历的对象分离
我们无需关心该序列底层数据结构是什么,只要拿到这个对象,使用迭代器就可以遍历这个对象的内部数据
创建迭代器对象
Iterator it=集合对象.iterator();
调用集合对象自己的iterator方法可以创建自己的迭代器
Iterator的方法
1 boolean hasNext(): 判断光标下一位是否还有元素,有就返回true,没有就返回false,
生成迭代器的时候,光标不是指向第一个元素的,而是在最顶端,没有指向任何元素,
光标不会自动复位,使用完之后,必须重新生成
2 E next():把光标向下移动一位,并返回该位上的数据
3 remove():删除当前光标指向的元素
方法使用的顺序是1,2,3
迭代器相当于一个索引器
迭代器一旦创建,如果集合中添加或者删除元素,那么迭代器必须重新生成,因为个数变了
否则调用next()方法会报错,但是更改数据不用重新生成
增强for循环,foreach就是为了让用iterator循环访问的形式变简单,写起来方便,但是需要有删除操作的话还是需要用原来的iterator形式
forEach也是迭代器,使用forEach和使用iterator一样,不能添加或删除集合数据,但是更改可以
而普通for和while这种遍历,都是和数据结构存储有关系的,比如数组需要使用arr[index]去访问,可以进行添加和删除操作
使用迭代器进行遍历时,如果需要删除元素,只能使用迭代器的remove()方法,不能使用集合的remove方法
public static void main(String[] args) {
//创建集合
Collection c=new ArrayList();
//向集合里添加元素
//int 自动装箱成Integer类型,然后Integer类型在向上转型成Object类型
c.add(12);
c.add("as");
//double自动装箱成Double类型,然后Double类型在向上转型成Object类型
c.add(2.4);
c.add(false);
c.add(null);
//创建迭代器
Iterator it=c.iterator();
//判断光标的下一位还有没有元素,没有就终止,.hasNext()返回布尔类型的值
while(it.hasNext()){
//把光标向下移动一位,并返回该位上的值
Object o=it.next();
System.out.println(o);
}
//光标已经落在了最后一位,下一位没有元素了
// false
System.out.println(it.hasNext());
// 如果此时 添加或者删除元素
c.add(1);
// 只要迭代器生成,集合添加或者删除,再使用这个迭代器就会报错
// java.util.ConcurrentModificationException
// 如果要使用迭代器,需要重新生成
it=c.iterator();
while(it.hasNext()){
// 能进来说明有
//把光标向下移动一位,并将该位上的数据给到o
Object o = it.next();
// 生成迭代器不能使用集合的remove,除非重新生成,否则报错
//c.remove(1);
// 但是可以使用迭代器的remove进行删除元素的操作,删除当前光标指向的元素
it.remove();
System.out.println(o);
}
// 循环删除完了
//输出集合中的元素个数
System.out.println(c.size());
}
3.List
-
List元素的特性 : 有序可重复
-
存入顺序和取出顺序是一致的,并且有指定的下标可以表示数据的唯一性,所以可以存在重复数据
-
底层实现
-
ArrayList : 底层是数组,查询效率较高,添加和删除效率较低
-
LinkedList : 底层是双向链表,查询效率较低,添加和删除效率较高
Vector 已经过时了,ArrayList是Vector的升级版,而Vector属于线程安全,ArrayList属于非线程安全,所以ArrayList效率较高
ArrayList
public class Collection05 {
/*ArrayList : 底层索引数组,下标0开始 初始化容量为 10 ,扩大容量为 原始容量的1.5倍.非线程安全,效率高
*
* Vector : 初始化容量为10 , 扩大容量为 原始容量的 2 倍,线程安全,效率低
*
* 所以 Vector已经废弃
*
* List遍历方式
* while , for, foreach , iterator
*
* add(E e) : 将指定元素添加到尾部
* add(int index,E e) : 将指定元素添加到指定位置,原位置内容统一向后移动
* set(int index,E e) : 更改指定位置上的元素值
* get(int index) : 获取指定索引上的元素
* remove(int index) : 删除指定索引上的元素
* remove(Object o) : 删除指定元素
* */
public static void main(String[] args) {
List li = new ArrayList();
// 数组中 : 数组只能保存同一数据类型
// 集合中 : 可以存储任意类型
// 而ArrayList底层又是数组,很简单,因为ArrayList底层是Object[] 数组,
// 同理 我们单独声明Object[]数组的话,也可以说可以存储任意数据
// 因为任何数据 都可以转型为Object类型,包括基本(自动装箱再向上转型)
// 把数据添加到尾部
li.add(1);
// 把指定元素,插入到指定位置,该位置上的元素统一向后移动
li.add(0, 2);
System.out.println(li);
// 更改指定位置上的元素
li.set(0, 3);
System.out.println(li);
// 获取指定下标对应的数据
Object o = li.get(1);
System.out.println(o);
// 删除 ,方法重载,如果 是 int 说明是要删除元素的下标是什么, Object 才是被删除的对象
// 删除下标为3的元素
// li.remove(3);
// 删除元素值 为 Integer并且值为3的元素
li.remove(new Integer(3));
System.out.println(li);
li.add("aa");
li.add("aa2");
li.add("aa3");
for (Object object : li) {
System.out.println(object);
}
System.out.println("----");
for (int i = 0; i < li.size(); i++) {
System.out.println(li.get(i));
}
}
}
LinkedList
public class Collection06 {
// LinkedList : 底层是双向链表
// 链表 : 链表中保存的是节点,每个节点中有三个元素
// 1 自身对象(添加的元素) , 2 上一个节点的地址 , 3 下一个节点的地址
// 链表是没有下标的,内存空间也不是连续的,所以查询比较慢
// 由于内存空间不是连续的,只是保存上下节点的引用,随意添加删除比较快
//
// add(E e) : 添加到尾部
// push(E e) : 添加到头部
// addFirst(E e) : 添加到头部
// addLast(E e) : 添加到尾部
// offerFirst(E e) 添加到头部,成功返回true
// offerLast(E e) : 添加到尾部,成功返回true
// get(int index) : 返回指定下标对应的数据(链表没有下标,只是模拟下标,方便我们查询使用)
//
// 本质 就调用了两个方法 : linkLast 和 linkFirst 所以没任何区别
public static void main(String[] args) {
//底层是双向链表
LinkedList linkedList = new LinkedList();
//添加到尾部
linkedList.add(1);
//添加到头部
linkedList.addFirst(2);
//添加到尾部
linkedList.addLast(3);
//添加到头部
linkedList.push(4);
//添加到头部,成功返回true
linkedList.offerFirst(5);
//添加到尾部,成功返回true
linkedList.offerLast(6);
//foreach 遍历
for (Object object : linkedList) {
System.out.println(object);
}
System.out.println("----");
for (int i = 0; i < linkedList.size(); i++) {
System.out.println(linkedList.get(i));
}
// --------
System.out.println("===");
LinkedList linked = new LinkedList();
//添加到尾部
linked.add("a");
linked.add("b");
linked.add("c");
//输出链表元素个数
System.out.println(linked.size());
//获取链表指定位置上的元素
System.out.println(linked.get(2));
}
}
4、Set
Set元素特点 : 无序不可重复
- TreeSet : 底层红黑树
- HashSet : 底层散列表
public class Collection07 {
//Set元素特定:无序,不可重复
public static void main(String[] args) {
Set s = new TreeSet();
//添加到尾部
s.add(1);
s.add(3);
//输出集合中元素个数
System.out.println(s.size());
//Set覆写了toString方法
System.out.println(s);
//遍历
for (Object object : s) {
System.out.println(object);
}
}
}
TreeSet
-
元素必须有序,添加的元素会按照某种规则自动排序,所以添加元素时需要比较元素,那么注意添加不同类型时会报错
-
想要使用TreeSet,元素必须要排序
数字 : 默认从小到大
字符串 : 默认比较每位ASCII码
日期 : 默认比较自然日期 昨天-今天-明天下面是代码
public static void main(String[] args) {
//创建TreeSet类的对象treeSet
TreeSet treeSet = new TreeSet();
treeSet.add(1);
treeSet.add(3);
treeSet.add(2);
treeSet.add(7);
// 上面是数字,下面如果是字符串就会导致 报错,因为添加的时候需要比较元素大小
// 而不同类型 是没有可比性的
// treeSet.add("a");
for (Object object : treeSet) {
System.out.println(object);
}
System.out.println("----");
TreeSet treeSet2 = new TreeSet();
treeSet2.add("a");
treeSet2.add("1");
// 如果有多个字符,先比较第一位,如果第一位不同,则第一位小的在上面
// 如果第一位相同,再比较第二位,以此类推
treeSet2.add("11");
treeSet2.add("101");
treeSet2.add("b");
treeSet2.add("n");
for (Object object : treeSet2) {
System.out.println(object);
}
}
public class Collection09 {
// 比较器有两种 : 1 元素自身比较器, 2 比较器类
//
// 为什么字符串,Integer,Date可以排序?
// 因为都实现了 implements Comparable
//
// 因为使用treeSet在进行数据添加的时候,会自动调用该对象的compareTo()方法和集合内元素进行比较
//
// 如果我们想要存储自定义类型怎么办?
// 需要实现comparable接口才行
public static void main(String[] args) {
TreeSet treeSet = new TreeSet();
User user1 = new User(11);
User user2 = new User(13);
User user3 = new User(6);
//添加元素时自动调用User类的compareTo()方法和集合内元素进行比较
treeSet.add(user1);
treeSet.add(user2);
treeSet.add(user3);
System.out.println(treeSet.size());
for (Object object : treeSet) {
System.out.println(object);
}
}
}
class User implements Comparable{
private int age;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public User(int age) {
super();
this.age = age;
}
@Override
public String toString() {
return "User [age=" + age + "]";
}
//实现Comparable接口覆写compareTo方法
@Override
public int compareTo(Object o) {
// this 是当前对象
// o 是集合内对象
// 并且 返回值为0 表示 相等,则不添加,set不允许有重复元素
// 返回大于0 的 表示 要添加的元素大,则放到后面
// 返回小于0 的 表示 要添加的元素小,则放到前面
User user = (User) o;
// 升序
return this.age - user.age;
// 降序
// return this.age - user.age;
}
}
共有条评论 网友评论