java语言基础及特性-02

1.hashMap与hashTable

     Java中数据存储方式最底层的两种结构,一种是数组,另一种就是链表,数组的特点:连续空间,寻址迅速,但是在删除或者添加元素的时候需要有较大幅度的移动,所以查询速度快,增删较慢。而链表正好相反,由于空间不连续,寻址困难,增删元素只需修改指针,所以查询慢、增删快。有没有一种数据结构来综合一下数组和链表,以便发挥他们各自的优势?答案是肯定的!就是:哈希表。哈希表具有较快(常量级)的查询速度,及相对较快的增删速度,所以很适合在海量数据的环境中使用。一般实现哈希表的方法采用“拉链法”,我们可以理解为“链表的数组”,如下图:


     从上图中,我们可以发现哈希表是由数组 链表组成的,一个长度为16的数组中,每个元素存储的是一个链表的头结点。那么这些元素是按照什么样的规则存储到数组中呢。一般情况是通过hash(key)%len获得,也就是元素的key的哈希值对数组长度取模得到。比如上述哈希表中,12%16=12,28%16=12,108%16=12,140%16=12。所以12、28、108以及140都存储在数组下标为12的位置。

1.1 关于hashMap更详细的内容见:HashMap的存储与实现

1.2 HashTable的内部存储结构

HashTable和HashMap采用相同的存储机制,二者的实现基本一致,不同的是:
1.HashMap是非线程安全的,HashTable是线程安全的,内部的方法基本都是synchronized。
2.HashTable不允许有null值的存在,而hashMap则允许key或value为null。

在HashTable中调用put方法时,如果key为null,直接抛出NullPointerException。其它细微的差别还有,比如初始化Entry数组的大小等等,但基本思想和HashMap一样。

2.遍历map的4种方式的比较

public static void main(String[] args) {

  Map<String, String> map = new HashMap<String, String>();
  map.put("1", "value1");
  map.put("2", "value2");
  map.put("3", "value3");
  
  //第一种:普遍使用,二次取值
  System.out.println("通过Map.keySet遍历key和value:");
  for (String key : map.keySet()) {
   System.out.println("key= "+ key + " and value= " + map.get(key));
  }
  
  //第二种
  System.out.println("通过Map.entrySet使用iterator遍历key和value:");
  Iterator<Map.Entry<String, String>> it = map.entrySet().iterator();
  while (it.hasNext()) {
   Map.Entry<String, String> entry = it.next();
   System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue());
  }
  
  //第三种:推荐,尤其是容量大时
  System.out.println("通过Map.entrySet遍历key和value");
  for (Map.Entry<String, String> entry : map.entrySet()) {
   System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue());
  }
  //第四种
  System.out.println("通过Map.values()遍历所有的value,但不能遍历key");
  for (String v : map.values()) {
   System.out.println("value= " + v);
  }
 }

Map接口提供了三种collection:key set,value set和key-value set,每一种都可以转成List,如下:

        //map
        HashMap<Integer,Integer> map = new HashMap<>();
        map.put(1,10);
        map.put(2,20);
        map.put(3,30);
        //key list
        ArrayList<Integer> keyList = new ArrayList<>(map.keySet());
        keyList.forEach(k-> System.out.println(k));

        //value list
        ArrayList<Integer> valueList = new ArrayList<>(map.values());
        valueList.forEach(k-> System.out.println(k));

        //key-value list
        ArrayList<Map.Entry<Integer,Integer>> entryList = new ArrayList<>(map.entrySet());
        entryList.forEach(k-> System.out.println("key = " + k.getKey() + ", value = " + k.getValue()));

参考:Java Map中的几个常见问题

3.Java中多线程

http://www.cnblogs.com/lwbqqyumidi/p/3804883.html

4.Java连接Oracle

这是道经常被写的面试题,http://m.blog.csdn.net/article/details?id=9068765,同时需要掌握socket的长连接和短连接;socket的同步和异步。

5.Java8中的新特性:stream

5.1利用stream将List转为Map
《利用Java8将list转为map》

5.2stream中常用几个API
《java8--List转为Map、分组、统计等操作》

//java8利用sream中map函数实现list转list
public List<Long> getIdList(List<Account> accounts) {
    return accounts.stream().map(Account::getId).collect(Collectors.toList());
    //return accounts.stream().map(Account->Account.getId).collect(Collectors.toList());
}

《Java8中的Steams API详解》

5.3利用stream将Map转为List

    //利用Java8中Stream接口map处理函数,将Map集合转为List
    private static void convert_map_to_list_with_java8_lambda() {
        Map<Integer, Movie> movieMap = new HashMap<Integer, Movie>();
        movieMap.put(1, new Movie(1, "a"));
        movieMap.put(2, new Movie(2, "b"));
        movieMap.put(3, new Movie(3, "c"));

        //将Map集合中所有的value值构建一个List
        List<Movie> movieList = movieMap.entrySet().stream().map(o->o.getValue()).collect(Collectors.toList());
        for (Movie movie:movieList) {
            System.out.println(movie.getMoveName());
        }
        //利用Java8中forEach和lambda特性循环遍历输出
        movieList.forEach(movie -> System.out.println(movie));
    }

5.4 Java8新特性全搜罗
5.5 Java8特性官网  JAVA8新特性[第四季]-强大的Stream API

6.常用的非空判断

6.1字符串非空判断
org.apache.commons.lang3.StringUtils.StringUtils.isNoneEmpty(str);

一般情况下,在自己的代码中会继承org.apache.commons.lang3.StringUtils.StringUtils这个类而封装一个本地项目使用的工具类。

其实,在spring的框架中也有一个StringUtils工具类包。

6.2集合非空判断
org.springframework.util.CollectionUtils.isEmpty(list);

6.3 对象非空判断
obj != null;

7.Java8中的新特性:Lambda表达式

众所周知,lambda 表达式是匿名函数,它们天生就很简洁。普通的函数或方法通常有 4 个元素:
1、一个名称
2、返回类型
3、参数列表
4、主体
在这里可以看到,lambda 表达式只有这 4 元素中的最后两个:(parameter list) -> body

“->” 将参数列表与函数主体分离,旨在对给定参数进行处理,函数的主体可能是一个表达式或一条语句。下面给出了一个示例:
(Integer e) -> e * 2
在此代码中,主体只有一行:一个返回给定参数两次的表达式。信噪比很高,没有分号,也不需要 return 关键字。这就是一个理想的 lambda 表达式。
多行 lambda 表达式
在 Java 中,lambda 表达式的主体也可能是一个复杂的表达式或声明语句;也就是说,一个 lambda 表达式包含多行。在这种情况下,分号必不可少。如果 lambda 表达式返回一个结果,也会需要 return 关键字。下面给出了一个示例:

(Integer e) -> {
  double sqrt = Math.sqrt(e);
  double log = Math.log(e);
  
  return sqrt + log;
}
本示例中的 lambda 表达式返回了 sqrt 和给定参数的 log 的和。因为主体包含多行,所以括号 ( {} )、分号 ( ; ) 和 return 关键字都是必需的。
如果感觉好像 Java 因为我们编写多行 lambda 表达式而惩罚我们,—或许我们应该接受这样的暗示。

函数组合的强大功能
函数式编码风格利用了函数组合的表达能力。比较两段代码时,很容易看出富于表达的好处。第一段代码是用命令式风格编写的:

int result = 0;
for(int e : values) {
  if(e > 3 && e % 2 == 0) {
    result = e * 2;
    break;
  }
}
现在考虑用函数式风格编写的相同代码:
int result = values.stream()
  .filter(e -> e > 3)
  .filter(e -> e % 2 == 0)
  .map(e -> e * 2)
  .findFirst()
  .orElse(0);
两段代码获得了相同的结果。在命令式代码中,我们需要读入 for 循环,按照分支和中断来跟随流程。第二段代码使用了函数组合,更容易阅读一些。因为它是从上往下执行的,所以我们只需要传递该代码一次。
本质上,第二段代码读起来像是一个问题陈述: 给定一些值,仅选择大于 3 的值。从这些值中,仅选择偶数值,并将它们乘以 2。最后,挑选第一个结果。如果没有任何值存在,则返回 0。

最后,结合代码再理解下:

package com.xiaojukeji.multiThread;

import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;

/**
 * Description:
 1.java8函数式接口:关于概念自行百度查询
 下面是 Java SE 7 中已经存在的函数式接口:
 java.lang.Runnable
 java.util.concurrent.Callable
 java.security.PrivilegedAction
 java.util.Comparator
 java.io.FileFilter
 java.beans.PropertyChangeListener
 除此之外,Java SE 8中增加了一个新的包:java.util.function,它里面包含了常用的函数式接口,例如:
 Predicate<T>——接收 T 并返回 boolean
 Consumer<T>——接收 T,不返回值
 Function<T, R>——接收 T,返回 R
 Supplier<T>——提供 T 对象(例如工厂),不接收值
 UnaryOperator<T>——接收 T 对象,返回 T
 BinaryOperator<T>——接收两个 T,返回 T
 除了以上JDK以实现的函数式接口外,我们也可以自定义函数式接口;

 2.Lamdba表达式的返回值就是以上函数式接口的某一种,也就是说lambda表达式返回值是函数式接口,
 至于具体是哪一种函数式接口,则由java8根据上下文语义自行匹配,正是这种自适应匹配的能力,我们也可
 将lambda返回值的类型称为"目标类型";

 3.以下面ThreadApi类为例:
   Runnable runnable = () -> {threadApi.m();};
   new Tread(runnable).start;
   即:() -> {threadApi.m();}返回值是一个只包含run一个方法的Runnable类型的函数式接口;
 
 * Created by Guo_guo on 2017-7-16.
 */
public class ThreadApi {
    public /*volatile*/ int count = 0;
    public /*synchronized*/ void m (){
        for (int i=0; i<10000; i++) {
            count++;
        }
    }

    public static void main(String[] args) {
        ThreadApi threadApi = new ThreadApi();

        List<Thread> threadList = new ArrayList<>(10);
        for (int i=0; i<10; i++) {
            //java8 lambda表达式构建线程,注意此处的ThreadApi没必要继承Runnable接口;
            //() -> threadApi.m()的返回值是Runnable函数式接口
            threadList.add(new Thread(() -> threadApi.m(), "thread-"+i) );
            /*threadList.add(new Thread(
                    new Runnable() {
                        @Override
                        public void run() {
                            threadApi.m();
                        }
                    }
                    )//new Thread
            );//.add
            */

            /*
            函数式接口:lanbda表达式返回值类型是一种函数式接口,
            由于Java8中定义的函数式接口有多种类型,而lamdba表达式返回值能自适应目标类型,所以
            也亦可将lambda表达式返回值的类型称之为"目标类型";
            下面的例子中,(x) -> {System.out.print(x+": ");return "Function";}
            返回值是Function类型的函数式接口类型,而在Function函数式接口类型中只有一个apply函数;
            */
            Function<String,String> function = (x) -> {System.out.print(x+": ");return "Function";};
            System.out.println(function.apply("hello world"));
        }

        threadList.forEach(obj -> obj.start());
        threadList.forEach(obj -> {
            try {
                obj.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        System.out.println(threadApi.count);
    }
}

备注:关于Java中常见的一些坑或错误点,向大家推荐一本书,人民邮电出版社李刚主编的《疯狂Java:突破程序员基本功的16课》


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值