Going back to the original question, joins are what databases do. If they can't do joins well, they fail miserablyin the market. So you'll find that either of the variations discussed here will be fast.
Note that "users" is a reserved word in Oracle -- you probably want to call your table something else. Also note that you'll raise errors in the original formulation if there are multiple records in addresses for a user. Normal joins will just give you multiple rows back.
Using tables of my own with reasonable indexing and amounts of data, the explain plans were:
Original
SELECT STATEMENT ALL_ROWSCost: 226 Bytes: 390,570 Cardinality: 39,057
2 TABLE ACCESS BY INDEX ROWID TABLE X83109.FN_AR_INVOICE Cost: 2 Bytes: 13 Cardinality: 1
1 INDEX UNIQUE SCAN INDEX (UNIQUE) X83109.I_FN_AR_INVOICE Cost: 1 Cardinality: 1
3 TABLE ACCESS FULL TABLE X83109.FN_AR_LINE_ITEM Cost: 226 Bytes: 390,570 Cardinality: 39,057
Either ANSI Join (as above) or traditional Oracle syntax, shown below
select users.username as username, addresses.state as email from users, addresses where users.username = addresses.username;
SELECT STATEMENT ALL_ROWSCost: 377 Bytes: 898,311 Cardinality: 39,057
3 HASH JOIN Cost: 377 Bytes: 898,311 Cardinality: 39,057
1 TABLE ACCESS FULL TABLE X83109.FN_AR_INVOICE Cost: 149 Bytes: 333,788 Cardinality: 25,676
2 TABLE ACCESS FULL TABLE X83109.FN_AR_LINE_ITEM Cost: 226 Bytes: 390,570 Cardinality: 39,057
So there's not a lot of difference. You should feel free to use joins, but also monitor performance.