JAVA集合学习
一、集合的概念:
集合类是Java数据结构的实现。Java的集合类是java.util包中的重要内容,它允许以各种方式将元素分组,并定义了各种使这些元素更容易操作的方法。Java集合类是Java将一些基本的和使用频率极高的基础类进行封装和增强后再以一个类的形式提供。集合类是可以往里面保存多个对象的类,存放的是对象,不同的集合类有不同的功能和特点,适合不同的场合,用以解决一些实际问题。
二、集合和数组的区别
1、数组的长度是固定的,而集合的长度是可变的。
2、集合中只能存放引用类型,而数组中既能够存放引用类型,也能存放基本类型。
3、集合能够存储不同类型的元素,而数组只能存储一种类型的元素。
三、集合的框架图
蓝色的为实现类,黄色、红色的为接口类。
四、Collection接口
实现类及子类接口
集合中含有两个接口
Collection接口
1.Collection接口又被List和Set接口继承
2.List接口被ArrayList(底层是数组,查询快,增删慢)、LinkedList(底层是双向链表,查询慢,增删快)、Vector(底层也是数组)实现类实现
3.Set被HashSet实现类实现,被SortedSet接口继承,TreeSet实现SortedSet接口,LinkedHashSet类继承HashSet类
常用方法
添加 add()
Collection collection = new ArrayList();
//add 添加元素
collection.add(111);
collection.add(123);
collection.add(new String("abc"));
toArray() 将集合转换为数组
bject[] objects = collection.toArray();
remove 移除元素
boolean remove = collection.remove("abc");//返回一个boolean类型的值,判断是否成功
Object[] objects1 = collection.toArray();
System.out.println(remove);
for (int i = 0; i < objects1.length; i++) {
System.out.println("移除元素后 " + objects1[i]);
}
五、List接口
实现接口
List集合类中元素有序,且可重复,集合中每个元素都有对应的顺序索引–> ‘动态’数组 |-----List接口
- |—ArrayList:作为List接口的主要实现类;线程不安全的,效率高,底层是Object[] elementData的数组
- |—LinkedList:底层使用双向链表存储,对于频繁的插入,删除操作,效率比ArrayList高
- |—Vector:作为List接口的古老实现类,线程是安全的,效率低,底层也是Object[] elementData的数组
List接口存储的特点:存储有序,元素可重复
ArrayList 类
在jdk7中,当new ArrayList之后,底层会在内存中创建一个Object类型的数组,并且默认长度为10
而在jdk8中,new ArrayList时不会创建数组,而是在使用add方法添加元素时,创建一个默认长度为10的Object数组
当数据存储长度超过默认长度时,底层会自动给数组长度进行阔肉,默认扩容为原来的1.5倍。
常用方法
增:add(object obj):
ArrayList arr = new ArrayList();
arr.add(123);
arr.add(456);
arr.add("Tom");
arr.add(new Person("张",21));
Object remove(int index):移除指定index位置的元素,并返回此元素
Object remove = arr.remove(2);
这里有个要注意的点
现我们有这么一行代码
public static void main(String args[]){
ArrayList list = new ArrayList();
list.add(1);
list.add(3);
list.add(2);
removeNum(list);
System.out.println(list);//这里的输出结果时{1,2};
//被删除的是3
//如果删除2的话 代码应为
list.remove(new Intager(2));
public void removeNum(ArrayList list){
list.remove(2);
}
}
改:Object set(int index,object ele): 设置指定index位置的元素为
//Object set(int index,object ele): 设置指定index位置的元素为
arr.set(0,"BB");
System.out.println(arr);
查:Object get(int index): 获取指定index位置的元素
//get
Object o = arr.get(1);
System.out.println(o);
插入:void add(int index,Object ele):在index位置插入ele元素
arr.add(2,"BB");
System.out.println(arr);
int indexOf(object obj): 返回obj在集合中首次出现的位置
int i = arr.indexOf(123);
System.out.println(i); //如果不存在,返回-1
int lastIndexOf(Object obj):返回obj在当前集合中末次出现的位置
int i = arr.lastIndexOf(456);
System.out.println(i);//如果不存在返回-1
LinkedList 类
在底层会生成一个Node类型的first和last属性,分别来指向前一位和后一位,从而形成链表。
常用方法和ArrayList一致
六、Set接口
实现接口及实现类
Set接口:存储无序的,且不能重复 -->高中所说的’ 集合 ';
- |------HashSet:作为Set的主要实现类;线程不安全;可以存储null值
- |------LinkedHashSet:作为HashSet的子类,遍历时,可以按照添加数据的顺序进行遍历
- |------TreeSet:可以按照添加对象的指定属性,进行排序。
说明Set接口的无序性,区别于随机性
以HashSet为例:
首先不等同于随机性。相较于List的存储顺序,HashSet的存储顺序不是按数组索引顺序进行排序,而是根据数据的Hash值来进行排序
添加元素,以HashSet为例:
添加元素的过程: 以Hashset 为例:
我们向HashSet中添加元素a,首先调用元素a所在类的hashCode()方法,计算元素a的哈希值,此哈希值接着通过某种算法计算出在HashSet底层数组中的存放位置(即为: 索引位置),
判断数组此位置上是否已经有元素:
如果此位置上没有其他元素,则元素a添加成功。 —>情况1
如果此位置上有其他元素b(或以链表形式存在的多个元素),
则比较元素a与元素b的hash值!如果hash值不相同,则元素a添加成功。—>情况2
如果hash值相同,进而需要调用元素a所在类的equals()方法
equals()返回true,元素a添加失败
equals()返回false,则元素a添加成功。—>情况3
对于添加成功的情况2和情况3而言: 元素与已经存在指定索引位置上数据以链表的方式存储:
idk 7 :元素a放到数组中,指向原来的元素。
idk 8 : 原来的元素在数组中,指向元素a
图解:
**注意:
1、添加元素时,要求元素所在类一定要重写equals()和hashCode()方法。
2、重写的hashCode()和equals()尽可能保持一致性:相同的对象必须具有相同的散列码
常用方法:
Set中没有额外定义新的方法,使用的都是Collection中声明过的方法。
有
增:add;删:remove;改:set; 查:get;
七、Map接口
Map接口的继承接口及实现类
|----Map:双列接口,存储是一个key-value数据 ----类似于高中的y=f(x)
- |----HashMap:作为Map接口的主要实现类,线程不安全,效率高,键值对可以使用null进行存储
- |----LinkedHashMap:作为HashMap的子类,存有key-value的同时,同时存在一对指针,指向上一个元素和下一个元素
对于频繁的遍历,效率高于HashMap - |----TreeMap:保证按照添加的key-value对进行排序,实现排序遍历。此时考虑的是key的自然排序或定制排序
底层使用红黑树 - |----Hashtable:作为Map接口的古老实现类,线程安全,效率低,键值对不能使用null进行存储
|----properties:作为Hashtable的子类,常用来处理配置文件,其key-value对是String类型的
Map结构的理解
- Map的key:是无序的,不可重复的,使用Set存储 —>以HashMap为例,key所在类必须要重写equals()和hashCode()方法
- Map中的value:是无序的,可重复的,使用Collection存储 —>value所在的类要重写equals()
- Map中的键值对:key-value其实是一个Entry对象
- Map中的entry:是无序的,不可重复的,使用Set存储
图解:
Map中常用方法:(以HashMap为例)
1、添加、删除、修改操作
object put(Object key,Object value): 将指定key-value添加到(或修改)当前map对象中
HashMap<Object, Object> map = new HashMap<>();
//添加
map.put("AA",123);
map.put(45,123);
map.put("BB",123);
//修改
map.put("AA",66);
Object remove(object key): 移除指定key的key-value对,并返回value
//删除
Object aa = map.remove("AA");
System.out.println(aa);
void cLear(): 清空当前map 中的所有数据
map.clear();
//这时是将map对象中的数组table中的元素清除,而不是将map置为null
System.out.println(map);。//这里输出的值为{}
2、元素查询操作
查询:Object get(Object key): 获取指定key对应的vaLue
Object value = map.get("AA");
System.out.println(value);
boolean containsKey(object key): 是否包含指定的key
boolean containsValue(Object value): 是否包合指定的value
Boolean a = map.containsKey("AA");
System.out.println(b);
Boolean b = map.containsValue("123");
System.out.println(b);
3、元视图操作方法:
Set keySet():返回所有key构成的Set集合
Set<Object> set = map.keySet();
Iterator<Object> iterator = set.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
Collection values(): 返回所有vaLue构成的Collection集合
// Collection values(): 返回所有vaLue构成的Collection集合
//遍历的顺序跟key的顺序一致
Collection<Object> col = map.values();
Iterator<Object> iterator1= col.iterator();
while (iterator1.hasNext()){
System.out.println(iterator1.next());
}
Set entrySet(): 返回所有key-value对构成的Set集合
Set<Map.Entry<Object, Object>> entries = map.entrySet();
Iterator<Map.Entry<Object, Object>> iterator2 = entries.iterator();
while (iterator2.hasNext()){
System.out.println(iterator2.next());
}
HashMap类
底层分析:
HashMap的底层原理 ?以jdk 7为例
HashMap map = new HashMap()
在实例化之后,在底层创建了一个默认长度为16的数组Entry[] table
。。。可能已经执行过多次put。。。
map.put(key1)的过程:
首先,调用key1所在类的hashCode计算key1的哈希值,此哈希值根据某种算法过后,在Entry数组中得到一个存放的位置。
如果此位置为空,那么直接添加
如果此位置有数据(一个或者多个数据(以链表的形式存在)),比较key1和已经存在的一个或多个数据的哈希值
如果key1的哈希值跟已存在的数据的哈希值都不用,直接添加啊
如果key1的哈希值跟已存在的数据(key2-value2)的哈希值相同,继续比较:调用key1所在类的equals(key2)
如果结果返回为false:此时key1-value1添加成功。
如果返回值为ture,此时将key1的value1替换value2. (区别于HashSet)
在不断添加的过程中,会涉及到扩容问题,当超出临界值(且存放位置为非空)时进行扩容,扩容为原来的2倍,并将原有的数据复制过来
idk8 相较于idk7在底层实现方面的不同:
1. new HashMap(): 层没有创建一个长度为16的数组idk 8底层的数组是: Node[],而Entry[]
2. 首次调用put()方法时,底层创建长度为16的数组
3.idk7底层结构只有:数组+链表。idk8中底层结构: 数组+链表+红黑树。
4. 当数组的某一个索引位置上的元素以链表形式存在的数个数 >8 且当前数组的长度 >64时此时此索引位置上的所有数据改为使用红黑树存储。
jdk8中HashMap源码的常量:
DEFAULT INITIAL CAPACITY : HashMap的默认容量,16
DEFAULT LOAD FACTOR: HashMap的默认加载因子:0.75
threshold: 容的临界值,=容量*填充因子:16 * 75 => 12
TREEIFY THRESHOLD: Bucket中链表长度大于该默认值,转化为红黑树:8
MIN TREEIFY CAPACITY: 桶的Node被树化时最小的hash表容量:64
TreeMap类
1、要求:
- 向TreeMap中添加key-value,要求key必须是由同一类创建的对象
- 因为要按key排序:自然排序和定制排序
2、排序
①、自然排序
User u1 = new User("AA", 12);
User u2 = new User("EE", 22);
User u3 = new User("CC", 33);
User u4 = new User("DD", 66);
TreeMap<Object, Object> map = new TreeMap<>();
map.put(u1, 10);
map.put(u2, 22);
map.put(u3, 33);
map.put(u4, 40);
//自然排序
Set set = map.entrySet();
Iterator iterator = set.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
//在User类继承了Comparable接口,并重写了Comparato()方法以及hashCode()和equals()方法
②、定制排序
创建Comparator对象,并重写compara方法
然后创建TreeMap对象,将创建的Comparator的引用对象作为参数丢入TreeMap对象中。
//定制排序
Comparator com = new Comparator() {
@Override
public int compare(Object o1, Object o2) {
if (o1 instanceof User && o2 instanceof User) {
User u1 = (User) o1;
User u2 = (User) o2;
return Integer.compare(u1.getAge(),u2.getAge());
} else {
throw new RuntimeException("输入的参数有误");
}
}
};
TreeMap treeMap = new TreeMap<>(com);
treeMap.put(u1, 10);
treeMap.put(u2, 22);
treeMap.put(u3, 33);
treeMap.put(u4, 40);
Set set1 = treeMap.entrySet();
Iterator iterator1 = set1.iterator();
while (iterator1.hasNext()) {
System.out.println(iterator1.next());
}
八、一个可以操作集合的常用类Collections类
常用方法
都是static方法:
reverse(List): 反转 List 中元素的顺序
shuffle(List): List 集合元素进行随机排序
sort(List): 根据元素的自然顺序对指定 List 集合元素按升序排序
sort(List,Comparator): 根据指定的 Comparator 产生的顺序对 List 集合元素进行排序
swap(List,int, int): 将指定list 集合中的第 i 处和第 j 处进行交换
object max(Collection): 根据元素的自然顺序,返回给定集合中的最大元素
object max(Collection,Comparator): 根 Comparator 指定的顺序,返回给定集合中的最大元素
Object min(Collection)
Object min(Collection, Comparator)
int frequency(Collection,object): 返回指定集合中指定元素的出现次数
void copy(List dest,List src): 将src中的内容复制到dest中
boolean replaceAll(List list,Object oldVal, Object newVal): 使用新值换 List对象中的所有旧值void copy(List dest,List src): 将src中的内容复制到dest中
//copy操作
ArrayList<Object> list = new ArrayList<>();
list.add(123);
list.add(34);
list.add(-23);
list.add(0);
list.add(44);
System.out.println(list);
// ArrayList<Object> dest = new ArrayList<>();
// //这样会报错
// Collections.copy(dest,list);
//正确使用copy的方法
List<Object> dest = Arrays.asList(new Object[list.size()]);
Collections.copy(dest,list);
System.out.println(dest);
reverse(List): 反转 List 中元素的顺序
ArrayList<Object> list = new ArrayList<>();
list.add(123);
list.add(34);
list.add(-23);
list.add(0);
list.add(44);
System.out.println(list);
Collections.reverse(list);
System.out.println(list);
输出结果:
swap(List,int i, int j): 将指定list 集合中的第 i 处和第 j 处进行交换
Collections.swap(list,1,2);
System.out.println(list);