Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Lambda 表达式带来的复杂性的破解之道

Lambda 表达式带来的复杂性的破解之道

作者头像
明明如月学长
修改于 2021-11-29 03:19:08
修改于 2021-11-29 03:19:08
76700
代码可运行
举报
运行总次数:0
代码可运行

一、背景

Java 8 的 Lambda 表达式已经不再是“新特性”。

曾经很多人抵触 Lambda 表达式,现在几乎也成了标配。

实际开发中最常见的是,很多人使用 Stream 来处理集合类。

但是由于 Lambda 表达式的滥用,代码可读性会变差,那么该如何解决?

本文会讨论一下这个问题,并给出自己的几个解决办法。

二、看法

对于 Lambda 表达式或者 Stream 的看法不尽一致。

2.1 支持

使用 Lambda 表达式可以减少类或者方法的常见。

使用 Stream 可以享受链式编程的乐趣。

有些人看别人都在用,似乎有些高端,或者担心自己被淘汰也跟着大量使用。

2.2 反对

有些人对 lambda 表达式持反对意见。

他们认为大量使用 lambda 表达式写出的代码不容易理解

还有的团队老人比较多,不太容易接受新的事物,不提倡使用 Lambda 表达式。

如:

Stream 的广泛使用带来了很多样板方法。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
List<String> tom = dogs.stream().filter(dog -> dog.getName().startsWith("tom")).map(dog -> dog.getName().toLowerCase()).collect(Collectors.toList());

甚至经常有人会在 Stream 的 map 函数中写大量转换代码。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import lombok.Data;

@Data
public class DogDO {
    private String name;

    private String nickname;

    private String address;

    private String owner;

}

DogVO 和 DogDO 结构相同。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
   List<DogVO> result = dogs.stream().filter(dog -> dog.getName().startsWith("tom")).map(dog -> {
            DogVO dogVO = new DogVO();
            dogVO.setName(dog.getName());
            dogVO.setAddress(dog.getAddress());
            dogVO.setOwner(dog.getOwner());
            return dogVO;
        }).collect(Collectors.toList());

更有甚者直接将整个 Stream 表达结果当做参数传入方法中:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
   result.addAll(dogs.stream().filter(dog -> dog.getName().startsWith("tom")).map(dog -> {
            DogVO dogVO = new DogVO();
            dogVO.setName(dog.getName());
            dogVO.setAddress(dog.getAddress());
            dogVO.setOwner(dog.getOwner());
            return dogVO;
        }).collect(Collectors.toList()));

当一个方法中大量出现上述现象时,代码就没法读了。

还有人担心 Stream 会带来一些副作用。

三、底层原理

参见我的另外一篇文章

《深入理解 Lambda 表达式》

四、建议

Lambda 可以简化代码,但是要把握度,如果滥用 lambda 表达式,代码可读性会很差。

4.1 使用方法引用

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 List<String> names = new LinkedList<>();
names.addAll(users.stream().map(user -> user.getName()).filter(userName -> userName != null).collect(Collectors.toList()));
names.addAll(users.stream().map(user -> user.getNickname()).filter(nickname -> nickname != null).collect(Collectors.toList()));

可以优化为:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
List<String> names = new LinkedList<>();
        names.addAll(users.stream().map(User::getName).filter(Objects::nonNull).collect(Collectors.toList()));
        names.addAll(users.stream().map(User::getNickname).filter(Objects::nonNull).collect(Collectors.toList()));

4.2 复杂代码抽取出来

对于部分复杂逻辑、对于部分需要复用的逻辑,建议封装成独立的类。

Stream参数中常用的 java.util.function 包下的 PredicateFunctionConsumer 类和 Comparator 等。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
   result.addAll(dogs.stream().filter(dog -> dog.getName().startsWith("tom")).map(dog -> {
            DogVO dogVO = new DogVO();
            dogVO.setName(dog.getName());
            dogVO.setAddress(dog.getAddress());
            dogVO.setOwner(dog.getOwner());
            return dogVO;
        }).collect(Collectors.toList()));

改造如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import java.util.function.Function;

public class DogDO2VOConverter implements Function<DogDO, DogVO> {
    @Override
    public DogVO apply(DogDO dogDO) {
        DogVO dogVO = new DogVO();
        dogVO.setName(dogDO.getName());
        dogVO.setAddress(dogDO.getAddress());
        dogVO.setOwner(dogDO.getOwner());
        return dogVO;
    }
}

改造

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 result.addAll(dogs.stream().filter(dog -> dog.getName().startsWith("tom")).map(new DogDO2VOConverter()).collect(Collectors.toList()));

或者定义静态方法

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class DogDO2VOConverter {
    
    public static DogVO toVo(DogDO dogDO) {
        DogVO dogVO = new DogVO();
        dogVO.setName(dogDO.getName());
        dogVO.setAddress(dogDO.getAddress());
        dogVO.setOwner(dogDO.getOwner());
        return dogVO;
    }
}

直接使用方法调用即可

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
        result.addAll(dogs.stream().filter(dog -> dog.getName().startsWith("tom")).map(DogDO2VOConverter::toVo).collect(Collectors.toList()));

4.3 不要将stream 操作放在方法参数中

正如上面的代码一样

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
        result.addAll(dogs.stream().filter(dog -> dog.getName().startsWith("tom")).map(DogDO2VOConverter::toVo).collect(Collectors.toList()));

很多人写代码时,喜欢将 Stream 操作放到方法参数中来节省一个局部变量。

我个人非常反对这种行为,这样做极大降低了代码的可读性。

我们应该将 Stream 的操作定义具有明确含义的返回值,然后再使用。

如:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
   List<DogVO> toms = dogs.stream().filter(dog -> dog.getName().startsWith("tom")).map(DogDO2VOConverter::toVo).collect(Collectors.toList());
  result.addAll(toms);

4.4 Lambda 表达式不宜过长

很多人尝到了链式编程的甜头以后,总喜欢把代码写的很长。

如:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 Optional.ofNullable(dogs).orElse(new ArrayList<>()).stream().filter(dog -> dog.getName().startsWith("tom")).map(DogDO2VOConverter::toVo).collect(Collectors.toList()));

但看这一句代码都很费劲,当一个函数中出现大量这种代码时,简直要吐血。

对于这种长的 Lambda 表达式写法,建议尽可能拆分出来。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    List<Dog> dogs = Optional.ofNullable(dogs).orElse(new ArrayList<>());
        List<String> toms = dogs.stream().filter(dog -> dog.getName().startsWith("tom")).map(DogDO2VOConverter::toVo).collect(Collectors.toList()))

然后进一步将 dogs.stream 这部分逻辑封装为子函数。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    List<Dog> dogs = Optional.ofNullable(dogs).orElse(new ArrayList<>());
        List<String> toms = getDogNamesStartWithTom(dogs)

这样就比较清晰易懂。

4.5 样板方法使用泛型封装

如果你发现你的项目中大量使用 Lambda,而且有很多代码的逻辑非常相似,可以考虑使用泛型封装工具类来简化代码。

下面给出两个简单的示例。

4.5.1 Stream 对象转换

实际开发中类似先过滤后转换的代码非常多:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
List<DogVO> vos = dogs.stream().map(DogDO2VOConverter::toVo).collect(Collectors.toList())

实际上这种写法习以为常,但一个方法出现多次这种写法就非常不宜读。

封装成工具类之后就比较简洁:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 List<DogVO> vos = MyCollectionUtils.convert(dogs,DogDO2VOConverter::toVo);

工具类:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class MyCollectionUtils {

    public static <S, T> List<T> convert(List<S> source, Function<S, T> function) {

        if (CollectionUtils.isEmpty(source)) {
            return new ArrayList<>();
        }

        return source.stream().map(function).collect(Collectors.toList());
    }

    public static <S, T> List<T> convert(List<S> source, Predicate<S> predicate, Function<S, T> function) {

        if (CollectionUtils.isEmpty(source)) {
            return new ArrayList<>();
        }

        return source.stream().filter(predicate).map(function).collect(Collectors.toList());
    }
}

通过将常见的样板方法封装成工具类,可以极大简化使用时的代码。

4.5.2 Spring 策略模式案例

《巧用 Spring 自动注入实现策略模式升级版》 中提到,如下案例:

定义接口

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public interface Handler {

    String getType();

    void someThing();
}

VIP 用户实现:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import org.springframework.stereotype.Component;

@Component
public class VipHandler implements Handler{
    @Override
    public String getType() {
        return "Vip";
    }

    @Override
    public void someThing() {
        System.out.println("Vip用户,走这里的逻辑");
    }
}

普通用户实现:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Component
public class CommonHandler implements Handler{

    @Override
    public String getType() {
        return "Common";
    }

    @Override
    public void someThing() {
        System.out.println("普通用户,走这里的逻辑");
    }
}

模拟 Service 中使用:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Service
public class DemoService implements ApplicationContextAware {


    private Map<String, List<Handler>> type2HandlersMap;

    public void test(){
      String type ="Vip";
      for(Handler handler : type2HandlersMap.get(type)){
          handler.someThing();;
      }
    }


    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {

        Map<String, Handler> beansOfType = applicationContext.getBeansOfType(Handler.class);
        beansOfType.forEach((k,v)->{
            type2HandlersMap = new HashMap<>();
            String type =v.getType();
            type2HandlersMap.putIfAbsent(type,new ArrayList<>());
            type2HandlersMap.get(type).add(v);
        });
    }
}

其中 setApplicationContext 里面的代码非常相似。

可以编写工具类

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import org.apache.commons.collections4.MapUtils;
import org.springframework.context.ApplicationContext;

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

public class BeanStrategyUtils {

// 构造type 到多个 bean 的映射
  public static <K,B> Map<K, List<B>> buildTypeBeansMap(ApplicationContext applicationContext, Class<B> beanClass, Function<B,K> keyFunc) {
        Map<K, List<B>> result = new HashMap<>();

        Map<String, B> beansOfType = applicationContext.getBeansOfType(beanClass);
       if(MapUtils.isEmpty(beansOfType)){
           return result;
       }

        for(B bean : beansOfType.values()){
            K type = keyFunc.apply(bean);
            result.putIfAbsent(type,new ArrayList<>());
            result.get(type).add(bean);
        }
        return result;
    }

// 构造type 到单个 bean 的映射
    public static <K,B> Map<K, B> buildType2BeanMap(ApplicationContext applicationContext, Class<B> beanClass, Function<B,K> keyFunc) {
        Map<K, B> result = new HashMap<>();

        Map<String, B> beansOfType = applicationContext.getBeansOfType(beanClass);
        if(MapUtils.isEmpty(beansOfType)){
            return result;
        }

        for(B bean : beansOfType.values()){
            K type = keyFunc.apply(bean);
            result.put(type,bean);
        }
        return result;
    }
}

改造后

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Service
public class DemoService  implements ApplicationContextAware {

    private Map<String, List<Handler>> type2HandlersMap;

    public void test(){
        String type ="Vip";
        for(Handler handler : type2HandlersMap.get(type)){
            handler.someThing();;
        }
    }


    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        type2HandlersMap = BeanStrategyUtils.buildTypeBeansMap(applicationContext,Handler.class, Handler::getType);
    }
}

很多人可能会说,写工具方法也很花时间呢。

但是,写工具方法之后,代码重复率降低;代码更加简洁,可读性提高;后续类似逻辑都可以实现代码复用,开发效率也提高了;一举多得。

4.6 使用加强包

前面讲到了,可以通过封装工具类来减少 Lambda 代码的复杂性。

此外,我们还可以考虑使用一些加强包来解决这个问题。

4.6.1 StreamEx

StreamEx

Maven 依赖

https://mvnrepository.com/artifact/one.util/streamex

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<dependency>
    <groupId>one.utilgroupId>
    <artifactId>streamexartifactId>
    <version>0.8.0version>
dependency>

Java 8 写法

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 Map<Role, List<User>> role2users = users.stream().collect(Collectors.groupingBy(User::getRole));

StreamEx 写法:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Map<Role, List<User>> role2users = StreamEx.of(users).groupingBy(User::getRole);

前面的案例

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
List<DogVO> vos = dogs.stream().map(DogDO2VOConverter::toVo).collect(Collectors.toList())

就可以改为

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 List<DogVO> vos = StreamEx.of(dogs).map(DogDO2VOConverter::toVo).toList();

4.6.2 vavr

vavr

用户文档:https://docs.vavr.io/

Maven 依赖

https://mvnrepository.com/artifact/io.vavr/vavr

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<dependency>
    <groupId>io.vavrgroupId>
    <artifactId>vavrartifactId>
    <version>1.0.0-alpha-4version>
dependency>

Java 8 中的写法:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// = ["1", "2", "3"] in Java 8
Arrays.asList(1, 2, 3)
      .stream()
      .map(Object::toString)
      .collect(Collectors.toList())

vavr 中的写法:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// = Stream("1", "2", "3") in Vavr
Stream.of(1, 2, 3).map(Object::toString)

4.7 有些场景不用 Lambda 表达式

如果你发现某个函数里使用 Lambda 过多时(实际工作中,发现会有人一个函数里一半以上都是 lambda 表达式,非常头疼),可以考虑将部分不容易懂的 Lambda 写法改为普通写法,通常可读性会大大提高。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
List<String> names = new LinkedList<>();
        names.addAll(users.stream().map(User::getName).filter(Objects::nonNull).collect(Collectors.toList()));
        names.addAll(users.stream().map(User::getNickname).filter(Objects::nonNull).collect(Collectors.toList()));

优化为

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 List<String> names = new LinkedList<>();
        for(User user : users) {
            String name = user.getName();
            if(name!= null ){
                names.add(name);
            }
            
            String nickname = user.getNickname();
            if(nickname != null){
                names.add(nickname);
            }
        }

虽然代码更长,但是更容易看懂。

还可将该部分逻辑封装为一个子函数并给一个有意义的命名。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
  /**
     * 获取名称和昵称
     */
    private  List<String> getNamesAndNickNames(List<User> users) {
        List<String> names = new LinkedList<>();
        for (User user : users) {
            String name = user.getName();
            if (name != null) {
                names.add(name);
            }

            String nickname = user.getNickname();
            if (nickname != null) {
                names.add(nickname);
            }
        }
        return names;
    }

使用时直接调用即可:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
List<String> names = getNamesAndNickNames(users);

这样外层函数可以明确知道这部分逻辑的意图,压根不需要看这么多代码,可读性大大提高。

五、思考

过犹不及,我们使用 Lambda 表达式时,一定不要忽略可读性。

Lambda 表达式没有错,错在很多人滥用 Lambda 表达式。

我们在编码过程中要注意做好权衡,掌握好度。

本文简单谈了 Lambda 表达式的利弊,给出自己的几点破解之法。

希望对大家有帮助。

当然,可能还有很多解决办法,欢迎留言补充。

希望大家能够努力做个有追求的程序员。

创作不易,如果本文对你有帮助,你的支持和鼓励,是我创作的最大动力。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021/11/27 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
复盘百度移动化,下一步去哪儿?
©原创2015-02-06罗超 在发布百度2014年财报时,李彦宏表示“2014年是百度取得巨大成就的一年,百度成功地从一家以PC为中心的互联网公司,转型为移动先行的公司。”这是百度首次对外表示自己已成为一家移动互联网公司。能说明这一点的数据有两点:2014年下半年百度移动流量已超过PC、Q4移动营收占比达到42%,12月份移动营收更是反超PC。流量和营收正在全面转向移动,百度做对了哪些事情?移动化并非目的和重点,百度下一步尤其是明年将走向何方?面临什么挑战? 如何移动化?四大“Mobile First”
罗超频道
2018/04/28
7860
华尔街“聪明钱”缘何看好百度移动布局?
最近美国《福布斯》的一篇题为《除了西蒙斯和索罗斯,谁还持有百度股票?》的文章流传甚广,其中提到,今年一季度以来,以索罗斯、SAC Capital的Steven Cohen和文艺复兴基金的西蒙斯为首的对冲基金大额都加仓了百度(BIDU)。除上述买方(buyside)外,卖方(sellside)的德意志银行和摩根大通等华尔街投资银行也纷纷将百度目标价调至200美元以上。聪明的钱(smart money)看好百度的原因是什么呢?而百度目前的市值是否能够反映其业务前景呢?笔者认为,市场缺乏对百度移动互联网战略的了
罗超频道
2018/04/25
5230
华尔街“聪明钱”缘何看好百度移动布局?
每日科技报-互联网论功行赏
一、都是央视“群发短信我不回”害的 尽管三大运营商还未公布今年拜年短信的数量,但一些运营商地方分公司的统计显示,今年拜年短信同比去年肯定下降了,降幅在10%至20%左右,但各地情况不一定完全一样,东部拜年短信的降幅高于中西部。浙江绍兴当地媒体披露的数据,今年绍兴地区除夕短信发送量为1100多万条,比去年下降约21%。 点评:去年春节微信用户数为3亿。运营商拜年短信不降反增。今年春节拐点还是来了。目前微信用户为7亿,国内超过5亿。拜年短信往往发生在不熟悉的人之间,现在,微信好友正在加入越来越多的不熟悉
罗超频道
2018/04/28
6300
移动转型之后,BAT 2015走向何方?
文:罗超。封面图:李彦宏参加2015年极客公园创新大会。 第一次看到李彦宏真身是在2013年初的极客公园创新大会上,这是他目前唯一参加的媒体举办的年会。两年前百度转型刚起步,李彦宏说搜索依然是移动互联网的入口,百度要做提供技术和服务的平台。两年后百度移动流量超越PC标志着移动化的成功。 两年后,李彦宏相约2015年极客公园,再谈移动转型,已是两重境界。 不止是百度,过去的两年是整个桌面互联网巨头移动转型、移动App创业大军从入局到出局的关键阶段。在两年前的极客大会上,所有互联网公司都还在谈移动转型,啪啪徐朝
罗超频道
2018/04/28
6130
那个会做产品的百度回来了
有没有发现,百度这段时间移动端的大动作忽然多了起来?先是宣布投资有赞和凯叔讲故事,接着与快手一起投资知乎,再接着,8月13日,百度官方宣布:百度App日活破2亿了,而且,这些动作不是孤立的,而是有着紧密联系的,这体现出百度移动产品正在排兵布阵,加快步伐。
罗超频道
2019/08/19
4690
那个会做产品的百度回来了
突发,AI大牛景鲲离职百度!CIO李莹接任小度CEO
即日起,百度集团副总裁、百度集团首席信息官(CIO)李莹担任小度科技CEO,向集团董事长兼CEO李彦宏直接汇报。
量子位
2023/10/08
2290
突发,AI大牛景鲲离职百度!CIO李莹接任小度CEO
2013,躲不开的BAT,战犹酣的三国杀
2013年年初,啪啪CEO许朝军说,PC互联网是正在沉没的泰坦尼克号。现在这个结论已经被大量的数据证明,从PC出货量到PC端流量再到PC端用户时长,均被移动端甩在了后面。2013年各巨头的首要目标似乎都是:抢船票。 一年过去,成绩单显示腾讯已经拥有微信、手机QQ和手机QQ空间三张船票;百度凭着拥有4亿用户的手机百度客户端,百度地图、以及在应用分发上的领先,拥有3张船票。阿里系进入移动DAU(日活跃)TOP10的仅有手机淘宝,且垫底。算上投资的新浪微博和UC浏览器,勉强算有2张站票。(数据来源
罗超频道
2018/04/28
7760
从不断的架构调整,看BAT的风格迥异
百度迎来最近一年内的第三次架构大调整,成立搜索公司、升级创新业务,被外界视作是“对标Google成立AlphaBet行为的调整”。关于架构调整本身,我已有过解读,核心主旨是,百度此举将PC和移动结合起来,信息和服务合二为一,并注重面向未来的创新业务布局。在关注百度架构调整之外,不妨从BAT近年来的架构调整思路,来看各家发展的风格迥异。 百度:调整以“合”为主,架构最精简 百度在2015年之前,架构调整不算频繁:2011年、2013年分别有两次大的架构调整。 2011年,百度架构迎来大调整,形成了销售、运营、
罗超频道
2018/04/25
1.2K0
朱光晋升百度高级副总裁,百度金融“1+3+2”阵型要做改革派
在年初组织架构大调整之后,百度金融服务事业群组已经与百度搜索公司和百度新兴业务事业群组并列,成为百度新的三驾马车。李彦宏更是明确,其个人会将更多精力放在互联网金融、无人车、人工智能等创新业务上。互联网
罗超频道
2018/04/27
8410
朱光晋升百度高级副总裁,百度金融“1+3+2”阵型要做改革派
百度股价破记录,移动聚变开始
摘要 : 已经尝到移动互联网甜头的百度,移动聚变反应还会持续。在后移动互联网时代,基于大数据的商业模式,基于人工智能的软硬件产品,围绕O2O的移动电商模式,都是百度移动“聚变”释放的能量波及之处,百度不再只是一个搜索引擎。 昨日百度公布Q3财报,Q3百度总营收约合22.03亿美元,同比增长52.0%;净利润约合6.315亿美元,同比增长27.2%,这两个增长率在去年Q3分别为42.3%和1.3%,无论是在收入还是盈利指标都证明了百度的成长性。漂亮的财务数据让百度股价上涨5.55%,市值近830亿美元,突破历
罗超频道
2018/04/28
5950
百度股价破记录,移动聚变开始
互联网巨头企业为什么都要做小程序?
2017年1月9日,微信小程序正式面世。这款在微信内使用、无需安装、触手可及、用完即走、无需卸载的轻应用,向用户展示了不同于网页、App的一种产品体验,同时也向开发者和创业者提供了更为便捷、低成本的创业选择。
闪云科技小程序
2019/03/28
8300
互联网巨头企业为什么都要做小程序?
百度无人车两年幻梦:10亿美元拆分未成,大神携手离职创业
百度无人车业务调整的背后,是一场10亿美元的幻梦,及得而复失的大神们…… △ 调整前的百度官网高管团队页面 量子位(QbitAI) | 舒石 李林 跑爷 发自 凹非寺 百度极有效率,邮件发出的当天,“
量子位
2018/03/21
8190
百度无人车两年幻梦:10亿美元拆分未成,大神携手离职创业
动态 | 百度宣布陆奇辞去集团总裁兼COO,留任副董事长
5月18日消息,百度宣布集团总裁兼首席运营官陆奇由于个人和家庭原因,无法继续全职在北京工作,将从7月起不再担任集团总裁兼首席运营官,但仍将继续担任集团公司副董事长。同时,副总裁王海峰晋升为高级副总裁并担任AIG (AI技术平台体系)总负责人。 陆奇不再负责百度日常运营管理工作后,主要业务部门负责人张亚勤、向海龙、王海峰、朱光等人将直接向百度董事长兼CEO李彦宏汇报,智能驾驶事业群组(IDG)总经理李震宇转向张亚勤汇报,景鲲将担任智能生活事业群组(SLG)总经理一职,未来一段时间里直接向李彦宏汇报。 李彦宏
昱良
2018/06/25
3580
技术往事:微信估值已超5千亿,雷军曾有机会收编张小龙及其Foxmail
据汇丰银行(HSBC)发布的报告显示,腾讯公司旗下手机通讯app微信市场价值估计高达836亿美元(约合人民币5344亿元),几乎是腾讯市值的一半。由此可见腾讯高市值的背后是微信在支撑着,腾讯的未来全靠微信。因为微信不仅是腾讯的移动互联网船票,还是令国内各大互联网公司颤抖的航空母舰。
JackJiang
2018/08/29
2.2K1
又一家无人机公司被曝裁员停工,百度原副总裁李明远参股
两年前,无人机风头无两。2015年,无人机被达沃斯列入“年度十大新兴科技之一”,2016年央视春晚,29架无人机亮相广州分场舞台。越来越多的创业公司跑步入场,涉水无人机行业。如今,无人机公司的日子似乎
机器人网
2018/04/24
8560
又一家无人机公司被曝裁员停工,百度原副总裁李明远参股
用户在闲暇时刻最爱用这些App,百度上榜一点不让人意外
3月8日,腾讯一天内同时投资斗鱼和虎牙,再现“互联网收割机”的本色。同时,映客、虎牙、快手都在谋求IPO,陌陌、YY和天鸽互动三家已经上市的公司业绩也都表现抢眼,可以明显感受到,内容产业比2017年更
罗超频道
2018/04/17
7800
用户在闲暇时刻最爱用这些App,百度上榜一点不让人意外
从工具到生态,百度App是如何构建搜索护城河的?
“搜索这个赛道从来不乏竞争者,平均每两年都有一个新的入局者,但百度始终保持80%以上的市场份额。”
用户2908108
2019/08/13
6920
从工具到生态,百度App是如何构建搜索护城河的?
BAT护城河的保质期
《汉书·蒯通传》有书:「必将婴城固守,皆为金城汤池,不可攻也。」成语固若金汤,即源出于此。这里面的「汤」,其实指的就是如同滚水般的护城河。
镁客网
2019/05/21
4090
BAT护城河的保质期
百度高层又有重大变动!陆奇卸任集团总裁兼首席运营官
百度高层又有重大变动!陆奇卸任集团总裁兼首席运营官
数据猿
2018/05/31
5510
支撑下一个百度的是连接铁三角
百度 Q2 财报发布之后股价连续几天上扬,截止周五收盘时市值已高达 792 亿美金,距离 800 亿美金俱乐部只有一步之遥,现在如果再说百度很快会进入 1000 亿美金俱乐部,恐怕不会有太多人认为这是夸大其词,这也验证了「百度被低估」的论断。资本市场之所以对百度如此热捧,与百度财报中的两项关键数据有关系: 1、百度营收和盈利平稳快速增长,Q2 总营收约 19.32 亿美元,同比增长 58.5%;净利润约 5.717 亿美元,同比增长 34.1%。 2、百度移动营收能力强劲,移动收入比例已突破 30%,超
罗超频道
2018/04/25
7960
推荐阅读
相关推荐
复盘百度移动化,下一步去哪儿?
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验