集合
-
什么是集合?
一种引用数据类型,可以存储多个数据
-
为什么要学习集合?
数组的缺点:
(1)空间长度不可以改变。
(2)没办法获得数组中真实的元素个数。
(3)增加,删除 操作复杂 。
(4)不能去重;数组没有做功能的封装
------------不同点
(1)可以存储基本数据类型,也可以存储引用数据类型
(2)只能存储相同数据类型
(3)数组肯定有下标
集合的好处:
(1)空间长度可以改变
(2)有一个属性可以获得真实的元素个数
(3)增,删,改,查 操作都很方便
(1)又去重功能,有排序功能,有方法的封装。。。
----------不同点
(1)只能存储应用数据类型
(2)可以存储多种类型,但不推荐使用
(3)有的集合类型有下标,有的集合类型没有下标
一,“两个图” 集合的分类
-
一个一个存储的(大爷家)
有序---》下标有序---存和取的顺序一致
Collection( 无序的,可重复)
List(有序,可重复) Set(无序,去重)
ArrayList Vector LinkedList TreeSet(有序,去重) HashSet(无序,去重)
-
一对一对存储的(二大爷家)
Map (key 无序,不可重复 - value 无序,可以重复)
TreeMap HashMap
二,七个接口
-
Collection (无序,可重复)
单列集合的祖宗接口,它的功能全部单列集合都可以继承
实现类---ArrayList
-
增加:add(); addAll();
Collection collection = new ArrayList(); collection.add("123"); Collection collection1 = new ArrayList(); collection1.addAll(collection); //将另一个集合添加进来
-
删除:clear(); removeAll(); remove();
collection1.clear();//清空 Collection collection = new ArrayList(); collection.add("1238"); collection.add("195647"); collection.remove("1238");//删除元素 System.out.println(collection);
-
修改:
-
查询:size();
Collection collection = new ArrayList(); System.out.println(collection.size());//获得真实的元素个数
-
判断:isEmpty(); contains(); containsAll();
collection1 = new ArrayList(); System.out.println("isEmpty:" + collection1.isEmpty());//没有元素 System.out.println("判断1123是否存在:" + collection1.contains(1234));
-
遍历:iterator(){迭代器}----
专门用来循环遍历Collection集合类型的
6.单列集合和数组可以用增强for来遍历
Iterator iterator = collection1.iterator(); //Iterator 迭代器: 用于循环遍历集合
while(iterator.hasNext()){//判断有没有下一个元素
System.out.println(iterator.next());//如果有:获得下一个元素
}
//循环遍历
for (Object obj : collection1
) {
System.out.println(obj);
}
System.out.println("===========================");
7.转换:toArray() (转换--将集合转换成数组)
Collection collection = new ArrayList();
System.out.println(collection.toArray());
-
List(有序,可重复)
-
增加:add(2,"张三");add(2,list);
List list = new LinkedList(); list.add(0, "张三"); list.add("李四"); list.add(2, "张三");
-
删除:list.remove(0)(会返回被删除的元素);
list.remove(0);//删除下标为0的元素
-
修改:list.set(0,"往五");
list.set(0, "王五");//下标0的元素改成王五
-
查询:lastIndexOf("李四");indexOf("李四");list.get(0)
System.out.println(list.lastIndexOf("李四"));// 查找 从后往前 list.indexOf("李四");//查找 从前往后 System.out.println(list.get(0));//查照下标为0的元素是什么
-
截取:subList(1,3);
List list = new LinkedList(); list.add(0, "张三"); list.add("李四"); list.add(2, "张三"); System.out.println(list.subList(1, 3));//截取下标[1,3)的元素
-
排序:list.sort(); 元素内容排序
LIst 遍历(三种)
//遍历 list 1 Iterator<StuEntry> iterator = list.iterator(); while (iterator.hasNext()) { StuEntry next = iterator.next(); System.out.println(next); } ================================================================ //遍历list 2 for (StuEntry stuEntry : list) { System.out.println(stuEntry); } ================================================================ //遍历list 3 for (int i = 0; i < list.size(); i++) { System.out.println(list.get(i)); }
-
LinkedList (双向链表)()
-
增加 addFirst(E e) addLast(E e) offer(E e) offerFirst(E e) offerLast(E e)
LinkedList<String> list = new LinkedList<>(); list.add("fffff"); list.addFirst("jj");//添加元素在开头 list.addLast("hh");//添加元素在尾端 list.offer("kk");//添加元素在尾端 list.offerFirst("pp");//添加元素在开头 list.offerLast("rr");//添加元素在尾端 System.out.println(list);//LinkedList可以添加重复数据
-
删除 poll() pollFirst() pollLast() ---》JDK1.6以后新出的方法,提高了代码的健壮性 removeFirst() removeLast()
LinkedList<String> list = new LinkedList<>(); list.add("fffff"); list.addFirst("jj");//添加元素在开头 list.addLast("hh");//添加元素在尾端 list.offer("kk");//添加元素在尾端 list.offerFirst("pp");//添加元素在开头 list.offerLast("rr");//添加元素在尾端 System.out.println(list);//LinkedList可以添加重复数据 System.out.println(list.poll());//删除头上的元素并且将元素输出 System.out.println(list.pollFirst());//删除头上的元素并将元素输出 System.out.println(list.pollLast());//删除尾端的元素并将元素输出 System.out.println(list.removeFirst());//删除头上的元素并将元素输出 System.out.println(list.removeLast());//删除尾端的元素并将元素输出 System.out.println(list);//LinkedList可以添加重复数据
-
修改
-
查询 element() getFirst() getLast() peek() peekFirst() peekLast()
LinkedList<String> list = new LinkedList<>(); list.add("fffff"); list.addFirst("jj");//添加元素在开头 list.addLast("hh");//添加元素在尾端 list.offer("kk");//添加元素在尾端 list.offerFirst("pp");//添加元素在开头 list.offerLast("rr");//添加元素在尾端 System.out.println(list);//LinkedList可以添加重复数据 System.out.println(list.element());//查询第一个元素 System.out.println(list.getFirst());//查询第一个元素 System.out.println(list.getLast());//查询尾端的元素 System.out.println(list.peek());//查询第一个元素 System.out.println(list.peekFirst());//查询第一个元素 System.out.println(list.peekLast());//查询尾端的元素
-
判断
-
简要底部原理图
-
Vector
Vector和ArrayList的区别和联系
实现原理相同,功能相同,都是长度可变的数组结构,很多情况下可以互用
两者的主要区别如下
- Vector是早期JDK接口,ArrayList是替代Vector的新接口
- Vector线程安全,ArrayList重速度轻安全,线程非安全
- 长度需增长时,Vector默认增长一倍,ArrayList增长50%
-
Set(无序,不可重复)
Set 遍历 (两种)
//遍历 Set 1 Iterator<Book> iterator = set1.iterator(); while (iterator.hasNext()){ Book next = iterator.next(); System.out.println(next); } ============================================================ //遍历 Set 2 for (Book book : set1) { System.out.println(book); }
-
TreeSet()(无序,不重复) ------ Comparable Comparator
排序和去重原理:借助比较器接口,重写比较方法
比较器接口:(1)内部比较器 Comparable (在Book类中进行方法重写)
public class Book implements Comparable<Book>
@Override
public int compareTo(Book o) {//compare 能比较,能去重
/* if (this.price > o.price){
return 1;
} else if (this.price==o.price) {
return 0;
}else {
return -1;
}*/
if (!this.id.equals(o.getId())){
return this.price.compareTo(o.getPrice());
}else {
return 0;//代表重复,不能存储
}
(2)外部比较器 Comparator 更灵活,推荐使用 (创建了一个新的类,重写方法,作为外部比较器)
1.新增比较策略的时候,不用改原码
public class BookCompoter implements Comparator<Book> {
@Override
public int compare(Book o1, Book o2) {
if (o1.getId().equals(o2.getId())){
return 0;
}else {
return o1.getPrice().compareTo(o2.getPrice());
}
}
TreeSet<Book>setbooks = new TreeSet<>(new BookCompoter());
setbooks.add(new Book("001", "西游记",32.4 , "清华出版社"));
setbooks.add(new Book("002", "红楼梦", 52.5, "清华出版社"));
setbooks.add(new Book("003", "三国演义", 72.2, "清华出版社"));
setbooks.add(new Book("004", "水浒传", 62.4, "清华出版社"));
System.out.println(setbooks);
System.out.println(setbooks.size());
-
HashSet(无序,不重复)
存储无序,不重复的数据。
1.去重原理:重写Object类中的HashCode()+equals()
因为Object类中的equals() 比较地址;HashCode()根据内存地址获得哈希值;
现在我们需要equals() 比较 属性值;HashCode()根据属性值获得哈希值;
2.简要原理图
@Override
public boolean equals(Object o) {
/* if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Book book = (Book) o;
return Objects.equals(id, book.id) && Objects.equals(name, book.name) && Objects.equals(price, book.price) && Objects.equals(publisher, book.publisher);
*/
if(this==o){
return true;
}
if(!(o instanceof Book)){
return false;
}
Book book=(Book) o;
if(this.getId().equals(book.getId()) &&
this.getPrice().equals(book.getPrice()) &&
this.getPublisher().equals(book.getPublisher()) &&
this.getName().equals(book.getName())){
return true;
}else{
return false;
}
}
@Override
public int hashCode() {
return (int)(this.getId().hashCode()+
this.getName().hashCode()+
this.getPublisher().hashCode()+
this.getPrice());
}
HashSet<String> set=new HashSet<>();
set.add("张三");
HashSet<Book> set1=new HashSet<>();
set1.add(new Book("001", "西游记",32.4 , "清华出版社"));
set1.add(new Book("002", "红楼梦", 52.5, "清华出版社"));
set1.add(new Book("003", "三国演义", 72.2, "清华出版社"));
set1.add(new Book("004", "水浒传", 62.4, "清华出版社"));
System.out.println(set1.size()+";"+set1);
}
-
Map:一对一对存储数据
原因:
- 把常用到的,能唯一代表当前对象的属性,单独存放,就是key(无序,唯一);
- 把不常用到的,大而全的对象属性,存放在value(无序,可重复的)里。
- ---绝大部分情况下,直接使用key就可以;极少部分需要通过key获得value,来解决问题。
1.遍历
public static void main(String[] args) {
Map<String, Book> map = new TreeMap<>();
Book b1 = new Book("124", "红楼梦", 52.1, "吉林出版社");
Book b2 = new Book("123", "西游记", 52.1, "吉林出版社");
Book b3 = new Book("125", "水浒传", 52.1, "吉林出版社");
map.put(b1.getId(), b1);//添加元素
map.put(b2.getId(), b2);
map.put(b3.getId(), b3);
/* map.get("123");//取出元素 根据key去拿value
map.size();//个数查询
map.remove("125");//删除元素 根据key去删除value
map.containsKey("125");//判断key是否存在
map.replace("123",b3);//替换 根据key去替换value*/
//循环遍历Map
//方式一
System.out.println("遍历集合Map,方式一");
Collection<Book> values = map.values();
for (Book boo :
values) {
System.out.println(boo);
}
//方式二
System.out.println("遍历集合Map,方式二");
Iterator<Book> iterator = map.values().iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
//方式三
System.out.println("遍历集合Map,方式三");
Set<String> strings = map.keySet();
for (String k:strings) {
System.out.println(map.get(k));
}
//方式四
System.out.println("遍历集合Map,方式四");
Iterator<String> iterator1 = map.keySet().iterator();
while (iterator.hasNext()) {
Book boo=map.get(iterator1.next());
System.out.println(boo);
}
//方式五
System.out.println("遍历集合Map,方式五");
Set<Map.Entry<String, Book>> entries = map.entrySet();
Iterator<Map.Entry<String, Book>> iterator2 = entries.iterator();
while (iterator2.hasNext()){
Map.Entry<String, Book> next = iterator2.next();
System.out.println(next.getValue());
}
//方式六
System.out.println("遍历集合Map,方式六");
Set<Map.Entry<String, Book>> entries1 = map.entrySet();
for (Map.Entry<String,Book> entry:entries1){
System.out.println(entry.getValue());
}
}
2.部分结构图
-
HashMap(无序,去重)
-
TreeMap(有序,去重)
-
泛型集合
Collection<Integer> collection = new ArrayList<>();
---泛型集合 只作用在编译期,运行期没有泛型集合!
---反射可以验证:反射在运行期起作用!
---Collection<E>, List<E>, ArrayList<E> 这个<E>就是类型参数,即泛型。
1.泛型类和泛型接口
(1)泛型的实例化
//运行
class Test{
//这是main方法,程序的入口
public static void main(String[] args) {
//GenericTest进行实例化:
//(1)实例化的时候不指定泛型:如果实例化的时候不明确的指定类的泛型,那么认为此泛型为Object类型
GenericTest gt1 = new GenericTest();
gt1.a("abc");
gt1.a(17);
gt1.a(9.8);
gt1.b(new String[]{"a","b","c"});
//(2)实例化的时候指定泛型:---》推荐方式
GenericTest<String> gt2 = new GenericTest<>();
gt2.sex = "男";
gt2.a("abc");
gt2.b(new String[]{"a","b","c"});
}
}
=========================================================
//准备
package com.msb.test02;
/**
* @author : msb-zhaoss
* GenericTes就是一个普通的类
* GenericTest<E> 就是一个泛型类
* <>里面就是一个参数类型,但是这个类型是什么呢?这个类型现在是不确定的,相当于一个占位
* 但是现在确定的是这个类型一定是一个引用数据类型,而不是基本数据类型
*/
public class GenericTest<E> {
int age;
String name;
E sex;
public void a(E n){
}
public void b(E[] m){
}
}
(2)继承情况
a.父类指定泛型:
class SubGenericTest extends GenericTest<Integer>{
}
class Demo{
//这是main方法,程序的入口
public static void main(String[] args) {
//指定父类泛型,那么子类就不需要再指定泛型了,可以直接使用
SubGenericTest sgt = new SubGenericTest();
sgt.a(19);
}
}
b.父类不指定泛型:
如果父类不指定泛型,那么子类也会变成一个泛型类,那这个E的类型可以在创建子类对象的时候确定。
class SubGenericTest2<E> extends GenericTest<E>{
}
class Demo2{
//这是main方法,程序的入口
public static void main(String[] args) {
SubGenericTest2<String> s = new SubGenericTest2<>();
s.a("abc");
s.sex = "女";
}
}
(3)应用场合
(4)细节
a.泛型类可以定义多个参数类型
b.泛型类的构造器的写法
c.不同的泛型的引用类型不可以相互赋值
d.泛型如果不指定,那么就会被擦除,反应对应的类型为Object类型
e.反省类中的静态方法不能使用类的泛型
f.不能直接使用E[]的创建
2.泛型方法
package com.jr.test04;
/**
* @author : jr-mayang
* 1.什么是泛型方法:
* 不是带泛型的方法就是泛型方法
* 泛型方法有要求:这个方法的泛型的参数类型要和当前的类的泛型无关
* 换个角度:
* 泛型方法对应的那个泛型参数类型 和 当前所在的这个类 是否是泛型类,泛型是啥 无关
* 2.泛型方法定义的时候,前面要加上<T>
* 原因:如果不加的话,会把T当做一种数据类型,然而代码中没有T类型那么就会报错
* 3.T的类型是在调用方法的时候确定的
* 4.泛型方法可否是静态方法?可以是静态方法
*/
public class TestGeneric<E> {
//不是泛型方法 (不能是静态方法)
public static void a(E e){
}
//是泛型方法
public static <T> void b(T t){
}
}
==================================================================
class Demo{
//这是main方法,程序的入口
public static void main(String[] args) {
TestGeneric<String> tg = new TestGeneric<>();
tg.a("abc");
tg.b("abc");
tg.b(19);
tg.b(true);
}
}
3. 泛型参数存在继承关系的情况
4.通配符
(1)没有通配符时
//下面的a方法,相当于方法的重复定义,报错
public class Test {
/*public void a(List<Object> list){
}
public void a(List<String> list){
}
public void a(List<Integer> list){
}*/
}
(2)引入通配符
public class Demo {
//这是main方法,程序的入口
public static void main(String[] args) {
List<Object> list1 = new ArrayList<>();
List<String> list2 = new ArrayList<>();
List<Integer> list3 = new ArrayList<>();
List<?> list = null;
list = list1;
list = list2;
list = list3;
}
}
-----发现: A 和 B是子类父类的关系,G<A>和G<B>不存在子类父类关系,是并列的
加入通配符?后,G<?>就变成了 G<A>和G<B>的父类
(2)使用通配符
public class Test {
/*public void a(List<Object> list){
}
public void a(List<String> list){
}
public void a(List<Integer> list){
}*/
public void a(List<?> list){
//内部遍历的时候用Object即可,不用?
for(Object a:list){
System.out.println(a);
}
}
}
==============================================================
class T{
//这是main方法,程序的入口
public static void main(String[] args) {
Test t = new Test();
t.a(new ArrayList<Integer>());
t.a(new ArrayList<String>());
t.a(new ArrayList<Object>());
细节:
public class Test {
public void a(List<?> list){
//1.遍历:
for(Object a:list){
System.out.println(a);
}
//2.数据的写入操作 :
//list.add("abc");-->出错,不能随意的添加数据
list.add(null);
//3.数据的读取操作:
Object s = list.get(0);
}
}
=================================================================
class T{
//这是main方法,程序的入口
public static void main(String[] args) {
Test t = new Test();
t.a(new ArrayList<Integer>());
t.a(new ArrayList<String>());
t.a(new ArrayList<Object>());
}
}
5.泛型受限
public class Test {
//这是main方法,程序的入口
public static void main(String[] args) {
//a,b,c三个集合是并列的关系:
List<Object> a = new ArrayList<>();
List<Person> b = new ArrayList<>();
List<Student> c = new ArrayList<>();
/*开始使用泛型受限:泛型的上限
List<? extends Person>:
就相当于:
List<? extends Person>可以是List<Person>的类型,也可以是List<Person的子类>类型
*/
List<? extends Person> list1 = null;
/*list1 = a;// 报错
list1 = b;
list1 = c;*/
/*开始使用泛型受限:泛型的下限
List<? super Person>
就相当于:
List<? super Person>可以是List<Person>的类型,也可以是List<Person的父类>类型
*/
List<? super Person> list2 = null;
list2 = a;
list2 = b;
list3 = c;//报错
}
}
三,一个类
-
Collections
Collection和Collections的区别
Collection是Java提供的集合接口,存储一组不唯一,无序的对象。它有两个子接口List和Set。
Java中还有一个Collections类,专门用来操作集合类 ,它提供一系列静态方法实现对各种集合的搜索、排序、线程安全化等操作。
public static void main(String[] args) {
//Collections不支持创建对象,因为构造器私有化了
/*Collections cols = new Collections();*/
//里面的属性和方法都是被static修饰,我们可以直接用类名.去调用即可:
//常用方法:
//addAll:
ArrayList<String> list = new ArrayList<>();
list.add("cc");
list.add("bb");
list.add("aa");
Collections.addAll(list,"ee","dd","ff");
Collections.addAll(list,new String[]{"gg","oo","pp"});
System.out.println(list);
//binarySearch必须在有序的集合中查找:--》排序:
Collections.sort(list);//sort提供的是升序排列
System.out.println(list);
//binarySearch
System.out.println(Collections.binarySearch(list, "cc"));
//copy:替换方法
ArrayList<String> list2 = new ArrayList<>();
Collections.addAll(list2,"tt","ss");
Collections.copy(list,list2);//将list2的内容替换到list上去
System.out.println(list);
System.out.println(list2);
//fill 填充
Collections.fill(list2,"yyy");
System.out.println(list2);
集合总结:
“两个图”:Collecton ;map
“7个接口”:Collecton list set map Iterator Comparable,Comparator
“1个类”:Collections
“两个去重原理”: TessSet(比较器) HashSet(hashCode + equals)
"3种map遍历": Values() keySet() entrySet()
例题
功能1:定义方法public void listToMap( )将List中Student元素封装到Map中
z使用构造方法Student(int id,String name,int age,String sex )创建多个学生信息并加入List
- 遍历List,输出每个Student信息
- 将List中数据放入Map,使用Student的id属性作为key,使用Student对象信息作为value
- 遍历Map,输出每个Entry的key和value
package set.homework; import org.omg.CORBA.INTERNAL; import java.util.Objects; public class Student implements Comparable<Student> { //a.创建Student类, private Integer id; private String name; private Integer age; public Student() { } public Student(Integer id, String name, Integer age) { this.id = id; this.name = name; this.age = age; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } @Override public String toString() { return "Student{" + "id=" + id + ", name='" + name + '\'' + ", age=" + age + '}'; } //按照年龄从大到小排序 @Override public int compareTo(Student o) { return this.age-o.age; } }
package listmap; import java.util.*; public class ListToMap { public static void main(String[] args) { List<Student1> list = new ArrayList<>(); list.add(new Student1(1, "张三", 15, "男")); list.add(new Student1(2, "李四", 16, "男")); list.add(new Student1(3, "王五", 17, "男")); list.add(new Student1(4, "唐六", 18, "男")); list.add(new Student1(5, "蒋七", 19, "女")); for (Student1 s: list) { System.out.println(s); } System.out.println("====================="); Map<Integer, Student1> map = new TreeMap<>(); Iterator<Student1> it = list.iterator(); while(it.hasNext()){ Student1 stu = it.next(); map.put(stu.getId(), stu); } Set<Map.Entry<Integer, Student1>> entries = map.entrySet(); Iterator<Map.Entry<Integer, Student1>> iterator2 = entries.iterator(); while (iterator2.hasNext()){ Map.Entry<Integer, Student1> next = iterator2.next(); System.out.println(next.getValue()); } } }