
数组其实就是一个集合。集合实际上就是一个容器。可以来容纳其它类型的数据。
1.2 集合有什么用?集合为什么说在开发使用较多?
集合是一个容器,是一个载体,可以一次容纳多个对象。在实际开发中,假设连接数据库,数据库当中有10条记录,那么假设把这10条数据查询出来,在java程序中会将10条数据封装成10个java对象(new出来的对象),然后将10个对象放到某一个集合当中,将集合传到前端,然后遍历集合,将一个数据一个数据展现出来。
集合不能直接存储基本数据类型,另外集合也不是直接存储java对象。集合当中存休的都是java对象的内存地址,(或者说集合中存储的是引用)。
list.add(100);//自动装箱Integer注意:
- 集合在java中本身也是一个容器,是一个对象。集合中任何时候存储的都是"引用"。
1.4 不同的集合对应不同的数据结构集合可以存储集合,集合存储的是对象的地址值
在java中每一个不同的集合,底层会对应不同的数据结构。往不同的集合中存储元素,等同于将数据放到不同的数据结构当中。什么是数据结构?数据存储的结构就是数据结构。不同的数据结构,数据存储方式不同。
例如:数组、二叉树、链表、哈希表…。这些都是常见的数据结构。
你往集合c1中放数据。可能是放到了数组上了。
你往集合c2中放数据,可能是放到了二叉树上了。
new ArrayList(); 创建一个集合对象,底层是数据。
new TreeSet(); 创建一个集合对象,底层是二叉树。
在java中集合分为两大类:
一类是单个方式存储元素:
单个方式存储元素,这一类集合中的超级父接口:java.util.Collection;
一类是以键值对的方式存储元素:
以键值对的方式存储元素,这一类集合中的超级父接口:java.util.Map
集合继承结构图
List接口的实现类
ArrayList集合
底层采用了数组这种数据结构。Array集合是非线程安全的。
linkedList集合
底层采用了双向链表的数据结构。
Vector集合
底层采用了数组这种数据结构。Vector集合是线程安全的。Vector所有的方法都是有synchronized关键字 修饰,所以线程安全,但是效率低,现在有别的方案,所以Vector使用较少了。
Set接口的实现类
HashSet集合
实际上HashSet集合在new的时候,底层实际上new了一个HashMap集合。想HashSet集合中存储元素,实际上是存储到HashMap集合中了。HashMap集合是一个哈希表数据结构。
SortedSet接口继承Set接口,实现类是TreeSet
SortedSet集合存储元素的特点:由于继承了Set集合,所以它的特点是无序不可重复(没有下标),但是放在SortedSet集合中元素可以自动排序。我们称为可排序集合,放到该集合中的元素是自动按照大小顺序排序的。
TreeSet集合底层实际上是TreeMap,new TreeSet集合的时候,底层实际上new了一个TreeMap集合。往TreeSet集合中存储数据的时候,实际上是往TreeMap集合中存储数据。TreeMap集合底层采用了二叉树的数据结构。
1.6 Map集合继承结构图总结(所有的实现类):
ArrayList:底层是数组。linkedList:底层是双向链表。Vector:底层是数组,线程安全的,效率低,使用较少。HashSet:底层是HashMap,放到HashSet集合中的元素等同于放到了HashMap集合key部分了。TreeSet:底层是TreeMap,放到TreeSet集合中的元素等同于放到了TreeMap集合key部分了。HashMap:底层是哈希表。Hashtable:底层是哈希表,线程安全,效率低,使用较少。Properties:是线程安全的,并且key和value只能存户字符串String。TreeMap:底层是二叉树。TreeMap集合的key可以自定安装大小顺序排序。
List集合存储元素的特点:
有序可重复的
有序:存储进去的顺序和取出的顺序是一样的,List集合中的元素有下标,怎么进去就怎么出来。重复:存进去1,可以在存储一个1。
Set(Map)集合存储元素的特点:
无序不可重复
无序:存进去的顺序和取出来的顺序不一定相同,Set集合红的元素没有下标。不可重复:存进去1,就不可在存储1了。
SortedSet(SortedMap)集合存储元素的特点:
无序不可重复
无序:存进去的顺序和取出来的顺序不一定相同,Set集合红的元素没有下标。不可重复:存进去1,就不可在存储1了。
可排序
排序:可以按照大小顺序排列。
Map集合的key,就是以Set集合。往Set集合中方数据,实际上放到了Map集合的key部分。
关于Collection的常用方法
package com.ccy.java.List.Collection;
import java.util.ArrayList;
import java.util.Collection;
public class Collection01 {
public static void main(String[] args) {
//创建一个集合对象
//Collection collection = new Collection();//接口是抽象的,无法实例化
//多态
Collection al = new ArrayList();
//测试Collection接口中的常用方法
al.add(1200);//自定装箱(java5的新特性)实际上是放进去了一个对象的内存地址。Integer x = new Integer(1200);
al.add(new Object());
al.add(true);//自动装箱
//获取集合红元素的个数
System.out.println("集合中元素的个数:"+al.size()); //3
//清空集合中的元素
System.out.println("清空集合中的元素,结果如下显示:");
al.clear();
System.out.println("集合中元素的个数:"+al.size()); //0
//再向集合中添加元素
al.add("nihao");//“nihao”对象的内存地址放到了集合中
al.add("word");
System.out.println("集合中元素的个数:"+al.size()); //2
//判断集合中是否包含指定的元素
boolean word = al.contains("word");
boolean word2 = al.contains("word2");
System.out.println(word);//true
System.out.println(word2);//false
//删除集合中的某个元素
System.out.println("删除集合中的某个元素,结果如下:");
al.remove("word");
System.out.println("集合中元素的个数:"+al.size()); //1
//判断集合是否为空
boolean empty = al.isEmpty();
System.out.println(empty);//false
//将集合转换成数组(了解)
System.out.println("遍历数组,结果如下:");
al.add(100);
al.add("abcd");
Object[] objects = al.toArray();
for(int i = 0;i
结果显示
集合中元素的个数:3
清空集合中的元素,结果如下显示:
集合中元素的个数:0
集合中元素的个数:2
true
false
删除集合中的某个元素,结果如下:
集合中元素的个数:1
false
遍历数组,结果如下:
nihao
100
abcd
2.2 Iterator
关于集合的迭代/遍历
package com.ccy.java.List.Collection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
public class Collection02 {
public static void main(String[] args) {
//以下遍历/迭代的方式,是所有Collection通用的一种方式(包括子类),但在Map集合中不能使用
//创建一个ArrayList集合对象
System.out.println("ArrayList集合:");
Collection c = new ArrayList();
//向集合中添加元素
c.add(100);
c.add("hello");
c.add("nihao");
c.add(new Student());
//第一步:获取集合对象的迭代器对象Iterator
Iterator it = c.iterator();
//取出集合中的数据,返回的Object对象
while (it.hasNext()){
//存进去什么类型,取出来还是什么类型
Object next = it.next();
//只是在打印时,prinlt会将其他类型转换成字符串类型打印输出
System.out.println(next);
}
System.out.println();
System.out.println("HashSet集合:");
//创建一个HashSet集合对象
Collection hs = new HashSet();//无序不可重复
//向HashSet集合中添加元素
hs.add(10);
hs.add(30);
hs.add(1);
hs.add(100);
hs.add(10);
//获取迭代器
Iterator i = hs.iterator();
//迭代数据
while(i.hasNext()){
System.out.println(i.next());
}
}
}
class Student{ }
显示结果
ArrayList集合:
100
hello
nihao
com.ccy.java.List.Collection.Student@4554617c
HashSet集合:
1
100
10
30
2.3 contains原理
package com.ccy.java.List.Collection;
import java.util.ArrayList;
import java.util.Collection;
public class Contains03 {
public static void main(String[] args) {
Collection c = new ArrayList();
String str1 = new String("abc");
c.add(str1);
String str2 = new String("ccy");
c.add(str1);
String str3 = new String("abc");
//str3没有添加到集合中
//c.contains(str3)在底层是这么写的:str3.equals(str1)
System.out.println(c.contains(str3));//true
}
}
2.4 contains和remove原理测试
测试contains方法
结论:存放在一个集合中的类型,一定要重写equals方法,如果不是自己new出来的对象就不用重写equals方法
package com.ccy.java.List.Collection;
import java.util.ArrayList;
import java.util.Collection;
public class contains04 {
public static void main(String[] args) {
//创建ArrayList集合对象
Collection c = new ArrayList();
//实例化User对象
User ccy1 = new User("ccy");
//向集合中添加元素
c.add(ccy1);
//实例化User对象
User ccy2 = new User("ccy");
//自己new出来的对象,没有重写equals方法,则会使用Object中的equals方法,比较的是地址值
//System.out.println(c.contains(ccy2));//false
//User类重写了equals方法,比较的是内容值
System.out.println(c.contains(ccy2));//true
//删除ccy2在集合中的元素
c.remove(ccy2);//使用remove方法,底层会调用equals方法,删除ccy2就相当于删除了ccy1
System.out.println(c.size());//0
}
}
class User{
private String name;
public User() { }
public User(String name) { this.name = name; }
//重写equals方法
//如果调用了当前类,也会调用equals方法,但是一定调用的是重写过的方法
//这个equals方法的比较原理:只要内容一样,则返回true
@Override
public boolean equals(Object o){
//判断集合中为空或者是传进来的o对象不是当前的类。则返回false
if(o == null || !(o instanceof User)) return false;
//判断传进来的o对象的地址值和当前类的地址值是否是同一个地址值,是则返回true
if(o == this) return true;
//将传进来的o对象强转为当前类
User u = (User)o;
//重写过的equals的方法,比较的是内容,而不是比较地址值了
return u.name == this.name;
}
}
关于remove方法
当集合的结构发生改变时,迭代器必须重新获取,如果不刷新则会出现异常:java.util.ConcurrentModificationException
在迭代集合元素的过程中,不能调用集合对象的remove方法删除元素(c.remove()),因为使用了会报错:java.util.ConcurrentModificationException
在迭代的过程中,一定要使用迭代器Iterator的remove方法删除元素,不要使用集合自带的remove方法删除元素,会报错:java.util.ConcurrentModificationException
3.List接口
3.1 和list接口特有的方法
void add(int index, E element) 将指定元素插入此列表中的指定位置(可选 *** 作)。
E get(int index) 返回此列表中指定位置的元素,也可是遍历是的一种方式。
int indexOf(Object o) 返回此列表中第一次出现的指定元素的索引,如果此列表不包含该元素,则返回-1。
int lastIndexOf(Object o) 返回此列表中指定元素最后一次出现的索引,如果此列表不包含该元素,则返回-1。
E remove(int index) 删除此列表中指定位置的元素(可选 *** 作)。
E set(int index, E element) 用指定的元素替换此列表中指定位置的元素(可选 *** 作)。
3.2 ArrayList源码分析
默认初始化容量10(底层先创建一个长度为0的数组,当添加一个元素的时候,初始化才会变为10)
集合底层是一个Object[]数组
构造方法
new ArrayList(); 默认容量是10new ArrayList(20);new ArrayList(); 参数可以是一个集合
//默认容量是10
List list1 = new ArrayList();
//容量初始化20
List list2 = new ArrayList(20);
//构造方法的参数可以是一个集合
Collection ha = new HashSet();
ha.add("nihao");
List list3 = new ArrayList(ha);
for (int i = 0; i < ha.size(); i++) {
System.out.println(list.get(i));//nihao
}
ArrayList集合的扩容
增长到原容量的1.5倍
ArrayList集合底层是数组,怎么优化?
尽可能减少扩容,因为数组扩容了效率比较低,建议在使用ArrayList集合的时候预估计元素的个数,给 定一个初始化容量
数组的优点:检索效率比较高(每个元素占位空间大小相同,内存地址是连续的,知道首元素内存地址,然后知道下标,通过数学表达式计算元素的内存地址,所有检索效率高)
数组的缺点:
随机增删元素效率低数组无法存储大数据容量,很难找到一块巨大的连续的内存空间
向数组末尾添加元素,效率效率还是挺高的
3.3 linkedList集合
单向链表结构图
单向链表的基本单位是节点,节点有两个属:数据、下一个节点对象的内存地址
链表的优点:
由于链表上的元素的内存地址是不连续的,所以随机增删的时候不会有大量高的元素位移,因此增删效率较高,以后在开发中,如果遇到增删集合中的元素,就可以使用linkedList集合。
链表的缺点:
不能通过数学表达式计算出被查找元素的内存地址,每一次查找都是从头节点一个一个的遍历,直到找到为止,所有linkedList集合的检索/查找的效率不高。
注意:
ArrayList集合的检索效率高,不是因为他有下标,而是他底层是一个数组
linkedList集合照样有下标,但是他底层是双向链表,只能从头节点一个一个遍历
双向链表结构图
linkedList集合内存结构图(通过源码分析而来)
、
注意:
//linkedList集合有初始化容量吗?没有
//最初这个链表中没有任何元素,first和last引用都是null
//不管是ArrayList集合还是linkedList集合,以后写代码时不需要关心具体是哪个集合的,因为我们面向的是接口编程,调用的都是接口中的方法
List list = new ArrayList();//这样写表示底层是数组
List link = new linkedList();//这样写表示底层是双向链表
3.4 Vector
Vector底层是一个数组初始化容量是10扩展之后是原来的2倍 10—>20---->40…Vector是线程安全的,用的比较少java.utils.Collections是一个工具类,可以将非线程安全的转换成线程安全的,Collections.synchronizedList(传非线程的集合对象);
3.5 迭代的几种方式
package com.ccy.java.List.Collection;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class Generic03 {
public static void main(String[] args) {
List list = new ArrayList<>();
list.add("nihao");
list.add("hello");
list.add("123");
//迭代器
Iterator i = list.iterator();
while (i.hasNext()){
String next = i.next();
System.out.println(next);
}
//for循环
for (int j = 0; j < list.size(); j++) {
String s = list.get(j);//get(index); 是List接口中的特有的方法
System.out.println(s);
}
//foreach
//foreach的缺点:就是没有下标,全部都遍历出来
for (String s : list) {
System.out.println(s);
}
}
}
4.Set接口
5.Map接口
6.Collection工具类
7.泛型(Generic)
7.1 初步了解泛型
JDK5.0之后推出的新特性:泛型泛型这种语法机制,只在编译阶段起作用,只是给编译器参考的。(运行阶段时没用)使用泛型有什么好处?
集合中存储的元素同一了。(被约束了)从集合中取出的数据类型是泛型指定的类型,不需要大量的向下转型 使用泛型的缺点?
导致集合中存储的元素没有多样化
package com.ccy.java.List.Collection;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class Generic01 {
public static void main(String[] args) {
//创建ArrayList集合对象
List list = new ArrayList();
//实例化对象
Cat cat = new Cat();
Bird bird = new Bird();
//向集合中添加数据
list.add(cat);
list.add(bird);
//迭代数据
//使用了泛型,只能存储指定的泛型类型的数据,存储其他的数据类型的就会报错
Iterator i = list.iterator();
while (i.hasNext()){
Animal next = i.next();
next.move();
//判断是否属于Cat类型的,然后进行强转
if(next instanceof Cat){
((Cat) next).run();
}
//判断是否属于Bird类型的,然后进行强转
if(next instanceof Bird){
((Bird) next).fly();
}
}
}
}
class Animal{
public void move(){
System.out.println("动物在移动!");
}
}
class Cat extends Animal{
public void run(){
System.out.println("猫在运动!");
}
}
class Bird extends Animal{
public void fly(){
System.out.println("鸟儿在飞!");
}
}
显示结果
动物在移动!
猫在运动!
动物在移动!
鸟儿在飞!
7.2 砖石表达式
JDK之后引入了:自动类型推动机制(又称为砖石表达式)
package com.ccy.java.List.Collection;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class Generic02 {
public static void main(String[] args) {
//ArrayList<>(这里的类型会自动推断);后面的泛型可以不用写,不会报错,前提是JDK8.0
List list = new ArrayList<>();
//只能添加String类型的数据
list.add("https://www.baidu.com");
list.add("https://www.bilibili.com");
list.add("https://www.132.com");
//迭代器使用了泛型,只能调用String类型的中的方法,调用其他类型中的方法会报错
Iterator i = list.iterator();
while (i.hasNext()){
String s = i.next();
//截取字符串
String substring = s.substring(8);
System.out.println(substring);
}
}
}
显示结果
www.baidu.com
www.bilibili.com
www.132.com
7.3 自定义泛型
package com.ccy.java.List.Collection;
public class Generic03 {
public void go(E o){
System.out.println(o);
}
public static void main(String[] args) {
//new对象的时候泛型指定的是String类型
Generic03 g = new Generic03<>();
g.go("hello");//hello
//类型不匹配,需要的类型是字符串不是数字
//g.go(100);
}
}
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)