目录
1.与集合有关的几种数据结构
(1)栈
(2)队列
(3)数组
(4)链表
(5)红黑树
2.List接口
List接口特点:
-
1.它是一个元素存取有序的集合。(有序的意思是说存储和取出元素的顺序是一致的)例如,存元素的顺序是11、22、33。那么集合中,元素的存储就是按照11、22、33的顺序完成的)。
-
2.它是一个带有索引的集合,通过索引就可以精确的操作集合中的元素(与数组的索引是一个道理)。
-
3.集合中可以有重复的元素,通过元素的equals方法,来比较是否为重复的元素。
List接口中常用带索引的方法:
注意:操作索引的时候一定要防止索引越界异常
代码演示:
package com.boboyu;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class Demo01 {
public static void main(String[] args) {
//创建一个List集合对象,多态
List<String> list = new ArrayList<>();
//1.使用add方法往list集合中添加元素
list.add("a");
list.add("b");
list.add("c");
list.add("d");
list.add("a");
//public void add(int index, E element)`: 将指定的元素,添加到该集合中的指定位置上。
//2.在c和d之间添加“boboyu”
list.add(3,"boboyu");
System.out.println(list); //重写了toString方法
//public E remove(int index)`: 移除列表中指定位置的元素, 返回的是被移除的元素。
//3.移除指定元素
String removeEle = list.remove(2);
System.out.println("被移除的元素是:"+removeEle);
System.out.println(list);
//public E set(int index, E element)`:用指定元素替换集合中指定位置的元素,返回值的更新前的元素。
//4.把最后一个a替换为A
String setEle = list.set(3,"A");
System.out.println("被替换的元素为:"+setEle);
System.out.println(list);
//5.List的三种遍历方式
//第一种:使用普通循环来遍历List
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
System.out.println("------------------");
//第二种:使用迭代器遍历List
Iterator<String> it = list.iterator();
while(it.hasNext())
{
String s=it.next(); //这里一定要使用临时对象来获取next方法的返回结果,
//不能在循环中多次调用next方法,因为每调用一次指针都会后移一位
System.out.println(s);
}
System.out.println("------------------");
//第三种:使用增强for循环遍历List
for (String s : list) {
System.out.println(s);
}
}
}
3.List集合的实现类
3.1 ArrayList集合
与数组的最大区别:数组的长度不可以发生变化,而ArrayList集合的长度是可以变化的
(1)常用构造方法
代码示例:
package com.boboyu;
import java.util.ArrayList;
public class Demo01 {
public static void main(String[] args) {
//创建一个ArrayList集合对象list
//备注:从JDK1.7开始,右侧的尖括号内可以不写内容,但<>本身还是要写的
ArrayList<String> list = new ArrayList<>();
System.out.println(list); //对于ArrayList来说,直接打印的不是地址值,而是内容
//如果内容是空,得到的是空的中括号:[]
}
}
(2)常用方法
同上述List接口中的add、get、remove、set方法
三种遍历方法也参考上述List接口
(3)ArrayList存储基本数据类型
如果希望往ArrayList中存储基本数据类型,必须使用基本数据类型对应的包装类
基本类型 | 包装类(引用类型,位于java.lang包下) | |
byte | Byte | |
short | Short | |
int | Integer | 特殊 |
long | Long | |
float | Float | |
double | Double | |
char | Character | 特殊 |
boolean | Boolean |
package com.boboyu;
import java.util.ArrayList;
public class Demo01 {
public static void main(String[] args) {
//错误写法,泛型只能使用引用类型,不能使用基本类型
//ArrayList<int> list01 = new ArrayList<>();
ArrayList<Integer> list02 = new ArrayList<>();
list02.add(100);
list02.add(200);
System.out.println(list02);
int num=list02.get(1);
System.out.println("索引1处的元素是:"+num);
//注意:从java1.5开始,支持自动装箱、自动拆箱
// 自动装箱:基本类型---->包装类型
// 自动拆箱:包装类型---->基本类型
}
}
(4)ArrayList的部分源码
不是同步的就意味着多线程,效率高
- 1.ArrayList底层就是数组
- 2.ArrayList的add方法底层是先判断,元素个数+1后是否大于底层数组的长度,如果大于,进行扩容,建立一个新的数组,并将原来数组中的元素复制到新的数组的对应位置
注意:开发时不能随意地使用ArrayList完成任何需求
3.2 LinkedList集合
LinkedList的特点:
- 1.底层是一个双向链表:查询慢,增删快
- 2.里边包含了大量的操作首尾元素的方法
- 3.
即为多线程
- 注意:使用LinkedList集合中特有的方法时,不能使用多态
LinkedList操作首尾元素的方法:
1)添加的方法
package com.boboyu;
import java.util.LinkedList;
public class Demo01 {
public static void main(String[] args) {
show01();
}
//添加元素演示
private static void show01()
{
//创建LinkedList集合对象
LinkedList<String> linked = new LinkedList<>();
//使用add方法往集合添加元素
linked.add("a");
linked.add("b");
linked.add("c");
System.out.println(linked);
//使用addFirst将指定元素添加到链表的头部
linked.addFirst("boboyu0");
System.out.println(linked);
//使用push将指定元素添加到链表的头部
linked.push("boboyu1");
System.out.println(linked);
//使用addLast将指定元素添加到链表的尾部,此方法等效于add
linked.addLast("boboyu2");
System.out.println(linked);
}
}
2)获取元素的方法
package com.boboyu;
import java.util.LinkedList;
public class Demo01 {
public static void main(String[] args) {
show02();
}
//添加元素演示
private static void show02()
{
//创建LinkedList集合对象
LinkedList<String> linked = new LinkedList<>();
//使用add方法往集合添加元素
linked.add("a");
linked.add("b");
linked.add("c");
String first1=linked.getFirst();
System.out.println("linked的第一个元素为:"+first1);
String last1=linked.getLast();
System.out.println("linked的最后一个元素为:"+last1);
linked.clear();//清空集合中的元素,在获取集合中的元素时会抛出NoSuchElementException
if(!linked.isEmpty())
{
String first2=linked.getFirst();
System.out.println("linked的第一个元素为:"+first2);
String last2=linked.getLast();
System.out.println("linked的最后一个元素为:"+last2);
}
}
}
3)移除方法
package com.boboyu;
import java.util.LinkedList;
public class Demo01 {
public static void main(String[] args) {
show03();
}
//移除元素演示
private static void show03()
{
//创建LinkedList集合对象
LinkedList<String> linked = new LinkedList<>();
//使用add方法往集合添加元素
linked.add("a");
linked.add("b");
linked.add("c");
linked.add("d");
System.out.println(linked);
System.out.println(linked.removeFirst());
System.out.println(linked.removeLast());
System.out.println(linked);
System.out.println(linked.pop()); //pop相当于removeFirst
System.out.println(linked);
}
}
3.3 Vector集合
与ArrayList相同的是底层都是数组,不同的是Vector是同步的,同步的就是单线程的,速度慢,后面被ArrayList取代了
4.Set接口及其实现类
java.util.Set
接口和java.util.List
接口一样,同样继承自Collection
接口,它与Collection
接口中的方法基本一致,并没有对Collection
接口进行功能上的扩充,只是比Collection
接口更加严格了
特点:
- 1.不包含重复元素
- 2.这个接口没有索引,没有带索引的方法,也不能使用普通的for循环来遍历
4.1 HashSet
(1)HashSet实现类的特点
- 1.不包含重复元素
- 2.这个接口没有索引,没有带索引的方法,也不能使用普通的for循环来遍历
- 3.是一个无序的集合,存储元素和取出元素的顺序有可能不一致
- 4.底层是一个哈希表结构,查询非常快
package com.boboyu;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
public class Demo01 {
public static void main(String[] args) {
Set<Integer> set = new HashSet<>();
set.add(1);
set.add(3);
set.add(2);
set.add(1);
//1.使用迭代器遍历set集合
Iterator<Integer> it = set.iterator();
while(it.hasNext())
{
Integer n = it.next();
System.out.println(n);
}
//2.使用增强for循环遍历set集合
System.out.println("-----------------");
for(Integer i : set)
{
System.out.println(i);
}
}
}
(2)哈希表
1)哈希值
哈希值:是一个十进制的整数,由系统随机给出(就是对象的地址值),是一个逻辑地址,是一个模拟出来的地址,不是数据实际存储的物理地址
在Object类中有一个方法,可以获取对象的哈希码值
以下是hashCode()的源码
native表明该方法调用的是本地操作系统的方法
代码示例:
package com.boboyu;
public class Demo01 {
public static void main(String[] args) {
Person p1 = new Person();
int h1 = p1.hashCode();
System.out.println(h1);
Person p2 = new Person();
int h2 = p2.hashCode();
System.out.println(h2);
/*
toString方法的源码
return getClass().getName() + "@" + Integer.toHexString(hashCode());
*/
System.out.println(p1);
System.out.println(p2);
}
}
class Person {
}
String类重写了Object类的hashCode方法
package com.boboyu;
public class Demo01 {
public static void main(String[] args) {
String s1 = new String("abc");
String s2 = new String("abc");
System.out.println(s1.hashCode());
System.out.println(s2.hashCode());
System.out.println("重地".hashCode());
System.out.println("通话".hashCode());
}
}
2)哈希表
总而言之,JDK1.8引入红黑树大程度优化了HashMap的性能,那么对于我们来讲保证HashSet集合元素的唯一,其实就是根据对象的hashCode和equals方法来决定的。如果我们往集合中存放自定义的对象,那么保证其唯一,就必须复写hashCode和equals方法建立属于当前对象的比较方式。
(3)Set集合中元素不重复的原理
(4)HashSet存储自定义类型的元素
给HashSet中存放自定义类型元素时,需要重写对象中的hashCode和equals方法,建立自己的比较方式,才能保证HashSet集合中的对象唯一
示例代码:
要求:同名同年龄的人,视为同一个人,只存储一次
不重写hashCode和equals方法:
Person.java
package com.boboyu;
public class Person {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
Demo01.java
package com.boboyu;
import java.util.HashSet;
public class Demo01 {
public static void main(String[] args) {
//创建一个HashSet集合,让它存储Person
HashSet<Person> set = new HashSet<>();
Person p1 = new Person("小明",18);
Person p2 = new Person("小明",18);
Person p3 = new Person("小明",19);
System.out.println(p1.hashCode());
System.out.println(p2.hashCode());
System.out.println(p1==p2);
System.out.println(p1.equals(p2));
set.add(p1);
set.add(p2);
set.add(p3);
System.out.println(set);
}
}
可以发现在没有重写hashCode和equals方法前,相同姓名和相同年龄的对象是不同的对象
重写hashCode和equals方法:
Person.java
package com.boboyu;
import java.util.Objects;
public class Person {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@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 String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
Demo01.java
package com.boboyu;
import java.util.HashSet;
public class Demo01 {
public static void main(String[] args) {
//创建一个HashSet集合,让它存储Person
HashSet<Person> set = new HashSet<>();
Person p1 = new Person("小明",18);
Person p2 = new Person("小明",18);
Person p3 = new Person("小明",19);
System.out.println(p1.hashCode());
System.out.println(p2.hashCode());
System.out.println(p1==p2);
System.out.println(p1.equals(p2));
set.add(p1);
set.add(p2);
set.add(p3);
System.out.println(set);
}
}
4.2 LinkedSet
LinkedSet继承了HashSet
特点:
- 底层是一个哈希表(数组+链表/红黑树)+链表,多了一条链表(记录元素的存储顺序),保证元素有序
示例代码:
package com.boboyu;
import java.util.HashSet;
import java.util.LinkedHashSet;
public class Demo01 {
public static void main(String[] args) {
HashSet<String> set = new HashSet<>();
set.add("www");
set.add("abc");
set.add("abc");
set.add("com");
System.out.println(set); //无序,不允许元素重复
LinkedHashSet<String> linked = new LinkedHashSet<>();
linked.add("www");
linked.add("abc");
linked.add("abc");
linked.add("com");
System.out.println(linked); //有序,不允许元素重复
}
}