2010-09-23 53 views
4

我从一个任务创建一个商店出租这本书的问题,使用Store.java和Book.java。我完成了这个任务,但我很好奇为特定部分提供更好的算法。Java:For循环和如果算法

-

Book.java

public class Book { 

    private String name; 

    Book(String name) 
     this.name = name; 

    public String getName() 
     return name; 

} 

Store.java

在main();

Book bookObj[] = new Book[3]; //Create 3 Array of Object. 
bookObj[0] = new Book("Game Over"); 
bookObj[1] = new Book("Shrek"); 
bookObj[2] = new Book("Ghost"); 
Scanner console = new Scanner(System.in) 
input = console.nextLine(); 

假设,input = Devil。

现在,我需要做一个简单的搜索来检查特定的书是否存在。

实施例:

for(int i = 0; i < bookObj.length; i++) { 
    if(bookObj[i].getName().equals(input)) 
     System.out.println("Book Found!"); 
} 

显然,这是一个for循环,通过对象和检查阵列周期是否这样预订存在。现在,当我想给出一个没有找到该书的输出时,就会出现问题。

实施例:

for(int i = 0; i < bookObj.length; i++) { 
    if(bookObj[i].getName().equals(input)) 
     System.out.println("Book Found!"); 
    else 
     System.out.println("Book not Found!"); 
} 

与上面的代码的问题是,不预订实测值将被打印三次。我的目标是避免这样的问题。我确实有解决方案,但我仍然在寻找一个更好的使用getName()的方法,这在我看来仍然有改进的空间。

通常情况下,在结构化编程,我会做以下,

for(int i = 0; i < bookObj.length; i++) { 
    if(bookObj[i].getName().equals(input)) 
     System.out.println("Book Found!"); 
    else if(i == bookObj.length - 1) 
     System.out.println("Book not Found!"); 
} 

这有助于弄清楚究竟是一个循环的结束,并在搜索结束,但没有成功的结果从搜索。

我应该如何以面向对象的方式来思考它?

所有的一切,我的问题是,

  1. 有没有更好的方式来写上面的代码,而不是检查,它是该行的结束?
  2. 有没有更好的方法来利用getName()方法或使用其他方法?
+3

首先这个问题:你允许使用[Collections框架](http://download.oracle.com/javase/tutorial/collections/index.html)而不是数组吗?有很大的改进空间,但是功课通常只限于你实际学到的东西。而且,因为这还不清楚...... – BalusC 2010-09-23 14:40:24

+0

谢谢你的提示,但不幸的是,我被绑定到使用一个对象的数组这个任务。 – 2010-09-23 14:55:03

回答

6

您应该遍历数组并使用索引/布尔型标志来存储是否找到该书。然后根据索引/标志值在最后打印消息。

int foundAtIndex = -1; 
for(int i = 0; i < bookObj.length; i++) { 
    if(bookObj[i].getName().equals(input)) { 
     foundAtIndex = i; // store the actual index for later use 
     break;    // no need to search further 
    } 
} 
if(foundAtIndex >= 0) 
    System.out.println("Book Found!"); 
else 
    System.out.println("Book not Found!"); 

或者(除非你的任务明确要求使用数组),你应该更喜欢Set,它可以做搜索你到contains()一个电话。

我该如何以面向对象的方式来思考它?

在查看单个方法时,程序和OO风格之间没有太大区别。当试图组织大量概念上相关的数据和方法来处理这些差异时,差异开始出现在更高的层次上。

OO范例是将方法与它们操作的数据绑定在一起,并将它们封装在相关的对象和类中。这些类最好是重要领域概念的表示。因此,对于您的书店,您可能希望将所有与图书相关的代码放入Book课程中。然而,上述搜索方法(和它运行在藏书)不涉及任何特定的书实例,让你有不同的选择:

  • 把两者的藏书和搜索方法为Store(大概是普通会员)或
  • 将它们放入Book作为static成员。

第一个选择更自然,所以我通常会更喜欢这个。但是,在特定情况下,第二种选择可能更可取。在(OO)设计中,几乎没有干净的“是/否”答案 - 而是在不同选项之间进行权衡,每个选项都有自己的优势和弱点。

+0

这是正确的。使用集合(如'Set')而不是数组。 – 2010-09-23 14:42:21

+0

+1,尤其是提到Set – Randolpho 2010-09-23 14:46:43

+1

让我感到悲伤的是,数组被教导为主数据结构。它们现在变成了特殊情况的数据结构。 – 2010-09-23 15:45:28

2

您可以引入状态并记住您是否找到该书。

如果你不使用Java 1.4或更早版本,你也可以使用foreach循环语法:

boolean bookFound = false; 
for(Book currentBook : bookObj) { 
    if(currentBook.getName().equals(input)) 
    //TODO: see above 
} 

另外,我建议寻找到Collections library,并用列表或设置更换您的阵列:

Set<Book> books = new HashSet<Book>(); 
books.put(new Book("Game Over")); 
books.put(new Book("Shrek")); 
books.put(new Book("Ghost")); 

而且,虽然在它,你也可以考虑两本书是否相等,并相应地重写equals()和hashCode()。如果equal()会更改为检查标题,则可以简单地使用books.contains(new Book(input));并让库为您完成工作。

+0

请注意,当你找到你的物品时(或者使用break;或者在for循环中测试bookFound),你应该从循环中断开。还要注意,在当前的java中增强的循环是一个更好的解决方案。 – KevinDTimm 2010-09-23 14:48:52

+0

包含()没有出现在原始规范中 - equals()但是 - downvote否定 – KevinDTimm 2010-09-23 14:50:47

+0

@KevinDTimm我仍然编辑我的答案以包含contains()建议,因为它可能有用。 – 2010-09-23 14:53:25

1

要更好地解决问题,您必须了解Java的强大功能不是来自语言本身,而是来自Java Framework。

您应该了解Java Collection类的用法(不再适用于数组)。然后,你就可以用代码只是一条线,解决了搜索:

ArrayList<Book> listOfBooks; 
// init your list here 
listOfBooks.contains(new Book(input)); 

为了使这项工作,你还必须学会如何正确地实现了equals()Book类的方法。

快乐学习!

0

这里是一个工作的解决方案:

import java.util.Scanner; 

public class Store { 

    private static class Book { 

     private String name; 

     Book(String name) { 
     this.name = name; 
     } 

     public String getName() { 
     return name; 
     } 

    } 

    public static void main(String[] args) { 

     String input; 

     Book[] bookObj = new Book[3]; 

     bookObj[0] = new Book("Game Over"); 
     bookObj[1] = new Book("Shrek"); 
     bookObj[2] = new Book("Ghost"); 

     Scanner console = new Scanner(System.in); 
     input = console.nextLine(); 

     boolean found = false; 
     int i = 0; 
     while(!found && i < bookObj.length) { 

     if(bookObj[i].getName().equals(input)) { 

      System.out.println("Book Found at position : " + i); 
      found = true; 

     } else { 
      i++; 
     } 
     } 

     if(!found) { 
     System.out.println("Book not Found!"); 
     } 

     // Here i contains the indice of the element found in the array. 

    } 

} 
0

迄今为止您得到了一些非常好的建议。你问是否有更多的面向对象的方式来思考这个问题,所以我想我会试着去阐明它。正如Peter在设计的这个级别中已经提到的那样,这是一种单一的方法实现,所以这种方法将与程序方法相当类似。有什么优势?用一个词重用。如果你需要在很多地方按名字找到一本书,那么将代码移到它自己的类中将会有所帮助。

因此,您拥有的是一个Book实例,以封装单个图书的行为,但您希望拥有关于多本图书或书籍集合的行为。您可以保存数据(书本数组),并按照您在程序中列出的方法将它们分开的方法分开。但是,如果我们想收集一个地方在一堆书上做行为,我们可以定义一个新的类。让我们把它叫做图书馆,我们可以做一些这样的:

public class Library { 
    private Book[] books; 
    private bookCount = 0; 

    public Library(int numberOfTotalBooks) { 
     books = new Book[numberOfTotalBooks]; 
    } 

    public boolean addBook(Book book) { 
     if(bookCount < book.length) { 
     books[bookCount++] = book; 
     return true; 
     } 
     return false; 
    } 

    public Book findByTitle(String title) { 
     for(int i = 0; i < bookCount; i++) { 
      if(books[i].getTitle().equals(title)) { 
      return books[i]; 
      } 
     } 
     // didn't find one 
     return null; 
    } 
} 

那么几件事情需要注意做事这种方式。一个是,当我们与一个图书馆合作时,我们不知道那里有一个数组。我们可以使用一个数组,一个Set,一个List或一个数据库(最常见)。调用这些函数的代码只是与库的接口(不是文字Java接口,而是库的方法签名)一起工作。这也是一个更高层次的界面。我们不用担心迭代书籍,做循环,if语句等。我们只是调用一种方法,说“嘿,在图书馆找到这本书的名字”。如何做到这一点我们不在乎。这是面向对象的基本租户,称为封装,它看似强大。这是关于我们如何将责任委托给我们的计划,并将工作细节提供给个人班级。如果图书馆只有公共成员(即书籍和bookCount),或者吸收/设置者,那么客户端将不会获得任何优势,因为客户端仍然需要完成所有繁重的工作。面向对象的诀窍是弄清楚可以委托给对象的内容,而不会产生问题。这需要练习和经验。

这里的第二件事是我们将演示文稿和寻找书的行为分开了。你写的方法假定下一步是打印“嘿,我们找到了。”但是,Library对象只是在找到它时返回Book,否则返回null。这样就可以打印到控制台,在GUI中显示或者将其序列化为服务器中的JSON流。找到一本书的行为与可视化是分开的。这是编程的另一个重要方面,但一些与面向对象和封装有关。这通常称为关注点分离。控制台应用程序担心支持UI和打印控制台。图书馆只管理编目和管理图书集。这些细节如何执行都不在乎。

最后库是一个可重用的类。我们可以在控制台应用程序,桌面,Web或中间件服务器中使用它。更重要的是,我们也可以在单个程序中重复使用来自多个位置的findByTitle或addBooks的调用。同样,通过将数据与方法相结合,我们创造了一个障碍,可以使用该功能。你不能在你的程序中的任何地方做它。你必须有一个对图书馆的参考。如果你没有引用一个库实例,那么你不应该调用它。这对于新开发人员来说可能会很麻烦,因为他们缺乏经验来正确组织他们的程序以避免陷入困境(然后他们开始做价值对象,创建静态,单身等等,事情变成一大块泥潭)。这是一把双刃剑。

我还想指出的另一件事是说我们想模拟两个库。我们有一个图书馆在市中心和市中心,我们希望允许人们从图书馆借阅书籍。使用面向对象的代码很容易:

Library uptown = new Library(50); 
Library downtown = new Library(100); 

现在我们可以检查出其中的一本书。而且我没有使用静态(即全局变量),因此重复使用该逻辑非常简单。这些是面向对象的基础知识,所以它们是非常深刻的主题。奇怪的是,我可以在非常简单的主题上写很多东西。无论如何,我希望这有助于你更深入地理解你的程序,并看看如何使用面向对象来帮助你。

+0

谢谢,这是一个很好的提示,为我的未来编程! – 2010-09-25 12:18:16

-1

chubbsondubs最接近给予正确回答这个问题

他所错过的是,他的算法不正确,因为它包含了两个测试,当只需要一个人来。正确的代码只需要3条语句,如下所示:

public boolean zLibaryContains(String title) { 
     books[bookCount] = title; 
     int xBook = 0; 
     while(true) 
      if(books[xBook].getTitle().equals(title)) 
       return xBook != bookCount; 
      else xBook++;    
    } 

明显小于所有其他解决方案并且速度更快。简化,简化,简化。

面向对象的代码是支持糟糕设计的拐杖,否则这些设计太复杂而难以理解。我们的目标是编写代码,它很容易理解并保持OO是不必要的,并且会使程序变得更糟。当你的程序可以通过添加面向对象来改进时,这意味着你开始时做错了什么。

+0

-1用于发布可能在第一行中抛出一个'ArrayIndexOutOfBoundsException'的代码,同时将其他解决方案抨击为“不正确”。更不用说忽略Java编码惯例。 – 2014-10-21 07:13:54

+0

@PéterTörök我的代码中使用的技术是最有效的方法。第一行初始化一个标记(阅读Knuth 1.4.4)。对于使用标志变量并且甚至不知道标记是如何批评我的代码的“程序员”有点滑稽。 – 2014-10-21 21:19:27

+0

您一再对其他人可能或不可能知道的关于编程的知识(基于最少的信息)进行彻底的指控。放心,我读Knuth,并知道什么是哨兵。然而,用一种语言工作的东西可能不会自动移植到另一种语言。正确性胜过效率。事实上,索引一个超过其分配大小的Java数组会导致一个'ArrayIndexOutOfBoundsException'。 (除非你事先保留一个额外的元素 - 这可能并不总是可能的,而且在你的答案的任何地方都没有提及。) – 2014-10-22 09:55:04