Java数据集合应用

数据类型

数据结构:
  1.基本构成
    数组和链表
  2.具体的数据结构
    动态数组         ArrayList 
    队列              Queue接口作为队列数据结构   LinkedList  LinkedList类实现了Queue接口和 Deque
    双端队列          Deque接口(双端队列) Deque接口所能代表的数据结构:队列,双端队列,栈 ArrayDeque
                      栈而言,有入栈(push)和出栈(pop),遵循先进后出原则,将Deque限制为只能从一端入队和出队,则可实现栈的数据结构
    哈希表           HashMap HashSet  LinkedHashMap (顺序地去存储key-value 保存了记录的插入顺序) LinkedHashSet
    排序二叉树       TreeMap TreeSet
    堆-完全二叉树    PriorityQueue   优先级队列,但也是比较通用的实现了堆性质的数据结构
    位向量           EnumSet 是用位向量实现的
    B-tree           数据库中的索引常用-- 多路搜索树  带有顺序访问指针的B+Tree
    bitMap

Map数据类型

1.Map填充数据- 一行的是一个value
  其中 : List<Row> TestRow
  //步骤一: 声明变量,生成实例
      Map<String ,ExpBean> setBean = new HashMap<>();
   if(TestRow != null && !TestRow.isEmpty()) {    
      for (Row row : TestRow) {
         String testCd= row.getAs("testCd").toString().toLowerCase();
         // 一行数据生成一个testDataBean
         ExpBean   testDataBean = new ExpBean();
         setBean.put(testCd,testDataBean);
         }
     }

2.Map填充数据- 一类的是一个value
    其中 : List<Row> TestRow
    //步骤一: 声明变量,生成实例
    Map<String ,ExpBean> setBean = new HashMap<>();
    if(TestRow != null && !TestRow.isEmpty()) {    
        for (Row row : TestRow) {
            String testCd= row.getAs("testCd").toString().toLowerCase();
        //步骤二:containsKey 作为判断的条件
         if(setBean.containsKey(testCd)) {
            //提取ExpBean,并且调用ExpBean的方法
            ExpBean testDataBean  = setBean.get(testCd);
            testDataBean.deal(row);
           }else{
              //不存在则生成ExpBean, 并且调用ExpBean的方法
              ExpBean testDataBean = new ExpBean();
              testDataBean.deal(row);
              //将对应的ExpBean 实例存放到Map中去
              setBean.put(testCd,testDataBean);
            }
         }
     }

3.另外一种实现
    其中 : List<Row> TestRow
    //步骤一: 声明变量,生成实例
    Map<String ,ExpBean> setBean = new HashMap<>();
    if(TestRow != null && !TestRow.isEmpty()) {    
        for (Row row : TestRow) {
            String testCd= row.getAs("testCd").toString().toLowerCase();
        //步骤二:先使用get来取数据,一类数据生成一个testDataBean
            ExpBean testDataBean  = setBean.get(testCd);
        //步骤三: 判断是否为空,不为空,则代表有,是空,则代表没有,新生成相应的对象
            if(testDataBean == null) {
                testDataBean = new ExpBean();
                setBean.put(testCd,testDataBean);
            }
        //步骤四: Bean对象的操作
            testDataBean.deal(row);
            }
    }

Map遍历的实现

1.Map遍历的方式一
1.Iterator来遍历 
// 获取键值对的迭代器  Iterator<Entry<String, String>> it = strMap.entrySet().iterator();
// 获取键集合的迭代器  Iterator<String>  it = strMap.keySet().iterator();
// 获取值集合的迭代器  Iterator<String>  it = strMap.values().iterator();
 遍历HashMapentrySet键值对集合
  1.通过HashMap.entrySet()得到键值对集合;
  2.通过迭代器Iterator遍历键值对集合得到key值和value值;
  eg
   Map<String, String> strMap = new HashMap<String, String>();
 if(!strMap.isEmpty()) {
    // 获取键值对的迭代器
   Iterator<Entry<String, String>> it = strMap.entrySet().iterator();
   while (it.hasNext()) {
                   Entry<String, String> en = it.next();
                   String key = en.getKey();
                   String stb = en.getValue();
                System.out.println("key:" + key + "---" + "value:" + value);
    }
  }
遍历方式二
2.for循环遍历
   //  strMap.entrySet()  strMap.keySet()  strMap.values()
   //
 eg:
   // Set<Map.Entry<String, String>> entrySet = strMap.entrySet();//把这个set取出来
    for (Map.Entry<String, String> entry : strMap.entrySet()) {/*---*/
        System.out.print("key= " + entry.getKey() + " and value= " + entry.getValue() + "; ");
    }

进一步的方法

 /**
 * @since 1.8
 */
  default V getOrDefault(Object key, V defaultValue) {
    V v;
    return (((v = get(key)) != null) || containsKey(key))
        ? v
        : defaultValue;
   }
   forEach
   putIfAbsent
   merge
 类定义
    public class HashMap<K,V>       extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable {}
    public class LinkedHashMap<K,V> extends HashMap<K,V>     implements Map<K,V>{}
    public class TreeMap<K,V>       extends AbstractMap<K,V> implements NavigableMap<K,V>, Cloneable, java.io.Serializable{}
    其中 SortedMap 提供排序接口  
     public interface NavigableMap<K,V> extends SortedMap<K,V> {
     public interface SortedMap<K,V> extends Map<K,V> {}

Set

HashSet: 没有重复,且没有顺序
    HashSet以及HashMap要求元素重写 hashCode和equals方法,
      如果两个对象equals相同,则hashCode必须相同,自定义尤其要注意
    HashSet内部是用HashMap实现的,内部有一个HashMap的实例对象 ,HashSet相当于只有键,而值是相同的
    这个value值是 private static final Object PRESENT = new Object(); //固定的共享值
LinkedHashSet 介于HashSet和TreeSet之间。它也是一个hash表 --输出的顺序时确定的,就是插入的顺序。
TreeSet :有顺序

Set<String> defineClassSet = new HashSet<>();
遍历的方法
    用Iterator来遍历 
       Iterator<> iterator =  defineClassSet.iterator();
       While(iterator.hasNext()){
         System.out.println( iterator.next());
       }
    用for循环遍历
      for( String examp: defineClassSet){
       System.out.println( examp);
      }
public class HashSet<E>        extends AbstractSet<E>  implements Set<E>, Cloneable, java.io.Serializable
public class LinkedHashSet<E>  extends HashSet<E>      implements Set<E>, Cloneable, java.io.Serializable {
public class TreeSet<E>        extends AbstractSet<E>  implements NavigableSet<E>, Cloneable, java.io.Serializable
  SortedSet和SortedMap 这两个接口提供排序操作
    public interface NavigableSet<E> extends SortedSet<E> {
    public interface SortedSet<E> extends Set<E> {

Queue

  * Java Collections Framework</a>.
  *
  * @see java.util.Collection
  * @see LinkedList
  * @see PriorityQueue
  * @see java.util.concurrent.LinkedBlockingQueue
  * @see java.util.concurrent.BlockingQueue
  * @see java.util.concurrent.ArrayBlockingQueue
  * @see java.util.concurrent.LinkedBlockingQueue
  * @see java.util.concurrent.PriorityBlockingQueue
  * @since 1.5
  * @author Doug Lea
  * @param <E> the type of elements held in this collection
  */
 public interface Queue<E> extends Collection<E> {

ArrayList 以及LinkedList

    List<String> list = new ArrayList<String>();
    //方法1
    Iterator it1 = list.iterator();
    while(it1.hasNext()){
        System.out.println(it1.next());
    }

    //方法2
    for(String tmp:list){
        System.out.println(tmp);
    }

    //方法3
    for(Iterator it2 = list.iterator();it2.hasNext();){
         System.out.println(it2.next());
    }
    //方法4
    for(int i = 0;i < list.size(); i ++){
        System.out.println(list.get(i));
    }

public class ArrayList<E>   extends AbstractList<E>           implements List<E>, RandomAccess, Cloneable, java.io.Serializable
public class LinkedList<E>  extends AbstractSequentialList<E> implements List<E>, Deque<E>, Cloneable, java.io.Serializable{}
2.Fail-fast 机制
  是java集合(Collection)中的一种错误机制。当多个线程对同一个集合的内容进行操作时,就可能会产生fail-fast事件
ArrayList中有个成员变量 modCount ,继承于AbstractList。
这个成员变量记录着集合的修改次数,也就每次add或者remove它的值都会加1
next和remove操作之前都会先调用checkForComodification来检查expectedModCount和modCount是否相等 
 ConcurrentModificationException 异常

重写hashCode() 以及 equal()

1.为什么要重写
hashCode()方法确定元素在数据结构中存放的位置
Java的继承体系中Object类是所有类的超类
  也就是说实际上自定义的类是继承了Object类的
  因此没有重写hashCode()方法那么调用的就是Object类的hashCode()方法了
equals()
   而使用equals()来确认当两个元素存放的位置发生冲突时是应该将两个元素都存入数据结构还是说只需要存放其中一个
   如果equals()方法判断两个元素是一样的那么当然只需要存放其中一个既可
   但如果equals()方法判断两个对象是不同的那么当然两个都需要存放到数据结构中
2.怎样重写
equal()
  首先判断是否是自己和自己比较如果是那么肯定是相同的因为是同一个对象
   然后再逐个比较对象的属性如果属性值都相同那么说明就是相同的对象
例子Spark源码中Optional 重写
    @Override
    public int hashCode() {
      return value == null ? 0 : value.hashCode();
    }

    @Override
    public boolean equals(Object obj) {
      if (!(obj instanceof Optional)) {
        return false;
      }
      Optional<?> other = (Optional<?>) obj;
      return Objects.equals(value, other.value);
    }
    @Override
    public String toString() {
      return value == null ? "Optional.empty" : String.format("Optional[%s]", value);
    }

Flink中的源码
     @Override
    public int hashCode() {
        return enumClass.hashCode();
    }

    @Override
    public boolean equals(Object obj) {
        if (obj instanceof EnumSerializer) {
            EnumSerializer<?> other = (EnumSerializer<?>) obj;
            return other.enumClass == this.enumClass && Arrays.equals(values, other.values);
        } else {
            return false;
        }
       }
    @Override
    public String toString() {
        return "EnumSerializer{" +
                "enumClass=" + enumClass +
                ", values=" + Arrays.toString(values) +
                '}';
    }

使用场景
在重写 equals 方法的时侯一定要重写 hashcode 方法

编程中递归

增加IF语句进行初始化
增加中间变量用于数据变换
递归函数意味着函数可以调用它本身,在满足条件的时候函数会返回。
 递归-- 实现循环
1.递归的关键点
 01.一个递归算法,必须要有这样一个边界条件
 02. 分解和组合
用例
   01.递归对于列表的一种的处理方式: 即对一个列表的操作,可转化为对第一个元素,及剩余列表的相同操作
2.过程关键点
 整个递归的终止条件。         找整个递归的终止条件:递归应该在什么时候结束?
 一级递归需要做什么?         找返回值:应该给上一级返回什么信息?
 应该返回给上一级的返回值是什么?本级递归应该做什么:在这一级递归中,应该完成什么任务?

3.递归的形式
 01.直接递归 direct recursion -- 互递归 (trivial case).
         递归算法需要保持调用堆栈,效率较低,如果调用次数较多,会耗尽内存或栈溢出,即
    如果递归嵌套很深,容易出现栈溢出的问题
 02.间接递归 indirect recursion
 03.尾递归  tail recursion
   -- 尾递归函数 Tail Recursive Function 所有递归形式的调用都出现在函数的尾部
   就是最后一步是递归操作本身(即没有和其它参数参与),
   只有到达特定状态的时候, 再去构造返回结果, 而其它时候, 只需要更新一些状态变量即可
   尾递归可以进行优化,将递归转换成循环实现,避免栈的溢出
4.具体语言:
Scala语言特别增加了一个注释@tailrec
  @tailrec
Java
  -- 递归实现字符串的反转
  用例
  public String reverse(String str){
    if(str == null || str.length() <= 1){
        return str;
    }else
     {
       return reverse(str.subString(1)) + str.charAt(0);
      }   
  }

参考:

 源码
 Flink和Spark

blogroll

social