Java集合简介
数组的限制:
- 数组初始化后大小不可取
- 数组只能按索引顺序存取
Java.util.Collection
除Map外的所有其他集合类的根接口。java.util包主要提供三种类型的集合:List、Set、Map
- List 一种有序列表的的集合,按索引排列
- Set一种没有重复元素的集合
- Map一种通过键值key-value查找的映射表集合
- Java集合的设计有几个特点:一是实现了接口和实现类相分离,例如有序表的接口是List,具体的实现类有ArrayList、linkList,二是支持泛型,限制在一个集合中只能放入同一种数据类型的元素
- Java访问集合通过统一的方式迭代器Iterator来实现。
使用List
ArrayList把添加和删除的操作封装起来,本质是数组。对List的操作,相当于对数组的操作。
List
- 末尾添加一个元素,void add(E e)
- 指定索引添加一个元素 void add(int index,E e)
- 删除指定索引的元素 int remove(int inde)
- 删除某个元素 int remove(Object e)
- 获取指定索引的元素 E get(int index)
- 获取链表大小 int size()
LinkedList(链表),也实现了List接口。
Array | LinkedList | |
---|---|---|
获取指定元素 | 速度很快 | 需要从头开始查找元素 |
添加元素到末尾 | 速度很快 | 速度很快 |
在指定位置添加/删除 | 需要移动元素 | 不需要移动元素 |
内存占用 | 少 | 较大 |
- List允许添加null
- 用迭代器Iterator访问List
- Iterator对象有两个方法:boolean hasNext()是否有下一个元素 E next()返回下一个元素
public class Main {
public static void main(String[] args) {
List<String> list = List.of("apple", "pear", "banana");
for (Iterator<String> it = list.iterator(); it.hasNext(); ) {
String s = it.next();
System.out.println(s);
}
}
}
List和Array之间的转换
- toArray(T[])方法的泛型参数
并不是List接口定义的泛型参数 ,所以,我们实际上可以传入其他类型的数组,例如我们传入Number类型的数组,返回的仍然是Number类型 - 如果我们传入类型不匹配的数组,例如,String[]类型的数组,由于List的元素是Integer,所以无法放入String数组,这个方法会抛出ArrayStoreException。
- 将Array变为List
Integer[] array = { 1, 2, 3 };
List<Integer> list = List.of(array);
编写equals方法
- List内部并不是通过==判断两个元素是否相等,而是使用equals()
- 要使用List的contains()必须要覆写equals()方法
- equals()方法要满足几个特性:自反性,对称性,传递性,一致性
public boolean equals(Object o) {
if (o instanceof Person) {
Person p = (Person) o;
return this.name.equals(p.name) && this.age == p.age;
}
return false;
}//覆写equals 字符串用equals 数值用==
使用Map
public class Main {
public static void main(String[] args) {
Student s = new Student("Xiao Ming", 99);
Map<String, Student> map = new HashMap<>();
map.put("Xiao Ming", s); // 将"Xiao Ming"和Student实例映射并关联
Student target = map.get("Xiao Ming"); // 通过key查找并返回映射的Student实例
System.out.println(target == s); // true,同一个实例
System.out.println(target.score); // 99
Student another = map.get("Bob"); // 通过另一个key查找
System.out.println(another); // 未找到返回null
}
}
class Student {
public String name;
public int score;
public Student(String name, int score) {
this.name = name;
this.score = score;
}
}
- Map<K,V>是一种键值映射表,put放入map,get 获取对应的value,containsKey()判断是否有这个Key
- Map也是一个接口,常用实现的类时HashMap
- 重复放入key-value不会有问题,但是一个key只能有一个value,会覆盖。
- key不能重复,value可以重复
遍历Map
- map.keyset() 键的值域
- Map遍历顺序不一定,随机的
编写equals和hashCode
- HashMap内部的数组长度总是2的次方
- 我们覆写equals()和hashCode()时一定要确定两个对象相等,他们的hashCode要相等
- 两个对象不相等,hascode尽量不相等
- 通过key计算索引的方式就是调用key对象的hashCode()方法,返回一个int整数。HashMap通过这个方法直接定位Key对应的value索引,继而返回value
使用TreeMap
Map接口有两个实现类,一个是HashMap,另一个是TreeMap。HashMap内部的Key是无序的,而SortMap接口的实现类TreeMap内部Key是有序的。
- HashMap是一种以空间换时间的映射表,SortMap会对KEY排序,实现类是TreeMAP
- 使用TreeMap,放入的Key必须实现Comparable接口,定义比较规则
使用Properties
编写程序时,需要读写配置文件,一般Key-Value是String-String类型的。Java集合库提供了一个Properties来表示配置。本质是一个Hashtable。
- 配置文件信息
- 以.properties为扩展名,以#为开头
# setting.properties
last_open_file=/data/hello.txt
auto_save_interval=60
String f = "setting.properties";
Properties props = new Properties();
props.load(new java.io.FileInputStream(f));
String filepath = props.getProperty("last_open_file");
String interval = props.getProperty("auto_save_interval", "120");
- 读取配置文件一共有三步
- 创建Properties实例
- 调用load()读取文件
- 调用getProperty获取配置
- InputStream和Reader的区别是一个是字节流,一个是字符流。
- 字符流用CHAR表示,不涉及编码问题,UTF-8
使用Set
- 用于存储不重复的元素集合。
- 添加元素 add()
- 删除元素 remove()
- 判断是否包含元素 contains()
Set的实现类HashSet和TreeSet,而HashSet是对HashMap的一个封装。同样的,HashSet无序,TreeSet有序。
使用Queue
- 通过add()/offer()将元素添加到队尾
- 通过remove/poll取首元素并删除
- 通过element/peek获取首元素但不删除
PriorityQueue优先队列
- 放入优先队列的元素,必须实现Comparable接口,队列会根据排序顺序决定优先级
public class Main {
public static void main(String[] args) {
Queue<User> q = new PriorityQueue<>(new UserComparator());
// 添加3个元素到队列:
q.offer(new User("Bob", "A1"));
q.offer(new User("Alice", "A2"));
q.offer(new User("Boss", "V1"));
System.out.println(q.poll()); // Boss/V1
System.out.println(q.poll()); // Bob/A1
System.out.println(q.poll()); // Alice/A2
System.out.println(q.poll()); // null,因为队列为空
}
}
class UserComparator implements Comparator<User> {
public int compare(User u1, User u2) {
if (u1.number.charAt(0) == u2.number.charAt(0)) {
// 如果两人的号都是A开头或者都是V开头,比较号的大小:
return u1.number.compareTo(u2.number);
}
if (u1.number.charAt(0) == 'V') {
// u1的号码是V开头,优先级高:
return -1;
} else {
return 1;
}
}
}
class User {
public final String name;
public final String number;
public User(String name, String number) {
this.name = name;
this.number = number;
}
public String toString() {
return name + "/" + number;
}
}
//比较比较小的先出
使用Deque
- 允许两头都进,两头都出。双端队列(Double Ended Queue) 学名Deque
- Java集合提供了接口Deque实现一个双端队列
Queue | Deque | |
---|---|---|
添加元素到队尾 | add(E e) / offer(E e) | addLast(E e) / offerLast(E e) |
取队首元素并删除 | E remove() / E poll() | E removeFirst() / E pollFirst() |
取队首元素但不删除 | E element() / E peek() | E getFirst() / E peekFirst() |
添加元素到队首 | 无 | addFirst(E e) / offerFirst(E e) |
取队尾元素并删除 | 无 | E removeLast() / E pollLast() |
取队尾元素但不删除 | 无 | E getLast() / E peekLast() |
使用Stack
- 栈 LISO
- 把元素压栈:push(E)
- 把栈顶的元素“弹出”:pop(E)
- 取栈顶元素但不弹出:peek(E)
- Java集合类没有单独的Stack接口,只能用Deque接口来模拟一个Stack
- 对整数进行进制的转换就可以利用栈
使用Iterator 迭代器
- 编译器把for each循环通过Iterator改写为普通的for循环
for (Iterator<String> it = list.iterator(); it.hasNext(); ) {
String s = it.next();
System.out.println(s);
}
- 使用迭代器的好处在于,调用方总是以统一的方式遍历各种集合类型,而不必关系它们内部的存储结构
- 集合类实现Iterable接口,该接口要求返回一个Iterator对象;
- 用Iterator对象迭代集合内部数据。