Java基础--集合
集合
当需要将相同结构的个体整合到一起的时候,需要集合。
Collection接口
注意!!!集合存入的都是引用数据类型,不能存基本数据类型
常用方法:
增 add(e) addAll(collection c)
删 clear() remove(Object o )
查 size() ietrator()
判断:contains(Object o) equals(Object o) isEmpty()
//接口不能创建对象,用它的实现类创建对象
Collection c1 = new ArrayList();
//集合只能存放引用数据类型的数据,不能存基本数据类型
//基本数据类型自动装箱,转换成对应的包装类 int--->Integer
c1.add(18); //添加元素
c1.add(12);
c1.add(17);
System.out.println(c1.toString()); //输出[18, 12, 17]
//addAll(Collection c) 传入一个集合名字,将集合c添加到本集合中
List list = Arrays.asList(new Integer[]{23,25,29,36});
c1.addAll(list);
System.out.println(c1.toString()); //输出[18, 12, 17, 23, 25, 29, 36]
//equals比较的是两个集合中的值是否对应相等,==比较的是两个集合的地址
//contains判断集合是否包含某元素
System.out.println("是否包含9:"+c1.contains(9));
遍历方式:
方法一:增强for
for (Object o:c1) //(用什么接收:要遍历哪个)
{ System.out.println(o);
}
方法二:迭代器iterator
Iterator it = c1.iterator(); //把迭代器看作一个“指针”
while(it.hasNext()){ //通过hashNex()判断是否有下一个元素,返回true
System.out.println(it.next()); //next()将元素获取到,“指针”下移
}
List接口:不唯一,有序
常用方法:扩展的方法都和索引相关
//list接口常用方法-是collection接口的子接口,继承了collection,还有一些通过索引来操作的
//增 add(int index, E element)
//删 remove(int index) remove(Object o)
//改 set(int index,E element)
//查 get(int index)
List list = new ArrayList(); //接口不能创建实例,用实现类创建
list.add(13);
list.add(1);
list.add(3);
list.add(130);
list.add("zxy");
System.out.println(list);
list.add(3,33); //下标从0开始,在下标为3的位置添加33
list.set(3,77); //把下标为3的数改成77
list.remove(4); //把下标为4的删除
System.out.println(list); //若在集合中存的是Integer类型数据,调用哪个remove都可以
list.remove("zxy"); //若是其他类型,必须调用remove(Object o)才能删除
//通过索引获取元素
Object o = list.get(5); //集合里有多种类型,要用Object类型接收
System.out.println(o);
遍历方法:
//方式一:普通for循环
System.out.println("===========================");
for (int i = 0; i <list.size() ; i++) {
System.out.println(list.get(i)); //可以用下标获取元素
}
//方式二:增强for循环
System.out.println("===========================");
for (Object value : list) {
System.out.println(value);
}
//方式三:迭代器
Iterator it = list.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
ArrayList实现类
ArrayList的源码与StringBuider极为相似,都有两个重要的属性:ArrayList是Object[]型数组,int size指代数组中的有效长度
而StringBuider中是char[] value--底层的存储数组, int count--数组中的有效长度
JDK1.8源码:底层是数组,在调用构造器时,底层数组为{},在调用了add()方法后,底层数组才重新赋值为新数组,长度为10-->节省了内存
数据结构:
- 物理结构:紧密结构(顺序结构)
- 逻辑结构:线性表(数组)
vector实现类(已淘汰)
JDK1.8源码:底层Object类型数组,int类型属性表示有效长度
vector与ArrayList的联系与区别:
联系:底层都是数组的扩容
区别:1)ArrayList底层扩容长度为原数组的1.5倍,线程不安全,效率高
2)vector底层扩容长度为原数组的2倍,线程安全,效率低 (已淘汰 )
-
什么是泛型:相当于标签--实际就是一个<>引起来的参数类型,在使用时才会确定具体类型
-
没有泛型时:集合中任何的引用类型都能存,有多种类型的,不太方便。
-
加入泛型:在编译时期会对类型进行检查,不是对应的类型就不会加入这个集合。此时集合中是同一类型
-
使用了泛型,后序遍历操作简单;泛型一般都是用来修饰集合,所以泛型的类型都是引用数据类型,不能是基本数据类型
-
ArrayList<Integer> a = new ArrayList<>();
---泛型类
//test04就是一个普通类,而test04<E>就是个泛型类
//<>里面是一个参数类型,但这个类型现在不确定,相当于一个占位符,
// 唯一确定的是他一定是一个引用数据类型
public class test04 <E>{
int age;
String name;
E sex;
public void a(E n){ }
public void b(E[] m){ }}
public static void main(String[] args) {
//实例化泛型类
//实例化时不指定泛型,默认此泛型为Object类型
test04 t = new test04();
t.a("zxy");
t.a(2);
t.b(new String[]{"a","b"});
//实例化时指定泛型!!!
test04<String> t1 = new test04<>(); //确定了泛型为String类型,故上面的E对应String
t1.sex = "男";
t1.a("abc");
继承情况:
1)父类指定泛型,子类就不必指定
class subtest04 extends test04<Integer>{
}
2)父类不指定泛型,子类也会变成一个泛型类,E的类型可以在创建子类对象时确定
class subtest004<E> extends test04<E>{
}
细节:
1)泛型类可以定义多个参数
2)泛型类对应的构造器--不可以加泛型<>
3)不同的泛型的引用类型不可以相互赋值
public void c(){
ArrayList<String> list1 = null;
ArrayList<Integer> list1 = null;
list1 = list2; //错误代码,不可以相互赋值
}
4)泛型如果不指定,用的时候就会默认为Object类型
5)泛型类中的静态方法不能使用类的泛型
public static void b(E[] m){ } //报错,静态方法在最开始就会被加载,而泛型在实例化后才能确定
6)不能直接使用E[]创建:e[] i = new e[10]; //不可以
---泛型接口:类似于泛型类
---泛型方法:这个方法的泛型参数类型 与 当前所在类 是否是泛型类,泛型是啥 无关!
定义时要加上
T 的类型是在调用的时候才确定的(根据传入的参数类型),所以泛型方法可以是静态方法
public class test05 <E> {
public void a(E e){ } //不是泛型方法
public <T> void b(T t){ } //是泛型方法
public static <T> void b(T t){ } //是泛型方法
}
---泛型参数存在继承关系
Object obj = new Object();
String s = new String();
obj = s; //多态的一种形式---父类的引用指向子类
Object[] objArr = new Object[];
String[] strArr = new String[];
objArr = strArr; //多态的一种形式
List<Object> list1 = new ArrayList<>();
List<String> list2 = new ArrayList<>();
list1 = list2; //报错--ArrayList底层都是Object数组,利用泛型给他加限制,只是在编译时限制,
// 所以list1和list2是并列关系
总结:A和B是子类父类关系,但G和G不存在继承关系,是并列的
---通配符 <?>
-
在没有通配符时,下面相当于方法的重复定义,报错
public void a(List<Object> list){ } public void a(List<String> list){ } public void a(List<Integer> list){ }
-
public void a(List<?> list){ for (Object o:list) { //遍历时用Object接收 System.out.println(o); } }
//引入通配符后,传什么类型都可以 public static void main(String[] args) { test07 t = new test07(); t.a(new ArrayList<Integer>()); t.a(new ArrayList<String>()); t.a(new ArrayList<Object>());
//数据的写入操作
//list.add("abc"); 不能随意添加数据,只能填null
list.add(null);
//数据的读取操作--用Object类型接收
Object s = list.get(0);
泛型受限:List<? extends Person>
//使用泛型受限--上限,也就是说Person是天花板
List<? extends Person> list1 = null;
// 报错 list1 = a;
list1 = b; //只能是Person类或者他的子类
list1 = c;
//使用泛型受限--下限,也就是说Person是地板
List<? super Person> list2 = null;
list2 = a;
list2 = b; //只能是Person类或者他的父类
// 报错 list1 = c;
LinkList实现类
常用方法:
LinkList常用方法:
增 addFirst(E e) addLast(E e)
offer(E e) offerFirst(E e) offerLast(E e)
删 poll() pollFirst() pollLast() //poll是JDK1.6之后的,代码健壮性好
removeFirst() removeLast()
查 element() getFirst() getLast()
indexOf(Object o) lastIndexOf(Object o)
peek() peekFirst() peekLast()
//创建一个对象
LinkedList<String> list = new LinkedList<>();
list.add("zzzzzz");
list.add("xxxxxxx");
list.add("yyyyyyy");
list.add("zzzzzz"); //可以添加重复数据
list.addFirst("hh"); //向头、尾添加
list.addLast("gg");
list.offer("aa"); //添加到尾部
list.offerFirst("bb"); //头部
list.offerLast("cc"); //尾部
list.poll(); //删除头部元素 pollFirst()
list.pollLast(); //删除尾部元素
list.clear(); //清空
System.out.println(list);
System.out.println(list.pollFirst()); //集合为空,返回null
System.out.println(list.removeFirst()); //若集合为空,报错了
遍历方法:
实现List接口,和他一样有三种遍历方式。。。
迭代器遍历有另一种写法:更节省内存
Iterator it = list.iterator();
while(it.hasNext()){
System.out.println(it.next());}
for(Iterator<String> it = list.iterator(); it.hasNext();){
System.out.println(it.next());
}
底层原理:
数据结构:
- 物理结构:跳转结构(链式结构)
- 逻辑结构:线性表(链表)
- LinkedList底层是是双向链表 --一个节点包含三部分:前一个元素地址+当前存入的元素+后一个元素地址
Set接口:唯一,无序(无序不是随机)
遍历:迭代器,增强for循环
Set接口:唯一,无序(无序不是随机)
方法:add(),clear(),remove()...
HashSet<Integer> hs = new HashSet<>();
System.out.println(hs.add(10)); //true
hs.add(0);
hs.add(15);
System.out.println(hs.add(10)); //false,这个10没有返回到集合中
hs.add(12);
System.out.println(hs.size()); //唯一,不存在重复元素
System.out.println(hs);
HashSet<test10.Student> ts = new HashSet<>(); //自定义数据类型--不满足唯一
ts.add(new test10.Student("lili",20,"男"));
ts.add(new test10.Student("ll",15,"女"));
ts.add(new test10.Student("lili",20,"男"));
ts.add(new test10.Student("li",200,"女"));
System.out.println(ts.size()); //4
//在自定义的类型中,重写hashCode方法和equals方法就可以达到唯一
底层原理:
哈希表=数组+链表
注意:如果放入HashSet中的数据,一定要重写两个方法:hashCode,equals
LinkedHashSet实现类
特点:唯一,有序--按照输入顺序进行输出
底层:在HashSet的基础上多了一个总链表,把元素按照顺序链接起来,方便有序遍历
插入知识点:比较器
-
比较Int类型数据:将比较的数据作差,然后返回一个int类型的数据,将这个int类型的数值 按照 =0 >0 <0
-
比较String类型数据:String类实现了Comparable接口,这个接口中有一个抽象方法compareTo,String类中重写这个方法即可。
public static void main(String[] args) { String a = "A"; String b = "B"; System.out.println(a.compareTo(b)); //-1 //String实现了Comparable接口,Comparable里有抽象方法compareTo,在String中重写了这个方法 }
-
比较double类型数据:将数据包装成Double类,调用compareTo方法
double c = 9.987; double d = 8.876; System.out.println(((Double)c).compareTo((Double)d));
-
比较自定义的数据类型:
1)内部比较器(实现Comparable接口)--每次只能比较一个(在类里面写)
@Override //实现Comparable接口必须重写compareTo方法 public int compareTo(Student o) { //比较年龄 // return this.getAge() - o.getAge(); //比较身高 // return ((Double)(this.getHeight())).compareTo((Double)(o.getHeight())); //比较名字 return this.getName().compareTo(o.getName()); }
public static void main(String[] args) { Student s1 = new Student(16,165.2,"nana"); Student s2 = new Student(18,160.6,"lala"); System.out.println(s1.compareTo(s2)); }
2)外部比较器--更好,多态,扩展性好
在类之外写外部比较器(实现 Comparator接口 )--可以同时比较多个
public class Student1 { ... } class biJiao1 implements Comparator<Student> { @Override //重写了Comparator接口中的compare方法 public int compare(Student o1, Student o2) { //比较年龄 return o1.getAge() - o2.getAge(); } } class biJiao2 implements Comparator<Student> { @Override public int compare(Student o1, Student o2) { //比较身高 return ((Double) (o1.getHeight())).compareTo((Double) (o2.getHeight())); } }
//获取外部比较器--多态 Comparator bj1 = new biJiao1(); System.out.println(bj1.compare(s1,s2)); Comparator bj2 = new biJiao2(); System.out.println(bj2.compare(s1,s2));
TreeSet实现类
-
存入Integer类型数据:底层是内部比较器,唯一,有序(按升序进行遍历),无序(不是按照输入顺序)
-
存入String类型数据--底层实现类内部比较器
-
放入自定义的Student类型数据--可以利用内部比较器--也可以利用外部比较器
-
原理:底层-二叉树(逻辑结构)-物理结构:跳转结构(链式结构)
在数中放入数据时,最重要的是比较的实现-->内部比较器或者外部比较器
底层的二叉树遍历是中序遍历得到的,即按照升序结果出现
Map接口
HashMap实现类
底层原理:数组+链表+红黑树(java8新增红黑树)
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步