文章目录
集合概念
数组 ——>容器 存储一组数据
数组就是容器:同一类型,创建时指定容量,长度不变,在内存空间连续存储
不足:长度固定,不能不变
需求:程序在运行时,数据数量随时会发生变化,需要的存储结构也会有特殊需求
(增删多链表结构,查询多数组结构)
集合API
集合体系概述
Java的集合框架是由很多接口、抽象类、具体类组成的,都位于java.util包中
Collection接口
接口中常用的方法(单列集合共有的方法),定义了存取一组对象的方法,其子接口Set和List分别定义了存储方式。
●Set中的数据对象没有顺序且不可以重复。
●List中的数据对象有顺序且可以重复。
Java中集合类默认使用泛型,如果没有定义集合中存储的数据类型,默认数据类型为Object。建议使用泛型语法为集合指明数据类型,类型统一就不会出现转型问题(把类型当作参数传递)
import java.util.ArrayList;
import java.util.Collection;
public class CollectDemo1 {
public static void main(String[] args) {
Collection<String> c = new ArrayList<String>();
c.add("a");//添加元素
c.add("b");
// c.contains("a"); 是否包含此元素,若包含此元素输出返回true,反之为false
System.out.println(c.contains("a"));
// c.isEmpty();是否为空
System.out.println(c.isEmpty());
// c.size();集合长度
System.out.println(c.size());
// c.remove("a");删除指定元素(返回为boolean类型,true或false)
System.out.println(c.remove("a"));
c.clear();//删除集合中所有元素
System.out.println(c);
}
}
集合转数组
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
public class CollectionDemo2 {
public static void main(String[] args) {
Collection<String> c1 = new ArrayList<String>();
c1.add("a");
c1.add("b");
Collection<String> c2 = new ArrayList<String>();
c2.add("c");
c2.add("d");
c1.addAll(c2);
System.out.println(c1);//把c2集合添加到c1集合
System.out.println(c1.containsAll(c2));//c1里面是否包含c2元素,包含返回true
System.out.println(c1.retainAll(c2));
System.out.println(c1.retainAll(c2));//保留两个集合中的交集部分,内容改变返回true,不变返回false
System.out.println(c1);
//集合转数组
Object[] o = c1.toArray();//默认Object
System.out.println(Arrays.toString(o));
String[] array = c1.toArray(new String[(c1.size())]);//明确类型
System.out.println(Arrays.toString(array));
}
}
List 接口及实现类
List接口继承自Collection接口,并且继承了Collection接口中的全部方法。
有三个实现类:
-
ArrayList
数组列表,数据采用数组方式存储
查询快;中间添加,删除 效率低
-
LinkedList
链表 查询慢从头或者尾结点开始查询;中间删除,添加效率高
-
Vector
数组列表,添加同步锁,线程安全的
ArrayList
实现了长度可变的数组,在内存中分配连续的空间
- ArrayList c = new ArrayList();
创建对象时不会创建数组,第一次添加时创建一个容量为10的数组 - ArrayList c = new ArrayList(10);
创建对象时,创建一个指定容量的数组 - add方法(底层源码实现方法)及接口方法 具体方法(个别):
import java.util.ArrayList;
public class ArrayListDemo1 {
public static void main(String[] args) {
/*
ArrayList<String> c = new ArrayList<String>();创建对象时不会创建数组,第一次添加时创建一个容量为10的数组
ArrayList<String> c = new ArrayList<String>(10);创建对象时,创建一个指定容量的数组
transient Object[] elementData; 底层存储数据的数组
add方法(底层源码实现方法)
add(E e) 向末尾添加元素进来;添加前判断元素添加进去后,数组能否放的下,如果可以,直接添加进去,如果不可以会创建一个数组(扩容)
public boolean add(E e) {
ensureCapacityInternal(size + 1); //检测容量
elementData[size++] = e;
return true;
}
if (minCapacity - elementData.length > 0) //添加后的长度-数组长度>0
grow(minCapacity);//底层数组扩容方法
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);//扩容为原来的1.5倍
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)//最大上限为int最大值-8( private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
*/
ArrayList<String> c = new ArrayList<String>();
c.add("e");
c.add("b");
c.add("c");
c.add("d");
c.add("a");
c.add("a");
c.add("a");
c.add("a");
c.add("a");
c.add("a");
c.add("a");
System.out.println(c.indexOf("a"));//返回此列表中指定元素的第一次出现的索引,如果此列表不包含元素,则返回-1
System.out.println(c);
System.out.println(c.get(3));
System.out.println(c.contains("r"));//如果此列表包含指定的元素,则返回 true
System.out.println(c.remove(1));//根据索引删除
System.out.println(c);
System.out.println(c.clone());//返回此 ArrayList实例的浅拷贝。
}
}
LinkedList的常用方法
import java.util.LinkedList;
public class LinkedListDemo {
public static void main(String[] args) {
/*
* LinkedList 存储重复元素,按照添加顺序排放
* E item; 数据
Node<E> next; 后
Node<E> prev; 前
* */
LinkedList<String> list = new LinkedList<>();
list.add("a");
list.add("b");
list.add("c");
list.add("d");
list.add("e");
list.addFirst("x");
list.addLast("x");
list.add(2, "q");//在索引为2的位置上添加q
list.removeFirst();//删除首位
list.removeLast();//删除末尾
list.getFirst();
list.getLast();
System.out.println(list.get(3));
System.out.println(list);
}
}
Vector
import java.util.Vector;
public class VectorDemo {
public static void main(String[] args) {
/*
Vector 底层是数组实现
public synchronized boolean add(E e) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = e;
return true;
}
*/
Vector<String> vector = new Vector<>();
vector.add("a");
vector.add("b");
}
}
List接口集合迭代
- for循环遍历
- 增强for循环的遍历
- 迭代器遍历(Iterator)
import java.util.ArrayList;
import java.util.Iterator;
public class ListDemo1 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<String>(10);
list.add("a");
list.add("b");
list.add("c");
list.add("d");
list.add("c");
list.add("c");
/*
for循环遍历:支持在遍历的过程中删除集合中的元素,注意索引的变化
*/
for (int i = 0; i < list.size(); i++) {
if (list.get(i).equals("c")){
list.remove("c");
}
System.out.println(list.get(i));
}
/*
增强for循环的遍历:不支持在遍历时删除元素,如果删除会抛出java.util.ConcurrentModificationException(并发修改异常)
list.remove(item); 删除元素时会报错,break结束循环,可以继续向后执行
break;
*/
for (String item : list){
if (item.equals("c")){
list.remove(item);
break;
}
System.out.println(item);
}
/*
迭代器遍历(Iterator)
调用Iterator(),返回一个迭代器对象
*/
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()){
String s = iterator.next();
if (s.equals("c")){
iterator.remove();//删除当前指针指向的那个元素
}
}
System.out.println(list);
}
}
Set接口
Set接口和List接口同样继承自Collection接口,与Collection接口中的方法基本一致;Set中所存储的元素是不重复的,但是是无序的, Set中的元素是没有索引的
Set接口主要有两个实现类:HashSet和TreeSet
HashSet集合
HashSet类中的元素不能重复,即彼此调用equals方法比较,都返回false,并且元素都是无序的
底层数据结构是哈希表+链表
哈希表依赖于哈希值存储
import java.util.HashSet;
import java.util.Iterator;
public class HashSetDemo1 {
public static void main(String[] args) {
HashSet<String> hashSet = new HashSet<>();
hashSet.add("a");
hashSet.add("b");
hashSet.add("c");
hashSet.add("d");
hashSet.add("a");
// hashSet.clear();//删除
System.out.println(hashSet.contains("a"));
/*
set集合只能用增强for循环和迭代器遍历
for (String s : hashSet){
System.out.println(s);
}
*/
Iterator<String> iterator = hashSet.iterator();
while (iterator.hasNext()){
String string = iterator.next();
System.out.println(iterator.next());
}
}
}
在向HashSet集合中添加元素时注意的问题(代码中体现):
import java.util.HashSet;
import java.util.Iterator;
public class HashSetDemo2 {
public static void main(String[] args) {
/*
HashSet添加元素时,如何判断元素是否存在(也就是判断HashSet的键是如何判断重复)
add(E) 在添加元素时,在里面要判断元素是否存在,解决两个问题:1.效率要高 2.安全
add添加时,先用内容调用hashCode()方法计算出一个hash值(int类型),用哈希值比较是否相同
但是只用hash值比较是不安全的,这时会调用equals方法对每个字符进行比较
*/
HashSet<String> hashSet = new HashSet<>();
hashSet.add("a");
hashSet.add("b");
hashSet.add("c");
hashSet.add("d");
hashSet.add("a");
System.out.println(hashSet);
}
}
用一个案列进行演示如何将Person存入HashSet:
new Person() 在内存中创建一个对象,有一个内存地址:
public native int hashCode();
若Person类中没有重写Object类中hashCode(), 则默认调用的是Object中的,取出的是内存地址 ;
一般的类中都会重写Object类中的hashCode(), 重写后的计算方式都是根据对象中包含的内容的值来计算的,重写后添加时,调用自己类中的hashCode();
因此在向集合中存入元素时,为了保证HashSet正常工作,要在存入对象时重写Object类中的hashCode()和equals()方法。
import java.util.Objects;
public class Person {
private int id;
private String name;
public Person(int id, String name) {
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
Person person = (Person) o;
return id == person.id && name.equals(person.name);
}
@Override
public int hashCode() {
return Objects.hash(id, name);
}
}
import java.util.HashSet;
public class HashSetDemo3 {
public static void main(String[] args) {
HashSet<Person> hashSet = new HashSet<Person>();
Person p1 = new Person(1,"张三1");
Person p2 = new Person(2,"张三2");
Person p3 = new Person(3,"张三3");
Person p4 = new Person(1,"张三1");
Person p5 = new Person(5,"张三5");
hashSet.add(p1);
hashSet.add(p2);
hashSet.add(p3);
hashSet.add(p4);
hashSet.add(p5);
System.out.println(hashSet);
}
}
TreeSet集合
内部采用平衡二叉树来存储元素,其结构可以保证TreeSet集合中没有重复的元素,且可以对元素进行排序。
import java.util.TreeSet;
public class TreeSetDemo1 {
public static void main(String[] args) {
/*
TreeSet:不重复,可以根据元素进行排序
*/
TreeSet<Integer> treeSet = new TreeSet<>();
treeSet.add(3);
treeSet.add(1);
treeSet.add(2);
treeSet.add(5);
treeSet.add(5);
System.out.println(treeSet);
当向TreeSet集合中存储我们自定义的类型数据时,如下面例子中的Car(),因为我们自己定义的类型数据没有实现Comparable接口,因此不能够直接在 TreeSet集合中进行排序操作。
public class Car implements Comparable<Car>{
private int num;
private String name;
public Car(int num, String name) {
this.num = num;
this.name = name;
}
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Car{" +
"num=" + num +
", name='" + name + '\'' +
'}';
}
@Override
public int compareTo(Car o) {
// return this.num - o.num;//使用num比较排序,
return this.name.compareTo(o.name);//使用name比较排序
}
}
import java.util.TreeSet;
public class TreeSetDemo {
public static void main(String[] args) {
TreeSet<Car> set = new TreeSet<>();
Car car1 = new Car(1,"红旗H1");
Car car2 = new Car(3,"红旗H3");
Car car3 = new Car(2,"红旗H2");
Car car4 = new Car(8,"红旗H8");
Car car5 = new Car(1,"红旗H9");
set.add(car1);
set.add(car2);
set.add(car3);
set.add(car4);
set.add(car5);
System.out.println(set);
}
}
Map接口
Map接口是一种双列集合,它的每一个元素都包含一个key(键对象)和Value(值对象)。Map中的映射关系是一对一的,即一个键对象(key)只能对应一个值对象(Value),键和值对象可以是任意数据类型。
Map接口中定义的一些方法:
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class MapDemo1 {
public static void main(String[] args) {
Map<String,String> map = new HashMap<String, String>();
map.put("a", "aa");
map.put("b", "bb");
map.put("c", "cc");
map.put("d", "dd");
map.put("s", "ss");
map.put("a", "aaa");
System.out.println(map.containsKey("a"));//是否包含指定的键值
System.out.println(map.containsValue("bb"));//是否包含指定的值
System.out.println(map.size());
System.out.println(map.get("cc"));//根据指定的键找到对应的值
System.out.println(map.remove("s"));//删除指定的键,返回对应的值
System.out.println(map.getOrDefault("a", "aa"));
System.out.println(map.entrySet());
System.out.println(map);
Set<String> set = map.keySet();//获取map中键的那一列,存放到一个set中
Collection<String> list = map.values();
System.out.println(list);
}
}
HashMap集合
HashMap集合是Map接口中的一个实现类,用于存储键值映射关系,在集合体系概述中的图解中有体现,HashMap中元素的key值不能重复,排列顺序是不固定的,可以存储一个为null的键;底层由哈希表结构组成,即“数组+链表”的组合体,数组为HashMap的主体结构,链表则是为解决哈希值冲突而存在的分支结构。
import java.util.HashMap;
public class HashMapDemo {
public static void main(String[] args) {
HashMap<String,String> map = new HashMap<String, String>();
map.put("a", "aa");
map.put("b", "bb");
map.put("c", "cc");
map.put("a", "dd");
System.out.println(map);//输出:{a=dd, b=bb, c=cc}
/*
Map集合中的键具有唯一性,在向集合中添加已存在的键值时,会覆盖之前已存在的键值
*/
}
}
HashMap集合的简单存储示意图:
TreeMap集合
TreeMap集合中不允许出现重复的键,内部是通过二叉树原理保证键的唯一性;
TreeMap中所有的元素按照某种顺序排列,如果需要得到一个有序 的Map就应该使用TreeMap,key值所在类必须实现Comparable接口。
import java.util.*;
public class TreeMapDemo {
public static void main(String[] args) {
TreeMap<String,String> map = new TreeMap<String, String>();
map.put("a", "aa");
map.put("c", "cc");
map.put("b", "bb");
System.out.println(map);//输出:{a=aa, b=bb, c=cc}
}
}
HashTable集合
HashTable是Map接口的一个实现类,不能存储为null的键,HashTable是线程安全的,效率不及HashMap。
import java.util.*;
public class HashTableDemo {
public static void main(String[] args) {
Hashtable<String,String> map = new Hashtable<String,String>();
map.put("a", "aa");
// map.put(null, "bb");//不允许为键为空
map.put("c", "cc");
map.put("d", "dd");
System.out.println(map);
}
}
Map集合遍历
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class MapDemo2 {
public static void main(String[] args) {
HashMap<String,String> map = new HashMap<String, String>();
map.put("a", "aa");
map.put("b", "bb");
map.put("c", "cc");
map.put("d", "dd");
map.put("s", "ss");
map.put("a", "aaa");
/*
遍历方式一:
*/
Set<String> keyset = map.keySet();
for (String key : keyset){
System.out.println(map.get(key));
}
/*
遍历方式二:
entrySet(); 把map中的键值对封装在一个Entry对象中,将多个Entry对象装进Set集合中.
Entry对象包含键值
*/
Set<Map.Entry<String,String>> entrySet = map.entrySet();
for (Map.Entry entry : entrySet){
System.out.println(entry.getKey()+":"+entry.getValue());
}
}
}
Collections类
Collections是集合类的工具类,与数组的工具类Arrays类似.如将集合中的元素排序,从集合中查找某个元素等。
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class CollectionsDemo {
/*
addAll(Collection<? super T> c, T... elements)
*/
public static void main(String[] args) {
/* Collections.sort(list,new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2-o1;
}
});*/
//System.out.println(Collections.binarySearch(list,4));
//Collections.swap(list, 0, 3);
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
List<Integer> list1 = new ArrayList<>();
list1.add(5);
list1.add(6);
list1.add(7);
Collections.copy(list, list1);//将list1复制到list中去,从头开始复制,
System.out.println(list);//输出为:[5, 6, 7, 4]
List<Integer> list2 = Collections.emptyList();//返回一个空集合,集合不能使用 ,避免在判断时候出现空指针
System.out.println(Collections.max(list));
System.out.println(Collections.min(list));
//Collections.replaceAll(list, 2,6);//将集合中2替换为6,有几个2替换几个6
/*
Collections.fill(list, 5);//将list集合填充为5
System.out.println(list);//输出:[5, 5, 5, 5]
*/
Collections.reverse(list);//集合逆序操作
System.out.println(list);
}
public static void test1(){
test2(1,2,3,4,5);
}
//int...a 定义可变长度参数,本质是数组,一个参数列表中只能有一个,并且 放在参数列表的最后一位
public static void test2(int b,int...a){
}
}
泛型
- 什么是泛型?为什么用泛型?
泛型即“参数化类型”。
参数化类型即指将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式,在调用或使用时传入具体的类型。
早期的Object类型可以接收任意的对象类型,但是在实际的使用中,会有类型转换的问题。也就存在这隐患,所以Java提供了泛型来解决这个安全问题。
- 泛型类
泛型有三种使用方式,分别为:泛型类、泛型接口、泛型方法。
泛型类型用于类的定义中,被称为泛型类。通过泛型可以完成对一组类的操作对外开放相同的接口。