集合:java中提供的一种容器,可以用来存储数据.
与数组的异同:
1.长度:
数组长度固定.
集合长度可变(集合底层还是数组)
2.存储:
数组中存储
同一种类型的元素
.
集合存储的都是对象,而且
对象的类型可以不一样
.
(当对象多的时候,使用集合来存储)
(一般都是使用<T>范型指定相同类型的数据存储在一起,如果不指定范型,在取出数据时需要向下转型)
*学习类的时候,从父类开始,学习共有的特性.
集合框架:
按存储结构来分:
1.单列集合 java.util.Collection
继承结构:
2.双列集合 java.util.Map
继承结构:
ArrayList集合
对象数组
数组内放入对象(保存的是地址)
ArrayList
1.可调整大小的数组集合,
底层是数组
2.可以不断在其末端添加数据
3.只能存储引用数据类型(存储基本数据类型的包装类可以)
创建ArrayList:
ArrayList<String> list = new ArrayList<>();
ArrayList<E>:
<E>:表示范型,你可以填入你需要的存储的
引用数据类型(不包括8大基本类型);如果不填写,存储的类型可以是任何类型,但是在取出数据的时候要转换成你存储的数据类型,否则取出数据会报错.
*当你要存储基本类型时,需要把E转换为他的包装类.(如: int ->Integer)
ArrayList<Integer> list = new ArrayList<>();
//在JDK1.5的时候有了自动拆装箱的新特性,可以使集合的存取更加方便.
创建一个ArrayList: 创建时如果不new一个空间,会报空指针异常的错误(NullpointerException)
ArrayList<String> list = new ArrayList<>();//jdk1.7后,右边的<>里面不用添加数据类型
//添加数据
list.add("你的数据");
System.out.ptintln(list);//直接打印所有的元素
对数据的操作:
List接口的方法:(实现List接口的类都可以使用,包括ArrayList和LinkedList.)
Collection接口中的方法:(List和Set集合都能使用的方法)
LinkedList集合
LinkedList是一个双向链表,他的底层是链表.
链表的特点: 增删快, 查找慢
ArrayList的特点: 增删慢, 查找快
(我们可以在合适的场景选择自己需要的数据类型)
原因:
1.因为ArrayList的底层是数组,其实现的基本思想是首先创建一个10长度的数组,把我们需要存储的数据存储进去.
如果满了,则扩容继续存储,存储完了后,通过System.copyarray()方法把这个数组里面的数据存储到一个新的数组中去(就是我们最终得到的ArrayList数组).
由于每次都要创建数组,所以ArrayList的增删比较慢,但是底层是数组,只要知道其索引就可以轻松找到数据,所以查找快.
2.LinkedList的数据结构:
他是由2部分组成的, 一个是数据域,一个是指针域.
数据域里面存储数据,指针域里面存储指针. 多个数据就是通过指针连接起来,所以修改其中的一个数据,只要把指针的指向改变就可以完成了,所以增删快;而查找数据则需要通过指针一个一个查找数据,所以查找慢.
Set集合
常用子类:HashSet\LinkedHashSet
与Collection接口中的方法基本一致,没有对功能进行扩展,只是接收数据更加严格,数据无序并按哈希表排序,存入的元素不会重复.
作用:保证存储的数据的值相同只有一个.(去除相同值的数据)
HashSet集合(哈希表)
学习Set集合前,必须明白什么是哈希表:
*JDK1.8之前,
数组+链表
*JDK1.8之后,数组+链表+红黑树(优化迭代性能)
*当链条超过8个元素的时候,转换为红黑树.
*数组长度是16(扩扩容)
对象的哈希值:
*是一个10进制的整数
*通过调用hashCode()方法获得,如果子类没有重写,返回默认是地址值
*哈希值是对象存储到哈希表的重要依据.
哈希表存储元素过程:
1.获得元素的哈希值
2.使用哈希值对16进行取余(hashcode % 16),得到值index
3.把元素存入index位置.
4.如果该位置已经有元素,先比较2个元素的哈希值
5.如果哈希值不同,把新的元素存入index位置,新的元素记住旧元素的地址.
6.如果哈希值相同,继续比较地址值,如果地址值相同,不加入;否则加入新的元素.
字符串只要内容相同,哈希值一定相同.
字符串内容不一样,哈希值不一定不同.
自定义对象存入HashSet
*需要重写hashCode和equals方法.
class Student{
private String name;
private int age ;
//通过重写Object类的equals和hashCode方法,编译器可以自动生成
//快捷键 Alt + ins
@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);
}
}
哈希表自动扩容机制:
*实例的默认初始容量为16,加载因子为0.75(可修改)[当数据达到16*0.75=12个的时候扩容]
*扩容机制:每次扩容为2倍,当数组 >= 长度*加载因子 ,才扩容
*扩容后,会进行再哈希操作(哈希值%数组长度),重新排列数据位置.
LinkedHashSet集合
特点:
*继承HashSet,基本上与HashSet一样,只是在取出数据的时候是你存入数据的顺序.
*底层结构是哈希表+链表
集合遍历的方法:
迭代 == 遍历
为了遍历,JDK专门提供了java.util.Iterator接口.
迭代器:一个用来遍历集合的对象,该对象实现了Iterator接口
(实现了Iterator接口的对象都是迭代器)
获得迭代器:
通过调用集合对象的方法: Iterator<E> iterator()
常有方法:
boolean hasNext();判断是否有下一个元素
E next();获取下一个元素.使用指针.
//遍历
ArrayList<String> list = new ArrayList<>();
list.add("111");
list.add("111");
list.add("111");
//使用迭代器
Iterator<String> it = list.iterator();
//使用while进行循环
while(it.hasNext()){
System.out.println(it.next());
}
迭代器的优点:
1.屏蔽了集合内部实现,对外提供统一的遍历方式.
2.
单列集合都可以使用迭代器
增强for循环
也称为for each循环,
JDK1.5后出现
作用:专门用来遍历数组和集合
原理:内部为迭代器
缺点:遍历过程中,不能对集合中的元素进行增删改查.
Map集合
map接口是Collection接口下的集合
存储方式: 由
键和
值组成
*键是唯一的,值可以重复.
Map常用子类
-
HashMap:存储数据采用哈希表结构与HashSet类似.需要保证 键是唯一的, 值可以重复
-
LinkedHsahMap:与LindedHashSet类似,继承HashMap,能够按顺序迭代数据.
*HashMap的增\改方法都是put(K k,V v); 如果key存在于map中,则修改V的值,并返回原来V的值.
Map集合迭代的方法:
-
获取Map中所有的键Key,存储在Set集合中,再通过遍历Set集合把Key对应的Value取出来.
HashMap<String ,String> hashMap = new HashMap<>();
//添加数据到集合中,使用put(K k, V v); 而不是用add()方法
hashMap.put(key,value);
.
.
.
//使用getKey()把所有的key存储到集合中(返回值是Set<E>)
HashSet<String> hashSet = hashMap.keySet();
//遍历hashSet,当然你也可以使用迭代器
for(String key : hashSet){
//通过key获取 hashMap中的对应的value值
String value = hashMap.get(key);
}
-
Entry把键值对封装成对象.Entry是map里面的一个接口类,我们可以使用它对Map的键值对进行封装.数据结构(哈希值,key,value,指针)
//使用Entry进行迭代
HashMap<String,String > hashMap = new HashMap<>();
hashMap.put("hello","world");
hashMap.put("hello1","world");
hashMap.put("hello2","world");
hashMap.put("hello3","world");
//使用的是Set<E>进行接收,不能使用其子类进行接收.
Set<Map.Entry<String,String>> entry = hashMap.entrySet();
for (Map.Entry<String, String> stringStringEntry : entry) {
System.out.println(stringStringEntry.getKey()+"="+stringStringEntry.getValue());
}
JDK9.0对集合的优化:
原本我们对集合进行添加元素的时候需要使用add()方法,当我们添加的多个数据时就会使代码重复多变.
所以jdk9添加了几种集合工厂方法.更方便创建少量的集合(List\Set\Map)
Set<String> str1 = Set.of("a","b","c");
//str1.add("c");这里编译的时候不会错,但是执行的时候会报错,因为是不可变的集合
System.out.println(str1);
Map<String,Integer> str2 = Map.of("a",1,"b",2);
System.out.println(str2);
List<String> str3 = List.of("a","b");
System.out.println(str3);
1:of()方法只是Map,List,Set这三个接口的静态方法,其父类接口和子类实现并没有这类方法,比如
HashSet,ArrayList等等;
2:返回的集合是
不可变的;