Java集合类总结Collection

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

Collection

  1. collection的结构

List

  1. list集合的特点
    • 元素有序
    • 以线性方式存储,可以用索引来获取元素
  2. ArrayList
    • 元素有序可重复
    • 内部封装了一个长度可变的数组,当添加或者修改的时候会导致创建新的数组,所以适合查找,不适合大量的增删
    ArrayList list = new Arraylist();
    复制代码
  3. LinkedList
    • 元素有序可重复
    • 内部维护了一个双向链表,适用于增删操作
    LinkedList link = new LinkedList();
    复制代码

Set

  • 无序不重复
  • 不重复通过哈希来实现
  1. HashSet
  • 参考:www.cnblogs.com/runwulingsh…

  • 适合存取与查找

    1. 语法
    #1传入字符串
    HashSet set = new HashSet();
    
    #2传入引用类型
    //String类里实现了euqals方法,而自己的类里未创建,就需要自己重写hashcode()和equals()方法
    
    复制代码
    1. hashcode()方法产生相同的值但可能是不同的对象
      • 如果在自定义的类中没有重写hashcode(),那么,返回的则是object.hashcode(),即该对象的地址解析的哈希值
      • 如果重写了就可以自定义返回属性的hash值,比如name.hashcode,判断name的hashcode是否相同,name的值相同hashcode就相同,所以哈希值相同不代表是同一个对象
      • 两个对象重复的要素:哈希值相同,属性相同
        • equals方法默认等于==,也就是比较两个对象的地址,这个方法的用处就是让你重写,进行自定义比较,而String类里面已经重写过equals方法,比较两个值是否相等
        • 当只重写了hashcode方法时,equals没重写,就会导致系统认为这两个对象是不相同
        • 必须满足上面两个要素,才会认为对象重复
        • 只需要比较其中一个元素相同即可,其他元素不同也没事
      • equals的作用:区分哈希码相同,但不是同一个对象的对象
    2. 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);
     }
    }
    复制代码
  1. treeSet
  • 内部采用平衡二叉树实现
  • 适用于排序
  • 当插入数据的时候会使用compareTo()进行比较,而自己定义的类里需要按自己的规则重写compareTo()方法
    1. 根据compareTo()返回值进行插入
      • 返回1,当前值会被排在比较值的后面
      • 返回0,认为是相同元素不会插入
      • 返回-1,查到比较值前面
    2. 可以继承Comparable重写compareto,也可以继承Comparable然后自己写一个方法,就可以根据返回值进行排序(正数,复数,0)
    3. 实例
       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;
        }
       }
    
    复制代码

几种遍历

Iterator接口

  1. 作用:迭代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);
        }
    }
    复制代码
  2. 在迭代时,要删除元素必须不能使用list集合的remove方法,应为要是用list删除元素会导致迭代器的迭代索引发生改变,产生异常
    • 解决办法
      • 在要删除的那个元素找到后直接删除,然后break;退出循环即可
      • 使用iterator的remnove方法
  3. 这里能直接改变元素的值

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);
    }
}
复制代码
  1. foreach循环过程中改变元素的值:foreach通过零时变量来记住元素,因此在迭代中不能改变原集合中的值,只是将零时变量指向了别的地址
    • foreach中的元素能不能变取决于要改变的元素是可变对象还是不可变对象
    • 即new的对象不能变,而能通过get/set方法获取的就能改变(封装的重要性)

ListIterator

  1. 是Iterator的子类
  2. 实现了反向迭代,从后向前迭代
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

  1. Vector是List的子类,与ArrayList用法相同,但是线程安全
  2. 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);
       }
   }
复制代码