容器简单来说,就像是我们生活中用来容纳东西的物体,他可以容纳和管理数据,数组也是一种容器,他的优势可以快速的访问元素,效率高,但是他也有他的不足,他有固定的长度,不灵活,不会随着需求的变化而进行扩容操作,不能满足管理和组织的需求,所以引进了一种可以扩容更加灵活的容器,也成为集合(Collection)
容器的结构
单例集合:将数据一个一个进行存储
下面就分析单例集合的具体实现
单例集合的整体框架分析
总的单例集合的接口Collection,他有两个子接口,分别是List接口和Set接口
Collection接口中定义的抽象方法
List接口分析
List接口具体一下几个特点:
- 有序:存入集合的顺序和取出集合的顺序一致
- 可重复:允许添加重复的元素
List接口中在Collection基础上定义的抽象方法(List多了一些跟顺序也就是索引有关的方法)
List接口的具体实现类
- ArrayList(ArrayList底层是用数组实现的存储,查询效率高,增删效率低,线程不安全)
ArrayList类的使用
public class ArrayListTest {
public static void main(String[] args) {
//实例化一个容器
//在实例化一个对象的时候,对象引用应该用其接口类型,
//如果用接口定义对象的引用,之后发生改变对代码的改变较小
//下面使用的show方法是自定义遍历集合的方法,在最下面
List<String> list=new ArrayList<>();
//添加元素
//这个是Collection接口中定义的add方法,返回一个布尔类型
boolean flag=list.add("bjsxt");
boolean flag2=list.add("itbaizhan");
list.add("java全系列");
//这个是List接口中的add方法,在给定的索引位置插入指定的元素,索引的数值不可以超过现在集合中元素的个数
list.add(2,"wujunhong");
//获取元素 size() 方法,返回集合中元素的个数和get() 方法,获得集合中指定位置的元素的使用
for(int i=0;i<list.size();i++){
System.out.println(list.get(i));
}
//替换指定位置的元素,返回被替换掉的元素
String str=list.set(0,"北京尚学堂");
System.out.println(str);
show(list,"替换指定位置的元素");
//删除元素:根据索引删除元素,返回删除的元素,之后的元素都会向前移位
list.remove(0);
show(list,"根据索引删除元素");
//删除指定元素,返回布尔类型的值,如果删除成功就返回true
list.remove("java全系列");
show(list,"删除指定元素");
//清空集合中所有元素
list.clear();
show(list,"清空集合中所有元素");
//判断该集合是否为空,如果为true,则是空集合
System.out.println(list.isEmpty());
list.add("武俊宏");
list.add("苏青旭");
list.add("朱九思");
list.add("苏青旭");
show(list,"添加了四个元素");
//判断该集合中是否含有指定元素
System.out.println(list.contains("武俊宏"));
//查找元素在容器中的位置、、只可以找到第一次出现和最后一次出现的位置,中间的位置找不到
//如果元素查找不到 则返回-1
System.out.println(list.indexOf("苏青旭"));//该元素在容器中第一次出现的位置
System.out.println(list.lastIndexOf("苏青旭"));//该元素在容器中最后一次出现的位置
//将一个单例集合转化成数组
System.out.println("----------将单例集合转换成数组-----------");
Object[]arr=list.toArray();
for (int i=0;i<arr.length;i++){
System.out.println(arr[i]);
}
System.out.println("----------将单例集合转换成指定泛型的数组---------");
String[] str2=list.toArray(new String[list.size()]);
for (int i=0;i<str2.length;i++){
System.out.println(str2[i]);
}
System.out.println("-------集合的并集操作--------- ");
List<String> a=new ArrayList<>();
a.add("a");
a.add("b");
a.add("c");
List<String> b=new ArrayList<>();
b.add("b");
b.add("c");
b.add("d");
//a并b 并好的元素在a中
boolean flag7=a.addAll(b);
show(a,"集合并集操作");
//集合的交集操作
List<String> a1=new ArrayList<>();
a1.add("a");
a1.add("b");
a1.add("c");
List<String> b1=new ArrayList<>();
b1.add("b");
b1.add("c");
b1.add("d");
boolean flag8=a1.retainAll(b);
show(a1,"集合交集操作");
a.removeAll(a1);
show(a,"容器的差值操作");
}
public static void show(List list,String operation){
System.out.println("----------"+operation+"------------");
//获取元素 size() 方法,返回集合中元素的个数和get() 方法,获得集合中指定位置的元素的使用
for(int i=0;i<list.size();i++){
System.out.println(list.get(i));
}
}
}
2、LinkedList容器(LinkedList容器底层采用的是双向链表的存储,他查询效率低,增删效率高,线程不安全)
public class LinkedListTest {
public static void forEach(LinkedList<String> list){
System.out.println("-----------------------");
for (String str:list) {
System.out.println(str);
}
}
public static void main(String[] args) {
//非List标准,如果使用List标准,对象的引用使用List就可以,方法使用List接口中定义的方法
LinkedList<String> list=new LinkedList<>();
list.addFirst("a");
list.addFirst("b");
list.addFirst("c");
//返回列表中第一个元素
System.out.println(list.getFirst());
//返回此列表中最后一个元素
System.out.println(list.getLast());
forEach(list);
LinkedList<String> list1=new LinkedList<>();
list1.addLast("a");
list1.addLast("b");
list1.addLast("c");
forEach(list1);
//移除此列表中第一个元素
System.out.println(list1.removeFirst());
//移除此列表中最后一个元素
System.out.println(list1.removeLast());
//等效于removeFirst()
System.out.println(list1.pop());
//等效于addFirst
list1.push("f");
forEach(list1);
//判断是否包含元素,返回一个Boolean类型的值
System.out.println( list1.isEmpty());
}
}
3、Vector容器
和ArrayList的区别和联系
- Vector底层也是数组实现的,和ArrayList的所有操作都是一样的,但是Vector是单线程,串行的,加了同步检查,所以线程安全,效率低,而ArrayList是并行的,所以线程不安全
- 操作和ArrayList都一样
- ArrayList底层采用的是延迟初始化,在扩容的时候是1.5倍扩容
Vector底层采用的是立即初始化,在扩容的是时候是2倍扩容
4、Stack容器
Stack栈容器,是Vector的一个子类,他实现了标准的先进后出原则
Stack容器的使用以及案例
public class StackTest {
public static void main(String[] args) {
//实例化一个栈对象,对象的引用只能用Stack 因为list接口中没有操作栈的方法
Stack<String> s=new Stack<>();
//在栈中添加元素
s.push("a");
s.push("b");
s.push("c");
//查看栈顶元素
System.out.println(s.peek());
//判断栈是否为空
System.out.println(s.empty());
//返回元素在栈当中的位置
System.out.println(s.search("b"));
//获取栈容器中的元素,从栈顶移除元素
String str1=s.pop();
String str2=s.pop();
String str3=s.pop();
System.out.println(str1);
System.out.println(str2);
System.out.println(str3);
//再次判断栈是否为空
System.out.println(s.empty());
System.out.println("---------------------");
symmetry();
}
public static void symmetry(){
//Stack使用案例,判断元素的对称性
String str="...[...(...{......}...).....].....(....).....[...]..(.";
//创建一个Stack实例
Stack<String> stack=new Stack<>();
//假设修正法
boolean flag=true;
//拆分字符串,获取字符
for (int i=0;i<str.length();i++){
char c=str.charAt(i);
if (c=='{'){
stack.push("}");
}
if (c=='['){
stack.push("]");
}
if (c=='('){
stack.push(")");
}
if (c=='}'||c==']'||c==')'){
if (stack.empty()){
flag=false;
break;
}
String x=stack.pop();
if(x.charAt(0)!=c){
flag=false;
break;
}
}
}
if (!stack.empty()){
flag=false;
}
System.out.println(flag);
}
}
Set接口分析
特点:无序、不可重复。无序是指Set中的元素没有索引,我们只可以遍历查找,不可重复是指,新元素如果和Set中某个元素通过equals()方法对比为true,只能保留一个。
Set接口具体实现类
- HashSet
HashSet是一个没有重复元素的集合,不保证元素的顺序,HashSet允许有null元素,HashSet 是采用哈希算法实现的,底层是用HashMap实现的,下来介绍一下Hash原理:
存储的数据通过进行模9运算,然后存储到对应的位置,如果这个位置上有元素了,会调用对应的equals方法,判断两个元素是否相同,如果相同则不会添加,如果不相同,则会使用单向链表保存该元素。
HashSet类的使用
public class HashSetTest {
public static void main(String[] args) {
//实例化Hashset对象
HashSet<String> hashSet=new HashSet<>();
//添加元素,不能保证存放元素的顺序,以及不可以有元素重复
//线程不安全,可以有null
//无序:对元素的哈希值进行运算,来决定元素到底存放在哪里
hashSet.add("a1");
hashSet.add("c2");
hashSet.add("d3");
hashSet.add("a");
//删除元素
hashSet.remove("a1");
System.out.println(hashSet.size());
//从Hashset中获取元素,在set容器中没有索引,没有对应的get方法
for(String str:hashSet){
System.out.println(str);
}
System.out.println("***********HashSet存储自定义对象,User类在下面*************");
/**
* 如果想要识别存储对象属性的比较,是他们有互异性
* 需要在JavaBean中重写HashCode以及equals方法
*/
Set<Users> u=new HashSet<>();
Users u1=new Users("武俊宏",18);
Users u2=new Users("武俊宏",18);
System.out.println(u1.hashCode());
System.out.println(u2.hashCode());
u.add(u1);
u.add(u2);
for (Users users:u){
System.out.println(users);
}
}
}
public class Users implements Comparable<Users>{
private String userName;
private int userAge;
@Override
public boolean equals(Object o) {
System.out.println("程序执行到这里了");
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Users users = (Users) o;
return userAge == users.userAge && Objects.equals(userName, users.userName);
}
@Override
public int hashCode() {
return Objects.hash(userName, userAge);
}
public Users() {
}
public Users(String userName, int userAge) {
this.userName = userName;
this.userAge = userAge;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public int getUserAge() {
return userAge;
}
public void setUserAge(int userAge) {
this.userAge = userAge;
}
@Override
public String toString() {
return "Users{" +
"userName='" + userName + '\'' +
", userAge=" + userAge +
'}';
}
}
- TreeSet(TreeSet是一个可以对元素进行排序的容器,底层实际是用TreeMap实现的,通过Key来存储Set元素,因为他要排序,所以我们要给定排序规则)
排序规则1:通过元素自身实现比较规则,需要实现Comparable接口中的compareTo方法,该方法用来定义比较规则,TreeSet通过调用该方法来完成对元素的排序处理
public class Users implements Comparable<Users>{
private String userName;
private int userAge;
@Override
public boolean equals(Object o) {
System.out.println("程序执行到这里了");
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Users users = (Users) o;
return userAge == users.userAge && Objects.equals(userName, users.userName);
}
@Override
public int hashCode() {
return Objects.hash(userName, userAge);
}
public Users() {
}
public Users(String userName, int userAge) {
this.userName = userName;
this.userAge = userAge;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public int getUserAge() {
return userAge;
}
public void setUserAge(int userAge) {
this.userAge = userAge;
}
@Override
public String toString() {
return "Users{" +
"userName='" + userName + '\'' +
", userAge=" + userAge +
'}';
}
//定义比较规则
@Override
public int compareTo(Users o) {
if (this.userAge<o.getUserAge()){
return 1;
}
if (this.userAge==o.getUserAge()){
return this.userName.compareTo(o.userName);
}
return -1;
}
}
public class TreeSetTest {
public static void main(String[] args) {
//实例化一个TreeSet对象
Set<String> treeSet=new TreeSet<>();
treeSet.add("c");
treeSet.add("f");
treeSet.add("d");
treeSet.add("d");
treeSet.add("a");
for (String str: treeSet) {
System.out.println(str);
}
System.out.println("******************");
Set<Users> set=new TreeSet<>();
Users u1=new Users("wujunhong",15);
Users u5=new Users("oi",2);
Users u6=new Users("pi",2);
set.add(u1);
set.add(u5);
set.add(u6);
for (Users users:set){
System.out.println(users);
}
}
}
排序规则2:通过比较器实现比较排序规则,通过比较器定义比较规则时,我们需要单独创建一个比较器,比较器需要实现Comparator接口中的compare方法来定义比较规则,在实例化TreeSet时将比较器对象交给TreeSet来完成元素的排序处理,元素此时就不需要实现比较规则了
//定义一个Student类,方便后面的测试
public class Student {
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.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;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Student student = (Student) o;
return age == student.age && Objects.equals(name, student.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
//定义比较器
public class StudentComparator implements Comparator<Student> {
@Override
public int compare(Student o1, Student o2) {
if (o1.getAge()>o2.getAge()){
return 1;
}
if (o1.getAge()==o2.getAge()){
return o1.getName().compareTo(o2.getName());
}
return -1;
}
}
public class TreeSetTest {
//必须把比较器对象交给创建好的对象
Set<Student> s=new TreeSet<>(new StudentComparator());
Student student=new Student("wu",123);
Student student1=new Student("jun",15);
Student student2=new Student("hong",14);
Student student3=new Student("qong",14);
s.add(student);
s.add(student1);
s.add(student2);
s.add(student3);
for (Student stu:s){
System.out.println(stu);
}
}
}
双例集合:基于Key和Value的结构存储数据
下面就分析双例集合的具体实现
双例集合的整体框架分析(底层实现比较难描述,这里不具体展开描述)
总的双例集合的接口Map,整个框架中只有他一个接口,其他的都是接口的具体实现类
Map接口中定义的抽象方法
HashMap容器类
HashMap是Map接口的实现类,他采用哈希算法实现。由于底层采用哈希表存储数据,所以要求键不能重复,如果发生重复,新的值会替换旧的值。HashMap在查找、删除、修改方面都有非常高的效率,代码和之前的代码相似,但是具体如何遍历元素的三个方式是非常有意思的
public class HashMapTest {
public static void main(String[] args) {
//实例化一个hashMap容器
Map<String,String> map=new HashMap<>();
//添加元素,put方法,只有在容器中有重复的key时,他才会返回原来没有被覆盖的值
map.put("a","A");
String value=map.put("a","B");
System.out.println(value);
//获取元素,
//方法一:元素多,需要调用很多次get方法;而且需要提前知道key的值
System.out.println(map.get("a"));
System.out.println("---------------");
map.put("c","C");
map.put("d","D");
map.put("e","E");
map.put("f","F");
//方法二:获取HashMap容器中所有元素,可以使用keySet方法和get方法进行实现
Set<String>keys=map.keySet();
for (String str:keys){
String values=map.get(str);
System.out.println(str+"---"+values);
}
//方法三:通过entrySet方法获取Map.Entry类型(k-v映射出来的键值对)获取元素
System.out.println("**************");
Set<Map.Entry<String,String>> entrySet=map.entrySet();
for (Map.Entry<String,String> entry:entrySet){
String k=entry.getKey();
String v=entry.getValue();
System.out.println(k+"-----"+v);
}
System.out.println("-------HashMap的并集操作--------");
Map<String,String> map1=new HashMap<>();
map1.put("l","L");
map1.put("c","ccc"); // 这个会覆盖掉
map1.putAll(map);
Set<String> sets=map1.keySet();
for (String str:sets){
String values=map1.get(str);
System.out.println(str+"---"+values);
}
System.out.println("-----HashMap删除元素");
String v=map.remove("e");
System.out.println("删除的元素是:"+v);
Set<String> dele=map.keySet();
for (String str:dele){
String values=map.get(str);
System.out.println(str+"-----"+values);
}
System.out.println("------判断k和v是否存在");
boolean flag=map.containsKey("aaa");
boolean flag1=map.containsKey("a");
boolean flag2=map.containsValue("B");
boolean flag3=map.containsValue("BB");
}
}
Tree Map容器类
TreeMap和HashMap同样实现了Map接口,HashMap效率高于Tree Map,TreeMap是可以对键进行排序的一种容器,在需要排序的时候可以使用Tree Map,他的底层是基于红黑树实现的。同样需要给定排序规则
- 元素自身实现比较规则
public class TreeMapTest {
public static void main(String[] args) {
System.out.println("*********通过自身比较规则实现排序***********");
System.out.println("------存放普通字符类型对象--------");
//实例化一个terrMap对象
Map<String,String> treeMap=new TreeMap<>();
//添加元素
treeMap.put("a","A");
treeMap.put("d","D");
treeMap.put("f","F");
treeMap.put("b","B");
//对元素进行遍历
Set<String> strings=treeMap.keySet();
for (String str:strings){
String k=str;
String v=treeMap.get(k);
System.out.println(k+"------"+v);
}
System.out.println("------存放users对象--------");
Map<Users,String> map=new TreeMap<>();
Users u1=new Users("武俊宏",19);
Users u2=new Users("主机四",12);
Users u3=new Users("苏青旭",14);
Users u4=new Users("贾凯奇",33);
map.put(u1,"武俊宏");
map.put(u2,"主机四");
map.put(u3,"苏青旭");
map.put(u4,"贾凯奇");
Set<Users>set=map.keySet();
for (Users users:set){
System.out.println(users+"---"+map.get(users));
}
}
}
- 通过比较器实现比较规则
public class TreeMapTest {
public static void main(String[] args) {
//这里必须把比较器给了TreeMap,和TreeSet一样,因为Student没有实现Comparator接口
Map<Student,String> stringMap=new TreeMap<>(new StudentComparator());
Student student1=new Student("武俊宏",18);
Student student2=new Student("主机四",14);
Student student3=new Student("苏青旭",18);
Student student4=new Student("贾凯奇",55);
stringMap.put(student1,"武俊宏");
stringMap.put(student2,"主机四");
stringMap.put(student3,"苏青旭");
stringMap.put(student4,"贾凯奇");
Set<Student>students=stringMap.keySet();
for (Student student:students){
System.out.println(student+"----------"+stringMap.get(student));
}
}
}
到这里差不多就可以结束了,还有迭代器和Collections工具类的使用在这里就不一一描述了,容器这里的底层还是很有意思的,多看几遍可以对容器有更深的认识!