go语言中struct变量和struct指针的区别

在学习结构体指针的时候有些疑问, 首先定义一个结构体, 初始化结构体和它的结构体指针:

cat1 := Cat{"samy", 89.0}  
catPointer := &cat1  
  
catPointer.weight = 222  
fmt.Printf("%T \\n", cat1)  
fmt.Printf("%T \\n", catPointer)  
  
fmt.Println(cat1.weight)  
cat1.weight = 111  
fmt.Println(cat1.weight)
继续阅读“go语言中struct变量和struct指针的区别”

go语言中对闭包的理解和实例演示

go中的函数闭包(Function Closures)对于我来说比较难理解, 在之前的开发中也没有用到其他语言的闭包特性, 所以特意认真学习了一下. 下面我会详细解释我对go中闭包的理解和一个实例用法.

简单来说, 闭包在go中的实现方法就是在函数中嵌套另一个子函数, 如下代码片段所示(摘自官方教程):

func adder() func(int) int {
	sum := 0
	return func(x int) int {
		sum += x
		return sum
	}
}

可以看到, 创建了一个名为adder()的函数, 函数中有一个局部变量sum, 闭包的第一个特性就是可以直接访问父函数的变量, 所以在内部函数中直接使用了sum.

ad := adder()
fmt.Println(ad(10))

这样子就是对闭包特性的一次使用. 函数也是一个变量存在到内存中的, 所以可以把ad作为函数的reference, 在println语句中, 是ad第一次调用, 这里通过函数的reference传入的值实际上是传到了adder()中的匿名函数里, 即x = 10.

我在初次学习中对这里有疑惑, 不理解ad := adder(), 其实这句话可以根据字面意思理解, 即把adder()的返回值赋给ad, 由于adder()的返回值是一个匿名函数, 那么我们就拿到了匿名函数的reference, 下面使用ad(10)传入值也就理所当然了.

下面我用一个更好的例子来解释:

func minusValue(a, b int) func(int) int {
	fmt.Println("this is from minusValue, and a is", a, "b is", b)
	sum := a + b
	return func(para int) int {
		sum += para
		fmt.Println("this is from inner func and sum is", sum)
		return sum
	}
}

我直接使用minusValue(1,2)调用它, 那么控制台只会输出this is from minusValue, and a is 1 b is 2. 这里大家都理解. 当我用下面的方法调用, 就体现出了闭包的另一个特性, 闭包匿名函数中返回的变量只要还有reference在用, 那么它就会一直存在到内存中, 请看下面的调用和结果输出:

f := minusValue(1,2)
f(10)
f(10)
fmt.Println("----------------")
n := minusValue(10,20)
n(100)

输出:

这就是闭包的用法以及特性, 那么在实际的生产中, 也有着很多作用(我也是才知道), 例如(来源):
编写一个程序,具体要求如下:

编写一个函数 makeSuffix(suffix string) ,可以接收一个文件后缀名(比如.jpg),并返回一个闭包;
调用闭包,可以传入一个文件名,如果该文件名没有指定的后缀(比如 .jpg),则返回 文件名.jpg,如果有 .jpg后缀,则返回源文件名;
strings.HasSuffix,该函数可以判断某个字符串是否有指定的后缀。

答案:

package main

import (
	"fmt"
	"strings"
)
func makesuffix(suffix string) func(string) string {
	return func(name string) string {
		//如果name没有指定的后缀,则加上,否则就返回原来的名字
		if !strings.HasSuffix(name, suffix) {
			return name + suffix
		}
		return name
	}
}

func main() {
	f2 :=makesuffix(".jpg")
	fmt.Println("文件名处理后=", f2("winter"))
	fmt.Println("文件名处理后=", f2("bird.jpg"))
}

通过使用闭包的特性, 预先”设置”一个要处理的后缀, 之后通过不断调用闭包的reference, 就可以达到所需要求.

算法 – java常见算法题输入输出

字符串和数字相同道理,本文都适用于字符串。

输入一行数字

Scanner sc = new Scanner(System.in);

        System.out.println("读取一行数字:");
        String oneLine = sc.nextLine();
        String[] cs = oneLine.split(" ");
        for (String x : cs) {
        	int intValue = Integer.valueOf(x);
            System.out.print(intValue + " ");
        }

输入n行数字,行数确定

如果需要获得每行数字的值,用String的split方法分割为char[]后,再使用Integer.valueOf()转换成int即可,同上

System.out.println("读取n行数字:");
        int lines = 3;
        String[] mutiLines = new String[3];
        int i = 0;
        while (i < lines) {
            mutiLines[i++] = sc.nextLine();
        }
        for (String s : mutiLines) {
            System.out.println(s);
        }

输入多行数字,未知行数

没有对一行多数字做处理,处理方法和上述但行输入相同。

System.out.println("输入多行数字:");
        List<String> list = new ArrayList<>();

        String input = "";
        while (true) {
            input = sc.nextLine();
            if (!input.equals("q")) {
                list.add(input);
            }else {
                break;
            }
        }
        for (String s : list) {
            //只能转换一行一个数字,多个数字需要额外同上的操作
            //int intValue = Integer.valueOf(s);
            System.out.println(s);
        }

遇到其他情况再补充

算法 – 字符串包含[+++]

题目简介

给定一长字符串a -> ABCD,短字符串b -> BAD, 短字符串c -> BCE,则字符串b中的字母都在字符串a中,b是a的真子集,所以对于a,返回true,对于b,返回false,因为字符串b的元素E不在a中。
注意:都是大写字母

解法1,暴力轮询

拿出字符串b的每个元素,分别查询是否在a中出现,若一旦有一个不出现,则返回false,如果所有元素都出现,则返回true
时间复杂度O(nm),其中n和m为字符串a,b的长度。

package chapter2_String_Contains;

/**
 * @Author: codefog
 * @email: at20s@sina.com
 * @Date: 2018/9/18 1:52 PM
 */
public class Solution1 {

    /**
     * 暴力轮询
     *
     * @param args
     */
    public static void main(String[] args) {

        String a = "HelloWorld"; //j
        String b = "ldWor"; // i
        String c = "ABC";

        System.out.println(isContains1(a, b));
        System.out.println(isContains1(a, c));

    }

    public static boolean isContains(String a, String b) {
        char[] aStr = a.toCharArray();
        char[] bStr = b.toCharArray();

        //固定字符串b
        for (int i = 0; i < bStr.length; i++) {
            int flag = 0;
            for (int j = 0; j < aStr.length; j++) {
                if (bStr[i] == aStr[j]) {
                    flag = 1;
                    break;
                }
            }
            if (flag == 0) {
                return false;
            }
        }
        return true;
    }

    /**
     * 相同算法,第二种写法
     * @param a
     * @param b
     * @return
     */
    public static boolean isContains1(String a, String b) {
        char[] aStr = a.toCharArray();
        char[] bStr = b.toCharArray();

        //固定字符串b
        for (int i = 0; i < bStr.length; i++) {
            for (int j = 0; j < aStr.length; j++) {
                if (bStr[i] == aStr[j]) {
                    break;
                }
                //字符串b移动到最后一位,a也是最后一位,仍然没有找到相同的,就返回false
                if (bStr[i] != aStr[j] && i == bStr.length - 1 && j == aStr.length - 1) {
                    return false;
                }
            }
        }
        return true;
    }
}


解法2,排序后再轮询

排序时间复杂度O(mlogm)+O(nlogn)+O(m+n),即两次排序和线性扫描的复杂度。与第一种解法类似,改进不大。

package chapter2_String_Contains;

import java.util.Arrays;

/**
 * @Author: codefog
 * @email: at20s@sina.com
 * @Date: 2018/9/18 4:59 PM
 */
public class Solution2 {

    /**
     * 排序轮询
     * @param args
     */
    public static void main(String[] args) {

        String a = "HelloWorld"; //j
        String b = "ldWor"; // i
        String c = "ABC";

        System.out.println(isContains(a, b));
        System.out.println(isContains(a, c));

    }

    public static boolean isContains(String a, String b) {

        char[] aStr = a.toCharArray();
        char[] bStr = b.toCharArray();
        Arrays.sort(aStr);
        Arrays.sort(bStr);

        //固定字符串b
        for (int i = 0; i < bStr.length; i++) {
            int flag = 0;
            for (int j = 0; j < aStr.length; j++) {
                if (bStr[i] == aStr[j]) {
                    flag = 1;
                    break;
                }
            }
            if (flag == 0) {
                return false;
            }
        }
        return true;

    }
}

解法3,素数相乘

时间复杂度O(m+n),最好情况O(n)。巧妙的利用了素数,非常聪明。缺点是获取的素数乘积会可能超过int的最大值,造成溢出。
证明: 每个正整数都可以唯一表示成素数的乘积.在本算法中,利用字母在编码中对应的数字,可以把字母当作一个正整数,获取到他们的乘积后,可以为一表示字符串。

首先证明存在性,  
用数学归纳法,n=2很显然,假设n<k时成立,当n=k时,如果k为素数,显然成立;如果k是合数,则至少有一个素因数p1,k=p1*a,而由归纳假设a<k能分解为素数乘积,所以n=k也成立.所以对于任意大于1的整数n都存在.  
然后证明唯一性,  
如果有两个分解式,2^p1*3^p2*5^p3*…=2^q1*3^q2*5^q3*…,则  
2^p1|2^q1*3^q2*5^q3*…,所以p1≤q1,同理q1≤p1,所以p1=q1,  
后边的类似证明.  

代码:

package chapter2_String_Contains;

/**
 * @Author: codefog
 * @email: at20s@sina.com
 * @Date: 2018/9/18 10:33 PM
 */
public class Solution3 {

    public static void main(String[] args) {

        String a = "HELLOWORLD"; //j
        String b = "LDWOR"; // i
        String c = "ABC";

        System.out.println(isContains(a, b));
        System.out.println(isContains(a, c));
    }


    public static boolean isContains(String a, String b) {

        final int[] pr = {2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101};

        long muti = 1;
        char[] aStr = a.toCharArray();
        char[] bStr = b.toCharArray();
        //遍历字符串a,获取到它所对应的全部素数的乘积
        for (int i = 0; i < aStr.length; i++) {
            //都是大写字母,所以直接相减就在26的范围内,即可确定索引值
            int index = aStr[i] - 'A';
            muti *= pr[index];
        }

        //遍历字符串b,然后让第一步获得的乘积和短字符串所对应的素数取余数,如果由余数,返回false
        for (int i = 0; i < bStr.length; i++) {
            int index = bStr[i] - 'A';
            if (muti % pr[index] > 0) {
                return false;
            }
        }
        return true;
    }

}

解法4,位运算法(HASH)

这个方法和解法3类似,只不过把算出的素数乘积换成了HASH值,然后在用b查询,所有操作使用位运算完成。时间复杂度同上O(n+m),空间复杂度为O(1),对空间友好。

package chapter2_String_Contains;

/**
 * @Author: codefog
 * @email: at20s@sina.com
 * @Date: 2018/9/19 12:06 AM
 */
public class Solution4 {

    public static void main(String[] args) {

        String a = "HELLOWORLD"; //j
        String b = "LDWOR"; // i
        String c = "ABC";

        System.out.println(isContains(a, b));
        System.out.println(isContains(a, c));
    }

    public static boolean isContains(String a, String b) {
        char[] aStr = a.toCharArray();
        char[] bStr = b.toCharArray();

        int HASH = 0;

        //遍历a字符串,为每个字符计算HASH值
        for (int i = 0; i < aStr.length; i++) {
            HASH |= (1 << (aStr[i] - 'A'));
        }
        //把b的每个字符放到a中查找
        for (int i = 0; i < bStr.length; i++) {
            if ( (HASH & (1 << (bStr[i] - 'A')) ) == 0) {
                return false;
            }
        }

        return true;
    }
}

其他资料算法

参考:
https://www.cnblogs.com/tgycoder/p/5241157.html
http://blog.jobbole.com/99205/

算法 – 字符串旋转

题目简介

字符串反转,字符串旋转,例如abcdef旋转为defabc

解法1,暴力

时间复杂度O(nm),空间O(1) (长度为n,移动m个字符)

坑:

java如果想覆盖字符串的值,不能和c/c++一样,直接传入指针就能修改原值,而是需要old = opeartion(old)这样子把旧的引用覆盖掉才行。

package string_reverse;

/**
 * @Author: codefog
 * @email: at20s@sina.com
 * @Date: 2018/9/17 10:44 PM
 */
public class Solution1 {


    /**
     * 暴力一次一个字符的移动
     * 时间复杂度O(nm),空间O(1) (长度为n,移动m个字符)
     * @param args
     */
    public static void main(String[] args) {
        String str = "hello world!";
        System.out.println(rotateString(str,3));

    }

    public static String shifting(String str) {

        char[] strs = str.toCharArray();
        char temp = strs[0];
        for (int i = 1; i < strs.length; ++i) {
            //从第一个开始,一次被后一个字符覆盖
            strs[i - 1] = strs[i];
        }
        strs[strs.length - 1] = temp;
        return String.valueOf(strs);
    }

    public static String rotateString(String string, int m) {
        while (m > 0) {
            string = shifting(string);
            m--;
        }
        return string;
    }



}

解法2,三步反转

时间复杂度O(n),空间复杂度O(1)

  1. 分割原字符串
  2. 分别反转
  3. 整体反转
public class Solution2 {

    /**
     * 三步反转
     * @param args
     */
    public static void main(String[] args) {
        String str = "hello world!";
        System.out.println(rotateString(str,4));

    }

    /**
     * 把字符串m到n的位置反转
     * @param string
     * @return
     */
    public static String reverseString(String string,int m, int n) {
        char[] cString = string.toCharArray();

        while (m < n) {
            char temp = cString[m];
            //第一个值被最后一个覆盖,然后移动m到下一个值
            cString[m++] = cString[n];
            //最后一个值被第一个覆盖,向前移动
            cString[n--] = temp;
        }
        return String.valueOf(cString);
    }

    public static String rotateString(String str, int m) {
        //m = m % length, 如果移动的位置数量超过长度,则相当于一个环旋转
        // 3 % 5 = 3, 小于字符串长度则没有问题
        m %= str.length();
        //反转第一部分
        str = reverseString(str,0, m);
        //反转第二部分
        str = reverseString(str, m, str.length() - 1);
        //整体反转
        str = reverseString(str, 0, str.length() - 1);

        return str;

    }
}

举一反三

反转句子中单词的位置,比如i am a student., 反转后student. a am i,标点符号作为单词的一部分处理.

思路

  1. 以空格分割句子为n(单词数量)部分
  2. 分别反转
  3. 总体反转
public class Others {

    /**
     * 反转句子中的单词
     * @param args
     */
    public static void main(String[] args) {
        String str = "i am quite a student.";
        System.out.println(rotateString(str));

    }

    /**
     * 把字符串m到n的位置反转
     * @param string
     * @return
     */
    public static String reverseString(String string,int m, int n) {
        char[] cString = string.toCharArray();

        while (m < n) {
            char temp = cString[m];
            //第一个值被最后一个覆盖,然后移动m到下一个值
            cString[m++] = cString[n];
            //最后一个值被第一个覆盖,向前移动
            cString[n--] = temp;
        }
        return String.valueOf(cString);
    }

    public static String rotateString(String str) {
        //以空格分割,正则表达
        String[] spString = str.split("\\s+");
        str = "";
        //分别反转分割后的字符串
        if (spString.length > 0) {
            for (int i = 0; i < spString.length; ++i) {
                spString[i] = reverseString(spString[i],0,spString[i].length() - 1);
                //拼接完整字符串
                str += " " + spString[i];
            }
        }
        //总体旋转
        str = reverseString(str, 0, str.length() - 1);

        return str;
    }

}

复杂度同上.

IBM的java系列教程笔记

迭代泛型

泛型使用处理一些实体(比如 List)的特殊语法增强了 Java 语言,您通常可能希望逐个元素地处理这些实体。举例而言,如果想迭代 ArrayList,可以将 清单 3 中的代码重写为:

private void processArrayList(ArrayList<String> theList) {
  for (String s : theList) {
    String s = theList.get(aa);
  }
}

此语法适用于任何 Iterable(即实现了 Iterable 接口)的对象类型

一个参数化的 List

在泛型语法中,创建 List 的代码类似于:

List listReference = new concreteListClass();
E(表示元素)是我之前提到的 “东西”。concreteListClass 是 JDK 中您实例化的类。该 JDK 包含多个 List 实现,但您使用了 ArrayList。您可能看到的泛型类的另一种形式是 Class,其中 T 表示类型。在 Java 代码中看到 E 时,它通常指的是某种类型的集合。当您看到 T 时,它表示一个参数化的类。

所以,要创建一个由 java.lang.Integer 组成的 ArrayList,可以这样做:

List<Integer> listOfIntegers = new ArrayList<Integer>();

原文地址
第20单元:泛型

java8中的Lambda表达式和Stream

非常非常棒的JAVA教程,来自于IBM

https://www.ibm.com/developerworks/cn/java/intro-to-java-course/index.html强烈推荐

Syntax

A lambda expression is characterized by the following syntax.
parameter -> expression body
Following are the important characteristics of a lambda expression.

  1. Optional type declaration − No need to declare the type of a parameter. The compiler can inference the same from the value of the parameter.
  2. Optional parenthesis around parameter − No need to declare a single parameter in parenthesis. For multiple parameters, parentheses are required.
  3. Optional curly braces − No need to use curly braces in expression body if the body contains a single statement.
  4. Optional return keyword − The compiler automatically returns the value if the body has a single expression to return the value. Curly braces are required to indicate that expression returns a value.

Lambda Expressions Example

public class Java8Tester {

   public static void main(String args[]) {
      Java8Tester tester = new Java8Tester();

      //with type declaration
      MathOperation addition = (int a, int b) -> a + b;

      //with out type declaration
      MathOperation subtraction = (a, b) -> a - b;

      //with return statement along with curly braces
      MathOperation multiplication = (int a, int b) -> { return a * b; };

      //without return statement and without curly braces
      MathOperation division = (int a, int b) -> a / b;

      System.out.println("10 + 5 = " + tester.operate(10, 5, addition));
      System.out.println("10 - 5 = " + tester.operate(10, 5, subtraction));
      System.out.println("10 x 5 = " + tester.operate(10, 5, multiplication));
      System.out.println("10 / 5 = " + tester.operate(10, 5, division));

      //without parenthesis
      GreetingService greetService1 = message ->
      System.out.println("Hello " + message);

      //with parenthesis
      GreetingService greetService2 = (message) ->
      System.out.println("Hello " + message);

      greetService1.sayMessage("Mahesh");
      greetService2.sayMessage("Suresh");
   }

   interface MathOperation {
      int operation(int a, int b);
   }

   interface GreetingService {
      void sayMessage(String message);
   }

   private int operate(int a, int b, MathOperation mathOperation) {
      return mathOperation.operation(a, b);
   }
}

Output:
10 + 5 = 15
10 – 5 = 5
10 x 5 = 50
10 / 5 = 2
Hello Mahesh
Hello Suresh

Following are the important points to be considered in the above example.

  1. Lambda expressions are used primarily to define inline implementation of a functional interface, i.e., an interface with a single method only. In the above example, we’ve used various types of lambda expressions to define the operation method of MathOperation interface. Then we have defined the implementation of sayMessage of GreetingService.
  2. Lambda expression eliminates the need of anonymous class and gives a very simple yet powerful functional programming capability to Java.

???

Still got questions about what situations that lambda is required.
need further practice.
simplify interface implementation?


java中的Stream(java8 new feature)

Stream represents a sequence of objects from a source, which supports aggregate operations. Following are the characteristics of a Stream −

  1. Sequence of elements − A stream provides a set of elements of specific type in a sequential manner. A stream gets/computes elements on demand. It never stores the elements.

  2. Source − Stream takes Collections, Arrays, or I/O resources as input source.

  3. Aggregate operations − Stream supports aggregate operations like filter, map, limit, reduce, find, match, and so on.

  4. Pipelining − Most of the stream operations return stream itself so that their result can be pipelined. These operations are called intermediate operations and their function is to take input, process them, and return output to the target. collect() method is a terminal operation which is normally present at the end of the pipelining operation to mark the end of the stream.

  5. Automatic iterations − Stream operations do the iterations internally over the source elements provided, in contrast to Collections where explicit iteration is required.

Generating Streams

With Java 8, Collection interface has two methods to generate a Stream.

  1. stream() − Returns a sequential stream considering collection as its source.

  2. parallelStream() − Returns a parallel Stream considering collection as its source.

为什么需要Stream

Java 8 中的 Stream 是对集合(Collection)对象功能的增强,它专注于对集合对象进行各种非常便利、高效的聚合操作(aggregate operation),或者大批量数据操作 (bulk data operation)。Stream API 借助于同样新出现的 Lambda 表达式,极大的提高编程效率和程序可读性。同时它提供串行和并行两种模式进行汇聚操作,并发模式能够充分利用多核处理器的优势,使用 fork/join 并行方式来拆分任务和加速处理过程。通常编写并行代码很难而且容易出错, 但使用 Stream API 无需编写一行多线程的代码,就可以很方便地写出高性能的并发程序。所以说,Java 8 中首次出现的 java.util.stream 是一个函数式语言+多核时代综合影响的产物。

更多资料请看Reference中IBM的链接

Reference

Java 8 – Lambda Expressions
Java 8 中的 Streams API 详解 推荐阅读