JAVA高级特性-集合框架
为何需要用到集合框架?
在我们已需要存储的数据的个数时,往往可以采用数组存储或者自定义链表等数据结构存储数据。但是如果数据的长度是未知的,则无法满足我们的需求。
因此需要使用到JAVA中的集合框架存储相关数据。
集合框架之间的关系
在集合框架中比较常用的一般是ArrayList、LinkedList、HashSet、HashMap
接下来谈谈它们在使用时的一些注意事项-个人观点
List
List接口存储一组 不唯一,有序(插入顺序) 的对象,其2个实现类为ArrayList,LinkedList。前者的底层结构为数组,后者为链表。
前者增删慢,查询遍历快,后者相反。
关于LIst集合常用的几种遍历方法
public class Demo_01 {
public static void main(String[] args) {
List<String> list=new ArrayList<String>();
list.add("1");
list.add("2");
list.add("3");
System.out.println("*****遍历方法一****");
for(int i=0;i<list.size();i++) {//普通for循环 利用get方法
System.out.println(list.get(i));
}
System.out.println("****遍历方法二*****");
for(String str:list) {//增强型for循环
System.out.println(str);
}
System.out.println("*****遍历方法三******");
Iterator it=list.iterator();
while(it.hasNext()) {//迭代器遍历
String s=(String)it.next();
System.out.println(s);
}
}
}
List重写了toString方法 ?
public static void main(String[] args) {
List<Integer> list=new ArrayList<Integer>();
list.add(1);
list.add(3);
System.out.println("****基本数据类型****");
System.out.println(list);
List<Student> stu=new ArrayList<Student>();
Student stu_1=new Student(12,"da");
Student stu_2=new Student(12,"da");
stu.add(stu_1);
stu.add(stu_2);
System.out.println("***引用数据类型*****");
System.out.println(stu);
}
结果:
****基本数据类型****
[1, 3, 1]
***引用数据类型*****
[day_29.work.Student@7de26db8, day_29.work.Student@1175e2db]
很多人以为List重写了toSring方法 ,其实通过结果可知,并不是这样子。因为包装类Integer(或基本数据类型)重写了toString方法,才可以输出其值,而不是哈希地址等。
Set接口
List集合存储的一般是可重复的元素,如果需要存储不重复的元素,则我们需要用到其他集合,例如Set。
Set接口存储一组唯一,无序的对象。
关于Set集合常用的两种遍历方法
public static void main(String[] args) {
Set<Integer> set=new HashSet<Integer>();
set.add(1);
set.add(2);
set.add(4);
set.add(2);//添加相同的元素
System.out.println(set);//输出其值,Integer类型里重写了toString
System.out.println("*******遍历方法一****");
for(Integer in:set) {//增强型for
System.out.println(in);
}
System.out.println("******遍历方法二*******");
Iterator it=set.iterator();//迭代器
while(it.hasNext()) {
System.out.println(it.next());
}
}
结果:
[1, 2, 4]
*******遍历方法一****
1
2
4
******遍历方法二*******
1
2
4
通过代码可发现,后续添加的相同元素,set结合只会保留前一个值,如果set中的类型为自定义数据类型,结果又是如何呢?
public static void main(String[] args) {
//自定义了一个Student类
Set<Student> set=new HashSet<Student>();
Student stu=new Student(1,"a");
Student stu2=new Student(4,"a");
Student stu3=new Student(1,"a");
Student stu4=new Student(5,"b");
set.add(stu);
set.add(stu2);
set.add(stu3);//添加相同的对象值(地址不一样)
set.add(stu4);
System.out.println(set);//Studetn未重写toString方法 ,则输出其地址字段(哈希)
System.out.println("*******遍历方法一****");
for(Student st:set) {//增强型for
System.out.println(st.getNum()+" "+st.getName());
}
System.out.println("******遍历方法二*******");
Iterator it=set.iterator();
while(it.hasNext()) {
Student su=(Student)it.next();
System.out.println(su.getNum()+" "+su.getName());
}
}
结果:
[day_29.work.Student@5e265ba4, day_29.work.Student@156643d4, day_29.work.Student@cac736f, day_29.work.Student@379619aa]
*******遍历方法一****
1 a
5 b
4 a
1 a
******遍历方法二*******
1 a
5 b
4 a
1 a
通过结果我们惊奇的发现,set中居然出现了相同的元素,则岂不是与set的唯一性冲突?其实这与set的底层数据结构有关系(哈希表),我们先保留这个问题。
Map
Map集合与Set集合类似,是一个唯一,无序的集合;只是多了一种映射关系。
关于Map集合常用的几种遍历方法
public static void main(String[] args) {
Map<String,Integer> map=new HashMap<String,Integer>();
map.put("a", 1);
map.put("b", 2);
map.put("c", 3);
map.put("a", 4);//添加相同映射关系
map.put("d", 3);
System.out.println("******遍历方法一******");
Iterator<String> it=map.keySet().iterator();//迭代器
while(it.hasNext()) {
String s=it.next();
Integer in=(Integer)map.get(s);
System.out.println(s+" "+in);
}
System.out.println("******遍历方法二******");
for(Map.Entry<String, Integer> maps:map.entrySet()) {//推荐使用
System.out.println(maps.getKey()+" "+maps.getValue());
}
System.out.println("****以自定义数据类型作为键****");
Map<Student,String> maps=new HashMap<Student,String>();
Student su=new Student(1,"小吴");
Student stu=new Student(2,"小米");
Student st=new Student(1,"小吴");
maps.put(su,"1");
maps.put(stu, "2");
maps.put(st, "1");
for(Map.Entry< Student,String> ma:maps.entrySet()) {
Student sus=ma.getKey();
System.out.println(sus.getNum()+" "+sus.getName()+"maps值:"+ma.getValue());
}
}
结果:
******遍历方法一******
a 4
b 2
c 3
d 3
******遍历方法二******
a 4
b 2
c 3
d 3
****以自定义数据类型作为键****
1 小吴maps值:1
1 小吴maps值:1
2 小米maps值:2
通过上述代码可发现,当map添加的数据类型为JAVA中定义好的类型时,如果有重复值,后续添加的相同元素会覆盖前面的元素,从而保证其key的唯一性。但是如果我们定义的数据类型为自定义数据类型,则会发现key的值是重复了的,与唯一性冲突。这其实也与Map的底层数据结构有关(哈希表)。
哈希表
哈希表底层使用的也是数组机制,数组中也存放对象,而这些对象往数组中存放时的位置比较特殊,当需要把这些对象给数组中存放时,那么会根据这些对象的特有数据结合相应的算法,计算出这个对象在数组中的位置,然后把这个对象存放在数组中。而这样的数组就称为哈希数组,即就是哈希表。
当向哈希表中存放元素时,需要根据元素的特有数据结合相应的算法,这个算法其实就是Object类中的hashCode方法。由于任何对象都是Object类的子类,所以任何对象有拥有这个方法。即就是在给哈希表中存放对象时,会调用对象的hashCode方法,算出对象在表中的存放位置,这里需要注意,如果两个对象hashCode方法算出结果一样,这样现象称为哈希冲突,如果2个hashcode的值不一样,就不会再调用对象的equals()方法,而是直接把元素添加到集合中。,否则会调用对象的equals方法,比较这两个对象是不是同一个对象,如果equals方法返回的是true,那么就不会把第二个对象存放在哈希表中,如果返回的是false,就会把这个值存放在哈希表中。
总结:如果我使用的数据类型不是java中自带的数据类型(jdk内部已写好),就必须重写equals方法或hashcode方法,保证其set和map集合的唯一性。
Set集合其实就是Map集合套个壳-之前遗留的问题
//jdk源码
public HashSet() {
map = new HashMap<>();
}
由此可知,Set集合的唯一性与Map集合唯一性的实现原理是一样的。
Map集合如何实现一对多的关系?
众所周知,map集合的唯一性使它可以一对一,多对一;那能不能一对多呢?答案当然是可以的。
我们可以借助Set或者List集合,将该集合与某一个key建立映射关系,即可实现。
public static void main(String[] args) {
Map<String,List<Integer>> map=new HashMap<String,List<Integer>>();
List<Integer> list=new ArrayList<Integer>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
map.put("a", list);
for(Map.Entry<String, List<Integer>> maps:map.entrySet()) {
List<Integer> li=maps.getValue();
System.out.println("键:"+maps.getKey());
System.out.println(" 值:");
for(Integer in:li) {
System.out.println("\t"+in);
}
}
}
结果:
键:a
值:
1
2
3
4