今天学的集合是Collection集合其中有两个接口List和Set集合,其中List接口的实现类有ArraryList类和LinkedList类其中ArraryList中数据结构是数组,LinkedList数据结构是链表,可以通过ListIterator遍历集合,Collection集合中的数据可以重复的====然后是Set集合,Set集合中的数据不可重复且没有带索引的方法所以不能通过for循环遍历,可以用增强for。然后Set集合接口有HashSet和TreeSet两个实现类,其次HashSet又被LinkedHashSet继承,区别是HashSet无序,LinkedHashSet和TreeSet是有序的。Set集合不允许有重复数据,其原理是通过重写hashCode()和equals()方法来实现的。 LinkedHashSet的有序是通过链表存储的数据结构保证的,然后TreeSet的有序是通过自然排序或是比较器实现的=======
-
自然排序,就是让元素所属的类实现Comparable接口----------->例如Student类继承Comparable<Student>然后重写comparaTo方法在添加元素时自动调用
-
比较器排序,就是让集合构造方法接收Comparator的实现类对象,重写compare(T o1,T o2)方法-------->有参构造TreeSet然后new一个匿名内部类,相当于创建一个普通类实现接口,实际上是创建的普通类的对象,不是接口的对象,用接口来接这个普通类的对象是用了多态。------重写compare方法
List集合
Listlterator
-
Listlterator:列表迭代器
-
通过List集合的listlterator()方法得到,所以说它是List集合特有的迭代器
-
用于允许程序员沿任一方向遍历列表的列表迭代器,在迭代期间修改列表,并获取列表中迭代器的当前位置
//使用ListIterator遍历的时候调用迭代器里的add方法不会并发修改异常
List<String> list = new ArrayList<>();
list.add("hello");
list.add("world");
list.add("java");
ListIterator<String> lit = list.listIterator();
while (lit.hasNext()){
String s = lit.next();
if (s.equals("world"))
lit.add("javase");
}
System.out.println(list);
-
数据结构
-
数据结构是计算机存储、组织数据的方式。是指相互之间存在一种或多种特定关系的数据元素的集合通常情况下,精心选择的数据结构可以带来更高的运行或者存储效率
-
数组
-
查询数据通过索引定位,查询任意数据耗时相同,查询效率高
-
删除数据时,要将原始数据删除,同时后面每个数据前移,删除效率低
-
添加数据时,添加位置后的每个数据后移,再添加元素添加效率极低
-
-
链表
-
链表是一种增删快的模型(对比数组)
-
链表是一种查询慢的模型(对比数组)
-
查询数据D是否存在,必须从头(head)开始查询
-
List子类集合的特点
-
ArrayList:底层数据结构是数组,查询快,增删慢
-
LinkedList:底层数据结构是链表,查询慢,增删快
Set集合
-
Set集合特点
-
不包含重复元素的集合
-
没有带索引的方法,所以不能使用普通for循环遍历
-
-
HashSet:对集合的迭代顺序不做任何保证
Set<String> set = new HashSet<>();
set.add("hello");
set.add("world");
set.add("java");
set.add("java"); //Set不包含重复元素的集合
for (String s : set) {
System.out.println(s);
}
OutPut:
world
java
hello
-
哈希值
哈希值:是JDK根据对象的地址或者字符串或者数字算出来的int类型的数值
Object类中有一个方法可以获取对象的哈希值
-
public int hashCode(): 返回对象的哈希值
Student s1 = new Student("张曼玉",30);
//同一个对象多次调用hashCode方法返回的哈希值是相同的
System.out.println(s1.hashCode()); //460141958
System.out.println(s1.hashCode()); //460141958
System.out.println("=========================");
//默认情况下不同对象的哈希值是不同的
Student s2 = new Student("林青霞",28);
System.out.println(s2.hashCode()); //1163157884
//通过方法重写可以实现不同对象的哈希值相同
System.out.println("=========================");
System.out.println("hello".hashCode()); //99162322
System.out.println("world".hashCode()); //113318802
System.out.println("world".hashCode()); //113318802
System.out.println("=========================");
System.out.println("重地".hashCode()); //1179395
System.out.println("通话".hashCode()); //1179395
HashSet
-
HashSet集合特点
-
底层数据结构是哈希表
-
对集合的迭代顺序不作任何保证,也就是说不保证存储和取出的元素顺序一致没有带索引的方法,所以不能使用普通for循环遍历
-
由于是Set集合,所以是不包含重复元素的集合
-
-
HashSet集合保证元素唯一性分析
-
要保证元素唯一性需要重写hashCode()和equals()
-
哈希表
哈希表
-
JDK8之前,底层采用数组+链表实现,可以说是一个元素为链表的数组
-
JDK8以后,在长度比较长的时候,底层实现了优化
LinkedHashSet
-
LinkedHashSet集合特点
-
哈希表和链表实现的Set接口,具有可预测的迭代次序
-
由链表保证元素有序,也就是说元素的存储和取出顺序是一致的
-
由哈希表保证元素唯一,也就是说没有重复的元素
TreeSet
-
元素有序,这里的顺序不是指存储和取出的顺序,而是按照一定的规则进行排序,具体排序方式取决于构造方法
-
TreeSet):根据其元素的自然排序进行排序
-
TreeSet(Comparator comparator):根据指定的比较器进行排序
-
没有带索引的方法,所以不能使用普通for循环遍历
TreeSet<Integer> i = new TreeSet<>();
i.add(10);
i.add(40);
i.add(30);
i.add(50);
i.add(20);
i.add(30); //不包含重复
for (Integer ib : i) { //自然排序
System.out.println(ib);
OutPut:
10
20
30
40
50
自然排序Compareble的使用
-
用TreeSet集合存储自定义对象,无参构造方法使用的是自然排序对元素进行排序的
-
自然排序,就是让元素所属的类实现Comparable接口
-
重写compareTo(To)方法重写方法时,一定要注意排序规则必须按照要求的主要条件和次要条件来写
TreeSet<Student> i = new TreeSet<>();
Student s1 = new Student("xishi",29);
Student s2 = new Student("diaohcan",28);
Student s3 = new Student("wangzhaojun",30);
Student s4 = new Student("yangyvhuan",33);
Student s5 = new Student("zhangmanyv",33);
Student s6 = new Student("zhangmanyv",33);
i.add(s1);
i.add(s2);
i.add(s3);
i.add(s4);
i.add(s5);
i.add(s6);
for (Student s : i) {
System.out.println(s.getName()+","+s.getAge());
}
@Override
public int compareTo(Student o) {
//按照年龄从小到大排序
int num = this.age - o.age;
//如果年龄相同那么判断名字是否相同
int num2 = num == 0 ? this.name.compareTo(o.name) : num;
return num2;
}
==============
diaohcan,28
xishi,29
wangzhaojun,30
yangyvhuan,33
zhangmanyv,33
比较器CompataTo的使用
-
用TreeSet集合存储自定义对象,带参构造方法使用的是比较器排序对元素进行排序的
-
比较器排序,就是让集合构造方法接收Comparator的实现类对象,重写compare(T o1,T o2)方法
-
重写方法时,一定要注意排序规则必须按照要求的主要条件和次要条件来写
TreeSet<Student> ts = new TreeSet<>(new Comparator<Student>() {
@Override
public int compare(Student s1, Student s2) {
int num = s1.getAge() - s2.getAge();
int num2 = num == 0? s1.getName().compareTo(s2.getName()) : num;
return num2;
}
});
Student s1 = new Student("xishi",29);
Student s2 = new Student("diaohcan",28);
Student s3 = new Student("wangzhaojun",30);
Student s4 = new Student("yangyvhuan",33);
Student s5 = new Student("zhangmanyv",33);
Student s6 = new Student("zhangmanyv",33);
ts.add(s1);
ts.add(s2);
ts.add(s3);
ts.add(s4);
ts.add(s5);
ts.add(s6);
//遍历集合
for (Student s : ts) {
System.out.println(s.getName()+","+s.getAge());
}
泛型
-
泛型:是JDK5中引入的特性,它提供了编译时类型安全检测机制,该机制允许在编译时检测到非法的类型它的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数一提到参数,最熟悉的就是定义方法时有形参,然后调用此方法时传递实参。那么参数化类型怎么理解呢?顾名思义,就是将类型由原来的具体的类型参数化,然后在使用/调用时传入具体的类型这种参数类型可以用在类、方法和接口中,分别被称为泛型类、泛型方法、泛型接口
-
<类型>:指定一种类型的格式。这里的类型可以看成是形参
-
<类型1,类型2...>:指定多种类型的格式,多种类型之间用逗号隔开。这里的类型可以看成是形参
-
将来具体调用时候给定的类型可以看成是实参,并且实参的类型只能是引用数据类型
Collection c = new ArrayList();
c.add("Hello");
c.add("Wrold");
c.add("Java");
Iterator it = c.iterator();
while (it.hasNext()){
Object obj = it.next();
System.out.println(obj);
}
========
Hello
Wrold
Java
泛型类
泛型类的定义格式:
-
格式:修饰符class类名<类型>{ }
-
范例: publicclass Generic<T>{ } 此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型
public class Student< T > {
private T name;
public T getName() {
return name;
}
public void setName(T name) {
this.name = name;
}
public static void main(String[] args) {
Student<String> s1 = new Student<String>();
s1.setName("林青霞");
System.out.println(s1.getName());
Student<Integer> s2 = new Student<Integer>();
s2.setName(30);
System.out.println(s2.getName());
}
}
=========
林青霞
30
泛型方法
-
泛型方法的定义格式: 格式:修饰符<类型>返回值类型方法名(类型变量名){}
-
范例: public <T> void show(T t){ }
泛型接口
-
泛型接口的定义格式:
-
格式:修饰符interface接口名<类型>{ }
-
范例: public interface Generic<T> { }
类型通配符
为了表示各种泛型List的父类,可以使用类型通配符
-
类型通配符:<?>
-
List<?>:表示元素类型未知的List,它的元素可以匹配任何的类型
-
这种带通配符的List仅表示它是各种泛型List的父类,并不能把元素添加到其中
如果说我们不希望List<?>是任何泛型List的父类,只希望它代表某一类泛型List的父类,可以使用类型通配符的上限
-
类型通配符上限: <?extends 类型>
-
List<? extends Number>:它表示的类型是Number或者其子类型
除了可以指定类型通配符的上限,我们也可以指定类型通配符的下限·
-
类型通配符下限:<?supe r类型>
-
List<? super Number>:它表示的类型是Number或者其父类型
List<?> list1 = new ArrayList<Object>();
List<?> list2 = new ArrayList<Number>();
List<?> list3 = new ArrayList<Integer>();
System.out.println("===================");
// List<? extends Number> list4 = new ArrayList<Object>();
List<? extends Number> list5 = new ArrayList<Number>();
List<? extends Number> list6 = new ArrayList<Integer>();
System.out.println("===================");
List<? super Number> list7 = new ArrayList<Object>();
List<? super Number> list8 = new ArrayList<Number>();
// List<? super Number> list9 = new ArrayList<Integer>();
可变参数
-
可变参数又称参数个数可变,用作方法的形参出现,那么方法参数个数就是可变的了
-
格式:修饰符返回值类型方法名(数据类型...变量名){}
-
范例: public static int sum(int...a){ }
-
可变参数注意事项
-
这里的变量其实是一个数组
-
如果一个方法有多个参数,包含可变参数,可变参数要放在最后
-
public static void main(String[] args) {
System.out.println(add(1));
System.out.println(add(1,2));
System.out.println(add(1,2,3));
}
public static int add(int ...a){ //传进来的是数组
int sum = 0;
for(int i :a){
sum +=i;
}
return sum;
}
//如果传入多个参数则将可变参数放在后面
public static int add(int a,int ...a){} //将传入的第一个参数后面的封装成数组
-
Arrays工具类中有一个静态方法:
-
public static <T> List<T> asList(T... a):
-
返回由指定数组支持的固定大小的列表返回的集合不能做增删操作,可以做修改操作
-
-
List接口中有一个静态方法:
-
public static <E> List<E> of(E... elements):
-
返回包含任意数量元素的不可变列表返回的集合不能做增删改操作
-
-
Set接口中有一个静态方法:
-
public static <E> Set<E> of(E... elements):
-
返回一个包含任意数量元素的不可变集合在给元素的时候,不能给重复的元素
-
返回的集合不能做增删操作,没有修改的方法
-