java学习笔记(8) - 面向对象(8) : 集合

集合的介绍

// TODO 集合
// 生活中也有集合的概念。是一个动词

// Java中的集合是一个名词,数据的一种容器,用于容纳数据
// Java提供了完整的集合框架

// TODO 问题:什么时候需要一个容纳数据的容器,也就是集合对象?
// Java集合框架中就包含了对不确定个数的数据处理的集合类
// TODO 问题:如果只是为了容纳数据,可以是直接使用数组,为什么要学习集合?
// 数组使用起来不方便。在数据个数不确定的场合,数组使用起来不是很方便

// TODO 总结:对不确定的有关系的数据进行相同的逻辑处理的场合,使用集合是一个不错的选择

// TODO 根据数据的不同,Java的集合分为2大体系:
// 1. 单一数据体系 : Collection接口定义了相关的规则
// 2. 成对出现的数据体系 : Map接口定义了相关的规则
// 所谓的成对的数据,就是2个数据有关系,可以根据第一个数据关联到第二个数据
// 也称之为键值对数据 ,(123123[身份证], zhangsan[人名]) => (key, value)

集合的常用接口和类

// TODO 集合
// 1. Collection接口
// 常用的子接口
// List :按照插入顺序保存数据,数据可以重复的(第一个插入,取出就是第一个)
// 具体的实现类: ArrayList, LinkedList
// Set : 集,无序保存,数据不能重复
// 具体的实现类 HashSet
// Queue : 队列
// 具体的实现类:ArrayBlockingQueue
// 2. Map接口
// 具体的实现 : HashMap, Hashtable

ArrayList(动态数组结构)的基本操作

package chapter06;

import java.util.ArrayList;

public class Collection_01 {
public static void main(String[] args) throws Exception {
// TODO 集合 - Collection - List
// ArrayList : Array + List
// TODO List : 列表,清单
// 按照数据插入顺序进行存储
// TODO Array : 数组,阵列

// TODO 创建第一个集合对象:ArrayList
ArrayList list = new ArrayList(3);
// 1. 不需要传递构造参数,直接new就可以,底层数组为空数组
// 2. 构造参数需要传递一个int类型的值,用于设定底层数组的长度
// 3. 构造参数需要传递一个Collection集合类型的值,用于将其他集合中的数据放置在当前集合中
// 4. 当新增的数据超过集合的长度时,jvm底层会自动创建一个新的数组长度(一般为10)来取代旧的,也就是所谓的扩容

// TODO 增加数据
// add方法可以增加数据,只要将数据作为参数传递到add方法即可
// 添加数据时,如果集合中没有任何的数据,那么底层会创建长度为10的数组
list.add("zhangsan");
list.add("zhangsan");
list.add("wangwu");
list.add("zhaoliu");
// TODO 访问集合中的数据
// 获取集合中数据的条数
System.out.println(list.size()); // 输出 4
// 获取指定位置的数据,可以采用索引的方式
System.out.println(list.get(1)); // 输出 "zhangsan"
// 遍历集合中的数据
for ( int i = 0; i < list.size(); i++ ) {
//System.out.println("集合中的数据:" + list.get(i));
}
// TODO 如果循环遍历集合数据时,不关心数据的位置,那么可以采用特殊的for循环
// for (循环对象:集合) {}
for ( Object obj : list ) {
System.out.println("集合中的数据:" + obj); // 循环输出集合中的数据
}

// TODO 修改数据
// 将指定位置的数据进行修改,set方法需要传递2个参数,第一个参数表示数据的位置,第二个参数修改的值。
// 方法会返回结果,这个结果就是更新前的值
Object oldVal = list.set(1, "lisi");
System.out.println("修改前的值:" + oldVal); // 成功修改集合中的数据同时该方法返回修改前的数据

// TODO 删除数据
// 将指定位置的数据进行删除,remove方法需要传递1个参数,参数表示数据的位置。
// 方法会返回结果,这个结果就是删除的值
Object removeVal = list.remove(1);
System.out.println("删除的值:" + removeVal); // 成功删除集合中的数据同时该方法返回删除前的数据

// TODO 打印集合对象
System.out.println(list); // [zhangsan, wangwu, zhaoliu]

System.out.println("main方法执行完毕");
}
}

ArrayList的常用方法

package chapter06;
import java.util.ArrayList;

public class Collection_ArrayList {
public static void main(String[] args) throws Exception {
// TODO 集合 - Collection - List
// ArrayList的常用方法
ArrayList list = new ArrayList();

list.add("zhangsan");
list.add("lisi");
list.add("wangwu");
// list.add("zhangsan");
// list.add("zhangsan");

// add方法可以传递2个参数的,第一个参数表示数据增加的位置(索引),第二个参数表示数据
list.add(1, "zhaoliu");

System.out.println(list.get(1)); // 获取第二个元素 输出: zhaoliu

ArrayList otherList = new ArrayList();
otherList.add("1");
otherList.add("2");
otherList.add("3");
list.addAll( otherList ); // 给该集合新增一个集合 -> [zhangsan,lisi,wangwu,1,2,3]

// size方法表示集合内部数据的数量
System.out.println(list.size()); // 输出该集合的长度,不管该集合是否为空
// 清空集合中的数据
// list.clear(); // []
// 删除指定集合中的数据
// list.removeAll(otherList);// 清空指定加入的集合,对应 addAll
// 判断集合中的数据是否为空
System.out.println(list.isEmpty()); // 判断当前集合是否为空

// 用于判断集合中是否存在某条数据,返回布尔类型的值
System.out.println(list.contains("zhangsan123"));
// 用于获取数据在索引中的第一个位置,如果数据不存在,那么返回-1
System.out.println(list.indexOf("zhangsan123"));
System.out.println(list.indexOf("zhangsan")); // 获取第一个符合元素的位置 0
System.out.println(list.lastIndexOf("zhangsan"));// 获取最后一个符合元素的位置

Object[] objects = list.toArray(); // 将集合转化为数组

// 复制新集合
Object clone = list.clone(); // 复制该集合
System.out.println(clone); // [zhangsan, zhaoliu, lisi, wangwu, 1, 2, 3]
ArrayList list1 = (ArrayList)clone;

System.out.println(list); // [zhangsan, zhaoliu, lisi, wangwu, 1, 2, 3]
System.out.println(list1); // [zhangsan, zhaoliu, lisi, wangwu, 1, 2, 3]
}
}

LinkedList(链表结构)的常用方法

  • LinkedListList接口的实现类,因此也实现了List的方法。但LinkedList是采用链表结构的方式来实现List接口的,因此在进 行insertremove动作时效率要比ArrayList高。

image

package chapter06;

import java.util.ArrayList;
import java.util.LinkedList;

public class Collection_02 {
public static void main(String[] args) throws Exception {
// TODO 集合 - Collection - List
// LinkedList : Linked(连接) + List
// 构建集合对象
LinkedList list = new LinkedList();

// 增加第一个数据
list.add("zhangsan");
list.add("lisi");
list.add("wangwu");
list.addFirst("lisi"); // 新增第一个连接数据(新增连接表头,更加高效)
list.add(1, "wangwu"); // 新增第二个数据

// 向前面新增一个元素
list.push("push");

// TODO 获取数据
System.out.println(list.getFirst()); // 获取第一个数据 wangwu
System.out.println(list.getLast()); // 获取最后一个数据 wangwu

// TODO 获取数据(遍历数据)
System.out.println(list.get(1)); // 获取第二个数据
for ( int i = 0; i < list.size(); i++ ) {
//System.out.println(list.get(i));
}

for ( Object obj : list ) {
System.out.println(obj);
}
// 修改数据
// list.set(1, "zhaoliu"); // 修改数据

// 删除数据
// list.remove("zhangsan"); // 移除某一个数据
list.remove(2); // 也可以根据下标来移除对应的元素

// contains 判断是否包含某一个属性
System.out.println(list.contains("lisi")); // true

// 其他方法
// list.pop(); // 删除第一个数据(对比 push -> 新增第一个数据 )
// list.addAll(); // 新增插入一个集合
// list.clear(); // 清除整个集合
// list.indexOf(); // 判断是否存在某一个元素返回该元素的下标(第一个),无则返回 -1
// list.lastIndexOf(); // 判断返回最后一个存在该元素的下标,无则返回 -1
// list.element(); // 获取该集合的第一个元素,相当于 .getFirst()

// TODO 打印集合数据
System.out.println(list); // [lisi, wangwu, lisi, wangwu]
}
}

新增操作图解

image

删除操作图解

image

集合 - 比较器(排序)

  • Java中的比较器(Comparator)是一种对象,用于定义两个对象之间的比较规则。它是一个独立的类,实现了Comparator接口,通常用于对集合中的元素进行排序。Comparator接口中有一个compare()方法,它接受两个对象作为参数,并返回一个int值,表示它们的顺序。

例如:

 
import java.util.*;

public class StringComparator implements Comparator<String> {
public int compare(String s1, String s2) {
return s1.compareTo(s2);
}
}

public class Main {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("apple");
list.add("banana");
list.add("pear");
list.add("orange");

// 使用自然排序
Collections.sort(list);
System.out.println("自然排序结果:" + list);

// 使用自定义比较器排序
StringComparator comparator = new StringComparator();
Collections.sort(list, comparator);
System.out.println("自定义比较器排序结果:" + list);
}
}

在这个例子中,我们定义了一个StringComparator类,实现了Comparator接口中的compare()方法,用于按字典序比较两个字符串的大小。然后使用Collections.sort()方法对字符串列表进行排序,分别使用自然排序和自定义比较器排序,输出排序结果。

再比如我们可以自定义一个排序方法:

package chapter06;

import java.util.ArrayList;
import java.util.Comparator;

public class Collection_sort {
public static void main(String[] args) {

// TODO 集合 - Collection - List
// Sort排序
ArrayList list = new ArrayList();
list.add(1);
list.add(3);
list.add(2);
// 1,3,2

// 1,2,3

Integer i1 = 1;
Integer i2 = 0;

// List对象进行排序,需要传递一个实现了比较器接口的对象
list.sort(new NumberComparator()); // .sort方法接收一个比较类为参数

System.out.println(list); // 第一个数比第二个数小 降序 -> [1, 2, 3]
}
}

// 重写一个 compare 类 但是需要继承 Comparator 接口且里面必须有compare方法,需要我们自己重写
class NumberComparator implements Comparator<Integer> {
@Override // 重写 compare 方法 (填入我们自己的逻辑,根据第一个与第二个数的大小来判断升序还是降序)
public int compare(Integer o1, Integer o2) {
if ( o1 < o2 ) { // 第一个数比第二个数小,设置为 -1 降序
// 正序排列 (第一个数比第二个数小 , 由小到大)
return -1;
} else if ( o1 == o2 ) {
// 不排列 (源数据排列)
return 0;
} else { // 第一个数比第二个数大,设置为 1 升序
// 倒序排列 (第一个数比第二个数大 , 由大到小)
return 1;
}
}
}

Comparable 接口定义

说明:根据这个方法的返回值来判断

大于0:说明前一个比后一个大

小于0:说明前一个小于后一个

等于0:说明这两个一样大

ArrayListlinkedList 的对比

  1. ArrayList:
    • 优点:ArrayList 是实现了基于动态数组的数据结构,因为地址连续,一旦数据存储好了,查询 操作效率会比较高(在内存里是连着放的)。当然是在不扩容的情况下
    • 缺点:因为地址连续, ArrayList要移动数据,需要把所有元素的位置向后移动或者向前移动,效率慢,所以插入和删除操作效率比较低。
  2. LinkedList
    • 优点:LinkedList基于链表的数据结构,地址是任意的,所以在开辟内存空间的时候不需要等一个连续的地址,对于新增和删除操作addremoveLinedList比较占优势。LinkedList 适用于要头尾操作或插入指定位置的场景
    • 因为LinkedList要移动指针,所以查询操作性能比较低。