一、集合体系
Java中常用集合分Collection
和Map
两大类,二者区别:
1、Collection是单列集合;Map是双列集合
2、Collection的数据结构是针对元素的;Map的数据结构是针对键的
Collection体系如下图:
简化后常用类:
Map体系如下图:
主要掌握:
List:ArrayList、LinkedList、Vector(过期)
Set:HashSet、TreeSet
Map:HashMap、LinkedHashMap、TreeMap、HashTable
二、Collection体系
Collection的共性方法
说明 | 共性方法 |
---|---|
添加 | boolean add(Object obj) boolean addAll(Collection<? extends E> c) |
删除 | boolean remove(Object obj) boolean removeAll(Collection coll) |
判断是否存在 | boolean contains(object obj) boolean containsAll(Collection coll) |
判空 | boolean isEmpty() |
获取集合大小 | int size() |
获取迭代 | Iterator iterator() |
集合转数组 | Object toArray() |
1、List:存取有序,有索引,可以根据索引来进行取值,元素可以重复
(1)ArrayList:① 底层数据结构是数组,查询快,增删慢。② 线程不安全,效率高。
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("aa");
list.add("bb");
list.add("cc");
// 第一种遍历方式,使用迭代器
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()){
String str = iterator.next();
System.out.println(str);
}
// 第二种遍历方式,使用foreach
for(String str : list){
System.out.println(str);
}
/*
输出结果:
aa
bb
cc
*/
}
(2)LinkedList:① 底层数据结构是链表,查询慢,增删快。② 线程不安全,效率高。
使用LinkedList来实现栈和队列;栈是先进后出,队列是先进先出。
// LinkedList实现栈
public class MyStack {
private LinkedList<String> linkedList= new LinkedList<>();
// 压栈
public void push(String str){
linkedList.addFirst(str);
}
// 出栈
public String pop(){
return linkedList.removeFirst();
}
// 查看
public String peek(){
return linkedList.peek();
}
// 判断是否为空
public boolean isEmpty(){
return linkedList.isEmpty();
}
}
public class MyQueue {
private LinkedList<String> linkedList = new LinkedList<>();
// 放入
public void put(String str){
linkedList.addFirst(str);
}
// 获取
public String linkedList(){
return linkedList.removeLast();
}
// 判断是否为空
public boolean isEmpty(){
return linkedList.isEmpty();
}
}
(3)Vector:已经过时,被ArrayList取代。① 底层数据结构是数组,查询快,增删慢。② 线程安全,效率低。
2、Set:存取无序,唯一
(1)HashSet:① 底层数据结构是哈希表。(无序,唯一) ② 如何来保证元素唯一性? 依赖两个方法:hashCode()和equals()。
使用HashSet存储字符串
public static void main(String[] args) {
Set<String> set = new HashSet<String>();
set.add("aa");
set.add("bb");
set.add("bb");
set.add("cc");
// 第一种遍历方式,使用迭代器
Iterator<String> it = set.iterator();
while(it.hasNext()){
String str = it.next();
System.out.println(str);
}
// 第二种遍历方式,使用foreach
for (String str : set){
System.out.println(str);
}
/*
输出结果:重复的已经去掉了
aa
bb
cc
*/
}
使用HashSet存储自定义对象,一定要实现hashCode和equals方法
// 自定义类Person
public class Person {
private int age;
private String name;
public Person(int age, String name) {
this.age = age;
this.name = name;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
if (age != person.age) return false;
return name != null ? name.equals(person.name) : person.name == null;
}
@Override
public int hashCode() {
int result = age;
result = 31 * result + (name != null ? name.hashCode() : 0);
return result;
}
@Override
public String toString() {
return "Person{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
}
//测试方法
public static void main(String[] args) {
// 利用HashSet来存取自定义对象 Person
Set<Person> set = new HashSet<Person>();
set.add(new Person(12,"张三"));
set.add(new Person(13,"李四"));
set.add(new Person(14,"王五"));
set.add(new Person(12,"张三"));
// 遍历
for (Person p : set){
System.out.println(p);
}
/*
输出结果:向集合中存储两个张三对象,但是集合中就成功存储了一个
Person{age=14, name='王五'}
Person{age=12, name='张三'}
Person{age=13, name='李四'}
*/
}
(2)LinkedHashSet:① 底层数据结构是链表和哈希表。(FIFO插入有序,唯一) ② 由链表保证元素有序,由哈希表保证元素唯一。
public static void main(String[] args) {
// 利用LinkedHashSet来存取自定义对象 Person
LinkedHashSet<Person> set = new LinkedHashSet<Person>();
set.add(new Person(12,"张三"));
set.add(new Person(13,"李四"));
set.add(new Person(14,"王五"));
set.add(new Person(12,"张三"));
// 遍历
for (Person p : set){
System.out.println(p);
}
/*
输出结果:向集合中存储两个张三对象,但是集合中就成功存储了一个,
并且存进的顺序,和取出来的顺序是一致的
Person{age=12, name='张三'}
Person{age=13, name='李四'}
Person{age=14, name='王五'}
*/
}
(3)TreeSet:① 底层数据结构是红黑树。(有序,唯一) ② 如何保证元素排序的呢? 自然排序 比较器排序
③ 如何保证元素唯一性的呢? 根据比较的返回值是否是0来决定
使用TreeSet存储String对象
public static void main(String[] args) {
TreeSet<String> treeSet = new TreeSet<String>();
treeSet.add("aa");
treeSet.add("vv");
treeSet.add("bb");
treeSet.add("xx");
for (String str : treeSet){
System.out.println(str);
}
/*
输出结果:取出来的结果是经过排序的
aa
bb
vv
xx
*/
}
TreeSet保证元素的唯一性是有两种方式:
1、自定义对象实现Comparable接口,重写comparaTo方法,该方法返回0表示相等,小于0表示准备存入的元素比被比较的元素小,否则大于0;
2、在创建TreeSet的时候向构造器中传入比较器Comparator接口实现类对象,实现Comparator接口重写compara方法。
如果向TreeSet存入自定义对象时,自定义类没有实现Comparable接口,或者没有传入Comparator比较器时,会出现ClassCastException异常。
第一种方式:传入的对象实现Comparable接口
public class Person implements Comparable<Person>{
private int age;
private String name;
public Person(int age, String name) {
this.age = age;
this.name = name;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
if (age != person.age) return false;
return name != null ? name.equals(person.name) : person.name == null;
}
@Override
public int hashCode() {
int result = age;
result = 31 * result + (name != null ? name.hashCode() : 0);
return result;
}
@Override
public String toString() {
return "Person{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
@Override
public int compareTo(Person o) {
int result = this.age - o.age;
if (result == 0){
return this.name.compareTo(o.name);
}
return result;
}
}
//测试方法
public static void main(String[] args) {
// 利用TreeSet来存储自定义类Person对象
TreeSet<Person> treeSet = new TreeSet<Person>();
// Person类实现了Comparable接口,并且重写comparaTo方法
// 比较规则是先按照 年龄排序,年龄相等的情况按照姓名排序
treeSet.add(new Person(12,"张三"));
treeSet.add(new Person(10,"李四"));
treeSet.add(new Person(14,"王五"));
treeSet.add(new Person(10,"赵大"));
for (Person p : treeSet){
System.out.println(p);
}
/*
输出结果:按照comparaTo方法内的逻辑来排序的
Person{age=10, name='李四'}
Person{age=10, name='赵大'}
Person{age=12, name='张三'}
Person{age=14, name='王五'}
*/
}
第二种方式:使用比较器Comparator
public static void main(String[] args) {
// 利用TreeSet来存储自定义类Person对象
// 创建TreeSet对象的时候传入Comparator比较器,使用匿名内部类的方式
// 比较规则是先按照 年龄排序,年龄相等的情况按照姓名排序
TreeSet<Person> treeSet = new TreeSet<>(new Comparator<Person>() {
@Override
public int compare(Person o1, Person o2) {
if (o1 == o2){
return 0;
}
int result = o1.getAge() - o2.getAge();
if (result == 0){
return o1.getName().compareTo(o2.getName());
}
return result;
}
});
treeSet.add(new Person(12,"张三"));
treeSet.add(new Person(10,"李四"));
treeSet.add(new Person(14,"王五"));
treeSet.add(new Person(10,"赵大"));
for (Person p : treeSet){
System.out.println(p);
}
/*
输出结果:按照compara方法内的逻辑来排序的
Person{age=10, name='李四'}
Person{age=10, name='赵大'}
Person{age=12, name='张三'}
Person{age=14, name='王五'}
*/
}
针对Collection集合我们到底使用谁呢?(掌握)
-
唯一吗?
-
是:Set
-
排序吗?
-
是:TreeSet或LinkedHashSet
-
否:HashSet
-
如果你知道是Set,但是不知道是哪个Set,就用HashSet。
-
-
-
-
否:List
-
要安全吗?
-
是:Vector
-
否:ArrayList或者LinkedList
-
查询多:ArrayList
-
增删多:LinkedList
-
如果你知道是List,但是不知道是哪个List,就用ArrayList。
-
-
-
如果你知道是Collection集合,但是不知道使用谁,就用ArrayList。
如果你知道用集合,就用ArrayList。
三、Map体系
Map的共性方法
说明 | 共性方法 |
---|---|
添加 | V put(K key, V value) void putAll(Map<? extends K,? extends V> m) |
删除 | V remove(Object key) void clear() |
根据key获取value | V get(Object key) |
判断是否存在 | boolean containsKey(Object key) boolean containsValue(Object value) |
判空 | boolean isEmpty() |
获取集合大小 | int size() |
获取迭代 | Set<Map.Entry<K,V>> entrySet() Set<K> keySet() |
集合转数组 | Object toArray() |
(1)HashMap:① 底层数据结构是数组+链表(解决冲突)+红黑树(JDK1.8),无序。② 线程不安全,效率高。
public static void main(String[] args) {
// 利用HashMap存储,自定义对象Person作为键
// 为了保证键的唯一性,必须重写hashCode和equals方法
HashMap<Person,String> map = new HashMap<Person,String>();
map.put(new Person(12,"张三"), "aa");
map.put(new Person( 13,"李四"), "bb");
map.put(new Person(14,"王五"), "cc");
map.put(new Person(12,"张三"), "dd");
// 第一种:通过Map.entrySet使用iterator遍历key和value
System.out.println("===================通过Map.entrySet使用iterator遍历key和value:===================");
Set<Map.Entry<Person, String>> entrySet = map.entrySet();
Iterator<Map.Entry<Person,String>> it = entrySet.iterator();
while (it.hasNext()) {
Map.Entry<Person, String> entry = it.next();
System.out.println("key= " + entry.getKey() + " and value= "
+ entry.getValue());
}
// 第二种:通过Map.entrySet遍历key和value
System.out.println("===================通过Map.entrySet遍历key和value:===================");
for (Map.Entry<Person, String> entry : map.entrySet()) {
System.out.println("key= " + entry.getKey() + " and value= "
+ entry.getValue());
}
// 第三种:通过Map.keySet遍历key和value
System.out.println("===================通过Map.keySet遍历key和value:===================");
for (Person key : map.keySet()) {
System.out.println("key= " + key + " and value= " + map.get(key));
}
// 第四种:通过Map.values()遍历所有的value,但是不能遍历键key
System.out.println("===================通过Map.values()遍历所有的value:===================");
for (String v : map.values()) {
System.out.println("value= " + v);
}
/*
输出结果:4种都是
key= Person{age=14, name='王五'} and value= cc
key= Person{age=12, name='张三'} and value= dd
key= Person{age=13, name='李四'} and value= bb
*/
}
(2)LinkedHashMap:① 底层数据结构是数组+链表(解决冲突)+红黑树(JDK1.8)+双向链表(保证遍历顺序和插入顺序一致),有序,存取一致。② 继承自HashMap,线程不安全。
public static void main(String[] args) {
// 利用LinkedHashMap存储,自定义对象Person作为键
// 为了保证键的唯一性,必须重写hashCode和equals方法
LinkedHashMap<Person,String> map = new LinkedHashMap<Person,String>();
map.put(new Person(12,"张三"), "aa");
map.put(new Person( 13,"李四"), "bb");
map.put(new Person(14,"王五"), "cc");
map.put(new Person(12,"张三"), "dd");
System.out.println("===================通过Map.entrySet遍历key和value:===================");
for (Map.Entry<Person, String> entry : map.entrySet()) {
System.out.println("key= " + entry.getKey() + " and value= "
+ entry.getValue());
}
/*
输出结果:取出来的顺序就是和存入的顺序保持一致
key= Person{age=12, name='张三'} and value= dd
key= Person{age=13, name='李四'} and value= bb
key= Person{age=14, name='王五'} and value= cc
*/
}
(3)TreeMap:① 底层数据结构是数组+红黑树,有序,对象实现Comparable接口或者给TreeMap集合传递一个Comparator接口对象实现排序。② 线程不安全。
使用自定义对象作为TreeMap集合的key值,由于TreeMap底层使用的二叉树,其中存放进去的所有数据都需要排序,要排序,就要求对象具备比较功能。对象所属的类需要实现Comparable接口,或者给TreeMap集合传递一个Comparator接口对象。
public static void main(String[] args) {
// 利用TreeMap存储,自定义对象Person作为键
// 自定义对象实现Comparable接口或者传入Comparator比较器
TreeMap<Person, String> map = new TreeMap<Person, String>(new Comparator<Person>() {
@Override
public int compare(Person o1, Person o2) {
if (o1 == o2) {
return 0;
}
int result = o1.getAge() - o2.getAge();
if (result == 0) {
return o1.getName().compareTo(o2.getName());
}
return result;
}
});
map.put(new Person(18, "张三"), "aa");
map.put(new Person(14, "李四"), "bb");
map.put(new Person(13, "王五"), "cc");
map.put(new Person(18, "张三"), "dd");
map.put(new Person(14, "赵大"), "ff");
System.out.println("===================通过Map.entrySet遍历key和value:===================");
for (Map.Entry<Person, String> entry : map.entrySet()) {
System.out.println("key= " + entry.getKey() + " and value= "
+ entry.getValue());
}
/*
输出结果:取出来的顺序就是和存入的顺序保持一致
key= Person{age=13, name='王五'} and value= cc
key= Person{age=14, name='李四'} and value= bb
key= Person{age=14, name='赵大'} and value= ff
key= Person{age=18, name='张三'} and value= dd
*/
}
(4)HashTable:① 底层数据结构是数组+链表(解决冲突),无序。② 线程安全,效率低。