Java学习记录8 容器

Posted chendejian

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java学习记录8 容器相关的知识,希望对你有一定的参考价值。

 

1. 容器架构介绍

容器

  1. 数组就是一种容器,可以在其中放置对象或基本数据类型。

  2. Set没有顺序不可重复,list有顺序可以重复。

数组的优劣势

  1. 优势:是一种简单的线性序列,可以快速的访问数组元素,效率高。从效率和类型检查的角度讲,数组是最好的。

  2. 劣势:不灵活。容量事先定义好,不能随着需求的变化而扩容。

2. 泛型概念-自定义泛型

  1. 泛型是JDK1.5以后增加的,它可以帮助我们建立类型安全的集合。

  2. 相当于容器的标签。

  3. 泛型的本质就是数据类型的参数化,可以理解为占位符(形式参数),即告诉编译器,在调用泛型时必须传入实际类型。

  4. 一般用T,E,V三个

  5. 容器相关类都定义了泛型。

  6. 要导入:java.util.List

    public static void main(String[] args) {
    MyCollection<String> mc = new MyCollection<String>();
    mc.set("cdj", 0);
    mc.set("8888", 1);

    String a = mc.get(1);
    String b = mc.get(0);

    System.out.println(a);
    System.out.println(b);
    }
    ?
    class MyCollection<E>{//MyCollection<String> mc = new MyCollection<String>();这里是啥类型,E就是啥类型
    Object[] objs = new Object[5];

    public void set(E e,int index){
    objs[index] = e;
    }

    public E get(int index){
    return (E)objs[index];
    }
    }

3. Collection接口

  1. Collection表示一组对象,他是集中、收集的意思。Collection接口的两个子接口是List、Set接口。

    ArrayList的常用方法,set和list方法一致

            Collection<String> c = new ArrayList<String>();
    System.out.println(c.size());//内容的大小
    System.out.println(c.isEmpty());//判断容器是否为空

    c.add("cdj");//容器添加元素
    c.add("cdj2");

    System.out.println(c);//默认toString

    System.out.println(c.size());

    System.out.println(c.contains("cdj"));//判断容器中是否包含某个字符串
    Object[] objs = c.toArray();//返回一个object数组
    System.out.println(objs);


    c.remove("cdj2");//从容器中移除元素,并不是删除这个对象,但是这个对象还在
    System.out.println(c);

    c.clear();//移除所有的元素
    System.out.println(c);
  2. ArrayList操作多个List-并集和交集

    List<String> list01 = new ArrayList<String>();
    list01.add("aa");
    list01.add("bb");
    list01.add("cc");

    List<String> list02 = new ArrayList<String>();
    list02.add("aa");
    // list02.add("dd");
    // list02.add("ee");

    System.out.println("list01:"+list01);
    //把list02中所有的元素都放到了list01中了
    //list01.addAll(list02);
    //把list01中与list02中的相同的元素去掉了
    //list01.removeAll(list02);
    //把list01中与list02中的不同的元素去掉了
    list01.retainAll(list02);
    System.out.println("list01:"+list01);
    //判断list01是否包含list02,是true,不是false
    System.out.println(list01.containsAll(list02));
  3. ArrayList索引和顺序相关方法

    1. List是有序、可重复的容器。

    2. 有序:List中每个元素都有索引标记,可以根据元素的索引标记(在List中的位置)访问元素,从而精确控制这些元素。

    3. 可重复:List允许加入重复的元素。List通常允许满足e1.equals(e2)的元素重复加入容器。

    4. List接口常用的实现类有3个:ArrayList、LinkedList和Vector。

    5. ArrayList底层是数组,LinkedList底层是链表,Vector的底层是数组,线程安全。

      List<String> list = new ArrayList<String>();
      list.add("A");
      list.add("B");
      list.add("C");
      list.add("D");
      //索引是从0开始的
      System.out.println(list);
      //往索引为2的位置插入一个元素
      list.add(2, "cdj");
      System.out.println(list);
      //删除索引位置的元素
      list.remove(2);
      System.out.println(list);
      //修改指定索引位置的元素
      list.set(2, "cdj");
      System.out.println(list);
      //获取指定索引位置处元素
      System.out.println(list.get(2));
      //返回指定元素第一次索引位置,查找不到返回-1
      list.add("C");
      list.add("B");
      list.add("A");
      System.out.println(list);
      System.out.println(list.indexOf("B"));
      System.out.println(list.indexOf("1B"));
      //返回指定元素最后一次索引位置,查找不到返回-1
      System.out.println(list.lastIndexOf("B"));
      System.out.println(list.lastIndexOf("B1"));
    6. ArrayList底层源码分析

      1. ArrayList底层是用数组实现的存储。

      2. 特点:查询效率高,增删效率低,线程不安全。一般使用。

      3. 数组长度是有限的,而ArrayList是可以存放任意数量的对象,长度不受限制,采用数组扩容(定义一个更大的数组,把原内容拷贝到这个数组)的方式实现。

    7. 手工实现ArrayList:(暂时不做)

LinkedList

  1. LinkedList底层用双向链表实现的存储。

  2. 特点查询效率低,增删效率高,线程不安全。

  3. 双向链表也叫双链表,是链表的一种,它的每个数据节点中都有两个指针,分别指向前一个节点和后一个节点。所以,从双向链表中的任意一个节点开始,都可以很方便的找到所有节点。

  4. 每个节点有3部分内容

   class Node{
      Node previous; //前一个节点
      Object element; //本节点保存的数据
      Node  next;//后一个节点
  }
  1. 手工实现LinkedList(暂时不做)

Vector向量

  1. 底层是用数组实现的List,相关方法都加了同步检查,因此线程安全,效率低。比如indexOf方法就增加了synchronized同步标记。

  2. 需要线程安全用Vector,对象需要多个线程共享时使用。

  3. 不存在线程安全问题时,并且查找较多用ArrayList(一般使用)。

  4. 不存在线程安全问题时,增删元素较多用LinkedList。

 

4. map接口

  1. Map就是用来存储“键-值对”的。

  2. Map类中存储的“键值对”通过键来标识,所以“键对象”不能重复。

  3. Map接口的实现类有HashMap、TreeMap、HashTable、Properties等。

  4. Map接口常用方法:

    Map<Integer,String> m1 = new HashMap<Integer,String>();
    //添加元素
    m1.put(1, "one");
    m1.put(2, "two");
    m1.put(3, "three");
    //获取元素
    System.out.println(m1.get(1));
    //m1的大小
    System.out.println(m1.size());
    //判断m1是否为空
    System.out.println(m1.isEmpty());
    //判断m1中是否包含键值
    System.out.println(m1.containsKey(1));
    //判断m1中是否包含Value值
    System.out.println(m1.containsValue("one"));

    Map<Integer,String> m2 = new HashMap<Integer,String>();
    m2.put(4, "四");
    m2.put(5, "五");
    //把m2加入m1中
    m1.putAll(m2);
    System.out.println(m1);

    //map中键不能重复,如果重复(是否重复是根据equals方法来判断的),则新的覆盖旧的。
    m1.put(3, "三");
    System.out.println(m1);
  5. HashMap的底层原理-存储键值对底层过程

    1. HashMap底层实现采用了哈希表,这是一种非常重要的数据结构。

    2. 哈希表的基本结构就是“数组+链表”。

    3. Entry[] table 是HashMap的核心数组结构,称为位桶数组,JDK1.8位Node

    4. 一个Entry对象存储了:

      1. key:键对象 value:值对象

      2. next:下一个节点

      3. hash:键对象的hash值

    5. 每一个Entry对象就是一个单向链表结构。

    6. 数组默认出始大小为16:0-15。

    7. 存储数据过程put(key,value):核心是如何产生hash值,该值用来对应数组的存储位置。

    8. 将“key-value两个对象”成对存放到HashMap的Entry[]数组中的步骤:

      1. 获得key对象的hashcode

        1. 首先调用key对象的hashcode()方法,获得hashcode。

      2. 根据hashcode计算出hash值(要求在[0,数组长度-1]区间)

        1. hashcode是一个整数,我们需要将它转换成[0,数组长度-1]的范围。要求转化后的hash值尽量均匀的分布在0-1之间,减少“hash冲突”。

          1. 一种极端简单和低下的算法:hash值 = hashcode/hashcode 。hash值总是1。意味着,键值对象都会存储到数组索引1位置,这样就形成一个非常长的链表。相当于每存储一个对象都会发生“hash冲突”,HashMap也退化成了一个链表。

          2. 一种简单和常用的算法(相除取余算法):hash值 = hashcode%数组长度,可以让hash值均匀分布在0-1之间,早期的HashTable就是采用这种算法。

          3. 改进算法:hash值 = hashcode&(数组长度-1)

        2. JDK1.8以后,长度大于8的,链表变成红黑树。

      3. 查找键值对过程:

        1. 获得key的hashcode,通过hash()散列算法得到hash值,进而定位到数组的位置。

        2. 在链表上挨个比较key对象。调用equals()方法,将key对象和链表上所有节点的key对象进行比较,直到碰到返回true的节点对象为止。

        3. 返回equals()为true的节点对象的value对象。

        注:hashcode()和equals方法的关系:Java规定,两个内容相同(equals()为true)的对象必须具有相等的hashCode。因为如果equals()为true而两个对象的hashcode不同,那在整个存储过程中就发生了悖论。

      4. 扩容问题:位桶数组出始大小为16,实际使用时,大小可变。如果数组中的元素达到(0.75*数组 length),就重新调整数组大小变为原来2倍大小。扩容很耗时。扩容的本质是定义新的更大的数组,并将旧数组内容挨个拷贝到新数组中。

      public static void main(String[] args) {
      Employee e1 = new Employee(1001, "cdj", 50000);
      Employee e2 = new Employee(1002, "cdj1", 5000);
      Employee e3 = new Employee(1003, "cdj2", 60000);
      Employee e4 = new Employee(1001, "cdj3", 6000);

      Map<Integer,Employee> map = new HashMap<>();
      map.put(1001, e1);
      map.put(1002, e2);
      map.put(1003, e3);
      map.put(1001, e4);

      Employee emp = map.get(1001);
      System.out.println(emp.getEname());
      System.out.println(map);

      }
      ?
      class Employee{
      private int id;
      private String ename;
      private double salary;
      public Employee(int id, String ename, double salary) {
      super();
      this.id = id;
      this.ename = ename;
      this.salary = salary;
      }

      @Override
      public String toString() {

      return "id:"+id+"name:"+ename+"薪水:"+salary;
      }
      public int getId() {
      return id;
      }
      public void setId(int id) {
      this.id = id;
      }
      public String getEname() {
      return ename;
      }
      public void setEname(String ename) {
      this.ename = ename;
      }
      public double getSalary() {
      return salary;
      }
      public void setSalary(double salary) {
      this.salary = salary;
      }


      }
      ?

    TreeMap

    1. TreeMap是红黑二叉树的典型实现。

    2. root用来存储整个树的根节点。

    3. TreeMap和HashMap实现了同样的接口Map。HashMap效率高于TreeMap;在需要排序的Map时才选用TreeMap。

    4. HashMap与HashTable的区别:

      1. HashMap:线程不安全,效率高。允许key或value为null。

      2. HashTable:线程安全,效率低。不允许key或value为null。

    public static void main(String[] args) {
    Map<Integer,String> tm1 = new TreeMap<>();

    tm1.put(20, "aa");
    tm1.put(3, "bb");
    tm1.put(6, "cc");
    //按照key递增的方式排序
    for(Integer key:tm1.keySet()){
    System.out.println(key + "-------"+tm1.get(key));
    }

    Map<Emp,String> mp2 = new TreeMap<>();
    mp2.put(new Emp(100, "cdj1",3000),"cdj是个");
    mp2.put(new Emp(20, "cdj2",3000),"cdj是个1");
    mp2.put(new Emp(150, "cdj3",1000),"cdj是个2");

    //按照key递增的方式排序
    for(Emp key:mp2.keySet()){
    System.out.println(key+"-----"+mp2.get(key));
    }
    }
    ?
    class Emp implements Comparable<Emp>{
    int id;
    String name;
    double salary;
    //按照salary排序,通过实现comparable接口
    public Emp(int id, String name, double salary) {
    super();
    this.id = id;
    this.name = name;
    this.salary = salary;
    }

    @Override
    public String toString() {

    return "id:"+id+",name:"+name+",salary:"+salary;
    }

    @Override
    public int compareTo(Emp o) {//负数:小于,0:等于,正数:大于

    if(this.salary > o.salary)
    {
    return 1;
    }
    else if(this.salary < o.salary)
    {
    return -1;
    }
    else{
    if(this.id > o.id){
    return 1;
    }
    else if(this.id<o.id)
    {
    return -1;
    }
    else
    {
    return 0;
    }
    }

    }


    }
    ?

5. Set接口

HashSet

  1. 继承collection接口。

  2. 没有顺序不可重复。

  3. 无序指的是Set中的元素没有索引,只能遍历查找。不可重复指不允许加入重复的元素。更确切地讲,新元素如果和Set中某个元素通过equals()方法对比为true,则不能加入。set中只能放入一个null,不能多个。

  4. Set常用的实现类有:HashSet、TreeSet等,一般使用HashSet。

  5. HashSet是采用哈希算法实现的,底层实际使用HashMap实现的(HashSet本质就是一个简化版HashMap),因此查询效率和增删效率都比较高。

  6. 核心属性map

    //Set:没有顺序,不可重复
    		//List:有顺序,可重复
    		Set<String> set1 = new HashSet<String>();
    		
    		set1.add("aa");
    		set1.add("bb");
    		set1.add("aa");
    		
    		System.out.println(set1);
    		
    		set1.remove("bb");
    		System.out.println(set1);
    		
    		Set<String> set2 = new HashSet<>();
    		set2.add("cdj");
    		set2.addAll(set1);
    		
    		System.out.println(set2);

TreeSet

  1. 底层使用TreeMap实现的,内部维持了一个简化版的TreeMap

  2. 实现Comparable接口,可以实现自定义类比较大小。

public static void main(String[] args) {
		Set<Integer> set = new TreeSet<>();
		set.add(300);
		set.add(200);
		set.add(600);
		//按照元素递增的方式排序
		for(Integer m:set){
			System.out.println(m);
		}
		
		Set<Emp2> set1 = new TreeSet<>();
		set1.add(new Emp2(100,"cdj1",3000));
		set1.add(new Emp2(50,"cdj2",2000));
		set1.add(new Emp2(150,"cdj3",8000));
		set1.add(new Emp2(30,"cdj4",20000));
		
		//按照元素递增的方式排序
		for(Emp2 m:set1){
			System.out.println(m);
		}
	}

class Emp2 implements Comparable<Emp2>{
	int id;
	String name;
	double salary;
	//按照salary排序,通过实现comparable接口
	public Emp2(int id, String name, double salary) {
		super();
		this.id = id;
		this.name = name;
		this.salary = salary;
	}
	
	@Override
	public String toString() {
		
		return "id:"+id+",name:"+name+",salary:"+salary;
	}
	
	@Override
	public int compareTo(Emp2 o) {//负数:小于,0:等于,正数:大于
		
		if(this.salary > o.salary)
		{
			return 1;
		}
		else if(this.salary < o.salary)
		{
			return -1;
		}
		else{
			if(this.id > o.id){
				return 1;
			}
			else if(this.id<o.id)
			{
				return -1;
			}
			else
			{
				return 0;
			}
		}
		
	}
	
	
}

 

6. 迭代器的使用(Iterator)遍历容器元素(List/Set/Map)

  1. 遍历List

    List<String> list = new ArrayList<>();
    		list.add("aa");
    		list.add("bb");
    		list.add("cc");
    		//使用iterator遍历List
    		for(Iterator<String> iter = list.iterator();iter.hasNext();){
    			String temp = iter.next();
    			System.out.println(temp);
    		}
  2. 遍历Set

    Set<String> set = new HashSet<>();
    		set.add("aa");
    		set.add("bb");
    		set.add("cc");
    		//使用iterator遍历List
    		for(Iterator<String> iter = set.iterator();iter.hasNext();){
    			String temp = iter.next();
    			System.out.println(temp);
    		}
  3. 遍历Map

    Map<Integer,String> map = new HashMap<>();
    		
    		map.put(100, "aa");
    		map.put(200, "bb");
    		map.put(300, "cc");
    		//第一种遍历Map的方式
    		Set<Entry<Integer,String>> set = map.entrySet();
    		for(Iterator<Entry<Integer, String>> iter = set.iterator();iter.hasNext();){
    			Entry<Integer, String> temp = iter.next();
    			System.out.println(temp.getKey()+"---"+temp.getValue());
    		}
    		System.out.println("***************");
    		//第二种遍历Map的方式
    		Set<Integer> ss = map.keySet();
    		
    		for(Iterator<Integer> iter = ss.iterator();iter.hasNext();){
    			Integer key = iter.next();
    			System.out.println(key + "---"+map.get(key));
    		}

7. 遍历各种容器的方法总结

  1. 遍历List方法

    public static void testList(){
    		List<String> list = new ArrayList<>();
    		list.add("aa");
    		list.add("bb");
    		list.add("cc");
    		//方法一:普通for循环
    		for(int i=0;i<list.size();i++){
    			System.out.println(list.get(i));
    		}
    		System.out.println("*****");
    		//方法二:增强for循环
    		for(String temp:list){
    			System.out.println(temp);
    		}
    		System.out.println("*****");
    		//方法三:使用Iterator迭代器和for循环
    		for(Iterator<String> iter = list.iterator();iter.hasNext();){
    			String temp = iter.next();
    			System.out.println(temp);
    		}
    		System.out.println("*****");
    		//方法四:使用Iterator迭代器和whiler循环
    		Iterator<String> iter = list.iterator();
    		while(iter.hasNext()){
    			String temp = iter.next();
    			iter.remove();//如果要遍历删除集合中的元素,使用这种方式
    			System.out.println(temp);
    			System.out.println(list);
    		}
    	}
  2. 遍历Set方法

    public static void testSet(){
    		Set<String> set = new HashSet<>();
    		
    		set.add("aa");
    		set.add("bb");
    		set.add("cc");
    		
    		//方法一:增强for循环
    		for(String temp:set){
    			System.out.println(temp);
    		}
    		System.out.println("******");
    		//方法二:使用Iterator迭代器
    		for(Iterator<String> iter = set.iterator();iter.hasNext();){
    			String temp = iter.next();
    			System.out.println(temp);
    		}
    	}
  3. 遍历Map

    public static void testMap(){
    		Map<Integer, String> map = new HashMap<>();
    		
    		map.put(100, "aa");
    		map.put(200, "bb");
    		map.put(300, "cc");
    		
    		//方法一:根据key获取value(一般使用)
    		Set<Integer> set = map.keySet();
    		
    		for(Integer i:set){
    			System.out.println(i+"---"+map.get(i));
    		}
    		System.out.println("*****");
    		
    		Set<Entry<Integer, String>> set1 = map.entrySet();
    		
    		for(Iterator<Entry<Integer, String>> iter = set1.iterator();iter.hasNext();){
    			Entry<Integer, String> temp = iter.next();
    			System.out.println(temp.getKey()+"---"+temp.getValue());
    		}
    	}

8. Collections工具类

  1. Collection是接口,Collections是辅助类

    List<String> list = new ArrayList<>();
    		
    		for(int i=0;i<10;i++){
    			list.add("cdj:"+i);
    		}
    		System.out.println(list);
    		Collections.shuffle(list);//随机排列list中的元素
    		System.out.println(list);
    		
    		Collections.reverse(list);//list逆序排列
    		System.out.println(list);
    		
    		Collections.sort(list);//按照递增方式排序.自定义的类使用Comparable接口。
    		System.out.println(list);
    		
    		//二分法查找元素
    		System.out.println(Collections.binarySearch(list, "cdj:0"));

 

9. 表格数据存储-map和list结合存储整张表

  1. 每一行数据用一个Map,整个表格用一个List。

  2. ORM思想:对象关系映射。

    Map<String, Object> row1 = new HashMap<>();
    		row1.put("id", 1001);
    		row1.put("name","cdj");
    		row1.put("salary", 20000);
    		row1.put("入职日期", "2018.5.5");
    		
    		Map<String, Object> row2 = new HashMap<>();
    		row2.put("id", 1002);
    		row2.put("name","cdj2");
    		row2.put("salary", 30000);
    		row2.put("入职日期", "2005.4.4");
    		
    		Map<String, Object> row3 = new HashMap<>();
    		row3.put("id", 1003);
    		row3.put("name","cdj2");
    		row3.put("salary", 3000);
    		row3.put("入职日期", "2020.5.4");
    		
    		List<Map<String, Object>> table1 = new ArrayList<>();
    		
    		table1.add(row1);
    		table1.add(row2);
    		table1.add(row3);
    		
    		for(Map<String, Object> row:table1){
    			Set<String> keyset = row.keySet();
    			for(String key:keyset){
    				System.out.print(key+":"+row.get(key)+"	");
    			}
    			System.out.println();
    		}

10. 表格数据存储-javabean和list结合存储整张表

  1. 每一行数据用一个javabean对象,整个表格用一个List/Map。

    public static void main(String[] args) {
    		User user1 = new User(1001,"cdj1",20000,"2018.5.5");
    		User user2 = new User(1002,"cdj2",30000,"2015.5.5");
    		
    		List<User> list = new ArrayList<>();
    		
    		list.add(user1);
    		list.add(user2);
    		
    		for(User u:list){
    			System.out.println(u);
    		}
    		
    		System.out.println("*****");
    		
    		Map<Integer, User> map = new HashMap<>();
    		
    		map.put(1, user1);
    		map.put(2, user2);
    		
    		Set<Integer> set = map.keySet();
    		for(Integer i:set){
    			System.out.println(i+"---"+map.get(i));
    		}
    		
    		
    	}
    
    class User{
    	private int id;
    	private String name;
    	private double salary;
    	private String hiredate;
    	
    	//一个完整的javabean。要有set和get方法,以及无参构造器!
    	public User() {
    		
    	}
    	public User(int id, String name, double salary, String hiredate) {
    		super();
    		this.id = id;
    		this.name = name;
    		this.salary = salary;
    		this.hiredate = hiredate;
    	}
    	
    	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 double getSalary() {
    		return salary;
    	}
    	public void setSalary(double salary) {
    		this.salary = salary;
    	}
    	public String getHiredate() {
    		return hiredate;
    	}
    	public void setHiredate(String hiredate) {
    		this.hiredate = hiredate;
    	}
    	
    	@Override
    	public String toString() {
    		// TODO Auto-generated method stub
    		return "id:"+id+"name:"+name+"salary:"+salary+"hiredate:"+hiredate;
    	}
    	
    }
    

     

以上是关于Java学习记录8 容器的主要内容,如果未能解决你的问题,请参考以下文章

错误记录Android Studio 编译报错 ( Could not determine java version from ‘11.0.8‘. | Android Studio 降级 )(代码片段

错误记录Android Studio 编译报错 ( Could not determine java version from ‘11.0.8‘. | Android Studio 降级 )(代码片段

java SpringRetry学习的代码片段

Java基础学习--对象容器

201521123089 《Java程序设计》第8周学习总结

ElasticSearch学习问题记录——Invalid shift value in prefixCoded bytes (is encoded value really an INT?)(代码片段