Collection
集合概述
集合体系共分为两大类,分别是List和Set,并且都继承了Collection。
Collection集合由于底层是Object[]数组,所以只能存储单个元素,并且只能保存引用数据类型,不能保存基本数据类型。存储基本数据类型可以使用基本数据类型对应的包装类,完成自动装箱操作
常用方法:
- boolean add() : 添加元素
- void clear() : 清空集合
- boolean remove() 删除某个元素
- boolean isEmpty() 判断集合中是否为空
- boolean contains() : 是否包含某个元素
- int size() 返回集合中 元素个数
如:
public class Collection_01 {
public static void main(String[] args) {
//Collection是一个接口,不能创建对象,所以使用ArrayList创建集合对象
//因为发生多态,所以丢失了ArrayList中特有的方法
//所以现在调用的方法都是Collection的方法
Collection c1 = new ArrayList();
//判断是否为空
System.out.println(c1.isEmpty());
//添加数据
c1.add("asd");
//123为int值,对自动装箱为Integer,再向上转型为Object
c1.add(123);
Object o1 = new Object();
c1.add(o1);
//判断是否为空
System.out.println(c1.isEmpty());
//输出集合中的个数
System.out.println(c1.size());
//遍历
//将集合转换为数组
Object[] arr = c1.toArray();
for (Object object : arr) {
System.out.println(object);
System.out.println(object.getClass());
}
System.out.println("---------------------------");
// 删除
c1.remove(123);
// 重新获取数组
arr = c1.toArray();
for (Object object : arr) {
System.out.println(object);
}
// 清空数组中元素
c1.clear();
System.out.println(c1.size());
}
}
继承体系
- Collection ——> List ——> ArrayList类
- Collection ——> List ——> LinkedList类
- Collection ——> Set ——> HashSet类
- Collection ——> Set ——> SortedSet接口 ——> TreeSet类
- Map ——> HashMap类
- Map ——> SortedMap ——> TreeMap类
collection注意事项
使用集合中
- boolean contains(Object o):判断集合中是否包含某个元素
- boolean remove(Object o):在集合中删除指定元素
这两个方法时,都会调用equals方法,所以在集合中存储类对象时,需要在类中重写toString方法
如:
public class Collection_03 {
public static void main(String[] args) {
Manager m1 = new Manager(2, "张三");
Manager m2 = new Manager(2, "张三");
c.add(m1);
//true
System.out.println(c.contains(m1));
//如果Manager没有重写equals,结果为false,因为调用的是Object的equals,比较的是内存地址
//如果Manager重写equals,结果为true
System.out.println(c.contains(m2));
}
}
class Manager {
private int id;
private String name;
public Manager(int id, String name) {
this.id = id;
this.name = name;
}
@Override
public String toString() {
return "Manager{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Manager manager = (Manager) o;
return id == manager.id && Objects.equals(name, manager.name);
}
@Override
public int hashCode() {
return Objects.hash(id, name);
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
迭代器
迭代器是一种模式,它可以使序列类型的数据结构的遍历行为和被遍历的对象分离。
也就是说我们无需关心序列的底层数据结构是什么样子的,只要拿到这个对象,使用迭代器就可以遍历这个对象的内部数据。
语法格式:
Iterator it = 集合对象.iterator();
通过调用集合对象自己的iterator方法就可以创建属于自己的迭代器。
常用方法:
- boolean hasNext() : 判断光标下一位是否还有元素,有就返回true,没有就返回false,生成迭代器的时候,光标不是指向第一个元素,而是在最顶端,没有指向任何元素,并且 光标不会自动复位,使用完之后,必须重新生成。
- E next() : 把光标向下移动一位,并返回该位上的数据(E表示返回的数据类型)
- remove() : 删除当前光标指向的元素
上述三个方法的使用顺序是从上到下使用,迭代器一旦创建,如果集合中添加或者删除元素,那么迭代器必须重新生成。否则调用next()方法会报错。更改数据不需要重新生成迭代器。
foreach循环就是迭代器的简单实现形式。
如:
public class Collection_02_Iterator {
public static void main(String[] args) {
//创建集合
Collection c = new ArrayList();
//添加元素
c.add(123);
c.add("asd");
c.add(2.3);
c.add(false);
c.add(null);
//创建迭代器
Iterator it = c.iterator();
//判断还有没有元素,没有就终止
while (it.hasNext()){
//能进循环说明还有下一个
Object o = it.next();
System.out.println(o);
}
System.out.println("=======================");
//false
System.out.println(it.hasNext());
//使用for完成
//不能进入for,因为光标已经到最后了
//如果想再次使用,只能重新创建迭代器
for (;it.hasNext();){
//能进循环说明还有下一个
Object o = it.next();
System.out.println(o);
}
c.add(1);
//只要迭代器生成,添加或删除元素,迭代器就会报错
//如果要使用,需要重新生成
//java.util.ConcurrentModificationException
it = c.iterator();
while (it.hasNext()){
Object o = it.next();
//一旦生成迭代器 不能使用 集合的remove,除非重新生成,否则报错
//c.remove(1);
//但是可以使用迭代器的remove进行删除操作
it.remove();
System.out.println(o);
}
System.out.println(c.size());
}
}
List
List集合的特点:有序可重复。并且 存入顺序和取出顺序是一致的,并且有指定的下标,可以表示数据的唯一性,所以可以存在重复数据。
ArrayList和LinkedList继承了List
- ArrayList : 底层是数组,查询效率较高,添加和删除效率较低
- LinkedList : 底层是双向链表,查询效率较低,添加和删除效率较高
ArrayList
ArrayList:底层是索引数组,下标从0开始,初始容量为10,扩大容量为原始容量的1.5倍。非线程安全,效率较高。
常用方法:
- add(E e) : 将指定元素添加到尾部
- add(int index,E e) : 将指定元素添加到指定位置,原位置内容统一向后移动
- set(int index,E e) : 更改指定位置上的元素值
- get(int index) : 获取指定索引上的元素
- remove(int index) : 删除指定索引上的元素
- remove(Object o) : 删除指定元素
如:
public class Collection_05_ArrayList {
public static void main(String[] args) {
List<> li = new ArrayList<>();
//集合底层是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+","+o.getClass());
//remove(),删除方法重载,如果是int,说明是要删除元素的下标是什么,Object才是被删除的对象
//li.remove(3);
//删除值为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
LinkedList:底层是双向链表,链表中保存的是节点信息,每个节点包括三个元素,分别是自身元素对象,上一个节点的地址和下一个节点的地址。
链表是没有下标的,内存空间不连续,所以查询较慢,删除增加快。
常用方法:
- add(E e) : 添加到尾部
- push(E e) : 添加到头部
- ddFirst(E e) : 添加到头部
- addLast(E e) : 添加到尾部
- offerFirst(E e) 添加到头部,成功返回true
- offerLast(E e) : 添加到尾部,成功返回true
- get(int index) : 返回指定下标对应的数据(链表没有下标,只是模拟下标,方便我们查询使用)
添加的本质其实就调用了linkLast()和linkFirst()两个方法。
如:
public class Collection_06_LinkedList {
public static void main(String[] args) {
LinkedList<Integer> linkedList = new LinkedList<>();
linkedList.add(1);
linkedList.addFirst(2);
linkedList.addLast(3);
linkedList.push(4);
linkedList.offerFirst(5);
linkedList.offerLast(6);
//使用foreach和for遍历
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<String> list = new LinkedList<String>();
list.add("a");
list.add("b");
list.add("c");
list.remove(1);
System.out.println(list.size());
System.out.println(list.get(1));
}
}
Set
Set特点:无序和不可重复
Set的子类:TreeSet和HashSet
TreeSet:底层红黑树
HashSet:底层散列表
ThreeSet
TreeSet:元素必须有序,添加的元素会按照某种规则自动排序。想要使用TreeSet,元素必须有序。
- 数字:默认从小到大
- 字符串:默认比较每位的ASCII码
如果有多个字符,先比较第一位,如果第一位不同,则第一位小的在上面; 如果第一位相同,再比较第二位,以此类推 - 日期:默认比较自然日期:昨天,今天,明天
如:
public class Collection_08_TreeSet {
public static void main(String[] args) {
TreeSet<Integer> ts = new TreeSet<Integer>();
ts.add(1);
ts.add(7);
ts.add(3);
ts.add(5);
//上面添加的是Integer类型
//因为要实现自动排序
//所以不能添加其他类型的元素
//ts.add("aa");
for (Object object : ts) {
System.out.println(object);
}
System.out.println("=======================");
TreeSet<String> ts2 = new TreeSet<>();
ts2.add("a");
ts2.add("1");
// 如果有多个字符,先比较第一位,如果第一位不同,则第一位小的在上面
// 如果第一位相同,再比较第二位,以此类推
ts2.add("11");
ts2.add("101");
ts2.add("b");
ts2.add("n");
for (Object object : ts2) {
System.out.println(object);
}
}
}
比较器的实现
比较器有两种:元素自身比较器和比较器类
使用TreeSet在进行数据添加的时候,会自动调用该对象的compareTo()方法和集合内元素进行比较。
必须通过实现Comparable接口对存储自定义类型进行比较,实现compareTo()方法。
如果不实现Comparable接口,在向TreeSet集合中添加元素时报错。
如:
public class Collection_09_TreeSet {
public static void main(String[] args) {
TreeSet<User> treeSet = new TreeSet<User>();
User user1 = new User(11);
User user2 = new User(13);
User user3 = new User(6);
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 User(int age) {
this.age = age;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"age=" + age +
'}';
}
@Override
public int compareTo(Object o) {
//this 是当前对象
//o 是集合内对象
//并且返回值值为0表示相等,则不添加
//返回大于0的表示要添加的元素大,则放到后面
//返回小于0的表示要添加的元素小,则放到前面
User user = (User) o;
//升序
//return this.age - user.age;
//降序
return user.age - this.age;
}
}
/*
输出结果为:
3
User{age=13}
User{age=11}
User{age=6}
*/