Java存储数据:数组与集合
一、数组(Array)
1. 逻辑特征
- 固定大小:创建时指定长度,不能动态改变
- 类型统一:所有元素必须是相同数据类型
- 内存连续:元素在内存中连续存储
- 效率高:通过索引直接访问,时间复杂度O(1)
- 功能简单:提供基本的存储和访问能力
2. 代码层面
// 1. 声明和初始化int[]numbers=newint[5];// 长度为5的整型数组String[]names={"张三","李四","王五"};// 2. 访问和修改numbers[0]=10;System.out.println(numbers[0]);// 输出: 10// 3. 遍历数组for(inti=0;i<numbers.length;i++){System.out.println(numbers[i]);}// 4. 增强for循环for(intnum:numbers){System.out.println(num);}// 5. 多维数组int[][]matrix=newint[3][3];matrix[0][0]=1;3. 数组的局限性
// 问题1: 数组大小固定,无法动态扩展int[]arr=newint[3];// arr[3] = 4; // ArrayIndexOutOfBoundsException// 问题2: 只能存储同一类型数据// arr[0] = "hello"; // 编译错误// 问题3: 缺少高级操作方法// 没有内置的add(), remove(), contains()等方法二、为什么需要集合(Collection)
1. 数组的不足
- 大小固定:不能动态增长或收缩
- 功能有限:缺少增删改查的高级方法
- 类型限制:只能存储相同类型
- 代码繁琐:需要手动处理很多逻辑
2. 集合的优势
- 动态扩容:自动调整大小
- 功能丰富:提供各种操作方法
- 类型灵活:通过泛型支持类型安全
- 算法支持:内置排序、查找等算法
三、集合知识体系
Java集合框架 (Java Collections Framework) │ ├── Collection接口 (单列集合) │ ├── List接口 (有序、可重复) │ │ ├── ArrayList: 数组实现,查询快,增删慢 │ │ ├── LinkedList: 链表实现,增删快,查询慢 │ │ └── Vector: 线程安全的ArrayList(已过时) │ │ │ ├── Set接口 (无序、不可重复) │ │ ├── HashSet: 哈希表实现 │ │ ├── LinkedHashSet: 有序的HashSet │ │ └── TreeSet: 红黑树实现,可排序 │ │ │ └── Queue接口 (队列) │ ├── LinkedList: 也实现了Queue │ ├── PriorityQueue: 优先级队列 │ └── ArrayDeque: 双端队列 │ └── Map接口 (双列集合,键值对) ├── HashMap: 最常用的Map ├── LinkedHashMap: 有序的HashMap ├── TreeMap: 可排序的Map └── Hashtable: 线程安全的Map(已过时)四、主要集合类示例
1. ArrayList(最常用)
// 创建ArrayListList<String>list=newArrayList<>();// 添加元素(自动扩容)list.add("Java");list.add("Python");list.add("C++");// 获取元素Stringfirst=list.get(0);// 修改元素list.set(1,"JavaScript");// 删除元素list.remove(2);// 遍历for(Stringlanguage:list){System.out.println(language);}// 其他常用方法intsize=list.size();// 大小booleanempty=list.isEmpty();// 是否为空booleancontains=list.contains("Java");// 是否包含2. LinkedList
// 创建LinkedListLinkedList<Integer>linkedList=newLinkedList<>();// 添加元素linkedList.add(10);linkedList.addFirst(5);// 头部添加linkedList.addLast(20);// 尾部添加// 作为队列使用linkedList.offer(30);// 入队inthead=linkedList.poll();// 出队// 作为栈使用linkedList.push(40);// 压栈inttop=linkedList.pop();// 弹栈3. HashSet
// 创建HashSetSet<String>set=newHashSet<>();// 添加元素set.add("Apple");set.add("Banana");set.add("Apple");// 重复,不会添加// 遍历(无序)for(Stringfruit:set){System.out.println(fruit);}// 常用操作set.remove("Banana");booleanhasApple=set.contains("Apple");4. HashMap
// 创建HashMapMap<String,Integer>map=newHashMap<>();// 添加键值对map.put("Alice",25);map.put("Bob",30);map.put("Charlie",28);// 获取值intage=map.get("Alice");// 遍历for(Map.Entry<String,Integer>entry:map.entrySet()){System.out.println(entry.getKey()+": "+entry.getValue());}// 键集合和值集合Set<String>keys=map.keySet();Collection<Integer>values=map.values();五、集合与数组的对比
| 特性 | 数组 | 集合 |
|---|---|---|
| 大小 | 固定,声明时确定 | 动态,可自动扩容 |
| 类型 | 必须统一 | 通过泛型支持多种类型 |
| 性能 | 访问快(O(1)) | 实现不同,性能各异 |
| 功能 | 基本操作 | 丰富的高级操作 |
| 存储 | 基本类型和对象 | 只能存储对象 |
| 线程安全 | 不安全 | 大部分不安全 |
六、最佳实践建议
- 优先使用集合:除非对性能有极致要求,否则优先使用集合
- 选择合适的集合:
- 需要频繁查询 → ArrayList
- 需要频繁增删 → LinkedList
- 需要去重 → HashSet
- 需要键值对 → HashMap
- 使用泛型:确保类型安全
- 考虑线程安全:多线程环境使用ConcurrentHashMap、CopyOnWriteArrayList
- 初始化大小:如果知道大概数据量,可以指定初始容量
// 指定初始容量List<String>list=newArrayList<>(1000);Map<String,Integer>map=newHashMap<>(500);七、总结
- 数组是Java语言的基础数据结构,简单高效但功能有限
- 集合是对数组的封装和扩展,提供了更强大、更灵活的数据管理能力
- 现代Java开发中,集合是首选,数组主要用于:
- 性能敏感的场景
- 处理基本数据类型
- 与遗留代码或API交互
- 理解不同集合的特性,根据需求选择最合适的工具
实际开发中,ArrayList和HashMap是最常用的集合类,掌握了它们就解决了80%的数据存储问题。
好的,专门说说遍历集合!这是天天要用的操作,分几种情况给你讲清楚。
List遍历(最常用)
1.普通for循环(有索引时用)
List<String>list=newArrayList<>(Arrays.asList("A","B","C"));for(inti=0;i<list.size();i++){Stringitem=list.get(i);System.out.println(item);}优点:能拿到索引,方便操作
缺点:只有ArrayList这种基于数组的用着快,LinkedList用这个就慢(因为要一个个数过去)
2.增强for循环(最常用、最简洁)
for(Stringitem:list){System.out.println(item);}优点:写法简单,可读性好
缺点:遍历时不能修改集合(删除、新增会抛异常)
3.迭代器Iterator(可以在遍历时安全删除)
Iterator<String>iterator=list.iterator();while(iterator.hasNext()){Stringitem=iterator.next();if("B".equals(item)){iterator.remove();// 安全删除当前元素}}优点:唯一能在遍历时安全删除元素的方式
缺点:代码稍多
4.ListIterator(双向遍历,可以修改)
ListIterator<String>listIterator=list.listIterator();while(listIterator.hasNext()){Stringitem=listIterator.next();if("B".equals(item)){listIterator.set("B+");// 修改当前元素}}// 还可以倒着遍历while(listIterator.hasPrevious()){System.out.println(listIterator.previous());}5.Java 8的forEach + Lambda(很流行)
// 方式1:Lambda表达式list.forEach(item->System.out.println(item));// 方式2:方法引用list.forEach(System.out::println);// 带索引的(Java 8没有原生支持,但可以这样)IntStream.range(0,list.size()).forEach(i->System.out.println(i+": "+list.get(i)));Set遍历
Set没索引,所以只能用这几种:
Set<String>set=newHashSet<>(Arrays.asList("A","B","C"));// 1. 增强for循环for(Stringitem:set){System.out.println(item);}// 2. 迭代器Iterator<String>iterator=set.iterator();while(iterator.hasNext()){System.out.println(iterator.next());}// 3. Java 8 forEachset.forEach(item->System.out.println(item));Map遍历(重点!)
1.遍历EntrySet(最推荐、最高效)
Map<String,Integer>map=newHashMap<>();map.put("A",1);map.put("B",2);for(Map.Entry<String,Integer>entry:map.entrySet()){Stringkey=entry.getKey();Integervalue=entry.getValue();System.out.println(key+"="+value);}为什么最推荐?:一次遍历同时拿到key和value,不用再通过key去查value(map.get(key)还有哈希计算的开销)
2.遍历KeySet(不推荐在遍历中取值)
for(Stringkey:map.keySet()){Integervalue=map.get(key);// 这里又做了一次哈希查找System.out.println(key+"="+value);}缺点:多了一次map.get(key)的哈希查找,效率低
3.遍历Values(只关心值时用)
for(Integervalue:map.values()){System.out.println(value);}4.迭代器方式
Iterator<Map.Entry<String,Integer>>iterator=map.entrySet().iterator();while(iterator.hasNext()){Map.Entry<String,Integer>entry=iterator.next();// 可以在遍历时删除if("A".equals(entry.getKey())){iterator.remove();}}5.Java 8的forEach(最简洁)
// Lambda表达式map.forEach((key,value)->System.out.println(key+"="+value));// 或者用entrySet的streammap.entrySet().stream().filter(entry->entry.getValue()>1).forEach(entry->System.out.println(entry.getKey()));遍历时的注意事项
1.不要在foreach循环里直接增删元素
// ❌ 错误!会抛 ConcurrentModificationExceptionfor(Stringitem:list){if("B".equals(item)){list.remove(item);// 直接调用list的remove}}// ✅ 正确!用迭代器的removeIterator<String>it=list.iterator();while(it.hasNext()){if("B".equals(it.next())){it.remove();// 用迭代器自己的remove方法}}2.Java 8的removeIf(删除元素新姿势)
// 删除所有值为"B"的元素list.removeIf(item->"B".equals(item));// Map删除满足条件的entrymap.entrySet().removeIf(entry->entry.getValue()>10);3.并行遍历(大数据量时考虑)
// 使用parallelStream(注意线程安全)list.parallelStream().forEach(item->{// 这里可以并行处理});// 或者用ConcurrentHashMap的forEach(线程安全)ConcurrentHashMap<String,Integer>concurrentMap=newConcurrentHashMap<>();concurrentMap.forEach(1,(key,value)->System.out.println(key));实际工作怎么选?
日常开发:
- 单纯遍历List/Set →增强for循环(最简洁)
- 遍历Map →entrySet + 增强for或map.forEach()
- 需要在遍历时删除 →迭代器或removeIf()
- Java 8+环境 → 多用forEach + Lambda(代码简洁)
性能考虑:
- 大数据量List →
ArrayList用普通for最快,LinkedList用迭代器 - Map遍历 →一定用entrySet,别用keySet+get
记忆口诀:
- List遍历:普通for要索引,增强for最方便,要删就用迭代器
- Map遍历:entrySet是王道,keySet效率低,Java8 forEach潮
这样清楚了吗?实际写代码时多试试,自然就熟练了!