集合框架基本概念

Collection 集合框架中用于存储一个个元素的容器,List 和 Set 是它的子类

  • List: 存储有序的,可重复的数据

    • ArrayList(主要实现类)、LinkedList、Vector
  • Set: 存储无序的,不可重复的数据

    • HashSet(主要实现类)、LinkedHashSet、TreeSet

Map 集合框架中用于存储 key 到 value 的键值映射的容器

  • Map:存储一对一对的数据

    • HashMap(主要实现类)、LinkedHashMap、TreeMap、Hashtable、Properties

Collections 用于操作集合框架的一个工具类

Collection 常用方法

定义两个集合

Collection collection = new ArrayList();
Collection collection1 = new ArrayList();
collection.add("test");
collection.add("测试");
Person person = new Person(10, "xiaohua");
collection.add(person);
collection1.addAll(collection);
collection1.add("123");

add、addAll

前者用于添加对象元素,后者用于添加一整个集合比较常见

System.out.println(collection);
System.out.println(collection1);

image-20240320183228912

clear、isEmpty、size

清除集合内的元素,用于查看集合是否为空,查看集合元素数量

collection1.clear();
System.out.println(collection1.isEmpty());
System.out.println(collection1.size());

image-20240320183427511

contains、containsAll

前者是判断集合中是否含有某个对象,后者是判断一个集合是否包含另一个集合

// 没有重写 equals 方法 对于对象的比较相当于 ==
// 注意,String 已经默认重写了 equals
System.out.println(collection);
System.out.println(collection1);
System.out.println(collection.contains(person)); // true
System.out.println(collection.contains(new Person(10, "xiaohua")));// false
System.out.println(collection.contains("test")); // true
// 集合之间的包含关系
System.out.println(collection.containsAll(collection1)); // false
System.out.println(collection1.containsAll(collection)); // true

image-20240320184032131

remove、removeAll、retainAll

删除指定元素,删除两个集合的交集,只保留两个集合的交集

System.out.println(collection);
System.out.println(collection1);
collection1.removeAll(collection);
System.out.println(collection1);

System.out.println("******************");

collection1.add("test");
collection1.add("1234");
System.out.println(collection);
System.out.println(collection1);
collection1.retainAll(collection);
System.out.println(collection1);

image-20240320191555533

toArray

&& asList

把集合转换成数组

@Test
public void test4() {
Collection collection = new ArrayList();
collection.add("test");
collection.add("测试");
collection.add(new Person(10, "xiaohua"));
// 其他方法
Object[] arr = collection.toArray();
System.out.println(Arrays.toString(arr));
System.out.println(arr[0]);
System.out.println(arr[1]);
}

image-20240320184520055

Arrays中的asList 可以将数组转换成集合

public void test6() {
String[] strings = new String[]{"a", "b", "c"};
Collection collection = Arrays.asList(strings);
System.out.println(collection);
}
Iterator

迭代器

迭代器是用来遍历元素的

next、hasNext

获取集合中下一个元素,hasNext 能够判断是否有下一个元素

next 两个作用,第一个是指针下移,第二个是获取当前的元素

@Test
public void test1() {
Collection collection = new ArrayList();
collection.add("test");
collection.add("测试");
Person person = new Person(10, "xiaohua");
collection.add(person);

// 获取迭代器对象
System.out.println(collection);
Iterator iterator = collection.iterator();
// for (int i = 0; i < collection.size(); i++) {
// System.out.println(iterator.next());
// }

// hasNext
while (iterator.hasNext()) {
System.out.println(iterator.next());
}

}

对于遍历元素还有一种方式,就是增强的 for 循环

使用增强 for 循环的时候,修改 for 循环中的临时变量,将不会修改原有的数据。因为增强 for 循环是将原有的数据赋值给临时变量。

// 增强 for 循环
for (Object obj : collection) {
System.out.println(obj);
}

image-20240321144919864

List 常用方法

和 Collection 相同,大体上的方法作用是相同的。这里仅介绍 List 作为有序的集合,相比于 Collection 的不同。

List 不同类的区别

  • ArrayList:List 主要实现类,线程不安全,效率高,底层使用 Object[] 数组储存
  • LinkedList:底层使用双向链表的方式进行储存,适用于频繁插入删除的操作
  • Vector:List 的古老实现类,线程安全,效率低,底层使用 Object[] 数组储存

List 的特殊方法

add(index,

element)

add 方法相对于 Collection 多了索引,所以可以在指定位置插入元素。同样,addAll也有这样的特性。

@Test
public void test1() {
List list = new ArrayList();
list.add("test");
list.add(123);
Person person = new Person(10, "xiaohu");
list.add(person);
System.out.println(list);

// add 插入指定索引的元素
list.add(2, "321");
System.out.println(list);

// addAll 插入指定索引的集合
List list1 = new ArrayList();
list1.add("test");
list1.add("test");
list1.add("test");
list1.addAll(1, list);
System.out.println(list1);

}

image-20240321153732711

remove(index)

remove

方法删除的是索引位置的元素,如果要删除数据,用Integer的 valueof 赋值成一个对象即可

// remove 元素删除的指定索引的元素
list1.remove(0);
list1.remove(0);
System.out.println(list1);

// 使用Integer创建对象来删除指定元素
list1.remove(Integer.valueOf(123));
System.out.println(list1);

image-20240321154311522

set,get方法

因为 List 有序,所以说对于某一个位置的元素获取变的更加容易

// get,set 方法
list.set(0, person);
System.out.println(list.get(0));

image-20240321154642258

indexOf,lastIndexOf,subList

第一个是获取指定元素的首次出现的位置,第二个是获取指定元素的最后出现的位置,subList 是获取从 start 到 end 的所有元素

@Test
public void test2() {
List list = new ArrayList();
for (int i = 0; i < 10; i ++){
list.add(i);
list.add(i+1);
}
System.out.println(list);
System.out.println(list.indexOf(1));
System.out.println(list.lastIndexOf(1));
System.out.println(list.subList(0, 10));
}

image-20240321155806789

List 综合测试

综合测试一

image-20240321183700520

public class StudentTest {
public static void main(String[] args) {
List student = new ArrayList();
Scanner scanner = new Scanner(System.in);

int flag = 1;
while (flag == 1) {
System.out.println("请输入学生的姓名:");
String name = scanner.next();
System.out.println("请输入学生的年龄:");
int age = scanner.nextInt();
student.add(new Student(name, age));
System.out.println("输入 1 继续录入,输入 0 停止录入");
flag = scanner.nextInt();
}

for (Object obj : student) {
Student stu = (Student) obj;
System.out.println(stu.toString());
}

scanner.close();

}
}

综合测试二

image-20240321183805554

对于这个题,list 中存的是 char 字符类型,有两种解决方法。

第一种,存入 list 中的时候在后边加一个 ""

list.add((char)number + "");

第二种,在比较的时候对 char 使用 String 的 valueOf 方法进行赋值

String c = String.valueOf(iterator.next());
String c = String.valueOf(obj);

完整代码

public class StringTest {
public static void main(String[] args) {
ArrayList list = new ArrayList();
for (int i = 0; i < 30; i++) {
int max = 122 - 97 + 1;
int number = (int) (Math.random() * max + 97);
list.add((char)number + "");
}
System.out.println(list);
int aCount = listTest1(list, "a");
int bCount = listTest1(list, "b");
int cCount = listTest1(list, "c");
int xCount = listTest1(list, "x");
System.out.printf("%d %d %d %d\n", aCount, bCount, cCount, xCount);

}
// 迭代器
public static int listTest2(Collection list, String s) {
int count = 0;
Iterator iterator = list.iterator();
while (iterator.hasNext()) {
String c = String.valueOf(iterator.next());
if(c.equals(s)){
count++;
}
}
return count;
}

// 增强 for 循环
public static int listTest1(Collection list, String s) {
int count = 0;
for (Object obj : list) {
String c = String.valueOf(obj);
if (c.equals(s)) {
count++;
}
}
return count;
}
}

image-20240321200435573

Set 常用方法

Set 声明的方法与 Collection 中声明的 15 个抽象方法相同

对于 Set 来说,我们更重要的是研究他的两个特性。

Set 的常见实现类

  • HashSet:主要实现类,底层使用 HashMap,也就是使用了数组,单向链表,红黑树结构进行存储
    • LinkedSet:HashSet 的子类,相比与父类,它在不同的元素之间增加了指针,实现了顺序遍历
  • TreeSet:底层使用红黑树存储, 可以根据添加的元素对指定的属性大小进行遍历。

Set 的无序性

这里的无序性并不是随机性,对与 Set 来说,它保存是数据的方法是 HashMap,也就是对于将要保存的元素,计算它的哈希值,哈希值即为这个元素的保存位置,所以当添加完元素后,你输入的数据将不会按照顺序保存,而是根据自己的计算结果保存到不同位置,这就是 Set 的无序性。

按照 1-> 5 的顺序进行输入,经过哈希算法计算后排列的位置如下图所示

image-20240322093201232

LinkedSet 相当于增加了一个指针,让 1 指向 2,2 指向 3,以此类推,最后遍历的时候便可以根据指针进行遍历。

Set 的不可重复性

比较的标准是首先判断 HashCode 得到的哈希值是否相同,如果相等则进行 euqals。只有两者都相同,则认为元素是相同的。

演示代码 HashSet

这里可能会有疑问,不是说 Set 中不存在重复的元素,难道对自定义的实体类不生效吗。

我们从Set 存入数据的过程看到,Set 首先要对元素进行哈希计算,这里我们 new 的两个 Person 对象,他们的实体类中没有重写 HashCode 方法,因此调用的是 Object 的方法,该方法并不会把这两个赋值相同的对象计算得到相同的哈希值,所以 Set 会把两个元素看做不同的元素。

为了避免这种问题,我们需要重写实体类中的 HashCode。

测试代码

@Test
public void test2() {
Set set = new HashSet();
set.add("test");
set.add(123);
set.add(new Person(10,"xiaohua"));
set.add(new Person(10,"xiaohua"));

System.out.println(set);
}

image-20240322094309728

重写

HashCode

@Override
public int hashCode() {
int result = age;
result = 31 * result + (name != null ? name.hashCode() : 0);
return result;
}

此时的运行结果

image-20240322094417769

演示代码 LinkedSet

LinkedSet 的优势就是能按照输入顺序输出

@Test
public void test1() {
// LinkedHashSet 实现了记录添加元素的顺序,方便进行遍历
Set set = new LinkedHashSet();
Person person = new Person(10,"xiaohua");
set.add("test");
set.add(123);
set.add(person);
System.out.println(set);
Iterator iterator = set.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}

image-20240322112159406

演示代码 TreeSet

对于 TreeSet 来说,最主要的用处是对插入元素的排序。例如对字符串进行排序

@Test
public void test3() {
Set set = new TreeSet();

set.add("BB");
set.add("AA");
set.add("GG");
set.add("ZZ");
set.add("YY");
// 同一个 TreeSet 中不能添加不同类型的元素,以下添加 123 就会出错
set.add(123);

System.out.println(set);
}

运行结果

image-20240322104916658

对于 TreeSet 添加自定义的实体类,进行排序的时候如何进行的判断,实际上是使用了自然排序和自定义排序,compareTo 和 compare 的返回值

以下代码中我重写了 User 中 compareTo 的方法,使得元素按照年龄排序,由于 TreeSet 采用的是红黑树的数据结构,存入的数据不能重复,所以说在按照年龄进行排序的时候,还需要别的元素进行辅助排序。

@Test
public void test4() {
Set set = new TreeSet();

Person p1 = new Person(10,"xiaohua");
Person p2 = new Person(12,"xiaohua1");
Person p3 = new Person(12,"xiaohua2");
Person p4 = new Person(13,"xiaohua3");
Person p5 = new Person(14,"xiaohua4");

set.add(p1);
set.add(p2);
set.add(p3);
set.add(p4);
set.add(p5);

for (Object o : set) {
System.out.println(o);
}
}
重写的

compareTo 方法

@Override
public int compareTo(Object o) {
if (this == o) {
return 0;
}
if (o instanceof Person) {
Person p = (Person) o;
int number = this.age - p.age;
if (number != 0){
return number;
}
else {
return this.name.compareTo(p.name);
}
}
throw new RuntimeException("类型不匹配");
}

输出结果

image-20240322110733499

对于不方便修改实体类中的比较方法的情况,可以直接给 TreeSet 一个 Comparator 方法

@Test
public void test4() {
Comparator comparator = new Comparator() {
@Override
public int compare(Object o1, Object o2) {
if (o1 instanceof Person && o2 instanceof Person) {
Person p1 = (Person) o1;
Person p2 = (Person) o2;

int value = p1.getName().compareTo(p2.getName());
if (value != 0) {
return value;
}
else {
return p1.getAge() - p2.getAge();
}
}
throw new RuntimeException("数据类型不匹配");
}
};

Set set = new TreeSet(comparator);

Person p1 = new Person(10,"A1");
Person p2 = new Person(13,"A1");
Person p3 = new Person(10,"B");
Person p4 = new Person(13,"C");
Person p5 = new Person(13,"D");

set.add(p1);
set.add(p2);
set.add(p3);
set.add(p4);
set.add(p5);

for (Object o : set) {
System.out.println(o);
}
}

image-20240322111924107

Set 综合测试

综合测试一

image-20240322112623167

@Test
public void test5() {
List list = new ArrayList();

list.add(11);
list.add(11);
list.add(11);
list.add(11);
list.add(22);
list.add(22);
list.add(22);
list.add(22);
list.add(33);
list.add(33);
list.add(33);
list.add(33);

System.out.println(list);
System.out.println(duplicateList(list));
}

public static List duplicateList(List list){
Set set = new LinkedHashSet(list);
List new_list = new ArrayList(set);
return new_list;
}

image-20240322113252614

综合测试二

image-20240322113353691

@Test
public void test7() {
Set set = new HashSet();
while (set.size() < 10) {
int randomNumber = (int)(Math.random() * 20) + 1;
set.add(randomNumber);
}
System.out.println(set);
}

image-20240322113754482

Map 常用方法

Map 不同类的区别

java.util.Map:

存储一对一对的数据,也就是 key-value 键值对

  • HashMap: 主要实现类,线程不安全的,效率高的。可以添加 null 的 key 与 value。底层使用数组,单向链表,红黑树的数据结构。
    • LinkedMap:HashMap 的子类,添加了一对双向链表,记录添加的元素的先后顺序。
  • TreeMap: 底层使用红黑树的数据结构,可以根据添加的 key 元素指定的属性大小进行遍历。
  • Hashtable: 古老实现类,线程安全的,效率低的。不可以添加 null 的 key 和 value。底层使用数组,单向链表的数据结构。
    • Properties:

演示代码 HashMap

在 HashMap 中,key 是不能够相同的,并且是无序的。而 value 是可以相同的,但由于 value 是 key 的映射,key 是无序的,value 也是无序的。

  • HashMap 中所有的 key 构成了一个 Set 集合,key 所在的类,需要重写 equals 和 HashCode 方法。

  • HashMap 中所有的 value 构成了一个Collection 集合,value 所在的类,需要重写 equals 方法。

增删改查

put、putAll、remove、put、get

@Test
public void test() {
// 增
Map map = new HashMap();
Person p1 = new Person(10,"xiaoming");

map.put("AA", 123);
map.put("BB", 13);
map.put("CC", 23);

map.put(p1, 55);

System.out.println(map);

Map map1 = new HashMap();
map1.putAll(map);

System.out.println(map1);
}

image-20240322175056275

@Test
public void test2() {
// 删 && 改 && 查
Map map = new HashMap();
Person p1 = new Person(10,"xiaoming");

map.put("AA", 123);
map.put("BB", 13);
map.put("CC", 23);

map.put(p1, 55);

Object value = map.remove("BB");
System.out.println(value);

map.put("CC", 15);

// value
System.out.println(map.get("CC"));

System.out.println(map);

}

长度 遍历

size、keySet、values、entrySet

@Test
public void test3() {
Map map = new HashMap();
Person p1 = new Person(10, "xiaoming");

map.put("AA", 123);
map.put("BB", 13);
map.put("CC", 23);

map.put(p1, 55);

System.out.println(map.size());

// keySet 遍历 key
Set keySet = map.keySet();

for (Object o :keySet) {
System.out.println(o);
}

// values 遍历 value
Collection values = map.values();

for (Object o : values) {
System.out.println(o);
}

// entrySet 遍历 entry 键值对
Set entrySet = map.entrySet();

for (Object o :entrySet) {
System.out.println(o);
}


}

image-20240322175647489

演示代码 TreeMap

对于 TreeMap 来说,和 TreeSet 一样,重要的是自然排序和定制排序的写法。

自然排序,根据实体类中的

compareTo 方法

@Test
public void test2() {
// 自然排序
Map map = new TreeMap();
Person p1 = new Person(10, "zeus");
Person p2 = new Person(14, "faker");
Person p3 = new Person(12, "jerry");
Person p4 = new Person(12, "tom");

map.put(p1, 12);
map.put(p2, 63);
map.put(p3, 42);
map.put(p4, 31);

Set entry = map.entrySet();
for (Object o : entry) {
System.out.println(o);
}
}

image-20240322183609548

定制排序,TreeMap

同样可以传入一个 comparator,在这里 comparator 中我们可以自定义按照类的什么属性进行排序

@Test
public void test1() {

Comparator comparator = new Comparator() {
@Override
public int compare(Object o1, Object o2) {
if (o1 instanceof Person && o2 instanceof Person) {
Person p1 = (Person) o1;
Person p2 = (Person) o2;

int value = p1.getName().compareTo(p2.getName());
if (value != 0) {
return value;
}
else {
return p1.getAge() - p2.getAge();
}
}
throw new RuntimeException("数据类型不匹配");
}
};

Map map = new TreeMap(comparator);
Person p1 = new Person(10, "zeus");
Person p2 = new Person(14, "faker");
Person p3 = new Person(12, "jerry");
Person p4 = new Person(12, "tom");

map.put(p1, 12);
map.put(p2, 63);
map.put(p3, 42);
map.put(p4, 31);

Set entry = map.entrySet();
for (Object o : entry) {
System.out.println(o);
}

}

image-20240322183750711

演示代码 Properties

Properties 多用于在文件的 IO 流中,使用 Properties 读取配置文件可以有效的提高代码的可维护性。

@Test
public void test1() throws IOException {
File file = new File("config.properties");
System.out.println(file.getAbsolutePath());


FileInputStream fileInputStream = new FileInputStream(file);
Properties properties = new Properties();
properties.load(fileInputStream);

System.out.println(properties.get("name"));
System.out.println(properties.get("password"));

fileInputStream.close();

}

image-20240322194853229

Collections 工具类

排序操作

// 排序操作
@Test
public void test1() {
List list = Arrays.asList(1,42,32,43,45,3,4,53,23,31,45,98);
System.out.println(list);

// reverse 反转 list 中元素的顺序
Collections.reverse(list);
System.out.println(list);

// shuffle 对 list 中的元素进行随机排序
Collections.shuffle(list);
System.out.println(list);

// sort 对 list 中的元素进行自然排序
Collections.sort(list);
System.out.println(list);

Comparator comparator = new Comparator() {
@Override
public int compare(Object o1, Object o2) {
if (o1 instanceof Integer && o2 instanceof Integer) {
Integer i1 = (Integer) o1;
Integer i2 = (Integer) o2;
return -(i1.intValue() - i2.intValue());
}
throw new RuntimeException("类型不匹配");
}
};
// sort 对 list 中的元素进行定制排序
Collections.sort(list, comparator);
System.out.println(list);

}

image-20240322202849252

查找操作

// 查找操作
@Test
public void test2() {
List list = Arrays.asList(1, 42, 1, 43, 45, 1, 4, 53, 23, 31, 45, 98);
System.out.println(list);

// max 获取 list 中的最大值
Object max = Collections.max(list);
System.out.println(max);

Comparator comparator = new Comparator() {
@Override
public int compare(Object o1, Object o2) {
if (o1 instanceof Integer && o2 instanceof Integer) {
Integer i1 = (Integer) o1;
Integer i2 = (Integer) o2;
return -(i1.intValue() - i2.intValue());
}
throw new RuntimeException("类型不匹配");
}
};
// max 同样可以接受一个 comparator,因为它获取的逻辑是排序后的最右边的数据,如果 comparator 传入的是倒序,最大值反而是最小值
Object max1 = Collections.max(list, comparator);
System.out.println(max1);

// frequency 返回 list 中指定元素出现的个数
int number = Collections.frequency(list, 1);
System.out.println(number);
}

image-20240322202931047

复制,替换操作

@Test
public void test3() {
List list = Arrays.asList(1, 42, 1, 43, 45, 1, 4, 53, 23, 31, 45, 98);
System.out.println(list);
// copy 将一个 list 赋值到一个新的 list,但是新的 list 的 size 必须大于等于旧的 list
List new_list = Arrays.asList(new Object[list.size()]);
Collections.copy(new_list, list);
System.out.println(new_list);

}

image-20240323105340302

unmodifiableList

生成一个只读格式的 list

@Test
public void test4() {
List list = Arrays.asList(1, 42, 1, 43, 45, 1, 4, 53, 23, 31, 45, 98);
System.out.println(list);
List new_list = Collections.unmodifiableList(list);

// unmodifiableList 将 list 转换成一个新的只读的 list
new_list.add(12);
}

image-20240323111052535


关于我