Java集合

概述

  1. 数组和集合的元素存储的个数问题
    • 数组定义后类型确定,长度固定
    • 集合类型可以不固定,大小是可变的
  2. 数组和集合存储元素的类型问题
    • 基本类型和引用类型的数据数组都可以存储
    • 集合只能存储引用类型的数据
  3. 数组和集合适用的场景
    • 数组适合数据个数和类型确定的场景
    • 集合和C语言的链表很像,适合数据个数不确定,且要做增删元素的场景

Collection集合

特点

Collection是单列集合,每个元素只包含一个值,Map是双列集合,每个元素包含两个值(键值对)

  • List系列集合:添加的元素有序、可重复、有索引

    • 独有方法:add(int index, E element)、remove(int index)、get(int index)、set(int index, E element)
    • ArrayList:基于数组实现,查询元素快,增删相对慢。在第一次创建集合并添加第一个元素时,在底层创建一个默认长度为10的数组,如果该数组容量不够,按当前容量的一半进行扩容
    • LinkedList:基于双链表实现,可以实现队列。查询元素慢,增删首尾元素快
      • 独有方法:pop()、push()、add/removeFirst()、add/removeLast()... ...
  • Set系列集合:添加的元素无序、不重复、无索引

    • HashSet:无序、不重复、无索引;采取哈希表存储数据,从JDK1.8开始采用数组+链表+红黑树组成。

      让HashSet去除自定义类重复:重写equals()和hashCode()方法

    • LinkedHashSet:有序、不重复、无索引,在哈希表的基础上将每个元素之间用双链表连接,记录存储的顺序从而实现有序

    • TreeSet:按照大小默认升序排序、不重复、无索引,基于红黑树实现排序,增删改查性能较好

遍历集合

iterator()方法:返回一个迭代器对象,可以实现对集合的遍历

Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
    System.out.println(iterator.next());
}

增强型for循环

  • 既可以遍历集合也可以遍历数组,其内部原理是一个Iterator迭代器,遍历集合相当于是迭代器的简化写法
  • 实现Iterable接口的类才可以使用迭代器和增强for
for (String s : list) {
    System.out.println(s);
}

Lambda表达式遍历集合

forEach(Consumer<? super T> action)方法

list.forEach(new Consumer<String>() {
    @Override
    public void accept(String s) {
		System.out.println(s);
    }
});
// 可以简化为:
list.forEach(s -> System.out.println(s));
// 极简写法:
list.forEach(System.out::println);

for循环(List集合存在索引)

for (int i = 0; i < list.size(); i++) {
    System.out.println(list.get(i));
}

存储自定义类型的对象

ArrayList<Movie> movies = new ArrayList<>();
movies.add(new Movie("《你好,李焕英》", 95, "郑秀文"));
movies.forEach(System.out::println);

集合的并发修改异常问题的解决方法

在用迭代器和(增强型)for循环遍历集合且直接用集合删除元素时可能出现

错误示例:

public static void main(String[] args) {
    ArrayList<String> list = new ArrayList<>();
    list.add("123");
    list.add("123");
    list.add("345");
    list.forEach(s -> {
        if (s.equals("123")) {
            list.remove("123");
        }
    });
}

Exception in thread "main" java.util.ConcurrentModificationException
	at java.util.ArrayList.forEach(ArrayList.java:1262)
	at com.collection.Demo1.main(Demo1.java:15)

由于forEach在迭代下一个元素会给指针进行+1操作,remove也相当于进行+1操作,如果此时要删除两个相邻的重复的元素就会造成漏删的情况。

解决方法:使用迭代器的remove()方法或手动在for循环if内部进行-1操作

Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
    if (iterator.next().equals("123")) {
        iterator.remove();
    }
}

Map集合

特点

Map集合是一种双列集合,每个元素包含两个数据(键值对),格式为key=value

体系特点

  • Map集合的键是无序、不重复、无索引的(Set集合底层由Map集合实现),值可以重复
  • Map集合后面重复的键所对应的值会覆盖前面重复键的值
  • Map集合的键值对都可以为null

实现类特点(和Set基本相同)

  • HashMap:无序、不重复、无索引,值可以重复
  • LinkedHashMap:有序、不重复、无索引,值可以重复
  • TreeMap:排序、不重复、无索引,值不做要求

API

三种遍历方法

Map<String, Integer> info = new HashMap<>();
info.put("java", 4);
info.put("asp", 2);

// 1、键找值
Set<String> selects = info.keySet();
for (String select : selects) {
    System.out.println(select + info.get(select));
}

// 2、键值对
Set<Map.Entry<String, Integer>> entries = info.entrySet();
for (Map.Entry<String, Integer> entry : entries) {
    System.out.println(entry.getKey() + entry.getValue());
}

// 3、lambda表达式
info.forEach((k,v)-> System.out.println(k+v));

Properties

  • Properties其实就是一个Map集合,但是我们一般不会当集合使用,因为HashMap更好用。

  • Properties代表的是一个属性文件,可以把自己对象中的键值对信息存入到一个属性文件中去。

  • 属性文件:后缀是.properties结尾的文件,里面的内容都是 key=value,后续做系统配置信息的。

Properties和IO流结合的方法

构造器 说明
void load(InputStream inStream) 从输入字节流读取属性列表(键和元素对)
void load(Reader reader) 从输入字符流读取属性列表(键和元素对)
void store(OutputStream out, String comments) 将此属性列表(键和元素对)写入此 Properties表中,以适合于使用 load(InputStream)方法的格式写入输出字节流
void store(Writer writer, String comments) 将此属性列表(键和元素对)写入此 Properties表中,以适合使用 load(Reader)方法的格式写入输出字符流
public Object setProperty(String key, String value) 保存键值对(put)
public String getProperty(String key) 使用此属性列表中指定的键搜索属性值 (get)
public Set stringPropertyNames() 所有键的名称的集合 (keySet())

泛型

泛型类

  • 定义类时同时定义了泛型的类就是泛型类。

  • 泛型类的格式:修饰符 class 类名<泛型变量>{}

    public class MyArrayList<T>{} //此处泛型变量T可以随便写为任意标识,常见的如E、T、K、V等
    
  • 作用:编译阶段可以指定数据类型,类似于集合的作用

泛型方法

  • 定义方法时同时定义了泛型的方法就是泛型方法。

  • 泛型方法的格式:修饰符 <泛型变量> 方法返回值 方法名称(形参列表){}

    public <T> void show(T t){}
    
  • 作用:方法中可以使用泛型接收一切实际类型的参数,方法更具备通用性

泛型接口

  • 使用了泛型定义的接口就是泛型接口。

  • 泛型接口的格式:修饰符 interface 接口名称<泛型变量>{}

    public interface Data<E>{}
    
  • 作用:泛型接口可以让实现类选择当前功能需要操作的数据类型

泛型通配符

? 可以在使用泛型的时候代表一切类型

泛型的上下限:

  • ? extends Car:?必须是Car或者其子类 泛型上限
  • ? super Car : ?必须是Car或者其父类 泛型下限
posted @   TimQiu  阅读(30)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
点击右上角即可分享
微信分享提示