Java基础之集合算法与数据结构

集合:存储批量对象,可以看成可变长度的数组。

Collection :单列集合 --List:有序可重复

 --ArrayList:底层数据结构为数组,元素连续,增删慢,查找修改快。(数据存放顺序为添加顺序)
 --LinkedList:底层数据结构为双向链表,元素可以不连续,增删快,查找修改慢。
遍历:for,for-each,迭代器

单项(向后)Iterator接口:hasNext()、next、remove

双向遍历ListIterator子接口:hasPrevious(指针当前元素前是否含有元素)、previous(返回所指元素前一项,指针上移)、add(添加)、set(替换)

 --Set:无序不可重复
  --HashSet:底层数据结构为哈希算法     hashCode,equals
  --TreeSet:底层数据结构为二叉树,按照自排序
               实现Comparable,并实现compareTo方法,根据方法的返回值的结果进行排序。
 add,remove,size
 <? extends E>: E类型及其子类型
 <? super E>: E类型及其父类型 

Map:双列集合
  存储具有映射关系的一对一对的数据,key---value
  key保证唯一,value可重复
 --Map:
  ---HashMap:key是按照哈希算法存储 hashCode equals
  ---TreeMap:key是按照二叉树存储的,key需要进行自然顺序排序  实现Comparable接口,实现CompareTo方法

Java.Map的三种遍历方法

package com.collection;

import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

/**
 * 集合的一个很重要的操作---遍历,学习了三种遍历方法,三种方法各有优缺点
 * @author chengjie
 */
public class TestMap {

    /**
     * 最常规的一种遍历方法,最常规就是最常用的,虽然不复杂但很重要,最熟悉的
     * values 返回包含映射内所有值得集合
     */
    public static void work(Map<String, Student> map) {
        Collection<Student> c = map.values();
        Iterator it = c.iterator();
        for (; it.hasNext();) {
            System.out.println(it.next());
        }
    }

    /**
     * 利用keyset进行遍历,它的优点在于可以根据你所想要的key值得到你想要的 values,更具灵活性
     * keySet 返回映射所有键的集合
     */
    public static void workByKeySet(Map<String, Student> map) {
        Set<String> key = map.keySet();
        for (Iterator it = key.iterator(); it.hasNext();) {
            String s = (String) it.next();
            System.out.println(map.get(s));
        }
    }

    /**
     * 比较复杂的一种遍历在这里,呵呵~~他很暴力哦,它的灵活性太强了,想得到什么就能得到什么
     * entrySet 返回包含所有键值对的集合
     */
    public static void workByEntry(Map<String, Student> map) {
        Set<Map.Entry<String, Student>> set = map.entrySet();
        for (Iterator<Map.Entry<String, Student>> it = set.iterator(); it.hasNext();) {
            Map.Entry<String, Student> entry = (Map.Entry<String, Student>) it.next();
            System.out.println(entry.getKey() + "--->" + entry.getValue());
        }
    }
    
    public static void main(String[] args) {
        Map<String, Student> map = new HashMap<String, Student>();
        Student s1 = new Student("宋江", "1001", 38);
        Student s2 = new Student("卢俊义", "1002", 35);
        Student s3 = new Student("吴用", "1003", 34);
        
        map.put("1001", s1);
        map.put("1002", s2);
        map.put("1003", s3);
        
        Map<String, Student> subMap = new HashMap<String, Student>();
        subMap.put("1008", new Student("tom", "1008", 12));
        subMap.put("1009", new Student("jerry", "1009", 10));
        map.putAll(subMap);
        
        work(map);
        workByKeySet(map);
        workByEntry(map);
    }
}

class Student {
    private String name;
    private String id;
    private int age;
    
    public Student(String name, String id, int age) {
        this.name = name;
        this.id = id;
        this.age = age;
    }
    
    @Override
    public String toString() {
        return "Student{" + "name=" + name + "id=" + id + "age=" + age + '}';
    }
}

Set集合数据结构

TreeSet:底层数据结构为:二叉树,将元素按照自然顺序顺序存放。

  存储自定义对象时,必须实现Comparable接口,并实现该接口的compareTo方法

  根据该方法的返回值进行自然顺序排序,

  返回值为正整数,负整数,0,如果是0意味着是相同元素。

 HashSet:底层数据结构为:哈希算法。

  每个要存储的对象都会先计算哈希码值(hashCode方法),如果哈希值不同则存入到集合中;如果哈希值相同,会通过equals方法进行第二次校验,比较是否是相同对象。

package com.collection;

import java.util.TreeSet;

public class TreeSetDemo {
    public static void main(String[] args) {
        TreeSet<Teacher> set = new TreeSet<Teacher>();
        set.add(new Teacher("lisi01", 18));
        set.add(new Teacher("lisi11", 22));
        set.add(new Teacher("lisi21", 19));
        set.add(new Teacher("lisi05", 19));
        System.out.println(set);
    }
}

class Teacher implements Comparable<Teacher>{
    private String name;
    private int age;

    public Teacher(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
    @Override
    public String toString() {
        return name+"...."+age;
    }

    public int compareTo(Teacher o) {
        int num = this.age - o.age;
        if(num == 0)
            return this.name.compareTo(o.name) ;
        return num;
    }
}
View Code

HashSet哈希算法实现原理剖析:

任何对象均有自己的哈希值,该值通过调用对象的hashCode()方法(继承自Object类)获得。

Object类:是所有对象的直接或者是间接根类;boolean equals(Object obj):比较两个对象是否相同;int hashCode():返回的是对象的内在散列码值(哈希值)

注意:哈希值相同不能说明是对象相同,对象相同那么哈希值一定相同。

package com.collection;

/**
 * 自定义类对其对象等同性比较时自行实现equals方法
 * @author chengjie
 *
 */
public class MyDate {
    private int day;
    private int month;
    private int year;
    
    public MyDate(int day, int month, int year) {
        this.day = day;
        this.month = month;
        this.year = year;
    }

    @Override
    public boolean equals(Object obj) {
        boolean result = false;
        if ((obj != null) && (obj instanceof MyDate)) {
            MyDate d = (MyDate) obj;
            if ((day == d.day) && (month == d.month) && (year == d.year)) {
                result = true;
            }
        }
        return result;
//        if (this == obj)
//            return true;
//        if (obj == null)
//            return false;
//        if (getClass() != obj.getClass())
//            return false;
//        MyDate other = (MyDate) obj;
//        if (day != other.day)
//            return false;
//        if (month != other.month)
//            return false;
//        if (year != other.year)
//            return false;
//        return true;
    }
    
    /** date属性按位异或,保证相等的MyDate对象具有相同的散列码值,不同日期返回不同的值  */
    @Override
    public int hashCode() {
        return (day << 4 ^ month ^ year);
    }
}

当向HashSet集合中添加元素时:

  如果数组该index位置没有元素,则直接将该元素添加至该位置;
  如果数组该index位置已经有元素,则调用该元素的equals方法进行判断;
  如果该方法返回true,则认为两个元素相等,重复了,不再添加;如果该方法返回false,则认为两个元素不等,扩大数组的长度,重新进行添加。
如果2个实例,调用equals方法返回true,但是调用hashCode方法返回的hashCode值不同, 2 3
上面的机制能否确保添加的元素都不重复?答案是不能
如何避免?
  调用equals方法返回true的两个实例,调用hashCode方法,应该返回相同的int值。
HashSet哈希算法实现原理具体代码如下:
import java.util.Arrays;

public class HashSetDemo {

    public static Phone[] phones=new Phone[9];

    public static void main(String[] args) {

        Phone p1=new Phone("apple", "iphone7", 32, 128);
        Phone p2=new Phone("apple", "iphone7", 32, 128);
        Phone p3=new Phone("apple", "iphone7", 64, 128);
        Phone p4=new Phone("apple", "iphone7", 64, 64);
        Phone[] array={p1,p2,p3,p4};
        for(Phone p:array){
            add(p);
        }
    }

    public static void add(Phone p){
        System.out.println("-----------------添加1个元素--------------------");
        int hashCode=p.hashCode();
        System.out.println("调取p的hashCode方法,得到的值是:"+hashCode);
        int index=hashCode%phones.length;
        System.out.println("用hashCode对数组长度取余,得到下标:"+index);
        boolean flag=phones[index]==null?true:false;
        System.out.println("判断数组对应位置是否有元素:"+(flag?"没有元素":"有元素"));
        if(flag){//没有元素
            System.out.println("直接执行添加操作");
            phones[index]=p;
            System.out.println("添加完成");
        }else{//有元素
            boolean flag2=phones[index].equals(p);
            System.out.println("调用equals方法,返回值为:"+flag2);
            if(flag2){
                System.out.println("两个元素相等,说明重复,不再添加");
            }else{
                System.out.println("两个元素不等,扩大数组长度,重新尝试添加");
                phones=Arrays.copyOf(phones, phones.length*2);
            }
        }
        System.out.println("-----------------完成1次方法调用--------------------");
    }
}

 

posted @ 2017-11-23 19:39  jxtx92  阅读(413)  评论(0)    收藏  举报