java中的集合类可以用来存储任何类型的对象,集合存储的对象必须是基本数据类型
- 综合资讯
- 2024-10-02 08:05:50
- 2

《Java集合:存储对象类型的多样性》在Java编程中,集合类是非常重要的一部分,与标题中所说的“集合存储的对象必须是基本数据类型”相反,Java中的集合类具有强大的功...
本文目录导读:
《Java集合:存储对象类型的多样性》
在Java编程中,集合类是非常重要的一部分,与标题中所说的“集合存储的对象必须是基本数据类型”相反,Java中的集合类具有强大的功能,可以用来存储任何类型的对象,包括自定义的类对象、基本数据类型的包装类对象等,这一特性使得集合在各种Java应用程序中得到广泛的应用,从简单的数据管理到复杂的企业级系统开发。
Java集合框架概述
(一)集合框架的层次结构
Java集合框架主要包含两大接口分支:Collection
和Map
。
1、Collection接口
Collection
是所有集合类的根接口,它定义了一些基本的操作,如添加、删除、遍历元素等。Collection
接口有三个主要的子接口:List
、Set
和Queue
。
List接口
List
是一个有序的集合,允许元素重复。ArrayList
和LinkedList
是List
接口的两个常见实现类。ArrayList
基于数组实现,具有快速的随机访问特性,适合频繁读取元素的场景,而LinkedList
基于链表实现,在插入和删除操作上具有较高的效率,特别是在列表的头部或尾部进行操作时。
Set接口
Set
是一个不允许元素重复的集合。HashSet
是Set
接口最常用的实现类之一,它基于哈希表实现,能够快速地查找元素。TreeSet
则是基于红黑树实现的Set
,它会对元素进行排序,适合需要对元素进行有序存储和检索的场景。
Queue接口
Queue
用于处理按特定顺序排队的元素,例如LinkedList
也可以作为Queue
来使用。PriorityQueue
是一种特殊的队列,它会根据元素的优先级对元素进行排序,优先级高的元素会先出队。
2、Map接口
Map
用于存储键 - 值对,其中键是唯一的。HashMap
是Map
接口最常用的实现类,它基于哈希表实现,提供了快速的查找、插入和删除操作。TreeMap
则是基于红黑树实现的Map
,会根据键对键 - 值对进行排序。
(二)集合类存储对象的基本原理
1、对象引用的存储
- 当把一个对象存储到集合中时,实际上存储的是对象的引用,在ArrayList
中,内部维护了一个对象引用的数组,当我们执行list.add(new MyObject())
(假设MyObject
是一个自定义类)时,new MyObject()
创建的对象在堆内存中,而ArrayList
只是存储了指向这个对象的引用。
2、内存管理与对象生命周期
- 集合中的对象生命周期与集合本身的生命周期以及Java的垃圾回收机制密切相关,如果一个对象只被集合中的引用所指向,并且当集合不再引用这个对象时(例如从集合中删除了这个对象的引用),这个对象就有可能被垃圾回收器回收。
存储自定义对象
1、自定义类的定义
- 假设我们定义了一个简单的Person
类:
```java
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
}
```
2、将自定义对象存储到集合中
- 我们可以将Person
对象存储到List
中:
```java
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<Person> personList = new ArrayList<>();
personList.add(new Person("Alice", 25));
personList.add(new Person("Bob", 30));
for (Person person : personList) {
System.out.println(person.getName() + " " + person.getAge());
}
}
}
```
- 同样,也可以将Person
对象存储到Set
中,如果我们使用HashSet
,需要注意自定义类要正确重写hashCode()
和equals()
方法,以确保对象的唯一性判断正确。
```java
class Person {
//...
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass()!= o.getClass()) return false;
Person person = (Person) o;
return age == person.age && Objects.equals(name, person.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
public class Main {
public static void main(String[] args) {
Set<Person> personSet = new HashSet<>();
personSet.add(new Person("Alice", 25));
personSet.add(new Person("Bob", 30));
personSet.add(new Person("Alice", 25));//这个对象不会被重复添加
for (Person person : personSet) {
System.out.println(person.getName() + " " + person.getAge());
}
}
}
```
存储基本数据类型的包装类
1、基本数据类型包装类简介
- 在Java中,基本数据类型(如int
、double
、boolean
等)有对应的包装类(Integer
、Double
、Boolean
等),这些包装类使得基本数据类型可以像对象一样被处理,从而可以存储在集合中。
2、存储包装类对象到集合中
- 我们可以将Integer
对象存储到List
中:
```java
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<Integer> integerList = new ArrayList<>();
integerList.add(Integer.valueOf(1));
integerList.add(Integer.valueOf(2));
for (Integer num : integerList) {
System.out.println(num);
}
}
}
```
- 在Java 5
及以后版本中,由于自动装箱和自动拆箱机制的引入,我们可以直接将基本数据类型赋值给包装类类型的变量或者将包装类对象赋值给基本数据类型变量,这使得在集合中使用基本数据类型的包装类更加方便。
```java
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<Integer> integerList = new ArrayList<>();
integerList.add(1);
integerList.add(2);
int num = integerList.get(0);
System.out.println(num);
}
}
```
集合操作中的类型安全性
1、泛型与类型安全
- Java中的泛型为集合提供了类型安全的保障,通过在定义集合时指定元素的类型,如List<String>
表示这个List
只能存储String
类型的对象,这样可以在编译时发现类型不匹配的错误,避免在运行时出现ClassCastException
等异常。
- 如果我们试图将一个Integer
对象添加到List<String>
中,在编译时就会报错:
```java
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<String> stringList = new ArrayList<>();
stringList.add("Hello");
//stringList.add(Integer.valueOf(1));//编译错误
}
}
```
2、类型擦除与运行时类型检查
- 虽然Java在编译时使用泛型进行类型检查,但在运行时会发生类型擦除,这意味着在运行时,集合中的对象类型信息会被部分擦除,不过,Java仍然会进行一些必要的运行时类型检查,例如在进行强制类型转换时,如果对象的实际类型与转换的目标类型不匹配,就会抛出ClassCastException
。
集合遍历与对象操作
1、遍历集合中的对象
- 对于List
和Set
等Collection
类型的集合,我们可以使用for - each
循环或者迭代器(Iterator
)来遍历集合中的对象。
- 使用for - each
循环:
```java
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<String> stringList = new ArrayList<>();
stringList.add("Apple");
stringList.add("Banana");
for (String fruit : stringList) {
System.out.println(fruit);
}
}
}
```
- 使用迭代器:
```java
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<String> stringList = new ArrayList<>();
stringList.add("Apple");
stringList.add("Banana");
Iterator<String> iterator = stringList.iterator();
while (iterator.hasNext()) {
String fruit = iterator.next();
System.out.println(fruit);
}
}
}
```
2、在遍历过程中操作对象
- 在遍历集合中的对象时,我们可以对对象进行各种操作,如修改对象的属性(对于可变对象)或者根据某些条件删除对象,对于一个存储Person
对象的List
,我们可以修改年龄属性:
```java
import java.util.ArrayList;
import java.util.List;
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
public class Main {
public static void main(String[] args) {
List<Person> personList = new ArrayList<>();
personList.add(new Person("Alice", 25));
personList.add(new Person("Bob", 30));
for (Person person : personList) {
if (person.getName().equals("Alice")) {
person.setAge(26);
}
}
for (Person person : personList) {
System.out.println(person.getName() + " " + person.getAge());
}
}
}
```
集合与多态
1、多态在集合中的体现
- 当我们将不同类型的对象存储到集合中时,多态性得到了很好的体现,我们定义了一个Animal
接口,以及Dog
和Cat
两个实现类:
```java
interface Animal {
void makeSound();
}
class Dog implements Animal {
@Override
public void makeSound() {
System.out.println("Woof!");
}
}
class Cat implements Animal {
@Override
public void makeSound() {
System.out.println("Meow!");
}
}
```
- 我们可以将Dog
和Cat
对象存储到一个List<Animal>
中:
```java
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<Animal> animalList = new ArrayList<>();
animalList.add(new Dog());
animalList.add(new Cat());
for (Animal animal : animalList) {
animal.makeSound();
}
}
}
```
2、利用多态进行通用的集合操作
- 通过多态,我们可以编写更通用的代码来处理集合中的对象,我们可以定义一个方法来处理List<Animal>
中的所有动物对象,而不需要为每个具体的动物类编写单独的方法:
```java
public class Main {
public static void handleAnimals(List<Animal> animalList) {
for (Animal animal : animalList) {
animal.makeSound();
}
}
public static void main(String[] args) {
List<Animal> animalList = new ArrayList<>();
animalList.add(new Dog());
animalList.add(new Cat());
handleAnimals(animalList);
}
}
```
集合的序列化与存储对象
1、对象序列化简介
- 对象序列化是将对象转换为字节流的过程,这样可以将对象存储到文件或者在网络上传输,对于集合中的对象,如果要进行序列化,集合中的对象类必须实现Serializable
接口。
2、序列化集合中的对象
- 我们将一个存储Person
对象的ArrayList
序列化到文件中:
```java
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.List;
class Person implements Serializable {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
//...
}
public class Main {
public static void main(String[] args) {
List<Person> personList = new ArrayList<>();
personList.add(new Person("Alice", 25));
personList.add(new Person("Bob", 30));
try {
FileOutputStream fileOut = new FileOutputStream("person.ser");
ObjectOutputStream out = new ObjectOutputStream(fileOut);
out.writeObject(personList);
out.close();
fileOut.close();
System.out.println("Serialized data is saved in person.ser");
} catch (IOException e) {
e.printStackTrace();
}
}
}
```
- 然后可以从文件中反序列化这个集合:
```java
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.List;
public class Main {
public static void main(String[] args) {
try {
FileInputStream fileIn = new FileInputStream("person.ser");
ObjectInputStream in = new ObjectInputStream(fileIn);
List<Person> personList = (List<Person>) in.readObject();
in.close();
fileIn.close();
for (Person person : personList) {
System.out.println(person.getName() + " " + person.getAge());
}
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
}
```
集合存储对象的性能考虑
1、不同集合类型的性能特点
ArrayList:在随机访问元素时性能非常好,时间复杂度为$O(1)$,但是在插入和删除元素时,尤其是在列表中间进行操作时,性能较差,因为需要移动大量的元素,插入和删除操作的平均时间复杂度为$O(n)$。
LinkedList:在插入和删除元素时,特别是在头部或尾部操作时性能较好,时间复杂度为$O(1)$,但是随机访问元素的性能较差,时间复杂度为$O(n)$。
HashSet:添加、删除和查找元素的时间复杂度接近常数时间$O(1)$,但是需要注意哈希冲突的处理。
TreeSet:查找、插入和删除操作的时间复杂度为$O(log n)$,由于需要维护元素的排序,性能相对HashSet
在某些操作上可能会稍慢一些。
2、选择合适的集合类型存储对象
- 如果需要频繁地随机访问元素,并且元素的数量相对固定或者插入删除操作较少,ArrayList
是一个较好的选择,如果经常需要在头部或尾部插入或删除元素,LinkedList
更合适,如果要确保元素的唯一性并且对查找速度要求较高,HashSet
是首选,而如果还需要对元素进行排序,TreeSet
则是更好的选择,对于键 - 值对的存储,如果不要求排序,HashMap
性能较好,要求排序则选择TreeMap
。
Java中的集合类具有很强的灵活性,可以存储任何类型的对象,包括自定义类对象和基本数据类型的包装类对象,通过合理地利用集合类的特性,如泛型保证类型安全、多态实现通用操作、序列化进行对象存储等,以及根据性能需求选择合适的集合类型,我们可以高效地管理和操作各种对象,这种对象存储的多样性使得集合类成为Java编程中不可或缺的一部分,广泛应用于从简单的桌面应用到复杂的企业级系统等各种场景中。
本文链接:https://www.zhitaoyun.cn/130100.html
发表评论