这是我参与11月更文挑战的第19天,活动详情查看:2021最后一次更文挑战
Collection
- collection的结构

List
- list集合的特点
- 元素有序
- 以线性方式存储,可以用索引来获取元素
- ArrayList
- 元素有序可重复
- 内部封装了一个长度可变的数组,当添加或者修改的时候会导致创建新的数组,所以适合查找,不适合大量的增删
ArrayList list = new Arraylist(); 复制代码 - LinkedList
- 元素有序可重复
- 内部维护了一个双向链表,适用于增删操作
LinkedList link = new LinkedList(); 复制代码
Set
- 无序不重复
- 不重复通过哈希来实现
- HashSet
-
适合存取与查找
- 语法
#1传入字符串 HashSet set = new HashSet(); #2传入引用类型 //String类里实现了euqals方法,而自己的类里未创建,就需要自己重写hashcode()和equals()方法 复制代码- hashcode()方法产生相同的值但可能是不同的对象
- 如果在自定义的类中没有重写hashcode(),那么,返回的则是
object.hashcode(),即该对象的地址解析的哈希值 - 如果重写了就可以自定义返回属性的hash值,比如
name.hashcode,判断name的hashcode是否相同,name的值相同hashcode就相同,所以哈希值相同不代表是同一个对象 - 两个对象重复的要素:哈希值相同,属性相同
- equals方法默认等于==,也就是比较两个对象的地址,这个方法的用处就是让你重写,进行自定义比较,而String类里面已经重写过equals方法,比较两个值是否相等
- 当只重写了hashcode方法时,equals没重写,就会导致系统认为这两个对象是不相同
- 必须满足上面两个要素,才会认为对象重复
- 只需要比较其中一个元素相同即可,其他元素不同也没事
- equals的作用:区分哈希码相同,但不是同一个对象的对象
- 如果在自定义的类中没有重写hashcode(),那么,返回的则是
- HashSet存储的内部结构
- hashset根据元素的哈希码进行存储,如果存入一个student类型的对象,里面包含(id=111,name="rose")(id=111,name="jack"),但是student类里重写的hashcode和equals都是根据name来判断,那么就会变成一个散列表(如下图),会导致遍历索引来查找元素

4. hashCode方法必须与equals方法必须兼容
- 如果我们自己定义了一个类,想对这个类的大量对象组织成散列表结构便于查找。有一点一定要注意:就是hashCode方法必须与equals方法向兼容。
- 为什么要这样,因为HashSet不允许相同元素(equals==ture)同时存在在结构中。假如employeeX(1111,“张三”)和employee(1111,"李四"),而Employee.equals比较的是name。这样的话,employeeX和employeeY的equals不相等。它们会根据相同的散列码1111加入到同一个散列单元所指向的列表中。这种情况多了,链表的数据将很庞大,散列冲突将非常严重,查找效率会大幅//hashCode与equals方法的兼容 public class Employee{ public int id; public String name=""; //相同id对象具有相同散列码 public int hashCode(){ return id; } //equals必须比较id public boolean equals(Employee x){ if(this.id==x.id) return true; else return false; } } 复制代码1.先定义一个HashSetTest主类 package test.base.collections; public class HashSetTest { private Integer id; private String name; public HashSetTest(Integer id, String name) { super(); this.id = id; this.name = name; } //这里即是没有重写过的hashcode,返回地址哈希值 public int hashCode() { return super.hashCode(); } //这里是重写过的hashcode,返回id的哈希值 // public int hashCode() { // return id.hashCode(); // } } 2.定义测试类 package test.base.collections; public class TestHashSetTest { public static void main(String[] args) { HashSetTest hst1 = new HashSetTest(1, "张1"); HashSetTest hst2 = new HashSetTest(2, "张2"); HashSetTest hst3 = new HashSetTest(3, "张3"); HashSetTest hst4 = new HashSetTest(4, "张4"); HashSetTest hst5 = new HashSetTest(1, "张1"); System.out.println(hst1.hashCode()); System.out.println(hst2.hashCode()); System.out.println(hst5.hashCode()); } } //这里重写过后的hashcode是一样的 //而调用未重写的hashcode方法,返回值是不一样的 复制代码1.主类 public class HashSetTest2 { //注意这里的id是String private String id; private String name; public HashSetTest2(String id, String name) { super(); this.id = id; this.name = name; } @Override public String toString() { return "HashSetTest2 [id=" + id + ", name=" + name + "]"; } @Override public int hashCode() { // TODO Auto-generated method stub return id.hashCode(); } @Override public boolean equals(Object obj) { //判断地址是否相同,相同直接返回true,不插入 if(this == obj) { return true; } //类型不同,直接插入 if(!(obj instanceof HashSetTest2)) { return false; } HashSetTest2 hst = (HashSetTest2)obj; //这里使用的是String的equals,所以判断的是两个id的值是否一样 boolean b = this.id.equals(hst.id); return b; } } 2.测试类 public class TestHashSetTest2 { public static void main(String[] args) { HashSet hs = new HashSet(); HashSetTest2 hst1 = new HashSetTest2("1","jack"); HashSetTest2 hst2 = new HashSetTest2("2","rose"); HashSetTest2 hst3 = new HashSetTest2("2","rosse"); hs.add(hst1); hs.add(hst2); hs.add(hst3); System.out.println(hs); } } 复制代码
- treeSet
- 内部采用平衡二叉树实现
- 适用于排序
- 当插入数据的时候会使用compareTo()进行比较,而自己定义的类里需要按自己的规则重写compareTo()方法
- 根据compareTo()返回值进行插入
- 返回1,当前值会被排在比较值的后面
- 返回0,认为是相同元素不会插入
- 返回-1,查到比较值前面
- 可以继承Comparable重写compareto,也可以继承Comparable然后自己写一个方法,就可以根据返回值进行排序(正数,复数,0)
- 实例
public class Student { private int id; private String name; private int score; 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; } public int getScore() { return score; } public void setScore(int score) { this.score = score; } public int compareTo(Object obj) { int r = 0; // 判断是不是 学生类型 if (obj instanceof Student) { Student ss = (Student) obj; // 判断是不是同一个对象(按照姓名进行判断),不是的话就把它加入进来,否则就不加入 if (ss.getName().equals(this.getName())) { // 如果不是同一个学生,那么就可以根据分数对他们进行排序了 if (ss.getScore() >= this.getScore()) { r = -1; } else { r = 1; } } else { r = 0; } // 不是学生类型对象的话就不要加入它 } else { r = 0; } return r; } } 复制代码 - 根据compareTo()返回值进行插入
几种遍历
Iterator接口
- 作用:迭代Collection中的元素
public static void main(){ ArrayList list = new Arraylist(); list.add("data_1"); list.add("data_2"); Iterator it = list.iterator(); while(it.hasNext()){ //通过这种迭代方式获取的元素都会被看成obj,要获得指定类型需强转 Object obj = it.next(); System.out.print(obj); } } 复制代码 - 在迭代时,要删除元素必须不能使用list集合的remove方法,应为要是用list删除元素会导致迭代器的迭代索引发生改变,产生异常
- 解决办法
- 在要删除的那个元素找到后直接删除,然后break;退出循环即可
- 使用iterator的remnove方法
- 解决办法
- 这里能直接改变元素的值
foreach
1.格式
public static void main(){
ArrayList list = new Arraylist();
list.add("data_1");
list.add("data_2");
Iterator it = list.iterator();
//元素类型 零时变量 : 集合类型
for(Object obj : list){
System.out.print(obj);
}
}
复制代码
- foreach循环过程中改变元素的值:foreach通过零时变量来记住元素,因此在迭代中不能改变原集合中的值,只是将零时变量指向了别的地址
- foreach中的元素能不能变取决于要改变的元素是可变对象还是不可变对象
- 即new的对象不能变,而能通过get/set方法获取的就能改变(封装的重要性)
ListIterator
- 是Iterator的子类
- 实现了反向迭代,从后向前迭代
public static void main(){
ArrayList list = new Arraylist();
list.add("data_1");
list.add("data_2");
System.out.print(list);
//此处需要传入一个参数来确定迭代开始的位置,此处传递的是集合的长度
ListIterator it = list.ListIterator(list.size();
while(it.hasPrevious()){
//通过这种迭代方式获取的元素都会被看成obj,要获得指定类型需强转
Object obj = it.previous();
System.out.print(obj + " ");
}
}
复制代码
Enumeration
- Vector是List的子类,与ArrayList用法相同,但是线程安全
- Vector提供了elements方法返回Enumeration对象,通过该对象迭代集合元素
public static void main(){
Vector v = new Vector();
list.add("data_1");
list.add("data_2");
Enumeration en = v.elements();
while(en.hasMoreElements){
//通过这种迭代方式获取的元素都会被看成obj,要获得指定类型需强转
Object obj = it.nextElement();
System.out.print(obj);
}
}
复制代码




近期评论