有一个题目要求:
给一个指定的文件夹,递归遍历该文件夹以及该文件夹下子文件夹,找到所有格式为jpg或者JPG的图片,把所有尺寸为800*800的并且大于300K的图片压缩。
找到700*700的图片并且大于500K的压缩。
当然,给定的这个文件夹相当的大,所以算法必须尽可能的优化。
这其实是我工作中遇到的一个任务把,我用了半天的时间写了出来,第一次跑的时候用了大约1天的时间。
师傅是不满意的,第二天陪我一起修改了我的代码,第二次跑了一遍,只用了8个小时。
而且给我的感觉不仅仅是算法上的优化,他修改完的代码就是给人一种一目了然的感觉,这就让我强烈的感觉到了一个初级开发人员和高级开发人员的差别。
各位如果有兴趣的话可以尝试写一下我上面的那个题目,也可以在此基础上添加一些特色的东西。
最后我会把我的代码也发上来和大家对比一下。
把代码相互对比一下,这就好比人写的字,一看就能看出来自己处于一个什么样的水平。200分赠送给算法最优化的那一位把。

解决方案 »

  1.   

    原来是标题党,还可以去c++区发一个“进来看看到底你c++处于一个什么样的水平把”,然后去.NET区发一个“进来看看到底你.NET处于一个什么样的水平把”
      

  2.   

    靠,又拿算法来装逼了。这事情,上网一搜索就一堆。怎么牛,也牛不过用系统自带搜索加rar压缩吧
      

  3.   

    靠,又拿算法来装逼了。这事情,上网一搜索就一堆。怎么牛,也牛不过用系统自带搜索加rar压缩吧
      

  4.   

    靠,又拿算法来装逼了。这事情,上网一搜索就一堆。怎么牛,也牛不过用系统自带搜索加rar压缩吧
      

  5.   

    实现功能倒是不难,不过听楼主这么一说,倒还真是挺有意思的。楼上那个说系统自带搜索和命令行调用 rar 的亮了,我觉得这确实是个比较不错的方案,
    不过感觉有点儿脱离 纯 java 实现的范畴了,另外还要求对系统命令的调用比较熟悉,
    如果还要跨平台的话。。这个你懂的!压缩操作我觉得是个比较重要的地方,用 jni 调用 c 代码对符合要求的图片做压缩处理应该效率提升比较大吧还有就是递归遍历,到底是在一遍历到符合条件的图片就马上做压缩处理好,
    还是先把符合条件的图片全部装起来,后面统一作压缩处理好呢?递归遍历还有水深水浅的问题么?
    比如说,一般较常用的就是 File.list() 方法了,难不成在高效率的要求下还要自己写个遍历孩子节点的方法出来?
    另外就是利用一些 java 底层的特性来达到提高效率的目的,这个我也是一头雾水,等高手了~还有一个比较关键的问题,获取图片的宽和高有简洁高效的方案么?
    按照我自己的常用习惯来看,我一般是先读取为 BufferedImage,然后通过 getWidth(),getHeight() 得到宽高,
    这样的话,有时候如果只是像了解一下图片的宽高,也得将整个图片读成 BufferedImage 对象,
    感觉有些奢侈了~有什么巧妙的途径能够避开这一点么 ,
    也就是说不用将图片数据全部加载到内存也能高效的获得图片宽高?权且抛砖引玉,坐等大婶发飙~
      

  6.   

    rar好用吗?java有自带zip包可以厌缩解厌文件,很简单
      

  7.   

    楼主说的压缩是说要打成rar或zip包吗?我怎么觉得他说的不是这个意思,他好像是说,如果图片大于某个值,比如300K就压缩下,是要把这张图片压缩到300K以下,然后仍然是jpg图片,而不是打包!压缩图片,图片肯定会失真,要让失真比较小,压缩速度又快,我倒觉得这是个比较不错的研究话题。
      

  8.   

    执行效率怎么样呢?用 jni 或着调用系统提供的命令行压缩工具感觉应该好些吧
      

  9.   

    写了一点儿代码做测试,发现在获取图片尺寸上面还真有捷径,下面给出相关的代码:
    ImageUtilTest.java
    package org.bruce.java.guru.performance;/**
     * @author Bruce Yang
     * 尝试以高效的方式获取图片的尺寸~
     * 
     * 程序输出:
     * ===========
     * viaImageIO:
     * 图片的宽度和高度分别为:3200 2000
     * 耗费的时间为:1.08 秒
     * 
     * ===============
     * viaImageReader:
     * 图片的宽度和高度分别为:3200 2000
     * 耗费的时间为:0.0080 秒
     */
    public class ImageUtilTest { /**
     * @param args
     */
    public static void main(String args[]) {
    String imgPath = "/org/bruce/java/guru/res/lion.jpg";
    viaImageIO(imgPath);
    viaImageReader(imgPath);
    }

    /**
     * ImageReader 用来获取图片的尺寸非常高效!!
     */
    public static void viaImageReader(String imgPath) {
    System.out.println("===============\nviaImageReader:");
    long l0 = System.currentTimeMillis();

    int[] imgSize = ImageUtil.getSize(imgPath);
    System.out.println("图片的宽度和高度分别为:" + imgSize[0] + " " + imgSize[1]);

    long l1 = System.currentTimeMillis();
    System.out.println("耗费的时间为:" + (double)(l1 - l0) / 1000 + " 秒\n");
    }

    /**
     * 常规方法,效率比较低~
     */
    public static void viaImageIO(String imgPath) {
    System.out.println("===========\nviaImageIO:");
    long l0 = System.currentTimeMillis(); int[] imgSize = ImageUtil.getSizeDeprecated(imgPath);
    System.out.println("图片的宽度和高度分别为:" + imgSize[0] + " " + imgSize[1]); long l1 = System.currentTimeMillis();
    System.out.println("耗费的时间为:" + (double)(l1 - l0) / 1000 + " 秒\n");
    }
    }ImageUtil.java
    package org.bruce.java.guru.performance;import java.awt.image.BufferedImage;
    import java.io.IOException;
    import java.io.InputStream;
    import java.util.Iterator;import javax.imageio.ImageIO;
    import javax.imageio.ImageReader;
    import javax.imageio.stream.ImageInputStream;/**
     * @author Bruce Yang
     * 获取图片尺寸的工具类~
     */
    public class ImageUtil { /**
     * @param imgPath 格式:/org/bruce/java/guru/res/lion.png
     * @return array[0]<->width, array[1]<->height
     */
    public static int[] getSize(String imgPath) {
    Iterator<ImageReader> readers = null;
    String lowerCase = imgPath.toLowerCase();
    if (lowerCase.endsWith(".jpg") || lowerCase.endsWith(".jpeg")) {
    readers = ImageIO.getImageReadersByFormatName("jpg");
    } else if (lowerCase.endsWith(".png")) {
    readers = ImageIO.getImageReadersByFormatName("png");
    } else {
    String strTips = "Unsupported image format!(try JPG or PNG)";
    System.err.println(strTips);
    System.exit(-1);
    }
    ImageReader reader = readers.next();

    InputStream is = Class.class.getResourceAsStream(imgPath);
    ImageInputStream iis = null;
    try {
    iis = ImageIO.createImageInputStream(is);
    reader.setInput(iis, true);
    } catch (IOException e1) {
    e1.printStackTrace();
    }

    int width = 0;
    int height = 0;

    try {
    width = reader.getWidth(0);
    height = reader.getHeight(0);
    iis.close();
    } catch (IOException e) {
    e.printStackTrace();
    }

    int[] retVal = {width, height};
    return retVal;
    }

    /**
     * @param imgPath 格式:/org/bruce/java/guru/res/lion.png
     * @return array[0]<->width, array[1]<->height
     */
    public static int[] getSizeDeprecated(String imgPath) {
    InputStream is = Class.class.getResourceAsStream(imgPath); BufferedImage bi = null;
    try {
    bi = ImageIO.read(is);
    is.close();
    } catch (IOException e) {
    e.printStackTrace();
    }

    int[] retVal = {bi.getWidth(), bi.getHeight()};
    return retVal;
    }
    }
      

  10.   

    理解,为公司做事不比自己兴趣爱好,还是低调稳妥些好,免得给自己徒增麻烦。
    譬如说 jni 虽然带来了高效老大很开心,但如果有一天老大让你对其他各个平台也做兼容,就得自己伤心了~
      

  11.   

    如果不是的话,那么对于Ruby来说,基本是一两行代码的事情啊考个什么劲啊。
      

  12.   

    这东西jpg压缩自己写的能有sun的程序员效率高?
      

  13.   

    压缩图片的程序是借用网上的代码改的,
    某些人很明显理解错了意思。
    找到300K以上的压缩肯定是图片压缩,打包成rar文件没什么用。
    另外我想表达的意思是一个看起来并不是很难的任务,但是其代码水平不一样,编出来的东西就是不一样。
    效率以及代码的清晰度差别其实挺大的。
    另外这绝对不是标题党,你想着能写出来和自己写出来对比别人的代码差别是很大的。
    我不相信一个人对比别人和自己代码的时候看不出来水平的高低。
      

  14.   

    取 JPEG 大小的话读取 JPEG header 中的部分就可以了,使用 JDK 的 ImageReader 读取的话还会读取其他的一些信息,这些信息在这个帖子中的意义不大。在这里,我们只关心图像的大小,参考代码如下:import java.io.FileInputStream;
    import java.io.IOException;
    import java.io.InputStream;public class JpegImageMetaReader {    private static final byte TAG_START = (byte)0xff;
        private static final byte START_OF_IMAGE = (byte)0xd8;
        private static final byte END_OF_IMAGE = (byte)0xd9;
        private static final byte START_OF_FRAME = (byte)0xc0;
        private static final byte RESTART_MODULO_START = (byte)0xd0;
        private static final byte RESTART_MODULO_END = (byte)0xd7;
        private static final byte START_OF_SCAN = (byte)0xda;    public static void main(String[] args) throws IOException {
            InputStream in = null;
            try {
                in = new FileInputStream("d:/images.jpg");
                ImageInfo info = getImageInfo(in);
                System.out.println(info);
            } finally {
                if (in != null) {
                    in.close();
                }
            }
        }    public static ImageInfo getImageInfo(InputStream in) throws IOException {        // 0-1: store JPEG tag
            // 2-3: store JPEG tag length
            // 4-5: store JPEG image height
            // 6-7: store JPEG image width
            byte[] seg = new byte[8];        // read JPEG START_OF_IMAGE tag
            if (in.read(seg, 0, 2) == -1) {
                return null;
            }        // if the first two bytes is not 0xff, 0xd8,
            // that is the image format is not JPEG
            if (seg[0] != TAG_START || seg[1] != START_OF_IMAGE) {
                return null;
            }        while (true) {            // read JPEG data tag, offset 0 must be 0xff
                if (in.read(seg, 0, 2) == -1) {
                    return null;
                }            // if tag does not start with 0xff,
                // the image format is not JPEG
                if (seg[0] != TAG_START) {
                    return null;
                }            // Ignore JPEG RESTART_MODULO tag
                if (seg[1] >= RESTART_MODULO_START && seg[1] <= RESTART_MODULO_END) {
                    continue;
                }            // find JPEG format START_OF_SCAN part,
                // data that starts with poisition is JPEG compression image data,
                // that never contains image meta information
                if (seg[1] == START_OF_SCAN) {
                    return null;
                }            // find JPEG format END_OF_IMAGE tag, finish scan
                if (seg[1] == END_OF_IMAGE) {
                    return null;
                }            // read JPEG data tag length
                if (in.read(seg, 2, 2) == -1) {
                    return null;
                }            // find START_OF_FRAME tag
                if (seg[1] == START_OF_FRAME) {
                    break;
                }            // skip JPEG data segement
                byte[] skip = new byte[toInt(seg, 2) - 2];
                if (in.read(skip) == -1) {
                    return null;
                }
            }        // ignore JPEG image precision byte
            if (in.read() == -1) {
                return null;
            }        // read JPEG image height and width bytes
            if (in.read(seg, 4, 4) == -1) {
                return null;
            }
            return new ImageInfo(toInt(seg, 4), toInt(seg, 6));
        }    private static int toInt(byte[] bys, int start) {
            return ((bys[start] & 0xff) << 8) | (bys[start + 1] & 0xff);
        }    public static class ImageInfo {
            private int height;
            private int width;
            public ImageInfo(int height, int width) {
                super();
                this.height = height;
                this.width = width;
            }
            public int getWidth() {
                return width;
            }
            public int getHeight() {
                return height;
            }
            @Override
            public String toString() {
                return "height: " + height + ", width: " + width;
            }
        }
    }
      

  15.   

    至于 JPEG 的压缩那就复杂了,这需要对于 JPEG 图像格式有相当地了解,主要是对于无损压缩的 Huffman、行程进行编码进行优化,以及一些有损压缩技术,比如提高像素采样率,再使用 DCT 算法转换频率空间,再通过 DC 和 AC 系数进行图像处理和编码。
      

  16.   

    很好很强大,长见识了,测了一下确实比 ImageReader 快了些不过我有一些疑问:
    1.class 前面带 static 是什么用法呢,
    2.另外,宽高都拿到了,为什么还要将 宽高丢到一个 ImageInfo 对象里面呢,直接使用整形数组的结果不是更快捷?
      

  17.   

    听起来就挺复杂的,感觉这个课题和算法沾边比 java 多
      

  18.   


    1:内部类使用 static 的话,一般说明这个内部类不需要使用到外部类中的任何成员变量或者成员方法,也就是说这个内部类相对于较为独立。如果有需要使用到外部类中数据的话,则需要把 static 去掉。不对外部类数据进行引用时,内部类应使用 static 类。2:对于这个问题,举个很简单的例子,如果我还想获取图像长宽的 DPI 信息,以及颜色深度信息,或者是 JPEG 中 Exif 信息的话,那使用 int[] 就没办法表示了。在面向对象程序设计中,返回值应尽量少使用 int[], String[], Map<String, Object> 之类的返回值,因为这样的返回值不是自描述的,为什么 int[0] 是表示长度呢?int[0] 就不能表示宽度么?我很能理解你认为返回 int[] 的理由是什么,可能是因为 new 一个对象比较耗时和占用内存空间吧?实际上并不是这样的,new 一个对象而且这个类的构造中没有任何复杂耗时的操作,对于系统消耗来说可以忽略不计,而对于 HotSpot 的 JVM 而言,其内存分配的速度是大于 C 语言中 malloc 的内存分配速度。在 HotSpot 的 JVM 中一个 new 操作只占用了十几个 CPU 指令的时间。new 一个对象和 new 一个 int 数据的效率是等价的。另外,new 一个对象所分配的内存更是可以忽略不计了,只是在 JVM 的 PermGen 方法区中多存放一些类的信息,以及方法的信息。